VirtualBox

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

Last change on this file since 57526 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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