VirtualBox

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

Last change on this file since 33223 was 33137, checked in by vboxsync, 14 years ago

PCI: MSI in USB controllers

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