VirtualBox

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

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

Devices/USB/DevOHCI: disabled probably over-cautious assertion

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