VirtualBox

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

Last change on this file since 77807 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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

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