VirtualBox

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

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

OHCI: More logging when buffer sizes don't add up.

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

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