VirtualBox

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

Last change on this file since 55365 was 54381, checked in by vboxsync, 10 years ago

Typo fix.

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