VirtualBox

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

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

Relevant PDMDevHlpPhysWrite changed to PDMDevHlpPCIPhysWrite. If this breaks anything, add PDM_DO_NOT_RESPECT_PCI_BM_BIT to VMM_COMMON_DEFS to disable the feature.

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