VirtualBox

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

Last change on this file since 99367 was 99367, checked in by vboxsync, 17 months ago

OHCI: More thorough cleanup of URB in-flight tracking.

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