VirtualBox

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

Last change on this file since 53426 was 53210, checked in by vboxsync, 10 years ago

PDM/VUSB: Provide port/device speed to HCs (and emulated devices).

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