VirtualBox

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

Last change on this file since 59775 was 59704, checked in by vboxsync, 9 years ago

VUSB: Some structural cleanup (#3 Make the VUSB URB specific data private to the VUSB stack, some interface changes required for XHCI because it previously required access to some VUsb members)

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