VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/DevOHCI.cpp@ 81031

最後變更 在這個檔案從81031是 81031,由 vboxsync 提交於 5 年 前

PDM,Devices: Moving the PDMPCIDEV structures into the PDMDEVINS allocation. Preps for extending the config space to 4KB. bugref:9218

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 217.1 KB
 
1/* $Id: DevOHCI.cpp 81031 2019-09-26 19:26:33Z vboxsync $ */
2/** @file
3 * DevOHCI - Open Host Controller Interface for USB.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_dev_ohci OHCI - Open Host Controller Interface Emulation.
19 *
20 * This component implements an OHCI USB controller. It is split roughly in
21 * to two main parts, the first part implements the register level
22 * specification of USB OHCI and the second part maintains the root hub (which
23 * is an integrated component of the device).
24 *
25 * The OHCI registers are used for the usual stuff like enabling and disabling
26 * interrupts. Since the USB time is divided in to 1ms frames and various
27 * interrupts may need to be triggered at frame boundary time, a timer-based
28 * approach was taken. Whenever the bus is enabled ohci->eof_timer will be set.
29 *
30 * The actual USB transfers are stored in main memory (along with endpoint and
31 * transfer descriptors). The ED's for all the control and bulk endpoints are
32 * found by consulting the HcControlHeadED and HcBulkHeadED registers
33 * respectively. Interrupt ED's are different, they are found by looking
34 * in the HCCA (another communication area in main memory).
35 *
36 * At the start of every frame (in function ohci_sof) we traverse all enabled
37 * ED lists and queue up as many transfers as possible. No attention is paid
38 * to control/bulk service ratios or bandwidth requirements since our USB
39 * could conceivably contain a dozen high speed busses so this would
40 * artificially limit the performance.
41 *
42 * Once we have a transfer ready to go (in function ohciR3ServiceTd) we
43 * allocate an URB on the stack, fill in all the relevant fields and submit
44 * it using the VUSBIRhSubmitUrb function. The roothub device and the virtual
45 * USB core code (vusb.c) coordinates everything else from this point onwards.
46 *
47 * When the URB has been successfully handed to the lower level driver, our
48 * prepare callback gets called and we can remove the TD from the ED transfer
49 * list. This stops us queueing it twice while it completes.
50 * bird: no, we don't remove it because that confuses the guest! (=> crashes)
51 *
52 * Completed URBs are reaped at the end of every frame (in function
53 * ohci_frame_boundary). Our completion routine makes use of the ED and TD
54 * fields in the URB to store the physical addresses of the descriptors so
55 * that they may be modified in the roothub callbacks. Our completion
56 * routine (ohciRhXferComplete) carries out a number of tasks:
57 * -# Retires the TD associated with the transfer, setting the
58 * relevant error code etc.
59 * -# Updates done-queue interrupt timer and potentially causes
60 * a writeback of the done-queue.
61 * -# If the transfer was device-to-host, we copy the data in to
62 * the host memory.
63 *
64 * As for error handling OHCI allows for 3 retries before failing a transfer,
65 * an error count is stored in each transfer descriptor. A halt flag is also
66 * stored in the transfer descriptor. That allows for ED's to be disabled
67 * without stopping the bus and de-queuing them.
68 *
69 * When the bus is started and stopped we call VUSBIDevPowerOn/Off() on our
70 * roothub to indicate it's powering up and powering down. Whenever we power
71 * down, the USB core makes sure to synchronously complete all outstanding
72 * requests so that the OHCI is never seen in an inconsistent state by the
73 * guest OS (Transfers are not meant to be unlinked until they've actually
74 * completed, but we can't do that unless we work synchronously, so we just
75 * have to fake it).
76 * bird: we do work synchronously now, anything causes guest crashes.
77 */
78
79
80/*********************************************************************************************************************************
81* Header Files *
82*********************************************************************************************************************************/
83#define LOG_GROUP LOG_GROUP_DEV_OHCI
84#include <VBox/pci.h>
85#include <VBox/vmm/pdm.h>
86#include <VBox/vmm/mm.h>
87#include <VBox/err.h>
88#include <VBox/log.h>
89#include <iprt/assert.h>
90#include <iprt/string.h>
91#include <iprt/asm.h>
92#include <iprt/asm-math.h>
93#include <iprt/semaphore.h>
94#include <iprt/critsect.h>
95#include <iprt/param.h>
96#ifdef IN_RING3
97# include <iprt/alloca.h>
98# include <iprt/mem.h>
99# include <iprt/thread.h>
100# include <iprt/uuid.h>
101#endif
102#include <VBox/vusb.h>
103#include "VBoxDD.h"
104
105
106#define VBOX_WITH_OHCI_PHYS_READ_CACHE
107//#define VBOX_WITH_OHCI_PHYS_READ_STATS
108
109
110/*********************************************************************************************************************************
111* Structures and Typedefs *
112*********************************************************************************************************************************/
113/** The current saved state version. */
114#define OHCI_SAVED_STATE_VERSION 5 /* Introduced post-4.3. */
115/** The saved state with support of up to 8 ports. */
116#define OHCI_SAVED_STATE_VERSION_8PORTS 4 /* Introduced in 3.1 or so. */
117
118
119/** Maximum supported number of Downstream Ports on the root hub. 15 ports
120 * is the maximum defined by the OHCI spec. Must match the number of status
121 * register words to the 'opreg' array.
122 */
123#define OHCI_NDP_MAX 15
124
125/** Default NDP, chosen to be compatible with everything. */
126#define OHCI_NDP_DEFAULT 12
127
128/* Macro to query the number of currently configured ports. */
129#define OHCI_NDP_CFG(pohci) ((pohci)->RootHub.desc_a & OHCI_RHA_NDP)
130
131/** Pointer to OHCI device data. */
132typedef struct OHCI *POHCI;
133/** Read-only pointer to the OHCI device data. */
134typedef struct OHCI const *PCOHCI;
135
136#ifndef VBOX_DEVICE_STRUCT_TESTCASE
137/**
138 * Host controller transfer descriptor data.
139 */
140typedef struct VUSBURBHCITDINT
141{
142 /** Type of TD. */
143 uint32_t TdType;
144 /** The address of the */
145 RTGCPHYS32 TdAddr;
146 /** A copy of the TD. */
147 uint32_t TdCopy[16];
148} VUSBURBHCITDINT;
149
150/**
151 * The host controller data associated with each URB.
152 */
153typedef struct VUSBURBHCIINT
154{
155 /** The endpoint descriptor address. */
156 RTGCPHYS32 EdAddr;
157 /** Number of Tds in the array. */
158 uint32_t cTds;
159 /** When this URB was created.
160 * (Used for isochronous frames and for logging.) */
161 uint32_t u32FrameNo;
162 /** Flag indicating that the TDs have been unlinked. */
163 bool fUnlinked;
164} VUSBURBHCIINT;
165#endif
166
167/**
168 * An OHCI root hub port.
169 */
170typedef struct OHCIHUBPORT
171{
172 /** The port register. */
173 uint32_t fReg;
174#if HC_ARCH_BITS == 64
175 uint32_t Alignment0; /**< Align the pointer correctly. */
176#endif
177 /** The device attached to the port. */
178 R3PTRTYPE(PVUSBIDEVICE) pDev;
179} OHCIHUBPORT;
180#if HC_ARCH_BITS == 64
181AssertCompile(sizeof(OHCIHUBPORT) == 16); /* saved state */
182#endif
183/** Pointer to an OHCI hub port. */
184typedef OHCIHUBPORT *POHCIHUBPORT;
185
186/**
187 * The OHCI root hub.
188 *
189 * @implements PDMIBASE
190 * @implements VUSBIROOTHUBPORT
191 * @implements PDMILEDPORTS
192 */
193typedef struct ohci_roothub
194{
195 /** Pointer to the base interface of the VUSB RootHub. */
196 R3PTRTYPE(PPDMIBASE) pIBase;
197 /** Pointer to the connector interface of the VUSB RootHub. */
198 R3PTRTYPE(PVUSBIROOTHUBCONNECTOR) pIRhConn;
199 /** Pointer to the device interface of the VUSB RootHub. */
200 R3PTRTYPE(PVUSBIDEVICE) pIDev;
201 /** The base interface exposed to the roothub driver. */
202 PDMIBASE IBase;
203 /** The roothub port interface exposed to the roothub driver. */
204 VUSBIROOTHUBPORT IRhPort;
205
206 /** The LED. */
207 PDMLED Led;
208 /** The LED ports. */
209 PDMILEDPORTS ILeds;
210 /** Partner of ILeds. */
211 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
212
213 uint32_t status;
214 uint32_t desc_a;
215 uint32_t desc_b;
216#if HC_ARCH_BITS == 64
217 uint32_t Alignment0; /**< Align aPorts on a 8 byte boundary. */
218#endif
219 OHCIHUBPORT aPorts[OHCI_NDP_MAX];
220 R3PTRTYPE(POHCI) pOhci;
221} OHCIROOTHUB;
222/** Pointer to the OHCI root hub. */
223typedef OHCIROOTHUB *POHCIROOTHUB;
224
225
226/**
227 * Data used for reattaching devices on a state load.
228 */
229typedef struct ohci_load {
230 /** Timer used once after state load to inform the guest about new devices.
231 * We do this to be sure the guest get any disconnect / reconnect on the
232 * same port. */
233 PTMTIMERR3 pTimer;
234 /** Number of detached devices. */
235 unsigned cDevs;
236 /** Array of devices which were detached. */
237 PVUSBIDEVICE apDevs[OHCI_NDP_MAX];
238} OHCILOAD;
239/** Pointer to an OHCILOAD structure. */
240typedef OHCILOAD *POHCILOAD;
241
242#ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
243typedef struct OHCIPAGECACHE
244{
245 /** Last read physical page address. */
246 RTGCPHYS GCPhysReadCacheAddr;
247 /** Copy of last read physical page. */
248 uint8_t au8PhysReadCache[PAGE_SIZE];
249} OHCIPAGECACHE, *POHCIPAGECACHE;
250#endif
251
252/**
253 * OHCI device data.
254 */
255typedef struct OHCI
256{
257 /** Pointer to the device instance - R3 ptr. */
258 PPDMDEVINSR3 pDevInsR3;
259 /** The End-Of-Frame timer - R3 Ptr. */
260 PTMTIMERR3 pEndOfFrameTimerR3;
261
262 /** Pointer to the device instance - R0 ptr */
263 PPDMDEVINSR0 pDevInsR0;
264 /** The End-Of-Frame timer - R0 Ptr. */
265 PTMTIMERR0 pEndOfFrameTimerR0;
266
267 /** Pointer to the device instance - RC ptr. */
268 PPDMDEVINSRC pDevInsRC;
269 /** The End-Of-Frame timer - RC Ptr. */
270 PTMTIMERRC pEndOfFrameTimerRC;
271
272 /** Start of current frame. */
273 uint64_t SofTime;
274 /* done queue interrupt counter */
275 uint32_t dqic : 3;
276 /** frame number overflow. */
277 uint32_t fno : 1;
278 /** Address of the MMIO region assigned by PCI. */
279 RTGCPHYS32 MMIOBase;
280
281 /* Root hub device */
282 OHCIROOTHUB RootHub;
283
284 /* OHCI registers */
285
286 /** @name Control partition
287 * @{ */
288 /** HcControl. */
289 uint32_t ctl;
290 /** HcCommandStatus. */
291 uint32_t status;
292 /** HcInterruptStatus. */
293 uint32_t intr_status;
294 /** HcInterruptEnabled. */
295 uint32_t intr;
296 /** @} */
297
298 /** @name Memory pointer partition
299 * @{ */
300 /** HcHCCA. */
301 uint32_t hcca;
302 /** HcPeriodCurrentEd. */
303 uint32_t per_cur;
304 /** HcControlCurrentED. */
305 uint32_t ctrl_cur;
306 /** HcControlHeadED. */
307 uint32_t ctrl_head;
308 /** HcBlockCurrendED. */
309 uint32_t bulk_cur;
310 /** HcBlockHeadED. */
311 uint32_t bulk_head;
312 /** HcDoneHead. */
313 uint32_t done;
314 /** @} */
315
316 /** @name Frame counter partition
317 * @{ */
318 /** HcFmInterval.FSMPS - FSLargestDataPacket */
319 uint32_t fsmps : 15;
320 /** HcFmInterval.FIT - FrameItervalToggle */
321 uint32_t fit : 1;
322 /** HcFmInterval.FI - FrameInterval */
323 uint32_t fi : 14;
324 /** HcFmRemaining.FRT - toggle bit. */
325 uint32_t frt : 1;
326 /** HcFmNumber.
327 * @remark The register size is 16-bit, but for debugging and performance
328 * reasons we maintain a 32-bit counter. */
329 uint32_t HcFmNumber;
330 /** HcPeriodicStart */
331 uint32_t pstart;
332 /** @} */
333
334 /** The number of virtual time ticks per frame. */
335 uint64_t cTicksPerFrame;
336 /** The number of virtual time ticks per USB bus tick. */
337 uint64_t cTicksPerUsbTick;
338
339 /** Number of in-flight TDs. */
340 unsigned cInFlight;
341 unsigned Alignment0; /**< Align aInFlight on a 8 byte boundary. */
342 /** Array of in-flight TDs. */
343 struct ohci_td_in_flight
344 {
345 /** Address of the transport descriptor. */
346 uint32_t GCPhysTD;
347 /** Flag indicating an inactive (not-linked) URB. */
348 bool fInactive;
349 /** Pointer to the URB. */
350 R3PTRTYPE(PVUSBURB) pUrb;
351 } aInFlight[257];
352
353#if HC_ARCH_BITS == 32
354 uint32_t Alignment1;
355#endif
356
357 /** Number of in-done-queue TDs. */
358 unsigned cInDoneQueue;
359 /** Array of in-done-queue TDs. */
360 struct ohci_td_in_done_queue
361 {
362 /** Address of the transport descriptor. */
363 uint32_t GCPhysTD;
364 } aInDoneQueue[64];
365 /** When the tail of the done queue was added.
366 * Used to calculate the age of the done queue. */
367 uint32_t u32FmDoneQueueTail;
368#if R3_ARCH_BITS == 32
369 /** Align pLoad, the stats and the struct size correctly. */
370 uint32_t Alignment2;
371#endif
372 /** Pointer to state load data. */
373 R3PTRTYPE(POHCILOAD) pLoad;
374
375 /** Detected canceled isochronous URBs. */
376 STAMCOUNTER StatCanceledIsocUrbs;
377 /** Detected canceled general URBs. */
378 STAMCOUNTER StatCanceledGenUrbs;
379 /** Dropped URBs (endpoint halted, or URB canceled). */
380 STAMCOUNTER StatDroppedUrbs;
381 /** Profiling ohciR3FrameBoundaryTimer. */
382 STAMPROFILE StatTimer;
383
384 /** This member and all the following are not part of saved state. */
385 uint64_t SavedStateEnd;
386
387 /** VM timer frequency used for frame timer calculations. */
388 uint64_t u64TimerHz;
389 /** Idle detection flag; must be cleared at start of frame */
390 bool fIdle;
391 /** A flag indicating that the bulk list may have in-flight URBs. */
392 bool fBulkNeedsCleaning;
393
394 /** Whether RC/R0 is enabled. */
395 bool fRZEnabled;
396
397 uint32_t Alignment3; /**< Align size on a 8 byte boundary. */
398
399 /** Critical section synchronising interrupt handling. */
400 PDMCRITSECT CsIrq;
401 /** Critical section to synchronize the framer and URB completion handler. */
402 RTCRITSECT CritSect;
403#ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
404 /** Last read physical page for caching ED reads in the framer thread. */
405 R3PTRTYPE(POHCIPAGECACHE) pCacheED;
406 /** Last read physical page for caching TD reads in the framer thread. */
407 R3PTRTYPE(POHCIPAGECACHE) pCacheTD;
408#endif
409
410} OHCI;
411
412/* Standard OHCI bus speed */
413#define OHCI_DEFAULT_TIMER_FREQ 1000
414
415/* Host Controller Communications Area */
416#define OHCI_HCCA_NUM_INTR 32
417#define OHCI_HCCA_OFS (OHCI_HCCA_NUM_INTR * sizeof(uint32_t))
418struct ohci_hcca
419{
420 uint16_t frame;
421 uint16_t pad;
422 uint32_t done;
423};
424AssertCompileSize(ohci_hcca, 8);
425
426/** @name OHCI Endpoint Descriptor
427 * @{ */
428
429#define ED_PTR_MASK (~(uint32_t)0xf)
430#define ED_HWINFO_MPS 0x07ff0000
431#define ED_HWINFO_ISO RT_BIT(15)
432#define ED_HWINFO_SKIP RT_BIT(14)
433#define ED_HWINFO_LOWSPEED RT_BIT(13)
434#define ED_HWINFO_IN RT_BIT(12)
435#define ED_HWINFO_OUT RT_BIT(11)
436#define ED_HWINFO_DIR (RT_BIT(11) | RT_BIT(12))
437#define ED_HWINFO_ENDPOINT 0x780 /* 4 bits */
438#define ED_HWINFO_ENDPOINT_SHIFT 7
439#define ED_HWINFO_FUNCTION 0x7f /* 7 bits */
440#define ED_HEAD_CARRY RT_BIT(1)
441#define ED_HEAD_HALTED RT_BIT(0)
442
443/**
444 * OHCI Endpoint Descriptor.
445 */
446typedef struct OHCIED
447{
448 /** Flags and stuff. */
449 uint32_t hwinfo;
450 /** TailP - TD Queue Tail pointer. Bits 0-3 ignored / preserved. */
451 uint32_t TailP;
452 /** HeadP - TD Queue head pointer. Bit 0 - Halted, Bit 1 - toggleCarry. Bit 2&3 - 0. */
453 uint32_t HeadP;
454 /** NextED - Next Endpoint Descriptor. Bits 0-3 ignored / preserved. */
455 uint32_t NextED;
456} OHCIED, *POHCIED;
457typedef const OHCIED *PCOHCIED;
458/** @} */
459AssertCompileSize(OHCIED, 16);
460
461
462/** @name Completion Codes
463 * @{ */
464#define OHCI_CC_NO_ERROR (UINT32_C(0x00) << 28)
465#define OHCI_CC_CRC (UINT32_C(0x01) << 28)
466#define OHCI_CC_STALL (UINT32_C(0x04) << 28)
467#define OHCI_CC_DEVICE_NOT_RESPONDING (UINT32_C(0x05) << 28)
468#define OHCI_CC_DNR OHCI_CC_DEVICE_NOT_RESPONDING
469#define OHCI_CC_PID_CHECK_FAILURE (UINT32_C(0x06) << 28)
470#define OHCI_CC_UNEXPECTED_PID (UINT32_C(0x07) << 28)
471#define OHCI_CC_DATA_OVERRUN (UINT32_C(0x08) << 28)
472#define OHCI_CC_DATA_UNDERRUN (UINT32_C(0x09) << 28)
473/* 0x0a..0x0b - reserved */
474#define OHCI_CC_BUFFER_OVERRUN (UINT32_C(0x0c) << 28)
475#define OHCI_CC_BUFFER_UNDERRUN (UINT32_C(0x0d) << 28)
476#define OHCI_CC_NOT_ACCESSED_0 (UINT32_C(0x0e) << 28)
477#define OHCI_CC_NOT_ACCESSED_1 (UINT32_C(0x0f) << 28)
478/** @} */
479
480
481/** @name OHCI General transfer descriptor
482 * @{ */
483
484/** Error count (EC) shift. */
485#define TD_ERRORS_SHIFT 26
486/** Error count max. (One greater than what the EC field can hold.) */
487#define TD_ERRORS_MAX 4
488
489/** CC - Condition code mask. */
490#define TD_HWINFO_CC (UINT32_C(0xf0000000))
491#define TD_HWINFO_CC_SHIFT 28
492/** EC - Error count. */
493#define TD_HWINFO_ERRORS (RT_BIT(26) | RT_BIT(27))
494/** T - Data toggle. */
495#define TD_HWINFO_TOGGLE (RT_BIT(24) | RT_BIT(25))
496#define TD_HWINFO_TOGGLE_HI (RT_BIT(25))
497#define TD_HWINFO_TOGGLE_LO (RT_BIT(24))
498/** DI - Delay interrupt. */
499#define TD_HWINFO_DI (RT_BIT(21) | RT_BIT(22) | RT_BIT(23))
500#define TD_HWINFO_IN (RT_BIT(20))
501#define TD_HWINFO_OUT (RT_BIT(19))
502/** DP - Direction / PID. */
503#define TD_HWINFO_DIR (RT_BIT(19) | RT_BIT(20))
504/** R - Buffer rounding. */
505#define TD_HWINFO_ROUNDING (RT_BIT(18))
506/** Bits that are reserved / unknown. */
507#define TD_HWINFO_UNKNOWN_MASK (UINT32_C(0x0003ffff))
508
509/** SETUP - to endpoint. */
510#define OHCI_TD_DIR_SETUP 0x0
511/** OUT - to endpoint. */
512#define OHCI_TD_DIR_OUT 0x1
513/** IN - from endpoint. */
514#define OHCI_TD_DIR_IN 0x2
515/** Reserved. */
516#define OHCI_TD_DIR_RESERVED 0x3
517
518/**
519 * OHCI general transfer descriptor
520 */
521typedef struct OHCITD
522{
523 uint32_t hwinfo;
524 /** CBP - Current Buffer Pointer. (32-bit physical address) */
525 uint32_t cbp;
526 /** NextTD - Link to the next transfer descriptor. (32-bit physical address, dword aligned) */
527 uint32_t NextTD;
528 /** BE - Buffer End (inclusive). (32-bit physical address) */
529 uint32_t be;
530} OHCITD, *POHCITD;
531typedef const OHCITD *PCOHCITD;
532/** @} */
533AssertCompileSize(OHCIED, 16);
534
535
536/** @name OHCI isochronous transfer descriptor.
537 * @{ */
538/** SF - Start frame number. */
539#define ITD_HWINFO_SF 0xffff
540/** DI - Delay interrupt. (TD_HWINFO_DI) */
541#define ITD_HWINFO_DI (RT_BIT(21) | RT_BIT(22) | RT_BIT(23))
542#define ITD_HWINFO_DI_SHIFT 21
543/** FC - Frame count. */
544#define ITD_HWINFO_FC (RT_BIT(24) | RT_BIT(25) | RT_BIT(26))
545#define ITD_HWINFO_FC_SHIFT 24
546/** CC - Condition code mask. (=TD_HWINFO_CC) */
547#define ITD_HWINFO_CC UINT32_C(0xf0000000)
548#define ITD_HWINFO_CC_SHIFT 28
549/** The buffer page 0 mask (lower 12 bits are ignored). */
550#define ITD_BP0_MASK UINT32_C(0xfffff000)
551
552#define ITD_NUM_PSW 8
553/** OFFSET - offset of the package into the buffer page.
554 * (Only valid when CC set to Not Accessed.)
555 *
556 * Note that the top bit of the OFFSET field is overlapping with the
557 * first bit in the CC field. This is ok because both 0xf and 0xe are
558 * defined as "Not Accessed".
559 */
560#define ITD_PSW_OFFSET 0x1fff
561/** SIZE field mask for IN bound transfers.
562 * (Only valid when CC isn't Not Accessed.)*/
563#define ITD_PSW_SIZE 0x07ff
564/** CC field mask.
565 * USed to indicate the format of SIZE (Not Accessed -> OFFSET). */
566#define ITD_PSW_CC 0xf000
567#define ITD_PSW_CC_SHIFT 12
568
569/**
570 * OHCI isochronous transfer descriptor.
571 */
572typedef struct OHCIITD
573{
574 uint32_t HwInfo;
575 /** BP0 - Buffer Page 0. The lower 12 bits are ignored. */
576 uint32_t BP0;
577 /** NextTD - Link to the next transfer descriptor. (32-bit physical address, dword aligned) */
578 uint32_t NextTD;
579 /** BE - Buffer End (inclusive). (32-bit physical address) */
580 uint32_t BE;
581 /** (OffsetN/)PSWN - package status word array (0..7).
582 * The format varies depending on whether the package has been completed or not. */
583 uint16_t aPSW[ITD_NUM_PSW];
584} OHCIITD, *POHCIITD;
585typedef const OHCIITD *PCOHCIITD;
586/** @} */
587AssertCompileSize(OHCIITD, 32);
588
589/**
590 * OHCI register operator.
591 */
592typedef struct ohci_opreg
593{
594 const char *pszName;
595 int (*pfnRead )(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value);
596 int (*pfnWrite)(POHCI pThis, uint32_t iReg, uint32_t u32Value);
597} OHCIOPREG;
598
599
600/* OHCI Local stuff */
601#define OHCI_CTL_CBSR ((1<<0)|(1<<1)) /* Control/Bulk Service Ratio. */
602#define OHCI_CTL_PLE (1<<2) /* Periodic List Enable. */
603#define OHCI_CTL_IE (1<<3) /* Isochronous Enable. */
604#define OHCI_CTL_CLE (1<<4) /* Control List Enable. */
605#define OHCI_CTL_BLE (1<<5) /* Bulk List Enable. */
606#define OHCI_CTL_HCFS ((1<<6)|(1<<7)) /* Host Controller Functional State. */
607#define OHCI_USB_RESET 0x00
608#define OHCI_USB_RESUME 0x40
609#define OHCI_USB_OPERATIONAL 0x80
610#define OHCI_USB_SUSPEND 0xc0
611#define OHCI_CTL_IR (1<<8) /* Interrupt Routing (host/SMI). */
612#define OHCI_CTL_RWC (1<<9) /* Remote Wakeup Connected. */
613#define OHCI_CTL_RWE (1<<10) /* Remote Wakeup Enabled. */
614
615#define OHCI_STATUS_HCR (1<<0) /* Host Controller Reset. */
616#define OHCI_STATUS_CLF (1<<1) /* Control List Filled. */
617#define OHCI_STATUS_BLF (1<<2) /* Bulk List Filled. */
618#define OHCI_STATUS_OCR (1<<3) /* Ownership Change Request. */
619#define OHCI_STATUS_SOC ((1<<6)|(1<<7)) /* Scheduling Overrun Count. */
620
621/** @name Interrupt Status and Enabled/Disabled Flags
622 * @{ */
623/** SO - Scheduling overrun. */
624#define OHCI_INTR_SCHEDULING_OVERRUN RT_BIT(0)
625/** WDH - HcDoneHead writeback. */
626#define OHCI_INTR_WRITE_DONE_HEAD RT_BIT(1)
627/** SF - Start of frame. */
628#define OHCI_INTR_START_OF_FRAME RT_BIT(2)
629/** RD - Resume detect. */
630#define OHCI_INTR_RESUME_DETECT RT_BIT(3)
631/** UE - Unrecoverable error. */
632#define OHCI_INTR_UNRECOVERABLE_ERROR RT_BIT(4)
633/** FNO - Frame number overflow. */
634#define OHCI_INTR_FRAMENUMBER_OVERFLOW RT_BIT(5)
635/** RHSC- Root hub status change. */
636#define OHCI_INTR_ROOT_HUB_STATUS_CHANGE RT_BIT(6)
637/** OC - Ownership change. */
638#define OHCI_INTR_OWNERSHIP_CHANGE RT_BIT(30)
639/** MIE - Master interrupt enable. */
640#define OHCI_INTR_MASTER_INTERRUPT_ENABLED RT_BIT(31)
641/** @} */
642
643#define OHCI_HCCA_SIZE 0x100
644#define OHCI_HCCA_MASK UINT32_C(0xffffff00)
645
646#define OHCI_FMI_FI UINT32_C(0x00003fff) /* Frame Interval. */
647#define OHCI_FMI_FSMPS UINT32_C(0x7fff0000) /* Full-Speed Max Packet Size. */
648#define OHCI_FMI_FSMPS_SHIFT 16
649#define OHCI_FMI_FIT UINT32_C(0x80000000) /* Frame Interval Toggle. */
650#define OHCI_FMI_FIT_SHIFT 31
651
652#define OHCI_FR_FRT RT_BIT_32(31) /* Frame Remaining Toggle */
653
654#define OHCI_LS_THRESH 0x628 /* Low-Speed Threshold. */
655
656#define OHCI_RHA_NDP (0xff) /* Number of Downstream Ports. */
657#define OHCI_RHA_PSM RT_BIT_32(8) /* Power Switching Mode. */
658#define OHCI_RHA_NPS RT_BIT_32(9) /* No Power Switching. */
659#define OHCI_RHA_DT RT_BIT_32(10) /* Device Type. */
660#define OHCI_RHA_OCPM RT_BIT_32(11) /* Over-Current Protection Mode. */
661#define OHCI_RHA_NOCP RT_BIT_32(12) /* No Over-Current Protection. */
662#define OHCI_RHA_POTPGP UINT32_C(0xff000000) /* Power On To Power Good Time. */
663
664#define OHCI_RHS_LPS RT_BIT_32(0) /* Local Power Status. */
665#define OHCI_RHS_OCI RT_BIT_32(1) /* Over-Current Indicator. */
666#define OHCI_RHS_DRWE RT_BIT_32(15) /* Device Remote Wakeup Enable. */
667#define OHCI_RHS_LPSC RT_BIT_32(16) /* Local Power Status Change. */
668#define OHCI_RHS_OCIC RT_BIT_32(17) /* Over-Current Indicator Change. */
669#define OHCI_RHS_CRWE RT_BIT_32(31) /* Clear Remote Wakeup Enable. */
670
671/** @name HcRhPortStatus[n] - RH Port Status register (read).
672 * @{ */
673/** CCS - CurrentConnectionStatus - 0 = no device, 1 = device. */
674#define OHCI_PORT_CCS RT_BIT(0)
675/** ClearPortEnable (when writing CCS). */
676#define OHCI_PORT_CLRPE OHCI_PORT_CCS
677/** PES - PortEnableStatus. */
678#define OHCI_PORT_PES RT_BIT(1)
679/** PSS - PortSuspendStatus */
680#define OHCI_PORT_PSS RT_BIT(2)
681/** POCI- PortOverCurrentIndicator. */
682#define OHCI_PORT_POCI RT_BIT(3)
683/** ClearSuspendStatus (when writing POCI). */
684#define OHCI_PORT_CLRSS OHCI_PORT_POCI
685/** PRS - PortResetStatus */
686#define OHCI_PORT_PRS RT_BIT(4)
687/** PPS - PortPowerStatus */
688#define OHCI_PORT_PPS RT_BIT(8)
689/** LSDA - LowSpeedDeviceAttached */
690#define OHCI_PORT_LSDA RT_BIT(9)
691/** ClearPortPower (when writing LSDA). */
692#define OHCI_PORT_CLRPP OHCI_PORT_LSDA
693/** CSC - ConnectStatusChange */
694#define OHCI_PORT_CSC RT_BIT(16)
695/** PESC - PortEnableStatusChange */
696#define OHCI_PORT_PESC RT_BIT(17)
697/** PSSC - PortSuspendStatusChange */
698#define OHCI_PORT_PSSC RT_BIT(18)
699/** OCIC - OverCurrentIndicatorChange */
700#define OHCI_PORT_OCIC RT_BIT(19)
701/** PRSC - PortResetStatusChange */
702#define OHCI_PORT_PRSC RT_BIT(20)
703/** The mask of RW1C bits. */
704#define OHCI_PORT_CLEAR_CHANGE_MASK (OHCI_PORT_CSC | OHCI_PORT_PESC | OHCI_PORT_PSSC | OHCI_PORT_OCIC | OHCI_PORT_PRSC)
705/** @} */
706
707
708#ifndef VBOX_DEVICE_STRUCT_TESTCASE
709
710#ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
711/*
712 * Explain
713 */
714typedef struct OHCIDESCREADSTATS
715{
716 uint32_t cReads;
717 uint32_t cPageChange;
718 uint32_t cMinReadsPerPage;
719 uint32_t cMaxReadsPerPage;
720
721 uint32_t cReadsLastPage;
722 uint32_t u32LastPageAddr;
723} OHCIDESCREADSTATS;
724typedef OHCIDESCREADSTATS *POHCIDESCREADSTATS;
725
726typedef struct OHCIPHYSREADSTATS
727{
728 OHCIDESCREADSTATS ed;
729 OHCIDESCREADSTATS td;
730 OHCIDESCREADSTATS all;
731
732 uint32_t cCrossReads;
733 uint32_t cCacheReads;
734 uint32_t cPageReads;
735} OHCIPHYSREADSTATS;
736typedef OHCIPHYSREADSTATS *POHCIPHYSREADSTATS;
737typedef OHCIPHYSREADSTATS const *PCOHCIPHYSREADSTATS;
738#endif /* VBOX_WITH_OHCI_PHYS_READ_STATS */
739
740
741/*********************************************************************************************************************************
742* Global Variables *
743*********************************************************************************************************************************/
744#if defined(VBOX_WITH_OHCI_PHYS_READ_STATS) && defined(IN_RING3)
745static OHCIPHYSREADSTATS g_PhysReadState;
746#endif
747
748#if defined(LOG_ENABLED) && defined(IN_RING3)
749static bool g_fLogBulkEPs = false;
750static bool g_fLogControlEPs = false;
751static bool g_fLogInterruptEPs = false;
752#endif
753#ifdef IN_RING3
754/**
755 * SSM descriptor table for the OHCI structure.
756 */
757static SSMFIELD const g_aOhciFields[] =
758{
759 SSMFIELD_ENTRY( OHCI, SofTime),
760 SSMFIELD_ENTRY_CUSTOM( dpic+fno, RT_OFFSETOF(OHCI, SofTime) + RT_SIZEOFMEMB(OHCI, SofTime), 4),
761 SSMFIELD_ENTRY( OHCI, RootHub.status),
762 SSMFIELD_ENTRY( OHCI, RootHub.desc_a),
763 SSMFIELD_ENTRY( OHCI, RootHub.desc_b),
764 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[0].fReg),
765 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[1].fReg),
766 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[2].fReg),
767 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[3].fReg),
768 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[4].fReg),
769 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[5].fReg),
770 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[6].fReg),
771 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[7].fReg),
772 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[8].fReg),
773 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[9].fReg),
774 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[10].fReg),
775 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[11].fReg),
776 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[12].fReg),
777 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[13].fReg),
778 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[14].fReg),
779 SSMFIELD_ENTRY( OHCI, ctl),
780 SSMFIELD_ENTRY( OHCI, status),
781 SSMFIELD_ENTRY( OHCI, intr_status),
782 SSMFIELD_ENTRY( OHCI, intr),
783 SSMFIELD_ENTRY( OHCI, hcca),
784 SSMFIELD_ENTRY( OHCI, per_cur),
785 SSMFIELD_ENTRY( OHCI, ctrl_cur),
786 SSMFIELD_ENTRY( OHCI, ctrl_head),
787 SSMFIELD_ENTRY( OHCI, bulk_cur),
788 SSMFIELD_ENTRY( OHCI, bulk_head),
789 SSMFIELD_ENTRY( OHCI, done),
790 SSMFIELD_ENTRY_CUSTOM( fsmps+fit+fi+frt, RT_OFFSETOF(OHCI, done) + RT_SIZEOFMEMB(OHCI, done), 4),
791 SSMFIELD_ENTRY( OHCI, HcFmNumber),
792 SSMFIELD_ENTRY( OHCI, pstart),
793 SSMFIELD_ENTRY_TERM()
794};
795
796/**
797 * SSM descriptor table for the older 8-port OHCI structure.
798 */
799static SSMFIELD const g_aOhciFields8Ports[] =
800{
801 SSMFIELD_ENTRY( OHCI, SofTime),
802 SSMFIELD_ENTRY_CUSTOM( dpic+fno, RT_OFFSETOF(OHCI, SofTime) + RT_SIZEOFMEMB(OHCI, SofTime), 4),
803 SSMFIELD_ENTRY( OHCI, RootHub.status),
804 SSMFIELD_ENTRY( OHCI, RootHub.desc_a),
805 SSMFIELD_ENTRY( OHCI, RootHub.desc_b),
806 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[0].fReg),
807 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[1].fReg),
808 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[2].fReg),
809 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[3].fReg),
810 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[4].fReg),
811 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[5].fReg),
812 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[6].fReg),
813 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[7].fReg),
814 SSMFIELD_ENTRY( OHCI, ctl),
815 SSMFIELD_ENTRY( OHCI, status),
816 SSMFIELD_ENTRY( OHCI, intr_status),
817 SSMFIELD_ENTRY( OHCI, intr),
818 SSMFIELD_ENTRY( OHCI, hcca),
819 SSMFIELD_ENTRY( OHCI, per_cur),
820 SSMFIELD_ENTRY( OHCI, ctrl_cur),
821 SSMFIELD_ENTRY( OHCI, ctrl_head),
822 SSMFIELD_ENTRY( OHCI, bulk_cur),
823 SSMFIELD_ENTRY( OHCI, bulk_head),
824 SSMFIELD_ENTRY( OHCI, done),
825 SSMFIELD_ENTRY_CUSTOM( fsmps+fit+fi+frt, RT_OFFSETOF(OHCI, done) + RT_SIZEOFMEMB(OHCI, done), 4),
826 SSMFIELD_ENTRY( OHCI, HcFmNumber),
827 SSMFIELD_ENTRY( OHCI, pstart),
828 SSMFIELD_ENTRY_TERM()
829};
830#endif
831
832
833/*********************************************************************************************************************************
834* Internal Functions *
835*********************************************************************************************************************************/
836RT_C_DECLS_BEGIN
837#ifdef IN_RING3
838/* Update host controller state to reflect a device attach */
839static void ohciR3RhPortPower(POHCIROOTHUB pRh, unsigned iPort, bool fPowerUp);
840static void ohciR3BusResume(POHCI ohci, bool fHardware);
841static void ohciR3BusStop(POHCI pThis);
842#ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
843static void ohciR3PhysReadCacheClear(POHCIPAGECACHE pPageCache);
844#endif
845
846static DECLCALLBACK(void) ohciR3RhXferCompletion(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb);
847static DECLCALLBACK(bool) ohciR3RhXferError(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb);
848
849static int ohciR3InFlightFind(POHCI pThis, uint32_t GCPhysTD);
850# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
851static int ohciR3InDoneQueueFind(POHCI pThis, uint32_t GCPhysTD);
852# endif
853static DECLCALLBACK(void) ohciR3LoadReattachDevices(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
854#endif /* IN_RING3 */
855RT_C_DECLS_END
856
857
858/**
859 * Update PCI IRQ levels
860 */
861static void ohciUpdateInterruptLocked(POHCI ohci, const char *msg)
862{
863 int level = 0;
864
865 if ( (ohci->intr & OHCI_INTR_MASTER_INTERRUPT_ENABLED)
866 && (ohci->intr_status & ohci->intr)
867 && !(ohci->ctl & OHCI_CTL_IR))
868 level = 1;
869
870 PDMDevHlpPCISetIrq(ohci->CTX_SUFF(pDevIns), 0, level);
871 if (level)
872 {
873 uint32_t val = ohci->intr_status & ohci->intr;
874 Log2(("ohci: Fired off interrupt %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d - %s\n",
875 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
876 (val >> 6) & 1, (val >> 30) & 1, msg)); NOREF(val); NOREF(msg);
877 }
878}
879
880#ifdef IN_RING3
881
882/**
883 * Set an interrupt, use the wrapper ohciSetInterrupt.
884 */
885DECLINLINE(int) ohciR3SetInterruptInt(POHCI ohci, int rcBusy, uint32_t intr, const char *msg)
886{
887 int rc = PDMCritSectEnter(&ohci->CsIrq, rcBusy);
888 if (rc != VINF_SUCCESS)
889 return rc;
890
891 if ( (ohci->intr_status & intr) != intr )
892 {
893 ohci->intr_status |= intr;
894 ohciUpdateInterruptLocked(ohci, msg);
895 }
896
897 PDMCritSectLeave(&ohci->CsIrq);
898 return rc;
899}
900
901/**
902 * Set an interrupt wrapper macro for logging purposes.
903 */
904# define ohciR3SetInterrupt(ohci, intr) ohciR3SetInterruptInt(ohci, VERR_IGNORED, intr, #intr)
905
906
907/**
908 * Sets the HC in the unrecoverable error state and raises the appropriate interrupt.
909 *
910 * @returns nothing.
911 * @param pThis The OHCI instance.
912 * @param iCode Diagnostic code.
913 */
914DECLINLINE(void) ohciR3RaiseUnrecoverableError(POHCI pThis, int iCode)
915{
916 LogRelMax(10, ("OHCI#%d: Raising unrecoverable error (%d)\n", pThis->pDevInsR3->iInstance, iCode));
917 ohciR3SetInterrupt(pThis, OHCI_INTR_UNRECOVERABLE_ERROR);
918}
919
920
921/* Carry out a hardware remote wakeup */
922static void ohciR3RemoteWakeup(POHCI pThis)
923{
924 if ((pThis->ctl & OHCI_CTL_HCFS) != OHCI_USB_SUSPEND)
925 return;
926 if (!(pThis->RootHub.status & OHCI_RHS_DRWE))
927 return;
928 ohciR3BusResume(pThis, true /* hardware */);
929}
930
931
932/**
933 * Query interface method for the roothub LUN.
934 */
935static DECLCALLBACK(void *) ohciR3RhQueryInterface(PPDMIBASE pInterface, const char *pszIID)
936{
937 POHCI pThis = RT_FROM_MEMBER(pInterface, OHCI, RootHub.IBase);
938 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->RootHub.IBase);
939 PDMIBASE_RETURN_INTERFACE(pszIID, VUSBIROOTHUBPORT, &pThis->RootHub.IRhPort);
940 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->RootHub.ILeds);
941 return NULL;
942}
943
944/**
945 * Gets the pointer to the status LED of a unit.
946 *
947 * @returns VBox status code.
948 * @param pInterface Pointer to the interface structure containing the called function pointer.
949 * @param iLUN The unit which status LED we desire.
950 * @param ppLed Where to store the LED pointer.
951 */
952static DECLCALLBACK(int) ohciR3RhQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
953{
954 POHCI pThis = (POHCI)((uintptr_t)pInterface - RT_OFFSETOF(OHCI, RootHub.ILeds));
955 if (iLUN == 0)
956 {
957 *ppLed = &pThis->RootHub.Led;
958 return VINF_SUCCESS;
959 }
960 return VERR_PDM_LUN_NOT_FOUND;
961}
962
963
964/** Converts a OHCI.roothub.IRhPort pointer to a POHCI. */
965#define VUSBIROOTHUBPORT_2_OHCI(pInterface) ((POHCI)( (uintptr_t)(pInterface) - RT_OFFSETOF(OHCI, RootHub.IRhPort) ))
966
967/**
968 * Get the number of available ports in the hub.
969 *
970 * @returns The number of ports available.
971 * @param pInterface Pointer to this structure.
972 * @param pAvailable Bitmap indicating the available ports. Set bit == available port.
973 */
974static DECLCALLBACK(unsigned) ohciR3RhGetAvailablePorts(PVUSBIROOTHUBPORT pInterface, PVUSBPORTBITMAP pAvailable)
975{
976 POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
977 unsigned iPort;
978 unsigned cPorts = 0;
979
980 memset(pAvailable, 0, sizeof(*pAvailable));
981
982 PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
983 for (iPort = 0; iPort < OHCI_NDP_CFG(pThis); iPort++)
984 {
985 if (!pThis->RootHub.aPorts[iPort].pDev)
986 {
987 cPorts++;
988 ASMBitSet(pAvailable, iPort + 1);
989 }
990 }
991 PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
992
993 return cPorts;
994}
995
996
997/**
998 * Gets the supported USB versions.
999 *
1000 * @returns The mask of supported USB versions.
1001 * @param pInterface Pointer to this structure.
1002 */
1003static DECLCALLBACK(uint32_t) ohciR3RhGetUSBVersions(PVUSBIROOTHUBPORT pInterface)
1004{
1005 RT_NOREF(pInterface);
1006 return VUSB_STDVER_11;
1007}
1008
1009
1010/**
1011 * A device is being attached to a port in the roothub.
1012 *
1013 * @param pInterface Pointer to this structure.
1014 * @param pDev Pointer to the device being attached.
1015 * @param uPort The port number assigned to the device.
1016 */
1017static DECLCALLBACK(int) ohciR3RhAttach(PVUSBIROOTHUBPORT pInterface, PVUSBIDEVICE pDev, unsigned uPort)
1018{
1019 POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
1020 VUSBSPEED enmSpeed;
1021 LogFlow(("ohciR3RhAttach: pDev=%p uPort=%u\n", pDev, uPort));
1022 PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
1023
1024 /*
1025 * Validate and adjust input.
1026 */
1027 Assert(uPort >= 1 && uPort <= OHCI_NDP_CFG(pThis));
1028 uPort--;
1029 Assert(!pThis->RootHub.aPorts[uPort].pDev);
1030 enmSpeed = pDev->pfnGetSpeed(pDev);
1031 /* Only LS/FS devices can end up here. */
1032 Assert(enmSpeed == VUSB_SPEED_LOW || enmSpeed == VUSB_SPEED_FULL);
1033
1034 /*
1035 * Attach it.
1036 */
1037 pThis->RootHub.aPorts[uPort].fReg = OHCI_PORT_CCS | OHCI_PORT_CSC;
1038 if (enmSpeed == VUSB_SPEED_LOW)
1039 pThis->RootHub.aPorts[uPort].fReg |= OHCI_PORT_LSDA;
1040 pThis->RootHub.aPorts[uPort].pDev = pDev;
1041 ohciR3RhPortPower(&pThis->RootHub, uPort, 1 /* power on */);
1042
1043 ohciR3RemoteWakeup(pThis);
1044 ohciR3SetInterrupt(pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
1045
1046 PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
1047 return VINF_SUCCESS;
1048}
1049
1050
1051/**
1052 * A device is being detached from a port in the roothub.
1053 *
1054 * @param pInterface Pointer to this structure.
1055 * @param pDev Pointer to the device being detached.
1056 * @param uPort The port number assigned to the device.
1057 */
1058static DECLCALLBACK(void) ohciR3RhDetach(PVUSBIROOTHUBPORT pInterface, PVUSBIDEVICE pDev, unsigned uPort)
1059{
1060 RT_NOREF(pDev);
1061 POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
1062 LogFlow(("ohciR3RhDetach: pDev=%p uPort=%u\n", pDev, uPort));
1063 PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
1064
1065 /*
1066 * Validate and adjust input.
1067 */
1068 Assert(uPort >= 1 && uPort <= OHCI_NDP_CFG(pThis));
1069 uPort--;
1070 Assert(pThis->RootHub.aPorts[uPort].pDev == pDev);
1071
1072 /*
1073 * Detach it.
1074 */
1075 pThis->RootHub.aPorts[uPort].pDev = NULL;
1076 if (pThis->RootHub.aPorts[uPort].fReg & OHCI_PORT_PES)
1077 pThis->RootHub.aPorts[uPort].fReg = OHCI_PORT_CSC | OHCI_PORT_PESC;
1078 else
1079 pThis->RootHub.aPorts[uPort].fReg = OHCI_PORT_CSC;
1080
1081 ohciR3RemoteWakeup(pThis);
1082 ohciR3SetInterrupt(pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
1083
1084 PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
1085}
1086
1087
1088/**
1089 * One of the roothub devices has completed its reset operation.
1090 *
1091 * Currently, we don't think anything is required to be done here
1092 * so it's just a stub for forcing async resetting of the devices
1093 * during a root hub reset.
1094 *
1095 * @param pDev The root hub device.
1096 * @param rc The result of the operation.
1097 * @param pvUser Pointer to the controller.
1098 */
1099static DECLCALLBACK(void) ohciR3RhResetDoneOneDev(PVUSBIDEVICE pDev, int rc, void *pvUser)
1100{
1101 LogRel(("OHCI: root hub reset completed with %Rrc\n", rc));
1102 NOREF(pDev); NOREF(rc); NOREF(pvUser);
1103}
1104
1105
1106/**
1107 * Reset the root hub.
1108 *
1109 * @returns VBox status code.
1110 * @param pInterface Pointer to this structure.
1111 * @param fResetOnLinux This is used to indicate whether we're at VM reset time and
1112 * can do real resets or if we're at any other time where that
1113 * isn't such a good idea.
1114 * @remark Do NOT call VUSBIDevReset on the root hub in an async fashion!
1115 * @thread EMT
1116 */
1117static DECLCALLBACK(int) ohciR3RhReset(PVUSBIROOTHUBPORT pInterface, bool fResetOnLinux)
1118{
1119 POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
1120 PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
1121
1122 Log(("ohci: root hub reset%s\n", fResetOnLinux ? " (reset on linux)" : ""));
1123
1124 pThis->RootHub.status = 0;
1125 pThis->RootHub.desc_a = OHCI_RHA_NPS | OHCI_NDP_CFG(pThis); /* Preserve NDP value. */
1126 pThis->RootHub.desc_b = 0x0; /* Impl. specific */
1127
1128 /*
1129 * We're pending to _reattach_ the device without resetting them.
1130 * Except, during VM reset where we use the opportunity to do a proper
1131 * reset before the guest comes along and expect things.
1132 *
1133 * However, it's very very likely that we're not doing the right thing
1134 * here if coming from the guest (USB Reset state). The docs talks about
1135 * root hub resetting, however what exact behaviour in terms of root hub
1136 * status and changed bits, and HC interrupts aren't stated clearly. IF we
1137 * get trouble and see the guest doing "USB Resets" we will have to look
1138 * into this. For the time being we stick with simple.
1139 */
1140 for (unsigned iPort = 0; iPort < OHCI_NDP_CFG(pThis); iPort++)
1141 {
1142 if (pThis->RootHub.aPorts[iPort].pDev)
1143 {
1144 pThis->RootHub.aPorts[iPort].fReg = OHCI_PORT_CCS | OHCI_PORT_CSC | OHCI_PORT_PPS;
1145 if (fResetOnLinux)
1146 {
1147 PVM pVM = PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns));
1148 VUSBIDevReset(pThis->RootHub.aPorts[iPort].pDev, fResetOnLinux, ohciR3RhResetDoneOneDev, pThis, pVM);
1149 }
1150 }
1151 else
1152 pThis->RootHub.aPorts[iPort].fReg = 0;
1153 }
1154 ohciR3SetInterrupt(pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
1155
1156 PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
1157 return VINF_SUCCESS;
1158}
1159
1160/**
1161 * Does a software or hardware reset of the controller.
1162 *
1163 * This is called in response to setting HcCommandStatus.HCR, hardware reset,
1164 * and device construction.
1165 *
1166 * @param pThis The ohci instance data.
1167 * @param fNewMode The new mode of operation. This is UsbSuspend if it's a
1168 * software reset, and UsbReset if it's a hardware reset / cold boot.
1169 * @param fResetOnLinux Set if we can do a real reset of the devices attached to the root hub.
1170 * This is really a just a hack for the non-working linux device reset.
1171 * Linux has this feature called 'logical disconnect' if device reset fails
1172 * which prevents us from doing resets when the guest asks for it - the guest
1173 * will get confused when the device seems to be reconnected everytime it tries
1174 * to reset it. But if we're at hardware reset time, we can allow a device to
1175 * be 'reconnected' without upsetting the guest.
1176 *
1177 * @remark This hasn't got anything to do with software setting the mode to UsbReset.
1178 */
1179static void ohciR3DoReset(POHCI pThis, uint32_t fNewMode, bool fResetOnLinux)
1180{
1181 Log(("ohci: %s reset%s\n", fNewMode == OHCI_USB_RESET ? "hardware" : "software",
1182 fResetOnLinux ? " (reset on linux)" : ""));
1183
1184 /* Stop the bus in any case, disabling walking the lists. */
1185 ohciR3BusStop(pThis);
1186
1187 /*
1188 * Cancel all outstanding URBs.
1189 *
1190 * We can't, and won't, deal with URBs until we're moved out of the
1191 * suspend/reset state. Also, a real HC isn't going to send anything
1192 * any more when a reset has been signaled.
1193 */
1194 pThis->RootHub.pIRhConn->pfnCancelAllUrbs(pThis->RootHub.pIRhConn);
1195
1196 /*
1197 * Reset the hardware registers.
1198 */
1199 if (fNewMode == OHCI_USB_RESET)
1200 pThis->ctl = OHCI_CTL_RWC; /* We're the firmware, set RemoteWakeupConnected. */
1201 else
1202 pThis->ctl &= OHCI_CTL_IR | OHCI_CTL_RWC; /* IR and RWC are preserved on software reset. */
1203
1204 /* Clear the HCFS bits first to make setting the new state work. */
1205 pThis->ctl &= ~OHCI_CTL_HCFS;
1206 pThis->ctl |= fNewMode;
1207 pThis->status = 0;
1208 pThis->intr_status = 0;
1209 pThis->intr = 0;
1210 PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, 0);
1211
1212 pThis->hcca = 0;
1213 pThis->per_cur = 0;
1214 pThis->ctrl_head = pThis->ctrl_cur = 0;
1215 pThis->bulk_head = pThis->bulk_cur = 0;
1216 pThis->done = 0;
1217
1218 pThis->fsmps = 0x2778; /* To-Be-Defined, use the value linux sets...*/
1219 pThis->fit = 0;
1220 pThis->fi = 11999; /* (12MHz ticks, one frame is 1ms) */
1221 pThis->frt = 0;
1222 pThis->HcFmNumber = 0;
1223 pThis->pstart = 0;
1224
1225 pThis->dqic = 0x7;
1226 pThis->fno = 0;
1227
1228#ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
1229 ohciR3PhysReadCacheClear(pThis->pCacheED);
1230 ohciR3PhysReadCacheClear(pThis->pCacheTD);
1231#endif
1232
1233 /*
1234 * If this is a hardware reset, we will initialize the root hub too.
1235 * Software resets doesn't do this according to the specs.
1236 * (It's not possible to have device connected at the time of the
1237 * device construction, so nothing to worry about there.)
1238 */
1239 if (fNewMode == OHCI_USB_RESET)
1240 VUSBIDevReset(pThis->RootHub.pIDev, fResetOnLinux, NULL, NULL, NULL);
1241}
1242
1243
1244/**
1245 * Reads physical memory.
1246 */
1247DECLINLINE(void) ohciR3PhysRead(POHCI pThis, uint32_t Addr, void *pvBuf, size_t cbBuf)
1248{
1249 if (cbBuf)
1250 PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), Addr, pvBuf, cbBuf);
1251}
1252
1253/**
1254 * Writes physical memory.
1255 */
1256DECLINLINE(void) ohciR3PhysWrite(POHCI pThis, uint32_t Addr, const void *pvBuf, size_t cbBuf)
1257{
1258 if (cbBuf)
1259 PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), Addr, pvBuf, cbBuf);
1260}
1261
1262/**
1263 * Read an array of dwords from physical memory and correct endianness.
1264 */
1265DECLINLINE(void) ohciR3GetDWords(POHCI pThis, uint32_t Addr, uint32_t *pau32s, int c32s)
1266{
1267 ohciR3PhysRead(pThis, Addr, pau32s, c32s * sizeof(uint32_t));
1268# ifndef RT_LITTLE_ENDIAN
1269 for(int i = 0; i < c32s; i++)
1270 pau32s[i] = RT_H2LE_U32(pau32s[i]);
1271# endif
1272}
1273
1274/**
1275 * Write an array of dwords from physical memory and correct endianness.
1276 */
1277DECLINLINE(void) ohciR3PutDWords(POHCI pThis, uint32_t Addr, const uint32_t *pau32s, int cu32s)
1278{
1279# ifdef RT_LITTLE_ENDIAN
1280 ohciR3PhysWrite(pThis, Addr, pau32s, cu32s << 2);
1281# else
1282 for (int i = 0; i < c32s; i++, pau32s++, Addr += sizeof(*pau32s))
1283 {
1284 uint32_t u32Tmp = RT_H2LE_U32(*pau32s);
1285 ohciR3PhysWrite(pThis, Addr, (uint8_t *)&u32Tmp, sizeof(u32Tmp));
1286 }
1287# endif
1288}
1289
1290
1291
1292# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
1293
1294static void descReadStatsReset(POHCIDESCREADSTATS p)
1295{
1296 p->cReads = 0;
1297 p->cPageChange = 0;
1298 p->cMinReadsPerPage = UINT32_MAX;
1299 p->cMaxReadsPerPage = 0;
1300
1301 p->cReadsLastPage = 0;
1302 p->u32LastPageAddr = 0;
1303}
1304
1305static void physReadStatsReset(POHCIPHYSREADSTATS p)
1306{
1307 descReadStatsReset(&p->ed);
1308 descReadStatsReset(&p->td);
1309 descReadStatsReset(&p->all);
1310
1311 p->cCrossReads = 0;
1312 p->cCacheReads = 0;
1313 p->cPageReads = 0;
1314}
1315
1316static void physReadStatsUpdateDesc(POHCIDESCREADSTATS p, uint32_t u32Addr)
1317{
1318 const uint32_t u32PageAddr = u32Addr & ~UINT32_C(0xFFF);
1319
1320 ++p->cReads;
1321
1322 if (p->u32LastPageAddr == 0)
1323 {
1324 /* First call. */
1325 ++p->cReadsLastPage;
1326 p->u32LastPageAddr = u32PageAddr;
1327 }
1328 else if (u32PageAddr != p->u32LastPageAddr)
1329 {
1330 /* New page. */
1331 ++p->cPageChange;
1332
1333 p->cMinReadsPerPage = RT_MIN(p->cMinReadsPerPage, p->cReadsLastPage);
1334 p->cMaxReadsPerPage = RT_MAX(p->cMaxReadsPerPage, p->cReadsLastPage);;
1335
1336 p->cReadsLastPage = 1;
1337 p->u32LastPageAddr = u32PageAddr;
1338 }
1339 else
1340 {
1341 /* Read on the same page. */
1342 ++p->cReadsLastPage;
1343 }
1344}
1345
1346static void physReadStatsPrint(POHCIPHYSREADSTATS p)
1347{
1348 p->ed.cMinReadsPerPage = RT_MIN(p->ed.cMinReadsPerPage, p->ed.cReadsLastPage);
1349 p->ed.cMaxReadsPerPage = RT_MAX(p->ed.cMaxReadsPerPage, p->ed.cReadsLastPage);;
1350
1351 p->td.cMinReadsPerPage = RT_MIN(p->td.cMinReadsPerPage, p->td.cReadsLastPage);
1352 p->td.cMaxReadsPerPage = RT_MAX(p->td.cMaxReadsPerPage, p->td.cReadsLastPage);;
1353
1354 p->all.cMinReadsPerPage = RT_MIN(p->all.cMinReadsPerPage, p->all.cReadsLastPage);
1355 p->all.cMaxReadsPerPage = RT_MAX(p->all.cMaxReadsPerPage, p->all.cReadsLastPage);;
1356
1357 LogRel(("PHYSREAD:\n"
1358 " ED: %d, %d, %d/%d\n"
1359 " TD: %d, %d, %d/%d\n"
1360 " ALL: %d, %d, %d/%d\n"
1361 " C: %d, %d, %d\n"
1362 "",
1363 p->ed.cReads, p->ed.cPageChange, p->ed.cMinReadsPerPage, p->ed.cMaxReadsPerPage,
1364 p->td.cReads, p->td.cPageChange, p->td.cMinReadsPerPage, p->td.cMaxReadsPerPage,
1365 p->all.cReads, p->all.cPageChange, p->all.cMinReadsPerPage, p->all.cMaxReadsPerPage,
1366 p->cCrossReads, p->cCacheReads, p->cPageReads
1367 ));
1368
1369 physReadStatsReset(p);
1370}
1371
1372# endif /* VBOX_WITH_OHCI_PHYS_READ_STATS */
1373# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
1374
1375static POHCIPAGECACHE ohciR3PhysReadCacheAlloc(void)
1376{
1377 return (POHCIPAGECACHE)RTMemAlloc(sizeof(OHCIPAGECACHE));
1378}
1379
1380static void ohciR3PhysReadCacheFree(POHCIPAGECACHE pPageCache)
1381{
1382 RTMemFree(pPageCache);
1383}
1384
1385static void ohciR3PhysReadCacheClear(POHCIPAGECACHE pPageCache)
1386{
1387 pPageCache->GCPhysReadCacheAddr = NIL_RTGCPHYS;
1388}
1389
1390static void ohciR3PhysReadCacheRead(POHCI pThis, POHCIPAGECACHE pPageCache, RTGCPHYS GCPhys, void *pvBuf, size_t cbBuf)
1391{
1392 const RTGCPHYS PageAddr = PAGE_ADDRESS(GCPhys);
1393
1394 if (PageAddr == PAGE_ADDRESS(GCPhys + cbBuf))
1395 {
1396 if (PageAddr != pPageCache->GCPhysReadCacheAddr)
1397 {
1398 PDMDevHlpPhysRead(pThis->pDevInsR3, PageAddr,
1399 pPageCache->au8PhysReadCache, sizeof(pPageCache->au8PhysReadCache));
1400 pPageCache->GCPhysReadCacheAddr = PageAddr;
1401# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
1402 ++g_PhysReadState.cPageReads;
1403# endif
1404 }
1405
1406 memcpy(pvBuf, &pPageCache->au8PhysReadCache[GCPhys & PAGE_OFFSET_MASK], cbBuf);
1407# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
1408 ++g_PhysReadState.cCacheReads;
1409# endif
1410 }
1411 else
1412 {
1413 PDMDevHlpPhysRead(pThis->pDevInsR3, GCPhys, pvBuf, cbBuf);
1414# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
1415 ++g_PhysReadState.cCrossReads;
1416# endif
1417 }
1418}
1419
1420
1421/**
1422 * Updates the data in the given page cache if the given guest physical address is currently contained
1423 * in the cache.
1424 *
1425 * @returns nothing.
1426 * @param pPageCache The page cache to update.
1427 * @param GCPhys The guest physical address needing the update.
1428 * @param pvBuf Pointer to the buffer to update the page cache with.
1429 * @param cbBuf Number of bytes to update.
1430 */
1431static void ohciR3PhysCacheUpdate(POHCIPAGECACHE pPageCache, RTGCPHYS GCPhys, const void *pvBuf, size_t cbBuf)
1432{
1433 const RTGCPHYS GCPhysPage = PAGE_ADDRESS(GCPhys);
1434
1435 if (GCPhysPage == pPageCache->GCPhysReadCacheAddr)
1436 {
1437 uint32_t offPage = GCPhys & PAGE_OFFSET_MASK;
1438 memcpy(&pPageCache->au8PhysReadCache[offPage], pvBuf, RT_MIN(PAGE_SIZE - offPage, cbBuf));
1439 }
1440}
1441
1442/**
1443 * Update any cached ED data with the given endpoint descriptor at the given address.
1444 *
1445 * @returns nothing.
1446 * @param pThis The OHCI instance data.
1447 * @param EdAddr Endpoint descriptor address.
1448 * @param pEd The endpoint descriptor which got updated.
1449 */
1450DECLINLINE(void) ohciR3CacheEdUpdate(POHCI pThis, RTGCPHYS32 EdAddr, PCOHCIED pEd)
1451{
1452 ohciR3PhysCacheUpdate(pThis->pCacheED, EdAddr + RT_OFFSETOF(OHCIED, HeadP), &pEd->HeadP, sizeof(uint32_t));
1453}
1454
1455
1456/**
1457 * Update any cached TD data with the given transfer descriptor at the given address.
1458 *
1459 * @returns nothing.
1460 * @param pThis The OHCI instance data.
1461 * @param TdAddr Transfer descriptor address.
1462 * @param pTd The transfer descriptor which got updated.
1463 */
1464DECLINLINE(void) ohciR3CacheTdUpdate(POHCI pThis, RTGCPHYS32 TdAddr, PCOHCITD pTd)
1465{
1466 ohciR3PhysCacheUpdate(pThis->pCacheTD, TdAddr, pTd, sizeof(*pTd));
1467}
1468
1469# endif /* VBOX_WITH_OHCI_PHYS_READ_CACHE */
1470
1471/**
1472 * Reads an OHCIED.
1473 */
1474DECLINLINE(void) ohciR3ReadEd(POHCI pThis, uint32_t EdAddr, POHCIED pEd)
1475{
1476# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
1477 physReadStatsUpdateDesc(&g_PhysReadState.ed, EdAddr);
1478 physReadStatsUpdateDesc(&g_PhysReadState.all, EdAddr);
1479# endif
1480#ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
1481 ohciR3PhysReadCacheRead(pThis, pThis->pCacheED, EdAddr, pEd, sizeof(*pEd));
1482#else
1483 ohciR3GetDWords(pThis, EdAddr, (uint32_t *)pEd, sizeof(*pEd) >> 2);
1484#endif
1485}
1486
1487/**
1488 * Reads an OHCITD.
1489 */
1490DECLINLINE(void) ohciR3ReadTd(POHCI pThis, uint32_t TdAddr, POHCITD pTd)
1491{
1492# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
1493 physReadStatsUpdateDesc(&g_PhysReadState.td, TdAddr);
1494 physReadStatsUpdateDesc(&g_PhysReadState.all, TdAddr);
1495# endif
1496#ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
1497 ohciR3PhysReadCacheRead(pThis, pThis->pCacheTD, TdAddr, pTd, sizeof(*pTd));
1498#else
1499 ohciR3GetDWords(pThis, TdAddr, (uint32_t *)pTd, sizeof(*pTd) >> 2);
1500#endif
1501# ifdef LOG_ENABLED
1502 if (LogIs3Enabled())
1503 {
1504 uint32_t hichg;
1505 hichg = pTd->hwinfo;
1506 Log3(("ohciR3ReadTd(,%#010x,): R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x UNK=%#x\n",
1507 TdAddr,
1508 (pTd->hwinfo >> 18) & 1,
1509 (pTd->hwinfo >> 19) & 3,
1510 (pTd->hwinfo >> 21) & 7,
1511 (pTd->hwinfo >> 24) & 3,
1512 (pTd->hwinfo >> 26) & 3,
1513 (pTd->hwinfo >> 28) &15,
1514 pTd->cbp,
1515 pTd->NextTD,
1516 pTd->be,
1517 pTd->hwinfo & TD_HWINFO_UNKNOWN_MASK));
1518# if 0
1519 if (LogIs3Enabled())
1520 {
1521 /*
1522 * usbohci.sys (32-bit XP) allocates 0x80 bytes per TD:
1523 * 0x00-0x0f is the OHCI TD.
1524 * 0x10-0x1f for isochronous TDs
1525 * 0x20 is the physical address of this TD.
1526 * 0x24 is initialized with 0x64745948, probably a magic.
1527 * 0x28 is some kind of flags. the first bit begin the allocated / not allocated indicator.
1528 * 0x30 is a pointer to something. endpoint? interface? device?
1529 * 0x38 is initialized to 0xdeadface. but is changed into a pointer or something.
1530 * 0x40 looks like a pointer.
1531 * The rest is unknown and initialized with zeros.
1532 */
1533 uint8_t abXpTd[0x80];
1534 ohciR3PhysRead(pThis, TdAddr, abXpTd, sizeof(abXpTd));
1535 Log3(("WinXpTd: alloc=%d PhysSelf=%RX32 s2=%RX32 magic=%RX32 s4=%RX32 s5=%RX32\n"
1536 "%.*Rhxd\n",
1537 abXpTd[28] & RT_BIT(0),
1538 *((uint32_t *)&abXpTd[0x20]), *((uint32_t *)&abXpTd[0x30]),
1539 *((uint32_t *)&abXpTd[0x24]), *((uint32_t *)&abXpTd[0x38]),
1540 *((uint32_t *)&abXpTd[0x40]),
1541 sizeof(abXpTd), &abXpTd[0]));
1542 }
1543# endif
1544 }
1545# endif
1546}
1547
1548/**
1549 * Reads an OHCIITD.
1550 */
1551DECLINLINE(void) ohciR3ReadITd(POHCI pThis, uint32_t ITdAddr, POHCIITD pITd)
1552{
1553 ohciR3GetDWords(pThis, ITdAddr, (uint32_t *)pITd, sizeof(*pITd) / sizeof(uint32_t));
1554# ifdef LOG_ENABLED
1555 if (LogIs3Enabled())
1556 {
1557 Log3(("ohciR3ReadITd(,%#010x,): SF=%#06x (%#RX32) DI=%#x FC=%d CC=%#x BP0=%#010x NextTD=%#010x BE=%#010x\n",
1558 ITdAddr,
1559 pITd->HwInfo & 0xffff, pThis->HcFmNumber,
1560 (pITd->HwInfo >> 21) & 7,
1561 (pITd->HwInfo >> 24) & 7,
1562 (pITd->HwInfo >> 28) &15,
1563 pITd->BP0,
1564 pITd->NextTD,
1565 pITd->BE));
1566 Log3(("psw0=%x:%03x psw1=%x:%03x psw2=%x:%03x psw3=%x:%03x psw4=%x:%03x psw5=%x:%03x psw6=%x:%03x psw7=%x:%03x\n",
1567 pITd->aPSW[0] >> 12, pITd->aPSW[0] & 0xfff,
1568 pITd->aPSW[1] >> 12, pITd->aPSW[1] & 0xfff,
1569 pITd->aPSW[2] >> 12, pITd->aPSW[2] & 0xfff,
1570 pITd->aPSW[3] >> 12, pITd->aPSW[3] & 0xfff,
1571 pITd->aPSW[4] >> 12, pITd->aPSW[4] & 0xfff,
1572 pITd->aPSW[5] >> 12, pITd->aPSW[5] & 0xfff,
1573 pITd->aPSW[6] >> 12, pITd->aPSW[6] & 0xfff,
1574 pITd->aPSW[7] >> 12, pITd->aPSW[7] & 0xfff));
1575 }
1576# endif
1577}
1578
1579
1580/**
1581 * Writes an OHCIED.
1582 */
1583DECLINLINE(void) ohciR3WriteEd(POHCI pThis, uint32_t EdAddr, PCOHCIED pEd)
1584{
1585# ifdef LOG_ENABLED
1586 if (LogIs3Enabled())
1587 {
1588 OHCIED EdOld;
1589 uint32_t hichg;
1590
1591 ohciR3GetDWords(pThis, EdAddr, (uint32_t *)&EdOld, sizeof(EdOld) >> 2);
1592 hichg = EdOld.hwinfo ^ pEd->hwinfo;
1593 Log3(("ohciR3WriteEd(,%#010x,): %sFA=%#x %sEN=%#x %sD=%#x %sS=%d %sK=%d %sF=%d %sMPS=%#x %sTailP=%#010x %sHeadP=%#010x %sH=%d %sC=%d %sNextED=%#010x\n",
1594 EdAddr,
1595 (hichg >> 0) & 0x7f ? "*" : "", (pEd->hwinfo >> 0) & 0x7f,
1596 (hichg >> 7) & 0xf ? "*" : "", (pEd->hwinfo >> 7) & 0xf,
1597 (hichg >> 11) & 3 ? "*" : "", (pEd->hwinfo >> 11) & 3,
1598 (hichg >> 13) & 1 ? "*" : "", (pEd->hwinfo >> 13) & 1,
1599 (hichg >> 14) & 1 ? "*" : "", (pEd->hwinfo >> 14) & 1,
1600 (hichg >> 15) & 1 ? "*" : "", (pEd->hwinfo >> 15) & 1,
1601 (hichg >> 24) &0x3ff ? "*" : "", (pEd->hwinfo >> 16) &0x3ff,
1602 EdOld.TailP != pEd->TailP ? "*" : "", pEd->TailP,
1603 (EdOld.HeadP & ~3) != (pEd->HeadP & ~3) ? "*" : "", pEd->HeadP & ~3,
1604 (EdOld.HeadP ^ pEd->HeadP) & 1 ? "*" : "", pEd->HeadP & 1,
1605 (EdOld.HeadP ^ pEd->HeadP) & 2 ? "*" : "", (pEd->HeadP >> 1) & 1,
1606 EdOld.NextED != pEd->NextED ? "*" : "", pEd->NextED));
1607 }
1608# endif
1609
1610 ohciR3PutDWords(pThis, EdAddr + RT_OFFSETOF(OHCIED, HeadP), &pEd->HeadP, 1);
1611#ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
1612 ohciR3CacheEdUpdate(pThis, EdAddr, pEd);
1613#endif
1614}
1615
1616
1617/**
1618 * Writes an OHCITD.
1619 */
1620DECLINLINE(void) ohciR3WriteTd(POHCI pThis, uint32_t TdAddr, PCOHCITD pTd, const char *pszLogMsg)
1621{
1622# ifdef LOG_ENABLED
1623 if (LogIs3Enabled())
1624 {
1625 OHCITD TdOld;
1626 ohciR3GetDWords(pThis, TdAddr, (uint32_t *)&TdOld, sizeof(TdOld) >> 2);
1627 uint32_t hichg = TdOld.hwinfo ^ pTd->hwinfo;
1628 Log3(("ohciR3WriteTd(,%#010x,): %sR=%d %sDP=%d %sDI=%#x %sT=%d %sEC=%d %sCC=%#x %sCBP=%#010x %sNextTD=%#010x %sBE=%#010x (%s)\n",
1629 TdAddr,
1630 (hichg >> 18) & 1 ? "*" : "", (pTd->hwinfo >> 18) & 1,
1631 (hichg >> 19) & 3 ? "*" : "", (pTd->hwinfo >> 19) & 3,
1632 (hichg >> 21) & 7 ? "*" : "", (pTd->hwinfo >> 21) & 7,
1633 (hichg >> 24) & 3 ? "*" : "", (pTd->hwinfo >> 24) & 3,
1634 (hichg >> 26) & 3 ? "*" : "", (pTd->hwinfo >> 26) & 3,
1635 (hichg >> 28) &15 ? "*" : "", (pTd->hwinfo >> 28) &15,
1636 TdOld.cbp != pTd->cbp ? "*" : "", pTd->cbp,
1637 TdOld.NextTD != pTd->NextTD ? "*" : "", pTd->NextTD,
1638 TdOld.be != pTd->be ? "*" : "", pTd->be,
1639 pszLogMsg));
1640 }
1641# else
1642 RT_NOREF(pszLogMsg);
1643# endif
1644 ohciR3PutDWords(pThis, TdAddr, (uint32_t *)pTd, sizeof(*pTd) >> 2);
1645#ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
1646 ohciR3CacheTdUpdate(pThis, TdAddr, pTd);
1647#endif
1648}
1649
1650/**
1651 * Writes an OHCIITD.
1652 */
1653DECLINLINE(void) ohciR3WriteITd(POHCI pThis, uint32_t ITdAddr, PCOHCIITD pITd, const char *pszLogMsg)
1654{
1655# ifdef LOG_ENABLED
1656 if (LogIs3Enabled())
1657 {
1658 OHCIITD ITdOld;
1659 ohciR3GetDWords(pThis, ITdAddr, (uint32_t *)&ITdOld, sizeof(ITdOld) / sizeof(uint32_t));
1660 uint32_t HIChg = ITdOld.HwInfo ^ pITd->HwInfo;
1661 Log3(("ohciR3WriteITd(,%#010x,): %sSF=%#x (now=%#RX32) %sDI=%#x %sFC=%d %sCC=%#x %sBP0=%#010x %sNextTD=%#010x %sBE=%#010x (%s)\n",
1662 ITdAddr,
1663 (HIChg & 0xffff) & 1 ? "*" : "", pITd->HwInfo & 0xffff, pThis->HcFmNumber,
1664 (HIChg >> 21) & 7 ? "*" : "", (pITd->HwInfo >> 21) & 7,
1665 (HIChg >> 24) & 7 ? "*" : "", (pITd->HwInfo >> 24) & 7,
1666 (HIChg >> 28) &15 ? "*" : "", (pITd->HwInfo >> 28) &15,
1667 ITdOld.BP0 != pITd->BP0 ? "*" : "", pITd->BP0,
1668 ITdOld.NextTD != pITd->NextTD ? "*" : "", pITd->NextTD,
1669 ITdOld.BE != pITd->BE ? "*" : "", pITd->BE,
1670 pszLogMsg));
1671 Log3(("psw0=%s%x:%s%03x psw1=%s%x:%s%03x psw2=%s%x:%s%03x psw3=%s%x:%s%03x psw4=%s%x:%s%03x psw5=%s%x:%s%03x psw6=%s%x:%s%03x psw7=%s%x:%s%03x\n",
1672 (ITdOld.aPSW[0] >> 12) != (pITd->aPSW[0] >> 12) ? "*" : "", pITd->aPSW[0] >> 12, (ITdOld.aPSW[0] & 0xfff) != (pITd->aPSW[0] & 0xfff) ? "*" : "", pITd->aPSW[0] & 0xfff,
1673 (ITdOld.aPSW[1] >> 12) != (pITd->aPSW[1] >> 12) ? "*" : "", pITd->aPSW[1] >> 12, (ITdOld.aPSW[1] & 0xfff) != (pITd->aPSW[1] & 0xfff) ? "*" : "", pITd->aPSW[1] & 0xfff,
1674 (ITdOld.aPSW[2] >> 12) != (pITd->aPSW[2] >> 12) ? "*" : "", pITd->aPSW[2] >> 12, (ITdOld.aPSW[2] & 0xfff) != (pITd->aPSW[2] & 0xfff) ? "*" : "", pITd->aPSW[2] & 0xfff,
1675 (ITdOld.aPSW[3] >> 12) != (pITd->aPSW[3] >> 12) ? "*" : "", pITd->aPSW[3] >> 12, (ITdOld.aPSW[3] & 0xfff) != (pITd->aPSW[3] & 0xfff) ? "*" : "", pITd->aPSW[3] & 0xfff,
1676 (ITdOld.aPSW[4] >> 12) != (pITd->aPSW[4] >> 12) ? "*" : "", pITd->aPSW[4] >> 12, (ITdOld.aPSW[4] & 0xfff) != (pITd->aPSW[4] & 0xfff) ? "*" : "", pITd->aPSW[4] & 0xfff,
1677 (ITdOld.aPSW[5] >> 12) != (pITd->aPSW[5] >> 12) ? "*" : "", pITd->aPSW[5] >> 12, (ITdOld.aPSW[5] & 0xfff) != (pITd->aPSW[5] & 0xfff) ? "*" : "", pITd->aPSW[5] & 0xfff,
1678 (ITdOld.aPSW[6] >> 12) != (pITd->aPSW[6] >> 12) ? "*" : "", pITd->aPSW[6] >> 12, (ITdOld.aPSW[6] & 0xfff) != (pITd->aPSW[6] & 0xfff) ? "*" : "", pITd->aPSW[6] & 0xfff,
1679 (ITdOld.aPSW[7] >> 12) != (pITd->aPSW[7] >> 12) ? "*" : "", pITd->aPSW[7] >> 12, (ITdOld.aPSW[7] & 0xfff) != (pITd->aPSW[7] & 0xfff) ? "*" : "", pITd->aPSW[7] & 0xfff));
1680 }
1681# else
1682 RT_NOREF(pszLogMsg);
1683# endif
1684 ohciR3PutDWords(pThis, ITdAddr, (uint32_t *)pITd, sizeof(*pITd) / sizeof(uint32_t));
1685}
1686
1687
1688# ifdef LOG_ENABLED
1689
1690/**
1691 * Core TD queue dumper. LOG_ENABLED builds only.
1692 */
1693DECLINLINE(void) ohciR3DumpTdQueueCore(POHCI pThis, uint32_t GCPhysHead, uint32_t GCPhysTail, bool fFull)
1694{
1695 uint32_t GCPhys = GCPhysHead;
1696 int cMax = 100;
1697 for (;;)
1698 {
1699 OHCITD Td;
1700 Log4(("%#010x%s%s", GCPhys,
1701 GCPhys && ohciR3InFlightFind(pThis, GCPhys) >= 0 ? "~" : "",
1702 GCPhys && ohciR3InDoneQueueFind(pThis, GCPhys) >= 0 ? "^" : ""));
1703 if (GCPhys == 0 || GCPhys == GCPhysTail)
1704 break;
1705
1706 /* can't use ohciR3ReadTd() because of Log4. */
1707 ohciR3GetDWords(pThis, GCPhys, (uint32_t *)&Td, sizeof(Td) >> 2);
1708 if (fFull)
1709 Log4((" [R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x] -> ",
1710 (Td.hwinfo >> 18) & 1,
1711 (Td.hwinfo >> 19) & 3,
1712 (Td.hwinfo >> 21) & 7,
1713 (Td.hwinfo >> 24) & 3,
1714 (Td.hwinfo >> 26) & 3,
1715 (Td.hwinfo >> 28) &15,
1716 Td.cbp,
1717 Td.NextTD,
1718 Td.be));
1719 else
1720 Log4((" -> "));
1721 GCPhys = Td.NextTD & ED_PTR_MASK;
1722 Assert(GCPhys != GCPhysHead);
1723 Assert(cMax-- > 0); NOREF(cMax);
1724 }
1725}
1726
1727/**
1728 * Dumps a TD queue. LOG_ENABLED builds only.
1729 */
1730DECLINLINE(void) ohciR3DumpTdQueue(POHCI pThis, uint32_t GCPhysHead, const char *pszMsg)
1731{
1732 if (pszMsg)
1733 Log4(("%s: ", pszMsg));
1734 ohciR3DumpTdQueueCore(pThis, GCPhysHead, 0, true);
1735 Log4(("\n"));
1736}
1737
1738/**
1739 * Core ITD queue dumper. LOG_ENABLED builds only.
1740 */
1741DECLINLINE(void) ohciR3DumpITdQueueCore(POHCI pThis, uint32_t GCPhysHead, uint32_t GCPhysTail, bool fFull)
1742{
1743 RT_NOREF(fFull);
1744 uint32_t GCPhys = GCPhysHead;
1745 int cMax = 100;
1746 for (;;)
1747 {
1748 OHCIITD ITd;
1749 Log4(("%#010x%s%s", GCPhys,
1750 GCPhys && ohciR3InFlightFind(pThis, GCPhys) >= 0 ? "~" : "",
1751 GCPhys && ohciR3InDoneQueueFind(pThis, GCPhys) >= 0 ? "^" : ""));
1752 if (GCPhys == 0 || GCPhys == GCPhysTail)
1753 break;
1754
1755 /* can't use ohciR3ReadTd() because of Log4. */
1756 ohciR3GetDWords(pThis, GCPhys, (uint32_t *)&ITd, sizeof(ITd) / sizeof(uint32_t));
1757 /*if (fFull)
1758 Log4((" [R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x] -> ",
1759 (Td.hwinfo >> 18) & 1,
1760 (Td.hwinfo >> 19) & 3,
1761 (Td.hwinfo >> 21) & 7,
1762 (Td.hwinfo >> 24) & 3,
1763 (Td.hwinfo >> 26) & 3,
1764 (Td.hwinfo >> 28) &15,
1765 Td.cbp,
1766 Td.NextTD,
1767 Td.be));
1768 else*/
1769 Log4((" -> "));
1770 GCPhys = ITd.NextTD & ED_PTR_MASK;
1771 Assert(GCPhys != GCPhysHead);
1772 Assert(cMax-- > 0); NOREF(cMax);
1773 }
1774}
1775
1776/**
1777 * Dumps a ED list. LOG_ENABLED builds only.
1778 */
1779DECLINLINE(void) ohciR3DumpEdList(POHCI pThis, uint32_t GCPhysHead, const char *pszMsg, bool fTDs)
1780{
1781 RT_NOREF(fTDs);
1782 uint32_t GCPhys = GCPhysHead;
1783 if (pszMsg)
1784 Log4(("%s:", pszMsg));
1785 for (;;)
1786 {
1787 OHCIED Ed;
1788
1789 /* ED */
1790 Log4((" %#010x={", GCPhys));
1791 if (!GCPhys)
1792 {
1793 Log4(("END}\n"));
1794 return;
1795 }
1796
1797 /* TDs */
1798 ohciR3ReadEd(pThis, GCPhys, &Ed);
1799 if (Ed.hwinfo & ED_HWINFO_ISO)
1800 Log4(("[I]"));
1801 if ((Ed.HeadP & ED_HEAD_HALTED) || (Ed.hwinfo & ED_HWINFO_SKIP))
1802 {
1803 if ((Ed.HeadP & ED_HEAD_HALTED) && (Ed.hwinfo & ED_HWINFO_SKIP))
1804 Log4(("SH}"));
1805 else if (Ed.hwinfo & ED_HWINFO_SKIP)
1806 Log4(("S-}"));
1807 else
1808 Log4(("-H}"));
1809 }
1810 else
1811 {
1812 if (Ed.hwinfo & ED_HWINFO_ISO)
1813 ohciR3DumpITdQueueCore(pThis, Ed.HeadP & ED_PTR_MASK, Ed.TailP & ED_PTR_MASK, false);
1814 else
1815 ohciR3DumpTdQueueCore(pThis, Ed.HeadP & ED_PTR_MASK, Ed.TailP & ED_PTR_MASK, false);
1816 Log4(("}"));
1817 }
1818
1819 /* next */
1820 GCPhys = Ed.NextED & ED_PTR_MASK;
1821 Assert(GCPhys != GCPhysHead);
1822 }
1823 /* not reached */
1824}
1825
1826# endif /* LOG_ENABLED */
1827
1828
1829DECLINLINE(int) ohciR3InFlightFindFree(POHCI pThis, const int iStart)
1830{
1831 unsigned i = iStart;
1832 while (i < RT_ELEMENTS(pThis->aInFlight))
1833 {
1834 if (pThis->aInFlight[i].GCPhysTD == 0)
1835 return i;
1836 i++;
1837 }
1838 i = iStart;
1839 while (i-- > 0)
1840 {
1841 if (pThis->aInFlight[i].GCPhysTD == 0)
1842 return i;
1843 }
1844 return -1;
1845}
1846
1847
1848/**
1849 * Record an in-flight TD.
1850 *
1851 * @param pThis OHCI instance data.
1852 * @param GCPhysTD Physical address of the TD.
1853 * @param pUrb The URB.
1854 */
1855static void ohciR3InFlightAdd(POHCI pThis, uint32_t GCPhysTD, PVUSBURB pUrb)
1856{
1857 int i = ohciR3InFlightFindFree(pThis, (GCPhysTD >> 4) % RT_ELEMENTS(pThis->aInFlight));
1858 if (i >= 0)
1859 {
1860# ifdef LOG_ENABLED
1861 pUrb->pHci->u32FrameNo = pThis->HcFmNumber;
1862# endif
1863 pThis->aInFlight[i].GCPhysTD = GCPhysTD;
1864 pThis->aInFlight[i].pUrb = pUrb;
1865 pThis->cInFlight++;
1866 return;
1867 }
1868 AssertMsgFailed(("Out of space cInFlight=%d!\n", pThis->cInFlight));
1869}
1870
1871
1872/**
1873 * Record in-flight TDs for an URB.
1874 *
1875 * @param pThis OHCI instance data.
1876 * @param pUrb The URB.
1877 */
1878static void ohciR3InFlightAddUrb(POHCI pThis, PVUSBURB pUrb)
1879{
1880 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
1881 ohciR3InFlightAdd(pThis, pUrb->paTds[iTd].TdAddr, pUrb);
1882}
1883
1884
1885/**
1886 * Finds a in-flight TD.
1887 *
1888 * @returns Index of the record.
1889 * @returns -1 if not found.
1890 * @param pThis OHCI instance data.
1891 * @param GCPhysTD Physical address of the TD.
1892 * @remark This has to be fast.
1893 */
1894static int ohciR3InFlightFind(POHCI pThis, uint32_t GCPhysTD)
1895{
1896 unsigned cLeft = pThis->cInFlight;
1897 unsigned i = (GCPhysTD >> 4) % RT_ELEMENTS(pThis->aInFlight);
1898 const int iLast = i;
1899 while (i < RT_ELEMENTS(pThis->aInFlight))
1900 {
1901 if (pThis->aInFlight[i].GCPhysTD == GCPhysTD)
1902 return i;
1903 if (pThis->aInFlight[i].GCPhysTD)
1904 if (cLeft-- <= 1)
1905 return -1;
1906 i++;
1907 }
1908 i = iLast;
1909 while (i-- > 0)
1910 {
1911 if (pThis->aInFlight[i].GCPhysTD == GCPhysTD)
1912 return i;
1913 if (pThis->aInFlight[i].GCPhysTD)
1914 if (cLeft-- <= 1)
1915 return -1;
1916 }
1917 return -1;
1918}
1919
1920
1921/**
1922 * Checks if a TD is in-flight.
1923 *
1924 * @returns true if in flight, false if not.
1925 * @param pThis OHCI instance data.
1926 * @param GCPhysTD Physical address of the TD.
1927 */
1928static bool ohciR3IsTdInFlight(POHCI pThis, uint32_t GCPhysTD)
1929{
1930 return ohciR3InFlightFind(pThis, GCPhysTD) >= 0;
1931}
1932
1933/**
1934 * Returns a URB associated with an in-flight TD, if any.
1935 *
1936 * @returns pointer to URB if TD is in flight.
1937 * @returns NULL if not in flight.
1938 * @param pThis OHCI instance data.
1939 * @param GCPhysTD Physical address of the TD.
1940 */
1941static PVUSBURB ohciR3TdInFlightUrb(POHCI pThis, uint32_t GCPhysTD)
1942{
1943 int i;
1944
1945 i = ohciR3InFlightFind(pThis, GCPhysTD);
1946 if ( i >= 0 )
1947 return pThis->aInFlight[i].pUrb;
1948 return NULL;
1949}
1950
1951/**
1952 * Removes a in-flight TD.
1953 *
1954 * @returns 0 if found. For logged builds this is the number of frames the TD has been in-flight.
1955 * @returns -1 if not found.
1956 * @param pThis OHCI instance data.
1957 * @param GCPhysTD Physical address of the TD.
1958 */
1959static int ohciR3InFlightRemove(POHCI pThis, uint32_t GCPhysTD)
1960{
1961 int i = ohciR3InFlightFind(pThis, GCPhysTD);
1962 if (i >= 0)
1963 {
1964# ifdef LOG_ENABLED
1965 const int cFramesInFlight = pThis->HcFmNumber - pThis->aInFlight[i].pUrb->pHci->u32FrameNo;
1966# else
1967 const int cFramesInFlight = 0;
1968# endif
1969 Log2(("ohciR3InFlightRemove: reaping TD=%#010x %d frames (%#010x-%#010x)\n",
1970 GCPhysTD, cFramesInFlight, pThis->aInFlight[i].pUrb->pHci->u32FrameNo, pThis->HcFmNumber));
1971 pThis->aInFlight[i].GCPhysTD = 0;
1972 pThis->aInFlight[i].pUrb = NULL;
1973 pThis->cInFlight--;
1974 return cFramesInFlight;
1975 }
1976 AssertMsgFailed(("TD %#010x is not in flight\n", GCPhysTD));
1977 return -1;
1978}
1979
1980
1981/**
1982 * Removes all TDs associated with a URB from the in-flight tracking.
1983 *
1984 * @returns 0 if found. For logged builds this is the number of frames the TD has been in-flight.
1985 * @returns -1 if not found.
1986 * @param pThis OHCI instance data.
1987 * @param pUrb The URB.
1988 */
1989static int ohciR3InFlightRemoveUrb(POHCI pThis, PVUSBURB pUrb)
1990{
1991 int cFramesInFlight = ohciR3InFlightRemove(pThis, pUrb->paTds[0].TdAddr);
1992 if (pUrb->pHci->cTds > 1)
1993 {
1994 for (unsigned iTd = 1; iTd < pUrb->pHci->cTds; iTd++)
1995 if (ohciR3InFlightRemove(pThis, pUrb->paTds[iTd].TdAddr) < 0)
1996 cFramesInFlight = -1;
1997 }
1998 return cFramesInFlight;
1999}
2000
2001
2002# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
2003
2004/**
2005 * Empties the in-done-queue.
2006 * @param pThis OHCI instance data.
2007 */
2008static void ohciR3InDoneQueueZap(POHCI pThis)
2009{
2010 pThis->cInDoneQueue = 0;
2011}
2012
2013/**
2014 * Finds a TD in the in-done-queue.
2015 * @returns >= 0 on success.
2016 * @returns -1 if not found.
2017 * @param pThis OHCI instance data.
2018 * @param GCPhysTD Physical address of the TD.
2019 */
2020static int ohciR3InDoneQueueFind(POHCI pThis, uint32_t GCPhysTD)
2021{
2022 unsigned i = pThis->cInDoneQueue;
2023 while (i-- > 0)
2024 if (pThis->aInDoneQueue[i].GCPhysTD == GCPhysTD)
2025 return i;
2026 return -1;
2027}
2028
2029/**
2030 * Checks that the specified TD is not in the done queue.
2031 * @param pThis OHCI instance data.
2032 * @param GCPhysTD Physical address of the TD.
2033 */
2034static bool ohciR3InDoneQueueCheck(POHCI pThis, uint32_t GCPhysTD)
2035{
2036 int i = ohciR3InDoneQueueFind(pThis, GCPhysTD);
2037# if 0
2038 /* This condition has been observed with the USB tablet emulation or with
2039 * a real USB mouse and an SMP XP guest. I am also not sure if this is
2040 * really a problem for us. The assertion checks that the guest doesn't
2041 * re-submit a TD which is still in the done queue. It seems to me that
2042 * this should only be a problem if we either keep track of TDs in the done
2043 * queue somewhere else as well (in which case we should also free those
2044 * references in time, and I can't see any code doing that) or if we
2045 * manipulate TDs in the done queue in some way that might fail if they are
2046 * re-submitted (can't see anything like that either).
2047 */
2048 AssertMsg(i < 0, ("TD %#010x (i=%d)\n", GCPhysTD, i));
2049# endif
2050 return i < 0;
2051}
2052
2053
2054# if defined(VBOX_STRICT) && defined(LOG_ENABLED)
2055/**
2056 * Adds a TD to the in-done-queue tracking, checking that it's not there already.
2057 * @param pThis OHCI instance data.
2058 * @param GCPhysTD Physical address of the TD.
2059 */
2060static void ohciR3InDoneQueueAdd(POHCI pThis, uint32_t GCPhysTD)
2061{
2062 Assert(pThis->cInDoneQueue + 1 <= RT_ELEMENTS(pThis->aInDoneQueue));
2063 if (ohciR3InDoneQueueCheck(pThis, GCPhysTD))
2064 pThis->aInDoneQueue[pThis->cInDoneQueue++].GCPhysTD = GCPhysTD;
2065}
2066# endif /* VBOX_STRICT */
2067# endif /* defined(VBOX_STRICT) || defined(LOG_ENABLED) */
2068
2069
2070/**
2071 * OHCI Transport Buffer - represents a OHCI Transport Descriptor (TD).
2072 * A TD may be split over max 2 pages.
2073 */
2074typedef struct OHCIBUF
2075{
2076 /** Pages involved. */
2077 struct OHCIBUFVEC
2078 {
2079 /** The 32-bit physical address of this part. */
2080 uint32_t Addr;
2081 /** The length. */
2082 uint32_t cb;
2083 } aVecs[2];
2084 /** Number of valid entries in aVecs. */
2085 uint32_t cVecs;
2086 /** The total length. */
2087 uint32_t cbTotal;
2088} OHCIBUF, *POHCIBUF;
2089
2090
2091/**
2092 * Sets up a OHCI transport buffer.
2093 *
2094 * @param pBuf OHCI buffer.
2095 * @param cbp Current buffer pointer. 32-bit physical address.
2096 * @param be Last byte in buffer (BufferEnd). 32-bit physical address.
2097 */
2098static void ohciR3BufInit(POHCIBUF pBuf, uint32_t cbp, uint32_t be)
2099{
2100 if (!cbp || !be)
2101 {
2102 pBuf->cVecs = 0;
2103 pBuf->cbTotal = 0;
2104 Log2(("ohci: cbp=%#010x be=%#010x cbTotal=0 EMPTY\n", cbp, be));
2105 }
2106 else if ((cbp & ~0xfff) == (be & ~0xfff) && (cbp <= be))
2107 {
2108 pBuf->aVecs[0].Addr = cbp;
2109 pBuf->aVecs[0].cb = (be - cbp) + 1;
2110 pBuf->cVecs = 1;
2111 pBuf->cbTotal = pBuf->aVecs[0].cb;
2112 Log2(("ohci: cbp=%#010x be=%#010x cbTotal=%u\n", cbp, be, pBuf->cbTotal));
2113 }
2114 else
2115 {
2116 pBuf->aVecs[0].Addr = cbp;
2117 pBuf->aVecs[0].cb = 0x1000 - (cbp & 0xfff);
2118 pBuf->aVecs[1].Addr = be & ~0xfff;
2119 pBuf->aVecs[1].cb = (be & 0xfff) + 1;
2120 pBuf->cVecs = 2;
2121 pBuf->cbTotal = pBuf->aVecs[0].cb + pBuf->aVecs[1].cb;
2122 Log2(("ohci: cbp=%#010x be=%#010x cbTotal=%u PAGE FLIP\n", cbp, be, pBuf->cbTotal));
2123 }
2124}
2125
2126/**
2127 * Updates a OHCI transport buffer.
2128 *
2129 * This is called upon completion to adjust the sector lengths if
2130 * the total length has changed. (received less then we had space for
2131 * or a partial transfer.)
2132 *
2133 * @param pBuf The buffer to update. cbTotal contains the new total on input.
2134 * While the aVecs[*].cb members is updated upon return.
2135 */
2136static void ohciR3BufUpdate(POHCIBUF pBuf)
2137{
2138 for (uint32_t i = 0, cbCur = 0; i < pBuf->cVecs; i++)
2139 {
2140 if (cbCur + pBuf->aVecs[i].cb > pBuf->cbTotal)
2141 {
2142 pBuf->aVecs[i].cb = pBuf->cbTotal - cbCur;
2143 pBuf->cVecs = i + 1;
2144 return;
2145 }
2146 cbCur += pBuf->aVecs[i].cb;
2147 }
2148}
2149
2150
2151/** A worker for ohciR3UnlinkTds(). */
2152static bool ohciR3UnlinkIsochronousTdInList(POHCI pThis, uint32_t TdAddr, POHCIITD pITd, POHCIED pEd)
2153{
2154 const uint32_t LastTdAddr = pEd->TailP & ED_PTR_MASK;
2155 Log(("ohciUnlinkIsocTdInList: Unlinking non-head ITD! TdAddr=%#010RX32 HeadTdAddr=%#010RX32 LastEdAddr=%#010RX32\n",
2156 TdAddr, pEd->HeadP & ED_PTR_MASK, LastTdAddr));
2157 AssertMsgReturn(LastTdAddr != TdAddr, ("TdAddr=%#010RX32\n", TdAddr), false);
2158
2159 uint32_t cMax = 256;
2160 uint32_t CurTdAddr = pEd->HeadP & ED_PTR_MASK;
2161 while ( CurTdAddr != LastTdAddr
2162 && cMax-- > 0)
2163 {
2164 OHCIITD ITd;
2165 ohciR3ReadITd(pThis, CurTdAddr, &ITd);
2166 if ((ITd.NextTD & ED_PTR_MASK) == TdAddr)
2167 {
2168 ITd.NextTD = (pITd->NextTD & ED_PTR_MASK) | (ITd.NextTD & ~ED_PTR_MASK);
2169 ohciR3WriteITd(pThis, CurTdAddr, &ITd, "ohciUnlinkIsocTdInList");
2170 pITd->NextTD &= ~ED_PTR_MASK;
2171 return true;
2172 }
2173
2174 /* next */
2175 CurTdAddr = ITd.NextTD & ED_PTR_MASK;
2176 }
2177
2178 Log(("ohciUnlinkIsocTdInList: TdAddr=%#010RX32 wasn't found in the list!!! (cMax=%d)\n", TdAddr, cMax));
2179 return false;
2180}
2181
2182
2183/** A worker for ohciR3UnlinkTds(). */
2184static bool ohciR3UnlinkGeneralTdInList(POHCI pThis, uint32_t TdAddr, POHCITD pTd, POHCIED pEd)
2185{
2186 const uint32_t LastTdAddr = pEd->TailP & ED_PTR_MASK;
2187 Log(("ohciR3UnlinkGeneralTdInList: Unlinking non-head TD! TdAddr=%#010RX32 HeadTdAddr=%#010RX32 LastEdAddr=%#010RX32\n",
2188 TdAddr, pEd->HeadP & ED_PTR_MASK, LastTdAddr));
2189 AssertMsgReturn(LastTdAddr != TdAddr, ("TdAddr=%#010RX32\n", TdAddr), false);
2190
2191 uint32_t cMax = 256;
2192 uint32_t CurTdAddr = pEd->HeadP & ED_PTR_MASK;
2193 while ( CurTdAddr != LastTdAddr
2194 && cMax-- > 0)
2195 {
2196 OHCITD Td;
2197 ohciR3ReadTd(pThis, CurTdAddr, &Td);
2198 if ((Td.NextTD & ED_PTR_MASK) == TdAddr)
2199 {
2200 Td.NextTD = (pTd->NextTD & ED_PTR_MASK) | (Td.NextTD & ~ED_PTR_MASK);
2201 ohciR3WriteTd(pThis, CurTdAddr, &Td, "ohciR3UnlinkGeneralTdInList");
2202 pTd->NextTD &= ~ED_PTR_MASK;
2203 return true;
2204 }
2205
2206 /* next */
2207 CurTdAddr = Td.NextTD & ED_PTR_MASK;
2208 }
2209
2210 Log(("ohciR3UnlinkGeneralTdInList: TdAddr=%#010RX32 wasn't found in the list!!! (cMax=%d)\n", TdAddr, cMax));
2211 return false;
2212}
2213
2214
2215/**
2216 * Unlinks the TDs that makes up the URB from the ED.
2217 *
2218 * @returns success indicator. true if successfully unlinked.
2219 * @returns false if the TD was not found in the list.
2220 */
2221static bool ohciR3UnlinkTds(POHCI pThis, PVUSBURB pUrb, POHCIED pEd)
2222{
2223 /*
2224 * Don't unlink more than once.
2225 */
2226 if (pUrb->pHci->fUnlinked)
2227 return true;
2228 pUrb->pHci->fUnlinked = true;
2229
2230 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2231 {
2232 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2233 {
2234 POHCIITD pITd = (POHCIITD)&pUrb->paTds[iTd].TdCopy[0];
2235 const uint32_t ITdAddr = pUrb->paTds[iTd].TdAddr;
2236
2237 /*
2238 * Unlink the TD from the ED list.
2239 * The normal case is that it's at the head of the list.
2240 */
2241 Assert((ITdAddr & ED_PTR_MASK) == ITdAddr);
2242 if ((pEd->HeadP & ED_PTR_MASK) == ITdAddr)
2243 {
2244 pEd->HeadP = (pITd->NextTD & ED_PTR_MASK) | (pEd->HeadP & ~ED_PTR_MASK);
2245 pITd->NextTD &= ~ED_PTR_MASK;
2246 }
2247 else
2248 {
2249 /*
2250 * It's probably somewhere in the list, not a unlikely situation with
2251 * the current isochronous code.
2252 */
2253 if (!ohciR3UnlinkIsochronousTdInList(pThis, ITdAddr, pITd, pEd))
2254 return false;
2255 }
2256 }
2257 }
2258 else
2259 {
2260 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2261 {
2262 POHCITD pTd = (POHCITD)&pUrb->paTds[iTd].TdCopy[0];
2263 const uint32_t TdAddr = pUrb->paTds[iTd].TdAddr;
2264
2265 /** @todo r=bird: Messing with the toggle flag in prepare is probably not correct
2266 * when we encounter a STALL error, 4.3.1.3.7.2: ''If an endpoint returns a STALL
2267 * PID, the Host Controller retires the General TD with the ConditionCode set
2268 * to STALL and halts the endpoint. The CurrentBufferPointer, ErrorCount, and
2269 * dataToggle fields retain the values that they had at the start of the
2270 * transaction.'' */
2271
2272 /* update toggle and set data toggle carry */
2273 pTd->hwinfo &= ~TD_HWINFO_TOGGLE;
2274 if ( pTd->hwinfo & TD_HWINFO_TOGGLE_HI )
2275 {
2276 if ( !!(pTd->hwinfo & TD_HWINFO_TOGGLE_LO) ) /** @todo r=bird: is it just me or doesn't this make sense at all? */
2277 pTd->hwinfo |= TD_HWINFO_TOGGLE_LO;
2278 else
2279 pTd->hwinfo &= ~TD_HWINFO_TOGGLE_LO;
2280 }
2281 else
2282 {
2283 if ( !!(pEd->HeadP & ED_HEAD_CARRY) ) /** @todo r=bird: is it just me or doesn't this make sense at all? */
2284 pEd->HeadP |= ED_HEAD_CARRY;
2285 else
2286 pEd->HeadP &= ~ED_HEAD_CARRY;
2287 }
2288
2289 /*
2290 * Unlink the TD from the ED list.
2291 * The normal case is that it's at the head of the list.
2292 */
2293 Assert((TdAddr & ED_PTR_MASK) == TdAddr);
2294 if ((pEd->HeadP & ED_PTR_MASK) == TdAddr)
2295 {
2296 pEd->HeadP = (pTd->NextTD & ED_PTR_MASK) | (pEd->HeadP & ~ED_PTR_MASK);
2297 pTd->NextTD &= ~ED_PTR_MASK;
2298 }
2299 else
2300 {
2301 /*
2302 * The TD is probably somewhere in the list.
2303 *
2304 * This shouldn't ever happen unless there was a failure! Even on failure,
2305 * we can screw up the HCD state by picking out a TD from within the list
2306 * like this! If this turns out to be a problem, we have to find a better
2307 * solution. For now we'll hope the HCD handles it...
2308 */
2309 if (!ohciR3UnlinkGeneralTdInList(pThis, TdAddr, pTd, pEd))
2310 return false;
2311 }
2312
2313 /*
2314 * Only unlink the first TD on error.
2315 * See comment in ohciR3RhXferCompleteGeneralURB().
2316 */
2317 if (pUrb->enmStatus != VUSBSTATUS_OK)
2318 break;
2319 }
2320 }
2321
2322 return true;
2323}
2324
2325
2326/**
2327 * Checks that the transport descriptors associated with the URB
2328 * hasn't been changed in any way indicating that they may have been canceled.
2329 *
2330 * This rountine also updates the TD copies contained within the URB.
2331 *
2332 * @returns true if the URB has been canceled, otherwise false.
2333 * @param pThis The OHCI instance.
2334 * @param pUrb The URB in question.
2335 * @param pEd The ED pointer (optional).
2336 */
2337static bool ohciR3HasUrbBeenCanceled(POHCI pThis, PVUSBURB pUrb, PCOHCIED pEd)
2338{
2339 if (!pUrb)
2340 return true;
2341
2342 /*
2343 * Make sure we've got an endpoint descriptor so we can
2344 * check for tail TDs.
2345 */
2346 OHCIED Ed;
2347 if (!pEd)
2348 {
2349 ohciR3ReadEd(pThis, pUrb->pHci->EdAddr, &Ed);
2350 pEd = &Ed;
2351 }
2352
2353 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2354 {
2355 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2356 {
2357 union
2358 {
2359 OHCIITD ITd;
2360 uint32_t au32[8];
2361 } u;
2362 if ( (pUrb->paTds[iTd].TdAddr & ED_PTR_MASK)
2363 == (pEd->TailP & ED_PTR_MASK))
2364 {
2365 Log(("%s: ohciR3HasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled (tail)! [iso]\n",
2366 pUrb->pszDesc, iTd, pUrb->pHci->cTds, pUrb->paTds[iTd].TdAddr));
2367 STAM_COUNTER_INC(&pThis->StatCanceledIsocUrbs);
2368 return true;
2369 }
2370 ohciR3ReadITd(pThis, pUrb->paTds[iTd].TdAddr, &u.ITd);
2371 if ( u.au32[0] != pUrb->paTds[iTd].TdCopy[0] /* hwinfo */
2372 || u.au32[1] != pUrb->paTds[iTd].TdCopy[1] /* bp0 */
2373 || u.au32[3] != pUrb->paTds[iTd].TdCopy[3] /* be */
2374 || ( u.au32[2] != pUrb->paTds[iTd].TdCopy[2] /* NextTD */
2375 && iTd + 1 < pUrb->pHci->cTds /* ignore the last one */)
2376 || u.au32[4] != pUrb->paTds[iTd].TdCopy[4] /* psw0&1 */
2377 || u.au32[5] != pUrb->paTds[iTd].TdCopy[5] /* psw2&3 */
2378 || u.au32[6] != pUrb->paTds[iTd].TdCopy[6] /* psw4&5 */
2379 || u.au32[7] != pUrb->paTds[iTd].TdCopy[7] /* psw6&7 */
2380 )
2381 {
2382 Log(("%s: ohciR3HasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled! [iso]\n",
2383 pUrb->pszDesc, iTd, pUrb->pHci->cTds, pUrb->paTds[iTd].TdAddr));
2384 Log2((" %.*Rhxs (cur)\n"
2385 "!= %.*Rhxs (copy)\n",
2386 sizeof(u.ITd), &u.ITd, sizeof(u.ITd), &pUrb->paTds[iTd].TdCopy[0]));
2387 STAM_COUNTER_INC(&pThis->StatCanceledIsocUrbs);
2388 return true;
2389 }
2390 pUrb->paTds[iTd].TdCopy[2] = u.au32[2];
2391 }
2392 }
2393 else
2394 {
2395 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2396 {
2397 union
2398 {
2399 OHCITD Td;
2400 uint32_t au32[4];
2401 } u;
2402 if ( (pUrb->paTds[iTd].TdAddr & ED_PTR_MASK)
2403 == (pEd->TailP & ED_PTR_MASK))
2404 {
2405 Log(("%s: ohciR3HasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled (tail)!\n",
2406 pUrb->pszDesc, iTd, pUrb->pHci->cTds, pUrb->paTds[iTd].TdAddr));
2407 STAM_COUNTER_INC(&pThis->StatCanceledGenUrbs);
2408 return true;
2409 }
2410 ohciR3ReadTd(pThis, pUrb->paTds[iTd].TdAddr, &u.Td);
2411 if ( u.au32[0] != pUrb->paTds[iTd].TdCopy[0] /* hwinfo */
2412 || u.au32[1] != pUrb->paTds[iTd].TdCopy[1] /* cbp */
2413 || u.au32[3] != pUrb->paTds[iTd].TdCopy[3] /* be */
2414 || ( u.au32[2] != pUrb->paTds[iTd].TdCopy[2] /* NextTD */
2415 && iTd + 1 < pUrb->pHci->cTds /* ignore the last one */)
2416 )
2417 {
2418 Log(("%s: ohciR3HasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled!\n",
2419 pUrb->pszDesc, iTd, pUrb->pHci->cTds, pUrb->paTds[iTd].TdAddr));
2420 Log2((" %.*Rhxs (cur)\n"
2421 "!= %.*Rhxs (copy)\n",
2422 sizeof(u.Td), &u.Td, sizeof(u.Td), &pUrb->paTds[iTd].TdCopy[0]));
2423 STAM_COUNTER_INC(&pThis->StatCanceledGenUrbs);
2424 return true;
2425 }
2426 pUrb->paTds[iTd].TdCopy[2] = u.au32[2];
2427 }
2428 }
2429 return false;
2430}
2431
2432
2433/**
2434 * Returns the OHCI_CC_* corresponding to the VUSB status code.
2435 *
2436 * @returns OHCI_CC_* value.
2437 * @param enmStatus The VUSB status code.
2438 */
2439static uint32_t ohciR3VUsbStatus2OhciStatus(VUSBSTATUS enmStatus)
2440{
2441 switch (enmStatus)
2442 {
2443 case VUSBSTATUS_OK: return OHCI_CC_NO_ERROR;
2444 case VUSBSTATUS_STALL: return OHCI_CC_STALL;
2445 case VUSBSTATUS_CRC: return OHCI_CC_CRC;
2446 case VUSBSTATUS_DATA_UNDERRUN: return OHCI_CC_DATA_UNDERRUN;
2447 case VUSBSTATUS_DATA_OVERRUN: return OHCI_CC_DATA_OVERRUN;
2448 case VUSBSTATUS_DNR: return OHCI_CC_DNR;
2449 case VUSBSTATUS_NOT_ACCESSED: return OHCI_CC_NOT_ACCESSED_1;
2450 default:
2451 Log(("pUrb->enmStatus=%#x!!!\n", enmStatus));
2452 return OHCI_CC_DNR;
2453 }
2454}
2455
2456
2457/**
2458 * Lock the given OHCI controller instance.
2459 *
2460 * @returns nothing.
2461 * @param pThis The OHCI controller instance to lock.
2462 */
2463DECLINLINE(void) ohciR3Lock(POHCI pThis)
2464{
2465 RTCritSectEnter(&pThis->CritSect);
2466
2467# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
2468 /* Clear all caches here to avoid reading stale data from previous lock holders. */
2469 ohciR3PhysReadCacheClear(pThis->pCacheED);
2470 ohciR3PhysReadCacheClear(pThis->pCacheTD);
2471# endif
2472}
2473
2474
2475/**
2476 * Unlocks the given OHCI controller instance.
2477 *
2478 * @returns nothing.
2479 * @param pThis The OHCI controller instance to unlock.
2480 */
2481DECLINLINE(void) ohciR3Unlock(POHCI pThis)
2482{
2483# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
2484 /*
2485 * Clear all caches here to avoid leaving stale data behind (paranoia^2,
2486 * already done in ohciR3Lock).
2487 */
2488 ohciR3PhysReadCacheClear(pThis->pCacheED);
2489 ohciR3PhysReadCacheClear(pThis->pCacheTD);
2490# endif
2491
2492 RTCritSectLeave(&pThis->CritSect);
2493}
2494
2495
2496/**
2497 * Worker for ohciR3RhXferCompletion that handles the completion of
2498 * a URB made up of isochronous TDs.
2499 *
2500 * In general, all URBs should have status OK.
2501 */
2502static void ohciR3RhXferCompleteIsochronousURB(POHCI pThis, PVUSBURB pUrb /*, POHCIED pEd , int cFmAge*/)
2503{
2504 /*
2505 * Copy the data back (if IN operation) and update the TDs.
2506 */
2507 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2508 {
2509 POHCIITD pITd = (POHCIITD)&pUrb->paTds[iTd].TdCopy[0];
2510 const uint32_t ITdAddr = pUrb->paTds[iTd].TdAddr;
2511 const unsigned cFrames = ((pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
2512 unsigned R = (pUrb->pHci->u32FrameNo & ITD_HWINFO_SF) - (pITd->HwInfo & ITD_HWINFO_SF);
2513 if (R >= 8)
2514 R = 0; /* submitted ahead of time. */
2515
2516 /*
2517 * Only one case of TD level condition code is document, so
2518 * just set NO_ERROR here to reduce number duplicate code.
2519 */
2520 pITd->HwInfo &= ~TD_HWINFO_CC;
2521 AssertCompile(OHCI_CC_NO_ERROR == 0);
2522
2523 if (pUrb->enmStatus == VUSBSTATUS_OK)
2524 {
2525 /*
2526 * Update the frames and copy back the data.
2527 * We assume that we don't get incorrect lengths here.
2528 */
2529 for (unsigned i = 0; i < cFrames; i++)
2530 {
2531 if ( i < R
2532 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_NOT_ACCESSED)
2533 {
2534 /* It should already be NotAccessed. */
2535 pITd->aPSW[i] |= 0xe000; /* (Don't touch the 12th bit.) */
2536 continue;
2537 }
2538
2539 /* Update the PSW (save the offset first in case of a IN). */
2540 uint32_t off = pITd->aPSW[i] & ITD_PSW_OFFSET;
2541 pITd->aPSW[i] = ohciR3VUsbStatus2OhciStatus(pUrb->aIsocPkts[i - R].enmStatus)
2542 >> (TD_HWINFO_CC_SHIFT - ITD_PSW_CC_SHIFT);
2543
2544 if ( pUrb->enmDir == VUSBDIRECTION_IN
2545 && ( pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_OK
2546 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_DATA_UNDERRUN
2547 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_DATA_OVERRUN))
2548 {
2549 /* Set the size. */
2550 const unsigned cb = pUrb->aIsocPkts[i - R].cb;
2551 pITd->aPSW[i] |= cb & ITD_PSW_SIZE;
2552 /* Copy data. */
2553 if (cb)
2554 {
2555 uint8_t *pb = &pUrb->abData[pUrb->aIsocPkts[i - R].off];
2556 if (off + cb > 0x1000)
2557 {
2558 if (off < 0x1000)
2559 {
2560 /* both */
2561 const unsigned cb0 = 0x1000 - off;
2562 ohciR3PhysWrite(pThis, (pITd->BP0 & ITD_BP0_MASK) + off, pb, cb0);
2563 ohciR3PhysWrite(pThis, pITd->BE & ITD_BP0_MASK, pb + cb0, cb - cb0);
2564 }
2565 else /* only in the 2nd page */
2566 ohciR3PhysWrite(pThis, (pITd->BE & ITD_BP0_MASK) + (off & ITD_BP0_MASK), pb, cb);
2567 }
2568 else /* only in the 1st page */
2569 ohciR3PhysWrite(pThis, (pITd->BP0 & ITD_BP0_MASK) + off, pb, cb);
2570 Log5(("packet %d: off=%#x cb=%#x pb=%p (%#x)\n"
2571 "%.*Rhxd\n",
2572 i + R, off, cb, pb, pb - &pUrb->abData[0], cb, pb));
2573 //off += cb;
2574 }
2575 }
2576 }
2577
2578 /*
2579 * If the last package ended with a NotAccessed status, set ITD CC
2580 * to DataOverrun to indicate scheduling overrun.
2581 */
2582 if (pUrb->aIsocPkts[pUrb->cIsocPkts - 1].enmStatus == VUSBSTATUS_NOT_ACCESSED)
2583 pITd->HwInfo |= OHCI_CC_DATA_OVERRUN;
2584 }
2585 else
2586 {
2587 Log(("DevOHCI: Taking untested code path at line %d...\n", __LINE__));
2588 /*
2589 * Most status codes only applies to the individual packets.
2590 *
2591 * If we get a URB level error code of this kind, we'll distribute
2592 * it to all the packages unless some other status is available for
2593 * a package. This is a bit fuzzy, and we will get rid of this code
2594 * before long!
2595 */
2596 //if (pUrb->enmStatus != VUSBSTATUS_DATA_OVERRUN)
2597 {
2598 const unsigned uCC = ohciR3VUsbStatus2OhciStatus(pUrb->enmStatus)
2599 >> (TD_HWINFO_CC_SHIFT - ITD_PSW_CC_SHIFT);
2600 for (unsigned i = 0; i < cFrames; i++)
2601 pITd->aPSW[i] = uCC;
2602 }
2603 //else
2604 // pITd->HwInfo |= ohciR3VUsbStatus2OhciStatus(pUrb->enmStatus);
2605 }
2606
2607 /*
2608 * Update the done queue interrupt timer.
2609 */
2610 uint32_t DoneInt = (pITd->HwInfo & ITD_HWINFO_DI) >> ITD_HWINFO_DI_SHIFT;
2611 if ((pITd->HwInfo & TD_HWINFO_CC) != OHCI_CC_NO_ERROR)
2612 DoneInt = 0; /* It's cleared on error. */
2613 if ( DoneInt != 0x7
2614 && DoneInt < pThis->dqic)
2615 pThis->dqic = DoneInt;
2616
2617 /*
2618 * Move on to the done list and write back the modified TD.
2619 */
2620# ifdef LOG_ENABLED
2621 if (!pThis->done)
2622 pThis->u32FmDoneQueueTail = pThis->HcFmNumber;
2623# ifdef VBOX_STRICT
2624 ohciR3InDoneQueueAdd(pThis, ITdAddr);
2625# endif
2626# endif
2627 pITd->NextTD = pThis->done;
2628 pThis->done = ITdAddr;
2629
2630 Log(("%s: ohciR3RhXferCompleteIsochronousURB: ITdAddr=%#010x EdAddr=%#010x SF=%#x (%#x) CC=%#x FC=%d "
2631 "psw0=%x:%x psw1=%x:%x psw2=%x:%x psw3=%x:%x psw4=%x:%x psw5=%x:%x psw6=%x:%x psw7=%x:%x R=%d\n",
2632 pUrb->pszDesc, ITdAddr,
2633 pUrb->pHci->EdAddr,
2634 pITd->HwInfo & ITD_HWINFO_SF, pThis->HcFmNumber,
2635 (pITd->HwInfo & ITD_HWINFO_CC) >> ITD_HWINFO_CC_SHIFT,
2636 (pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT,
2637 pITd->aPSW[0] >> ITD_PSW_CC_SHIFT, pITd->aPSW[0] & ITD_PSW_SIZE,
2638 pITd->aPSW[1] >> ITD_PSW_CC_SHIFT, pITd->aPSW[1] & ITD_PSW_SIZE,
2639 pITd->aPSW[2] >> ITD_PSW_CC_SHIFT, pITd->aPSW[2] & ITD_PSW_SIZE,
2640 pITd->aPSW[3] >> ITD_PSW_CC_SHIFT, pITd->aPSW[3] & ITD_PSW_SIZE,
2641 pITd->aPSW[4] >> ITD_PSW_CC_SHIFT, pITd->aPSW[4] & ITD_PSW_SIZE,
2642 pITd->aPSW[5] >> ITD_PSW_CC_SHIFT, pITd->aPSW[5] & ITD_PSW_SIZE,
2643 pITd->aPSW[6] >> ITD_PSW_CC_SHIFT, pITd->aPSW[6] & ITD_PSW_SIZE,
2644 pITd->aPSW[7] >> ITD_PSW_CC_SHIFT, pITd->aPSW[7] & ITD_PSW_SIZE,
2645 R));
2646 ohciR3WriteITd(pThis, ITdAddr, pITd, "retired");
2647 }
2648}
2649
2650
2651/**
2652 * Worker for ohciR3RhXferCompletion that handles the completion of
2653 * a URB made up of general TDs.
2654 */
2655static void ohciR3RhXferCompleteGeneralURB(POHCI pThis, PVUSBURB pUrb, POHCIED pEd, int cFmAge)
2656{
2657 RT_NOREF(cFmAge);
2658
2659 /*
2660 * Copy the data back (if IN operation) and update the TDs.
2661 */
2662 unsigned cbLeft = pUrb->cbData;
2663 uint8_t *pb = &pUrb->abData[0];
2664 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2665 {
2666 POHCITD pTd = (POHCITD)&pUrb->paTds[iTd].TdCopy[0];
2667 const uint32_t TdAddr = pUrb->paTds[iTd].TdAddr;
2668
2669 /*
2670 * Setup a ohci transfer buffer and calc the new cbp value.
2671 */
2672 OHCIBUF Buf;
2673 ohciR3BufInit(&Buf, pTd->cbp, pTd->be);
2674 uint32_t NewCbp;
2675 if (cbLeft >= Buf.cbTotal)
2676 NewCbp = 0;
2677 else
2678 {
2679 /* (len may have changed for short transfers) */
2680 Buf.cbTotal = cbLeft;
2681 ohciR3BufUpdate(&Buf);
2682 Assert(Buf.cVecs >= 1);
2683 NewCbp = Buf.aVecs[Buf.cVecs-1].Addr + Buf.aVecs[Buf.cVecs-1].cb;
2684 }
2685
2686 /*
2687 * Write back IN buffers.
2688 */
2689 if ( pUrb->enmDir == VUSBDIRECTION_IN
2690 && ( pUrb->enmStatus == VUSBSTATUS_OK
2691 || pUrb->enmStatus == VUSBSTATUS_DATA_OVERRUN
2692 || pUrb->enmStatus == VUSBSTATUS_DATA_UNDERRUN)
2693 && Buf.cbTotal > 0)
2694 {
2695 Assert(Buf.cVecs > 0);
2696
2697 /* Be paranoid */
2698 if ( Buf.aVecs[0].cb > cbLeft
2699 || ( Buf.cVecs > 1
2700 && Buf.aVecs[1].cb > (cbLeft - Buf.aVecs[0].cb)))
2701 {
2702 ohciR3RaiseUnrecoverableError(pThis, 1);
2703 return;
2704 }
2705
2706 ohciR3PhysWrite(pThis, Buf.aVecs[0].Addr, pb, Buf.aVecs[0].cb);
2707 if (Buf.cVecs > 1)
2708 ohciR3PhysWrite(pThis, Buf.aVecs[1].Addr, pb + Buf.aVecs[0].cb, Buf.aVecs[1].cb);
2709 }
2710
2711 /* advance the data buffer. */
2712 cbLeft -= Buf.cbTotal;
2713 pb += Buf.cbTotal;
2714
2715 /*
2716 * Set writeback field.
2717 */
2718 /* zero out writeback fields for retirement */
2719 pTd->hwinfo &= ~TD_HWINFO_CC;
2720 /* always update the CurrentBufferPointer; essential for underrun/overrun errors */
2721 pTd->cbp = NewCbp;
2722
2723 if (pUrb->enmStatus == VUSBSTATUS_OK)
2724 {
2725 pTd->hwinfo &= ~TD_HWINFO_ERRORS;
2726
2727 /* update done queue interrupt timer */
2728 uint32_t DoneInt = (pTd->hwinfo & TD_HWINFO_DI) >> 21;
2729 if ( DoneInt != 0x7
2730 && DoneInt < pThis->dqic)
2731 pThis->dqic = DoneInt;
2732 Log(("%s: ohciR3RhXferCompleteGeneralURB: ED=%#010x TD=%#010x Age=%d enmStatus=%d cbTotal=%#x NewCbp=%#010RX32 dqic=%d\n",
2733 pUrb->pszDesc, pUrb->pHci->EdAddr, TdAddr, cFmAge, pUrb->enmStatus, Buf.cbTotal, NewCbp, pThis->dqic));
2734 }
2735 else
2736 {
2737 Log(("%s: ohciR3RhXferCompleteGeneralURB: HALTED ED=%#010x TD=%#010x (age %d) pUrb->enmStatus=%d\n",
2738 pUrb->pszDesc, pUrb->pHci->EdAddr, TdAddr, cFmAge, pUrb->enmStatus));
2739 pEd->HeadP |= ED_HEAD_HALTED;
2740 pThis->dqic = 0; /* "If the Transfer Descriptor is being retired with an error,
2741 * then the Done Queue Interrupt Counter is cleared as if the
2742 * InterruptDelay field were zero."
2743 */
2744 switch (pUrb->enmStatus)
2745 {
2746 case VUSBSTATUS_STALL:
2747 pTd->hwinfo |= OHCI_CC_STALL;
2748 break;
2749 case VUSBSTATUS_CRC:
2750 pTd->hwinfo |= OHCI_CC_CRC;
2751 break;
2752 case VUSBSTATUS_DATA_UNDERRUN:
2753 pTd->hwinfo |= OHCI_CC_DATA_UNDERRUN;
2754 break;
2755 case VUSBSTATUS_DATA_OVERRUN:
2756 pTd->hwinfo |= OHCI_CC_DATA_OVERRUN;
2757 break;
2758 default: /* what the hell */
2759 Log(("pUrb->enmStatus=%#x!!!\n", pUrb->enmStatus));
2760 RT_FALL_THRU();
2761 case VUSBSTATUS_DNR:
2762 pTd->hwinfo |= OHCI_CC_DNR;
2763 break;
2764 }
2765 }
2766
2767 /*
2768 * Move on to the done list and write back the modified TD.
2769 */
2770# ifdef LOG_ENABLED
2771 if (!pThis->done)
2772 pThis->u32FmDoneQueueTail = pThis->HcFmNumber;
2773# ifdef VBOX_STRICT
2774 ohciR3InDoneQueueAdd(pThis, TdAddr);
2775# endif
2776# endif
2777 pTd->NextTD = pThis->done;
2778 pThis->done = TdAddr;
2779
2780 ohciR3WriteTd(pThis, TdAddr, pTd, "retired");
2781
2782 /*
2783 * If we've halted the endpoint, we stop here.
2784 * ohciR3UnlinkTds() will make sure we've only unliked the first TD.
2785 *
2786 * The reason for this is that while we can have more than one TD in a URB, real
2787 * OHCI hardware will only deal with one TD at the time and it's therefore incorrect
2788 * to retire TDs after the endpoint has been halted. Win2k will crash or enter infinite
2789 * kernel loop if we don't behave correctly. (See @bugref{1646}.)
2790 */
2791 if (pEd->HeadP & ED_HEAD_HALTED)
2792 break;
2793 }
2794}
2795
2796
2797/**
2798 * Transfer completion callback routine.
2799 *
2800 * VUSB will call this when a transfer have been completed
2801 * in a one or another way.
2802 *
2803 * @param pInterface Pointer to OHCI::ROOTHUB::IRhPort.
2804 * @param pUrb Pointer to the URB in question.
2805 */
2806static DECLCALLBACK(void) ohciR3RhXferCompletion(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb)
2807{
2808 POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
2809 LogFlow(("%s: ohciR3RhXferCompletion: EdAddr=%#010RX32 cTds=%d TdAddr0=%#010RX32\n",
2810 pUrb->pszDesc, pUrb->pHci->EdAddr, pUrb->pHci->cTds, pUrb->paTds[0].TdAddr));
2811
2812 ohciR3Lock(pThis);
2813
2814 /* Do nothing if the HC encountered an unrecoverable error. */
2815 if (!(pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR))
2816 {
2817 pThis->fIdle = false; /* Mark as active */
2818
2819 /* get the current end point descriptor. */
2820 OHCIED Ed;
2821 ohciR3ReadEd(pThis, pUrb->pHci->EdAddr, &Ed);
2822
2823 /*
2824 * Check that the URB hasn't been canceled and then try unlink the TDs.
2825 *
2826 * We drop the URB if the ED is marked halted/skip ASSUMING that this
2827 * means the HCD has canceled the URB.
2828 *
2829 * If we succeed here (i.e. not dropping the URB), the TdCopy members will
2830 * be updated but not yet written. We will delay the writing till we're done
2831 * with the data copying, buffer pointer advancing and error handling.
2832 */
2833 int cFmAge = ohciR3InFlightRemoveUrb(pThis, pUrb);
2834 if (pUrb->enmStatus == VUSBSTATUS_UNDO)
2835 {
2836 /* Leave the TD alone - the HCD doesn't want us talking to the device. */
2837 Log(("%s: ohciR3RhXferCompletion: CANCELED {ED=%#010x cTds=%d TD0=%#010x age %d}\n",
2838 pUrb->pszDesc, pUrb->pHci->EdAddr, pUrb->pHci->cTds, pUrb->paTds[0].TdAddr, cFmAge));
2839 STAM_COUNTER_INC(&pThis->StatDroppedUrbs);
2840 ohciR3Unlock(pThis);
2841 return;
2842 }
2843 bool fHasBeenCanceled = false;
2844 if ( (Ed.HeadP & ED_HEAD_HALTED)
2845 || (Ed.hwinfo & ED_HWINFO_SKIP)
2846 || cFmAge < 0
2847 || (fHasBeenCanceled = ohciR3HasUrbBeenCanceled(pThis, pUrb, &Ed))
2848 || !ohciR3UnlinkTds(pThis, pUrb, &Ed)
2849 )
2850 {
2851 Log(("%s: ohciR3RhXferCompletion: DROPPED {ED=%#010x cTds=%d TD0=%#010x age %d} because:%s%s%s%s%s!!!\n",
2852 pUrb->pszDesc, pUrb->pHci->EdAddr, pUrb->pHci->cTds, pUrb->paTds[0].TdAddr, cFmAge,
2853 (Ed.HeadP & ED_HEAD_HALTED) ? " ep halted" : "",
2854 (Ed.hwinfo & ED_HWINFO_SKIP) ? " ep skip" : "",
2855 (Ed.HeadP & ED_PTR_MASK) != pUrb->paTds[0].TdAddr ? " ep head-changed" : "",
2856 cFmAge < 0 ? " td not-in-flight" : "",
2857 fHasBeenCanceled ? " td canceled" : ""));
2858 NOREF(fHasBeenCanceled);
2859 STAM_COUNTER_INC(&pThis->StatDroppedUrbs);
2860 ohciR3Unlock(pThis);
2861 return;
2862 }
2863
2864 /*
2865 * Complete the TD updating and write the back.
2866 * When appropriate also copy data back to the guest memory.
2867 */
2868 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2869 ohciR3RhXferCompleteIsochronousURB(pThis, pUrb /*, &Ed , cFmAge*/);
2870 else
2871 ohciR3RhXferCompleteGeneralURB(pThis, pUrb, &Ed, cFmAge);
2872
2873 /* finally write back the endpoint descriptor. */
2874 ohciR3WriteEd(pThis, pUrb->pHci->EdAddr, &Ed);
2875 }
2876
2877 ohciR3Unlock(pThis);
2878}
2879
2880
2881/**
2882 * Handle transfer errors.
2883 *
2884 * VUSB calls this when a transfer attempt failed. This function will respond
2885 * indicating whether to retry or complete the URB with failure.
2886 *
2887 * @returns true if the URB should be retired.
2888 * @returns false if the URB should be retried.
2889 * @param pInterface Pointer to OHCI::ROOTHUB::IRhPort.
2890 * @param pUrb Pointer to the URB in question.
2891 */
2892static DECLCALLBACK(bool) ohciR3RhXferError(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb)
2893{
2894 POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
2895
2896 /*
2897 * Isochronous URBs can't be retried.
2898 */
2899 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2900 return true;
2901
2902 /*
2903 * Don't retry on stall.
2904 */
2905 if (pUrb->enmStatus == VUSBSTATUS_STALL)
2906 {
2907 Log2(("%s: ohciR3RhXferError: STALL, giving up.\n", pUrb->pszDesc));
2908 return true;
2909 }
2910
2911 ohciR3Lock(pThis);
2912 bool fRetire = false;
2913 /*
2914 * Check if the TDs still are valid.
2915 * This will make sure the TdCopy is up to date.
2916 */
2917 const uint32_t TdAddr = pUrb->paTds[0].TdAddr;
2918/** @todo IMPORTANT! we must check if the ED is still valid at this point!!! */
2919 if (ohciR3HasUrbBeenCanceled(pThis, pUrb, NULL))
2920 {
2921 Log(("%s: ohciR3RhXferError: TdAddr0=%#x canceled!\n", pUrb->pszDesc, TdAddr));
2922 fRetire = true;
2923 }
2924 else
2925 {
2926 /*
2927 * Get and update the error counter.
2928 */
2929 POHCITD pTd = (POHCITD)&pUrb->paTds[0].TdCopy[0];
2930 unsigned cErrs = (pTd->hwinfo & TD_HWINFO_ERRORS) >> TD_ERRORS_SHIFT;
2931 pTd->hwinfo &= ~TD_HWINFO_ERRORS;
2932 cErrs++;
2933 pTd->hwinfo |= (cErrs % TD_ERRORS_MAX) << TD_ERRORS_SHIFT;
2934 ohciR3WriteTd(pThis, TdAddr, pTd, "ohciR3RhXferError");
2935
2936 if (cErrs >= TD_ERRORS_MAX - 1)
2937 {
2938 Log2(("%s: ohciR3RhXferError: too many errors, giving up!\n", pUrb->pszDesc));
2939 fRetire = true;
2940 }
2941 else
2942 Log2(("%s: ohciR3RhXferError: cErrs=%d: retrying...\n", pUrb->pszDesc, cErrs));
2943 }
2944
2945 ohciR3Unlock(pThis);
2946 return fRetire;
2947}
2948
2949
2950/**
2951 * Service a general transport descriptor.
2952 */
2953static bool ohciR3ServiceTd(POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, uint32_t TdAddr,
2954 uint32_t *pNextTdAddr, const char *pszListName)
2955{
2956 RT_NOREF(pszListName);
2957
2958 /*
2959 * Read the TD and setup the buffer data.
2960 */
2961 OHCITD Td;
2962 ohciR3ReadTd(pThis, TdAddr, &Td);
2963 OHCIBUF Buf;
2964 ohciR3BufInit(&Buf, Td.cbp, Td.be);
2965
2966 *pNextTdAddr = Td.NextTD & ED_PTR_MASK;
2967
2968 /*
2969 * Determine the direction.
2970 */
2971 VUSBDIRECTION enmDir;
2972 switch (pEd->hwinfo & ED_HWINFO_DIR)
2973 {
2974 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
2975 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
2976 default:
2977 switch (Td.hwinfo & TD_HWINFO_DIR)
2978 {
2979 case TD_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
2980 case TD_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
2981 case 0: enmDir = VUSBDIRECTION_SETUP; break;
2982 default:
2983 Log(("ohciR3ServiceTd: Invalid direction!!!! Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Td.hwinfo, pEd->hwinfo));
2984 ohciR3RaiseUnrecoverableError(pThis, 2);
2985 return false;
2986 }
2987 break;
2988 }
2989
2990 pThis->fIdle = false; /* Mark as active */
2991
2992 /*
2993 * Allocate and initialize a new URB.
2994 */
2995 PVUSBURB pUrb = VUSBIRhNewUrb(pThis->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, NULL,
2996 enmType, enmDir, Buf.cbTotal, 1, NULL);
2997 if (!pUrb)
2998 return false; /* retry later... */
2999
3000 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3001 pUrb->fShortNotOk = !(Td.hwinfo & TD_HWINFO_ROUNDING);
3002 pUrb->enmStatus = VUSBSTATUS_OK;
3003 pUrb->pHci->EdAddr = EdAddr;
3004 pUrb->pHci->fUnlinked = false;
3005 pUrb->pHci->cTds = 1;
3006 pUrb->paTds[0].TdAddr = TdAddr;
3007 pUrb->pHci->u32FrameNo = pThis->HcFmNumber;
3008 AssertCompile(sizeof(pUrb->paTds[0].TdCopy) >= sizeof(Td));
3009 memcpy(pUrb->paTds[0].TdCopy, &Td, sizeof(Td));
3010
3011 /* copy data if out bound transfer. */
3012 pUrb->cbData = Buf.cbTotal;
3013 if ( Buf.cbTotal
3014 && Buf.cVecs > 0
3015 && enmDir != VUSBDIRECTION_IN)
3016 {
3017 /* Be paranoid. */
3018 if ( Buf.aVecs[0].cb > pUrb->cbData
3019 || ( Buf.cVecs > 1
3020 && Buf.aVecs[1].cb > (pUrb->cbData - Buf.aVecs[0].cb)))
3021 {
3022 ohciR3RaiseUnrecoverableError(pThis, 3);
3023 VUSBIRhFreeUrb(pThis->RootHub.pIRhConn, pUrb);
3024 return false;
3025 }
3026
3027 ohciR3PhysRead(pThis, Buf.aVecs[0].Addr, pUrb->abData, Buf.aVecs[0].cb);
3028 if (Buf.cVecs > 1)
3029 ohciR3PhysRead(pThis, Buf.aVecs[1].Addr, &pUrb->abData[Buf.aVecs[0].cb], Buf.aVecs[1].cb);
3030 }
3031
3032 /*
3033 * Submit the URB.
3034 */
3035 ohciR3InFlightAdd(pThis, TdAddr, pUrb);
3036 Log(("%s: ohciR3ServiceTd: submitting TdAddr=%#010x EdAddr=%#010x cbData=%#x\n",
3037 pUrb->pszDesc, TdAddr, EdAddr, pUrb->cbData));
3038
3039 ohciR3Unlock(pThis);
3040 int rc = VUSBIRhSubmitUrb(pThis->RootHub.pIRhConn, pUrb, &pThis->RootHub.Led);
3041 ohciR3Lock(pThis);
3042 if (RT_SUCCESS(rc))
3043 return true;
3044
3045 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
3046 Log(("ohciR3ServiceTd: failed submitting TdAddr=%#010x EdAddr=%#010x pUrb=%p!!\n",
3047 TdAddr, EdAddr, pUrb));
3048 VUSBIRhFreeUrb(pThis->RootHub.pIRhConn, pUrb);
3049 ohciR3InFlightRemove(pThis, TdAddr);
3050 return false;
3051}
3052
3053
3054/**
3055 * Service a the head TD of an endpoint.
3056 */
3057static bool ohciR3ServiceHeadTd(POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
3058{
3059 /*
3060 * Read the TD, after first checking if it's already in-flight.
3061 */
3062 uint32_t TdAddr = pEd->HeadP & ED_PTR_MASK;
3063 if (ohciR3IsTdInFlight(pThis, TdAddr))
3064 return false;
3065# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
3066 ohciR3InDoneQueueCheck(pThis, TdAddr);
3067# endif
3068 return ohciR3ServiceTd(pThis, enmType, pEd, EdAddr, TdAddr, &TdAddr, pszListName);
3069}
3070
3071
3072/**
3073 * Service one or more general transport descriptors (bulk or interrupt).
3074 */
3075static bool ohciR3ServiceTdMultiple(POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr,
3076 uint32_t TdAddr, uint32_t *pNextTdAddr, const char *pszListName)
3077{
3078 RT_NOREF(pszListName);
3079
3080 /*
3081 * Read the TDs involved in this URB.
3082 */
3083 struct OHCITDENTRY
3084 {
3085 /** The TD. */
3086 OHCITD Td;
3087 /** The associated OHCI buffer tracker. */
3088 OHCIBUF Buf;
3089 /** The TD address. */
3090 uint32_t TdAddr;
3091 /** Pointer to the next element in the chain (stack). */
3092 struct OHCITDENTRY *pNext;
3093 } Head;
3094
3095# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
3096 ohciR3PhysReadCacheClear(pThis->pCacheTD);
3097# endif
3098
3099 /* read the head */
3100 ohciR3ReadTd(pThis, TdAddr, &Head.Td);
3101 ohciR3BufInit(&Head.Buf, Head.Td.cbp, Head.Td.be);
3102 Head.TdAddr = TdAddr;
3103 Head.pNext = NULL;
3104
3105 /* combine with more TDs. */
3106 struct OHCITDENTRY *pTail = &Head;
3107 unsigned cbTotal = pTail->Buf.cbTotal;
3108 unsigned cTds = 1;
3109 while ( (pTail->Buf.cbTotal == 0x1000 || pTail->Buf.cbTotal == 0x2000)
3110 && !(pTail->Td.hwinfo & TD_HWINFO_ROUNDING) /* This isn't right for *BSD, but let's not . */
3111 && (pTail->Td.NextTD & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3112 && cTds < 128)
3113 {
3114 struct OHCITDENTRY *pCur = (struct OHCITDENTRY *)alloca(sizeof(*pCur));
3115
3116 pCur->pNext = NULL;
3117 pCur->TdAddr = pTail->Td.NextTD & ED_PTR_MASK;
3118 ohciR3ReadTd(pThis, pCur->TdAddr, &pCur->Td);
3119 ohciR3BufInit(&pCur->Buf, pCur->Td.cbp, pCur->Td.be);
3120
3121 /* Don't combine if the direction doesn't match up. There can't actually be
3122 * a mismatch for bulk/interrupt EPs unless the guest is buggy.
3123 */
3124 if ( (pCur->Td.hwinfo & (TD_HWINFO_DIR))
3125 != (Head.Td.hwinfo & (TD_HWINFO_DIR)))
3126 break;
3127
3128 pTail->pNext = pCur;
3129 pTail = pCur;
3130 cbTotal += pCur->Buf.cbTotal;
3131 cTds++;
3132 }
3133
3134 /* calc next TD address */
3135 *pNextTdAddr = pTail->Td.NextTD & ED_PTR_MASK;
3136
3137 /*
3138 * Determine the direction.
3139 */
3140 VUSBDIRECTION enmDir;
3141 switch (pEd->hwinfo & ED_HWINFO_DIR)
3142 {
3143 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3144 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3145 default:
3146 Log(("ohciR3ServiceTdMultiple: WARNING! Ed.hwdinfo=%#x bulk or interrupt EP shouldn't rely on the TD for direction...\n", pEd->hwinfo));
3147 switch (Head.Td.hwinfo & TD_HWINFO_DIR)
3148 {
3149 case TD_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3150 case TD_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3151 default:
3152 Log(("ohciR3ServiceTdMultiple: Invalid direction!!!! Head.Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Head.Td.hwinfo, pEd->hwinfo));
3153 ohciR3RaiseUnrecoverableError(pThis, 4);
3154 return false;
3155 }
3156 break;
3157 }
3158
3159 pThis->fIdle = false; /* Mark as active */
3160
3161 /*
3162 * Allocate and initialize a new URB.
3163 */
3164 PVUSBURB pUrb = VUSBIRhNewUrb(pThis->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, NULL,
3165 enmType, enmDir, cbTotal, cTds, "ohciR3ServiceTdMultiple");
3166 if (!pUrb)
3167 /* retry later... */
3168 return false;
3169 Assert(pUrb->cbData == cbTotal);
3170
3171 pUrb->enmType = enmType;
3172 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3173 pUrb->enmDir = enmDir;
3174 pUrb->fShortNotOk = !(pTail->Td.hwinfo & TD_HWINFO_ROUNDING);
3175 pUrb->enmStatus = VUSBSTATUS_OK;
3176 pUrb->pHci->cTds = cTds;
3177 pUrb->pHci->EdAddr = EdAddr;
3178 pUrb->pHci->fUnlinked = false;
3179 pUrb->pHci->u32FrameNo = pThis->HcFmNumber;
3180
3181 /* Copy data and TD information. */
3182 unsigned iTd = 0;
3183 uint8_t *pb = &pUrb->abData[0];
3184 for (struct OHCITDENTRY *pCur = &Head; pCur; pCur = pCur->pNext, iTd++)
3185 {
3186 /* data */
3187 if ( cbTotal
3188 && enmDir != VUSBDIRECTION_IN
3189 && pCur->Buf.cVecs > 0)
3190 {
3191 ohciR3PhysRead(pThis, pCur->Buf.aVecs[0].Addr, pb, pCur->Buf.aVecs[0].cb);
3192 if (pCur->Buf.cVecs > 1)
3193 ohciR3PhysRead(pThis, pCur->Buf.aVecs[1].Addr, pb + pCur->Buf.aVecs[0].cb, pCur->Buf.aVecs[1].cb);
3194 }
3195 pb += pCur->Buf.cbTotal;
3196
3197 /* TD info */
3198 pUrb->paTds[iTd].TdAddr = pCur->TdAddr;
3199 AssertCompile(sizeof(pUrb->paTds[iTd].TdCopy) >= sizeof(pCur->Td));
3200 memcpy(pUrb->paTds[iTd].TdCopy, &pCur->Td, sizeof(pCur->Td));
3201 }
3202
3203 /*
3204 * Submit the URB.
3205 */
3206 ohciR3InFlightAddUrb(pThis, pUrb);
3207 Log(("%s: ohciR3ServiceTdMultiple: submitting cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x\n",
3208 pUrb->pszDesc, pUrb->cbData, EdAddr, cTds, TdAddr));
3209 ohciR3Unlock(pThis);
3210 int rc = VUSBIRhSubmitUrb(pThis->RootHub.pIRhConn, pUrb, &pThis->RootHub.Led);
3211 ohciR3Lock(pThis);
3212 if (RT_SUCCESS(rc))
3213 return true;
3214
3215 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
3216 Log(("ohciR3ServiceTdMultiple: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x - rc=%Rrc\n",
3217 pUrb, cbTotal, EdAddr, cTds, TdAddr, rc));
3218 for (struct OHCITDENTRY *pCur = &Head; pCur; pCur = pCur->pNext, iTd++)
3219 ohciR3InFlightRemove(pThis, pCur->TdAddr);
3220 VUSBIRhFreeUrb(pThis->RootHub.pIRhConn, pUrb);
3221 return false;
3222}
3223
3224
3225/**
3226 * Service the head TD of an endpoint.
3227 */
3228static bool ohciR3ServiceHeadTdMultiple(POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
3229{
3230 /*
3231 * First, check that it's not already in-flight.
3232 */
3233 uint32_t TdAddr = pEd->HeadP & ED_PTR_MASK;
3234 if (ohciR3IsTdInFlight(pThis, TdAddr))
3235 return false;
3236# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
3237 ohciR3InDoneQueueCheck(pThis, TdAddr);
3238# endif
3239 return ohciR3ServiceTdMultiple(pThis, enmType, pEd, EdAddr, TdAddr, &TdAddr, pszListName);
3240}
3241
3242
3243/**
3244 * A worker for ohciR3ServiceIsochronousEndpoint which unlinks a ITD
3245 * that belongs to the past.
3246 */
3247static bool ohciR3ServiceIsochronousTdUnlink(POHCI pThis, POHCIITD pITd, uint32_t ITdAddr, uint32_t ITdAddrPrev,
3248 PVUSBURB pUrb, POHCIED pEd, uint32_t EdAddr)
3249{
3250 LogFlow(("%s%sohciR3ServiceIsochronousTdUnlink: Unlinking ITD: ITdAddr=%#010x EdAddr=%#010x ITdAddrPrev=%#010x\n",
3251 pUrb ? pUrb->pszDesc : "", pUrb ? ": " : "", ITdAddr, EdAddr, ITdAddrPrev));
3252
3253 /*
3254 * Do the unlinking.
3255 */
3256 const uint32_t ITdAddrNext = pITd->NextTD & ED_PTR_MASK;
3257 if (ITdAddrPrev)
3258 {
3259 /* Get validate the previous TD */
3260 int iInFlightPrev = ohciR3InFlightFind(pThis, ITdAddrPrev);
3261 AssertMsgReturn(iInFlightPrev >= 0, ("ITdAddr=%#RX32\n", ITdAddrPrev), false);
3262 PVUSBURB pUrbPrev = pThis->aInFlight[iInFlightPrev].pUrb;
3263 if (ohciR3HasUrbBeenCanceled(pThis, pUrbPrev, pEd)) /* ensures the copy is correct. */
3264 return false;
3265
3266 /* Update the copy and write it back. */
3267 POHCIITD pITdPrev = ((POHCIITD)pUrbPrev->paTds[0].TdCopy);
3268 pITdPrev->NextTD = (pITdPrev->NextTD & ~ED_PTR_MASK) | ITdAddrNext;
3269 ohciR3WriteITd(pThis, ITdAddrPrev, pITdPrev, "ohciR3ServiceIsochronousEndpoint");
3270 }
3271 else
3272 {
3273 /* It's the head node. update the copy from the caller and write it back. */
3274 pEd->HeadP = (pEd->HeadP & ~ED_PTR_MASK) | ITdAddrNext;
3275 ohciR3WriteEd(pThis, EdAddr, pEd);
3276 }
3277
3278 /*
3279 * If it's in flight, just mark the URB as unlinked (there is only one ITD per URB atm).
3280 * Otherwise, retire it to the done queue with an error and cause a done line interrupt (?).
3281 */
3282 if (pUrb)
3283 {
3284 pUrb->pHci->fUnlinked = true;
3285 if (ohciR3HasUrbBeenCanceled(pThis, pUrb, pEd)) /* ensures the copy is correct (paranoia). */
3286 return false;
3287
3288 POHCIITD pITdCopy = ((POHCIITD)pUrb->paTds[0].TdCopy);
3289 pITd->NextTD = pITdCopy->NextTD &= ~ED_PTR_MASK;
3290 }
3291 else
3292 {
3293 pITd->HwInfo &= ~ITD_HWINFO_CC;
3294 pITd->HwInfo |= OHCI_CC_DATA_OVERRUN;
3295
3296 pITd->NextTD = pThis->done;
3297 pThis->done = ITdAddr;
3298
3299 pThis->dqic = 0;
3300 }
3301
3302 ohciR3WriteITd(pThis, ITdAddr, pITd, "ohciR3ServiceIsochronousTdUnlink");
3303 return true;
3304}
3305
3306
3307/**
3308 * A worker for ohciR3ServiceIsochronousEndpoint which submits the specified
3309 * TD.
3310 *
3311 * @returns true on success.
3312 * @returns false on failure to submit.
3313 * @param pThis The OHCI controller instance data.
3314 * @param pITd The transfer descriptor to service.
3315 * @param ITdAddr The address of the transfer descriptor in gues memory.
3316 * @param R The start packet (frame) relative to the start of frame in HwInfo.
3317 * @param pEd The OHCI endpoint descriptor.
3318 * @param EdAddr The endpoint descriptor address in guest memory.
3319 */
3320static bool ohciR3ServiceIsochronousTd(POHCI pThis, POHCIITD pITd, uint32_t ITdAddr, const unsigned R, PCOHCIED pEd, uint32_t EdAddr)
3321{
3322 /*
3323 * Determine the endpoint direction.
3324 */
3325 VUSBDIRECTION enmDir;
3326 switch (pEd->hwinfo & ED_HWINFO_DIR)
3327 {
3328 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3329 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3330 default:
3331 Log(("ohciR3ServiceIsochronousTd: Invalid direction!!!! Ed.hwdinfo=%#x\n", pEd->hwinfo));
3332 ohciR3RaiseUnrecoverableError(pThis, 5);
3333 return false;
3334 }
3335
3336 /*
3337 * Extract the packet sizes and calc the total URB size.
3338 */
3339 struct
3340 {
3341 uint16_t cb;
3342 uint16_t off;
3343 } aPkts[ITD_NUM_PSW];
3344
3345 /* first entry (R) */
3346 uint32_t cbTotal = 0;
3347 if (((uint32_t)pITd->aPSW[R] >> ITD_PSW_CC_SHIFT) < (OHCI_CC_NOT_ACCESSED_0 >> TD_HWINFO_CC_SHIFT))
3348 {
3349 Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, R, pITd->aPSW[R] >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
3350 pThis->intr_status |= OHCI_INTR_UNRECOVERABLE_ERROR;
3351 return false;
3352 }
3353 uint16_t offPrev = aPkts[0].off = (pITd->aPSW[R] & ITD_PSW_OFFSET);
3354
3355 /* R+1..cFrames */
3356 const unsigned cFrames = ((pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
3357 for (unsigned iR = R + 1; iR < cFrames; iR++)
3358 {
3359 const uint16_t PSW = pITd->aPSW[iR];
3360 const uint16_t off = aPkts[iR - R].off = (PSW & ITD_PSW_OFFSET);
3361 cbTotal += aPkts[iR - R - 1].cb = off - offPrev;
3362 if (off < offPrev)
3363 {
3364 Log(("ITdAddr=%RX32 PSW%d.offset=%#x < offPrev=%#x!\n", ITdAddr, iR, off, offPrev)); /* => Unrecoverable Error*/
3365 ohciR3RaiseUnrecoverableError(pThis, 6);
3366 return false;
3367 }
3368 if (((uint32_t)PSW >> ITD_PSW_CC_SHIFT) < (OHCI_CC_NOT_ACCESSED_0 >> TD_HWINFO_CC_SHIFT))
3369 {
3370 Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, iR, PSW >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
3371 ohciR3RaiseUnrecoverableError(pThis, 7);
3372 return false;
3373 }
3374 offPrev = off;
3375 }
3376
3377 /* calc offEnd and figure out the size of the last packet. */
3378 const uint32_t offEnd = (pITd->BE & 0xfff)
3379 + (((pITd->BE & ITD_BP0_MASK) != (pITd->BP0 & ITD_BP0_MASK)) << 12)
3380 + 1 /* BE is inclusive */;
3381 if (offEnd < offPrev)
3382 {
3383 Log(("ITdAddr=%RX32 offEnd=%#x < offPrev=%#x!\n", ITdAddr, offEnd, offPrev)); /* => Unrecoverable Error*/
3384 ohciR3RaiseUnrecoverableError(pThis, 8);
3385 return false;
3386 }
3387 cbTotal += aPkts[cFrames - 1 - R].cb = offEnd - offPrev;
3388 Assert(cbTotal <= 0x2000);
3389
3390 pThis->fIdle = false; /* Mark as active */
3391
3392 /*
3393 * Allocate and initialize a new URB.
3394 */
3395 PVUSBURB pUrb = VUSBIRhNewUrb(pThis->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, NULL,
3396 VUSBXFERTYPE_ISOC, enmDir, cbTotal, 1, NULL);
3397 if (!pUrb)
3398 /* retry later... */
3399 return false;
3400
3401 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3402 pUrb->fShortNotOk = false;
3403 pUrb->enmStatus = VUSBSTATUS_OK;
3404 pUrb->pHci->EdAddr = EdAddr;
3405 pUrb->pHci->cTds = 1;
3406 pUrb->pHci->fUnlinked = false;
3407 pUrb->pHci->u32FrameNo = pThis->HcFmNumber;
3408 pUrb->paTds[0].TdAddr = ITdAddr;
3409 AssertCompile(sizeof(pUrb->paTds[0].TdCopy) >= sizeof(*pITd));
3410 memcpy(pUrb->paTds[0].TdCopy, pITd, sizeof(*pITd));
3411# if 0 /* color the data */
3412 memset(pUrb->abData, 0xfe, cbTotal);
3413# endif
3414
3415 /* copy the data */
3416 if ( cbTotal
3417 && enmDir != VUSBDIRECTION_IN)
3418 {
3419 const uint32_t off0 = pITd->aPSW[R] & ITD_PSW_OFFSET;
3420 if (off0 < 0x1000)
3421 {
3422 if (offEnd > 0x1000)
3423 {
3424 /* both pages. */
3425 const unsigned cb0 = 0x1000 - off0;
3426 ohciR3PhysRead(pThis, (pITd->BP0 & ITD_BP0_MASK) + off0, &pUrb->abData[0], cb0);
3427 ohciR3PhysRead(pThis, pITd->BE & ITD_BP0_MASK, &pUrb->abData[cb0], offEnd & 0xfff);
3428 }
3429 else /* a portion of the 1st page. */
3430 ohciR3PhysRead(pThis, (pITd->BP0 & ITD_BP0_MASK) + off0, pUrb->abData, offEnd - off0);
3431 }
3432 else /* a portion of the 2nd page. */
3433 ohciR3PhysRead(pThis, (pITd->BE & UINT32_C(0xfffff000)) + (off0 & 0xfff), pUrb->abData, cbTotal);
3434 }
3435
3436 /* setup the packets */
3437 pUrb->cIsocPkts = cFrames - R;
3438 unsigned off = 0;
3439 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
3440 {
3441 pUrb->aIsocPkts[i].enmStatus = VUSBSTATUS_NOT_ACCESSED;
3442 pUrb->aIsocPkts[i].off = off;
3443 off += pUrb->aIsocPkts[i].cb = aPkts[i].cb;
3444 }
3445 Assert(off == cbTotal);
3446
3447 /*
3448 * Submit the URB.
3449 */
3450 ohciR3InFlightAddUrb(pThis, pUrb);
3451 Log(("%s: ohciR3ServiceIsochronousTd: submitting cbData=%#x cIsocPkts=%d EdAddr=%#010x TdAddr=%#010x SF=%#x (%#x)\n",
3452 pUrb->pszDesc, pUrb->cbData, pUrb->cIsocPkts, EdAddr, ITdAddr, pITd->HwInfo & ITD_HWINFO_SF, pThis->HcFmNumber));
3453 ohciR3Unlock(pThis);
3454 int rc = VUSBIRhSubmitUrb(pThis->RootHub.pIRhConn, pUrb, &pThis->RootHub.Led);
3455 ohciR3Lock(pThis);
3456 if (RT_SUCCESS(rc))
3457 return true;
3458
3459 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
3460 Log(("ohciR3ServiceIsochronousTd: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d ITdAddr0=%#010x - rc=%Rrc\n",
3461 pUrb, cbTotal, EdAddr, 1, ITdAddr, rc));
3462 ohciR3InFlightRemove(pThis, ITdAddr);
3463 VUSBIRhFreeUrb(pThis->RootHub.pIRhConn, pUrb);
3464 return false;
3465}
3466
3467
3468/**
3469 * Service an isochronous endpoint.
3470 */
3471static void ohciR3ServiceIsochronousEndpoint(POHCI pThis, POHCIED pEd, uint32_t EdAddr)
3472{
3473 /*
3474 * We currently process this as if the guest follows the interrupt end point chaining
3475 * hierarchy described in the documenation. This means that for an isochronous endpoint
3476 * with a 1 ms interval we expect to find in-flight TDs at the head of the list. We will
3477 * skip over all in-flight TDs which timeframe has been exceed. Those which aren't in
3478 * flight but which are too late will be retired (possibly out of order, but, we don't
3479 * care right now).
3480 *
3481 * When we reach a TD which still has a buffer which is due for take off, we will
3482 * stop iterating TDs. If it's in-flight, there isn't anything to be done. Otherwise
3483 * we will push it onto the runway for immediate take off. In this process we
3484 * might have to complete buffers which didn't make it on time, something which
3485 * complicates the kind of status info we need to keep around for the TD.
3486 *
3487 * Note: We're currently not making any attempt at reassembling ITDs into URBs.
3488 * However, this will become necessary because of EMT scheduling and guest
3489 * like linux using one TD for each frame (simple but inefficient for us).
3490 */
3491 OHCIITD ITd;
3492 uint32_t ITdAddr = pEd->HeadP & ED_PTR_MASK;
3493 uint32_t ITdAddrPrev = 0;
3494 uint32_t u32NextFrame = UINT32_MAX;
3495 const uint16_t u16CurFrame = pThis->HcFmNumber;
3496 for (;;)
3497 {
3498 /* check for end-of-chain. */
3499 if ( ITdAddr == (pEd->TailP & ED_PTR_MASK)
3500 || !ITdAddr)
3501 break;
3502
3503 /*
3504 * If isochronous endpoints are around, don't slow down the timer. Getting the timing right
3505 * is difficult enough as it is.
3506 */
3507 pThis->fIdle = false;
3508
3509 /*
3510 * Read the current ITD and check what we're supposed to do about it.
3511 */
3512 ohciR3ReadITd(pThis, ITdAddr, &ITd);
3513 const uint32_t ITdAddrNext = ITd.NextTD & ED_PTR_MASK;
3514 const int16_t R = u16CurFrame - (uint16_t)(ITd.HwInfo & ITD_HWINFO_SF); /* 4.3.2.3 */
3515 const int16_t cFrames = ((ITd.HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
3516
3517 if (R < cFrames)
3518 {
3519 /*
3520 * It's inside the current or a future launch window.
3521 *
3522 * We will try maximize the TD in flight here to deal with EMT scheduling
3523 * issues and similar stuff which will screw up the time. So, we will only
3524 * stop submitting TD when we reach a gap (in time) or end of the list.
3525 */
3526 if ( R < 0 /* (a future frame) */
3527 && (uint16_t)u32NextFrame != (uint16_t)(ITd.HwInfo & ITD_HWINFO_SF))
3528 break;
3529 if (ohciR3InFlightFind(pThis, ITdAddr) < 0)
3530 if (!ohciR3ServiceIsochronousTd(pThis, &ITd, ITdAddr, R < 0 ? 0 : R, pEd, EdAddr))
3531 break;
3532
3533 ITdAddrPrev = ITdAddr;
3534 }
3535 else
3536 {
3537# if 1
3538 /*
3539 * Ok, the launch window for this TD has passed.
3540 * If it's not in flight it should be retired with a DataOverrun status (TD).
3541 *
3542 * Don't remove in-flight TDs before they complete.
3543 * Windows will, upon the completion of another ITD it seems, check for if
3544 * any other TDs has been unlinked. If we unlink them before they really
3545 * complete all the packet status codes will be NotAccessed and Windows
3546 * will fail the URB with status USBD_STATUS_ISOCH_REQUEST_FAILED.
3547 *
3548 * I don't know if unlinking TDs out of order could cause similar problems,
3549 * time will show.
3550 */
3551 int iInFlight = ohciR3InFlightFind(pThis, ITdAddr);
3552 if (iInFlight >= 0)
3553 ITdAddrPrev = ITdAddr;
3554 else if (!ohciR3ServiceIsochronousTdUnlink(pThis, &ITd, ITdAddr, ITdAddrPrev,
3555 NULL, pEd, EdAddr))
3556 {
3557 Log(("ohciR3ServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
3558 break;
3559 }
3560# else /* BAD IDEA: */
3561 /*
3562 * Ok, the launch window for this TD has passed.
3563 * If it's not in flight it should be retired with a DataOverrun status (TD).
3564 *
3565 * If it's in flight we will try unlink it from the list prematurely to
3566 * help the guest to move on and shorten the list we have to walk. We currently
3567 * are successful with the first URB but then it goes too slowly...
3568 */
3569 int iInFlight = ohciR3InFlightFind(pThis, ITdAddr);
3570 if (!ohciR3ServiceIsochronousTdUnlink(pThis, &ITd, ITdAddr, ITdAddrPrev,
3571 iInFlight < 0 ? NULL : pThis->aInFlight[iInFlight].pUrb,
3572 pEd, EdAddr))
3573 {
3574 Log(("ohciR3ServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
3575 break;
3576 }
3577# endif
3578 }
3579
3580 /* advance to the next ITD */
3581 ITdAddr = ITdAddrNext;
3582 u32NextFrame = (ITd.HwInfo & ITD_HWINFO_SF) + cFrames;
3583 }
3584}
3585
3586
3587/**
3588 * Checks if a endpoints has TDs queued and is ready to have them processed.
3589 *
3590 * @returns true if it's ok to process TDs.
3591 * @param pEd The endpoint data.
3592 */
3593DECLINLINE(bool) ohciR3IsEdReady(PCOHCIED pEd)
3594{
3595 return (pEd->HeadP & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3596 && !(pEd->HeadP & ED_HEAD_HALTED)
3597 && !(pEd->hwinfo & ED_HWINFO_SKIP);
3598}
3599
3600
3601/**
3602 * Checks if an endpoint has TDs queued (not necessarily ready to have them processed).
3603 *
3604 * @returns true if endpoint may have TDs queued.
3605 * @param pEd The endpoint data.
3606 */
3607DECLINLINE(bool) ohciR3IsEdPresent(PCOHCIED pEd)
3608{
3609 return (pEd->HeadP & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3610 && !(pEd->HeadP & ED_HEAD_HALTED);
3611}
3612
3613
3614/**
3615 * Services the bulk list.
3616 *
3617 * On the bulk list we must reassemble URBs from multiple TDs using heuristics
3618 * derived from USB tracing done in the guests and guest source code (when available).
3619 */
3620static void ohciR3ServiceBulkList(POHCI pThis)
3621{
3622# ifdef LOG_ENABLED
3623 if (g_fLogBulkEPs)
3624 ohciR3DumpEdList(pThis, pThis->bulk_head, "Bulk before", true);
3625 if (pThis->bulk_cur)
3626 Log(("ohciR3ServiceBulkList: bulk_cur=%#010x before listprocessing!!! HCD have positioned us!!!\n", pThis->bulk_cur));
3627# endif
3628
3629 /*
3630 * ", HC will start processing the Bulk list and will set BF [BulkListFilled] to 0"
3631 * - We've simplified and are always starting at the head of the list and working
3632 * our way thru to the end each time.
3633 */
3634 pThis->status &= ~OHCI_STATUS_BLF;
3635 pThis->fBulkNeedsCleaning = false;
3636 pThis->bulk_cur = 0;
3637
3638 uint32_t EdAddr = pThis->bulk_head;
3639 while (EdAddr)
3640 {
3641 OHCIED Ed;
3642 ohciR3ReadEd(pThis, EdAddr, &Ed);
3643 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3644 if (ohciR3IsEdReady(&Ed))
3645 {
3646 pThis->status |= OHCI_STATUS_BLF;
3647 pThis->fBulkNeedsCleaning = true;
3648
3649# if 1
3650 /*
3651
3652 * After we figured out that all the TDs submitted for dealing with MSD
3653 * read/write data really makes up on single URB, and that we must
3654 * reassemble these TDs into an URB before submitting it, there is no
3655 * longer any need for servicing anything other than the head *URB*
3656 * on a bulk endpoint.
3657 */
3658 ohciR3ServiceHeadTdMultiple(pThis, VUSBXFERTYPE_BULK, &Ed, EdAddr, "Bulk");
3659# else
3660 /*
3661 * This alternative code was used before we started reassembling URBs from
3662 * multiple TDs. We keep it handy for debugging.
3663 */
3664 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3665 if (!ohciR3IsTdInFlight(pThis, TdAddr))
3666 {
3667 do
3668 {
3669 if (!ohciR3ServiceTdMultiple(pThis, VUSBXFERTYPE_BULK, &Ed, EdAddr, TdAddr, &TdAddr, "Bulk"))
3670 {
3671 LogFlow(("ohciR3ServiceBulkList: ohciR3ServiceTdMultiple -> false\n"));
3672 break;
3673 }
3674 if ( (TdAddr & ED_PTR_MASK) == (Ed.TailP & ED_PTR_MASK)
3675 || !TdAddr /* paranoia */)
3676 {
3677 LogFlow(("ohciR3ServiceBulkList: TdAddr=%#010RX32 Ed.TailP=%#010RX32\n", TdAddr, Ed.TailP));
3678 break;
3679 }
3680
3681 ohciR3ReadEd(pThis, EdAddr, &Ed); /* It might have been updated on URB completion. */
3682 } while (ohciR3IsEdReady(&Ed));
3683 }
3684# endif
3685 }
3686 else
3687 {
3688 if (Ed.hwinfo & ED_HWINFO_SKIP)
3689 {
3690 LogFlow(("ohciR3ServiceBulkList: Ed=%#010RX32 Ed.TailP=%#010RX32 SKIP\n", EdAddr, Ed.TailP));
3691 /* If the ED is in 'skip' state, no transactions on it are allowed and we must
3692 * cancel outstanding URBs, if any.
3693 */
3694 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3695 PVUSBURB pUrb = ohciR3TdInFlightUrb(pThis, TdAddr);
3696 if (pUrb)
3697 pThis->RootHub.pIRhConn->pfnCancelUrbsEp(pThis->RootHub.pIRhConn, pUrb);
3698 }
3699 }
3700
3701 /* next end point */
3702 EdAddr = Ed.NextED & ED_PTR_MASK;
3703
3704 }
3705
3706# ifdef LOG_ENABLED
3707 if (g_fLogBulkEPs)
3708 ohciR3DumpEdList(pThis, pThis->bulk_head, "Bulk after ", true);
3709# endif
3710}
3711
3712/**
3713 * Abort outstanding transfers on the bulk list.
3714 *
3715 * If the guest disabled bulk list processing, we must abort any outstanding transfers
3716 * (that is, cancel in-flight URBs associated with the list). This is required because
3717 * there may be outstanding read URBs that will never get a response from the device
3718 * and would block further communication.
3719 */
3720static void ohciR3UndoBulkList(POHCI pThis)
3721{
3722# ifdef LOG_ENABLED
3723 if (g_fLogBulkEPs)
3724 ohciR3DumpEdList(pThis, pThis->bulk_head, "Bulk before", true);
3725 if (pThis->bulk_cur)
3726 Log(("ohciR3UndoBulkList: bulk_cur=%#010x before list processing!!! HCD has positioned us!!!\n", pThis->bulk_cur));
3727# endif
3728
3729 /* This flag follows OHCI_STATUS_BLF, but BLF doesn't change when list processing is disabled. */
3730 pThis->fBulkNeedsCleaning = false;
3731
3732 uint32_t EdAddr = pThis->bulk_head;
3733 while (EdAddr)
3734 {
3735 OHCIED Ed;
3736
3737 ohciR3ReadEd(pThis, EdAddr, &Ed);
3738 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3739 if (ohciR3IsEdPresent(&Ed))
3740 {
3741 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3742 if (ohciR3IsTdInFlight(pThis, TdAddr))
3743 {
3744 LogFlow(("ohciR3UndoBulkList: Ed=%#010RX32 Ed.TailP=%#010RX32 UNDO\n", EdAddr, Ed.TailP));
3745 PVUSBURB pUrb = ohciR3TdInFlightUrb(pThis, TdAddr);
3746 if (pUrb)
3747 pThis->RootHub.pIRhConn->pfnCancelUrbsEp(pThis->RootHub.pIRhConn, pUrb);
3748 }
3749 }
3750 /* next endpoint */
3751 EdAddr = Ed.NextED & ED_PTR_MASK;
3752 }
3753}
3754
3755
3756/**
3757 * Services the control list.
3758 *
3759 * The control list has complex URB assembling, but that's taken
3760 * care of at VUSB level (unlike the other transfer types).
3761 */
3762static void ohciR3ServiceCtrlList(POHCI pThis)
3763{
3764# ifdef LOG_ENABLED
3765 if (g_fLogControlEPs)
3766 ohciR3DumpEdList(pThis, pThis->ctrl_head, "Ctrl before", true);
3767 if (pThis->ctrl_cur)
3768 Log(("ohciR3ServiceCtrlList: ctrl_cur=%010x before list processing!!! HCD have positioned us!!!\n", pThis->ctrl_cur));
3769# endif
3770
3771 /*
3772 * ", HC will start processing the list and will set ControlListFilled to 0"
3773 * - We've simplified and are always starting at the head of the list and working
3774 * our way thru to the end each time.
3775 */
3776 pThis->status &= ~OHCI_STATUS_CLF;
3777 pThis->ctrl_cur = 0;
3778
3779 uint32_t EdAddr = pThis->ctrl_head;
3780 while (EdAddr)
3781 {
3782 OHCIED Ed;
3783 ohciR3ReadEd(pThis, EdAddr, &Ed);
3784 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3785 if (ohciR3IsEdReady(&Ed))
3786 {
3787# if 1
3788 /*
3789 * Control TDs depends on order and stage. Only one can be in-flight
3790 * at any given time. OTOH, some stages are completed immediately,
3791 * so we process the list until we've got a head which is in-flight
3792 * or reach the end of the list.
3793 */
3794 do
3795 {
3796 if ( !ohciR3ServiceHeadTd(pThis, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control")
3797 || ohciR3IsTdInFlight(pThis, Ed.HeadP & ED_PTR_MASK))
3798 {
3799 pThis->status |= OHCI_STATUS_CLF;
3800 break;
3801 }
3802 ohciR3ReadEd(pThis, EdAddr, &Ed); /* It might have been updated on URB completion. */
3803 } while (ohciR3IsEdReady(&Ed));
3804# else
3805 /* Simplistic, for debugging. */
3806 ohciR3ServiceHeadTd(pThis, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control");
3807 pThis->status |= OHCI_STATUS_CLF;
3808# endif
3809 }
3810
3811 /* next end point */
3812 EdAddr = Ed.NextED & ED_PTR_MASK;
3813 }
3814
3815# ifdef LOG_ENABLED
3816 if (g_fLogControlEPs)
3817 ohciR3DumpEdList(pThis, pThis->ctrl_head, "Ctrl after ", true);
3818# endif
3819}
3820
3821
3822/**
3823 * Services the periodic list.
3824 *
3825 * On the interrupt portion of the periodic list we must reassemble URBs from multiple
3826 * TDs using heuristics derived from USB tracing done in the guests and guest source
3827 * code (when available).
3828 */
3829static void ohciR3ServicePeriodicList(POHCI pThis)
3830{
3831 /*
3832 * Read the list head from the HCCA.
3833 */
3834 const unsigned iList = pThis->HcFmNumber % OHCI_HCCA_NUM_INTR;
3835 uint32_t EdAddr;
3836 ohciR3GetDWords(pThis, pThis->hcca + iList * sizeof(EdAddr), &EdAddr, 1);
3837
3838# ifdef LOG_ENABLED
3839 const uint32_t EdAddrHead = EdAddr;
3840 if (g_fLogInterruptEPs)
3841 {
3842 char sz[48];
3843 RTStrPrintf(sz, sizeof(sz), "Int%02x before", iList);
3844 ohciR3DumpEdList(pThis, EdAddrHead, sz, true);
3845 }
3846# endif
3847
3848 /*
3849 * Iterate the endpoint list.
3850 */
3851 while (EdAddr)
3852 {
3853 OHCIED Ed;
3854
3855 ohciR3ReadEd(pThis, EdAddr, &Ed);
3856 if (ohciR3IsEdReady(&Ed))
3857 {
3858 /*
3859 * "There is no separate head pointer of isochronous transfers. The first
3860 * isochronous Endpoint Descriptor simply links to the last interrupt
3861 * Endpoint Descriptor."
3862 */
3863 if (!(Ed.hwinfo & ED_HWINFO_ISO))
3864 {
3865 /*
3866 * Presently we will only process the head URB on an interrupt endpoint.
3867 */
3868 ohciR3ServiceHeadTdMultiple(pThis, VUSBXFERTYPE_INTR, &Ed, EdAddr, "Periodic");
3869 }
3870 else if (pThis->ctl & OHCI_CTL_IE)
3871 {
3872 /*
3873 * Presently only the head ITD.
3874 */
3875 ohciR3ServiceIsochronousEndpoint(pThis, &Ed, EdAddr);
3876 }
3877 else
3878 break;
3879 }
3880 else
3881 {
3882 if (Ed.hwinfo & ED_HWINFO_SKIP)
3883 {
3884 Log3(("ohciR3ServicePeriodicList: Ed=%#010RX32 Ed.TailP=%#010RX32 SKIP\n", EdAddr, Ed.TailP));
3885 /* If the ED is in 'skip' state, no transactions on it are allowed and we must
3886 * cancel outstanding URBs, if any.
3887 */
3888 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3889 PVUSBURB pUrb = ohciR3TdInFlightUrb(pThis, TdAddr);
3890 if (pUrb)
3891 pThis->RootHub.pIRhConn->pfnCancelUrbsEp(pThis->RootHub.pIRhConn, pUrb);
3892 }
3893 }
3894 /* next end point */
3895 EdAddr = Ed.NextED & ED_PTR_MASK;
3896 }
3897
3898# ifdef LOG_ENABLED
3899 if (g_fLogInterruptEPs)
3900 {
3901 char sz[48];
3902 RTStrPrintf(sz, sizeof(sz), "Int%02x after ", iList);
3903 ohciR3DumpEdList(pThis, EdAddrHead, sz, true);
3904 }
3905# endif
3906}
3907
3908
3909/**
3910 * Update the HCCA.
3911 *
3912 * @param pThis The OHCI instance data.
3913 */
3914static void ohciR3UpdateHCCA(POHCI pThis)
3915{
3916 struct ohci_hcca hcca;
3917 ohciR3PhysRead(pThis, pThis->hcca + OHCI_HCCA_OFS, &hcca, sizeof(hcca));
3918
3919 hcca.frame = RT_H2LE_U16((uint16_t)pThis->HcFmNumber);
3920 hcca.pad = 0;
3921
3922 bool fWriteDoneHeadInterrupt = false;
3923 if ( pThis->dqic == 0
3924 && (pThis->intr_status & OHCI_INTR_WRITE_DONE_HEAD) == 0)
3925 {
3926 uint32_t done = pThis->done;
3927
3928 if (pThis->intr_status & ~( OHCI_INTR_MASTER_INTERRUPT_ENABLED | OHCI_INTR_OWNERSHIP_CHANGE
3929 | OHCI_INTR_WRITE_DONE_HEAD) )
3930 done |= 0x1;
3931
3932 hcca.done = RT_H2LE_U32(done);
3933 pThis->done = 0;
3934 pThis->dqic = 0x7;
3935
3936 Log(("ohci: Writeback Done (%#010x) on frame %#x (age %#x)\n", hcca.done,
3937 pThis->HcFmNumber, pThis->HcFmNumber - pThis->u32FmDoneQueueTail));
3938# ifdef LOG_ENABLED
3939 ohciR3DumpTdQueue(pThis, hcca.done & ED_PTR_MASK, "DoneQueue");
3940# endif
3941 Assert(RT_OFFSETOF(struct ohci_hcca, done) == 4);
3942# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
3943 ohciR3InDoneQueueZap(pThis);
3944# endif
3945 fWriteDoneHeadInterrupt = true;
3946 }
3947
3948 Log3(("ohci: Updating HCCA on frame %#x\n", pThis->HcFmNumber));
3949 ohciR3PhysWrite(pThis, pThis->hcca + OHCI_HCCA_OFS, (uint8_t *)&hcca, sizeof(hcca));
3950 if (fWriteDoneHeadInterrupt)
3951 ohciR3SetInterrupt(pThis, OHCI_INTR_WRITE_DONE_HEAD);
3952}
3953
3954
3955/**
3956 * Go over the in-flight URB list and cancel any URBs that are no longer in use.
3957 * This occurs when the host removes EDs or TDs from the lists and we don't notice
3958 * the sKip bit. Such URBs must be promptly canceled, otherwise there is a risk
3959 * they might "steal" data destined for another URB.
3960 */
3961static void ohciR3CancelOrphanedURBs(POHCI pThis)
3962{
3963 bool fValidHCCA = !( pThis->hcca >= OHCI_HCCA_MASK
3964 || pThis->hcca < ~OHCI_HCCA_MASK);
3965 unsigned i, cLeft;
3966 int j;
3967 uint32_t EdAddr;
3968 PVUSBURB pUrb;
3969
3970 /* If the HCCA is not currently valid, or there are no in-flight URBs,
3971 * there's nothing to do.
3972 */
3973 if (!fValidHCCA || !pThis->cInFlight)
3974 return;
3975
3976 /* Initially mark all in-flight URBs as inactive. */
3977 for (i = 0, cLeft = pThis->cInFlight; cLeft && i < RT_ELEMENTS(pThis->aInFlight); i++)
3978 {
3979 if (pThis->aInFlight[i].pUrb)
3980 {
3981 pThis->aInFlight[i].fInactive = true;
3982 cLeft--;
3983 }
3984 }
3985 Assert(cLeft == 0);
3986
3987# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
3988 /* Get hcca data to minimize calls to ohciR3GetDWords/PDMDevHlpPhysRead. */
3989 uint32_t au32HCCA[OHCI_HCCA_NUM_INTR];
3990 ohciR3GetDWords(pThis, pThis->hcca, au32HCCA, OHCI_HCCA_NUM_INTR);
3991# endif
3992
3993 /* Go over all bulk/control/interrupt endpoint lists; any URB found in these lists
3994 * is marked as active again.
3995 */
3996 for (i = 0; i < OHCI_HCCA_NUM_INTR + 2; i++)
3997 {
3998 switch (i)
3999 {
4000 case OHCI_HCCA_NUM_INTR:
4001 EdAddr = pThis->bulk_head;
4002 break;
4003 case OHCI_HCCA_NUM_INTR + 1:
4004 EdAddr = pThis->ctrl_head;
4005 break;
4006 default:
4007# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
4008 EdAddr = au32HCCA[i];
4009# else
4010 ohciR3GetDWords(pThis, pThis->hcca + i * sizeof(EdAddr), &EdAddr, 1);
4011# endif
4012 break;
4013 }
4014 while (EdAddr)
4015 {
4016 OHCIED Ed;
4017 OHCITD Td;
4018
4019 ohciR3ReadEd(pThis, EdAddr, &Ed);
4020 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
4021 uint32_t TailP = Ed.TailP & ED_PTR_MASK;
4022 unsigned k = 0;
4023 if ( !(Ed.hwinfo & ED_HWINFO_SKIP)
4024 && (TdAddr != TailP))
4025 {
4026# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
4027 ohciR3PhysReadCacheClear(pThis->pCacheTD);
4028# endif
4029 do
4030 {
4031 ohciR3ReadTd(pThis, TdAddr, &Td);
4032 j = ohciR3InFlightFind(pThis, TdAddr);
4033 if (j > -1)
4034 pThis->aInFlight[j].fInactive = false;
4035 TdAddr = Td.NextTD & ED_PTR_MASK;
4036 /* See #8125.
4037 * Sometimes the ED is changed by the guest between ohciR3ReadEd above and here.
4038 * Then the code reads TD pointed by the new TailP, which is not allowed.
4039 * Luckily Windows guests have Td.NextTD = 0 in the tail TD.
4040 * Also having a real TD at 0 is very unlikely.
4041 * So do not continue.
4042 */
4043 if (TdAddr == 0)
4044 break;
4045 /* Failsafe for temporarily looped lists. */
4046 if (++k == 128)
4047 break;
4048 } while (TdAddr != (Ed.TailP & ED_PTR_MASK));
4049 }
4050 EdAddr = Ed.NextED & ED_PTR_MASK;
4051 }
4052 }
4053
4054 /* In-flight URBs still marked as inactive are not used anymore and need
4055 * to be canceled.
4056 */
4057 for (i = 0, cLeft = pThis->cInFlight; cLeft && i < RT_ELEMENTS(pThis->aInFlight); i++)
4058 {
4059 if (pThis->aInFlight[i].pUrb)
4060 {
4061 cLeft--;
4062 pUrb = pThis->aInFlight[i].pUrb;
4063 if (pThis->aInFlight[i].fInactive
4064 && pUrb->enmState == VUSBURBSTATE_IN_FLIGHT
4065 && pUrb->enmType != VUSBXFERTYPE_CTRL)
4066 pThis->RootHub.pIRhConn->pfnCancelUrbsEp(pThis->RootHub.pIRhConn, pUrb);
4067 }
4068 }
4069 Assert(cLeft == 0);
4070}
4071
4072/**
4073 * Generate a Start-Of-Frame event, and set a timer for End-Of-Frame.
4074 */
4075static void ohciR3StartOfFrame(POHCI pThis)
4076{
4077# ifdef LOG_ENABLED
4078 const uint32_t status_old = pThis->status;
4079# endif
4080
4081 /*
4082 * Update HcFmRemaining.FRT and update start of frame time.
4083 */
4084 pThis->frt = pThis->fit;
4085 pThis->SofTime += pThis->cTicksPerFrame;
4086
4087 /*
4088 * Check that the HCCA address isn't bogus. Linux 2.4.x is known to start
4089 * the bus with a hcca of 0 to work around problem with a specific controller.
4090 */
4091 bool fValidHCCA = !( pThis->hcca >= OHCI_HCCA_MASK
4092 || pThis->hcca < ~OHCI_HCCA_MASK);
4093
4094# if 1
4095 /*
4096 * Update the HCCA.
4097 * Should be done after SOF but before HC read first ED in this frame.
4098 */
4099 if (fValidHCCA)
4100 ohciR3UpdateHCCA(pThis);
4101# endif
4102
4103 /* "After writing to HCCA, HC will set SF in HcInterruptStatus" - guest isn't executing, so ignore the order! */
4104 ohciR3SetInterrupt(pThis, OHCI_INTR_START_OF_FRAME);
4105
4106 if (pThis->fno)
4107 {
4108 ohciR3SetInterrupt(pThis, OHCI_INTR_FRAMENUMBER_OVERFLOW);
4109 pThis->fno = 0;
4110 }
4111
4112 /* If the HCCA address is invalid, we're quitting here to avoid doing something which cannot be reported to the HCD. */
4113 if (!fValidHCCA)
4114 {
4115 Log(("ohciR3StartOfFrame: skipping hcca part because hcca=%RX32 (our 'valid' range: %RX32-%RX32)\n",
4116 pThis->hcca, ~OHCI_HCCA_MASK, OHCI_HCCA_MASK));
4117 return;
4118 }
4119
4120 /*
4121 * Periodic EPs.
4122 */
4123 if (pThis->ctl & OHCI_CTL_PLE)
4124 ohciR3ServicePeriodicList(pThis);
4125
4126 /*
4127 * Control EPs.
4128 */
4129 if ( (pThis->ctl & OHCI_CTL_CLE)
4130 && (pThis->status & OHCI_STATUS_CLF) )
4131 ohciR3ServiceCtrlList(pThis);
4132
4133 /*
4134 * Bulk EPs.
4135 */
4136 if ( (pThis->ctl & OHCI_CTL_BLE)
4137 && (pThis->status & OHCI_STATUS_BLF))
4138 ohciR3ServiceBulkList(pThis);
4139 else if ((pThis->status & OHCI_STATUS_BLF)
4140 && pThis->fBulkNeedsCleaning)
4141 ohciR3UndoBulkList(pThis); /* If list disabled but not empty, abort endpoints. */
4142
4143# if 0
4144 /*
4145 * Update the HCCA after processing the lists and everything. A bit experimental.
4146 *
4147 * ASSUME the guest won't be very upset if a TD is completed, retired and handed
4148 * back immediately. The idea is to be able to retire the data and/or status stages
4149 * of a control transfer together with the setup stage, thus saving a frame. This
4150 * behaviour is should be perfectly ok, since the setup (and maybe data) stages
4151 * have already taken at least one frame to complete.
4152 *
4153 * But, when implementing the first synchronous virtual USB devices, we'll have to
4154 * verify that the guest doesn't choke when having a TD returned in the same frame
4155 * as it was submitted.
4156 */
4157 ohciR3UpdateHCCA(pThis);
4158# endif
4159
4160# ifdef LOG_ENABLED
4161 if (pThis->status ^ status_old)
4162 {
4163 uint32_t val = pThis->status;
4164 uint32_t chg = val ^ status_old; NOREF(chg);
4165 Log2(("ohciR3StartOfFrame: HcCommandStatus=%#010x: %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
4166 val,
4167 chg & RT_BIT(0) ? "*" : "", val & 1,
4168 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
4169 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4170 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4171 chg & (3<<16)? "*" : "", (val >> 16) & 3));
4172 }
4173# endif
4174}
4175
4176/**
4177 * Updates the HcFmNumber and FNO registers.
4178 */
4179static void ohciR3BumpFrameNumber(POHCI pThis)
4180{
4181 const uint16_t u16OldFmNumber = pThis->HcFmNumber++;
4182 if ((u16OldFmNumber ^ pThis->HcFmNumber) & RT_BIT(15))
4183 pThis->fno = 1;
4184}
4185
4186/**
4187 * Callback for periodic frame processing.
4188 */
4189static DECLCALLBACK(bool) ohciR3StartFrame(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameNo)
4190{
4191 RT_NOREF(u32FrameNo);
4192 POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
4193
4194 ohciR3Lock(pThis);
4195
4196 /* Reset idle detection flag */
4197 pThis->fIdle = true;
4198
4199# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
4200 physReadStatsReset(&g_PhysReadState);
4201# endif
4202
4203 if (!(pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR))
4204 {
4205 /* Frame boundary, so do EOF stuff here. */
4206 ohciR3BumpFrameNumber(pThis);
4207 if ( (pThis->dqic != 0x7) && (pThis->dqic != 0))
4208 pThis->dqic--;
4209
4210 /* Clean up any URBs that have been removed. */
4211 ohciR3CancelOrphanedURBs(pThis);
4212
4213 /* Start the next frame. */
4214 ohciR3StartOfFrame(pThis);
4215 }
4216
4217# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
4218 physReadStatsPrint(&g_PhysReadState);
4219# endif
4220
4221 ohciR3Unlock(pThis);
4222 return pThis->fIdle;
4223}
4224
4225/** @interface_method_impl{VUSBIROOTHUBPORT,pfnFrameRateChanged} */
4226static DECLCALLBACK(void) ohciR3FrameRateChanged(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameRate)
4227{
4228 POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
4229
4230 Assert(u32FrameRate <= OHCI_DEFAULT_TIMER_FREQ);
4231
4232 pThis->cTicksPerFrame = pThis->u64TimerHz / u32FrameRate;
4233 if (!pThis->cTicksPerFrame)
4234 pThis->cTicksPerFrame = 1;
4235 pThis->cTicksPerUsbTick = pThis->u64TimerHz >= VUSB_BUS_HZ ? pThis->u64TimerHz / VUSB_BUS_HZ : 1;
4236}
4237
4238/**
4239 * Do frame processing on frame boundary
4240 */
4241static DECLCALLBACK(void) ohciR3FrameBoundaryTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
4242{
4243 RT_NOREF(pDevIns, pTimer, pvUser);
4244}
4245
4246/**
4247 * Start sending SOF tokens across the USB bus, lists are processed in
4248 * next frame
4249 */
4250static void ohciR3BusStart(POHCI pThis)
4251{
4252 VUSBIDevPowerOn(pThis->RootHub.pIDev);
4253 pThis->dqic = 0x7;
4254
4255 Log(("ohci: Bus started\n"));
4256
4257 pThis->SofTime = PDMDevHlpTMTimeVirtGet(pThis->CTX_SUFF(pDevIns));
4258 int rc = pThis->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThis->RootHub.pIRhConn, OHCI_DEFAULT_TIMER_FREQ);
4259 AssertRC(rc);
4260}
4261
4262/**
4263 * Stop sending SOF tokens on the bus
4264 */
4265static void ohciR3BusStop(POHCI pThis)
4266{
4267 int rc = pThis->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThis->RootHub.pIRhConn, 0);
4268 AssertRC(rc);
4269 VUSBIDevPowerOff(pThis->RootHub.pIDev);
4270}
4271
4272/**
4273 * Move in to resume state
4274 */
4275static void ohciR3BusResume(POHCI pThis, bool fHardware)
4276{
4277 pThis->ctl &= ~OHCI_CTL_HCFS;
4278 pThis->ctl |= OHCI_USB_RESUME;
4279
4280 LogFunc(("fHardware=%RTbool RWE=%s\n",
4281 fHardware, (pThis->ctl & OHCI_CTL_RWE) ? "on" : "off"));
4282
4283 if (fHardware && (pThis->ctl & OHCI_CTL_RWE))
4284 ohciR3SetInterrupt(pThis, OHCI_INTR_RESUME_DETECT);
4285
4286 ohciR3BusStart(pThis);
4287}
4288
4289
4290/* Power a port up or down */
4291static void ohciR3RhPortPower(POHCIROOTHUB pRh, unsigned iPort, bool fPowerUp)
4292{
4293 POHCIHUBPORT pPort = &pRh->aPorts[iPort];
4294 bool fOldPPS = !!(pPort->fReg & OHCI_PORT_PPS);
4295
4296 LogFlowFunc(("iPort=%u fPowerUp=%RTbool\n", iPort, fPowerUp));
4297
4298 if (fPowerUp)
4299 {
4300 /* power up */
4301 if (pPort->pDev)
4302 pPort->fReg |= OHCI_PORT_CCS;
4303 if (pPort->fReg & OHCI_PORT_CCS)
4304 pPort->fReg |= OHCI_PORT_PPS;
4305 if (pPort->pDev && !fOldPPS)
4306 VUSBIDevPowerOn(pPort->pDev);
4307 }
4308 else
4309 {
4310 /* power down */
4311 pPort->fReg &= ~(OHCI_PORT_PPS | OHCI_PORT_CCS | OHCI_PORT_PSS | OHCI_PORT_PRS);
4312 if (pPort->pDev && fOldPPS)
4313 VUSBIDevPowerOff(pPort->pDev);
4314 }
4315}
4316
4317#endif /* IN_RING3 */
4318
4319/**
4320 * Read the HcRevision register.
4321 */
4322static int HcRevision_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4323{
4324 RT_NOREF2(pThis, iReg);
4325 Log2(("HcRevision_r() -> 0x10\n"));
4326 *pu32Value = 0x10; /* OHCI revision 1.0, no emulation. */
4327 return VINF_SUCCESS;
4328}
4329
4330/**
4331 * Write to the HcRevision register.
4332 */
4333static int HcRevision_w(POHCI pThis, uint32_t iReg, uint32_t u32Value)
4334{
4335 RT_NOREF3(pThis, iReg, u32Value);
4336 Log2(("HcRevision_w(%#010x) - denied\n", u32Value));
4337 AssertMsgFailed(("Invalid operation!!! u32Value=%#010x\n", u32Value));
4338 return VINF_SUCCESS;
4339}
4340
4341/**
4342 * Read the HcControl register.
4343 */
4344static int HcControl_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4345{
4346 RT_NOREF1(iReg);
4347 uint32_t ctl = pThis->ctl;
4348 Log2(("HcControl_r -> %#010x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
4349 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
4350 (ctl >> 9) & 1, (ctl >> 10) & 1));
4351 *pu32Value = ctl;
4352 return VINF_SUCCESS;
4353}
4354
4355/**
4356 * Write the HcControl register.
4357 */
4358static int HcControl_w(POHCI pThis, uint32_t iReg, uint32_t val)
4359{
4360 RT_NOREF1(iReg);
4361
4362 /* log it. */
4363 uint32_t chg = pThis->ctl ^ val; NOREF(chg);
4364 Log2(("HcControl_w(%#010x) => %sCBSR=%d %sPLE=%d %sIE=%d %sCLE=%d %sBLE=%d %sHCFS=%#x %sIR=%d %sRWC=%d %sRWE=%d\n",
4365 val,
4366 chg & 3 ? "*" : "", val & 3,
4367 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4368 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4369 chg & RT_BIT(4) ? "*" : "", (val >> 4) & 1,
4370 chg & RT_BIT(5) ? "*" : "", (val >> 5) & 1,
4371 chg & (3 << 6)? "*" : "", (val >> 6) & 3,
4372 chg & RT_BIT(8) ? "*" : "", (val >> 8) & 1,
4373 chg & RT_BIT(9) ? "*" : "", (val >> 9) & 1,
4374 chg & RT_BIT(10) ? "*" : "", (val >> 10) & 1));
4375 if (val & ~0x07ff)
4376 Log2(("Unknown bits %#x are set!!!\n", val & ~0x07ff));
4377
4378 /* see what changed and take action on that. */
4379 uint32_t old_state = pThis->ctl & OHCI_CTL_HCFS;
4380 uint32_t new_state = val & OHCI_CTL_HCFS;
4381
4382#ifdef IN_RING3
4383 pThis->ctl = val;
4384 if (new_state != old_state)
4385 {
4386 switch (new_state)
4387 {
4388 case OHCI_USB_OPERATIONAL:
4389 LogRel(("OHCI: USB Operational\n"));
4390 ohciR3BusStart(pThis);
4391 break;
4392 case OHCI_USB_SUSPEND:
4393 ohciR3BusStop(pThis);
4394 LogRel(("OHCI: USB Suspended\n"));
4395 break;
4396 case OHCI_USB_RESUME:
4397 LogRel(("OHCI: USB Resume\n"));
4398 ohciR3BusResume(pThis, false /* not hardware */);
4399 break;
4400 case OHCI_USB_RESET:
4401 {
4402 LogRel(("OHCI: USB Reset\n"));
4403 ohciR3BusStop(pThis);
4404 /** @todo This should probably do a real reset, but we don't implement
4405 * that correctly in the roothub reset callback yet. check it's
4406 * comments and argument for more details. */
4407 VUSBIDevReset(pThis->RootHub.pIDev, false /* don't do a real reset */, NULL, NULL, NULL);
4408 break;
4409 }
4410 }
4411 }
4412#else /* !IN_RING3 */
4413 if ( new_state != old_state )
4414 {
4415 Log2(("HcControl_w: state changed -> VINF_IOM_R3_MMIO_WRITE\n"));
4416 return VINF_IOM_R3_MMIO_WRITE;
4417 }
4418 pThis->ctl = val;
4419#endif /* !IN_RING3 */
4420
4421 return VINF_SUCCESS;
4422}
4423
4424/**
4425 * Read the HcCommandStatus register.
4426 */
4427static int HcCommandStatus_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4428{
4429 uint32_t status = pThis->status;
4430 Log2(("HcCommandStatus_r() -> %#010x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
4431 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3));
4432 *pu32Value = status;
4433 RT_NOREF1(iReg);
4434 return VINF_SUCCESS;
4435}
4436
4437/**
4438 * Write to the HcCommandStatus register.
4439 */
4440static int HcCommandStatus_w(POHCI pThis, uint32_t iReg, uint32_t val)
4441{
4442 RT_NOREF1(iReg);
4443
4444 /* log */
4445 uint32_t chg = pThis->status ^ val; NOREF(chg);
4446 Log2(("HcCommandStatus_w(%#010x) => %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
4447 val,
4448 chg & RT_BIT(0) ? "*" : "", val & 1,
4449 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
4450 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4451 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4452 chg & (3<<16)? "!!!":"", (pThis->status >> 16) & 3));
4453 if (val & ~0x0003000f)
4454 Log2(("Unknown bits %#x are set!!!\n", val & ~0x0003000f));
4455
4456 /* SOC is read-only */
4457 val = (val & ~OHCI_STATUS_SOC);
4458
4459#ifdef IN_RING3
4460 /* "bits written as '0' remain unchanged in the register" */
4461 pThis->status |= val;
4462 if (pThis->status & OHCI_STATUS_HCR)
4463 {
4464 LogRel(("OHCI: Software reset\n"));
4465 ohciR3DoReset(pThis, OHCI_USB_SUSPEND, false /* N/A */);
4466 }
4467#else
4468 if ((pThis->status | val) & OHCI_STATUS_HCR)
4469 {
4470 LogFlow(("HcCommandStatus_w: reset -> VINF_IOM_R3_MMIO_WRITE\n"));
4471 return VINF_IOM_R3_MMIO_WRITE;
4472 }
4473 pThis->status |= val;
4474#endif
4475 return VINF_SUCCESS;
4476}
4477
4478/**
4479 * Read the HcInterruptStatus register.
4480 */
4481static int HcInterruptStatus_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4482{
4483 uint32_t val = pThis->intr_status;
4484 Log2(("HcInterruptStatus_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
4485 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4486 (val >> 6) & 1, (val >> 30) & 1));
4487 *pu32Value = val;
4488 RT_NOREF1(iReg);
4489 return VINF_SUCCESS;
4490}
4491
4492/**
4493 * Write to the HcInterruptStatus register.
4494 */
4495static int HcInterruptStatus_w(POHCI pThis, uint32_t iReg, uint32_t val)
4496{
4497 RT_NOREF1(iReg);
4498
4499 uint32_t res = pThis->intr_status & ~val;
4500 uint32_t chg = pThis->intr_status ^ res; NOREF(chg);
4501
4502 int rc = PDMCritSectEnter(&pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4503 if (rc != VINF_SUCCESS)
4504 return rc;
4505
4506 Log2(("HcInterruptStatus_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d\n",
4507 val,
4508 chg & RT_BIT(0) ? "*" : "", res & 1,
4509 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4510 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4511 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4512 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4513 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4514 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4515 chg & RT_BIT(30)? "*" : "", (res >> 30) & 1));
4516 if ( (val & ~0xc000007f)
4517 && val != 0xffffffff /* ignore clear-all-like requests from xp. */)
4518 Log2(("Unknown bits %#x are set!!!\n", val & ~0xc000007f));
4519
4520 /* "The Host Controller Driver may clear specific bits in this
4521 * register by writing '1' to bit positions to be cleared"
4522 */
4523 pThis->intr_status &= ~val;
4524 ohciUpdateInterruptLocked(pThis, "HcInterruptStatus_w");
4525 PDMCritSectLeave(&pThis->CsIrq);
4526 return VINF_SUCCESS;
4527}
4528
4529/**
4530 * Read the HcInterruptEnable register
4531 */
4532static int HcInterruptEnable_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4533{
4534 uint32_t val = pThis->intr;
4535 Log2(("HcInterruptEnable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
4536 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4537 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
4538 *pu32Value = val;
4539 RT_NOREF1(iReg);
4540 return VINF_SUCCESS;
4541}
4542
4543/**
4544 * Writes to the HcInterruptEnable register.
4545 */
4546static int HcInterruptEnable_w(POHCI pThis, uint32_t iReg, uint32_t val)
4547{
4548 RT_NOREF1(iReg);
4549 uint32_t res = pThis->intr | val;
4550 uint32_t chg = pThis->intr ^ res; NOREF(chg);
4551
4552 int rc = PDMCritSectEnter(&pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4553 if (rc != VINF_SUCCESS)
4554 return rc;
4555
4556 Log2(("HcInterruptEnable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
4557 val,
4558 chg & RT_BIT(0) ? "*" : "", res & 1,
4559 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4560 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4561 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4562 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4563 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4564 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4565 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
4566 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
4567 if (val & ~0xc000007f)
4568 Log2(("Uknown bits %#x are set!!!\n", val & ~0xc000007f));
4569
4570 pThis->intr |= val;
4571 ohciUpdateInterruptLocked(pThis, "HcInterruptEnable_w");
4572 PDMCritSectLeave(&pThis->CsIrq);
4573 return VINF_SUCCESS;
4574}
4575
4576/**
4577 * Reads the HcInterruptDisable register.
4578 */
4579static int HcInterruptDisable_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4580{
4581#if 1 /** @todo r=bird: "On read, the current value of the HcInterruptEnable register is returned." */
4582 uint32_t val = pThis->intr;
4583#else /* old code. */
4584 uint32_t val = ~pThis->intr;
4585#endif
4586 Log2(("HcInterruptDisable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
4587 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4588 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
4589
4590 *pu32Value = val;
4591 RT_NOREF1(iReg);
4592 return VINF_SUCCESS;
4593}
4594
4595/**
4596 * Writes to the HcInterruptDisable register.
4597 */
4598static int HcInterruptDisable_w(POHCI pThis, uint32_t iReg, uint32_t val)
4599{
4600 RT_NOREF1(iReg);
4601 uint32_t res = pThis->intr & ~val;
4602 uint32_t chg = pThis->intr ^ res; NOREF(chg);
4603
4604 int rc = PDMCritSectEnter(&pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4605 if (rc != VINF_SUCCESS)
4606 return rc;
4607
4608 Log2(("HcInterruptDisable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
4609 val,
4610 chg & RT_BIT(0) ? "*" : "", res & 1,
4611 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4612 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4613 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4614 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4615 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4616 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4617 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
4618 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
4619 /* Don't bitch about invalid bits here since it makes sense to disable
4620 * interrupts you don't know about. */
4621
4622 pThis->intr &= ~val;
4623 ohciUpdateInterruptLocked(pThis, "HcInterruptDisable_w");
4624 PDMCritSectLeave(&pThis->CsIrq);
4625 return VINF_SUCCESS;
4626}
4627
4628/**
4629 * Read the HcHCCA register (Host Controller Communications Area physical address).
4630 */
4631static int HcHCCA_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4632{
4633 Log2(("HcHCCA_r() -> %#010x\n", pThis->hcca));
4634 *pu32Value = pThis->hcca;
4635 RT_NOREF1(iReg);
4636 return VINF_SUCCESS;
4637}
4638
4639/**
4640 * Write to the HcHCCA register (Host Controller Communications Area physical address).
4641 */
4642static int HcHCCA_w(POHCI pThis, uint32_t iReg, uint32_t Value)
4643{
4644 Log2(("HcHCCA_w(%#010x) - old=%#010x new=%#010x\n", Value, pThis->hcca, Value & OHCI_HCCA_MASK));
4645 pThis->hcca = Value & OHCI_HCCA_MASK;
4646 RT_NOREF1(iReg);
4647 return VINF_SUCCESS;
4648}
4649
4650/**
4651 * Read the HcPeriodCurrentED register.
4652 */
4653static int HcPeriodCurrentED_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4654{
4655 Log2(("HcPeriodCurrentED_r() -> %#010x\n", pThis->per_cur));
4656 *pu32Value = pThis->per_cur;
4657 RT_NOREF1(iReg);
4658 return VINF_SUCCESS;
4659}
4660
4661/**
4662 * Write to the HcPeriodCurrentED register.
4663 */
4664static int HcPeriodCurrentED_w(POHCI pThis, uint32_t iReg, uint32_t val)
4665{
4666 Log(("HcPeriodCurrentED_w(%#010x) - old=%#010x new=%#010x (This is a read only register, only the linux guys don't respect that!)\n",
4667 val, pThis->per_cur, val & ~7));
4668 //AssertMsgFailed(("HCD (Host Controller Driver) should not write to HcPeriodCurrentED! val=%#010x (old=%#010x)\n", val, pThis->per_cur));
4669 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4670 pThis->per_cur = val & ~7;
4671 RT_NOREF1(iReg);
4672 return VINF_SUCCESS;
4673}
4674
4675/**
4676 * Read the HcControlHeadED register.
4677 */
4678static int HcControlHeadED_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4679{
4680 Log2(("HcControlHeadED_r() -> %#010x\n", pThis->ctrl_head));
4681 *pu32Value = pThis->ctrl_head;
4682 RT_NOREF1(iReg);
4683 return VINF_SUCCESS;
4684}
4685
4686/**
4687 * Write to the HcControlHeadED register.
4688 */
4689static int HcControlHeadED_w(POHCI pThis, uint32_t iReg, uint32_t val)
4690{
4691 Log2(("HcControlHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->ctrl_head, val & ~7));
4692 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4693 pThis->ctrl_head = val & ~7;
4694 RT_NOREF1(iReg);
4695 return VINF_SUCCESS;
4696}
4697
4698/**
4699 * Read the HcControlCurrentED register.
4700 */
4701static int HcControlCurrentED_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4702{
4703 Log2(("HcControlCurrentED_r() -> %#010x\n", pThis->ctrl_cur));
4704 *pu32Value = pThis->ctrl_cur;
4705 RT_NOREF1(iReg);
4706 return VINF_SUCCESS;
4707}
4708
4709/**
4710 * Write to the HcControlCurrentED register.
4711 */
4712static int HcControlCurrentED_w(POHCI pThis, uint32_t iReg, uint32_t val)
4713{
4714 Log2(("HcControlCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->ctrl_cur, val & ~7));
4715 AssertMsg(!(pThis->ctl & OHCI_CTL_CLE), ("Illegal write! HcControl.ControlListEnabled is set! val=%#010x\n", val));
4716 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4717 pThis->ctrl_cur = val & ~7;
4718 RT_NOREF1(iReg);
4719 return VINF_SUCCESS;
4720}
4721
4722/**
4723 * Read the HcBulkHeadED register.
4724 */
4725static int HcBulkHeadED_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4726{
4727 Log2(("HcBulkHeadED_r() -> %#010x\n", pThis->bulk_head));
4728 *pu32Value = pThis->bulk_head;
4729 RT_NOREF1(iReg);
4730 return VINF_SUCCESS;
4731}
4732
4733/**
4734 * Write to the HcBulkHeadED register.
4735 */
4736static int HcBulkHeadED_w(POHCI pThis, uint32_t iReg, uint32_t val)
4737{
4738 Log2(("HcBulkHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->bulk_head, val & ~7));
4739 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4740 pThis->bulk_head = val & ~7; /** @todo The ATI OHCI controller on my machine enforces 16-byte address alignment. */
4741 RT_NOREF1(iReg);
4742 return VINF_SUCCESS;
4743}
4744
4745/**
4746 * Read the HcBulkCurrentED register.
4747 */
4748static int HcBulkCurrentED_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4749{
4750 Log2(("HcBulkCurrentED_r() -> %#010x\n", pThis->bulk_cur));
4751 *pu32Value = pThis->bulk_cur;
4752 RT_NOREF1(iReg);
4753 return VINF_SUCCESS;
4754}
4755
4756/**
4757 * Write to the HcBulkCurrentED register.
4758 */
4759static int HcBulkCurrentED_w(POHCI pThis, uint32_t iReg, uint32_t val)
4760{
4761 Log2(("HcBulkCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->bulk_cur, val & ~7));
4762 AssertMsg(!(pThis->ctl & OHCI_CTL_BLE), ("Illegal write! HcControl.BulkListEnabled is set! val=%#010x\n", val));
4763 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4764 pThis->bulk_cur = val & ~7;
4765 RT_NOREF1(iReg);
4766 return VINF_SUCCESS;
4767}
4768
4769
4770/**
4771 * Read the HcDoneHead register.
4772 */
4773static int HcDoneHead_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4774{
4775 Log2(("HcDoneHead_r() -> 0x%#08x\n", pThis->done));
4776 *pu32Value = pThis->done;
4777 RT_NOREF1(iReg);
4778 return VINF_SUCCESS;
4779}
4780
4781/**
4782 * Write to the HcDoneHead register.
4783 */
4784static int HcDoneHead_w(POHCI pThis, uint32_t iReg, uint32_t val)
4785{
4786 RT_NOREF3(pThis, iReg, val);
4787 Log2(("HcDoneHead_w(0x%#08x) - denied!!!\n", val));
4788 /*AssertMsgFailed(("Illegal operation!!! val=%#010x\n", val)); - OS/2 does this */
4789 return VINF_SUCCESS;
4790}
4791
4792
4793/**
4794 * Read the HcFmInterval (Fm=Frame) register.
4795 */
4796static int HcFmInterval_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4797{
4798 uint32_t val = (pThis->fit << 31) | (pThis->fsmps << 16) | (pThis->fi);
4799 Log2(("HcFmInterval_r() -> 0x%#08x - FI=%d FSMPS=%d FIT=%d\n",
4800 val, val & 0x3fff, (val >> 16) & 0x7fff, val >> 31));
4801 *pu32Value = val;
4802 RT_NOREF1(iReg);
4803 return VINF_SUCCESS;
4804}
4805
4806/**
4807 * Write to the HcFmInterval (Fm = Frame) register.
4808 */
4809static int HcFmInterval_w(POHCI pThis, uint32_t iReg, uint32_t val)
4810{
4811 RT_NOREF1(iReg);
4812
4813 /* log */
4814 uint32_t chg = val ^ ((pThis->fit << 31) | (pThis->fsmps << 16) | pThis->fi); NOREF(chg);
4815 Log2(("HcFmInterval_w(%#010x) => %sFI=%d %sFSMPS=%d %sFIT=%d\n",
4816 val,
4817 chg & 0x00003fff ? "*" : "", val & 0x3fff,
4818 chg & 0x7fff0000 ? "*" : "", (val >> 16) & 0x7fff,
4819 chg >> 31 ? "*" : "", (val >> 31) & 1));
4820 if ( pThis->fi != (val & OHCI_FMI_FI) )
4821 {
4822 Log(("ohci: FrameInterval: %#010x -> %#010x\n", pThis->fi, val & OHCI_FMI_FI));
4823 AssertMsg(pThis->fit != ((val >> OHCI_FMI_FIT_SHIFT) & 1), ("HCD didn't toggle the FIT bit!!!\n"));
4824 }
4825
4826 /* update */
4827 pThis->fi = val & OHCI_FMI_FI;
4828 pThis->fit = (val & OHCI_FMI_FIT) >> OHCI_FMI_FIT_SHIFT;
4829 pThis->fsmps = (val & OHCI_FMI_FSMPS) >> OHCI_FMI_FSMPS_SHIFT;
4830 return VINF_SUCCESS;
4831}
4832
4833/**
4834 * Read the HcFmRemaining (Fm = Frame) register.
4835 */
4836static int HcFmRemaining_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4837{
4838 RT_NOREF1(iReg);
4839 uint32_t Value = pThis->frt << 31;
4840 if ((pThis->ctl & OHCI_CTL_HCFS) == OHCI_USB_OPERATIONAL)
4841 {
4842 /*
4843 * Being in USB operational state guarantees SofTime was set already.
4844 */
4845 uint64_t tks = PDMDevHlpTMTimeVirtGet(pThis->CTX_SUFF(pDevIns)) - pThis->SofTime;
4846 if (tks < pThis->cTicksPerFrame) /* avoid muldiv if possible */
4847 {
4848 uint16_t fr;
4849 tks = ASMMultU64ByU32DivByU32(1, tks, pThis->cTicksPerUsbTick);
4850 fr = (uint16_t)(pThis->fi - tks);
4851 Value |= fr;
4852 }
4853 }
4854
4855 Log2(("HcFmRemaining_r() -> %#010x - FR=%d FRT=%d\n", Value, Value & 0x3fff, Value >> 31));
4856 *pu32Value = Value;
4857 return VINF_SUCCESS;
4858}
4859
4860/**
4861 * Write to the HcFmRemaining (Fm = Frame) register.
4862 */
4863static int HcFmRemaining_w(POHCI pThis, uint32_t iReg, uint32_t val)
4864{
4865 RT_NOREF3(pThis, iReg, val);
4866 Log2(("HcFmRemaining_w(%#010x) - denied\n", val));
4867 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
4868 return VINF_SUCCESS;
4869}
4870
4871/**
4872 * Read the HcFmNumber (Fm = Frame) register.
4873 */
4874static int HcFmNumber_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4875{
4876 RT_NOREF1(iReg);
4877 uint32_t val = (uint16_t)pThis->HcFmNumber;
4878 Log2(("HcFmNumber_r() -> %#010x - FN=%#x(%d) (32-bit=%#x(%d))\n", val, val, val, pThis->HcFmNumber, pThis->HcFmNumber));
4879 *pu32Value = val;
4880 return VINF_SUCCESS;
4881}
4882
4883/**
4884 * Write to the HcFmNumber (Fm = Frame) register.
4885 */
4886static int HcFmNumber_w(POHCI pThis, uint32_t iReg, uint32_t val)
4887{
4888 RT_NOREF3(pThis, iReg, val);
4889 Log2(("HcFmNumber_w(%#010x) - denied\n", val));
4890 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
4891 return VINF_SUCCESS;
4892}
4893
4894/**
4895 * Read the HcPeriodicStart register.
4896 * The register determines when in a frame to switch from control&bulk to periodic lists.
4897 */
4898static int HcPeriodicStart_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4899{
4900 RT_NOREF1(iReg);
4901 Log2(("HcPeriodicStart_r() -> %#010x - PS=%d\n", pThis->pstart, pThis->pstart & 0x3fff));
4902 *pu32Value = pThis->pstart;
4903 return VINF_SUCCESS;
4904}
4905
4906/**
4907 * Write to the HcPeriodicStart register.
4908 * The register determines when in a frame to switch from control&bulk to periodic lists.
4909 */
4910static int HcPeriodicStart_w(POHCI pThis, uint32_t iReg, uint32_t val)
4911{
4912 RT_NOREF1(iReg);
4913 Log2(("HcPeriodicStart_w(%#010x) => PS=%d\n", val, val & 0x3fff));
4914 if (val & ~0x3fff)
4915 Log2(("Unknown bits %#x are set!!!\n", val & ~0x3fff));
4916 pThis->pstart = val; /** @todo r=bird: should we support setting the other bits? */
4917 return VINF_SUCCESS;
4918}
4919
4920/**
4921 * Read the HcLSThreshold register.
4922 */
4923static int HcLSThreshold_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4924{
4925 RT_NOREF2(pThis, iReg);
4926 Log2(("HcLSThreshold_r() -> %#010x\n", OHCI_LS_THRESH));
4927 *pu32Value = OHCI_LS_THRESH;
4928 return VINF_SUCCESS;
4929}
4930
4931/**
4932 * Write to the HcLSThreshold register.
4933 *
4934 * Docs are inconsistent here:
4935 *
4936 * "Neither the Host Controller nor the Host Controller Driver are allowed to change this value."
4937 *
4938 * "This value is calculated by HCD with the consideration of transmission and setup overhead."
4939 *
4940 * The register is marked "R/W" the HCD column.
4941 *
4942 */
4943static int HcLSThreshold_w(POHCI pThis, uint32_t iReg, uint32_t val)
4944{
4945 RT_NOREF3(pThis, iReg, val);
4946 Log2(("HcLSThreshold_w(%#010x) => LST=0x%03x(%d)\n", val, val & 0x0fff, val & 0x0fff));
4947 AssertMsg(val == OHCI_LS_THRESH,
4948 ("HCD tried to write bad LS threshold: 0x%x (see function header)\n", val));
4949 /** @todo the HCD can change this. */
4950 return VINF_SUCCESS;
4951}
4952
4953/**
4954 * Read the HcRhDescriptorA register.
4955 */
4956static int HcRhDescriptorA_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4957{
4958 RT_NOREF1(iReg);
4959 uint32_t val = pThis->RootHub.desc_a;
4960#if 0 /* annoying */
4961 Log2(("HcRhDescriptorA_r() -> %#010x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTGT=%#x\n",
4962 val, val & 0xff, (val >> 8) & 1, (val >> 9) & 1, (val >> 10) & 1, (val >> 11) & 1,
4963 (val >> 12) & 1, (val >> 24) & 0xff));
4964#endif
4965 *pu32Value = val;
4966 return VINF_SUCCESS;
4967}
4968
4969/**
4970 * Write to the HcRhDescriptorA register.
4971 */
4972static int HcRhDescriptorA_w(POHCI pThis, uint32_t iReg, uint32_t val)
4973{
4974 RT_NOREF1(iReg);
4975 uint32_t chg = val ^ pThis->RootHub.desc_a; NOREF(chg);
4976 Log2(("HcRhDescriptorA_w(%#010x) => %sNDP=%d %sPSM=%d %sNPS=%d %sDT=%d %sOCPM=%d %sNOCP=%d %sPOTGT=%#x - %sPowerSwitching Set%sPower\n",
4977 val,
4978 chg & 0xff ?"!!!": "", val & 0xff,
4979 (chg >> 8) & 1 ? "*" : "", (val >> 8) & 1,
4980 (chg >> 9) & 1 ? "*" : "", (val >> 9) & 1,
4981 (chg >> 10) & 1 ?"!!!": "", 0,
4982 (chg >> 11) & 1 ? "*" : "", (val >> 11) & 1,
4983 (chg >> 12) & 1 ? "*" : "", (val >> 12) & 1,
4984 (chg >> 24)&0xff? "*" : "", (val >> 24) & 0xff,
4985 val & OHCI_RHA_NPS ? "No" : "",
4986 val & OHCI_RHA_PSM ? "Port" : "Global"));
4987 if (val & ~0xff001fff)
4988 Log2(("Unknown bits %#x are set!!!\n", val & ~0xff001fff));
4989
4990
4991 if ((val & (OHCI_RHA_NDP | OHCI_RHA_DT)) != OHCI_NDP_CFG(pThis))
4992 {
4993 Log(("ohci: invalid write to NDP or DT in roothub descriptor A!!! val=0x%.8x\n", val));
4994 val &= ~(OHCI_RHA_NDP | OHCI_RHA_DT);
4995 val |= OHCI_NDP_CFG(pThis);
4996 }
4997
4998 pThis->RootHub.desc_a = val;
4999 return VINF_SUCCESS;
5000}
5001
5002/**
5003 * Read the HcRhDescriptorB register.
5004 */
5005static int HcRhDescriptorB_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5006{
5007 uint32_t val = pThis->RootHub.desc_b;
5008 Log2(("HcRhDescriptorB_r() -> %#010x - DR=0x%04x PPCM=0x%04x\n",
5009 val, val & 0xffff, val >> 16));
5010 *pu32Value = val;
5011 RT_NOREF1(iReg);
5012 return VINF_SUCCESS;
5013}
5014
5015/**
5016 * Write to the HcRhDescriptorB register.
5017 */
5018static int HcRhDescriptorB_w(POHCI pThis, uint32_t iReg, uint32_t val)
5019{
5020 RT_NOREF1(iReg);
5021 uint32_t chg = pThis->RootHub.desc_b ^ val; NOREF(chg);
5022 Log2(("HcRhDescriptorB_w(%#010x) => %sDR=0x%04x %sPPCM=0x%04x\n",
5023 val,
5024 chg & 0xffff ? "!!!" : "", val & 0xffff,
5025 chg >> 16 ? "!!!" : "", val >> 16));
5026
5027 if ( pThis->RootHub.desc_b != val )
5028 Log(("ohci: unsupported write to root descriptor B!!! 0x%.8x -> 0x%.8x\n", pThis->RootHub.desc_b, val));
5029 pThis->RootHub.desc_b = val;
5030 return VINF_SUCCESS;
5031}
5032
5033/**
5034 * Read the HcRhStatus (Rh = Root Hub) register.
5035 */
5036static int HcRhStatus_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5037{
5038 uint32_t val = pThis->RootHub.status;
5039 if (val & (OHCI_RHS_LPSC | OHCI_RHS_OCIC))
5040 Log2(("HcRhStatus_r() -> %#010x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n",
5041 val, val & 1, (val >> 1) & 1, (val >> 15) & 1, (val >> 16) & 1, (val >> 17) & 1, (val >> 31) & 1));
5042 *pu32Value = val;
5043 RT_NOREF1(iReg);
5044 return VINF_SUCCESS;
5045}
5046
5047/**
5048 * Write to the HcRhStatus (Rh = Root Hub) register.
5049 */
5050static int HcRhStatus_w(POHCI pThis, uint32_t iReg, uint32_t val)
5051{
5052#ifdef IN_RING3
5053 /* log */
5054 uint32_t old = pThis->RootHub.status;
5055 uint32_t chg;
5056 if (val & ~0x80038003)
5057 Log2(("HcRhStatus_w: Unknown bits %#x are set!!!\n", val & ~0x80038003));
5058 if ( (val & OHCI_RHS_LPSC) && (val & OHCI_RHS_LPS) )
5059 Log2(("HcRhStatus_w: Warning both CGP and SGP are set! (Clear/Set Global Power)\n"));
5060 if ( (val & OHCI_RHS_DRWE) && (val & OHCI_RHS_CRWE) )
5061 Log2(("HcRhStatus_w: Warning both CRWE and SRWE are set! (Clear/Set Remote Wakeup Enable)\n"));
5062
5063
5064 /* write 1 to clear OCIC */
5065 if ( val & OHCI_RHS_OCIC )
5066 pThis->RootHub.status &= ~OHCI_RHS_OCIC;
5067
5068 /* SetGlobalPower */
5069 if ( val & OHCI_RHS_LPSC )
5070 {
5071 unsigned i;
5072 Log2(("ohci: global power up\n"));
5073 for (i = 0; i < OHCI_NDP_CFG(pThis); i++)
5074 ohciR3RhPortPower(&pThis->RootHub, i, true /* power up */);
5075 }
5076
5077 /* ClearGlobalPower */
5078 if ( val & OHCI_RHS_LPS )
5079 {
5080 unsigned i;
5081 Log2(("ohci: global power down\n"));
5082 for (i = 0; i < OHCI_NDP_CFG(pThis); i++)
5083 ohciR3RhPortPower(&pThis->RootHub, i, false /* power down */);
5084 }
5085
5086 if ( val & OHCI_RHS_DRWE )
5087 pThis->RootHub.status |= OHCI_RHS_DRWE;
5088
5089 if ( val & OHCI_RHS_CRWE )
5090 pThis->RootHub.status &= ~OHCI_RHS_DRWE;
5091
5092 chg = pThis->RootHub.status ^ old;
5093 Log2(("HcRhStatus_w(%#010x) => %sCGP=%d %sOCI=%d %sSRWE=%d %sSGP=%d %sOCIC=%d %sCRWE=%d\n",
5094 val,
5095 chg & 1 ? "*" : "", val & 1,
5096 (chg >> 1) & 1 ?"!!!": "", (val >> 1) & 1,
5097 (chg >> 15) & 1 ? "*" : "", (val >> 15) & 1,
5098 (chg >> 16) & 1 ? "*" : "", (val >> 16) & 1,
5099 (chg >> 17) & 1 ? "*" : "", (val >> 17) & 1,
5100 (chg >> 31) & 1 ? "*" : "", (val >> 31) & 1));
5101 RT_NOREF1(iReg);
5102 return VINF_SUCCESS;
5103#else /* !IN_RING3 */
5104 RT_NOREF3(pThis, iReg, val);
5105 return VINF_IOM_R3_MMIO_WRITE;
5106#endif /* !IN_RING3 */
5107}
5108
5109/**
5110 * Read the HcRhPortStatus register of a port.
5111 */
5112static int HcRhPortStatus_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5113{
5114 const unsigned i = iReg - 21;
5115 uint32_t val = pThis->RootHub.aPorts[i].fReg | OHCI_PORT_PPS; /* PortPowerStatus: see todo on power in _w function. */
5116 if (val & OHCI_PORT_PRS)
5117 {
5118#ifdef IN_RING3
5119 RTThreadYield();
5120#else
5121 Log2(("HcRhPortStatus_r: yield -> VINF_IOM_R3_MMIO_READ\n"));
5122 return VINF_IOM_R3_MMIO_READ;
5123#endif
5124 }
5125 if (val & (OHCI_PORT_PRS | OHCI_PORT_CLEAR_CHANGE_MASK))
5126 Log2(("HcRhPortStatus_r(): port %u: -> %#010x - CCS=%d PES=%d PSS=%d POCI=%d RRS=%d PPS=%d LSDA=%d CSC=%d PESC=%d PSSC=%d OCIC=%d PRSC=%d\n",
5127 i, val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 8) & 1, (val >> 9) & 1,
5128 (val >> 16) & 1, (val >> 17) & 1, (val >> 18) & 1, (val >> 19) & 1, (val >> 20) & 1));
5129 *pu32Value = val;
5130 return VINF_SUCCESS;
5131}
5132
5133#ifdef IN_RING3
5134/**
5135 * Completion callback for the vusb_dev_reset() operation.
5136 * @thread EMT.
5137 */
5138static DECLCALLBACK(void) ohciR3PortResetDone(PVUSBIDEVICE pDev, int rc, void *pvUser)
5139{
5140 POHCI pThis = (POHCI)pvUser;
5141
5142 /*
5143 * Find the port in question
5144 */
5145 POHCIHUBPORT pPort = NULL;
5146 unsigned iPort;
5147 for (iPort = 0; iPort < OHCI_NDP_CFG(pThis); iPort++) /* lazy bird */
5148 if (pThis->RootHub.aPorts[iPort].pDev == pDev)
5149 {
5150 pPort = &pThis->RootHub.aPorts[iPort];
5151 break;
5152 }
5153 if (!pPort)
5154 {
5155 Assert(pPort); /* sometimes happens because of @bugref{1510} */
5156 return;
5157 }
5158
5159 if (RT_SUCCESS(rc))
5160 {
5161 /*
5162 * Successful reset.
5163 */
5164 Log2(("ohciR3PortResetDone: Reset completed.\n"));
5165 pPort->fReg &= ~(OHCI_PORT_PRS | OHCI_PORT_PSS | OHCI_PORT_PSSC);
5166 pPort->fReg |= OHCI_PORT_PES | OHCI_PORT_PRSC;
5167 }
5168 else
5169 {
5170 /* desperate measures. */
5171 if ( pPort->pDev
5172 && VUSBIDevGetState(pPort->pDev) == VUSB_DEVICE_STATE_ATTACHED)
5173 {
5174 /*
5175 * Damn, something weird happened during reset. We'll pretend the user did an
5176 * incredible fast reconnect or something. (probably not gonna work)
5177 */
5178 Log2(("ohciR3PortResetDone: The reset failed (rc=%Rrc)!!! Pretending reconnect at the speed of light.\n", rc));
5179 pPort->fReg = OHCI_PORT_CCS | OHCI_PORT_CSC;
5180 }
5181 else
5182 {
5183 /*
5184 * The device have / will be disconnected.
5185 */
5186 Log2(("ohciR3PortResetDone: Disconnected (rc=%Rrc)!!!\n", rc));
5187 pPort->fReg &= ~(OHCI_PORT_PRS | OHCI_PORT_PSS | OHCI_PORT_PSSC | OHCI_PORT_PRSC);
5188 pPort->fReg |= OHCI_PORT_CSC;
5189 }
5190 }
5191
5192 /* Raise roothub status change interrupt. */
5193 ohciR3SetInterrupt(pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5194}
5195
5196/**
5197 * Sets a flag in a port status register but only set it if a device is
5198 * connected, if not set ConnectStatusChange flag to force HCD to reevaluate
5199 * connect status.
5200 *
5201 * @returns true if device was connected and the flag was cleared.
5202 */
5203static bool ohciR3RhPortSetIfConnected(POHCIROOTHUB pRh, int iPort, uint32_t fValue)
5204{
5205 /*
5206 * Writing a 0 has no effect
5207 */
5208 if (fValue == 0)
5209 return false;
5210
5211 /*
5212 * If CurrentConnectStatus is cleared we set ConnectStatusChange.
5213 */
5214 if (!(pRh->aPorts[iPort].fReg & OHCI_PORT_CCS))
5215 {
5216 pRh->aPorts[iPort].fReg |= OHCI_PORT_CSC;
5217 ohciR3SetInterrupt(pRh->pOhci, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5218 return false;
5219 }
5220
5221 bool fRc = !(pRh->aPorts[iPort].fReg & fValue);
5222
5223 /* set the bit */
5224 pRh->aPorts[iPort].fReg |= fValue;
5225
5226 return fRc;
5227}
5228#endif /* IN_RING3 */
5229
5230/**
5231 * Write to the HcRhPortStatus register of a port.
5232 */
5233static int HcRhPortStatus_w(POHCI pThis, uint32_t iReg, uint32_t val)
5234{
5235#ifdef IN_RING3
5236 const unsigned i = iReg - 21;
5237 POHCIHUBPORT p = &pThis->RootHub.aPorts[i];
5238 uint32_t old_state = p->fReg;
5239
5240# ifdef LOG_ENABLED
5241 /*
5242 * Log it.
5243 */
5244 static const char *apszCmdNames[32] =
5245 {
5246 "ClearPortEnable", "SetPortEnable", "SetPortSuspend", "!!!ClearSuspendStatus",
5247 "SetPortReset", "!!!5", "!!!6", "!!!7",
5248 "SetPortPower", "ClearPortPower", "!!!10", "!!!11",
5249 "!!!12", "!!!13", "!!!14", "!!!15",
5250 "ClearCSC", "ClearPESC", "ClearPSSC", "ClearOCIC",
5251 "ClearPRSC", "!!!21", "!!!22", "!!!23",
5252 "!!!24", "!!!25", "!!!26", "!!!27",
5253 "!!!28", "!!!29", "!!!30", "!!!31"
5254 };
5255 Log2(("HcRhPortStatus_w(%#010x): port %u:", val, i));
5256 for (unsigned j = 0; j < RT_ELEMENTS(apszCmdNames); j++)
5257 if (val & (1 << j))
5258 Log2((" %s", apszCmdNames[j]));
5259 Log2(("\n"));
5260# endif
5261
5262 /* Write to clear any of the change bits: CSC, PESC, PSSC, OCIC and PRSC */
5263 if (val & OHCI_PORT_CLEAR_CHANGE_MASK)
5264 p->fReg &= ~(val & OHCI_PORT_CLEAR_CHANGE_MASK);
5265
5266 if (val & OHCI_PORT_CLRPE)
5267 {
5268 p->fReg &= ~OHCI_PORT_PES;
5269 Log2(("HcRhPortStatus_w(): port %u: DISABLE\n", i));
5270 }
5271
5272 if (ohciR3RhPortSetIfConnected(&pThis->RootHub, i, val & OHCI_PORT_PES))
5273 Log2(("HcRhPortStatus_w(): port %u: ENABLE\n", i));
5274
5275 if (ohciR3RhPortSetIfConnected(&pThis->RootHub, i, val & OHCI_PORT_PSS))
5276 Log2(("HcRhPortStatus_w(): port %u: SUSPEND - not implemented correctly!!!\n", i));
5277
5278 if (val & OHCI_PORT_PRS)
5279 {
5280 if (ohciR3RhPortSetIfConnected(&pThis->RootHub, i, val & OHCI_PORT_PRS))
5281 {
5282 PVM pVM = PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns));
5283 p->fReg &= ~OHCI_PORT_PRSC;
5284 VUSBIDevReset(p->pDev, false /* don't reset on linux */, ohciR3PortResetDone, pThis, pVM);
5285 }
5286 else if (p->fReg & OHCI_PORT_PRS)
5287 {
5288 /* the guest is getting impatient. */
5289 Log2(("HcRhPortStatus_w(): port %u: Impatient guest!\n", i));
5290 RTThreadYield();
5291 }
5292 }
5293
5294 if (!(pThis->RootHub.desc_a & OHCI_RHA_NPS))
5295 {
5296 /** @todo To implement per-device power-switching
5297 * we need to check PortPowerControlMask to make
5298 * sure it isn't gang powered
5299 */
5300 if (val & OHCI_PORT_CLRPP)
5301 ohciR3RhPortPower(&pThis->RootHub, i, false /* power down */);
5302 if (val & OHCI_PORT_PPS)
5303 ohciR3RhPortPower(&pThis->RootHub, i, true /* power up */);
5304 }
5305
5306 /** @todo r=frank: ClearSuspendStatus. Timing? */
5307 if (val & OHCI_PORT_CLRSS)
5308 {
5309 ohciR3RhPortPower(&pThis->RootHub, i, true /* power up */);
5310 pThis->RootHub.aPorts[i].fReg &= ~OHCI_PORT_PSS;
5311 pThis->RootHub.aPorts[i].fReg |= OHCI_PORT_PSSC;
5312 ohciR3SetInterrupt(pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5313 }
5314
5315 if (p->fReg != old_state)
5316 {
5317 uint32_t res = p->fReg;
5318 uint32_t chg = res ^ old_state; NOREF(chg);
5319 Log2(("HcRhPortStatus_w(%#010x): port %u: => %sCCS=%d %sPES=%d %sPSS=%d %sPOCI=%d %sRRS=%d %sPPS=%d %sLSDA=%d %sCSC=%d %sPESC=%d %sPSSC=%d %sOCIC=%d %sPRSC=%d\n",
5320 val, i,
5321 chg & 1 ? "*" : "", res & 1,
5322 (chg >> 1) & 1 ? "*" : "", (res >> 1) & 1,
5323 (chg >> 2) & 1 ? "*" : "", (res >> 2) & 1,
5324 (chg >> 3) & 1 ? "*" : "", (res >> 3) & 1,
5325 (chg >> 4) & 1 ? "*" : "", (res >> 4) & 1,
5326 (chg >> 8) & 1 ? "*" : "", (res >> 8) & 1,
5327 (chg >> 9) & 1 ? "*" : "", (res >> 9) & 1,
5328 (chg >> 16) & 1 ? "*" : "", (res >> 16) & 1,
5329 (chg >> 17) & 1 ? "*" : "", (res >> 17) & 1,
5330 (chg >> 18) & 1 ? "*" : "", (res >> 18) & 1,
5331 (chg >> 19) & 1 ? "*" : "", (res >> 19) & 1,
5332 (chg >> 20) & 1 ? "*" : "", (res >> 20) & 1));
5333 }
5334 return VINF_SUCCESS;
5335#else /* !IN_RING3 */
5336 RT_NOREF3(pThis, iReg, val);
5337 return VINF_IOM_R3_MMIO_WRITE;
5338#endif /* !IN_RING3 */
5339}
5340
5341/**
5342 * Register descriptor table
5343 */
5344static const OHCIOPREG g_aOpRegs[] =
5345{
5346 { "HcRevision", HcRevision_r, HcRevision_w }, /* 0 */
5347 { "HcControl", HcControl_r, HcControl_w }, /* 1 */
5348 { "HcCommandStatus", HcCommandStatus_r, HcCommandStatus_w }, /* 2 */
5349 { "HcInterruptStatus", HcInterruptStatus_r, HcInterruptStatus_w }, /* 3 */
5350 { "HcInterruptEnable", HcInterruptEnable_r, HcInterruptEnable_w }, /* 4 */
5351 { "HcInterruptDisable", HcInterruptDisable_r, HcInterruptDisable_w }, /* 5 */
5352 { "HcHCCA", HcHCCA_r, HcHCCA_w }, /* 6 */
5353 { "HcPeriodCurrentED", HcPeriodCurrentED_r, HcPeriodCurrentED_w }, /* 7 */
5354 { "HcControlHeadED", HcControlHeadED_r, HcControlHeadED_w }, /* 8 */
5355 { "HcControlCurrentED", HcControlCurrentED_r, HcControlCurrentED_w }, /* 9 */
5356 { "HcBulkHeadED", HcBulkHeadED_r, HcBulkHeadED_w }, /* 10 */
5357 { "HcBulkCurrentED", HcBulkCurrentED_r, HcBulkCurrentED_w }, /* 11 */
5358 { "HcDoneHead", HcDoneHead_r, HcDoneHead_w }, /* 12 */
5359 { "HcFmInterval", HcFmInterval_r, HcFmInterval_w }, /* 13 */
5360 { "HcFmRemaining", HcFmRemaining_r, HcFmRemaining_w }, /* 14 */
5361 { "HcFmNumber", HcFmNumber_r, HcFmNumber_w }, /* 15 */
5362 { "HcPeriodicStart", HcPeriodicStart_r, HcPeriodicStart_w }, /* 16 */
5363 { "HcLSThreshold", HcLSThreshold_r, HcLSThreshold_w }, /* 17 */
5364 { "HcRhDescriptorA", HcRhDescriptorA_r, HcRhDescriptorA_w }, /* 18 */
5365 { "HcRhDescriptorB", HcRhDescriptorB_r, HcRhDescriptorB_w }, /* 19 */
5366 { "HcRhStatus", HcRhStatus_r, HcRhStatus_w }, /* 20 */
5367
5368 /* The number of port status register depends on the definition
5369 * of OHCI_NDP_MAX macro
5370 */
5371 { "HcRhPortStatus[0]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 21 */
5372 { "HcRhPortStatus[1]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 22 */
5373 { "HcRhPortStatus[2]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 23 */
5374 { "HcRhPortStatus[3]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 24 */
5375 { "HcRhPortStatus[4]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 25 */
5376 { "HcRhPortStatus[5]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 26 */
5377 { "HcRhPortStatus[6]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 27 */
5378 { "HcRhPortStatus[7]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 28 */
5379 { "HcRhPortStatus[8]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 29 */
5380 { "HcRhPortStatus[9]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 30 */
5381 { "HcRhPortStatus[10]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 31 */
5382 { "HcRhPortStatus[11]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 32 */
5383 { "HcRhPortStatus[12]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 33 */
5384 { "HcRhPortStatus[13]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 34 */
5385 { "HcRhPortStatus[14]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 35 */
5386};
5387
5388/* Quick way to determine how many op regs are valid. Since at least one port must
5389 * be configured (and no more than 15), there will be between 22 and 36 registers.
5390 */
5391#define NUM_OP_REGS(pohci) (21 + OHCI_NDP_CFG(pohci))
5392
5393AssertCompile(RT_ELEMENTS(g_aOpRegs) > 21);
5394AssertCompile(RT_ELEMENTS(g_aOpRegs) <= 36);
5395
5396/**
5397 * @callback_method_impl{FNIOMMMIOREAD}
5398 */
5399PDMBOTHCBDECL(int) ohciMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
5400{
5401 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5402 RT_NOREF1(pvUser);
5403
5404 /* Paranoia: Assert that IOMMMIO_FLAGS_READ_DWORD works. */
5405 AssertReturn(cb == sizeof(uint32_t), VERR_INTERNAL_ERROR_3);
5406 AssertReturn(!(GCPhysAddr & 0x3), VERR_INTERNAL_ERROR_4);
5407
5408 /*
5409 * Validate the register and call the read operator.
5410 */
5411 int rc;
5412 const uint32_t iReg = (GCPhysAddr - pThis->MMIOBase) >> 2;
5413 if (iReg < NUM_OP_REGS(pThis))
5414 {
5415 const OHCIOPREG *pReg = &g_aOpRegs[iReg];
5416 rc = pReg->pfnRead(pThis, iReg, (uint32_t *)pv);
5417 }
5418 else
5419 {
5420 Log(("ohci: Trying to read register %u/%u!!!\n", iReg, NUM_OP_REGS(pThis)));
5421 rc = VINF_IOM_MMIO_UNUSED_FF;
5422 }
5423 return rc;
5424}
5425
5426
5427/**
5428 * @callback_method_impl{FNIOMMMIOWRITE}
5429 */
5430PDMBOTHCBDECL(int) ohciMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
5431{
5432 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5433 RT_NOREF1(pvUser);
5434
5435 /* Paranoia: Assert that IOMMMIO_FLAGS_WRITE_DWORD_ZEROED works. */
5436 AssertReturn(cb == sizeof(uint32_t), VERR_INTERNAL_ERROR_3);
5437 AssertReturn(!(GCPhysAddr & 0x3), VERR_INTERNAL_ERROR_4);
5438
5439 /*
5440 * Validate the register and call the read operator.
5441 */
5442 int rc;
5443 const uint32_t iReg = (GCPhysAddr - pThis->MMIOBase) >> 2;
5444 if (iReg < NUM_OP_REGS(pThis))
5445 {
5446 const OHCIOPREG *pReg = &g_aOpRegs[iReg];
5447 rc = pReg->pfnWrite(pThis, iReg, *(uint32_t const *)pv);
5448 }
5449 else
5450 {
5451 Log(("ohci: Trying to write to register %u/%u!!!\n", iReg, NUM_OP_REGS(pThis)));
5452 rc = VINF_SUCCESS;
5453 }
5454 return rc;
5455}
5456
5457#ifdef IN_RING3
5458
5459/**
5460 * @callback_method_impl{FNPCIIOREGIONMAP}
5461 */
5462static DECLCALLBACK(int) ohciR3Map(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5463 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
5464{
5465 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5466 RT_NOREF(pPciDev, iRegion, enmType);
5467 Assert(pPciDev == pDevIns->apPciDevs[0]);
5468
5469 int rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
5470 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED
5471 | IOMMMIO_FLAGS_DBGSTOP_ON_COMPLICATED_WRITE,
5472 ohciMmioWrite, ohciMmioRead, "USB OHCI");
5473 if (RT_FAILURE(rc))
5474 return rc;
5475
5476 if (pThis->fRZEnabled)
5477 {
5478 rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/, "ohciMmioWrite", "ohciMmioRead");
5479 if (RT_FAILURE(rc))
5480 return rc;
5481
5482 rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/, "ohciMmioWrite", "ohciMmioRead");
5483 if (RT_FAILURE(rc))
5484 return rc;
5485 }
5486
5487 pThis->MMIOBase = GCPhysAddress;
5488 return VINF_SUCCESS;
5489}
5490
5491
5492/**
5493 * Prepares for state saving.
5494 * All URBs needs to be canceled.
5495 *
5496 * @returns VBox status code.
5497 * @param pDevIns The device instance.
5498 * @param pSSM The handle to save the state to.
5499 */
5500static DECLCALLBACK(int) ohciR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5501{
5502 RT_NOREF(pSSM);
5503 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5504 POHCIROOTHUB pRh = &pThis->RootHub;
5505 LogFlow(("ohciR3SavePrep: \n"));
5506
5507 /*
5508 * Detach all proxied devices.
5509 */
5510 PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
5511 /** @todo this won't work well when continuing after saving! */
5512 for (unsigned i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
5513 {
5514 PVUSBIDEVICE pDev = pRh->aPorts[i].pDev;
5515 if (pDev)
5516 {
5517 if (!VUSBIDevIsSavedStateSupported(pDev))
5518 {
5519 VUSBIRhDetachDevice(pRh->pIRhConn, pDev);
5520 /*
5521 * Save the device pointers here so we can reattach them afterwards.
5522 * This will work fine even if the save fails since the Done handler is
5523 * called unconditionally if the Prep handler was called.
5524 */
5525 pRh->aPorts[i].pDev = pDev;
5526 }
5527 }
5528 }
5529
5530 /*
5531 * If the bus was started set the timer. This is ugly but avoids changing the
5532 * saved state version for now so we can backport the changes to other branches.
5533 */
5534 /** @todo Do it properly for 4.4 by changing the saved state. */
5535 if (VUSBIRhGetPeriodicFrameRate(pRh->pIRhConn) != 0)
5536 {
5537 /* Calculate a new timer expiration so this saved state works with older releases. */
5538 uint64_t u64Expire = PDMDevHlpTMTimeVirtGet(pThis->CTX_SUFF(pDevIns)) + pThis->cTicksPerFrame;
5539
5540 LogFlowFunc(("Bus is active, setting timer to %llu\n", u64Expire));
5541 int rc = TMTimerSet(pThis->pEndOfFrameTimerR3, u64Expire);
5542 AssertRC(rc);
5543 }
5544
5545 PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
5546
5547 /*
5548 * Kill old load data which might be hanging around.
5549 */
5550 if (pThis->pLoad)
5551 {
5552 TMR3TimerDestroy(pThis->pLoad->pTimer);
5553 MMR3HeapFree(pThis->pLoad);
5554 pThis->pLoad = NULL;
5555 }
5556 return VINF_SUCCESS;
5557}
5558
5559
5560/**
5561 * Saves the state of the OHCI device.
5562 *
5563 * @returns VBox status code.
5564 * @param pDevIns The device instance.
5565 * @param pSSM The handle to save the state to.
5566 */
5567static DECLCALLBACK(int) ohciR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5568{
5569 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5570 LogFlow(("ohciR3SaveExec: \n"));
5571
5572 int rc = SSMR3PutStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
5573 if (RT_SUCCESS(rc))
5574 rc = TMR3TimerSave(pThis->CTX_SUFF(pEndOfFrameTimer), pSSM);
5575 return rc;
5576}
5577
5578
5579/**
5580 * Done state save operation.
5581 *
5582 * @returns VBox load code.
5583 * @param pDevIns Device instance of the device which registered the data unit.
5584 * @param pSSM SSM operation handle.
5585 */
5586static DECLCALLBACK(int) ohciR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5587{
5588 RT_NOREF(pSSM);
5589 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5590 LogFlow(("ohciR3SaveDone: \n"));
5591
5592 /*
5593 * NULL the dev pointers.
5594 */
5595 POHCIROOTHUB pRh = &pThis->RootHub;
5596 OHCIROOTHUB Rh = *pRh;
5597 for (unsigned i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
5598 {
5599 if ( pRh->aPorts[i].pDev
5600 && !VUSBIDevIsSavedStateSupported(pRh->aPorts[i].pDev))
5601 pRh->aPorts[i].pDev = NULL;
5602 }
5603
5604 /*
5605 * Attach the devices.
5606 */
5607 for (unsigned i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
5608 {
5609 PVUSBIDEVICE pDev = Rh.aPorts[i].pDev;
5610 if ( pDev
5611 && !VUSBIDevIsSavedStateSupported(pDev))
5612 VUSBIRhAttachDevice(pRh->pIRhConn, pDev);
5613 }
5614
5615 return VINF_SUCCESS;
5616}
5617
5618
5619/**
5620 * Prepare loading the state of the OHCI device.
5621 * This must detach the devices currently attached and save
5622 * the up for reconnect after the state load have been completed
5623 *
5624 * @returns VBox status code.
5625 * @param pDevIns The device instance.
5626 * @param pSSM The handle to the saved state.
5627 */
5628static DECLCALLBACK(int) ohciR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5629{
5630 RT_NOREF(pSSM);
5631 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5632 LogFlow(("ohciR3LoadPrep:\n"));
5633 if (!pThis->pLoad)
5634 {
5635 /*
5636 * Detach all devices which are present in this session. Save them in the load
5637 * structure so we can reattach them after restoring the guest.
5638 */
5639 POHCIROOTHUB pRh = &pThis->RootHub;
5640 OHCILOAD Load;
5641 Load.pTimer = NULL;
5642 Load.cDevs = 0;
5643 for (unsigned i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
5644 {
5645 PVUSBIDEVICE pDev = pRh->aPorts[i].pDev;
5646 if ( pDev
5647 && !VUSBIDevIsSavedStateSupported(pDev))
5648 {
5649 Load.apDevs[Load.cDevs++] = pDev;
5650 VUSBIRhDetachDevice(pRh->pIRhConn, pDev);
5651 Assert(!pRh->aPorts[i].pDev);
5652 }
5653 }
5654
5655 /*
5656 * Any devices to reattach, if so duplicate the Load struct.
5657 */
5658 if (Load.cDevs)
5659 {
5660 pThis->pLoad = (POHCILOAD)PDMDevHlpMMHeapAlloc(pDevIns, sizeof(Load));
5661 if (!pThis->pLoad)
5662 return VERR_NO_MEMORY;
5663 *pThis->pLoad = Load;
5664 }
5665 }
5666 /* else: we ASSUME no device can be attached or detach in the period
5667 * between a state load and the pLoad stuff is processed. */
5668 return VINF_SUCCESS;
5669}
5670
5671
5672/**
5673 * Loads the state of the OHCI device.
5674 *
5675 * @returns VBox status code.
5676 * @param pDevIns The device instance.
5677 * @param pSSM The handle to the saved state.
5678 * @param uVersion The data unit version number.
5679 * @param uPass The data pass.
5680 */
5681static DECLCALLBACK(int) ohciR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5682{
5683 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5684 int rc;
5685 LogFlow(("ohciR3LoadExec:\n"));
5686 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
5687
5688 if (uVersion == OHCI_SAVED_STATE_VERSION)
5689 {
5690 rc = SSMR3GetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
5691 if (RT_FAILURE(rc))
5692 return rc;
5693 }
5694 else if (uVersion == OHCI_SAVED_STATE_VERSION_8PORTS)
5695 {
5696 rc = SSMR3GetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields8Ports[0], NULL);
5697 if (RT_FAILURE(rc))
5698 return rc;
5699 }
5700 else
5701 AssertMsgFailedReturn(("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
5702
5703 /*
5704 * Finally restore the timer.
5705 */
5706 return TMR3TimerLoad(pThis->pEndOfFrameTimerR3, pSSM);
5707}
5708
5709
5710/**
5711 * Done state load operation.
5712 *
5713 * @returns VBox load code.
5714 * @param pDevIns Device instance of the device which registered the data unit.
5715 * @param pSSM SSM operation handle.
5716 */
5717static DECLCALLBACK(int) ohciR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5718{
5719 RT_NOREF(pSSM);
5720 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5721 LogFlow(("ohciR3LoadDone:\n"));
5722
5723 /*
5724 * Start a timer if we've got devices to reattach
5725 */
5726 if (pThis->pLoad)
5727 {
5728 int rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ohciR3LoadReattachDevices, pThis,
5729 TMTIMER_FLAGS_NO_CRIT_SECT, "OHCI reattach devices on load",
5730 &pThis->pLoad->pTimer);
5731 if (RT_SUCCESS(rc))
5732 rc = TMTimerSetMillies(pThis->pLoad->pTimer, 250);
5733 return rc;
5734 }
5735
5736 return VINF_SUCCESS;
5737}
5738
5739
5740/**
5741 * Reattaches devices after a saved state load.
5742 */
5743static DECLCALLBACK(void) ohciR3LoadReattachDevices(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
5744{
5745 RT_NOREF(pDevIns);
5746 POHCI pThis = (POHCI)pvUser;
5747 POHCILOAD pLoad = pThis->pLoad;
5748 POHCIROOTHUB pRh = &pThis->RootHub;
5749 LogFlow(("ohciR3LoadReattachDevices:\n"));
5750
5751 /*
5752 * Reattach devices.
5753 */
5754 for (unsigned i = 0; i < pLoad->cDevs; i++)
5755 VUSBIRhAttachDevice(pRh->pIRhConn, pLoad->apDevs[i]);
5756
5757 /*
5758 * Cleanup.
5759 */
5760 TMR3TimerDestroy(pTimer);
5761 MMR3HeapFree(pLoad);
5762 pThis->pLoad = NULL;
5763}
5764
5765
5766/**
5767 * Reset notification.
5768 *
5769 * @returns VBox status code.
5770 * @param pDevIns The device instance data.
5771 */
5772static DECLCALLBACK(void) ohciR3Reset(PPDMDEVINS pDevIns)
5773{
5774 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5775 LogFlow(("ohciR3Reset:\n"));
5776
5777 /*
5778 * There is no distinction between cold boot, warm reboot and software reboots,
5779 * all of these are treated as cold boots. We are also doing the initialization
5780 * job of a BIOS or SMM driver.
5781 *
5782 * Important: Don't confuse UsbReset with hardware reset. Hardware reset is
5783 * just one way of getting into the UsbReset state.
5784 */
5785 ohciR3DoReset(pThis, OHCI_USB_RESET, true /* reset devices */);
5786}
5787
5788
5789/**
5790 * Resume notification.
5791 *
5792 * @returns VBox status code.
5793 * @param pDevIns The device instance data.
5794 */
5795static DECLCALLBACK(void) ohciR3Resume(PPDMDEVINS pDevIns)
5796{
5797 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5798 LogFlowFunc(("\n"));
5799
5800 /* Restart the frame thread if the timer is active. */
5801 if (TMTimerIsActive(pThis->pEndOfFrameTimerR3))
5802 {
5803 int rc = TMTimerStop(pThis->pEndOfFrameTimerR3);
5804 AssertRC(rc);
5805
5806 LogFlowFunc(("Bus was active, enable periodic frame processing\n"));
5807 rc = pThis->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThis->RootHub.pIRhConn, OHCI_DEFAULT_TIMER_FREQ);
5808 AssertRC(rc);
5809 }
5810}
5811
5812
5813/**
5814 * Info handler, device version. Dumps OHCI control registers.
5815 *
5816 * @param pDevIns Device instance which registered the info.
5817 * @param pHlp Callback functions for doing output.
5818 * @param pszArgs Argument string. Optional and specific to the handler.
5819 */
5820static DECLCALLBACK(void) ohciR3InfoRegs(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
5821{
5822 RT_NOREF(pszArgs);
5823 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5824 uint32_t val, ctl, status;
5825
5826 /* Control register */
5827 ctl = pThis->ctl;
5828 pHlp->pfnPrintf(pHlp, "HcControl: %08x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
5829 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
5830 (ctl >> 9) & 1, (ctl >> 10) & 1);
5831
5832 /* Command status register */
5833 status = pThis->status;
5834 pHlp->pfnPrintf(pHlp, "HcCommandStatus: %08x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
5835 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3);
5836
5837 /* Interrupt status register */
5838 val = pThis->intr_status;
5839 pHlp->pfnPrintf(pHlp, "HcInterruptStatus: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
5840 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5841 (val >> 6) & 1, (val >> 30) & 1);
5842
5843 /* Interrupt enable register */
5844 val = pThis->intr;
5845 pHlp->pfnPrintf(pHlp, "HcInterruptEnable: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
5846 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5847 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1);
5848
5849 /* HCCA address register */
5850 pHlp->pfnPrintf(pHlp, "HcHCCA: %08x\n", pThis->hcca);
5851
5852 /* Current periodic ED register */
5853 pHlp->pfnPrintf(pHlp, "HcPeriodCurrentED: %08x\n", pThis->per_cur);
5854
5855 /* Control ED registers */
5856 pHlp->pfnPrintf(pHlp, "HcControlHeadED: %08x\n", pThis->ctrl_head);
5857 pHlp->pfnPrintf(pHlp, "HcControlCurrentED: %08x\n", pThis->ctrl_cur);
5858
5859 /* Bulk ED registers */
5860 pHlp->pfnPrintf(pHlp, "HcBulkHeadED: %08x\n", pThis->bulk_head);
5861 pHlp->pfnPrintf(pHlp, "HcBulkCurrentED: %08x\n", pThis->bulk_cur);
5862
5863 /* Done head register */
5864 pHlp->pfnPrintf(pHlp, "HcDoneHead: %08x\n", pThis->done);
5865
5866 /* Done head register */
5867 pHlp->pfnPrintf(pHlp, "HcDoneHead: %08x\n", pThis->done);
5868
5869 /* Root hub descriptor A */
5870 val = pThis->RootHub.desc_a;
5871 pHlp->pfnPrintf(pHlp, "HcRhDescriptorA: %08x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTPGT=%d\n",
5872 val, (uint8_t)val, (val >> 8) & 1, (val >> 9) & 1, (val >> 10) & 1, (val >> 11) & 1, (val >> 12) & 1, (uint8_t)(val >> 24));
5873
5874 /* Root hub descriptor B */
5875 val = pThis->RootHub.desc_b;
5876 pHlp->pfnPrintf(pHlp, "HcRhDescriptorB: %08x - DR=%#04x PPCM=%#04x\n", val, (uint16_t)val, (uint16_t)(val >> 16));
5877
5878 /* Root hub status register */
5879 val = pThis->RootHub.status;
5880 pHlp->pfnPrintf(pHlp, "HcRhStatus: %08x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n\n",
5881 val, val & 1, (val >> 1) & 1, (val >> 15) & 1, (val >> 16) & 1, (val >> 17) & 1, (val >> 31) & 1);
5882
5883 /* Port status registers */
5884 for (unsigned i = 0; i < OHCI_NDP_CFG(pThis); ++i)
5885 {
5886 val = pThis->RootHub.aPorts[i].fReg;
5887 pHlp->pfnPrintf(pHlp, "HcRhPortStatus%02d: CCS=%d PES =%d PSS =%d POCI=%d PRS =%d PPS=%d LSDA=%d\n"
5888 " %08x - CSC=%d PESC=%d PSSC=%d OCIC=%d PRSC=%d\n",
5889 i, val & 1, (val >> 1) & 1, (val >> 2) & 1,(val >> 3) & 1, (val >> 4) & 1, (val >> 8) & 1, (val >> 9) & 1,
5890 val, (val >> 16) & 1, (val >> 17) & 1, (val >> 18) & 1, (val >> 19) & 1, (val >> 20) & 1);
5891 }
5892}
5893
5894
5895/**
5896 * Relocate device instance data.
5897 *
5898 * @returns VBox status code.
5899 * @param pDevIns The device instance data.
5900 * @param offDelta The relocation delta.
5901 */
5902static DECLCALLBACK(void) ohciR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5903{
5904 RT_NOREF(offDelta);
5905 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5906 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5907 pThis->pEndOfFrameTimerRC = TMTimerRCPtr(pThis->pEndOfFrameTimerR3);
5908}
5909
5910
5911/**
5912 * Destruct a device instance.
5913 *
5914 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
5915 * resources can be freed correctly.
5916 *
5917 * @returns VBox status code.
5918 * @param pDevIns The device instance data.
5919 */
5920static DECLCALLBACK(int) ohciR3Destruct(PPDMDEVINS pDevIns)
5921{
5922 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5923 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5924
5925#ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
5926 ohciR3PhysReadCacheFree(pThis->pCacheED);
5927 pThis->pCacheED = NULL;
5928 ohciR3PhysReadCacheFree(pThis->pCacheTD);
5929 pThis->pCacheTD = NULL;
5930#endif
5931
5932 if (RTCritSectIsInitialized(&pThis->CritSect))
5933 RTCritSectDelete(&pThis->CritSect);
5934 PDMR3CritSectDelete(&pThis->CsIrq);
5935
5936 /*
5937 * Tear down the per endpoint in-flight tracking...
5938 */
5939
5940 return VINF_SUCCESS;
5941}
5942
5943
5944/**
5945 * @interface_method_impl{PDMDEVREG,pfnConstruct,OHCI constructor}
5946 */
5947static DECLCALLBACK(int) ohciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5948{
5949 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5950 uint32_t cPorts;
5951 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5952
5953 /*
5954 * Init instance data.
5955 */
5956 pThis->pDevInsR3 = pDevIns;
5957 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
5958 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5959
5960 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
5961 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
5962
5963 PDMPciDevSetVendorId(pPciDev, 0x106b);
5964 PDMPciDevSetDeviceId(pPciDev, 0x003f);
5965 PDMPciDevSetClassProg(pPciDev, 0x10); /* OHCI */
5966 PDMPciDevSetClassSub(pPciDev, 0x03);
5967 PDMPciDevSetClassBase(pPciDev, 0x0c);
5968 PDMPciDevSetInterruptPin(pPciDev, 0x01);
5969#ifdef VBOX_WITH_MSI_DEVICES
5970 PDMPciDevSetStatus(pPciDev, VBOX_PCI_STATUS_CAP_LIST);
5971 PDMPciDevSetCapabilityList(pPciDev, 0x80);
5972#endif
5973
5974 pThis->RootHub.pOhci = pThis;
5975 pThis->RootHub.IBase.pfnQueryInterface = ohciR3RhQueryInterface;
5976 pThis->RootHub.IRhPort.pfnGetAvailablePorts = ohciR3RhGetAvailablePorts;
5977 pThis->RootHub.IRhPort.pfnGetUSBVersions = ohciR3RhGetUSBVersions;
5978 pThis->RootHub.IRhPort.pfnAttach = ohciR3RhAttach;
5979 pThis->RootHub.IRhPort.pfnDetach = ohciR3RhDetach;
5980 pThis->RootHub.IRhPort.pfnReset = ohciR3RhReset;
5981 pThis->RootHub.IRhPort.pfnXferCompletion = ohciR3RhXferCompletion;
5982 pThis->RootHub.IRhPort.pfnXferError = ohciR3RhXferError;
5983 pThis->RootHub.IRhPort.pfnStartFrame = ohciR3StartFrame;
5984 pThis->RootHub.IRhPort.pfnFrameRateChanged = ohciR3FrameRateChanged;
5985
5986 /* USB LED */
5987 pThis->RootHub.Led.u32Magic = PDMLED_MAGIC;
5988 pThis->RootHub.ILeds.pfnQueryStatusLed = ohciR3RhQueryStatusLed;
5989
5990
5991 /*
5992 * Read configuration.
5993 */
5994 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "RZEnabled", "");
5995 int rc = CFGMR3QueryBoolDef(pCfg, "RZEnabled", &pThis->fRZEnabled, true);
5996 AssertLogRelRCReturn(rc, rc);
5997
5998 /* Number of ports option. */
5999 rc = CFGMR3QueryU32Def(pCfg, "Ports", &cPorts, OHCI_NDP_DEFAULT);
6000 if (RT_FAILURE(rc))
6001 return PDMDEV_SET_ERROR(pDevIns, rc,
6002 N_("OHCI configuration error: failed to read Ports as integer"));
6003
6004 if (cPorts == 0 || cPorts > OHCI_NDP_MAX)
6005 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6006 N_("OHCI configuration error: Ports must be in range [%u,%u]"),
6007 1, OHCI_NDP_MAX);
6008
6009 /* Store the configured NDP; it will be used everywhere else from now on. */
6010 pThis->RootHub.desc_a = cPorts;
6011
6012 /*
6013 * Register PCI device and I/O region.
6014 */
6015 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
6016 if (RT_FAILURE(rc))
6017 return rc;
6018
6019#ifdef VBOX_WITH_MSI_DEVICES
6020 PDMMSIREG MsiReg;
6021 RT_ZERO(MsiReg);
6022 MsiReg.cMsiVectors = 1;
6023 MsiReg.iMsiCapOffset = 0x80;
6024 MsiReg.iMsiNextOffset = 0x00;
6025 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
6026 if (RT_FAILURE(rc))
6027 {
6028 PDMPciDevSetCapabilityList(pPciDev, 0x0);
6029 /* That's OK, we can work without MSI */
6030 }
6031#endif
6032
6033 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 4096, PCI_ADDRESS_SPACE_MEM, ohciR3Map);
6034 if (RT_FAILURE(rc))
6035 return rc;
6036
6037 /*
6038 * Create the end-of-frame timer.
6039 */
6040 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ohciR3FrameBoundaryTimer, pThis,
6041 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "USB Frame Timer",
6042 &pThis->pEndOfFrameTimerR3);
6043 if (RT_FAILURE(rc))
6044 return rc;
6045 pThis->pEndOfFrameTimerR0 = TMTimerR0Ptr(pThis->pEndOfFrameTimerR3);
6046 pThis->pEndOfFrameTimerRC = TMTimerRCPtr(pThis->pEndOfFrameTimerR3);
6047
6048 /*
6049 * Register the saved state data unit.
6050 */
6051 rc = PDMDevHlpSSMRegisterEx(pDevIns, OHCI_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
6052 NULL, NULL, NULL,
6053 ohciR3SavePrep, ohciR3SaveExec, ohciR3SaveDone,
6054 ohciR3LoadPrep, ohciR3LoadExec, ohciR3LoadDone);
6055 if (RT_FAILURE(rc))
6056 return rc;
6057
6058 /*
6059 * Attach to the VBox USB RootHub Driver on LUN #0.
6060 */
6061 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->RootHub.IBase, &pThis->RootHub.pIBase, "RootHub");
6062 if (RT_FAILURE(rc))
6063 {
6064 AssertMsgFailed(("Configuration error: No roothub driver attached to LUN #0!\n"));
6065 return rc;
6066 }
6067 pThis->RootHub.pIRhConn = PDMIBASE_QUERY_INTERFACE(pThis->RootHub.pIBase, VUSBIROOTHUBCONNECTOR);
6068 AssertMsgReturn(pThis->RootHub.pIRhConn,
6069 ("Configuration error: The driver doesn't provide the VUSBIROOTHUBCONNECTOR interface!\n"),
6070 VERR_PDM_MISSING_INTERFACE);
6071 pThis->RootHub.pIDev = PDMIBASE_QUERY_INTERFACE(pThis->RootHub.pIBase, VUSBIDEVICE);
6072 AssertMsgReturn(pThis->RootHub.pIDev,
6073 ("Configuration error: The driver doesn't provide the VUSBIDEVICE interface!\n"),
6074 VERR_PDM_MISSING_INTERFACE);
6075
6076 /*
6077 * Attach status driver (optional).
6078 */
6079 PPDMIBASE pBase;
6080 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->RootHub.IBase, &pBase, "Status Port");
6081 if (RT_SUCCESS(rc))
6082 pThis->RootHub.pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
6083 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
6084 {
6085 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
6086 return rc;
6087 }
6088
6089 /* Set URB parameters. */
6090 rc = VUSBIRhSetUrbParams(pThis->RootHub.pIRhConn, sizeof(VUSBURBHCIINT), sizeof(VUSBURBHCITDINT));
6091 if (RT_FAILURE(rc))
6092 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6093 N_("OHCI: Failed to set URB parameters"));
6094
6095 /*
6096 * Calculate the timer intervals.
6097 * This assumes that the VM timer doesn't change frequency during the run.
6098 */
6099 pThis->u64TimerHz = TMTimerGetFreq(pThis->CTX_SUFF(pEndOfFrameTimer));
6100
6101 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CsIrq, RT_SRC_POS, "OHCI#%uIrq", iInstance);
6102 if (RT_FAILURE(rc))
6103 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6104 N_("OHCI: Failed to create critical section"));
6105
6106 rc = RTCritSectInit(&pThis->CritSect);
6107 if (RT_FAILURE(rc))
6108 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6109 N_("OHCI: Failed to create critical section"));
6110
6111#ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
6112 pThis->pCacheED = ohciR3PhysReadCacheAlloc();
6113 pThis->pCacheTD = ohciR3PhysReadCacheAlloc();
6114 if (pThis->pCacheED == NULL || pThis->pCacheTD == NULL)
6115 return PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
6116 N_("OHCI: Failed to allocate PhysRead cache"));
6117#endif
6118
6119 /*
6120 * Do a hardware reset.
6121 */
6122 ohciR3DoReset(pThis, OHCI_USB_RESET, false /* don't reset devices */);
6123
6124#ifdef VBOX_WITH_STATISTICS
6125 /*
6126 * Register statistics.
6127 */
6128 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledIsocUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/CanceledIsocUrbs", STAMUNIT_OCCURENCES, "Detected canceled isochronous URBs.");
6129 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledGenUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/CanceledGenUrbs", STAMUNIT_OCCURENCES, "Detected canceled general URBs.");
6130 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatDroppedUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/DroppedUrbs", STAMUNIT_OCCURENCES, "Dropped URBs (endpoint halted, or URB canceled).");
6131#endif
6132
6133 /*
6134 * Register debugger info callbacks.
6135 */
6136 PDMDevHlpDBGFInfoRegister(pDevIns, "ohci", "OHCI control registers.", ohciR3InfoRegs);
6137
6138#if 0/*def DEBUG_bird*/
6139// g_fLogInterruptEPs = true;
6140 g_fLogControlEPs = true;
6141 g_fLogBulkEPs = true;
6142#endif
6143
6144 return VINF_SUCCESS;
6145}
6146
6147#endif /* IN_RING3 */
6148
6149const PDMDEVREG g_DeviceOHCI =
6150{
6151 /* .u32version = */ PDM_DEVREG_VERSION,
6152 /* .uReserved0 = */ 0,
6153 /* .szName = */ "usb-ohci",
6154 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
6155 /* .fClass = */ PDM_DEVREG_CLASS_BUS_USB,
6156 /* .cMaxInstances = */ ~0U,
6157 /* .uSharedVersion = */ 42,
6158 /* .cbInstanceShared = */ sizeof(OHCI),
6159 /* .cbInstanceCC = */ 0,
6160 /* .cbInstanceRC = */ 0,
6161 /* .cMaxPciDevices = */ 1,
6162 /* .cMaxMsixVectors = */ 0,
6163 /* .pszDescription = */ "OHCI USB controller.\n",
6164#if defined(IN_RING3)
6165 /* .pszRCMod = */ "VBoxDDRC.rc",
6166 /* .pszR0Mod = */ "VBoxDDR0.r0",
6167 /* .pfnConstruct = */ ohciR3Construct,
6168 /* .pfnDestruct = */ ohciR3Destruct,
6169 /* .pfnRelocate = */ ohciR3Relocate,
6170 /* .pfnMemSetup = */ NULL,
6171 /* .pfnPowerOn = */ NULL,
6172 /* .pfnReset = */ ohciR3Reset,
6173 /* .pfnSuspend = */ NULL,
6174 /* .pfnResume = */ ohciR3Resume,
6175 /* .pfnAttach = */ NULL,
6176 /* .pfnDetach = */ NULL,
6177 /* .pfnQueryInterface = */ NULL,
6178 /* .pfnInitComplete = */ NULL,
6179 /* .pfnPowerOff = */ NULL,
6180 /* .pfnSoftReset = */ NULL,
6181 /* .pfnReserved0 = */ NULL,
6182 /* .pfnReserved1 = */ NULL,
6183 /* .pfnReserved2 = */ NULL,
6184 /* .pfnReserved3 = */ NULL,
6185 /* .pfnReserved4 = */ NULL,
6186 /* .pfnReserved5 = */ NULL,
6187 /* .pfnReserved6 = */ NULL,
6188 /* .pfnReserved7 = */ NULL,
6189#elif defined(IN_RING0)
6190 /* .pfnEarlyConstruct = */ NULL,
6191 /* .pfnConstruct = */ NULL,
6192 /* .pfnDestruct = */ NULL,
6193 /* .pfnFinalDestruct = */ NULL,
6194 /* .pfnRequest = */ NULL,
6195 /* .pfnReserved0 = */ NULL,
6196 /* .pfnReserved1 = */ NULL,
6197 /* .pfnReserved2 = */ NULL,
6198 /* .pfnReserved3 = */ NULL,
6199 /* .pfnReserved4 = */ NULL,
6200 /* .pfnReserved5 = */ NULL,
6201 /* .pfnReserved6 = */ NULL,
6202 /* .pfnReserved7 = */ NULL,
6203#elif defined(IN_RC)
6204 /* .pfnConstruct = */ NULL,
6205 /* .pfnReserved0 = */ NULL,
6206 /* .pfnReserved1 = */ NULL,
6207 /* .pfnReserved2 = */ NULL,
6208 /* .pfnReserved3 = */ NULL,
6209 /* .pfnReserved4 = */ NULL,
6210 /* .pfnReserved5 = */ NULL,
6211 /* .pfnReserved6 = */ NULL,
6212 /* .pfnReserved7 = */ NULL,
6213#else
6214# error "Not in IN_RING3, IN_RING0 or IN_RC!"
6215#endif
6216 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
6217};
6218
6219#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette