VirtualBox

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

Last change on this file since 31522 was 31249, checked in by vboxsync, 15 years ago

Warning.

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