VirtualBox

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

Last change on this file since 82891 was 82661, checked in by vboxsync, 5 years ago

DevOHCI: Cleaned up old code (see bugref:9595).

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