VirtualBox

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

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

scm --update-copyright-year

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