VirtualBox

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

Last change on this file since 49958 was 49958, checked in by vboxsync, 11 years ago

EHCI,OHCI: Fix possible deadlock introduced with the first version of the rework. Use a separate critical section instead of the per device one to synchronise interrupt register access between EMT and the frame thread

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