VirtualBox

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

Last change on this file since 91348 was 90447, checked in by vboxsync, 4 years ago

Dev*: Checked up all the PDMDevHlpCritSectEnter calls to make sure the status code is checked. bugref:6695

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