VirtualBox

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

Last change on this file since 45761 was 45671, checked in by vboxsync, 12 years ago

OHCI: Endpoints may need to be skipped when looking for orphaned URBs.

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