VirtualBox

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

Last change on this file since 90332 was 90332, checked in by vboxsync, 3 years ago

Dev*: Use PDMCritSect API via PDMDevHlp. bugref:10074

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