VirtualBox

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

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

OHCI/VUSB: Propagate transfer cancellation from the guest to avoid stuck URBs.

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

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