VirtualBox

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

Last change on this file since 27798 was 27686, checked in by vboxsync, 15 years ago

DevOHCI: fixed segfault in rhport_set_if_connected()

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

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