VirtualBox

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

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

OHCI,EHCI: 6305: Maximal number of ports is set to 15

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