VirtualBox

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

Last change on this file since 37299 was 35353, checked in by vboxsync, 14 years ago

Move the misc files the in src/VBox/Devices/ directory into a build/ subdirectory, changing their names to match the target module.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette