VirtualBox

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

Last change on this file since 81872 was 81692, checked in by vboxsync, 5 years ago

Devices/USB/Dev*: Use default statistics prefix.

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