VirtualBox

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

Last change on this file since 79177 was 78549, checked in by vboxsync, 6 years ago

OHCI: Interpret the OHCI spec a bit more literally when processing a general TD (see bugref:9429).

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

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