VirtualBox

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

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

OHCI: Report low/full speed to guest. Currently theoretical.

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

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