VirtualBox

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

Last change on this file since 87965 was 87773, checked in by vboxsync, 4 years ago

VMM/TM,Devices: Store the timer name in the TMTIMER structure and limit it to 31 characters. Shortened most timer names. bugref:9943

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

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