VirtualBox

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

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

DevOHCI: Bumped saved state version and kicked out the dummy end-of-frame timer. bugref:9218

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

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