VirtualBox

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

Last change on this file since 62955 was 62955, checked in by vboxsync, 8 years ago

Devices: warnings

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

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