VirtualBox

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

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

Devices/USB: First part of the rework, move most of the work to dedicated threads to improve performance

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