VirtualBox

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

Last change on this file since 86405 was 84514, checked in by vboxsync, 5 years ago

Devices/USB: Convert to use new PDMDevHlp guest memory read/write helpers differentiating between metadata/userdata transfers for tracing, bugref:9210

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