VirtualBox

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

Last change on this file since 69901 was 69871, checked in by vboxsync, 7 years ago

OHCI: Renamed and refactored for clear R3/RC separation.

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

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