VirtualBox

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

Last change on this file since 37469 was 37469, checked in by vboxsync, 13 years ago

USB/Dev*: Locking fixes.

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