VirtualBox

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

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

Devices/USB/DevOHCI.cpp: Small addendum to r129929, add LogRelMax() statement to log whenever the HC raises an unrecoverable error

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