VirtualBox

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

Last change on this file since 69721 was 69721, checked in by vboxsync, 7 years ago

Devices/USB: bugref:9022 Update the possibly cached data when writing back endpoint or transfer descriptors. Otherwise a ED/TD might get serviced twice because of the URB completion handler racing the frame processing thread and the frame processing thread operating on stale data

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

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