VirtualBox

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

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

USB: Fix lock order violation when canceling all URBs

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