VirtualBox

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

Last change on this file since 45667 was 45667, checked in by vboxsync, 12 years ago

build fix

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