VirtualBox

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

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

OHCI: Immediately wake up the frame thread when an URB completed and reset the frame rate to 1KHz

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 210.4 KB
Line 
1/* $Id: DevOHCI.cpp 53911 2015-01-22 11:34:26Z 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 * Calculate frame timer variables given a frame rate (1,000 Hz is the full speed).
2157 */
2158static void ohciCalcTimerIntervals(POHCI pThis, uint32_t u32FrameRate)
2159{
2160 Assert(u32FrameRate <= OHCI_DEFAULT_TIMER_FREQ);
2161
2162 pThis->cTicksPerFrame = pThis->u64TimerHz / u32FrameRate;
2163 if (!pThis->cTicksPerFrame)
2164 pThis->cTicksPerFrame = 1;
2165 pThis->cTicksPerUsbTick = pThis->u64TimerHz >= VUSB_BUS_HZ ? pThis->u64TimerHz / VUSB_BUS_HZ : 1;
2166 pThis->nsWait = RT_NS_1SEC / u32FrameRate;
2167 pThis->uFrameRate = u32FrameRate;
2168}
2169
2170
2171/**
2172 * Calculates the new frame rate based on the idle detection and number of idle
2173 * cycles.
2174 *
2175 * @returns nothing.
2176 * @param pThis The OHCI device data.
2177 */
2178static void ohciFramerateCalcNew(POHCI pThis)
2179{
2180 uint32_t uNewFrameRate = pThis->uFrameRate;
2181
2182 /*
2183 * Adjust the frame timer interval based on idle detection.
2184 */
2185 if (pThis->fIdle)
2186 {
2187 pThis->cIdleCycles++;
2188 /* Set the new frame rate based on how long we've been idle. Tunable. */
2189 switch (pThis->cIdleCycles)
2190 {
2191 case 4: uNewFrameRate = 500; break; /* 2ms interval */
2192 case 16:uNewFrameRate = 125; break; /* 8ms interval */
2193 case 24:uNewFrameRate = 50; break; /* 20ms interval */
2194 default: break;
2195 }
2196 /* Avoid overflow. */
2197 if (pThis->cIdleCycles > 60000)
2198 pThis->cIdleCycles = 20000;
2199 }
2200 else
2201 {
2202 if (pThis->cIdleCycles)
2203 {
2204 pThis->cIdleCycles = 0;
2205 uNewFrameRate = OHCI_DEFAULT_TIMER_FREQ;
2206 }
2207 }
2208 if (uNewFrameRate != pThis->uFrameRate)
2209 ohciCalcTimerIntervals(pThis, uNewFrameRate);
2210}
2211
2212
2213/**
2214 * Returns the OHCI_CC_* corresponding to the VUSB status code.
2215 *
2216 * @returns OHCI_CC_* value.
2217 * @param enmStatus The VUSB status code.
2218 */
2219static uint32_t ohciVUsbStatus2OhciStatus(VUSBSTATUS enmStatus)
2220{
2221 switch (enmStatus)
2222 {
2223 case VUSBSTATUS_OK: return OHCI_CC_NO_ERROR;
2224 case VUSBSTATUS_STALL: return OHCI_CC_STALL;
2225 case VUSBSTATUS_CRC: return OHCI_CC_CRC;
2226 case VUSBSTATUS_DATA_UNDERRUN: return OHCI_CC_DATA_UNDERRUN;
2227 case VUSBSTATUS_DATA_OVERRUN: return OHCI_CC_DATA_OVERRUN;
2228 case VUSBSTATUS_DNR: return OHCI_CC_DNR;
2229 case VUSBSTATUS_NOT_ACCESSED: return OHCI_CC_NOT_ACCESSED_1;
2230 default:
2231 Log(("pUrb->enmStatus=%#x!!!\n", enmStatus));
2232 return OHCI_CC_DNR;
2233 }
2234}
2235
2236/**
2237 * Worker for ohciRhXferCompletion that handles the completion of
2238 * a URB made up of isochronous TDs.
2239 *
2240 * In general, all URBs should have status OK.
2241 */
2242static void ohciRhXferCompleteIsochronousURB(POHCI pThis, PVUSBURB pUrb, POHCIED pEd, int cFmAge)
2243{
2244 /*
2245 * Copy the data back (if IN operation) and update the TDs.
2246 */
2247 for (unsigned iTd = 0; iTd < pUrb->Hci.cTds; iTd++)
2248 {
2249 POHCIITD pITd = (POHCIITD)&pUrb->Hci.paTds[iTd].TdCopy[0];
2250 const uint32_t ITdAddr = pUrb->Hci.paTds[iTd].TdAddr;
2251 const unsigned cFrames = ((pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
2252 unsigned R = (pUrb->Hci.u32FrameNo & ITD_HWINFO_SF) - (pITd->HwInfo & ITD_HWINFO_SF);
2253 if (R >= 8)
2254 R = 0; /* submitted ahead of time. */
2255
2256 /*
2257 * Only one case of TD level condition code is document, so
2258 * just set NO_ERROR here to reduce number duplicate code.
2259 */
2260 pITd->HwInfo &= ~TD_HWINFO_CC;
2261 AssertCompile(OHCI_CC_NO_ERROR == 0);
2262
2263 if (pUrb->enmStatus == VUSBSTATUS_OK)
2264 {
2265 /*
2266 * Update the frames and copy back the data.
2267 * We assume that we don't get incorrect lengths here.
2268 */
2269 for (unsigned i = 0; i < cFrames; i++)
2270 {
2271 if ( i < R
2272 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_NOT_ACCESSED)
2273 {
2274 /* It should already be NotAccessed. */
2275 pITd->aPSW[i] |= 0xe000; /* (Don't touch the 12th bit.) */
2276 continue;
2277 }
2278
2279 /* Update the PSW (save the offset first in case of a IN). */
2280 uint32_t off = pITd->aPSW[i] & ITD_PSW_OFFSET;
2281 pITd->aPSW[i] = ohciVUsbStatus2OhciStatus(pUrb->aIsocPkts[i - R].enmStatus)
2282 >> (TD_HWINFO_CC_SHIFT - ITD_PSW_CC_SHIFT);
2283
2284 if ( pUrb->enmDir == VUSBDIRECTION_IN
2285 && ( pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_OK
2286 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_DATA_UNDERRUN
2287 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_DATA_OVERRUN))
2288 {
2289 /* Set the size. */
2290 const unsigned cb = pUrb->aIsocPkts[i - R].cb;
2291 pITd->aPSW[i] |= cb & ITD_PSW_SIZE;
2292 /* Copy data. */
2293 if (cb)
2294 {
2295 uint8_t *pb = &pUrb->abData[pUrb->aIsocPkts[i - R].off];
2296 if (off + cb > 0x1000)
2297 {
2298 if (off < 0x1000)
2299 {
2300 /* both */
2301 const unsigned cb0 = 0x1000 - off;
2302 ohciPhysWrite(pThis, (pITd->BP0 & ITD_BP0_MASK) + off, pb, cb0);
2303 ohciPhysWrite(pThis, pITd->BE & ITD_BP0_MASK, pb + cb0, cb - cb0);
2304 }
2305 else /* only in the 2nd page */
2306 ohciPhysWrite(pThis, (pITd->BE & ITD_BP0_MASK) + (off & ITD_BP0_MASK), pb, cb);
2307 }
2308 else /* only in the 1st page */
2309 ohciPhysWrite(pThis, (pITd->BP0 & ITD_BP0_MASK) + off, pb, cb);
2310 Log5(("packet %d: off=%#x cb=%#x pb=%p (%#x)\n"
2311 "%.*Rhxd\n",
2312 i + R, off, cb, pb, pb - &pUrb->abData[0], cb, pb));
2313 //off += cb;
2314 }
2315 }
2316 }
2317
2318 /*
2319 * If the last package ended with a NotAccessed status, set ITD CC
2320 * to DataOverrun to indicate scheduling overrun.
2321 */
2322 if (pUrb->aIsocPkts[pUrb->cIsocPkts - 1].enmStatus == VUSBSTATUS_NOT_ACCESSED)
2323 pITd->HwInfo |= OHCI_CC_DATA_OVERRUN;
2324 }
2325 else
2326 {
2327 Log(("DevOHCI: Taking untested code path at line %d...\n", __LINE__));
2328 /*
2329 * Most status codes only applies to the individual packets.
2330 *
2331 * If we get a URB level error code of this kind, we'll distribute
2332 * it to all the packages unless some other status is available for
2333 * a package. This is a bit fuzzy, and we will get rid of this code
2334 * before long!
2335 */
2336 //if (pUrb->enmStatus != VUSBSTATUS_DATA_OVERRUN)
2337 {
2338 const unsigned uCC = ohciVUsbStatus2OhciStatus(pUrb->enmStatus)
2339 >> (TD_HWINFO_CC_SHIFT - ITD_PSW_CC_SHIFT);
2340 for (unsigned i = 0; i < cFrames; i++)
2341 pITd->aPSW[i] = uCC;
2342 }
2343 //else
2344 // pITd->HwInfo |= ohciVUsbStatus2OhciStatus(pUrb->enmStatus);
2345 }
2346
2347 /*
2348 * Update the done queue interrupt timer.
2349 */
2350 uint32_t DoneInt = (pITd->HwInfo & ITD_HWINFO_DI) >> ITD_HWINFO_DI_SHIFT;
2351 if ((pITd->HwInfo & TD_HWINFO_CC) != OHCI_CC_NO_ERROR)
2352 DoneInt = 0; /* It's cleared on error. */
2353 if ( DoneInt != 0x7
2354 && DoneInt < pThis->dqic)
2355 pThis->dqic = DoneInt;
2356
2357 /*
2358 * Move on to the done list and write back the modified TD.
2359 */
2360#ifdef LOG_ENABLED
2361 if (!pThis->done)
2362 pThis->u32FmDoneQueueTail = pThis->HcFmNumber;
2363# ifdef VBOX_STRICT
2364 ohci_in_done_queue_add(pThis, ITdAddr);
2365# endif
2366#endif
2367 pITd->NextTD = pThis->done;
2368 pThis->done = ITdAddr;
2369
2370 Log(("%s: ohciRhXferCompleteIsochronousURB: ITdAddr=%#010x EdAddr=%#010x SF=%#x (%#x) CC=%#x FC=%d "
2371 "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",
2372 pUrb->pszDesc, ITdAddr,
2373 pUrb->Hci.EdAddr,
2374 pITd->HwInfo & ITD_HWINFO_SF, pThis->HcFmNumber,
2375 (pITd->HwInfo & ITD_HWINFO_CC) >> ITD_HWINFO_CC_SHIFT,
2376 (pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT,
2377 pITd->aPSW[0] >> ITD_PSW_CC_SHIFT, pITd->aPSW[0] & ITD_PSW_SIZE,
2378 pITd->aPSW[1] >> ITD_PSW_CC_SHIFT, pITd->aPSW[1] & ITD_PSW_SIZE,
2379 pITd->aPSW[2] >> ITD_PSW_CC_SHIFT, pITd->aPSW[2] & ITD_PSW_SIZE,
2380 pITd->aPSW[3] >> ITD_PSW_CC_SHIFT, pITd->aPSW[3] & ITD_PSW_SIZE,
2381 pITd->aPSW[4] >> ITD_PSW_CC_SHIFT, pITd->aPSW[4] & ITD_PSW_SIZE,
2382 pITd->aPSW[5] >> ITD_PSW_CC_SHIFT, pITd->aPSW[5] & ITD_PSW_SIZE,
2383 pITd->aPSW[6] >> ITD_PSW_CC_SHIFT, pITd->aPSW[6] & ITD_PSW_SIZE,
2384 pITd->aPSW[7] >> ITD_PSW_CC_SHIFT, pITd->aPSW[7] & ITD_PSW_SIZE,
2385 R));
2386 ohciWriteITd(pThis, ITdAddr, pITd, "retired");
2387 }
2388}
2389
2390
2391/**
2392 * Worker for ohciRhXferCompletion that handles the completion of
2393 * a URB made up of general TDs.
2394 */
2395static void ohciRhXferCompleteGeneralURB(POHCI pThis, PVUSBURB pUrb, POHCIED pEd, int cFmAge)
2396{
2397 /*
2398 * Copy the data back (if IN operation) and update the TDs.
2399 */
2400 unsigned cbLeft = pUrb->cbData;
2401 uint8_t *pb = &pUrb->abData[0];
2402 for (unsigned iTd = 0; iTd < pUrb->Hci.cTds; iTd++)
2403 {
2404 POHCITD pTd = (POHCITD)&pUrb->Hci.paTds[iTd].TdCopy[0];
2405 const uint32_t TdAddr = pUrb->Hci.paTds[iTd].TdAddr;
2406
2407 /*
2408 * Setup a ohci transfer buffer and calc the new cbp value.
2409 */
2410 OHCIBUF Buf;
2411 ohciBufInit(&Buf, pTd->cbp, pTd->be);
2412 uint32_t NewCbp;
2413 if (cbLeft >= Buf.cbTotal)
2414 NewCbp = 0;
2415 else
2416 {
2417 /* (len may have changed for short transfers) */
2418 Buf.cbTotal = cbLeft;
2419 ohciBufUpdate(&Buf);
2420 Assert(Buf.cVecs >= 1);
2421 NewCbp = Buf.aVecs[Buf.cVecs-1].Addr + Buf.aVecs[Buf.cVecs-1].cb;
2422 }
2423
2424 /*
2425 * Write back IN buffers.
2426 */
2427 if ( pUrb->enmDir == VUSBDIRECTION_IN
2428 && ( pUrb->enmStatus == VUSBSTATUS_OK
2429 || pUrb->enmStatus == VUSBSTATUS_DATA_OVERRUN
2430 || pUrb->enmStatus == VUSBSTATUS_DATA_UNDERRUN)
2431 && Buf.cbTotal > 0)
2432 {
2433 Assert(Buf.cVecs > 0);
2434 ohciPhysWrite(pThis, Buf.aVecs[0].Addr, pb, Buf.aVecs[0].cb);
2435 if (Buf.cVecs > 1)
2436 ohciPhysWrite(pThis, Buf.aVecs[1].Addr, pb + Buf.aVecs[0].cb, Buf.aVecs[1].cb);
2437 }
2438
2439 /* advance the data buffer. */
2440 cbLeft -= Buf.cbTotal;
2441 pb += Buf.cbTotal;
2442
2443 /*
2444 * Set writeback field.
2445 */
2446 /* zero out writeback fields for retirement */
2447 pTd->hwinfo &= ~TD_HWINFO_CC;
2448 /* always update the CurrentBufferPointer; essential for underrun/overrun errors */
2449 pTd->cbp = NewCbp;
2450
2451 if (pUrb->enmStatus == VUSBSTATUS_OK)
2452 {
2453 pTd->hwinfo &= ~TD_HWINFO_ERRORS;
2454
2455 /* update done queue interrupt timer */
2456 uint32_t DoneInt = (pTd->hwinfo & TD_HWINFO_DI) >> 21;
2457 if ( DoneInt != 0x7
2458 && DoneInt < pThis->dqic)
2459 pThis->dqic = DoneInt;
2460 Log(("%s: ohciRhXferCompleteGeneralURB: ED=%#010x TD=%#010x Age=%d cbTotal=%#x NewCbp=%#010RX32 dqic=%d\n",
2461 pUrb->pszDesc, pUrb->Hci.EdAddr, TdAddr, cFmAge, pUrb->enmStatus, Buf.cbTotal, NewCbp, pThis->dqic));
2462 }
2463 else
2464 {
2465 Log(("%s: ohciRhXferCompleteGeneralURB: HALTED ED=%#010x TD=%#010x (age %d) pUrb->enmStatus=%d\n",
2466 pUrb->pszDesc, pUrb->Hci.EdAddr, TdAddr, cFmAge, pUrb->enmStatus));
2467 pEd->HeadP |= ED_HEAD_HALTED;
2468 pThis->dqic = 0; /* "If the Transfer Descriptor is being retired with an error,
2469 * then the Done Queue Interrupt Counter is cleared as if the
2470 * InterruptDelay field were zero."
2471 */
2472 switch (pUrb->enmStatus)
2473 {
2474 case VUSBSTATUS_STALL:
2475 pTd->hwinfo |= OHCI_CC_STALL;
2476 break;
2477 case VUSBSTATUS_CRC:
2478 pTd->hwinfo |= OHCI_CC_CRC;
2479 break;
2480 case VUSBSTATUS_DATA_UNDERRUN:
2481 pTd->hwinfo |= OHCI_CC_DATA_UNDERRUN;
2482 break;
2483 case VUSBSTATUS_DATA_OVERRUN:
2484 pTd->hwinfo |= OHCI_CC_DATA_OVERRUN;
2485 break;
2486 default: /* what the hell */
2487 Log(("pUrb->enmStatus=%#x!!!\n", pUrb->enmStatus));
2488 case VUSBSTATUS_DNR:
2489 pTd->hwinfo |= OHCI_CC_DNR;
2490 break;
2491 }
2492 }
2493
2494 /*
2495 * Move on to the done list and write back the modified TD.
2496 */
2497#ifdef LOG_ENABLED
2498 if (!pThis->done)
2499 pThis->u32FmDoneQueueTail = pThis->HcFmNumber;
2500# ifdef VBOX_STRICT
2501 ohci_in_done_queue_add(pThis, TdAddr);
2502# endif
2503#endif
2504 pTd->NextTD = pThis->done;
2505 pThis->done = TdAddr;
2506
2507 ohciWriteTd(pThis, TdAddr, pTd, "retired");
2508
2509 /*
2510 * If we've halted the endpoint, we stop here.
2511 * ohciUnlinkTds() will make sure we've only unliked the first TD.
2512 *
2513 * The reason for this is that while we can have more than one TD in a URB, real
2514 * OHCI hardware will only deal with one TD at the time and it's therefore incorrect
2515 * to retire TDs after the endpoint has been halted. Win2k will crash or enter infinite
2516 * kernel loop if we don't behave correctly. (See @bugref{1646}.)
2517 */
2518 if (pEd->HeadP & ED_HEAD_HALTED)
2519 break;
2520 }
2521}
2522
2523
2524/**
2525 * Transfer completion callback routine.
2526 *
2527 * VUSB will call this when a transfer have been completed
2528 * in a one or another way.
2529 *
2530 * @param pInterface Pointer to OHCI::ROOTHUB::IRhPort.
2531 * @param pUrb Pointer to the URB in question.
2532 */
2533static DECLCALLBACK(void) ohciRhXferCompletion(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb)
2534{
2535 POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
2536 LogFlow(("%s: ohciRhXferCompletion: EdAddr=%#010RX32 cTds=%d TdAddr0=%#010RX32\n",
2537 pUrb->pszDesc, pUrb->Hci.EdAddr, pUrb->Hci.cTds, pUrb->Hci.paTds[0].TdAddr));
2538
2539 RTCritSectEnter(&pThis->CritSect);
2540 pThis->fIdle = false; /* Mark as active */
2541
2542 /* get the current end point descriptor. */
2543 OHCIED Ed;
2544 ohciReadEd(pThis, pUrb->Hci.EdAddr, &Ed);
2545
2546 /*
2547 * Check that the URB hasn't been canceled and then try unlink the TDs.
2548 *
2549 * We drop the URB if the ED is marked halted/skip ASSUMING that this
2550 * means the HCD has canceled the URB.
2551 *
2552 * If we succeed here (i.e. not dropping the URB), the TdCopy members will
2553 * be updated but not yet written. We will delay the writing till we're done
2554 * with the data copying, buffer pointer advancing and error handling.
2555 */
2556 int cFmAge = ohci_in_flight_remove_urb(pThis, pUrb);
2557 if (pUrb->enmStatus == VUSBSTATUS_UNDO)
2558 {
2559 /* Leave the TD alone - the HCD doesn't want us talking to the device. */
2560 Log(("%s: ohciRhXferCompletion: CANCELED {ED=%#010x cTds=%d TD0=%#010x age %d}\n",
2561 pUrb->pszDesc, pUrb->Hci.EdAddr, pUrb->Hci.cTds, pUrb->Hci.paTds[0].TdAddr, cFmAge));
2562 STAM_COUNTER_INC(&pThis->StatDroppedUrbs);
2563 RTCritSectLeave(&pThis->CritSect);
2564 return;
2565 }
2566 bool fHasBeenCanceled = false;
2567 if ( (Ed.HeadP & ED_HEAD_HALTED)
2568 || (Ed.hwinfo & ED_HWINFO_SKIP)
2569 || cFmAge < 0
2570 || (fHasBeenCanceled = ohciHasUrbBeenCanceled(pThis, pUrb, &Ed))
2571 || !ohciUnlinkTds(pThis, pUrb, &Ed)
2572 )
2573 {
2574 Log(("%s: ohciRhXferCompletion: DROPPED {ED=%#010x cTds=%d TD0=%#010x age %d} because:%s%s%s%s%s!!!\n",
2575 pUrb->pszDesc, pUrb->Hci.EdAddr, pUrb->Hci.cTds, pUrb->Hci.paTds[0].TdAddr, cFmAge,
2576 (Ed.HeadP & ED_HEAD_HALTED) ? " ep halted" : "",
2577 (Ed.hwinfo & ED_HWINFO_SKIP) ? " ep skip" : "",
2578 (Ed.HeadP & ED_PTR_MASK) != pUrb->Hci.paTds[0].TdAddr ? " ep head-changed" : "",
2579 cFmAge < 0 ? " td not-in-flight" : "",
2580 fHasBeenCanceled ? " td canceled" : ""));
2581 NOREF(fHasBeenCanceled);
2582 STAM_COUNTER_INC(&pThis->StatDroppedUrbs);
2583 RTCritSectLeave(&pThis->CritSect);
2584 return;
2585 }
2586
2587 /*
2588 * Complete the TD updating and write the back.
2589 * When appropriate also copy data back to the guest memory.
2590 */
2591 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2592 ohciRhXferCompleteIsochronousURB(pThis, pUrb, &Ed, cFmAge);
2593 else
2594 ohciRhXferCompleteGeneralURB(pThis, pUrb, &Ed, cFmAge);
2595
2596 /* finally write back the endpoint descriptor. */
2597 ohciWriteEd(pThis, pUrb->Hci.EdAddr, &Ed);
2598
2599 /* Calculate new frame rate and wakeup the . */
2600 ohciFramerateCalcNew(pThis);
2601 RTSemEventSignal(pThis->hSemEventFrame);
2602 RTCritSectLeave(&pThis->CritSect);
2603}
2604
2605
2606/**
2607 * Handle transfer errors.
2608 *
2609 * VUSB calls this when a transfer attempt failed. This function will respond
2610 * indicating whether to retry or complete the URB with failure.
2611 *
2612 * @returns true if the URB should be retired.
2613 * @returns false if the URB should be retried.
2614 * @param pInterface Pointer to OHCI::ROOTHUB::IRhPort.
2615 * @param pUrb Pointer to the URB in question.
2616 */
2617static DECLCALLBACK(bool) ohciRhXferError(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb)
2618{
2619 POHCI pThis = VUSBIROOTHUBPORT_2_OHCI(pInterface);
2620
2621 /*
2622 * Isochronous URBs can't be retried.
2623 */
2624 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2625 return true;
2626
2627 /*
2628 * Don't retry on stall.
2629 */
2630 if (pUrb->enmStatus == VUSBSTATUS_STALL)
2631 {
2632 Log2(("%s: ohciRhXferError: STALL, giving up.\n", pUrb->pszDesc, pUrb->enmStatus));
2633 return true;
2634 }
2635
2636 RTCritSectEnter(&pThis->CritSect);
2637
2638 bool fRetire = false;
2639 /*
2640 * Check if the TDs still are valid.
2641 * This will make sure the TdCopy is up to date.
2642 */
2643 const uint32_t TdAddr = pUrb->Hci.paTds[0].TdAddr;
2644/** @todo IMPORTANT! we must check if the ED is still valid at this point!!! */
2645 if (ohciHasUrbBeenCanceled(pThis, pUrb, NULL))
2646 {
2647 Log(("%s: ohciRhXferError: TdAddr0=%#x canceled!\n", pUrb->pszDesc, TdAddr));
2648 fRetire = true;
2649 }
2650 else
2651 {
2652 /*
2653 * Get and update the error counter.
2654 */
2655 POHCITD pTd = (POHCITD)&pUrb->Hci.paTds[0].TdCopy[0];
2656 unsigned cErrs = (pTd->hwinfo & TD_HWINFO_ERRORS) >> TD_ERRORS_SHIFT;
2657 pTd->hwinfo &= ~TD_HWINFO_ERRORS;
2658 cErrs++;
2659 pTd->hwinfo |= (cErrs % TD_ERRORS_MAX) << TD_ERRORS_SHIFT;
2660 ohciWriteTd(pThis, TdAddr, pTd, "ohciRhXferError");
2661
2662 if (cErrs >= TD_ERRORS_MAX - 1)
2663 {
2664 Log2(("%s: ohciRhXferError: too many errors, giving up!\n", pUrb->pszDesc));
2665 fRetire = true;
2666 }
2667 else
2668 Log2(("%s: ohciRhXferError: cErrs=%d: retrying...\n", pUrb->pszDesc, cErrs));
2669 }
2670
2671 RTCritSectLeave(&pThis->CritSect);
2672 return fRetire;
2673}
2674
2675
2676/**
2677 * Service a general transport descriptor.
2678 */
2679static bool ohciServiceTd(POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, uint32_t TdAddr, uint32_t *pNextTdAddr, const char *pszListName)
2680{
2681 /*
2682 * Read the TD and setup the buffer data.
2683 */
2684 OHCITD Td;
2685 ohciReadTd(pThis, TdAddr, &Td);
2686 OHCIBUF Buf;
2687 ohciBufInit(&Buf, Td.cbp, Td.be);
2688
2689 *pNextTdAddr = Td.NextTD & ED_PTR_MASK;
2690
2691 /*
2692 * Determine the direction.
2693 */
2694 VUSBDIRECTION enmDir;
2695 switch (pEd->hwinfo & ED_HWINFO_DIR)
2696 {
2697 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
2698 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
2699 default:
2700 switch (Td.hwinfo & TD_HWINFO_DIR)
2701 {
2702 case TD_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
2703 case TD_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
2704 case 0: enmDir = VUSBDIRECTION_SETUP; break;
2705 default:
2706 Log(("ohciServiceTd: Invalid direction!!!! Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Td.hwinfo, pEd->hwinfo));
2707 /* TODO: Do the correct thing here */
2708 return false;
2709 }
2710 break;
2711 }
2712
2713 pThis->fIdle = false; /* Mark as active */
2714
2715 /*
2716 * Allocate and initialize a new URB.
2717 */
2718 PVUSBURB pUrb = VUSBIRhNewUrb(pThis->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, Buf.cbTotal, 1);
2719 if (!pUrb)
2720 return false; /* retry later... */
2721 Assert(pUrb->Hci.cTds == 1);
2722
2723 pUrb->enmType = enmType;
2724 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
2725 pUrb->enmDir = enmDir;
2726 pUrb->fShortNotOk = !(Td.hwinfo & TD_HWINFO_ROUNDING);
2727 pUrb->enmStatus = VUSBSTATUS_OK;
2728 pUrb->Hci.EdAddr = EdAddr;
2729 pUrb->Hci.fUnlinked = false;
2730 pUrb->Hci.paTds[0].TdAddr = TdAddr;
2731 pUrb->Hci.u32FrameNo = pThis->HcFmNumber;
2732 AssertCompile(sizeof(pUrb->Hci.paTds[0].TdCopy) >= sizeof(Td));
2733 memcpy(pUrb->Hci.paTds[0].TdCopy, &Td, sizeof(Td));
2734#ifdef LOG_ENABLED
2735 static unsigned s_iSerial = 0;
2736 s_iSerial = (s_iSerial + 1) % 10000;
2737 RTStrAPrintf(&pUrb->pszDesc, "URB %p %10s/s%c%04d", pUrb, pszListName,
2738 enmDir == VUSBDIRECTION_IN ? '<' : enmDir == VUSBDIRECTION_OUT ? '>' : '-', s_iSerial);
2739#endif
2740
2741 /* copy data if out bound transfer. */
2742 pUrb->cbData = Buf.cbTotal;
2743 if ( Buf.cbTotal
2744 && Buf.cVecs > 0
2745 && enmDir != VUSBDIRECTION_IN)
2746 {
2747 ohciPhysRead(pThis, Buf.aVecs[0].Addr, pUrb->abData, Buf.aVecs[0].cb);
2748 if (Buf.cVecs > 1)
2749 ohciPhysRead(pThis, Buf.aVecs[1].Addr, &pUrb->abData[Buf.aVecs[0].cb], Buf.aVecs[1].cb);
2750 }
2751
2752 /*
2753 * Submit the URB.
2754 */
2755 ohci_in_flight_add(pThis, TdAddr, pUrb);
2756 Log(("%s: ohciServiceTd: submitting TdAddr=%#010x EdAddr=%#010x cbData=%#x\n",
2757 pUrb->pszDesc, TdAddr, EdAddr, pUrb->cbData));
2758
2759 int rc = VUSBIRhSubmitUrb(pThis->RootHub.pIRhConn, pUrb, &pThis->RootHub.Led);
2760 if (RT_SUCCESS(rc))
2761 return true;
2762
2763 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
2764 Log(("ohciServiceTd: failed submitting TdAddr=%#010x EdAddr=%#010x pUrb=%p!!\n",
2765 TdAddr, EdAddr, pUrb));
2766 ohci_in_flight_remove(pThis, TdAddr);
2767 return false;
2768}
2769
2770
2771/**
2772 * Service a the head TD of an endpoint.
2773 */
2774static bool ohciServiceHeadTd(POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
2775{
2776 /*
2777 * Read the TD, after first checking if it's already in-flight.
2778 */
2779 uint32_t TdAddr = pEd->HeadP & ED_PTR_MASK;
2780 if (ohciIsTdInFlight(pThis, TdAddr))
2781 return false;
2782#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
2783 ohci_in_done_queue_check(pThis, TdAddr);
2784#endif
2785 return ohciServiceTd(pThis, enmType, pEd, EdAddr, TdAddr, &TdAddr, pszListName);
2786}
2787
2788
2789/**
2790 * Service one or more general transport descriptors (bulk or interrupt).
2791 */
2792static bool ohciServiceTdMultiple(POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr,
2793 uint32_t TdAddr, uint32_t *pNextTdAddr, const char *pszListName)
2794{
2795 /*
2796 * Read the TDs involved in this URB.
2797 */
2798 struct OHCITDENTRY
2799 {
2800 /** The TD. */
2801 OHCITD Td;
2802 /** The associated OHCI buffer tracker. */
2803 OHCIBUF Buf;
2804 /** The TD address. */
2805 uint32_t TdAddr;
2806 /** Pointer to the next element in the chain (stack). */
2807 struct OHCITDENTRY *pNext;
2808 } Head;
2809
2810 /* read the head */
2811 ohciReadTd(pThis, TdAddr, &Head.Td);
2812 ohciBufInit(&Head.Buf, Head.Td.cbp, Head.Td.be);
2813 Head.TdAddr = TdAddr;
2814 Head.pNext = NULL;
2815
2816 /* combine with more TDs. */
2817 struct OHCITDENTRY *pTail = &Head;
2818 unsigned cbTotal = pTail->Buf.cbTotal;
2819 unsigned cTds = 1;
2820 while ( (pTail->Buf.cbTotal == 0x1000 || pTail->Buf.cbTotal == 0x2000)
2821 && !(pTail->Td.hwinfo & TD_HWINFO_ROUNDING) /* This isn't right for *BSD, but let's not . */
2822 && (pTail->Td.NextTD & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
2823 && cTds < 128)
2824 {
2825 struct OHCITDENTRY *pCur = (struct OHCITDENTRY *)alloca(sizeof(*pCur));
2826
2827 pCur->pNext = NULL;
2828 pCur->TdAddr = pTail->Td.NextTD & ED_PTR_MASK;
2829 ohciReadTd(pThis, pCur->TdAddr, &pCur->Td);
2830 ohciBufInit(&pCur->Buf, pCur->Td.cbp, pCur->Td.be);
2831
2832 /* don't combine if the direction doesn't match up. */
2833 if ( (pCur->Td.hwinfo & (TD_HWINFO_DIR))
2834 != (pCur->Td.hwinfo & (TD_HWINFO_DIR)))
2835 break;
2836
2837 pTail->pNext = pCur;
2838 pTail = pCur;
2839 cbTotal += pCur->Buf.cbTotal;
2840 cTds++;
2841 }
2842
2843 /* calc next TD address */
2844 *pNextTdAddr = pTail->Td.NextTD & ED_PTR_MASK;
2845
2846 /*
2847 * Determine the direction.
2848 */
2849 VUSBDIRECTION enmDir;
2850 switch (pEd->hwinfo & ED_HWINFO_DIR)
2851 {
2852 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
2853 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
2854 default:
2855 Log(("ohciServiceTdMultiple: WARNING! Ed.hwdinfo=%#x bulk or interrupt EP shouldn't rely on the TD for direction...\n", pEd->hwinfo));
2856 switch (Head.Td.hwinfo & TD_HWINFO_DIR)
2857 {
2858 case TD_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
2859 case TD_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
2860 default:
2861 Log(("ohciServiceTdMultiple: Invalid direction!!!! Head.Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Head.Td.hwinfo, pEd->hwinfo));
2862 /* TODO: Do the correct thing here */
2863 return false;
2864 }
2865 break;
2866 }
2867
2868 pThis->fIdle = false; /* Mark as active */
2869
2870 /*
2871 * Allocate and initialize a new URB.
2872 */
2873 PVUSBURB pUrb = VUSBIRhNewUrb(pThis->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, cbTotal, cTds);
2874 if (!pUrb)
2875 /* retry later... */
2876 return false;
2877 Assert(pUrb->Hci.cTds == cTds);
2878 Assert(pUrb->cbData == cbTotal);
2879
2880 pUrb->enmType = enmType;
2881 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
2882 pUrb->enmDir = enmDir;
2883 pUrb->fShortNotOk = !(pTail->Td.hwinfo & TD_HWINFO_ROUNDING);
2884 pUrb->enmStatus = VUSBSTATUS_OK;
2885 pUrb->Hci.EdAddr = EdAddr;
2886 pUrb->Hci.fUnlinked = false;
2887 pUrb->Hci.u32FrameNo = pThis->HcFmNumber;
2888#ifdef LOG_ENABLED
2889 static unsigned s_iSerial = 0;
2890 s_iSerial = (s_iSerial + 1) % 10000;
2891 RTStrAPrintf(&pUrb->pszDesc, "URB %p %10s/m%c%04d", pUrb, pszListName,
2892 enmDir == VUSBDIRECTION_IN ? '<' : enmDir == VUSBDIRECTION_OUT ? '>' : '-', s_iSerial);
2893#endif
2894
2895 /* Copy data and TD information. */
2896 unsigned iTd = 0;
2897 uint8_t *pb = &pUrb->abData[0];
2898 for (struct OHCITDENTRY *pCur = &Head; pCur; pCur = pCur->pNext, iTd++)
2899 {
2900 /* data */
2901 if ( cbTotal
2902 && enmDir != VUSBDIRECTION_IN
2903 && pCur->Buf.cVecs > 0)
2904 {
2905 ohciPhysRead(pThis, pCur->Buf.aVecs[0].Addr, pb, pCur->Buf.aVecs[0].cb);
2906 if (pCur->Buf.cVecs > 1)
2907 ohciPhysRead(pThis, pCur->Buf.aVecs[1].Addr, pb + pCur->Buf.aVecs[0].cb, pCur->Buf.aVecs[1].cb);
2908 }
2909 pb += pCur->Buf.cbTotal;
2910
2911 /* TD info */
2912 pUrb->Hci.paTds[iTd].TdAddr = pCur->TdAddr;
2913 AssertCompile(sizeof(pUrb->Hci.paTds[iTd].TdCopy) >= sizeof(pCur->Td));
2914 memcpy(pUrb->Hci.paTds[iTd].TdCopy, &pCur->Td, sizeof(pCur->Td));
2915 }
2916
2917 /*
2918 * Submit the URB.
2919 */
2920 ohci_in_flight_add_urb(pThis, pUrb);
2921 Log(("%s: ohciServiceTdMultiple: submitting cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x\n",
2922 pUrb->pszDesc, pUrb->cbData, EdAddr, cTds, TdAddr));
2923 int rc = VUSBIRhSubmitUrb(pThis->RootHub.pIRhConn, pUrb, &pThis->RootHub.Led);
2924 if (RT_SUCCESS(rc))
2925 return true;
2926
2927 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
2928 Log(("ohciServiceTdMultiple: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x - rc=%Rrc\n",
2929 pUrb, cbTotal, EdAddr, cTds, TdAddr, rc));
2930 for (struct OHCITDENTRY *pCur = &Head; pCur; pCur = pCur->pNext, iTd++)
2931 ohci_in_flight_remove(pThis, pCur->TdAddr);
2932 return false;
2933}
2934
2935
2936/**
2937 * Service the head TD of an endpoint.
2938 */
2939static bool ohciServiceHeadTdMultiple(POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
2940{
2941 /*
2942 * First, check that it's not already in-flight.
2943 */
2944 uint32_t TdAddr = pEd->HeadP & ED_PTR_MASK;
2945 if (ohciIsTdInFlight(pThis, TdAddr))
2946 return false;
2947#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
2948 ohci_in_done_queue_check(pThis, TdAddr);
2949#endif
2950 return ohciServiceTdMultiple(pThis, enmType, pEd, EdAddr, TdAddr, &TdAddr, pszListName);
2951}
2952
2953
2954/**
2955 * A worker for ohciServiceIsochronousEndpoint which unlinks a ITD
2956 * that belongs to the past.
2957 */
2958static bool ohciServiceIsochronousTdUnlink(POHCI pThis, POHCIITD pITd, uint32_t ITdAddr, uint32_t ITdAddrPrev,
2959 PVUSBURB pUrb, POHCIED pEd, uint32_t EdAddr)
2960{
2961 LogFlow(("%s%sohciServiceIsochronousTdUnlink: Unlinking ITD: ITdAddr=%#010x EdAddr=%#010x ITdAddrPrev=%#010x\n",
2962 pUrb ? pUrb->pszDesc : "", pUrb ? ": " : "", ITdAddr, EdAddr, ITdAddrPrev));
2963
2964 /*
2965 * Do the unlinking.
2966 */
2967 const uint32_t ITdAddrNext = pITd->NextTD & ED_PTR_MASK;
2968 if (ITdAddrPrev)
2969 {
2970 /* Get validate the previous TD */
2971 int iInFlightPrev = ohci_in_flight_find(pThis, ITdAddr);
2972 AssertMsgReturn(iInFlightPrev >= 0, ("ITdAddr=%#RX32\n", ITdAddr), false);
2973 PVUSBURB pUrbPrev = pThis->aInFlight[iInFlightPrev].pUrb;
2974 if (ohciHasUrbBeenCanceled(pThis, pUrbPrev, pEd)) /* ensures the copy is correct. */
2975 return false;
2976
2977 /* Update the copy and write it back. */
2978 POHCIITD pITdPrev = ((POHCIITD)pUrbPrev->Hci.paTds[0].TdCopy);
2979 pITdPrev->NextTD = (pITdPrev->NextTD & ~ED_PTR_MASK) | ITdAddrNext;
2980 ohciWriteITd(pThis, ITdAddrPrev, pITdPrev, "ohciServiceIsochronousEndpoint");
2981 }
2982 else
2983 {
2984 /* It's the head node. update the copy from the caller and write it back. */
2985 pEd->HeadP = (pEd->HeadP & ~ED_PTR_MASK) | ITdAddrNext;
2986 ohciWriteEd(pThis, EdAddr, pEd);
2987 }
2988
2989 /*
2990 * If it's in flight, just mark the URB as unlinked (there is only one ITD per URB atm).
2991 * Otherwise, retire it to the done queue with an error and cause a done line interrupt (?).
2992 */
2993 if (pUrb)
2994 {
2995 pUrb->Hci.fUnlinked = true;
2996 if (ohciHasUrbBeenCanceled(pThis, pUrb, pEd)) /* ensures the copy is correct (paranoia). */
2997 return false;
2998
2999 POHCIITD pITdCopy = ((POHCIITD)pUrb->Hci.paTds[0].TdCopy);
3000 pITd->NextTD = pITdCopy->NextTD &= ~ED_PTR_MASK;
3001 }
3002 else
3003 {
3004 pITd->HwInfo &= ~ITD_HWINFO_CC;
3005 pITd->HwInfo |= OHCI_CC_DATA_OVERRUN;
3006
3007 pITd->NextTD = pThis->done;
3008 pThis->done = ITdAddr;
3009
3010 pThis->dqic = 0;
3011 }
3012
3013 ohciWriteITd(pThis, ITdAddr, pITd, "ohciServiceIsochronousTdUnlink");
3014 return true;
3015}
3016
3017
3018/**
3019 * A worker for ohciServiceIsochronousEndpoint which submits the specified TD.
3020 *
3021 * @returns true on success.
3022 * @returns false on failure to submit.
3023 * @param R The start packet (frame) relative to the start of frame in HwInfo.
3024 */
3025static bool ohciServiceIsochronousTd(POHCI pThis, POHCIITD pITd, uint32_t ITdAddr, const unsigned R, PCOHCIED pEd, uint32_t EdAddr)
3026{
3027 /*
3028 * Determine the endpoint direction.
3029 */
3030 VUSBDIRECTION enmDir;
3031 switch (pEd->hwinfo & ED_HWINFO_DIR)
3032 {
3033 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3034 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3035 default:
3036 Log(("ohciServiceIsochronousTd: Invalid direction!!!! Ed.hwdinfo=%#x\n", pEd->hwinfo));
3037 /* Should probably raise an unrecoverable HC error here */
3038 return false;
3039 }
3040
3041 /*
3042 * Extract the packet sizes and calc the total URB size.
3043 */
3044 struct
3045 {
3046 uint16_t cb;
3047 uint16_t off;
3048 } aPkts[ITD_NUM_PSW];
3049
3050 /* first entry (R) */
3051 uint32_t cbTotal = 0;
3052 if (((uint32_t)pITd->aPSW[R] >> ITD_PSW_CC_SHIFT) < (OHCI_CC_NOT_ACCESSED_0 >> TD_HWINFO_CC_SHIFT))
3053 Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, R, pITd->aPSW[R] >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
3054 uint16_t offPrev = aPkts[0].off = (pITd->aPSW[R] & ITD_PSW_OFFSET);
3055
3056 /* R+1..cFrames */
3057 const unsigned cFrames = ((pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
3058 for (unsigned iR = R + 1; iR < cFrames; iR++)
3059 {
3060 const uint16_t PSW = pITd->aPSW[iR];
3061 const uint16_t off = aPkts[iR - R].off = (PSW & ITD_PSW_OFFSET);
3062 cbTotal += aPkts[iR - R - 1].cb = off - offPrev;
3063 if (off < offPrev)
3064 Log(("ITdAddr=%RX32 PSW%d.offset=%#x < offPrev=%#x!\n", ITdAddr, iR, off, offPrev)); /* => Unrecoverable Error*/
3065 if (((uint32_t)PSW >> ITD_PSW_CC_SHIFT) < (OHCI_CC_NOT_ACCESSED_0 >> TD_HWINFO_CC_SHIFT))
3066 Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, iR, PSW >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
3067 offPrev = off;
3068 }
3069
3070 /* calc offEnd and figure out the size of the last packet. */
3071 const uint32_t offEnd = (pITd->BE & 0xfff)
3072 + (((pITd->BE & ITD_BP0_MASK) != (pITd->BP0 & ITD_BP0_MASK)) << 12)
3073 + 1 /* BE is inclusive */;
3074 if (offEnd < offPrev)
3075 Log(("ITdAddr=%RX32 offEnd=%#x < offPrev=%#x!\n", ITdAddr, offEnd, offPrev)); /* => Unrecoverable Error*/
3076 cbTotal += aPkts[cFrames - 1 - R].cb = offEnd - offPrev;
3077 Assert(cbTotal <= 0x2000);
3078
3079 pThis->fIdle = false; /* Mark as active */
3080
3081 /*
3082 * Allocate and initialize a new URB.
3083 */
3084 PVUSBURB pUrb = VUSBIRhNewUrb(pThis->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, cbTotal, 1);
3085 if (!pUrb)
3086 /* retry later... */
3087 return false;
3088
3089 pUrb->enmType = VUSBXFERTYPE_ISOC;
3090 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3091 pUrb->enmDir = enmDir;
3092 pUrb->fShortNotOk = false;
3093 pUrb->enmStatus = VUSBSTATUS_OK;
3094 pUrb->Hci.EdAddr = EdAddr;
3095 pUrb->Hci.fUnlinked = false;
3096 pUrb->Hci.u32FrameNo = pThis->HcFmNumber;
3097 pUrb->Hci.paTds[0].TdAddr = ITdAddr;
3098 AssertCompile(sizeof(pUrb->Hci.paTds[0].TdCopy) >= sizeof(*pITd));
3099 memcpy(pUrb->Hci.paTds[0].TdCopy, pITd, sizeof(*pITd));
3100#if 0 /* color the data */
3101 memset(pUrb->abData, 0xfe, cbTotal);
3102#endif
3103#ifdef LOG_ENABLED
3104 static unsigned s_iSerial = 0;
3105 s_iSerial = (s_iSerial + 1) % 10000;
3106 RTStrAPrintf(&pUrb->pszDesc, "URB %p isoc%c%04d", pUrb, enmDir == VUSBDIRECTION_IN ? '<' : '>', s_iSerial);
3107#endif
3108
3109 /* copy the data */
3110 if ( cbTotal
3111 && enmDir != VUSBDIRECTION_IN)
3112 {
3113 const uint32_t off0 = pITd->aPSW[R] & ITD_PSW_OFFSET;
3114 if (off0 < 0x1000)
3115 {
3116 if (offEnd > 0x1000)
3117 {
3118 /* both pages. */
3119 const unsigned cb0 = 0x1000 - off0;
3120 ohciPhysRead(pThis, (pITd->BP0 & ITD_BP0_MASK) + off0, &pUrb->abData[0], cb0);
3121 ohciPhysRead(pThis, pITd->BE & ITD_BP0_MASK, &pUrb->abData[cb0], offEnd & 0xfff);
3122 }
3123 else /* a portion of the 1st page. */
3124 ohciPhysRead(pThis, (pITd->BP0 & ITD_BP0_MASK) + off0, pUrb->abData, offEnd - off0);
3125 }
3126 else /* a portion of the 2nd page. */
3127 ohciPhysRead(pThis, (pITd->BE & UINT32_C(0xfffff000)) + (off0 & 0xfff), pUrb->abData, cbTotal);
3128 }
3129
3130 /* setup the packets */
3131 pUrb->cIsocPkts = cFrames - R;
3132 unsigned off = 0;
3133 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
3134 {
3135 pUrb->aIsocPkts[i].enmStatus = VUSBSTATUS_NOT_ACCESSED;
3136 pUrb->aIsocPkts[i].off = off;
3137 off += pUrb->aIsocPkts[i].cb = aPkts[i].cb;
3138 }
3139 Assert(off == cbTotal);
3140
3141 /*
3142 * Submit the URB.
3143 */
3144 ohci_in_flight_add_urb(pThis, pUrb);
3145 Log(("%s: ohciServiceIsochronousTd: submitting cbData=%#x cIsocPkts=%d EdAddr=%#010x TdAddr=%#010x SF=%#x (%#x)\n",
3146 pUrb->pszDesc, pUrb->cbData, pUrb->cIsocPkts, EdAddr, ITdAddr, pITd->HwInfo & ITD_HWINFO_SF, pThis->HcFmNumber));
3147 int rc = VUSBIRhSubmitUrb(pThis->RootHub.pIRhConn, pUrb, &pThis->RootHub.Led);
3148 if (RT_SUCCESS(rc))
3149 return true;
3150
3151 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
3152 Log(("ohciServiceIsochronousTd: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d ITdAddr0=%#010x - rc=%Rrc\n",
3153 pUrb, cbTotal, EdAddr, 1, ITdAddr, rc));
3154 ohci_in_flight_remove(pThis, ITdAddr);
3155 return false;
3156}
3157
3158
3159/**
3160 * Service an isochronous endpoint.
3161 */
3162static void ohciServiceIsochronousEndpoint(POHCI pThis, POHCIED pEd, uint32_t EdAddr)
3163{
3164 /*
3165 * We currently process this as if the guest follows the interrupt end point chaining
3166 * hierarchy described in the documenation. This means that for an isochronous endpoint
3167 * with a 1 ms interval we expect to find in-flight TDs at the head of the list. We will
3168 * skip over all in-flight TDs which timeframe has been exceed. Those which aren't in
3169 * flight but which are too late will be retired (possibly out of order, but, we don't
3170 * care right now).
3171 *
3172 * When we reach a TD which still has a buffer which is due for take off, we will
3173 * stop iterating TDs. If it's in-flight, there isn't anything to be done. Otherwise
3174 * we will push it onto the runway for immediate take off. In this process we
3175 * might have to complete buffers which didn't make it on time, something which
3176 * complicates the kind of status info we need to keep around for the TD.
3177 *
3178 * Note: We're currently not making any attempt at reassembling ITDs into URBs.
3179 * However, this will become necessary because of EMT scheduling and guest
3180 * like linux using one TD for each frame (simple but inefficient for us).
3181 */
3182 OHCIITD ITd;
3183 uint32_t ITdAddr = pEd->HeadP & ED_PTR_MASK;
3184 uint32_t ITdAddrPrev = 0;
3185 uint32_t u32NextFrame = UINT32_MAX;
3186 const uint16_t u16CurFrame = pThis->HcFmNumber;
3187 for (;;)
3188 {
3189 /* check for end-of-chain. */
3190 if ( ITdAddr == (pEd->TailP & ED_PTR_MASK)
3191 || !ITdAddr)
3192 break;
3193
3194 /*
3195 * If isochronous endpoints are around, don't slow down the timer. Getting the timing right
3196 * is difficult enough as it is.
3197 */
3198 pThis->fIdle = false;
3199
3200 /*
3201 * Read the current ITD and check what we're supposed to do about it.
3202 */
3203 ohciReadITd(pThis, ITdAddr, &ITd);
3204 const uint32_t ITdAddrNext = ITd.NextTD & ED_PTR_MASK;
3205 const int16_t R = u16CurFrame - (uint16_t)(ITd.HwInfo & ITD_HWINFO_SF); /* 4.3.2.3 */
3206 const int16_t cFrames = ((ITd.HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
3207
3208 if (R < cFrames)
3209 {
3210 /*
3211 * It's inside the current or a future launch window.
3212 *
3213 * We will try maximize the TD in flight here to deal with EMT scheduling
3214 * issues and similar stuff which will screw up the time. So, we will only
3215 * stop submitting TD when we reach a gap (in time) or end of the list.
3216 */
3217 if ( R < 0 /* (a future frame) */
3218 && (uint16_t)u32NextFrame != (uint16_t)(ITd.HwInfo & ITD_HWINFO_SF))
3219 break;
3220 if (ohci_in_flight_find(pThis, ITdAddr) < 0)
3221 if (!ohciServiceIsochronousTd(pThis, &ITd, ITdAddr, R < 0 ? 0 : R, pEd, EdAddr))
3222 break;
3223
3224 ITdAddrPrev = ITdAddr;
3225 }
3226 else
3227 {
3228#if 1
3229 /*
3230 * Ok, the launch window for this TD has passed.
3231 * If it's not in flight it should be retired with a DataOverrun status (TD).
3232 *
3233 * Don't remove in-flight TDs before they complete.
3234 * Windows will, upon the completion of another ITD it seems, check for if
3235 * any other TDs has been unlinked. If we unlink them before they really
3236 * complete all the packet status codes will be NotAccessed and Windows
3237 * will fail the URB with status USBD_STATUS_ISOCH_REQUEST_FAILED.
3238 *
3239 * I don't know if unlinking TDs out of order could cause similar problems,
3240 * time will show.
3241 */
3242 int iInFlight = ohci_in_flight_find(pThis, ITdAddr);
3243 if (iInFlight >= 0)
3244 ITdAddrPrev = ITdAddr;
3245 else if (!ohciServiceIsochronousTdUnlink(pThis, &ITd, ITdAddr, ITdAddrPrev,
3246 NULL, pEd, EdAddr))
3247 {
3248 Log(("ohciServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
3249 break;
3250 }
3251#else /* BAD IDEA: */
3252 /*
3253 * Ok, the launch window for this TD has passed.
3254 * If it's not in flight it should be retired with a DataOverrun status (TD).
3255 *
3256 * If it's in flight we will try unlink it from the list prematurely to
3257 * help the guest to move on and shorten the list we have to walk. We currently
3258 * are successful with the first URB but then it goes too slowly...
3259 */
3260 int iInFlight = ohci_in_flight_find(pThis, ITdAddr);
3261 if (!ohciServiceIsochronousTdUnlink(pThis, &ITd, ITdAddr, ITdAddrPrev,
3262 iInFlight < 0 ? NULL : pThis->aInFlight[iInFlight].pUrb,
3263 pEd, EdAddr))
3264 {
3265 Log(("ohciServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
3266 break;
3267 }
3268#endif
3269 }
3270
3271 /* advance to the next ITD */
3272 ITdAddr = ITdAddrNext;
3273 u32NextFrame = (ITd.HwInfo & ITD_HWINFO_SF) + cFrames;
3274 }
3275}
3276
3277
3278/**
3279 * Checks if a endpoints has TDs queued and is ready to have them processed.
3280 *
3281 * @returns true if it's ok to process TDs.
3282 * @param pEd The endpoint data.
3283 */
3284DECLINLINE(bool) ohciIsEdReady(PCOHCIED pEd)
3285{
3286 return (pEd->HeadP & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3287 && !(pEd->HeadP & ED_HEAD_HALTED)
3288 && !(pEd->hwinfo & ED_HWINFO_SKIP);
3289}
3290
3291
3292/**
3293 * Checks if an endpoint has TDs queued (not necessarily ready to have them processed).
3294 *
3295 * @returns true if endpoint may have TDs queued.
3296 * @param pEd The endpoint data.
3297 */
3298DECLINLINE(bool) ohciIsEdPresent(PCOHCIED pEd)
3299{
3300 return (pEd->HeadP & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3301 && !(pEd->HeadP & ED_HEAD_HALTED);
3302}
3303
3304
3305/**
3306 * Services the bulk list.
3307 *
3308 * On the bulk list we must reassemble URBs from multiple TDs using heuristics
3309 * derived from USB tracing done in the guests and guest source code (when available).
3310 */
3311static void ohciServiceBulkList(POHCI pThis)
3312{
3313#ifdef LOG_ENABLED
3314 if (g_fLogBulkEPs)
3315 ohciDumpEdList(pThis, pThis->bulk_head, "Bulk before", true);
3316 if (pThis->bulk_cur)
3317 Log(("ohciServiceBulkList: bulk_cur=%#010x before listprocessing!!! HCD have positioned us!!!\n", pThis->bulk_cur));
3318#endif
3319
3320 /*
3321 * ", HC will start processing the Bulk list and will set BF [BulkListFilled] to 0"
3322 * - We've simplified and are always starting at the head of the list and working
3323 * our way thru to the end each time.
3324 */
3325 pThis->status &= ~OHCI_STATUS_BLF;
3326 pThis->fBulkNeedsCleaning = false;
3327 pThis->bulk_cur = 0;
3328
3329 uint32_t EdAddr = pThis->bulk_head;
3330 while (EdAddr)
3331 {
3332 OHCIED Ed;
3333 ohciReadEd(pThis, EdAddr, &Ed);
3334 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3335 if (ohciIsEdReady(&Ed))
3336 {
3337 pThis->status |= OHCI_STATUS_BLF;
3338 pThis->fBulkNeedsCleaning = true;
3339
3340#if 1
3341 /*
3342
3343 * After we figured out that all the TDs submitted for dealing with MSD
3344 * read/write data really makes up on single URB, and that we must
3345 * reassemble these TDs into an URB before submitting it, there is no
3346 * longer any need for servicing anything other than the head *URB*
3347 * on a bulk endpoint.
3348 */
3349 ohciServiceHeadTdMultiple(pThis, VUSBXFERTYPE_BULK, &Ed, EdAddr, "Bulk");
3350#else
3351 /*
3352 * This alternative code was used before we started reassembling URBs from
3353 * multiple TDs. We keep it handy for debugging.
3354 */
3355 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3356 if (!ohciIsTdInFlight(pThis, TdAddr))
3357 {
3358 do
3359 {
3360 if (!ohciServiceTdMultiple(pThis, VUSBXFERTYPE_BULK, &Ed, EdAddr, TdAddr, &TdAddr, "Bulk"))
3361 {
3362 LogFlow(("ohciServiceBulkList: ohciServiceTdMultiple -> false\n"));
3363 break;
3364 }
3365 if ( (TdAddr & ED_PTR_MASK) == (Ed.TailP & ED_PTR_MASK)
3366 || !TdAddr /* paranoia */)
3367 {
3368 LogFlow(("ohciServiceBulkList: TdAddr=%#010RX32 Ed.TailP=%#010RX32\n", TdAddr, Ed.TailP));
3369 break;
3370 }
3371
3372 ohciReadEd(pThis, EdAddr, &Ed); /* It might have been updated on URB completion. */
3373 } while (ohciIsEdReady(&Ed));
3374 }
3375#endif
3376 }
3377 else
3378 {
3379 if (Ed.hwinfo & ED_HWINFO_SKIP)
3380 {
3381 LogFlow(("ohciServiceBulkList: Ed=%#010RX32 Ed.TailP=%#010RX32 SKIP\n", EdAddr, Ed.TailP));
3382 /* If the ED is in 'skip' state, no transactions on it are allowed and we must
3383 * cancel outstanding URBs, if any.
3384 */
3385 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3386 PVUSBURB pUrb = ohciTdInFlightUrb(pThis, TdAddr);
3387 if (pUrb)
3388 pThis->RootHub.pIRhConn->pfnCancelUrbsEp(pThis->RootHub.pIRhConn, pUrb);
3389 }
3390 }
3391
3392 /* next end point */
3393 EdAddr = Ed.NextED & ED_PTR_MASK;
3394
3395 }
3396
3397#ifdef LOG_ENABLED
3398 if (g_fLogBulkEPs)
3399 ohciDumpEdList(pThis, pThis->bulk_head, "Bulk after ", true);
3400#endif
3401}
3402
3403/**
3404 * Abort outstanding transfers on the bulk list.
3405 *
3406 * If the guest disabled bulk list processing, we must abort any outstanding transfers
3407 * (that is, cancel in-flight URBs associated with the list). This is required because
3408 * there may be outstanding read URBs that will never get a response from the device
3409 * and would block further communication.
3410 */
3411static void ohciUndoBulkList(POHCI pThis)
3412{
3413#ifdef LOG_ENABLED
3414 if (g_fLogBulkEPs)
3415 ohciDumpEdList(pThis, pThis->bulk_head, "Bulk before", true);
3416 if (pThis->bulk_cur)
3417 Log(("ohciUndoBulkList: bulk_cur=%#010x before list processing!!! HCD has positioned us!!!\n", pThis->bulk_cur));
3418#endif
3419
3420 /* This flag follows OHCI_STATUS_BLF, but BLF doesn't change when list processing is disabled. */
3421 pThis->fBulkNeedsCleaning = false;
3422
3423 uint32_t EdAddr = pThis->bulk_head;
3424 while (EdAddr)
3425 {
3426 OHCIED Ed;
3427 ohciReadEd(pThis, EdAddr, &Ed);
3428 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3429 if (ohciIsEdPresent(&Ed))
3430 {
3431 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3432 if (ohciIsTdInFlight(pThis, TdAddr))
3433 {
3434 LogFlow(("ohciUndoBulkList: Ed=%#010RX32 Ed.TailP=%#010RX32 UNDO\n", EdAddr, Ed.TailP));
3435 PVUSBURB pUrb = ohciTdInFlightUrb(pThis, TdAddr);
3436 if (pUrb)
3437 pThis->RootHub.pIRhConn->pfnCancelUrbsEp(pThis->RootHub.pIRhConn, pUrb);
3438 }
3439 }
3440 /* next endpoint */
3441 EdAddr = Ed.NextED & ED_PTR_MASK;
3442 }
3443}
3444
3445
3446/**
3447 * Services the control list.
3448 *
3449 * The control list has complex URB assembling, but that's taken
3450 * care of at VUSB level (unlike the other transfer types).
3451 */
3452static void ohciServiceCtrlList(POHCI pThis)
3453{
3454#ifdef LOG_ENABLED
3455 if (g_fLogControlEPs)
3456 ohciDumpEdList(pThis, pThis->ctrl_head, "Ctrl before", true);
3457 if (pThis->ctrl_cur)
3458 Log(("ohciServiceCtrlList: ctrl_cur=%010x before list processing!!! HCD have positioned us!!!\n", pThis->ctrl_cur));
3459#endif
3460
3461 /*
3462 * ", HC will start processing the list and will set ControlListFilled to 0"
3463 * - We've simplified and are always starting at the head of the list and working
3464 * our way thru to the end each time.
3465 */
3466 pThis->status &= ~OHCI_STATUS_CLF;
3467 pThis->ctrl_cur = 0;
3468
3469 uint32_t EdAddr = pThis->ctrl_head;
3470 while (EdAddr)
3471 {
3472 OHCIED Ed;
3473 ohciReadEd(pThis, EdAddr, &Ed);
3474 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3475 if (ohciIsEdReady(&Ed))
3476 {
3477#if 1
3478 /*
3479 * Control TDs depends on order and stage. Only one can be in-flight
3480 * at any given time. OTOH, some stages are completed immediately,
3481 * so we process the list until we've got a head which is in-flight
3482 * or reach the end of the list.
3483 */
3484 do
3485 {
3486 if ( !ohciServiceHeadTd(pThis, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control")
3487 || ohciIsTdInFlight(pThis, Ed.HeadP & ED_PTR_MASK))
3488 {
3489 pThis->status |= OHCI_STATUS_CLF;
3490 break;
3491 }
3492 ohciReadEd(pThis, EdAddr, &Ed); /* It might have been updated on URB completion. */
3493 } while (ohciIsEdReady(&Ed));
3494#else
3495 /* Simplistic, for debugging. */
3496 ohciServiceHeadTd(pThis, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control");
3497 pThis->status |= OHCI_STATUS_CLF;
3498#endif
3499 }
3500
3501 /* next end point */
3502 EdAddr = Ed.NextED & ED_PTR_MASK;
3503 }
3504
3505#ifdef LOG_ENABLED
3506 if (g_fLogControlEPs)
3507 ohciDumpEdList(pThis, pThis->ctrl_head, "Ctrl after ", true);
3508#endif
3509}
3510
3511
3512/**
3513 * Services the periodic list.
3514 *
3515 * On the interrupt portion of the periodic list we must reassemble URBs from multiple
3516 * TDs using heuristics derived from USB tracing done in the guests and guest source
3517 * code (when available).
3518 */
3519static void ohciServicePeriodicList(POHCI pThis)
3520{
3521 /*
3522 * Read the list head from the HCCA.
3523 */
3524 const unsigned iList = pThis->HcFmNumber % OHCI_HCCA_NUM_INTR;
3525 uint32_t EdAddr;
3526 ohciGetDWords(pThis, pThis->hcca + iList * sizeof(EdAddr), &EdAddr, 1);
3527
3528#ifdef LOG_ENABLED
3529 const uint32_t EdAddrHead = EdAddr;
3530 if (g_fLogInterruptEPs)
3531 {
3532 char sz[48];
3533 RTStrPrintf(sz, sizeof(sz), "Int%02x before", iList);
3534 ohciDumpEdList(pThis, EdAddrHead, sz, true);
3535 }
3536#endif
3537
3538 /*
3539 * Iterate the endpoint list.
3540 */
3541 while (EdAddr)
3542 {
3543 OHCIED Ed;
3544 ohciReadEd(pThis, EdAddr, &Ed);
3545
3546 if (ohciIsEdReady(&Ed))
3547 {
3548 /*
3549 * "There is no separate head pointer of isochronous transfers. The first
3550 * isochronous Endpoint Descriptor simply links to the last interrupt
3551 * Endpoint Descriptor."
3552 */
3553 if (!(Ed.hwinfo & ED_HWINFO_ISO))
3554 {
3555 /*
3556 * Presently we will only process the head URB on an interrupt endpoint.
3557 */
3558 ohciServiceHeadTdMultiple(pThis, VUSBXFERTYPE_INTR, &Ed, EdAddr, "Periodic");
3559 }
3560 else if (pThis->ctl & OHCI_CTL_IE)
3561 {
3562 /*
3563 * Presently only the head ITD.
3564 */
3565 ohciServiceIsochronousEndpoint(pThis, &Ed, EdAddr);
3566 }
3567 else
3568 break;
3569 }
3570 else
3571 {
3572 if (Ed.hwinfo & ED_HWINFO_SKIP)
3573 {
3574 LogFlow(("ohciServicePeriodicList: Ed=%#010RX32 Ed.TailP=%#010RX32 SKIP\n", EdAddr, Ed.TailP));
3575 /* If the ED is in 'skip' state, no transactions on it are allowed and we must
3576 * cancel outstanding URBs, if any.
3577 */
3578 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3579 PVUSBURB pUrb = ohciTdInFlightUrb(pThis, TdAddr);
3580 if (pUrb)
3581 pThis->RootHub.pIRhConn->pfnCancelUrbsEp(pThis->RootHub.pIRhConn, pUrb);
3582 }
3583 }
3584 /* next end point */
3585 EdAddr = Ed.NextED & ED_PTR_MASK;
3586 }
3587
3588#ifdef LOG_ENABLED
3589 if (g_fLogInterruptEPs)
3590 {
3591 char sz[48];
3592 RTStrPrintf(sz, sizeof(sz), "Int%02x after ", iList);
3593 ohciDumpEdList(pThis, EdAddrHead, sz, true);
3594 }
3595#endif
3596}
3597
3598
3599/**
3600 * Update the HCCA.
3601 *
3602 * @param pThis The OHCI instance data.
3603 */
3604static void ohciUpdateHCCA(POHCI pThis)
3605{
3606 struct ohci_hcca hcca;
3607 ohciPhysRead(pThis, pThis->hcca + OHCI_HCCA_OFS, &hcca, sizeof(hcca));
3608
3609 hcca.frame = RT_H2LE_U16((uint16_t)pThis->HcFmNumber);
3610 hcca.pad = 0;
3611
3612 bool fWriteDoneHeadInterrupt = false;
3613 if ( pThis->dqic == 0
3614 && (pThis->intr_status & OHCI_INTR_WRITE_DONE_HEAD) == 0)
3615 {
3616 uint32_t done = pThis->done;
3617
3618 if (pThis->intr_status & ~( OHCI_INTR_MASTER_INTERRUPT_ENABLED | OHCI_INTR_OWNERSHIP_CHANGE
3619 | OHCI_INTR_WRITE_DONE_HEAD) )
3620 done |= 0x1;
3621
3622 hcca.done = RT_H2LE_U32(done);
3623 pThis->done = 0;
3624 pThis->dqic = 0x7;
3625
3626 Log(("ohci: Writeback Done (%#010x) on frame %#x (age %#x)\n", hcca.done,
3627 pThis->HcFmNumber, pThis->HcFmNumber - pThis->u32FmDoneQueueTail));
3628#ifdef LOG_ENABLED
3629 ohciDumpTdQueue(pThis, hcca.done & ED_PTR_MASK, "DoneQueue");
3630#endif
3631 Assert(RT_OFFSETOF(struct ohci_hcca, done) == 4);
3632#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
3633 ohci_in_done_queue_zap(pThis);
3634#endif
3635 fWriteDoneHeadInterrupt = true;
3636 }
3637
3638 ohciPhysWrite(pThis, pThis->hcca + OHCI_HCCA_OFS, (uint8_t *)&hcca, sizeof(hcca));
3639 if (fWriteDoneHeadInterrupt)
3640 ohciR3SetInterrupt(pThis, OHCI_INTR_WRITE_DONE_HEAD);
3641}
3642
3643
3644/**
3645 * Go over the in-flight URB list and cancel any URBs that are no longer in use.
3646 * This occurs when the host removes EDs or TDs from the lists and we don't notice
3647 * the sKip bit. Such URBs must be promptly canceled, otherwise there is a risk
3648 * they might "steal" data destined for another URB.
3649 */
3650static void ohciCancelOrphanedURBs(POHCI pThis)
3651{
3652 bool fValidHCCA = !( pThis->hcca >= OHCI_HCCA_MASK
3653 || pThis->hcca < ~OHCI_HCCA_MASK);
3654 unsigned i, cLeft;
3655 int j;
3656 uint32_t EdAddr;
3657 PVUSBURB pUrb;
3658
3659 /* If the HCCA is not currently valid, or there are no in-flight URBs,
3660 * there's nothing to do.
3661 */
3662 if (!fValidHCCA || !pThis->cInFlight)
3663 return;
3664
3665 /* Initially mark all in-flight URBs as inactive. */
3666 for (i = 0, cLeft = pThis->cInFlight; cLeft && i < RT_ELEMENTS(pThis->aInFlight); i++)
3667 {
3668 if (pThis->aInFlight[i].pUrb)
3669 {
3670 pThis->aInFlight[i].fInactive = true;
3671 cLeft--;
3672 }
3673 }
3674 Assert(cLeft == 0);
3675
3676 /* Go over all bulk/control/interrupt endpoint lists; any URB found in these lists
3677 * is marked as active again.
3678 */
3679 for (i = 0; i < OHCI_HCCA_NUM_INTR + 2; i++)
3680 {
3681 switch (i)
3682 {
3683 case OHCI_HCCA_NUM_INTR:
3684 EdAddr = pThis->bulk_head;
3685 break;
3686 case OHCI_HCCA_NUM_INTR + 1:
3687 EdAddr = pThis->ctrl_head;
3688 break;
3689 default:
3690 ohciGetDWords(pThis, pThis->hcca + i * sizeof(EdAddr), &EdAddr, 1);
3691 break;
3692 }
3693 while (EdAddr)
3694 {
3695 OHCIED Ed;
3696 OHCITD Td;
3697 ohciReadEd(pThis, EdAddr, &Ed);
3698 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3699 uint32_t TailP = Ed.TailP & ED_PTR_MASK;
3700 unsigned k = 0;
3701 if ( !(Ed.hwinfo & ED_HWINFO_SKIP)
3702 && (TdAddr != TailP))
3703 {
3704 do
3705 {
3706 ohciReadTd(pThis, TdAddr, &Td);
3707 j = ohci_in_flight_find(pThis, TdAddr);
3708 if (j > -1)
3709 pThis->aInFlight[j].fInactive = false;
3710 TdAddr = Td.NextTD & ED_PTR_MASK;
3711 /* Failsafe for temporarily looped lists. */
3712 if (++k == 128)
3713 break;
3714 } while (TdAddr != (Ed.TailP & ED_PTR_MASK));
3715 }
3716 EdAddr = Ed.NextED & ED_PTR_MASK;
3717 }
3718 }
3719
3720 /* In-flight URBs still marked as inactive are not used anymore and need
3721 * to be canceled.
3722 */
3723 for (i = 0, cLeft = pThis->cInFlight; cLeft && i < RT_ELEMENTS(pThis->aInFlight); i++)
3724 {
3725 if (pThis->aInFlight[i].pUrb)
3726 {
3727 cLeft--;
3728 pUrb = pThis->aInFlight[i].pUrb;
3729 if (pThis->aInFlight[i].fInactive
3730 && pUrb->enmState == VUSBURBSTATE_IN_FLIGHT
3731 && !pUrb->enmType == VUSBXFERTYPE_CTRL)
3732 pThis->RootHub.pIRhConn->pfnCancelUrbsEp(pThis->RootHub.pIRhConn, pUrb);
3733 }
3734 }
3735 Assert(cLeft == 0);
3736}
3737
3738/**
3739 * Generate a Start-Of-Frame event, and set a timer for End-Of-Frame.
3740 */
3741static void ohciStartOfFrame(POHCI pThis)
3742{
3743#ifdef LOG_ENABLED
3744 const uint32_t status_old = pThis->status;
3745#endif
3746
3747 /*
3748 * Update HcFmRemaining.FRT and update start of frame time.
3749 */
3750 pThis->frt = pThis->fit;
3751 pThis->SofTime += pThis->cTicksPerFrame;
3752
3753 /*
3754 * Check that the HCCA address isn't bogus. Linux 2.4.x is known to start
3755 * the bus with a hcca of 0 to work around problem with a specific controller.
3756 */
3757 bool fValidHCCA = !( pThis->hcca >= OHCI_HCCA_MASK
3758 || pThis->hcca < ~OHCI_HCCA_MASK);
3759
3760#if 1
3761 /*
3762 * Update the HCCA.
3763 * Should be done after SOF but before HC read first ED in this frame.
3764 */
3765 if (fValidHCCA)
3766 ohciUpdateHCCA(pThis);
3767#endif
3768
3769 /* "After writing to HCCA, HC will set SF in HcInterruptStatus" - guest isn't executing, so ignore the order! */
3770 ohciR3SetInterrupt(pThis, OHCI_INTR_START_OF_FRAME);
3771
3772 if (pThis->fno)
3773 {
3774 ohciR3SetInterrupt(pThis, OHCI_INTR_FRAMENUMBER_OVERFLOW);
3775 pThis->fno = 0;
3776 }
3777
3778 /* If the HCCA address is invalid, we're quitting here to avoid doing something which cannot be reported to the HCD. */
3779 if (!fValidHCCA)
3780 {
3781 Log(("ohciStartOfFrame: skipping hcca part because hcca=%RX32 (our 'valid' range: %RX32-%RX32)\n",
3782 pThis->hcca, ~OHCI_HCCA_MASK, OHCI_HCCA_MASK));
3783 return;
3784 }
3785
3786 /*
3787 * Periodic EPs.
3788 */
3789 if (pThis->ctl & OHCI_CTL_PLE)
3790 ohciServicePeriodicList(pThis);
3791
3792 /*
3793 * Control EPs.
3794 */
3795 if ( (pThis->ctl & OHCI_CTL_CLE)
3796 && (pThis->status & OHCI_STATUS_CLF) )
3797 ohciServiceCtrlList(pThis);
3798
3799 /*
3800 * Bulk EPs.
3801 */
3802 if ( (pThis->ctl & OHCI_CTL_BLE)
3803 && (pThis->status & OHCI_STATUS_BLF))
3804 ohciServiceBulkList(pThis);
3805 else if ((pThis->status & OHCI_STATUS_BLF)
3806 && pThis->fBulkNeedsCleaning)
3807 ohciUndoBulkList(pThis); /* If list disabled but not empty, abort endpoints. */
3808
3809#if 0
3810 /*
3811 * Update the HCCA after processing the lists and everything. A bit experimental.
3812 *
3813 * ASSUME the guest won't be very upset if a TD is completed, retired and handed
3814 * back immediately. The idea is to be able to retire the data and/or status stages
3815 * of a control transfer together with the setup stage, thus saving a frame. This
3816 * behaviour is should be perfectly ok, since the setup (and maybe data) stages
3817 * have already taken at least one frame to complete.
3818 *
3819 * But, when implementing the first synchronous virtual USB devices, we'll have to
3820 * verify that the guest doesn't choke when having a TD returned in the same frame
3821 * as it was submitted.
3822 */
3823 ohciUpdateHCCA(pThis);
3824#endif
3825
3826#ifdef LOG_ENABLED
3827 if (pThis->status ^ status_old)
3828 {
3829 uint32_t val = pThis->status;
3830 uint32_t chg = val ^ status_old; NOREF(chg);
3831 Log2(("ohciStartOfFrame: HcCommandStatus=%#010x: %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
3832 val,
3833 chg & RT_BIT(0) ? "*" : "", val & 1,
3834 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
3835 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
3836 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
3837 chg & (3<<16)? "*" : "", (val >> 16) & 3));
3838 }
3839#endif
3840
3841 /*
3842 * Adjust the frame timer interval based on idle detection.
3843 */
3844 ohciFramerateCalcNew(pThis);
3845}
3846
3847/**
3848 * Updates the HcFmNumber and FNO registers.
3849 */
3850static void bump_frame_number(POHCI pThis)
3851{
3852 const uint16_t u16OldFmNumber = pThis->HcFmNumber++;
3853 if ((u16OldFmNumber ^ pThis->HcFmNumber) & RT_BIT(15))
3854 pThis->fno = 1;
3855}
3856
3857static DECLCALLBACK(int) ohciR3ThreadFrame(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
3858{
3859 int rc = VINF_SUCCESS;
3860 POHCI pThis = (POHCI)pThread->pvUser;
3861 uint64_t tsNanoStart = 0;
3862
3863 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
3864 return VINF_SUCCESS;
3865
3866 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
3867 {
3868 while ( !ASMAtomicReadBool(&pThis->fBusStarted)
3869 && pThread->enmState == PDMTHREADSTATE_RUNNING
3870 && RT_SUCCESS(rc))
3871 {
3872 /* Signal the waiter that we are stopped now. */
3873 rc = RTSemEventMultiSignal(pThis->hSemEventFrameStopped);
3874 AssertRC(rc);
3875 rc = RTSemEventWait(pThis->hSemEventFrame, RT_INDEFINITE_WAIT);
3876 }
3877
3878 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc);
3879 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
3880 break;
3881
3882 tsNanoStart = RTTimeNanoTS();
3883
3884 RTCritSectEnter(&pThis->CritSect);
3885
3886 /* Reset idle detection flag */
3887 pThis->fIdle = true;
3888
3889 /* Frame boundary, so do EOF stuff here. */
3890 bump_frame_number(pThis);
3891 if ( (pThis->dqic != 0x7) && (pThis->dqic != 0))
3892 pThis->dqic--;
3893
3894 /* Clean up any URBs that have been removed. */
3895 ohciCancelOrphanedURBs(pThis);
3896
3897 /* Start the next frame. */
3898 ohciStartOfFrame(pThis);
3899
3900 RTCritSectLeave(&pThis->CritSect);
3901
3902 /* Wait for the next round. */
3903 RTMSINTERVAL msWait = (RTMSINTERVAL)(((RTTimeNanoTS() + pThis->nsWait) - tsNanoStart) / 1000000);
3904 rc = RTSemEventWait(pThis->hSemEventFrame, msWait);
3905 }
3906
3907 return VINF_SUCCESS;
3908}
3909
3910/**
3911 * Unblock the framer thread so it can respond to a state change.
3912 *
3913 * @returns VBox status code.
3914 * @param pDevIns The device instance.
3915 * @param pThread The send thread.
3916 */
3917static DECLCALLBACK(int) ohciR3ThreadFrameWakeup(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
3918{
3919 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
3920 return RTSemEventSignal(pThis->hSemEventFrame);
3921}
3922
3923/**
3924 * Do frame processing on frame boundary
3925 */
3926static void ohciFrameBoundaryTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
3927{
3928 POHCI pThis = (POHCI)pvUser;
3929 STAM_PROFILE_START(&pThis->StatTimer, a);
3930 STAM_PROFILE_STOP(&pThis->StatTimer, a);
3931}
3932
3933/**
3934 * Start sending SOF tokens across the USB bus, lists are processed in
3935 * next frame
3936 */
3937static void ohciBusStart(POHCI pThis)
3938{
3939 VUSBIDevPowerOn(pThis->RootHub.pIDev);
3940 pThis->dqic = 0x7;
3941
3942 Log(("ohci: %s: Bus started\n", pThis->PciDev.name));
3943
3944 pThis->SofTime = PDMDevHlpTMTimeVirtGet(pThis->CTX_SUFF(pDevIns));
3945 bool fBusActive = ASMAtomicXchgBool(&pThis->fBusStarted, true);
3946 if (!fBusActive)
3947 RTSemEventSignal(pThis->hSemEventFrame);
3948}
3949
3950/**
3951 * Stop sending SOF tokens on the bus
3952 */
3953static void ohciBusStop(POHCI pThis)
3954{
3955 bool fBusActive = ASMAtomicXchgBool(&pThis->fBusStarted, false);
3956 if (fBusActive)
3957 {
3958 int rc = RTSemEventMultiReset(pThis->hSemEventFrameStopped);
3959 AssertRC(rc);
3960
3961 /* Signal the frame thread to stop. */
3962 RTSemEventSignal(pThis->hSemEventFrame);
3963
3964 /* Wait for signal from the thrad that it stopped. */
3965 rc = RTSemEventMultiWait(pThis->hSemEventFrameStopped, RT_INDEFINITE_WAIT);
3966 AssertRC(rc);
3967 }
3968 VUSBIDevPowerOff(pThis->RootHub.pIDev);
3969}
3970
3971/**
3972 * Move in to resume state
3973 */
3974static void ohciBusResume(POHCI pThis, bool fHardware)
3975{
3976 pThis->ctl &= ~OHCI_CTL_HCFS;
3977 pThis->ctl |= OHCI_USB_RESUME;
3978
3979 Log(("pThis: ohciBusResume fHardware=%RTbool RWE=%s\n",
3980 fHardware, (pThis->ctl & OHCI_CTL_RWE) ? "on" : "off"));
3981
3982 if (fHardware && (pThis->ctl & OHCI_CTL_RWE))
3983 ohciR3SetInterrupt(pThis, OHCI_INTR_RESUME_DETECT);
3984
3985 ohciBusStart(pThis);
3986}
3987
3988
3989/* Power a port up or down */
3990static void rhport_power(POHCIROOTHUB pRh, unsigned iPort, bool fPowerUp)
3991{
3992 POHCIHUBPORT pPort = &pRh->aPorts[iPort];
3993 bool fOldPPS = !!(pPort->fReg & OHCI_PORT_PPS);
3994 if (fPowerUp)
3995 {
3996 /* power up */
3997 if (pPort->pDev)
3998 pPort->fReg |= OHCI_PORT_R_CURRENT_CONNECT_STATUS;
3999 if (pPort->fReg & OHCI_PORT_R_CURRENT_CONNECT_STATUS)
4000 pPort->fReg |= OHCI_PORT_R_POWER_STATUS;
4001 if (pPort->pDev && !fOldPPS)
4002 VUSBIDevPowerOn(pPort->pDev);
4003 }
4004 else
4005 {
4006 /* power down */
4007 pPort->fReg &= ~( OHCI_PORT_R_POWER_STATUS
4008 | OHCI_PORT_R_CURRENT_CONNECT_STATUS
4009 | OHCI_PORT_R_SUSPEND_STATUS
4010 | OHCI_PORT_R_RESET_STATUS);
4011 if (pPort->pDev && fOldPPS)
4012 VUSBIDevPowerOff(pPort->pDev);
4013 }
4014}
4015
4016#endif /* IN_RING3 */
4017
4018/**
4019 * Read the HcRevision register.
4020 */
4021static int HcRevision_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4022{
4023 Log2(("HcRevision_r() -> 0x10\n"));
4024 *pu32Value = 0x10; /* OHCI revision 1.0, no emulation. */
4025 return VINF_SUCCESS;
4026}
4027
4028/**
4029 * Write to the HcRevision register.
4030 */
4031static int HcRevision_w(POHCI pThis, uint32_t iReg, uint32_t u32Value)
4032{
4033 Log2(("HcRevision_w(%#010x) - denied\n", u32Value));
4034 AssertMsgFailed(("Invalid operation!!! u32Value=%#010x\n", u32Value));
4035 return VINF_SUCCESS;
4036}
4037
4038/**
4039 * Read the HcControl register.
4040 */
4041static int HcControl_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4042{
4043 uint32_t ctl = pThis->ctl;
4044 Log2(("HcControl_r -> %#010x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
4045 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
4046 (ctl >> 9) & 1, (ctl >> 10) & 1));
4047 *pu32Value = ctl;
4048 return VINF_SUCCESS;
4049}
4050
4051/**
4052 * Write the HcControl register.
4053 */
4054static int HcControl_w(POHCI pThis, uint32_t iReg, uint32_t val)
4055{
4056 /* log it. */
4057 uint32_t chg = pThis->ctl ^ val; NOREF(chg);
4058 Log2(("HcControl_w(%#010x) => %sCBSR=%d %sPLE=%d %sIE=%d %sCLE=%d %sBLE=%d %sHCFS=%#x %sIR=%d %sRWC=%d %sRWE=%d\n",
4059 val,
4060 chg & 3 ? "*" : "", val & 3,
4061 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4062 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4063 chg & RT_BIT(4) ? "*" : "", (val >> 4) & 1,
4064 chg & RT_BIT(5) ? "*" : "", (val >> 5) & 1,
4065 chg & (3 << 6)? "*" : "", (val >> 6) & 3,
4066 chg & RT_BIT(8) ? "*" : "", (val >> 8) & 1,
4067 chg & RT_BIT(9) ? "*" : "", (val >> 9) & 1,
4068 chg & RT_BIT(10) ? "*" : "", (val >> 10) & 1));
4069 if (val & ~0x07ff)
4070 Log2(("Unknown bits %#x are set!!!\n", val & ~0x07ff));
4071
4072 /* see what changed and take action on that. */
4073 uint32_t old_state = pThis->ctl & OHCI_CTL_HCFS;
4074 uint32_t new_state = val & OHCI_CTL_HCFS;
4075
4076#ifdef IN_RING3
4077 pThis->ctl = val;
4078 if (new_state != old_state)
4079 {
4080 switch (new_state)
4081 {
4082 case OHCI_USB_OPERATIONAL:
4083 LogRel(("OHCI: USB Operational\n"));
4084 ohciBusStart(pThis);
4085 break;
4086 case OHCI_USB_SUSPEND:
4087 ohciBusStop(pThis);
4088 LogRel(("OHCI: USB Suspended\n"));
4089 break;
4090 case OHCI_USB_RESUME:
4091 LogRel(("OHCI: USB Resume\n"));
4092 ohciBusResume(pThis, false /* not hardware */);
4093 break;
4094 case OHCI_USB_RESET:
4095 {
4096 LogRel(("OHCI: USB Reset\n"));
4097 ohciBusStop(pThis);
4098 /** @todo This should probably do a real reset, but we don't implement
4099 * that correctly in the roothub reset callback yet. check it's
4100 * comments and argument for more details. */
4101 VUSBIDevReset(pThis->RootHub.pIDev, false /* don't do a real reset */, NULL, NULL, NULL);
4102 break;
4103 }
4104 }
4105 }
4106#else /* !IN_RING3 */
4107 if ( new_state != old_state )
4108 {
4109 Log2(("HcControl_w: state changed -> VINF_IOM_R3_MMIO_WRITE\n"));
4110 return VINF_IOM_R3_MMIO_WRITE;
4111 }
4112 pThis->ctl = val;
4113#endif /* !IN_RING3 */
4114
4115 return VINF_SUCCESS;
4116}
4117
4118/**
4119 * Read the HcCommandStatus register.
4120 */
4121static int HcCommandStatus_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4122{
4123 uint32_t status = pThis->status;
4124 Log2(("HcCommandStatus_r() -> %#010x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
4125 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3));
4126 *pu32Value = status;
4127 return VINF_SUCCESS;
4128}
4129
4130/**
4131 * Write to the HcCommandStatus register.
4132 */
4133static int HcCommandStatus_w(POHCI pThis, uint32_t iReg, uint32_t val)
4134{
4135 /* log */
4136 uint32_t chg = pThis->status ^ val; NOREF(chg);
4137 Log2(("HcCommandStatus_w(%#010x) => %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
4138 val,
4139 chg & RT_BIT(0) ? "*" : "", val & 1,
4140 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
4141 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4142 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4143 chg & (3<<16)? "!!!":"", (pThis->status >> 16) & 3));
4144 if (val & ~0x0003000f)
4145 Log2(("Unknown bits %#x are set!!!\n", val & ~0x0003000f));
4146
4147 /* SOC is read-only */
4148 val = (val & ~OHCI_STATUS_SOC);
4149
4150#ifdef IN_RING3
4151 /* "bits written as '0' remain unchanged in the register" */
4152 pThis->status |= val;
4153 if (pThis->status & OHCI_STATUS_HCR)
4154 {
4155 LogRel(("OHCI: Software reset\n"));
4156 ohciDoReset(pThis, OHCI_USB_SUSPEND, false /* N/A */);
4157 }
4158#else
4159 if ((pThis->status | val) & OHCI_STATUS_HCR)
4160 {
4161 LogFlow(("HcCommandStatus_w: reset -> VINF_IOM_R3_MMIO_WRITE\n"));
4162 return VINF_IOM_R3_MMIO_WRITE;
4163 }
4164 pThis->status |= val;
4165#endif
4166 return VINF_SUCCESS;
4167}
4168
4169/**
4170 * Read the HcInterruptStatus register.
4171 */
4172static int HcInterruptStatus_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4173{
4174 uint32_t val = pThis->intr_status;
4175 Log2(("HcInterruptStatus_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
4176 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4177 (val >> 6) & 1, (val >> 30) & 1));
4178 *pu32Value = val;
4179 return VINF_SUCCESS;
4180}
4181
4182/**
4183 * Write to the HcInterruptStatus register.
4184 */
4185static int HcInterruptStatus_w(POHCI pThis, uint32_t iReg, uint32_t val)
4186{
4187 uint32_t res = pThis->intr_status & ~val;
4188 uint32_t chg = pThis->intr_status ^ res; NOREF(chg);
4189
4190 int rc = PDMCritSectEnter(&pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4191 if (rc != VINF_SUCCESS)
4192 return rc;
4193
4194 Log2(("HcInterruptStatus_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d\n",
4195 val,
4196 chg & RT_BIT(0) ? "*" : "", res & 1,
4197 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4198 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4199 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4200 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4201 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4202 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4203 chg & RT_BIT(30)? "*" : "", (res >> 30) & 1));
4204 if ( (val & ~0xc000007f)
4205 && val != 0xffffffff /* ignore clear-all-like requests from xp. */)
4206 Log2(("Unknown bits %#x are set!!!\n", val & ~0xc000007f));
4207
4208 /* "The Host Controller Driver may clear specific bits in this
4209 * register by writing '1' to bit positions to be cleared"
4210 */
4211 pThis->intr_status &= ~val;
4212 ohciUpdateInterruptLocked(pThis, "HcInterruptStatus_w");
4213 PDMCritSectLeave(&pThis->CsIrq);
4214 return VINF_SUCCESS;
4215}
4216
4217/**
4218 * Read the HcInterruptEnable register
4219 */
4220static int HcInterruptEnable_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4221{
4222 uint32_t val = pThis->intr;
4223 Log2(("HcInterruptEnable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
4224 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4225 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
4226 *pu32Value = val;
4227 return VINF_SUCCESS;
4228}
4229
4230/**
4231 * Writes to the HcInterruptEnable register.
4232 */
4233static int HcInterruptEnable_w(POHCI pThis, uint32_t iReg, uint32_t val)
4234{
4235 uint32_t res = pThis->intr | val;
4236 uint32_t chg = pThis->intr ^ res; NOREF(chg);
4237
4238 int rc = PDMCritSectEnter(&pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4239 if (rc != VINF_SUCCESS)
4240 return rc;
4241
4242 Log2(("HcInterruptEnable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
4243 val,
4244 chg & RT_BIT(0) ? "*" : "", res & 1,
4245 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4246 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4247 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4248 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4249 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4250 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4251 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
4252 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
4253 if (val & ~0xc000007f)
4254 Log2(("Uknown bits %#x are set!!!\n", val & ~0xc000007f));
4255
4256 pThis->intr |= val;
4257 ohciUpdateInterruptLocked(pThis, "HcInterruptEnable_w");
4258 PDMCritSectLeave(&pThis->CsIrq);
4259 return VINF_SUCCESS;
4260}
4261
4262/**
4263 * Reads the HcInterruptDisable register.
4264 */
4265static int HcInterruptDisable_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4266{
4267#if 1 /** @todo r=bird: "On read, the current value of the HcInterruptEnable register is returned." */
4268 uint32_t val = pThis->intr;
4269#else /* old code. */
4270 uint32_t val = ~pThis->intr;
4271#endif
4272 Log2(("HcInterruptDisable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
4273 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4274 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
4275
4276 *pu32Value = val;
4277 return VINF_SUCCESS;
4278}
4279
4280/**
4281 * Writes to the HcInterruptDisable register.
4282 */
4283static int HcInterruptDisable_w(POHCI pThis, uint32_t iReg, uint32_t val)
4284{
4285 uint32_t res = pThis->intr & ~val;
4286 uint32_t chg = pThis->intr ^ res; NOREF(chg);
4287
4288 int rc = PDMCritSectEnter(&pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4289 if (rc != VINF_SUCCESS)
4290 return rc;
4291
4292 Log2(("HcInterruptDisable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
4293 val,
4294 chg & RT_BIT(0) ? "*" : "", res & 1,
4295 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4296 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4297 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4298 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4299 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4300 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4301 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
4302 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
4303 /* Don't bitch about invalid bits here since it makes sense to disable
4304 * interrupts you don't know about. */
4305
4306 pThis->intr &= ~val;
4307 ohciUpdateInterruptLocked(pThis, "HcInterruptDisable_w");
4308 PDMCritSectLeave(&pThis->CsIrq);
4309 return VINF_SUCCESS;
4310}
4311
4312/**
4313 * Read the HcHCCA register (Host Controller Communications Area physical address).
4314 */
4315static int HcHCCA_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4316{
4317 Log2(("HcHCCA_r() -> %#010x\n", pThis->hcca));
4318 *pu32Value = pThis->hcca;
4319 return VINF_SUCCESS;
4320}
4321
4322/**
4323 * Write to the HcHCCA register (Host Controller Communications Area physical address).
4324 */
4325static int HcHCCA_w(POHCI pThis, uint32_t iReg, uint32_t Value)
4326{
4327 Log2(("HcHCCA_w(%#010x) - old=%#010x new=%#010x\n", Value, pThis->hcca, Value & OHCI_HCCA_MASK));
4328 pThis->hcca = Value & OHCI_HCCA_MASK;
4329 return VINF_SUCCESS;
4330}
4331
4332/**
4333 * Read the HcPeriodCurrentED register.
4334 */
4335static int HcPeriodCurrentED_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4336{
4337 Log2(("HcPeriodCurrentED_r() -> %#010x\n", pThis->per_cur));
4338 *pu32Value = pThis->per_cur;
4339 return VINF_SUCCESS;
4340}
4341
4342/**
4343 * Write to the HcPeriodCurrentED register.
4344 */
4345static int HcPeriodCurrentED_w(POHCI pThis, uint32_t iReg, uint32_t val)
4346{
4347 Log(("HcPeriodCurrentED_w(%#010x) - old=%#010x new=%#010x (This is a read only register, only the linux guys don't respect that!)\n",
4348 val, pThis->per_cur, val & ~7));
4349 //AssertMsgFailed(("HCD (Host Controller Driver) should not write to HcPeriodCurrentED! val=%#010x (old=%#010x)\n", val, pThis->per_cur));
4350 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4351 pThis->per_cur = val & ~7;
4352 return VINF_SUCCESS;
4353}
4354
4355/**
4356 * Read the HcControlHeadED register.
4357 */
4358static int HcControlHeadED_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4359{
4360 Log2(("HcControlHeadED_r() -> %#010x\n", pThis->ctrl_head));
4361 *pu32Value = pThis->ctrl_head;
4362 return VINF_SUCCESS;
4363}
4364
4365/**
4366 * Write to the HcControlHeadED register.
4367 */
4368static int HcControlHeadED_w(POHCI pThis, uint32_t iReg, uint32_t val)
4369{
4370 Log2(("HcControlHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->ctrl_head, val & ~7));
4371 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4372 pThis->ctrl_head = val & ~7;
4373 return VINF_SUCCESS;
4374}
4375
4376/**
4377 * Read the HcControlCurrentED register.
4378 */
4379static int HcControlCurrentED_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4380{
4381 Log2(("HcControlCurrentED_r() -> %#010x\n", pThis->ctrl_cur));
4382 *pu32Value = pThis->ctrl_cur;
4383 return VINF_SUCCESS;
4384}
4385
4386/**
4387 * Write to the HcControlCurrentED register.
4388 */
4389static int HcControlCurrentED_w(POHCI pThis, uint32_t iReg, uint32_t val)
4390{
4391 Log2(("HcControlCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->ctrl_cur, val & ~7));
4392 AssertMsg(!(pThis->ctl & OHCI_CTL_CLE), ("Illegal write! HcControl.ControlListEnabled is set! val=%#010x\n", val));
4393 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4394 pThis->ctrl_cur = val & ~7;
4395 return VINF_SUCCESS;
4396}
4397
4398/**
4399 * Read the HcBulkHeadED register.
4400 */
4401static int HcBulkHeadED_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4402{
4403 Log2(("HcBulkHeadED_r() -> %#010x\n", pThis->bulk_head));
4404 *pu32Value = pThis->bulk_head;
4405 return VINF_SUCCESS;
4406}
4407
4408/**
4409 * Write to the HcBulkHeadED register.
4410 */
4411static int HcBulkHeadED_w(POHCI pThis, uint32_t iReg, uint32_t val)
4412{
4413 Log2(("HcBulkHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->bulk_head, val & ~7));
4414 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4415 pThis->bulk_head = val & ~7; /** @todo The ATI OHCI controller on my machine enforces 16-byte address alignment. */
4416 return VINF_SUCCESS;
4417}
4418
4419/**
4420 * Read the HcBulkCurrentED register.
4421 */
4422static int HcBulkCurrentED_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4423{
4424 Log2(("HcBulkCurrentED_r() -> %#010x\n", pThis->bulk_cur));
4425 *pu32Value = pThis->bulk_cur;
4426 return VINF_SUCCESS;
4427}
4428
4429/**
4430 * Write to the HcBulkCurrentED register.
4431 */
4432static int HcBulkCurrentED_w(POHCI pThis, uint32_t iReg, uint32_t val)
4433{
4434 Log2(("HcBulkCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->bulk_cur, val & ~7));
4435 AssertMsg(!(pThis->ctl & OHCI_CTL_BLE), ("Illegal write! HcControl.BulkListEnabled is set! val=%#010x\n", val));
4436 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4437 pThis->bulk_cur = val & ~7;
4438 return VINF_SUCCESS;
4439}
4440
4441
4442/**
4443 * Read the HcDoneHead register.
4444 */
4445static int HcDoneHead_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4446{
4447 Log2(("HcDoneHead_r() -> 0x%#08x\n", pThis->done));
4448 *pu32Value = pThis->done;
4449 return VINF_SUCCESS;
4450}
4451
4452/**
4453 * Write to the HcDoneHead register.
4454 */
4455static int HcDoneHead_w(POHCI pThis, uint32_t iReg, uint32_t val)
4456{
4457 Log2(("HcDoneHead_w(0x%#08x) - denied!!!\n", val));
4458 /*AssertMsgFailed(("Illegal operation!!! val=%#010x\n", val)); - OS/2 does this */
4459 return VINF_SUCCESS;
4460}
4461
4462
4463/**
4464 * Read the HcFmInterval (Fm=Frame) register.
4465 */
4466static int HcFmInterval_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4467{
4468 uint32_t val = (pThis->fit << 31) | (pThis->fsmps << 16) | (pThis->fi);
4469 Log2(("HcFmInterval_r() -> 0x%#08x - FI=%d FSMPS=%d FIT=%d\n",
4470 val, val & 0x3fff, (val >> 16) & 0x7fff, val >> 31));
4471 *pu32Value = val;
4472 return VINF_SUCCESS;
4473}
4474
4475/**
4476 * Write to the HcFmInterval (Fm = Frame) register.
4477 */
4478static int HcFmInterval_w(POHCI pThis, uint32_t iReg, uint32_t val)
4479{
4480 /* log */
4481 uint32_t chg = val ^ ((pThis->fit << 31) | (pThis->fsmps << 16) | pThis->fi); NOREF(chg);
4482 Log2(("HcFmInterval_w(%#010x) => %sFI=%d %sFSMPS=%d %sFIT=%d\n",
4483 val,
4484 chg & 0x00003fff ? "*" : "", val & 0x3fff,
4485 chg & 0x7fff0000 ? "*" : "", (val >> 16) & 0x7fff,
4486 chg >> 31 ? "*" : "", (val >> 31) & 1));
4487 if ( pThis->fi != (val & OHCI_FMI_FI) )
4488 {
4489 Log(("ohci: FrameInterval: %#010x -> %#010x\n", pThis->fi, val & OHCI_FMI_FI));
4490 AssertMsg(pThis->fit != ((val >> OHCI_FMI_FIT_SHIFT) & 1), ("HCD didn't toggle the FIT bit!!!\n"));
4491 }
4492
4493 /* update */
4494 pThis->fi = val & OHCI_FMI_FI;
4495 pThis->fit = (val & OHCI_FMI_FIT) >> OHCI_FMI_FIT_SHIFT;
4496 pThis->fsmps = (val & OHCI_FMI_FSMPS) >> OHCI_FMI_FSMPS_SHIFT;
4497 return VINF_SUCCESS;
4498}
4499
4500/**
4501 * Read the HcFmRemaining (Fm = Frame) register.
4502 */
4503static int HcFmRemaining_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4504{
4505 uint32_t Value = pThis->frt << 31;
4506 if ((pThis->ctl & OHCI_CTL_HCFS) == OHCI_USB_OPERATIONAL)
4507 {
4508 /*
4509 * Being in USB operational state guarantees SofTime was set already.
4510 */
4511 uint64_t tks = PDMDevHlpTMTimeVirtGet(pThis->CTX_SUFF(pDevIns)) - pThis->SofTime;
4512 if (tks < pThis->cTicksPerFrame) /* avoid muldiv if possible */
4513 {
4514 uint16_t fr;
4515 tks = ASMMultU64ByU32DivByU32(1, tks, pThis->cTicksPerUsbTick);
4516 fr = (uint16_t)(pThis->fi - tks);
4517 Value |= fr;
4518 }
4519 }
4520
4521 Log2(("HcFmRemaining_r() -> %#010x - FR=%d FRT=%d\n", Value, Value & 0x3fff, Value >> 31));
4522 *pu32Value = Value;
4523 return VINF_SUCCESS;
4524}
4525
4526/**
4527 * Write to the HcFmRemaining (Fm = Frame) register.
4528 */
4529static int HcFmRemaining_w(POHCI pThis, uint32_t iReg, uint32_t val)
4530{
4531 Log2(("HcFmRemaining_w(%#010x) - denied\n", val));
4532 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
4533 return VINF_SUCCESS;
4534}
4535
4536/**
4537 * Read the HcFmNumber (Fm = Frame) register.
4538 */
4539static int HcFmNumber_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4540{
4541 uint32_t val = (uint16_t)pThis->HcFmNumber;
4542 Log2(("HcFmNumber_r() -> %#010x - FN=%#x(%d) (32-bit=%#x(%d))\n", val, val, val, pThis->HcFmNumber, pThis->HcFmNumber));
4543 *pu32Value = val;
4544 return VINF_SUCCESS;
4545}
4546
4547/**
4548 * Write to the HcFmNumber (Fm = Frame) register.
4549 */
4550static int HcFmNumber_w(POHCI pThis, uint32_t iReg, uint32_t val)
4551{
4552 Log2(("HcFmNumber_w(%#010x) - denied\n", val));
4553 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
4554 return VINF_SUCCESS;
4555}
4556
4557/**
4558 * Read the HcPeriodicStart register.
4559 * The register determines when in a frame to switch from control&bulk to periodic lists.
4560 */
4561static int HcPeriodicStart_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4562{
4563 Log2(("HcPeriodicStart_r() -> %#010x - PS=%d\n", pThis->pstart, pThis->pstart & 0x3fff));
4564 *pu32Value = pThis->pstart;
4565 return VINF_SUCCESS;
4566}
4567
4568/**
4569 * Write to the HcPeriodicStart register.
4570 * The register determines when in a frame to switch from control&bulk to periodic lists.
4571 */
4572static int HcPeriodicStart_w(POHCI pThis, uint32_t iReg, uint32_t val)
4573{
4574 Log2(("HcPeriodicStart_w(%#010x) => PS=%d\n", val, val & 0x3fff));
4575 if (val & ~0x3fff)
4576 Log2(("Unknown bits %#x are set!!!\n", val & ~0x3fff));
4577 pThis->pstart = val; /** @todo r=bird: should we support setting the other bits? */
4578 return VINF_SUCCESS;
4579}
4580
4581/**
4582 * Read the HcLSThreshold register.
4583 */
4584static int HcLSThreshold_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4585{
4586 Log2(("HcLSThreshold_r() -> %#010x\n", OHCI_LS_THRESH));
4587 *pu32Value = OHCI_LS_THRESH;
4588 return VINF_SUCCESS;
4589}
4590
4591/**
4592 * Write to the HcLSThreshold register.
4593 *
4594 * Docs are inconsistent here:
4595 *
4596 * "Neither the Host Controller nor the Host Controller Driver are allowed to change this value."
4597 *
4598 * "This value is calculated by HCD with the consideration of transmission and setup overhead."
4599 *
4600 * The register is marked "R/W" the HCD column.
4601 *
4602 */
4603static int HcLSThreshold_w(POHCI pThis, uint32_t iReg, uint32_t val)
4604{
4605 Log2(("HcLSThreshold_w(%#010x) => LST=0x%03x(%d)\n", val, val & 0x0fff));
4606 AssertMsg(val == OHCI_LS_THRESH,
4607 ("HCD tried to write bad LS threshold: 0x%x (see function header)\n", val));
4608 /** @todo the HCD can change this. */
4609 return VINF_SUCCESS;
4610}
4611
4612/**
4613 * Read the HcRhDescriptorA register.
4614 */
4615static int HcRhDescriptorA_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4616{
4617 uint32_t val = pThis->RootHub.desc_a;
4618#if 0 /* annoying */
4619 Log2(("HcRhDescriptorA_r() -> %#010x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTGT=%#x\n",
4620 val, val & 0xff, (val >> 8) & 1, (val >> 9) & 1, (val >> 10) & 1, (val >> 11) & 1,
4621 (val >> 12) & 1, (val >> 24) & 0xff));
4622#endif
4623 *pu32Value = val;
4624 return VINF_SUCCESS;
4625}
4626
4627/**
4628 * Write to the HcRhDescriptorA register.
4629 */
4630static int HcRhDescriptorA_w(POHCI pThis, uint32_t iReg, uint32_t val)
4631{
4632 uint32_t chg = val ^ pThis->RootHub.desc_a; NOREF(chg);
4633 Log2(("HcRhDescriptorA_w(%#010x) => %sNDP=%d %sPSM=%d %sNPS=%d %sDT=%d %sOCPM=%d %sNOCP=%d %sPOTGT=%#x - %sPowerSwitching Set%sPower\n",
4634 val,
4635 chg & 0xff ?"!!!": "", OHCI_NDP,
4636 (chg >> 8) & 1 ? "*" : "", (val >> 8) & 1,
4637 (chg >> 9) & 1 ? "*" : "", (val >> 9) & 1,
4638 (chg >> 10) & 1 ?"!!!": "", 0,
4639 (chg >> 11) & 1 ? "*" : "", (val >> 11) & 1,
4640 (chg >> 12) & 1 ? "*" : "", (val >> 12) & 1,
4641 (chg >> 24)&0xff? "*" : "", (val >> 24) & 0xff,
4642 val & OHCI_RHA_NPS ? "No" : "",
4643 val & OHCI_RHA_PSM ? "Port" : "Global"));
4644 if (val & ~0xff001fff)
4645 Log2(("Unknown bits %#x are set!!!\n", val & ~0xff001fff));
4646
4647
4648 if ((val & (OHCI_RHA_NDP | OHCI_RHA_DT)) != OHCI_NDP)
4649 {
4650 Log(("ohci: %s: invalid write to NDP or DT in roothub descriptor A!!! val=0x%.8x\n",
4651 pThis->PciDev.name, val));
4652 val &= ~(OHCI_RHA_NDP | OHCI_RHA_DT);
4653 val |= OHCI_NDP;
4654 }
4655
4656 pThis->RootHub.desc_a = val;
4657 return VINF_SUCCESS;
4658}
4659
4660/**
4661 * Read the HcRhDescriptorB register.
4662 */
4663static int HcRhDescriptorB_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4664{
4665 uint32_t val = pThis->RootHub.desc_b;
4666 Log2(("HcRhDescriptorB_r() -> %#010x - DR=0x%04x PPCM=0x%04x\n",
4667 val, val & 0xffff, val >> 16));
4668 *pu32Value = val;
4669 return VINF_SUCCESS;
4670}
4671
4672/**
4673 * Write to the HcRhDescriptorB register.
4674 */
4675static int HcRhDescriptorB_w(POHCI pThis, uint32_t iReg, uint32_t val)
4676{
4677 uint32_t chg = pThis->RootHub.desc_b ^ val; NOREF(chg);
4678 Log2(("HcRhDescriptorB_w(%#010x) => %sDR=0x%04x %sPPCM=0x%04x\n",
4679 val,
4680 chg & 0xffff ? "!!!" : "", val & 0xffff,
4681 chg >> 16 ? "!!!" : "", val >> 16));
4682
4683 if ( pThis->RootHub.desc_b != val )
4684 Log(("ohci: %s: unsupported write to root descriptor B!!! 0x%.8x -> 0x%.8x\n",
4685 pThis->PciDev.name,
4686 pThis->RootHub.desc_b, val));
4687 pThis->RootHub.desc_b = val;
4688 return VINF_SUCCESS;
4689}
4690
4691/**
4692 * Read the HcRhStatus (Rh = Root Hub) register.
4693 */
4694static int HcRhStatus_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4695{
4696 uint32_t val = pThis->RootHub.status;
4697 if (val & (OHCI_RHS_LPSC | OHCI_RHS_OCIC))
4698 Log2(("HcRhStatus_r() -> %#010x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n",
4699 val, val & 1, (val >> 1) & 1, (val >> 15) & 1, (val >> 16) & 1, (val >> 17) & 1, (val >> 31) & 1));
4700 *pu32Value = val;
4701 return VINF_SUCCESS;
4702}
4703
4704/**
4705 * Write to the HcRhStatus (Rh = Root Hub) register.
4706 */
4707static int HcRhStatus_w(POHCI pThis, uint32_t iReg, uint32_t val)
4708{
4709#ifdef IN_RING3
4710 /* log */
4711 uint32_t old = pThis->RootHub.status;
4712 uint32_t chg;
4713 if (val & ~0x80038003)
4714 Log2(("HcRhStatus_w: Unknown bits %#x are set!!!\n", val & ~0x80038003));
4715 if ( (val & OHCI_RHS_LPSC) && (val & OHCI_RHS_LPS) )
4716 Log2(("HcRhStatus_w: Warning both CGP and SGP are set! (Clear/Set Global Power)\n"));
4717 if ( (val & OHCI_RHS_DRWE) && (val & OHCI_RHS_CRWE) )
4718 Log2(("HcRhStatus_w: Warning both CRWE and SRWE are set! (Clear/Set Remote Wakeup Enable)\n"));
4719
4720
4721 /* write 1 to clear OCIC */
4722 if ( val & OHCI_RHS_OCIC )
4723 pThis->RootHub.status &= ~OHCI_RHS_OCIC;
4724
4725 /* SetGlobalPower */
4726 if ( val & OHCI_RHS_LPSC )
4727 {
4728 int i;
4729 Log2(("ohci: %s: global power up\n", pThis->PciDev.name));
4730 for (i = 0; i < OHCI_NDP; i++)
4731 rhport_power(&pThis->RootHub, i, true /* power up */);
4732 }
4733
4734 /* ClearGlobalPower */
4735 if ( val & OHCI_RHS_LPS )
4736 {
4737 int i;
4738 Log2(("ohci: %s: global power down\n", pThis->PciDev.name));
4739 for (i = 0; i < OHCI_NDP; i++)
4740 rhport_power(&pThis->RootHub, i, false /* power down */);
4741 }
4742
4743 if ( val & OHCI_RHS_DRWE )
4744 pThis->RootHub.status |= OHCI_RHS_DRWE;
4745
4746 if ( val & OHCI_RHS_CRWE )
4747 pThis->RootHub.status &= ~OHCI_RHS_DRWE;
4748
4749 chg = pThis->RootHub.status ^ old;
4750 Log2(("HcRhStatus_w(%#010x) => %sCGP=%d %sOCI=%d %sSRWE=%d %sSGP=%d %sOCIC=%d %sCRWE=%d\n",
4751 val,
4752 chg & 1 ? "*" : "", val & 1,
4753 (chg >> 1) & 1 ?"!!!": "", (val >> 1) & 1,
4754 (chg >> 15) & 1 ? "*" : "", (val >> 15) & 1,
4755 (chg >> 16) & 1 ? "*" : "", (val >> 16) & 1,
4756 (chg >> 17) & 1 ? "*" : "", (val >> 17) & 1,
4757 (chg >> 31) & 1 ? "*" : "", (val >> 31) & 1));
4758 return VINF_SUCCESS;
4759#else /* !IN_RING3 */
4760 return VINF_IOM_R3_MMIO_WRITE;
4761#endif /* !IN_RING3 */
4762}
4763
4764/**
4765 * Read the HcRhPortStatus register of a port.
4766 */
4767static int HcRhPortStatus_r(PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4768{
4769 const unsigned i = iReg - 21;
4770 uint32_t val = pThis->RootHub.aPorts[i].fReg | OHCI_PORT_R_POWER_STATUS; /* PortPowerStatus: see todo on power in _w function. */
4771 if (val & OHCI_PORT_R_RESET_STATUS)
4772 {
4773#ifdef IN_RING3
4774 RTThreadYield();
4775#else
4776 Log2(("HcRhPortStatus_r: yield -> VINF_IOM_R3_MMIO_READ\n"));
4777 return VINF_IOM_R3_MMIO_READ;
4778#endif
4779 }
4780 if (val & (OHCI_PORT_R_RESET_STATUS | OHCI_PORT_CSC | OHCI_PORT_PESC | OHCI_PORT_PSSC | OHCI_PORT_OCIC | OHCI_PORT_PRSC))
4781 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",
4782 i, val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 8) & 1, (val >> 9) & 1,
4783 (val >> 16) & 1, (val >> 17) & 1, (val >> 18) & 1, (val >> 19) & 1, (val >> 20) & 1));
4784 *pu32Value = val;
4785 return VINF_SUCCESS;
4786}
4787
4788#ifdef IN_RING3
4789/**
4790 * Completion callback for the vusb_dev_reset() operation.
4791 * @thread EMT.
4792 */
4793static DECLCALLBACK(void) uchi_port_reset_done(PVUSBIDEVICE pDev, int rc, void *pvUser)
4794{
4795 POHCI pThis = (POHCI)pvUser;
4796
4797 /*
4798 * Find the port in question
4799 */
4800 POHCIHUBPORT pPort = NULL;
4801 unsigned iPort;
4802 for (iPort = 0; iPort < RT_ELEMENTS(pThis->RootHub.aPorts); iPort++) /* lazy bird */
4803 if (pThis->RootHub.aPorts[iPort].pDev == pDev)
4804 {
4805 pPort = &pThis->RootHub.aPorts[iPort];
4806 break;
4807 }
4808 if (!pPort)
4809 {
4810 Assert(pPort); /* sometimes happens because of @bugref{1510} */
4811 return;
4812 }
4813
4814 if (RT_SUCCESS(rc))
4815 {
4816 /*
4817 * Successful reset.
4818 */
4819 Log2(("uchi_port_reset_done: Reset completed.\n"));
4820 pPort->fReg &= ~(OHCI_PORT_R_RESET_STATUS | OHCI_PORT_R_SUSPEND_STATUS | OHCI_PORT_R_SUSPEND_STATUS_CHANGE);
4821 pPort->fReg |= OHCI_PORT_R_ENABLE_STATUS | OHCI_PORT_R_RESET_STATUS_CHANGE;
4822 }
4823 else
4824 {
4825 /* desperate measures. */
4826 if ( pPort->pDev
4827 && VUSBIDevGetState(pPort->pDev) == VUSB_DEVICE_STATE_ATTACHED)
4828 {
4829 /*
4830 * Damn, something weird happened during reset. We'll pretend the user did an
4831 * incredible fast reconnect or something. (probably not gonna work)
4832 */
4833 Log2(("uchi_port_reset_done: The reset failed (rc=%Rrc)!!! Pretending reconnect at the speed of light.\n", rc));
4834 pPort->fReg = OHCI_PORT_R_CURRENT_CONNECT_STATUS | OHCI_PORT_R_CONNECT_STATUS_CHANGE;
4835 }
4836 else
4837 {
4838 /*
4839 * The device have / will be disconnected.
4840 */
4841 Log2(("uchi_port_reset_done: Disconnected (rc=%Rrc)!!!\n", rc));
4842 pPort->fReg &= ~(OHCI_PORT_R_RESET_STATUS | OHCI_PORT_R_SUSPEND_STATUS | OHCI_PORT_R_SUSPEND_STATUS_CHANGE | OHCI_PORT_R_RESET_STATUS_CHANGE);
4843 pPort->fReg |= OHCI_PORT_R_CONNECT_STATUS_CHANGE;
4844 }
4845 }
4846
4847 /* Raise roothub status change interrupt. */
4848 ohciR3SetInterrupt(pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
4849}
4850
4851/**
4852 * Sets a flag in a port status register but only set it if a device is
4853 * connected, if not set ConnectStatusChange flag to force HCD to reevaluate
4854 * connect status.
4855 *
4856 * @returns true if device was connected and the flag was cleared.
4857 */
4858static bool rhport_set_if_connected(POHCIROOTHUB pRh, int iPort, uint32_t fValue)
4859{
4860 /*
4861 * Writing a 0 has no effect
4862 */
4863 if (fValue == 0)
4864 return false;
4865
4866 /*
4867 * If CurrentConnectStatus is cleared we set ConnectStatusChange.
4868 */
4869 if (!(pRh->aPorts[iPort].fReg & OHCI_PORT_R_CURRENT_CONNECT_STATUS))
4870 {
4871 pRh->aPorts[iPort].fReg |= OHCI_PORT_R_CONNECT_STATUS_CHANGE;
4872 ohciR3SetInterrupt(pRh->pOhci, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
4873 return false;
4874 }
4875
4876 bool fRc = !(pRh->aPorts[iPort].fReg & fValue);
4877
4878 /* set the bit */
4879 pRh->aPorts[iPort].fReg |= fValue;
4880
4881 return fRc;
4882}
4883#endif /* IN_RING3 */
4884
4885/**
4886 * Write to the HcRhPortStatus register of a port.
4887 */
4888static int HcRhPortStatus_w(POHCI pThis, uint32_t iReg, uint32_t val)
4889{
4890#ifdef IN_RING3
4891 const unsigned i = iReg - 21;
4892 POHCIHUBPORT p = &pThis->RootHub.aPorts[i];
4893 uint32_t old_state = p->fReg;
4894
4895#ifdef LOG_ENABLED
4896 /*
4897 * Log it.
4898 */
4899 static const char *apszCmdNames[32] =
4900 {
4901 "ClearPortEnable", "SetPortEnable", "SetPortSuspend", "!!!ClearSuspendStatus",
4902 "SetPortReset", "!!!5", "!!!6", "!!!7",
4903 "SetPortPower", "ClearPortPower", "!!!10", "!!!11",
4904 "!!!12", "!!!13", "!!!14", "!!!15",
4905 "ClearCSC", "ClearPESC", "ClearPSSC", "ClearOCIC",
4906 "ClearPRSC", "!!!21", "!!!22", "!!!23",
4907 "!!!24", "!!!25", "!!!26", "!!!27",
4908 "!!!28", "!!!29", "!!!30", "!!!31"
4909 };
4910 Log2(("HcRhPortStatus_w(%#010x): port %u:", val, i));
4911 for (unsigned j = 0; j < RT_ELEMENTS(apszCmdNames); j++)
4912 if (val & (1 << j))
4913 Log2((" %s", apszCmdNames[j]));
4914 Log2(("\n"));
4915#endif
4916
4917 /* Write to clear any of the change bits: CSC, PESC, PSSC, OCIC and PRSC */
4918 if (val & OHCI_PORT_W_CLEAR_CHANGE_MASK)
4919 p->fReg &= ~(val & OHCI_PORT_W_CLEAR_CHANGE_MASK);
4920
4921 if (val & OHCI_PORT_W_CLEAR_ENABLE)
4922 {
4923 p->fReg &= ~OHCI_PORT_R_ENABLE_STATUS;
4924 Log2(("HcRhPortStatus_w(): port %u: DISABLE\n", i));
4925 }
4926
4927 if (rhport_set_if_connected(&pThis->RootHub, i, val & OHCI_PORT_W_SET_ENABLE))
4928 Log2(("HcRhPortStatus_w(): port %u: ENABLE\n", i));
4929
4930 if (rhport_set_if_connected(&pThis->RootHub, i, val & OHCI_PORT_W_SET_SUSPEND))
4931 Log2(("HcRhPortStatus_w(): port %u: SUSPEND - not implemented correctly!!!\n", i));
4932
4933 if (val & OHCI_PORT_W_SET_RESET)
4934 {
4935 if (rhport_set_if_connected(&pThis->RootHub, i, val & OHCI_PORT_W_SET_RESET))
4936 {
4937 PVM pVM = PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns));
4938 p->fReg &= ~OHCI_PORT_R_RESET_STATUS_CHANGE;
4939 VUSBIDevReset(p->pDev, false /* don't reset on linux */, uchi_port_reset_done, pThis, pVM);
4940 }
4941 else if (p->fReg & OHCI_PORT_R_RESET_STATUS)
4942 {
4943 /* the guest is getting impatient. */
4944 Log2(("HcRhPortStatus_w(): port %u: Impatient guest!\n"));
4945 RTThreadYield();
4946 }
4947 }
4948
4949 if (!(pThis->RootHub.desc_a & OHCI_RHA_NPS))
4950 {
4951 /** @todo To implement per-device power-switching
4952 * we need to check PortPowerControlMask to make
4953 * sure it isn't gang powered
4954 */
4955 if (val & OHCI_PORT_W_CLEAR_POWER)
4956 rhport_power(&pThis->RootHub, i, false /* power down */);
4957 if (val & OHCI_PORT_W_SET_POWER)
4958 rhport_power(&pThis->RootHub, i, true /* power up */);
4959 }
4960
4961 /** @todo r=frank: ClearSuspendStatus. Timing? */
4962 if (val & OHCI_PORT_W_CLEAR_SUSPEND_STATUS)
4963 {
4964 rhport_power(&pThis->RootHub, i, true /* power up */);
4965 pThis->RootHub.aPorts[i].fReg &= ~OHCI_PORT_R_SUSPEND_STATUS;
4966 pThis->RootHub.aPorts[i].fReg |= OHCI_PORT_R_SUSPEND_STATUS_CHANGE;
4967 ohciR3SetInterrupt(pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
4968 }
4969
4970 if (p->fReg != old_state)
4971 {
4972 uint32_t res = p->fReg;
4973 uint32_t chg = res ^ old_state; NOREF(chg);
4974 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",
4975 val, i,
4976 chg & 1 ? "*" : "", res & 1,
4977 (chg >> 1) & 1 ? "*" : "", (res >> 1) & 1,
4978 (chg >> 2) & 1 ? "*" : "", (res >> 2) & 1,
4979 (chg >> 3) & 1 ? "*" : "", (res >> 3) & 1,
4980 (chg >> 4) & 1 ? "*" : "", (res >> 4) & 1,
4981 (chg >> 8) & 1 ? "*" : "", (res >> 8) & 1,
4982 (chg >> 9) & 1 ? "*" : "", (res >> 9) & 1,
4983 (chg >> 16) & 1 ? "*" : "", (res >> 16) & 1,
4984 (chg >> 17) & 1 ? "*" : "", (res >> 17) & 1,
4985 (chg >> 18) & 1 ? "*" : "", (res >> 18) & 1,
4986 (chg >> 19) & 1 ? "*" : "", (res >> 19) & 1,
4987 (chg >> 20) & 1 ? "*" : "", (res >> 20) & 1));
4988 }
4989 return VINF_SUCCESS;
4990#else /* !IN_RING3 */
4991 return VINF_IOM_R3_MMIO_WRITE;
4992#endif /* !IN_RING3 */
4993}
4994
4995/**
4996 * Register descriptor table
4997 */
4998static const OHCIOPREG g_aOpRegs[] =
4999{
5000 { "HcRevision", HcRevision_r, HcRevision_w }, /* 0 */
5001 { "HcControl", HcControl_r, HcControl_w }, /* 1 */
5002 { "HcCommandStatus", HcCommandStatus_r, HcCommandStatus_w }, /* 2 */
5003 { "HcInterruptStatus", HcInterruptStatus_r, HcInterruptStatus_w }, /* 3 */
5004 { "HcInterruptEnable", HcInterruptEnable_r, HcInterruptEnable_w }, /* 4 */
5005 { "HcInterruptDisable", HcInterruptDisable_r, HcInterruptDisable_w }, /* 5 */
5006 { "HcHCCA", HcHCCA_r, HcHCCA_w }, /* 6 */
5007 { "HcPeriodCurrentED", HcPeriodCurrentED_r, HcPeriodCurrentED_w }, /* 7 */
5008 { "HcControlHeadED", HcControlHeadED_r, HcControlHeadED_w }, /* 8 */
5009 { "HcControlCurrentED", HcControlCurrentED_r, HcControlCurrentED_w }, /* 9 */
5010 { "HcBulkHeadED", HcBulkHeadED_r, HcBulkHeadED_w }, /* 10 */
5011 { "HcBulkCurrentED", HcBulkCurrentED_r, HcBulkCurrentED_w }, /* 11 */
5012 { "HcDoneHead", HcDoneHead_r, HcDoneHead_w }, /* 12 */
5013 { "HcFmInterval", HcFmInterval_r, HcFmInterval_w }, /* 13 */
5014 { "HcFmRemaining", HcFmRemaining_r, HcFmRemaining_w }, /* 14 */
5015 { "HcFmNumber", HcFmNumber_r, HcFmNumber_w }, /* 15 */
5016 { "HcPeriodicStart", HcPeriodicStart_r, HcPeriodicStart_w }, /* 16 */
5017 { "HcLSThreshold", HcLSThreshold_r, HcLSThreshold_w }, /* 17 */
5018 { "HcRhDescriptorA", HcRhDescriptorA_r, HcRhDescriptorA_w }, /* 18 */
5019 { "HcRhDescriptorB", HcRhDescriptorB_r, HcRhDescriptorB_w }, /* 19 */
5020 { "HcRhStatus", HcRhStatus_r, HcRhStatus_w }, /* 20 */
5021
5022 /* The number of port status register depends on the definition
5023 * of OHCI_NDP macro
5024 */
5025 { "HcRhPortStatus[0]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 21 */
5026 { "HcRhPortStatus[1]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 22 */
5027 { "HcRhPortStatus[2]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 23 */
5028 { "HcRhPortStatus[3]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 24 */
5029 { "HcRhPortStatus[4]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 25 */
5030 { "HcRhPortStatus[5]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 26 */
5031 { "HcRhPortStatus[6]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 27 */
5032 { "HcRhPortStatus[7]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 28 */
5033 { "HcRhPortStatus[8]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 29 */
5034 { "HcRhPortStatus[9]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 30 */
5035 { "HcRhPortStatus[10]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 31 */
5036 { "HcRhPortStatus[11]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 32 */
5037 { "HcRhPortStatus[12]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 33 */
5038 { "HcRhPortStatus[13]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 34 */
5039 { "HcRhPortStatus[14]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 35 */
5040};
5041
5042
5043/**
5044 * @callback_method_impl{FNIOMMMIOREAD}
5045 */
5046PDMBOTHCBDECL(int) ohciMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
5047{
5048 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5049
5050 /* Paranoia: Assert that IOMMMIO_FLAGS_READ_DWORD works. */
5051 AssertReturn(cb == sizeof(uint32_t), VERR_INTERNAL_ERROR_3);
5052 AssertReturn(!(GCPhysAddr & 0x3), VERR_INTERNAL_ERROR_4);
5053
5054 /*
5055 * Validate the register and call the read operator.
5056 */
5057 int rc;
5058 const uint32_t iReg = (GCPhysAddr - pThis->MMIOBase) >> 2;
5059 if (iReg < RT_ELEMENTS(g_aOpRegs))
5060 {
5061 const OHCIOPREG *pReg = &g_aOpRegs[iReg];
5062 rc = pReg->pfnRead(pThis, iReg, (uint32_t *)pv);
5063 }
5064 else
5065 {
5066 Log(("ohci: Trying to read register %u/%u!!!\n", iReg, RT_ELEMENTS(g_aOpRegs)));
5067 rc = VINF_IOM_MMIO_UNUSED_FF;
5068 }
5069 return rc;
5070}
5071
5072
5073/**
5074 * @callback_method_impl{FNIOMMMIOWRITE}
5075 */
5076PDMBOTHCBDECL(int) ohciMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
5077{
5078 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5079
5080 /* Paranoia: Assert that IOMMMIO_FLAGS_WRITE_DWORD_ZEROED works. */
5081 AssertReturn(cb == sizeof(uint32_t), VERR_INTERNAL_ERROR_3);
5082 AssertReturn(!(GCPhysAddr & 0x3), VERR_INTERNAL_ERROR_4);
5083
5084 /*
5085 * Validate the register and call the read operator.
5086 */
5087 int rc;
5088 const uint32_t iReg = (GCPhysAddr - pThis->MMIOBase) >> 2;
5089 if (iReg < RT_ELEMENTS(g_aOpRegs))
5090 {
5091 const OHCIOPREG *pReg = &g_aOpRegs[iReg];
5092 rc = pReg->pfnWrite(pThis, iReg, *(uint32_t const *)pv);
5093 }
5094 else
5095 {
5096 Log(("ohci: Trying to write to register %u/%u!!!\n", iReg, RT_ELEMENTS(g_aOpRegs)));
5097 rc = VINF_SUCCESS;
5098 }
5099 return rc;
5100}
5101
5102#ifdef IN_RING3
5103
5104/**
5105 * @callback_method_impl{FNPCIIOREGIONMAP}
5106 */
5107static DECLCALLBACK(int) ohciR3Map(PPCIDEVICE pPciDev, int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
5108{
5109 POHCI pThis = (POHCI)pPciDev;
5110 int rc = PDMDevHlpMMIORegister(pThis->CTX_SUFF(pDevIns), GCPhysAddress, cb, NULL /*pvUser*/,
5111 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED
5112 | IOMMMIO_FLAGS_DBGSTOP_ON_COMPLICATED_WRITE,
5113 ohciMmioWrite, ohciMmioRead, "USB OHCI");
5114 if (RT_FAILURE(rc))
5115 return rc;
5116
5117 if (pThis->fRZEnabled)
5118 {
5119 rc = PDMDevHlpMMIORegisterRC(pThis->CTX_SUFF(pDevIns), GCPhysAddress, cb,
5120 NIL_RTRCPTR /*pvUser*/, "ohciMmioWrite", "ohciMmioRead");
5121 if (RT_FAILURE(rc))
5122 return rc;
5123
5124 rc = PDMDevHlpMMIORegisterR0(pThis->CTX_SUFF(pDevIns), GCPhysAddress, cb,
5125 NIL_RTR0PTR /*pvUser*/, "ohciMmioWrite", "ohciMmioRead");
5126 if (RT_FAILURE(rc))
5127 return rc;
5128 }
5129
5130 pThis->MMIOBase = GCPhysAddress;
5131 return VINF_SUCCESS;
5132}
5133
5134
5135/**
5136 * Prepares for state saving.
5137 * All URBs needs to be canceled.
5138 *
5139 * @returns VBox status code.
5140 * @param pDevIns The device instance.
5141 * @param pSSM The handle to save the state to.
5142 */
5143static DECLCALLBACK(int) ohciR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5144{
5145 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5146 POHCIROOTHUB pRh = &pThis->RootHub;
5147 LogFlow(("ohciR3SavePrep: \n"));
5148
5149 /*
5150 * Detach all proxied devices.
5151 */
5152 PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
5153 /** @todo this won't work well when continuing after saving! */
5154 for (unsigned i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
5155 {
5156 PVUSBIDEVICE pDev = pRh->aPorts[i].pDev;
5157 if (pDev)
5158 {
5159 if (!VUSBIDevIsEmulated(pDev))
5160 {
5161 VUSBIRhDetachDevice(pRh->pIRhConn, pDev);
5162 /*
5163 * Save the device pointers here so we can reattach them afterwards.
5164 * This will work fine even if the save fails since the Done handler is
5165 * called unconditionally if the Prep handler was called.
5166 */
5167 pRh->aPorts[i].pDev = pDev;
5168 }
5169 }
5170 }
5171
5172 /*
5173 * If the bus was started set the timer. This is ugly but avoids changing the
5174 * saved state version for now so we can backport the changes to other branches.
5175 */
5176 /** @todo: Do it properly for 4.4 by changing the saved state. */
5177 if (pThis->fBusStarted)
5178 {
5179 /* Calculate a new timer expiration so this saved state works with older releases. */
5180 uint64_t u64Expire = PDMDevHlpTMTimeVirtGet(pThis->CTX_SUFF(pDevIns)) + pThis->cTicksPerFrame;
5181
5182 LogFlowFunc(("Bus is active, setting timer to %llu\n", u64Expire));
5183 int rc = TMTimerSet(pThis->pEndOfFrameTimerR3, u64Expire);
5184 AssertRC(rc);
5185 }
5186
5187 PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
5188
5189 /*
5190 * Kill old load data which might be hanging around.
5191 */
5192 if (pThis->pLoad)
5193 {
5194 TMR3TimerDestroy(pThis->pLoad->pTimer);
5195 MMR3HeapFree(pThis->pLoad);
5196 pThis->pLoad = NULL;
5197 }
5198 return VINF_SUCCESS;
5199}
5200
5201
5202/**
5203 * Saves the state of the OHCI device.
5204 *
5205 * @returns VBox status code.
5206 * @param pDevIns The device instance.
5207 * @param pSSM The handle to save the state to.
5208 */
5209static DECLCALLBACK(int) ohciR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5210{
5211 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5212 LogFlow(("ohciR3SaveExec: \n"));
5213
5214 int rc = SSMR3PutStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
5215 if (RT_SUCCESS(rc))
5216 rc = TMR3TimerSave(pThis->CTX_SUFF(pEndOfFrameTimer), pSSM);
5217 return rc;
5218}
5219
5220
5221/**
5222 * Done state save operation.
5223 *
5224 * @returns VBox load code.
5225 * @param pDevIns Device instance of the device which registered the data unit.
5226 * @param pSSM SSM operation handle.
5227 */
5228static DECLCALLBACK(int) ohciR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5229{
5230 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5231 POHCIROOTHUB pRh = &pThis->RootHub;
5232 OHCIROOTHUB Rh;
5233 unsigned i;
5234 LogFlow(("ohciR3SaveDone: \n"));
5235
5236 /*
5237 * NULL the dev pointers.
5238 */
5239 Rh = *pRh;
5240 for (i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
5241 {
5242 if ( pRh->aPorts[i].pDev
5243 && !VUSBIDevIsEmulated(pRh->aPorts[i].pDev))
5244 pRh->aPorts[i].pDev = NULL;
5245 }
5246
5247 /*
5248 * Attach the devices.
5249 */
5250 for (i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
5251 {
5252 PVUSBIDEVICE pDev = Rh.aPorts[i].pDev;
5253 if ( pDev
5254 && !VUSBIDevIsEmulated(pDev))
5255 VUSBIRhAttachDevice(pRh->pIRhConn, pDev);
5256 }
5257
5258 return VINF_SUCCESS;
5259}
5260
5261
5262/**
5263 * Prepare loading the state of the OHCI device.
5264 * This must detach the devices currently attached and save
5265 * the up for reconnect after the state load have been completed
5266 *
5267 * @returns VBox status code.
5268 * @param pDevIns The device instance.
5269 * @param pSSM The handle to the saved state.
5270 * @param u32Version The data unit version number.
5271 */
5272static DECLCALLBACK(int) ohciR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5273{
5274 int rc = VINF_SUCCESS;
5275 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5276 LogFlow(("ohciR3LoadPrep:\n"));
5277 if (!pThis->pLoad)
5278 {
5279 POHCIROOTHUB pRh = &pThis->RootHub;
5280 OHCILOAD Load;
5281 unsigned i;
5282
5283 /*
5284 * Detach all devices which are present in this session. Save them in the load
5285 * structure so we can reattach them after restoring the guest.
5286 */
5287 Load.pTimer = NULL;
5288 Load.cDevs = 0;
5289 for (i = 0; i < RT_ELEMENTS(pRh->aPorts); i++)
5290 {
5291 PVUSBIDEVICE pDev = pRh->aPorts[i].pDev;
5292 if ( pDev
5293 && !VUSBIDevIsEmulated(pDev))
5294 {
5295 Load.apDevs[Load.cDevs++] = pDev;
5296 VUSBIRhDetachDevice(pRh->pIRhConn, pDev);
5297 Assert(!pRh->aPorts[i].pDev);
5298 }
5299 }
5300
5301 /*
5302 * Any devices to reattach, if so duplicate the Load struct.
5303 */
5304 if (Load.cDevs)
5305 {
5306 pThis->pLoad = (POHCILOAD)PDMDevHlpMMHeapAlloc(pDevIns, sizeof(Load));
5307 if (!pThis->pLoad)
5308 return VERR_NO_MEMORY;
5309 *pThis->pLoad = Load;
5310 }
5311 }
5312 /* else: we ASSUME no device can be attached or detach in the period
5313 * between a state load and the pLoad stuff is processed. */
5314 return rc;
5315}
5316
5317
5318/**
5319 * Loads the state of the OHCI device.
5320 *
5321 * @returns VBox status code.
5322 * @param pDevIns The device instance.
5323 * @param pSSM The handle to the saved state.
5324 * @param uVersion The data unit version number.
5325 * @param uPass The data pass.
5326 */
5327static DECLCALLBACK(int) ohciR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5328{
5329 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5330 int rc;
5331 LogFlow(("ohciR3LoadExec:\n"));
5332 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
5333
5334 if (uVersion == OHCI_SAVED_STATE_VERSION)
5335 {
5336 rc = SSMR3GetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
5337 if (RT_FAILURE(rc))
5338 return rc;
5339 }
5340 else if (uVersion == OHCI_SAVED_STATE_VERSION_8PORTS)
5341 {
5342 static SSMFIELD const s_aOhciFields8Ports[] =
5343 {
5344 SSMFIELD_ENTRY( OHCI, SofTime),
5345 SSMFIELD_ENTRY_CUSTOM( dpic+fno, RT_OFFSETOF(OHCI, SofTime) + RT_SIZEOFMEMB(OHCI, SofTime), 4),
5346 SSMFIELD_ENTRY( OHCI, RootHub.status),
5347 SSMFIELD_ENTRY( OHCI, RootHub.desc_a),
5348 SSMFIELD_ENTRY( OHCI, RootHub.desc_b),
5349 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[0].fReg),
5350 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[1].fReg),
5351 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[2].fReg),
5352 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[3].fReg),
5353 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[4].fReg),
5354 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[5].fReg),
5355 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[6].fReg),
5356 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[7].fReg),
5357 SSMFIELD_ENTRY( OHCI, ctl),
5358 SSMFIELD_ENTRY( OHCI, status),
5359 SSMFIELD_ENTRY( OHCI, intr_status),
5360 SSMFIELD_ENTRY( OHCI, intr),
5361 SSMFIELD_ENTRY( OHCI, hcca),
5362 SSMFIELD_ENTRY( OHCI, per_cur),
5363 SSMFIELD_ENTRY( OHCI, ctrl_cur),
5364 SSMFIELD_ENTRY( OHCI, ctrl_head),
5365 SSMFIELD_ENTRY( OHCI, bulk_cur),
5366 SSMFIELD_ENTRY( OHCI, bulk_head),
5367 SSMFIELD_ENTRY( OHCI, done),
5368 SSMFIELD_ENTRY_CUSTOM( fsmps+fit+fi+frt, RT_OFFSETOF(OHCI, done) + RT_SIZEOFMEMB(OHCI, done), 4),
5369 SSMFIELD_ENTRY( OHCI, HcFmNumber),
5370 SSMFIELD_ENTRY( OHCI, pstart),
5371 SSMFIELD_ENTRY_TERM()
5372 };
5373
5374 rc = SSMR3GetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &s_aOhciFields8Ports[0], NULL);
5375 if (RT_FAILURE(rc))
5376 return rc;
5377 }
5378 else if (uVersion == OHCI_SAVED_STATE_VERSION_MEM_HELL)
5379 {
5380 static SSMFIELD const s_aOhciFields22[] =
5381 {
5382 SSMFIELD_ENTRY_OLD( PciDev.config, 256), /* DevPCI restores this. */
5383 SSMFIELD_ENTRY_OLD( PciDev.Int, 224),
5384 SSMFIELD_ENTRY_OLD( PciDev.devfn, 4),
5385 SSMFIELD_ENTRY_OLD( PciDev.Alignment0, 4),
5386 SSMFIELD_ENTRY_OLD_HCPTR( PciDev.name),
5387 SSMFIELD_ENTRY_OLD_HCPTR( PciDev.pDevIns),
5388 SSMFIELD_ENTRY_OLD_HCPTR( pDevInsR3),
5389 SSMFIELD_ENTRY_OLD_HCPTR( pEndOfFrameTimerR3),
5390 SSMFIELD_ENTRY_OLD_HCPTR( pDevInsR0),
5391 SSMFIELD_ENTRY_OLD_HCPTR( pEndOfFrameTimerR0),
5392 SSMFIELD_ENTRY_OLD_RCPTR( pDevInsRC),
5393 SSMFIELD_ENTRY_OLD_RCPTR( pEndOfFrameTimerRC),
5394 SSMFIELD_ENTRY( OHCI, SofTime),
5395 SSMFIELD_ENTRY_CUSTOM( dpic+fno, RT_OFFSETOF(OHCI, SofTime) + RT_SIZEOFMEMB(OHCI, SofTime), 4),
5396 SSMFIELD_ENTRY_OLD( MMIOBase, 4), /* DevPCI implicitly restores this. */
5397 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.pIBase),
5398 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.pIRhConn),
5399 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.pIDev),
5400 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IBase.pfnQueryInterface),
5401 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnGetAvailablePorts),
5402 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnGetUSBVersions),
5403 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnAttach),
5404 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnDetach),
5405 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnReset),
5406 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnXferCompletion),
5407 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.pfnXferError),
5408 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.IRhPort.Alignment),
5409 SSMFIELD_ENTRY_OLD( RootHub.Led, 16), /* No device restored. */
5410 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.ILeds.pfnQueryStatusLed),
5411 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.pLedsConnector),
5412 SSMFIELD_ENTRY( OHCI, RootHub.status),
5413 SSMFIELD_ENTRY( OHCI, RootHub.desc_a),
5414 SSMFIELD_ENTRY( OHCI, RootHub.desc_b),
5415 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.Alignment0, 4),
5416 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[0].fReg),
5417 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[0].Alignment0, 4),
5418 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[0].pDev),
5419 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[1].fReg),
5420 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[1].Alignment0, 4),
5421 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[1].pDev),
5422 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[2].fReg),
5423 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[2].Alignment0, 4),
5424 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[2].pDev),
5425 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[3].fReg),
5426 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[3].Alignment0, 4),
5427 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[3].pDev),
5428 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[4].fReg),
5429 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[4].Alignment0, 4),
5430 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[4].pDev),
5431 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[5].fReg),
5432 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[5].Alignment0, 4),
5433 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[5].pDev),
5434 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[6].fReg),
5435 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[6].Alignment0, 4),
5436 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[6].pDev),
5437 SSMFIELD_ENTRY( OHCI, RootHub.aPorts[7].fReg),
5438 SSMFIELD_ENTRY_OLD_PAD_HC64( RootHub.aPorts[7].Alignment0, 4),
5439 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.aPorts[7].pDev),
5440 SSMFIELD_ENTRY_OLD_HCPTR( RootHub.pThis),
5441 SSMFIELD_ENTRY( OHCI, ctl),
5442 SSMFIELD_ENTRY( OHCI, status),
5443 SSMFIELD_ENTRY( OHCI, intr_status),
5444 SSMFIELD_ENTRY( OHCI, intr),
5445 SSMFIELD_ENTRY( OHCI, hcca),
5446 SSMFIELD_ENTRY( OHCI, per_cur),
5447 SSMFIELD_ENTRY( OHCI, ctrl_cur),
5448 SSMFIELD_ENTRY( OHCI, ctrl_head),
5449 SSMFIELD_ENTRY( OHCI, bulk_cur),
5450 SSMFIELD_ENTRY( OHCI, bulk_head),
5451 SSMFIELD_ENTRY( OHCI, done),
5452 SSMFIELD_ENTRY_CUSTOM( fsmps+fit+fi+frt, RT_OFFSETOF(OHCI, done) + RT_SIZEOFMEMB(OHCI, done), 4),
5453 SSMFIELD_ENTRY( OHCI, HcFmNumber),
5454 SSMFIELD_ENTRY( OHCI, pstart),
5455 SSMFIELD_ENTRY_OLD( cTicksPerFrame, 8), /* done by the constructor */
5456 SSMFIELD_ENTRY_OLD( cTicksPerUsbTick, 8), /* ditto */
5457 SSMFIELD_ENTRY_OLD( cInFlight, 4), /* no in-flight stuff when saving. */
5458 SSMFIELD_ENTRY_OLD( Alignment1, 4),
5459 SSMFIELD_ENTRY_OLD( aInFlight, 257 * 8),
5460 SSMFIELD_ENTRY_OLD_PAD_HC64( aInFlight, 257 * 8),
5461 SSMFIELD_ENTRY_OLD( cInDoneQueue, 4), /* strict builds only, so don't bother. */
5462 SSMFIELD_ENTRY_OLD( aInDoneQueue, 4*64),
5463 SSMFIELD_ENTRY_OLD( u32FmDoneQueueTail, 4), /* logging only */
5464 SSMFIELD_ENTRY_OLD_PAD_HC32( Alignment2, 4),
5465 SSMFIELD_ENTRY_OLD_HCPTR( pLoad),
5466 SSMFIELD_ENTRY_OLD( StatCanceledIsocUrbs, 8),
5467 SSMFIELD_ENTRY_OLD( StatCanceledGenUrbs, 8),
5468 SSMFIELD_ENTRY_OLD( StatDroppedUrbs, 8),
5469 SSMFIELD_ENTRY_OLD( StatTimer, 32),
5470 SSMFIELD_ENTRY_TERM()
5471 };
5472
5473 /* deserialize the struct */
5474 rc = SSMR3GetStructEx(pSSM, pThis, sizeof(*pThis), SSMSTRUCT_FLAGS_NO_MARKERS /*fFlags*/, &s_aOhciFields22[0], NULL);
5475 if (RT_FAILURE(rc))
5476 return rc;
5477
5478 /* check delimiter */
5479 uint32_t u32;
5480 rc = SSMR3GetU32(pSSM, &u32);
5481 if (RT_FAILURE(rc))
5482 return rc;
5483 AssertMsgReturn(u32 == ~0U, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
5484 }
5485 else
5486 AssertMsgFailedReturn(("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
5487
5488 /*
5489 * Finally restore the timer.
5490 */
5491 return TMR3TimerLoad(pThis->pEndOfFrameTimerR3, pSSM);
5492}
5493
5494
5495/**
5496 * Done state load operation.
5497 *
5498 * @returns VBox load code.
5499 * @param pDevIns Device instance of the device which registered the data unit.
5500 * @param pSSM SSM operation handle.
5501 */
5502static DECLCALLBACK(int) ohciR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5503{
5504 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5505 LogFlow(("ohciR3LoadDone:\n"));
5506
5507 /*
5508 * Start a timer if we've got devices to reattach
5509 */
5510 if (pThis->pLoad)
5511 {
5512 int rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ohciR3LoadReattachDevices, pThis,
5513 TMTIMER_FLAGS_NO_CRIT_SECT, "OHCI reattach devices on load",
5514 &pThis->pLoad->pTimer);
5515 if (RT_SUCCESS(rc))
5516 rc = TMTimerSetMillies(pThis->pLoad->pTimer, 250);
5517 return rc;
5518 }
5519
5520 return VINF_SUCCESS;
5521}
5522
5523
5524/**
5525 * Reattaches devices after a saved state load.
5526 */
5527static DECLCALLBACK(void) ohciR3LoadReattachDevices(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
5528{
5529 POHCI pThis = (POHCI)pvUser;
5530 POHCILOAD pLoad = pThis->pLoad;
5531 POHCIROOTHUB pRh = &pThis->RootHub;
5532 LogFlow(("ohciR3LoadReattachDevices:\n"));
5533
5534 /*
5535 * Reattach devices.
5536 */
5537 for (unsigned i = 0; i < pLoad->cDevs; i++)
5538 VUSBIRhAttachDevice(pRh->pIRhConn, pLoad->apDevs[i]);
5539
5540 /*
5541 * Cleanup.
5542 */
5543 TMR3TimerDestroy(pTimer);
5544 MMR3HeapFree(pLoad);
5545 pThis->pLoad = NULL;
5546}
5547
5548
5549/**
5550 * Reset notification.
5551 *
5552 * @returns VBox status.
5553 * @param pDevIns The device instance data.
5554 */
5555static DECLCALLBACK(void) ohciR3Reset(PPDMDEVINS pDevIns)
5556{
5557 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5558 LogFlow(("ohciR3Reset:\n"));
5559
5560 /*
5561 * There is no distinction between cold boot, warm reboot and software reboots,
5562 * all of these are treated as cold boots. We are also doing the initialization
5563 * job of a BIOS or SMM driver.
5564 *
5565 * Important: Don't confuse UsbReset with hardware reset. Hardware reset is
5566 * just one way of getting into the UsbReset state.
5567 */
5568 ohciDoReset(pThis, OHCI_USB_RESET, true /* reset devices */);
5569}
5570
5571
5572/**
5573 * Resume notification.
5574 *
5575 * @returns VBox status.
5576 * @param pDevIns The device instance data.
5577 */
5578static DECLCALLBACK(void) ohciR3Resume(PPDMDEVINS pDevIns)
5579{
5580 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5581 LogFlowFunc(("\n"));
5582
5583 /* Restart the frame thread if the timer is active. */
5584 if (TMTimerIsActive(pThis->pEndOfFrameTimerR3))
5585 {
5586 int rc = TMTimerStop(pThis->pEndOfFrameTimerR3);
5587 AssertRC(rc);
5588
5589 LogFlowFunc(("Bus was active, restart frame thread\n"));
5590 ASMAtomicXchgBool(&pThis->fBusStarted, true);
5591 RTSemEventSignal(pThis->hSemEventFrame);
5592 }
5593}
5594
5595
5596/**
5597 * Info handler, device version. Dumps OHCI control registers.
5598 *
5599 * @param pDevIns Device instance which registered the info.
5600 * @param pHlp Callback functions for doing output.
5601 * @param pszArgs Argument string. Optional and specific to the handler.
5602 */
5603static DECLCALLBACK(void) ohciR3InfoRegs(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
5604{
5605 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5606 uint32_t val, ctl, status;
5607
5608 /* Control register */
5609 ctl = pThis->ctl;
5610 pHlp->pfnPrintf(pHlp, "HcControl: %08x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
5611 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
5612 (ctl >> 9) & 1, (ctl >> 10) & 1);
5613
5614 /* Command status register */
5615 status = pThis->status;
5616 pHlp->pfnPrintf(pHlp, "HcCommandStatus: %08x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
5617 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3);
5618
5619 /* Interrupt status register */
5620 val = pThis->intr_status;
5621 pHlp->pfnPrintf(pHlp, "HcInterruptStatus: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
5622 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5623 (val >> 6) & 1, (val >> 30) & 1);
5624
5625 /* Interrupt enable register */
5626 val = pThis->intr;
5627 pHlp->pfnPrintf(pHlp, "HcInterruptEnable: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
5628 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5629 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1);
5630
5631 /* HCCA address register */
5632 pHlp->pfnPrintf(pHlp, "HcHCCA: %08x\n", pThis->hcca);
5633
5634 /* Current periodic ED register */
5635 pHlp->pfnPrintf(pHlp, "HcPeriodCurrentED: %08x\n", pThis->per_cur);
5636
5637 /* Control ED registers */
5638 pHlp->pfnPrintf(pHlp, "HcControlHeadED: %08x\n", pThis->ctrl_head);
5639 pHlp->pfnPrintf(pHlp, "HcControlCurrentED: %08x\n", pThis->ctrl_cur);
5640
5641 /* Bulk ED registers */
5642 pHlp->pfnPrintf(pHlp, "HcBulkHeadED: %08x\n", pThis->bulk_head);
5643 pHlp->pfnPrintf(pHlp, "HcBulkCurrentED: %08x\n", pThis->bulk_cur);
5644
5645 /* Done head register */
5646 pHlp->pfnPrintf(pHlp, "HcDoneHead: %08x\n", pThis->done);
5647
5648 pHlp->pfnPrintf(pHlp, "\n");
5649}
5650
5651
5652/**
5653 * Relocate device instance data.
5654 *
5655 * @returns VBox status.
5656 * @param pDevIns The device instance data.
5657 * @param offDelta The relocation delta.
5658 */
5659static DECLCALLBACK(void) ohciR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5660{
5661 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5662 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5663 pThis->pEndOfFrameTimerRC = TMTimerRCPtr(pThis->pEndOfFrameTimerR3);
5664}
5665
5666
5667/**
5668 * Destruct a device instance.
5669 *
5670 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
5671 * resources can be freed correctly.
5672 *
5673 * @returns VBox status.
5674 * @param pDevIns The device instance data.
5675 */
5676static DECLCALLBACK(int) ohciR3Destruct(PPDMDEVINS pDevIns)
5677{
5678 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5679 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5680
5681 /*
5682 * Destroy event sempahores.
5683 */
5684 if (pThis->hSemEventFrame)
5685 RTSemEventDestroy(pThis->hSemEventFrame);
5686 if (pThis->hSemEventFrameStopped)
5687 RTSemEventMultiDestroy(pThis->hSemEventFrameStopped);
5688 if (RTCritSectIsInitialized(&pThis->CritSect))
5689 RTCritSectDelete(&pThis->CritSect);
5690 PDMR3CritSectDelete(&pThis->CsIrq);
5691
5692 /*
5693 * Tear down the per endpoint in-flight tracking...
5694 */
5695
5696 return VINF_SUCCESS;
5697}
5698
5699
5700/**
5701 * @interface_method_impl{PDMDEVREG,pfnConstruct,OHCI constructor}
5702 */
5703static DECLCALLBACK(int) ohciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5704{
5705 POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
5706 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5707
5708 /*
5709 * Init instance data.
5710 */
5711 pThis->pDevInsR3 = pDevIns;
5712 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
5713 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5714
5715 PCIDevSetVendorId (&pThis->PciDev, 0x106b);
5716 PCIDevSetDeviceId (&pThis->PciDev, 0x003f);
5717 PCIDevSetClassProg (&pThis->PciDev, 0x10); /* OHCI */
5718 PCIDevSetClassSub (&pThis->PciDev, 0x03);
5719 PCIDevSetClassBase (&pThis->PciDev, 0x0c);
5720 PCIDevSetInterruptPin (&pThis->PciDev, 0x01);
5721#ifdef VBOX_WITH_MSI_DEVICES
5722 PCIDevSetStatus (&pThis->PciDev, VBOX_PCI_STATUS_CAP_LIST);
5723 PCIDevSetCapabilityList(&pThis->PciDev, 0x80);
5724#endif
5725
5726 pThis->RootHub.pOhci = pThis;
5727 pThis->RootHub.IBase.pfnQueryInterface = ohciRhQueryInterface;
5728 pThis->RootHub.IRhPort.pfnGetAvailablePorts = ohciRhGetAvailablePorts;
5729 pThis->RootHub.IRhPort.pfnGetUSBVersions = ohciRhGetUSBVersions;
5730 pThis->RootHub.IRhPort.pfnAttach = ohciRhAttach;
5731 pThis->RootHub.IRhPort.pfnDetach = ohciRhDetach;
5732 pThis->RootHub.IRhPort.pfnReset = ohciRhReset;
5733 pThis->RootHub.IRhPort.pfnXferCompletion = ohciRhXferCompletion;
5734 pThis->RootHub.IRhPort.pfnXferError = ohciRhXferError;
5735
5736 /* USB LED */
5737 pThis->RootHub.Led.u32Magic = PDMLED_MAGIC;
5738 pThis->RootHub.ILeds.pfnQueryStatusLed = ohciRhQueryStatusLed;
5739
5740
5741 /*
5742 * Read configuration. No configuration keys are currently supported.
5743 */
5744 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "RZEnabled", "");
5745 int rc = CFGMR3QueryBoolDef(pCfg, "RZEnabled", &pThis->fRZEnabled, true);
5746 AssertLogRelRCReturn(rc, rc);
5747
5748
5749 /*
5750 * Register PCI device and I/O region.
5751 */
5752 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
5753 if (RT_FAILURE(rc))
5754 return rc;
5755
5756#ifdef VBOX_WITH_MSI_DEVICES
5757 PDMMSIREG MsiReg;
5758 RT_ZERO(MsiReg);
5759 MsiReg.cMsiVectors = 1;
5760 MsiReg.iMsiCapOffset = 0x80;
5761 MsiReg.iMsiNextOffset = 0x00;
5762 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
5763 if (RT_FAILURE(rc))
5764 {
5765 PCIDevSetCapabilityList(&pThis->PciDev, 0x0);
5766 /* That's OK, we can work without MSI */
5767 }
5768#endif
5769
5770 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 4096, PCI_ADDRESS_SPACE_MEM, ohciR3Map);
5771 if (RT_FAILURE(rc))
5772 return rc;
5773
5774 /*
5775 * Create the end-of-frame timer.
5776 */
5777 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ohciFrameBoundaryTimer, pThis,
5778 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "USB Frame Timer",
5779 &pThis->pEndOfFrameTimerR3);
5780 if (RT_FAILURE(rc))
5781 return rc;
5782 pThis->pEndOfFrameTimerR0 = TMTimerR0Ptr(pThis->pEndOfFrameTimerR3);
5783 pThis->pEndOfFrameTimerRC = TMTimerRCPtr(pThis->pEndOfFrameTimerR3);
5784
5785 /*
5786 * Register the saved state data unit.
5787 */
5788 rc = PDMDevHlpSSMRegisterEx(pDevIns, OHCI_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
5789 NULL, NULL, NULL,
5790 ohciR3SavePrep, ohciR3SaveExec, ohciR3SaveDone,
5791 ohciR3LoadPrep, ohciR3LoadExec, ohciR3LoadDone);
5792 if (RT_FAILURE(rc))
5793 return rc;
5794
5795 /*
5796 * Attach to the VBox USB RootHub Driver on LUN #0.
5797 */
5798 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->RootHub.IBase, &pThis->RootHub.pIBase, "RootHub");
5799 if (RT_FAILURE(rc))
5800 {
5801 AssertMsgFailed(("Configuration error: No roothub driver attached to LUN #0!\n"));
5802 return rc;
5803 }
5804 pThis->RootHub.pIRhConn = PDMIBASE_QUERY_INTERFACE(pThis->RootHub.pIBase, VUSBIROOTHUBCONNECTOR);
5805 AssertMsgReturn(pThis->RootHub.pIRhConn,
5806 ("Configuration error: The driver doesn't provide the VUSBIROOTHUBCONNECTOR interface!\n"),
5807 VERR_PDM_MISSING_INTERFACE);
5808 pThis->RootHub.pIDev = PDMIBASE_QUERY_INTERFACE(pThis->RootHub.pIBase, VUSBIDEVICE);
5809 AssertMsgReturn(pThis->RootHub.pIDev,
5810 ("Configuration error: The driver doesn't provide the VUSBIDEVICE interface!\n"),
5811 VERR_PDM_MISSING_INTERFACE);
5812
5813 /*
5814 * Attach status driver (optional).
5815 */
5816 PPDMIBASE pBase;
5817 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->RootHub.IBase, &pBase, "Status Port");
5818 if (RT_SUCCESS(rc))
5819 pThis->RootHub.pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5820 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
5821 {
5822 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
5823 return rc;
5824 }
5825
5826 /*
5827 * Calculate the timer intervals.
5828 * This assumes that the VM timer doesn't change frequency during the run.
5829 */
5830 pThis->u64TimerHz = TMTimerGetFreq(pThis->CTX_SUFF(pEndOfFrameTimer));
5831 ohciCalcTimerIntervals(pThis, OHCI_DEFAULT_TIMER_FREQ);
5832 Log(("ohci: cTicksPerFrame=%RU64 cTicksPerUsbTick=%RU64\n",
5833 pThis->cTicksPerFrame, pThis->cTicksPerUsbTick));
5834
5835 pThis->fBusStarted = false;
5836
5837 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CsIrq, RT_SRC_POS, "OHCI#%uIrq", iInstance);
5838 if (RT_FAILURE(rc))
5839 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
5840 N_("EHCI: Failed to create critical section"));
5841
5842 rc = RTSemEventCreate(&pThis->hSemEventFrame);
5843 AssertRCReturn(rc, rc);
5844
5845 rc = RTSemEventMultiCreate(&pThis->hSemEventFrameStopped);
5846 AssertRCReturn(rc, rc);
5847
5848 rc = RTCritSectInit(&pThis->CritSect);
5849 if (RT_FAILURE(rc))
5850 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
5851 N_("OHCI: Failed to create critical section"));
5852
5853 rc = PDMDevHlpThreadCreate(pDevIns, &pThis->hThreadFrame, pThis, ohciR3ThreadFrame,
5854 ohciR3ThreadFrameWakeup, 0, RTTHREADTYPE_IO, "OhciFramer");
5855 if (RT_FAILURE(rc))
5856 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
5857 N_("OHCI: Failed to create worker thread"));
5858
5859 /*
5860 * Do a hardware reset.
5861 */
5862 ohciDoReset(pThis, OHCI_USB_RESET, false /* don't reset devices */);
5863
5864#ifdef VBOX_WITH_STATISTICS
5865 /*
5866 * Register statistics.
5867 */
5868 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledIsocUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/CanceledIsocUrbs", STAMUNIT_OCCURENCES, "Detected canceled isochronous URBs.");
5869 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledGenUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/CanceledGenUrbs", STAMUNIT_OCCURENCES, "Detected canceled general URBs.");
5870 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatDroppedUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/DroppedUrbs", STAMUNIT_OCCURENCES, "Dropped URBs (endpoint halted, or URB canceled).");
5871 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, "/Devices/OHCI/Timer", STAMUNIT_TICKS_PER_CALL, "Profiling ohciFrameBoundaryTimer.");
5872#endif
5873
5874 /*
5875 * Register debugger info callbacks.
5876 */
5877 PDMDevHlpDBGFInfoRegister(pDevIns, "ohci", "OHCI control registers.", ohciR3InfoRegs);
5878
5879#if 0/*def DEBUG_bird*/
5880// g_fLogInterruptEPs = true;
5881 g_fLogControlEPs = true;
5882 g_fLogBulkEPs = true;
5883#endif
5884
5885 return VINF_SUCCESS;
5886}
5887
5888
5889const PDMDEVREG g_DeviceOHCI =
5890{
5891 /* u32version */
5892 PDM_DEVREG_VERSION,
5893 /* szName */
5894 "usb-ohci",
5895 /* szRCMod */
5896 "VBoxDDGC.gc",
5897 /* szR0Mod */
5898 "VBoxDDR0.r0",
5899 /* pszDescription */
5900 "OHCI USB controller.\n",
5901 /* fFlags */
5902 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
5903 /* fClass */
5904 PDM_DEVREG_CLASS_BUS_USB,
5905 /* cMaxInstances */
5906 ~0U,
5907 /* cbInstance */
5908 sizeof(OHCI),
5909 /* pfnConstruct */
5910 ohciR3Construct,
5911 /* pfnDestruct */
5912 ohciR3Destruct,
5913 /* pfnRelocate */
5914 ohciR3Relocate,
5915 /* pfnMemSetup */
5916 NULL,
5917 /* pfnPowerOn */
5918 NULL,
5919 /* pfnReset */
5920 ohciR3Reset,
5921 /* pfnSuspend */
5922 NULL,
5923 /* pfnResume */
5924 ohciR3Resume,
5925 /* pfnAttach */
5926 NULL,
5927 /* pfnDetach */
5928 NULL,
5929 /* pfnQueryInterface */
5930 NULL,
5931 /* pfnInitComplete */
5932 NULL,
5933 /* pfnPowerOff */
5934 NULL,
5935 /* pfnSoftReset */
5936 NULL,
5937 /* u32VersionEnd */
5938 PDM_DEVREG_VERSION
5939};
5940
5941#endif /* IN_RING3 */
5942#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