VirtualBox

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

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

Devices/Dev*: Replaced some leftover 'PDMBOTHCBDECL' usage with 'static DECLCALLBACK' (noexcept/c++17).

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

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