VirtualBox

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

Last change on this file since 97577 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 219.2 KB
Line 
1/* $Id: DevOHCI.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * DevOHCI - Open Host Controller Interface for USB.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 * Removes all TDs associated with a URB from the in-flight tracking.
2053 *
2054 * @returns 0 if found. For logged builds this is the number of frames the TD has been in-flight.
2055 * @returns -1 if not found.
2056 * @param pThis OHCI instance data, shared edition (for logging).
2057 * @param pThisCC OHCI instance data, ring-3 edition.
2058 * @param pUrb The URB.
2059 */
2060static int ohciR3InFlightRemoveUrb(POHCI pThis, POHCICC pThisCC, PVUSBURB pUrb)
2061{
2062 int cFramesInFlight = ohciR3InFlightRemove(pThis, pThisCC, pUrb->paTds[0].TdAddr);
2063 if (pUrb->pHci->cTds > 1)
2064 {
2065 for (unsigned iTd = 1; iTd < pUrb->pHci->cTds; iTd++)
2066 if (ohciR3InFlightRemove(pThis, pThisCC, pUrb->paTds[iTd].TdAddr) < 0)
2067 cFramesInFlight = -1;
2068 }
2069 return cFramesInFlight;
2070}
2071
2072
2073# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
2074
2075/**
2076 * Empties the in-done-queue.
2077 * @param pThisCC OHCI instance data, ring-3 edition.
2078 */
2079static void ohciR3InDoneQueueZap(POHCICC pThisCC)
2080{
2081 pThisCC->cInDoneQueue = 0;
2082}
2083
2084/**
2085 * Finds a TD in the in-done-queue.
2086 * @returns >= 0 on success.
2087 * @returns -1 if not found.
2088 * @param pThisCC OHCI instance data, ring-3 edition.
2089 * @param GCPhysTD Physical address of the TD.
2090 */
2091static int ohciR3InDoneQueueFind(POHCICC pThisCC, uint32_t GCPhysTD)
2092{
2093 unsigned i = pThisCC->cInDoneQueue;
2094 while (i-- > 0)
2095 if (pThisCC->aInDoneQueue[i].GCPhysTD == GCPhysTD)
2096 return i;
2097 return -1;
2098}
2099
2100/**
2101 * Checks that the specified TD is not in the done queue.
2102 * @param pThisCC OHCI instance data, ring-3 edition.
2103 * @param GCPhysTD Physical address of the TD.
2104 */
2105static bool ohciR3InDoneQueueCheck(POHCICC pThisCC, uint32_t GCPhysTD)
2106{
2107 int i = ohciR3InDoneQueueFind(pThisCC, GCPhysTD);
2108# if 0
2109 /* This condition has been observed with the USB tablet emulation or with
2110 * a real USB mouse and an SMP XP guest. I am also not sure if this is
2111 * really a problem for us. The assertion checks that the guest doesn't
2112 * re-submit a TD which is still in the done queue. It seems to me that
2113 * this should only be a problem if we either keep track of TDs in the done
2114 * queue somewhere else as well (in which case we should also free those
2115 * references in time, and I can't see any code doing that) or if we
2116 * manipulate TDs in the done queue in some way that might fail if they are
2117 * re-submitted (can't see anything like that either).
2118 */
2119 AssertMsg(i < 0, ("TD %#010x (i=%d)\n", GCPhysTD, i));
2120# endif
2121 return i < 0;
2122}
2123
2124
2125# if defined(VBOX_STRICT) && defined(LOG_ENABLED)
2126/**
2127 * Adds a TD to the in-done-queue tracking, checking that it's not there already.
2128 * @param pThisCC OHCI instance data, ring-3 edition.
2129 * @param GCPhysTD Physical address of the TD.
2130 */
2131static void ohciR3InDoneQueueAdd(POHCICC pThisCC, uint32_t GCPhysTD)
2132{
2133 Assert(pThisCC->cInDoneQueue + 1 <= RT_ELEMENTS(pThisCC->aInDoneQueue));
2134 if (ohciR3InDoneQueueCheck(pThisCC, GCPhysTD))
2135 pThisCC->aInDoneQueue[pThisCC->cInDoneQueue++].GCPhysTD = GCPhysTD;
2136}
2137# endif /* VBOX_STRICT */
2138# endif /* defined(VBOX_STRICT) || defined(LOG_ENABLED) */
2139
2140
2141/**
2142 * OHCI Transport Buffer - represents a OHCI Transport Descriptor (TD).
2143 * A TD may be split over max 2 pages.
2144 */
2145typedef struct OHCIBUF
2146{
2147 /** Pages involved. */
2148 struct OHCIBUFVEC
2149 {
2150 /** The 32-bit physical address of this part. */
2151 uint32_t Addr;
2152 /** The length. */
2153 uint32_t cb;
2154 } aVecs[2];
2155 /** Number of valid entries in aVecs. */
2156 uint32_t cVecs;
2157 /** The total length. */
2158 uint32_t cbTotal;
2159} OHCIBUF, *POHCIBUF;
2160
2161
2162/**
2163 * Sets up a OHCI transport buffer.
2164 *
2165 * @param pBuf OHCI buffer.
2166 * @param cbp Current buffer pointer. 32-bit physical address.
2167 * @param be Last byte in buffer (BufferEnd). 32-bit physical address.
2168 */
2169static void ohciR3BufInit(POHCIBUF pBuf, uint32_t cbp, uint32_t be)
2170{
2171 if (!cbp || !be)
2172 {
2173 pBuf->cVecs = 0;
2174 pBuf->cbTotal = 0;
2175 Log2(("ohci: cbp=%#010x be=%#010x cbTotal=0 EMPTY\n", cbp, be));
2176 }
2177 else if ((cbp & ~0xfff) == (be & ~0xfff) && (cbp <= be))
2178 {
2179 pBuf->aVecs[0].Addr = cbp;
2180 pBuf->aVecs[0].cb = (be - cbp) + 1;
2181 pBuf->cVecs = 1;
2182 pBuf->cbTotal = pBuf->aVecs[0].cb;
2183 Log2(("ohci: cbp=%#010x be=%#010x cbTotal=%u\n", cbp, be, pBuf->cbTotal));
2184 }
2185 else
2186 {
2187 pBuf->aVecs[0].Addr = cbp;
2188 pBuf->aVecs[0].cb = 0x1000 - (cbp & 0xfff);
2189 pBuf->aVecs[1].Addr = be & ~0xfff;
2190 pBuf->aVecs[1].cb = (be & 0xfff) + 1;
2191 pBuf->cVecs = 2;
2192 pBuf->cbTotal = pBuf->aVecs[0].cb + pBuf->aVecs[1].cb;
2193 Log2(("ohci: cbp=%#010x be=%#010x cbTotal=%u PAGE FLIP\n", cbp, be, pBuf->cbTotal));
2194 }
2195}
2196
2197/**
2198 * Updates a OHCI transport buffer.
2199 *
2200 * This is called upon completion to adjust the sector lengths if
2201 * the total length has changed. (received less then we had space for
2202 * or a partial transfer.)
2203 *
2204 * @param pBuf The buffer to update. cbTotal contains the new total on input.
2205 * While the aVecs[*].cb members is updated upon return.
2206 */
2207static void ohciR3BufUpdate(POHCIBUF pBuf)
2208{
2209 for (uint32_t i = 0, cbCur = 0; i < pBuf->cVecs; i++)
2210 {
2211 if (cbCur + pBuf->aVecs[i].cb > pBuf->cbTotal)
2212 {
2213 pBuf->aVecs[i].cb = pBuf->cbTotal - cbCur;
2214 pBuf->cVecs = i + 1;
2215 return;
2216 }
2217 cbCur += pBuf->aVecs[i].cb;
2218 }
2219}
2220
2221
2222/** A worker for ohciR3UnlinkTds(). */
2223static bool ohciR3UnlinkIsochronousTdInList(PPDMDEVINS pDevIns, POHCI pThis, uint32_t TdAddr, POHCIITD pITd, POHCIED pEd)
2224{
2225 const uint32_t LastTdAddr = pEd->TailP & ED_PTR_MASK;
2226 Log(("ohciUnlinkIsocTdInList: Unlinking non-head ITD! TdAddr=%#010RX32 HeadTdAddr=%#010RX32 LastEdAddr=%#010RX32\n",
2227 TdAddr, pEd->HeadP & ED_PTR_MASK, LastTdAddr));
2228 AssertMsgReturn(LastTdAddr != TdAddr, ("TdAddr=%#010RX32\n", TdAddr), false);
2229
2230 uint32_t cIterations = 256;
2231 uint32_t CurTdAddr = pEd->HeadP & ED_PTR_MASK;
2232 while ( CurTdAddr != LastTdAddr
2233 && cIterations-- > 0)
2234 {
2235 OHCIITD ITd;
2236 ohciR3ReadITd(pDevIns, pThis, CurTdAddr, &ITd);
2237 if ((ITd.NextTD & ED_PTR_MASK) == TdAddr)
2238 {
2239 ITd.NextTD = (pITd->NextTD & ED_PTR_MASK) | (ITd.NextTD & ~ED_PTR_MASK);
2240 ohciR3WriteITd(pDevIns, pThis, CurTdAddr, &ITd, "ohciUnlinkIsocTdInList");
2241 pITd->NextTD &= ~ED_PTR_MASK;
2242 return true;
2243 }
2244
2245 /* next */
2246 CurTdAddr = ITd.NextTD & ED_PTR_MASK;
2247 }
2248
2249 Log(("ohciUnlinkIsocTdInList: TdAddr=%#010RX32 wasn't found in the list!!! (cIterations=%d)\n", TdAddr, cIterations));
2250 return false;
2251}
2252
2253
2254/** A worker for ohciR3UnlinkTds(). */
2255static bool ohciR3UnlinkGeneralTdInList(PPDMDEVINS pDevIns, uint32_t TdAddr, POHCITD pTd, POHCIED pEd)
2256{
2257 const uint32_t LastTdAddr = pEd->TailP & ED_PTR_MASK;
2258 Log(("ohciR3UnlinkGeneralTdInList: Unlinking non-head TD! TdAddr=%#010RX32 HeadTdAddr=%#010RX32 LastEdAddr=%#010RX32\n",
2259 TdAddr, pEd->HeadP & ED_PTR_MASK, LastTdAddr));
2260 AssertMsgReturn(LastTdAddr != TdAddr, ("TdAddr=%#010RX32\n", TdAddr), false);
2261
2262 uint32_t cIterations = 256;
2263 uint32_t CurTdAddr = pEd->HeadP & ED_PTR_MASK;
2264 while ( CurTdAddr != LastTdAddr
2265 && cIterations-- > 0)
2266 {
2267 OHCITD Td;
2268 ohciR3ReadTd(pDevIns, CurTdAddr, &Td);
2269 if ((Td.NextTD & ED_PTR_MASK) == TdAddr)
2270 {
2271 Td.NextTD = (pTd->NextTD & ED_PTR_MASK) | (Td.NextTD & ~ED_PTR_MASK);
2272 ohciR3WriteTd(pDevIns, CurTdAddr, &Td, "ohciR3UnlinkGeneralTdInList");
2273 pTd->NextTD &= ~ED_PTR_MASK;
2274 return true;
2275 }
2276
2277 /* next */
2278 CurTdAddr = Td.NextTD & ED_PTR_MASK;
2279 }
2280
2281 Log(("ohciR3UnlinkGeneralTdInList: TdAddr=%#010RX32 wasn't found in the list!!! (cIterations=%d)\n", TdAddr, cIterations));
2282 return false;
2283}
2284
2285
2286/**
2287 * Unlinks the TDs that makes up the URB from the ED.
2288 *
2289 * @returns success indicator. true if successfully unlinked.
2290 * @returns false if the TD was not found in the list.
2291 */
2292static bool ohciR3UnlinkTds(PPDMDEVINS pDevIns, POHCI pThis, PVUSBURB pUrb, POHCIED pEd)
2293{
2294 /*
2295 * Don't unlink more than once.
2296 */
2297 if (pUrb->pHci->fUnlinked)
2298 return true;
2299 pUrb->pHci->fUnlinked = true;
2300
2301 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2302 {
2303 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2304 {
2305 POHCIITD pITd = (POHCIITD)&pUrb->paTds[iTd].TdCopy[0];
2306 const uint32_t ITdAddr = pUrb->paTds[iTd].TdAddr;
2307
2308 /*
2309 * Unlink the TD from the ED list.
2310 * The normal case is that it's at the head of the list.
2311 */
2312 Assert((ITdAddr & ED_PTR_MASK) == ITdAddr);
2313 if ((pEd->HeadP & ED_PTR_MASK) == ITdAddr)
2314 {
2315 pEd->HeadP = (pITd->NextTD & ED_PTR_MASK) | (pEd->HeadP & ~ED_PTR_MASK);
2316 pITd->NextTD &= ~ED_PTR_MASK;
2317 }
2318 else
2319 {
2320 /*
2321 * It's probably somewhere in the list, not a unlikely situation with
2322 * the current isochronous code.
2323 */
2324 if (!ohciR3UnlinkIsochronousTdInList(pDevIns, pThis, ITdAddr, pITd, pEd))
2325 return false;
2326 }
2327 }
2328 }
2329 else
2330 {
2331 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2332 {
2333 POHCITD pTd = (POHCITD)&pUrb->paTds[iTd].TdCopy[0];
2334 const uint32_t TdAddr = pUrb->paTds[iTd].TdAddr;
2335
2336 /** @todo r=bird: Messing with the toggle flag in prepare is probably not correct
2337 * when we encounter a STALL error, 4.3.1.3.7.2: ''If an endpoint returns a STALL
2338 * PID, the Host Controller retires the General TD with the ConditionCode set
2339 * to STALL and halts the endpoint. The CurrentBufferPointer, ErrorCount, and
2340 * dataToggle fields retain the values that they had at the start of the
2341 * transaction.'' */
2342
2343 /* update toggle and set data toggle carry */
2344 pTd->hwinfo &= ~TD_HWINFO_TOGGLE;
2345 if ( pTd->hwinfo & TD_HWINFO_TOGGLE_HI )
2346 {
2347 if ( !!(pTd->hwinfo & TD_HWINFO_TOGGLE_LO) ) /** @todo r=bird: is it just me or doesn't this make sense at all? */
2348 pTd->hwinfo |= TD_HWINFO_TOGGLE_LO;
2349 else
2350 pTd->hwinfo &= ~TD_HWINFO_TOGGLE_LO;
2351 }
2352 else
2353 {
2354 if ( !!(pEd->HeadP & ED_HEAD_CARRY) ) /** @todo r=bird: is it just me or doesn't this make sense at all? */
2355 pEd->HeadP |= ED_HEAD_CARRY;
2356 else
2357 pEd->HeadP &= ~ED_HEAD_CARRY;
2358 }
2359
2360 /*
2361 * Unlink the TD from the ED list.
2362 * The normal case is that it's at the head of the list.
2363 */
2364 Assert((TdAddr & ED_PTR_MASK) == TdAddr);
2365 if ((pEd->HeadP & ED_PTR_MASK) == TdAddr)
2366 {
2367 pEd->HeadP = (pTd->NextTD & ED_PTR_MASK) | (pEd->HeadP & ~ED_PTR_MASK);
2368 pTd->NextTD &= ~ED_PTR_MASK;
2369 }
2370 else
2371 {
2372 /*
2373 * The TD is probably somewhere in the list.
2374 *
2375 * This shouldn't ever happen unless there was a failure! Even on failure,
2376 * we can screw up the HCD state by picking out a TD from within the list
2377 * like this! If this turns out to be a problem, we have to find a better
2378 * solution. For now we'll hope the HCD handles it...
2379 */
2380 if (!ohciR3UnlinkGeneralTdInList(pDevIns, TdAddr, pTd, pEd))
2381 return false;
2382 }
2383
2384 /*
2385 * Only unlink the first TD on error.
2386 * See comment in ohciR3RhXferCompleteGeneralURB().
2387 */
2388 if (pUrb->enmStatus != VUSBSTATUS_OK)
2389 break;
2390 }
2391 }
2392
2393 return true;
2394}
2395
2396
2397/**
2398 * Checks that the transport descriptors associated with the URB
2399 * hasn't been changed in any way indicating that they may have been canceled.
2400 *
2401 * This rountine also updates the TD copies contained within the URB.
2402 *
2403 * @returns true if the URB has been canceled, otherwise false.
2404 * @param pDevIns The device instance.
2405 * @param pThis The OHCI instance.
2406 * @param pUrb The URB in question.
2407 * @param pEd The ED pointer (optional).
2408 */
2409static bool ohciR3HasUrbBeenCanceled(PPDMDEVINS pDevIns, POHCI pThis, PVUSBURB pUrb, PCOHCIED pEd)
2410{
2411 if (!pUrb)
2412 return true;
2413
2414 /*
2415 * Make sure we've got an endpoint descriptor so we can
2416 * check for tail TDs.
2417 */
2418 OHCIED Ed;
2419 if (!pEd)
2420 {
2421 ohciR3ReadEd(pDevIns, pUrb->pHci->EdAddr, &Ed);
2422 pEd = &Ed;
2423 }
2424
2425 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2426 {
2427 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2428 {
2429 union
2430 {
2431 OHCIITD ITd;
2432 uint32_t au32[8];
2433 } u;
2434 if ( (pUrb->paTds[iTd].TdAddr & ED_PTR_MASK)
2435 == (pEd->TailP & ED_PTR_MASK))
2436 {
2437 Log(("%s: ohciR3HasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled (tail)! [iso]\n",
2438 pUrb->pszDesc, iTd, pUrb->pHci->cTds, pUrb->paTds[iTd].TdAddr));
2439 STAM_COUNTER_INC(&pThis->StatCanceledIsocUrbs);
2440 return true;
2441 }
2442 ohciR3ReadITd(pDevIns, pThis, pUrb->paTds[iTd].TdAddr, &u.ITd);
2443 if ( u.au32[0] != pUrb->paTds[iTd].TdCopy[0] /* hwinfo */
2444 || u.au32[1] != pUrb->paTds[iTd].TdCopy[1] /* bp0 */
2445 || u.au32[3] != pUrb->paTds[iTd].TdCopy[3] /* be */
2446 || ( u.au32[2] != pUrb->paTds[iTd].TdCopy[2] /* NextTD */
2447 && iTd + 1 < pUrb->pHci->cTds /* ignore the last one */)
2448 || u.au32[4] != pUrb->paTds[iTd].TdCopy[4] /* psw0&1 */
2449 || u.au32[5] != pUrb->paTds[iTd].TdCopy[5] /* psw2&3 */
2450 || u.au32[6] != pUrb->paTds[iTd].TdCopy[6] /* psw4&5 */
2451 || u.au32[7] != pUrb->paTds[iTd].TdCopy[7] /* psw6&7 */
2452 )
2453 {
2454 Log(("%s: ohciR3HasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled! [iso]\n",
2455 pUrb->pszDesc, iTd, pUrb->pHci->cTds, pUrb->paTds[iTd].TdAddr));
2456 Log2((" %.*Rhxs (cur)\n"
2457 "!= %.*Rhxs (copy)\n",
2458 sizeof(u.ITd), &u.ITd, sizeof(u.ITd), &pUrb->paTds[iTd].TdCopy[0]));
2459 STAM_COUNTER_INC(&pThis->StatCanceledIsocUrbs);
2460 return true;
2461 }
2462 pUrb->paTds[iTd].TdCopy[2] = u.au32[2];
2463 }
2464 }
2465 else
2466 {
2467 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2468 {
2469 union
2470 {
2471 OHCITD Td;
2472 uint32_t au32[4];
2473 } u;
2474 if ( (pUrb->paTds[iTd].TdAddr & ED_PTR_MASK)
2475 == (pEd->TailP & ED_PTR_MASK))
2476 {
2477 Log(("%s: ohciR3HasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled (tail)!\n",
2478 pUrb->pszDesc, iTd, pUrb->pHci->cTds, pUrb->paTds[iTd].TdAddr));
2479 STAM_COUNTER_INC(&pThis->StatCanceledGenUrbs);
2480 return true;
2481 }
2482 ohciR3ReadTd(pDevIns, pUrb->paTds[iTd].TdAddr, &u.Td);
2483 if ( u.au32[0] != pUrb->paTds[iTd].TdCopy[0] /* hwinfo */
2484 || u.au32[1] != pUrb->paTds[iTd].TdCopy[1] /* cbp */
2485 || u.au32[3] != pUrb->paTds[iTd].TdCopy[3] /* be */
2486 || ( u.au32[2] != pUrb->paTds[iTd].TdCopy[2] /* NextTD */
2487 && iTd + 1 < pUrb->pHci->cTds /* ignore the last one */)
2488 )
2489 {
2490 Log(("%s: ohciR3HasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled!\n",
2491 pUrb->pszDesc, iTd, pUrb->pHci->cTds, pUrb->paTds[iTd].TdAddr));
2492 Log2((" %.*Rhxs (cur)\n"
2493 "!= %.*Rhxs (copy)\n",
2494 sizeof(u.Td), &u.Td, sizeof(u.Td), &pUrb->paTds[iTd].TdCopy[0]));
2495 STAM_COUNTER_INC(&pThis->StatCanceledGenUrbs);
2496 return true;
2497 }
2498 pUrb->paTds[iTd].TdCopy[2] = u.au32[2];
2499 }
2500 }
2501 return false;
2502}
2503
2504
2505/**
2506 * Returns the OHCI_CC_* corresponding to the VUSB status code.
2507 *
2508 * @returns OHCI_CC_* value.
2509 * @param enmStatus The VUSB status code.
2510 */
2511static uint32_t ohciR3VUsbStatus2OhciStatus(VUSBSTATUS enmStatus)
2512{
2513 switch (enmStatus)
2514 {
2515 case VUSBSTATUS_OK: return OHCI_CC_NO_ERROR;
2516 case VUSBSTATUS_STALL: return OHCI_CC_STALL;
2517 case VUSBSTATUS_CRC: return OHCI_CC_CRC;
2518 case VUSBSTATUS_DATA_UNDERRUN: return OHCI_CC_DATA_UNDERRUN;
2519 case VUSBSTATUS_DATA_OVERRUN: return OHCI_CC_DATA_OVERRUN;
2520 case VUSBSTATUS_DNR: return OHCI_CC_DNR;
2521 case VUSBSTATUS_NOT_ACCESSED: return OHCI_CC_NOT_ACCESSED_1;
2522 default:
2523 Log(("pUrb->enmStatus=%#x!!!\n", enmStatus));
2524 return OHCI_CC_DNR;
2525 }
2526}
2527
2528
2529/**
2530 * Lock the given OHCI controller instance.
2531 *
2532 * @returns nothing.
2533 * @param pThisCC The OHCI controller instance to lock, ring-3 edition.
2534 */
2535DECLINLINE(void) ohciR3Lock(POHCICC pThisCC)
2536{
2537 RTCritSectEnter(&pThisCC->CritSect);
2538
2539# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
2540 /* Clear all caches here to avoid reading stale data from previous lock holders. */
2541 ohciR3PhysReadCacheInvalidate(&pThisCC->CacheED);
2542 ohciR3PhysReadCacheInvalidate(&pThisCC->CacheTD);
2543# endif
2544}
2545
2546
2547/**
2548 * Unlocks the given OHCI controller instance.
2549 *
2550 * @returns nothing.
2551 * @param pThisCC The OHCI controller instance to unlock, ring-3 edition.
2552 */
2553DECLINLINE(void) ohciR3Unlock(POHCICC pThisCC)
2554{
2555# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
2556 /*
2557 * Clear all caches here to avoid leaving stale data behind (paranoia^2,
2558 * already done in ohciR3Lock).
2559 */
2560 ohciR3PhysReadCacheInvalidate(&pThisCC->CacheED);
2561 ohciR3PhysReadCacheInvalidate(&pThisCC->CacheTD);
2562# endif
2563
2564 RTCritSectLeave(&pThisCC->CritSect);
2565}
2566
2567
2568/**
2569 * Worker for ohciR3RhXferCompletion that handles the completion of
2570 * a URB made up of isochronous TDs.
2571 *
2572 * In general, all URBs should have status OK.
2573 */
2574static void ohciR3RhXferCompleteIsochronousURB(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, PVUSBURB pUrb
2575 /*, POHCIED pEd , int cFmAge*/)
2576{
2577 /*
2578 * Copy the data back (if IN operation) and update the TDs.
2579 */
2580 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2581 {
2582 POHCIITD pITd = (POHCIITD)&pUrb->paTds[iTd].TdCopy[0];
2583 const uint32_t ITdAddr = pUrb->paTds[iTd].TdAddr;
2584 const unsigned cFrames = ((pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
2585 unsigned R = (pUrb->pHci->u32FrameNo & ITD_HWINFO_SF) - (pITd->HwInfo & ITD_HWINFO_SF);
2586 if (R >= 8)
2587 R = 0; /* submitted ahead of time. */
2588
2589 /*
2590 * Only one case of TD level condition code is document, so
2591 * just set NO_ERROR here to reduce number duplicate code.
2592 */
2593 pITd->HwInfo &= ~TD_HWINFO_CC;
2594 AssertCompile(OHCI_CC_NO_ERROR == 0);
2595
2596 if (pUrb->enmStatus == VUSBSTATUS_OK)
2597 {
2598 /*
2599 * Update the frames and copy back the data.
2600 * We assume that we don't get incorrect lengths here.
2601 */
2602 for (unsigned i = 0; i < cFrames; i++)
2603 {
2604 if ( i < R
2605 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_NOT_ACCESSED)
2606 {
2607 /* It should already be NotAccessed. */
2608 pITd->aPSW[i] |= 0xe000; /* (Don't touch the 12th bit.) */
2609 continue;
2610 }
2611
2612 /* Update the PSW (save the offset first in case of a IN). */
2613 uint32_t off = pITd->aPSW[i] & ITD_PSW_OFFSET;
2614 pITd->aPSW[i] = ohciR3VUsbStatus2OhciStatus(pUrb->aIsocPkts[i - R].enmStatus)
2615 >> (TD_HWINFO_CC_SHIFT - ITD_PSW_CC_SHIFT);
2616
2617 if ( pUrb->enmDir == VUSBDIRECTION_IN
2618 && ( pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_OK
2619 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_DATA_UNDERRUN
2620 || pUrb->aIsocPkts[i - R].enmStatus == VUSBSTATUS_DATA_OVERRUN))
2621 {
2622 /* Set the size. */
2623 const unsigned cb = pUrb->aIsocPkts[i - R].cb;
2624 pITd->aPSW[i] |= cb & ITD_PSW_SIZE;
2625 /* Copy data. */
2626 if (cb)
2627 {
2628 uint8_t *pb = &pUrb->abData[pUrb->aIsocPkts[i - R].off];
2629 if (off + cb > 0x1000)
2630 {
2631 if (off < 0x1000)
2632 {
2633 /* both */
2634 const unsigned cb0 = 0x1000 - off;
2635 ohciR3PhysWrite(pDevIns, (pITd->BP0 & ITD_BP0_MASK) + off, pb, cb0);
2636 ohciR3PhysWrite(pDevIns, pITd->BE & ITD_BP0_MASK, pb + cb0, cb - cb0);
2637 }
2638 else /* only in the 2nd page */
2639 ohciR3PhysWrite(pDevIns, (pITd->BE & ITD_BP0_MASK) + (off & ITD_BP0_MASK), pb, cb);
2640 }
2641 else /* only in the 1st page */
2642 ohciR3PhysWrite(pDevIns, (pITd->BP0 & ITD_BP0_MASK) + off, pb, cb);
2643 Log5(("packet %d: off=%#x cb=%#x pb=%p (%#x)\n"
2644 "%.*Rhxd\n",
2645 i + R, off, cb, pb, pb - &pUrb->abData[0], cb, pb));
2646 //off += cb;
2647 }
2648 }
2649 }
2650
2651 /*
2652 * If the last package ended with a NotAccessed status, set ITD CC
2653 * to DataOverrun to indicate scheduling overrun.
2654 */
2655 if (pUrb->aIsocPkts[pUrb->cIsocPkts - 1].enmStatus == VUSBSTATUS_NOT_ACCESSED)
2656 pITd->HwInfo |= OHCI_CC_DATA_OVERRUN;
2657 }
2658 else
2659 {
2660 Log(("DevOHCI: Taking untested code path at line %d...\n", __LINE__));
2661 /*
2662 * Most status codes only applies to the individual packets.
2663 *
2664 * If we get a URB level error code of this kind, we'll distribute
2665 * it to all the packages unless some other status is available for
2666 * a package. This is a bit fuzzy, and we will get rid of this code
2667 * before long!
2668 */
2669 //if (pUrb->enmStatus != VUSBSTATUS_DATA_OVERRUN)
2670 {
2671 const unsigned uCC = ohciR3VUsbStatus2OhciStatus(pUrb->enmStatus)
2672 >> (TD_HWINFO_CC_SHIFT - ITD_PSW_CC_SHIFT);
2673 for (unsigned i = 0; i < cFrames; i++)
2674 pITd->aPSW[i] = uCC;
2675 }
2676 //else
2677 // pITd->HwInfo |= ohciR3VUsbStatus2OhciStatus(pUrb->enmStatus);
2678 }
2679
2680 /*
2681 * Update the done queue interrupt timer.
2682 */
2683 uint32_t DoneInt = (pITd->HwInfo & ITD_HWINFO_DI) >> ITD_HWINFO_DI_SHIFT;
2684 if ((pITd->HwInfo & TD_HWINFO_CC) != OHCI_CC_NO_ERROR)
2685 DoneInt = 0; /* It's cleared on error. */
2686 if ( DoneInt != 0x7
2687 && DoneInt < pThis->dqic)
2688 pThis->dqic = DoneInt;
2689
2690 /*
2691 * Move on to the done list and write back the modified TD.
2692 */
2693# ifdef LOG_ENABLED
2694 if (!pThis->done)
2695 pThisCC->u32FmDoneQueueTail = pThis->HcFmNumber;
2696# ifdef VBOX_STRICT
2697 ohciR3InDoneQueueAdd(pThisCC, ITdAddr);
2698# endif
2699# endif
2700 pITd->NextTD = pThis->done;
2701 pThis->done = ITdAddr;
2702
2703 Log(("%s: ohciR3RhXferCompleteIsochronousURB: ITdAddr=%#010x EdAddr=%#010x SF=%#x (%#x) CC=%#x FC=%d "
2704 "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",
2705 pUrb->pszDesc, ITdAddr,
2706 pUrb->pHci->EdAddr,
2707 pITd->HwInfo & ITD_HWINFO_SF, pThis->HcFmNumber,
2708 (pITd->HwInfo & ITD_HWINFO_CC) >> ITD_HWINFO_CC_SHIFT,
2709 (pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT,
2710 pITd->aPSW[0] >> ITD_PSW_CC_SHIFT, pITd->aPSW[0] & ITD_PSW_SIZE,
2711 pITd->aPSW[1] >> ITD_PSW_CC_SHIFT, pITd->aPSW[1] & ITD_PSW_SIZE,
2712 pITd->aPSW[2] >> ITD_PSW_CC_SHIFT, pITd->aPSW[2] & ITD_PSW_SIZE,
2713 pITd->aPSW[3] >> ITD_PSW_CC_SHIFT, pITd->aPSW[3] & ITD_PSW_SIZE,
2714 pITd->aPSW[4] >> ITD_PSW_CC_SHIFT, pITd->aPSW[4] & ITD_PSW_SIZE,
2715 pITd->aPSW[5] >> ITD_PSW_CC_SHIFT, pITd->aPSW[5] & ITD_PSW_SIZE,
2716 pITd->aPSW[6] >> ITD_PSW_CC_SHIFT, pITd->aPSW[6] & ITD_PSW_SIZE,
2717 pITd->aPSW[7] >> ITD_PSW_CC_SHIFT, pITd->aPSW[7] & ITD_PSW_SIZE,
2718 R));
2719 ohciR3WriteITd(pDevIns, pThis, ITdAddr, pITd, "retired");
2720 }
2721 RT_NOREF(pThisCC);
2722}
2723
2724
2725/**
2726 * Worker for ohciR3RhXferCompletion that handles the completion of
2727 * a URB made up of general TDs.
2728 */
2729static void ohciR3RhXferCompleteGeneralURB(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, PVUSBURB pUrb,
2730 POHCIED pEd, int cFmAge)
2731{
2732 RT_NOREF(cFmAge);
2733
2734 /*
2735 * Copy the data back (if IN operation) and update the TDs.
2736 */
2737 unsigned cbLeft = pUrb->cbData;
2738 uint8_t *pb = &pUrb->abData[0];
2739 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
2740 {
2741 POHCITD pTd = (POHCITD)&pUrb->paTds[iTd].TdCopy[0];
2742 const uint32_t TdAddr = pUrb->paTds[iTd].TdAddr;
2743
2744 /*
2745 * Setup a ohci transfer buffer and calc the new cbp value.
2746 */
2747 OHCIBUF Buf;
2748 ohciR3BufInit(&Buf, pTd->cbp, pTd->be);
2749 uint32_t NewCbp;
2750 if (cbLeft >= Buf.cbTotal)
2751 NewCbp = 0;
2752 else
2753 {
2754 /* (len may have changed for short transfers) */
2755 Buf.cbTotal = cbLeft;
2756 ohciR3BufUpdate(&Buf);
2757 Assert(Buf.cVecs >= 1);
2758 NewCbp = Buf.aVecs[Buf.cVecs-1].Addr + Buf.aVecs[Buf.cVecs-1].cb;
2759 }
2760
2761 /*
2762 * Write back IN buffers.
2763 */
2764 if ( pUrb->enmDir == VUSBDIRECTION_IN
2765 && ( pUrb->enmStatus == VUSBSTATUS_OK
2766 || pUrb->enmStatus == VUSBSTATUS_DATA_OVERRUN
2767 || pUrb->enmStatus == VUSBSTATUS_DATA_UNDERRUN)
2768 && Buf.cbTotal > 0)
2769 {
2770 Assert(Buf.cVecs > 0);
2771
2772 /* Be paranoid */
2773 if ( Buf.aVecs[0].cb > cbLeft
2774 || ( Buf.cVecs > 1
2775 && Buf.aVecs[1].cb > (cbLeft - Buf.aVecs[0].cb)))
2776 {
2777 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 1);
2778 return;
2779 }
2780
2781 ohciR3PhysWrite(pDevIns, Buf.aVecs[0].Addr, pb, Buf.aVecs[0].cb);
2782 if (Buf.cVecs > 1)
2783 ohciR3PhysWrite(pDevIns, Buf.aVecs[1].Addr, pb + Buf.aVecs[0].cb, Buf.aVecs[1].cb);
2784 }
2785
2786 /* advance the data buffer. */
2787 cbLeft -= Buf.cbTotal;
2788 pb += Buf.cbTotal;
2789
2790 /*
2791 * Set writeback field.
2792 */
2793 /* zero out writeback fields for retirement */
2794 pTd->hwinfo &= ~TD_HWINFO_CC;
2795 /* always update the CurrentBufferPointer; essential for underrun/overrun errors */
2796 pTd->cbp = NewCbp;
2797
2798 if (pUrb->enmStatus == VUSBSTATUS_OK)
2799 {
2800 pTd->hwinfo &= ~TD_HWINFO_ERRORS;
2801
2802 /* update done queue interrupt timer */
2803 uint32_t DoneInt = (pTd->hwinfo & TD_HWINFO_DI) >> 21;
2804 if ( DoneInt != 0x7
2805 && DoneInt < pThis->dqic)
2806 pThis->dqic = DoneInt;
2807 Log(("%s: ohciR3RhXferCompleteGeneralURB: ED=%#010x TD=%#010x Age=%d enmStatus=%d cbTotal=%#x NewCbp=%#010RX32 dqic=%d\n",
2808 pUrb->pszDesc, pUrb->pHci->EdAddr, TdAddr, cFmAge, pUrb->enmStatus, Buf.cbTotal, NewCbp, pThis->dqic));
2809 }
2810 else
2811 {
2812 Log(("%s: ohciR3RhXferCompleteGeneralURB: HALTED ED=%#010x TD=%#010x (age %d) pUrb->enmStatus=%d\n",
2813 pUrb->pszDesc, pUrb->pHci->EdAddr, TdAddr, cFmAge, pUrb->enmStatus));
2814 pEd->HeadP |= ED_HEAD_HALTED;
2815 pThis->dqic = 0; /* "If the Transfer Descriptor is being retired with an error,
2816 * then the Done Queue Interrupt Counter is cleared as if the
2817 * InterruptDelay field were zero."
2818 */
2819 switch (pUrb->enmStatus)
2820 {
2821 case VUSBSTATUS_STALL:
2822 pTd->hwinfo |= OHCI_CC_STALL;
2823 break;
2824 case VUSBSTATUS_CRC:
2825 pTd->hwinfo |= OHCI_CC_CRC;
2826 break;
2827 case VUSBSTATUS_DATA_UNDERRUN:
2828 pTd->hwinfo |= OHCI_CC_DATA_UNDERRUN;
2829 break;
2830 case VUSBSTATUS_DATA_OVERRUN:
2831 pTd->hwinfo |= OHCI_CC_DATA_OVERRUN;
2832 break;
2833 default: /* what the hell */
2834 Log(("pUrb->enmStatus=%#x!!!\n", pUrb->enmStatus));
2835 RT_FALL_THRU();
2836 case VUSBSTATUS_DNR:
2837 pTd->hwinfo |= OHCI_CC_DNR;
2838 break;
2839 }
2840 }
2841
2842 /*
2843 * Move on to the done list and write back the modified TD.
2844 */
2845# ifdef LOG_ENABLED
2846 if (!pThis->done)
2847 pThisCC->u32FmDoneQueueTail = pThis->HcFmNumber;
2848# ifdef VBOX_STRICT
2849 ohciR3InDoneQueueAdd(pThisCC, TdAddr);
2850# endif
2851# endif
2852 pTd->NextTD = pThis->done;
2853 pThis->done = TdAddr;
2854
2855 ohciR3WriteTd(pDevIns, TdAddr, pTd, "retired");
2856
2857 /*
2858 * If we've halted the endpoint, we stop here.
2859 * ohciR3UnlinkTds() will make sure we've only unliked the first TD.
2860 *
2861 * The reason for this is that while we can have more than one TD in a URB, real
2862 * OHCI hardware will only deal with one TD at the time and it's therefore incorrect
2863 * to retire TDs after the endpoint has been halted. Win2k will crash or enter infinite
2864 * kernel loop if we don't behave correctly. (See @bugref{1646}.)
2865 */
2866 if (pEd->HeadP & ED_HEAD_HALTED)
2867 break;
2868 }
2869 RT_NOREF(pThisCC);
2870}
2871
2872
2873/**
2874 * Transfer completion callback routine.
2875 *
2876 * VUSB will call this when a transfer have been completed
2877 * in a one or another way.
2878 *
2879 * @param pInterface Pointer to OHCI::ROOTHUB::IRhPort.
2880 * @param pUrb Pointer to the URB in question.
2881 */
2882static DECLCALLBACK(void) ohciR3RhXferCompletion(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb)
2883{
2884 POHCICC pThisCC = VUSBIROOTHUBPORT_2_OHCI(pInterface);
2885 PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
2886 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
2887 LogFlow(("%s: ohciR3RhXferCompletion: EdAddr=%#010RX32 cTds=%d TdAddr0=%#010RX32\n",
2888 pUrb->pszDesc, pUrb->pHci->EdAddr, pUrb->pHci->cTds, pUrb->paTds[0].TdAddr));
2889
2890 ohciR3Lock(pThisCC);
2891
2892 int cFmAge = ohciR3InFlightRemoveUrb(pThis, pThisCC, pUrb);
2893
2894 /* Do nothing requiring memory access if the HC encountered an unrecoverable error. */
2895 if (!(pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR))
2896 {
2897 pThis->fIdle = false; /* Mark as active */
2898
2899 /* get the current end point descriptor. */
2900 OHCIED Ed;
2901 ohciR3ReadEd(pDevIns, pUrb->pHci->EdAddr, &Ed);
2902
2903 /*
2904 * Check that the URB hasn't been canceled and then try unlink the TDs.
2905 *
2906 * We drop the URB if the ED is marked halted/skip ASSUMING that this
2907 * means the HCD has canceled the URB.
2908 *
2909 * If we succeed here (i.e. not dropping the URB), the TdCopy members will
2910 * be updated but not yet written. We will delay the writing till we're done
2911 * with the data copying, buffer pointer advancing and error handling.
2912 */
2913 if (pUrb->enmStatus == VUSBSTATUS_UNDO)
2914 {
2915 /* Leave the TD alone - the HCD doesn't want us talking to the device. */
2916 Log(("%s: ohciR3RhXferCompletion: CANCELED {ED=%#010x cTds=%d TD0=%#010x age %d}\n",
2917 pUrb->pszDesc, pUrb->pHci->EdAddr, pUrb->pHci->cTds, pUrb->paTds[0].TdAddr, cFmAge));
2918 STAM_COUNTER_INC(&pThis->StatDroppedUrbs);
2919 ohciR3Unlock(pThisCC);
2920 return;
2921 }
2922 bool fHasBeenCanceled = false;
2923 if ( (Ed.HeadP & ED_HEAD_HALTED)
2924 || (Ed.hwinfo & ED_HWINFO_SKIP)
2925 || cFmAge < 0
2926 || (fHasBeenCanceled = ohciR3HasUrbBeenCanceled(pDevIns, pThis, pUrb, &Ed))
2927 || !ohciR3UnlinkTds(pDevIns, pThis, pUrb, &Ed)
2928 )
2929 {
2930 Log(("%s: ohciR3RhXferCompletion: DROPPED {ED=%#010x cTds=%d TD0=%#010x age %d} because:%s%s%s%s%s!!!\n",
2931 pUrb->pszDesc, pUrb->pHci->EdAddr, pUrb->pHci->cTds, pUrb->paTds[0].TdAddr, cFmAge,
2932 (Ed.HeadP & ED_HEAD_HALTED) ? " ep halted" : "",
2933 (Ed.hwinfo & ED_HWINFO_SKIP) ? " ep skip" : "",
2934 (Ed.HeadP & ED_PTR_MASK) != pUrb->paTds[0].TdAddr ? " ep head-changed" : "",
2935 cFmAge < 0 ? " td not-in-flight" : "",
2936 fHasBeenCanceled ? " td canceled" : ""));
2937 NOREF(fHasBeenCanceled);
2938 STAM_COUNTER_INC(&pThis->StatDroppedUrbs);
2939 ohciR3Unlock(pThisCC);
2940 return;
2941 }
2942
2943 /*
2944 * Complete the TD updating and write the back.
2945 * When appropriate also copy data back to the guest memory.
2946 */
2947 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2948 ohciR3RhXferCompleteIsochronousURB(pDevIns, pThis, pThisCC, pUrb /*, &Ed , cFmAge*/);
2949 else
2950 ohciR3RhXferCompleteGeneralURB(pDevIns, pThis, pThisCC, pUrb, &Ed, cFmAge);
2951
2952 /* finally write back the endpoint descriptor. */
2953 ohciR3WriteEd(pDevIns, pUrb->pHci->EdAddr, &Ed);
2954 }
2955
2956 ohciR3Unlock(pThisCC);
2957}
2958
2959
2960/**
2961 * Handle transfer errors.
2962 *
2963 * VUSB calls this when a transfer attempt failed. This function will respond
2964 * indicating whether to retry or complete the URB with failure.
2965 *
2966 * @returns true if the URB should be retired.
2967 * @returns false if the URB should be retried.
2968 * @param pInterface Pointer to OHCI::ROOTHUB::IRhPort.
2969 * @param pUrb Pointer to the URB in question.
2970 */
2971static DECLCALLBACK(bool) ohciR3RhXferError(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb)
2972{
2973 POHCICC pThisCC = VUSBIROOTHUBPORT_2_OHCI(pInterface);
2974 PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
2975 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
2976
2977 /*
2978 * Isochronous URBs can't be retried.
2979 */
2980 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
2981 return true;
2982
2983 /*
2984 * Don't retry on stall.
2985 */
2986 if (pUrb->enmStatus == VUSBSTATUS_STALL)
2987 {
2988 Log2(("%s: ohciR3RhXferError: STALL, giving up.\n", pUrb->pszDesc));
2989 return true;
2990 }
2991
2992 ohciR3Lock(pThisCC);
2993 bool fRetire = false;
2994 /*
2995 * Check if the TDs still are valid.
2996 * This will make sure the TdCopy is up to date.
2997 */
2998 const uint32_t TdAddr = pUrb->paTds[0].TdAddr;
2999/** @todo IMPORTANT! we must check if the ED is still valid at this point!!! */
3000 if (ohciR3HasUrbBeenCanceled(pDevIns, pThis, pUrb, NULL))
3001 {
3002 Log(("%s: ohciR3RhXferError: TdAddr0=%#x canceled!\n", pUrb->pszDesc, TdAddr));
3003 fRetire = true;
3004 }
3005 else
3006 {
3007 /*
3008 * Get and update the error counter.
3009 */
3010 POHCITD pTd = (POHCITD)&pUrb->paTds[0].TdCopy[0];
3011 unsigned cErrs = (pTd->hwinfo & TD_HWINFO_ERRORS) >> TD_ERRORS_SHIFT;
3012 pTd->hwinfo &= ~TD_HWINFO_ERRORS;
3013 cErrs++;
3014 pTd->hwinfo |= (cErrs % TD_ERRORS_MAX) << TD_ERRORS_SHIFT;
3015 ohciR3WriteTd(pDevIns, TdAddr, pTd, "ohciR3RhXferError");
3016
3017 if (cErrs >= TD_ERRORS_MAX - 1)
3018 {
3019 Log2(("%s: ohciR3RhXferError: too many errors, giving up!\n", pUrb->pszDesc));
3020 fRetire = true;
3021 }
3022 else
3023 Log2(("%s: ohciR3RhXferError: cErrs=%d: retrying...\n", pUrb->pszDesc, cErrs));
3024 }
3025
3026 ohciR3Unlock(pThisCC);
3027 return fRetire;
3028}
3029
3030
3031/**
3032 * Service a general transport descriptor.
3033 */
3034static bool ohciR3ServiceTd(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, VUSBXFERTYPE enmType,
3035 PCOHCIED pEd, uint32_t EdAddr, uint32_t TdAddr, uint32_t *pNextTdAddr, const char *pszListName)
3036{
3037 RT_NOREF(pszListName);
3038
3039 /*
3040 * Read the TD and setup the buffer data.
3041 */
3042 OHCITD Td;
3043 ohciR3ReadTd(pDevIns, TdAddr, &Td);
3044 OHCIBUF Buf;
3045 ohciR3BufInit(&Buf, Td.cbp, Td.be);
3046
3047 *pNextTdAddr = Td.NextTD & ED_PTR_MASK;
3048
3049 /*
3050 * Determine the direction.
3051 */
3052 VUSBDIRECTION enmDir;
3053 switch (pEd->hwinfo & ED_HWINFO_DIR)
3054 {
3055 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3056 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3057 default:
3058 switch (Td.hwinfo & TD_HWINFO_DIR)
3059 {
3060 case TD_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3061 case TD_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3062 case 0: enmDir = VUSBDIRECTION_SETUP; break;
3063 default:
3064 Log(("ohciR3ServiceTd: Invalid direction!!!! Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Td.hwinfo, pEd->hwinfo));
3065 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 2);
3066 return false;
3067 }
3068 break;
3069 }
3070
3071 pThis->fIdle = false; /* Mark as active */
3072
3073 /*
3074 * Allocate and initialize a new URB.
3075 */
3076 PVUSBURB pUrb = VUSBIRhNewUrb(pThisCC->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, VUSB_DEVICE_PORT_INVALID,
3077 enmType, enmDir, Buf.cbTotal, 1, NULL);
3078 if (!pUrb)
3079 return false; /* retry later... */
3080
3081 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3082 pUrb->fShortNotOk = !(Td.hwinfo & TD_HWINFO_ROUNDING);
3083 pUrb->enmStatus = VUSBSTATUS_OK;
3084 pUrb->pHci->EdAddr = EdAddr;
3085 pUrb->pHci->fUnlinked = false;
3086 pUrb->pHci->cTds = 1;
3087 pUrb->paTds[0].TdAddr = TdAddr;
3088 pUrb->pHci->u32FrameNo = pThis->HcFmNumber;
3089 AssertCompile(sizeof(pUrb->paTds[0].TdCopy) >= sizeof(Td));
3090 memcpy(pUrb->paTds[0].TdCopy, &Td, sizeof(Td));
3091
3092 /* copy data if out bound transfer. */
3093 pUrb->cbData = Buf.cbTotal;
3094 if ( Buf.cbTotal
3095 && Buf.cVecs > 0
3096 && enmDir != VUSBDIRECTION_IN)
3097 {
3098 /* Be paranoid. */
3099 if ( Buf.aVecs[0].cb > pUrb->cbData
3100 || ( Buf.cVecs > 1
3101 && Buf.aVecs[1].cb > (pUrb->cbData - Buf.aVecs[0].cb)))
3102 {
3103 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 3);
3104 VUSBIRhFreeUrb(pThisCC->RootHub.pIRhConn, pUrb);
3105 return false;
3106 }
3107
3108 ohciR3PhysRead(pDevIns, Buf.aVecs[0].Addr, pUrb->abData, Buf.aVecs[0].cb);
3109 if (Buf.cVecs > 1)
3110 ohciR3PhysRead(pDevIns, Buf.aVecs[1].Addr, &pUrb->abData[Buf.aVecs[0].cb], Buf.aVecs[1].cb);
3111 }
3112
3113 /*
3114 * Submit the URB.
3115 */
3116 ohciR3InFlightAdd(pThis, pThisCC, TdAddr, pUrb);
3117 Log(("%s: ohciR3ServiceTd: submitting TdAddr=%#010x EdAddr=%#010x cbData=%#x\n",
3118 pUrb->pszDesc, TdAddr, EdAddr, pUrb->cbData));
3119
3120 ohciR3Unlock(pThisCC);
3121 int rc = VUSBIRhSubmitUrb(pThisCC->RootHub.pIRhConn, pUrb, &pThisCC->RootHub.Led);
3122 ohciR3Lock(pThisCC);
3123 if (RT_SUCCESS(rc))
3124 return true;
3125
3126 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
3127 Log(("ohciR3ServiceTd: failed submitting TdAddr=%#010x EdAddr=%#010x pUrb=%p!!\n",
3128 TdAddr, EdAddr, pUrb));
3129 ohciR3InFlightRemove(pThis, pThisCC, TdAddr);
3130 return false;
3131}
3132
3133
3134/**
3135 * Service a the head TD of an endpoint.
3136 */
3137static bool ohciR3ServiceHeadTd(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, VUSBXFERTYPE enmType,
3138 PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
3139{
3140 /*
3141 * Read the TD, after first checking if it's already in-flight.
3142 */
3143 uint32_t TdAddr = pEd->HeadP & ED_PTR_MASK;
3144 if (ohciR3IsTdInFlight(pThisCC, TdAddr))
3145 return false;
3146# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
3147 ohciR3InDoneQueueCheck(pThisCC, TdAddr);
3148# endif
3149 return ohciR3ServiceTd(pDevIns, pThis, pThisCC, enmType, pEd, EdAddr, TdAddr, &TdAddr, pszListName);
3150}
3151
3152
3153/**
3154 * Service one or more general transport descriptors (bulk or interrupt).
3155 */
3156static bool ohciR3ServiceTdMultiple(PPDMDEVINS pDevIns, POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr,
3157 uint32_t TdAddr, uint32_t *pNextTdAddr, const char *pszListName)
3158{
3159 RT_NOREF(pszListName);
3160
3161 /*
3162 * Read the TDs involved in this URB.
3163 */
3164 struct OHCITDENTRY
3165 {
3166 /** The TD. */
3167 OHCITD Td;
3168 /** The associated OHCI buffer tracker. */
3169 OHCIBUF Buf;
3170 /** The TD address. */
3171 uint32_t TdAddr;
3172 /** Pointer to the next element in the chain (stack). */
3173 struct OHCITDENTRY *pNext;
3174 } Head;
3175
3176 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
3177# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
3178 ohciR3PhysReadCacheInvalidate(&pThisCC->CacheTD);
3179# endif
3180
3181 /* read the head */
3182 ohciR3ReadTd(pDevIns, TdAddr, &Head.Td);
3183 ohciR3BufInit(&Head.Buf, Head.Td.cbp, Head.Td.be);
3184 Head.TdAddr = TdAddr;
3185 Head.pNext = NULL;
3186
3187 /* combine with more TDs. */
3188 struct OHCITDENTRY *pTail = &Head;
3189 unsigned cbTotal = pTail->Buf.cbTotal;
3190 unsigned cTds = 1;
3191 while ( (pTail->Buf.cbTotal == 0x1000 || pTail->Buf.cbTotal == 0x2000)
3192 && !(pTail->Td.hwinfo & TD_HWINFO_ROUNDING) /* This isn't right for *BSD, but let's not . */
3193 && (pTail->Td.NextTD & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3194 && cTds < 128)
3195 {
3196 struct OHCITDENTRY *pCur = (struct OHCITDENTRY *)alloca(sizeof(*pCur));
3197
3198 pCur->pNext = NULL;
3199 pCur->TdAddr = pTail->Td.NextTD & ED_PTR_MASK;
3200 ohciR3ReadTd(pDevIns, pCur->TdAddr, &pCur->Td);
3201 ohciR3BufInit(&pCur->Buf, pCur->Td.cbp, pCur->Td.be);
3202
3203 /* Don't combine if the direction doesn't match up. There can't actually be
3204 * a mismatch for bulk/interrupt EPs unless the guest is buggy.
3205 */
3206 if ( (pCur->Td.hwinfo & (TD_HWINFO_DIR))
3207 != (Head.Td.hwinfo & (TD_HWINFO_DIR)))
3208 break;
3209
3210 pTail->pNext = pCur;
3211 pTail = pCur;
3212 cbTotal += pCur->Buf.cbTotal;
3213 cTds++;
3214 }
3215
3216 /* calc next TD address */
3217 *pNextTdAddr = pTail->Td.NextTD & ED_PTR_MASK;
3218
3219 /*
3220 * Determine the direction.
3221 */
3222 VUSBDIRECTION enmDir;
3223 switch (pEd->hwinfo & ED_HWINFO_DIR)
3224 {
3225 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3226 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3227 default:
3228 Log(("ohciR3ServiceTdMultiple: WARNING! Ed.hwdinfo=%#x bulk or interrupt EP shouldn't rely on the TD for direction...\n", pEd->hwinfo));
3229 switch (Head.Td.hwinfo & TD_HWINFO_DIR)
3230 {
3231 case TD_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3232 case TD_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3233 default:
3234 Log(("ohciR3ServiceTdMultiple: Invalid direction!!!! Head.Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Head.Td.hwinfo, pEd->hwinfo));
3235 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 4);
3236 return false;
3237 }
3238 break;
3239 }
3240
3241 pThis->fIdle = false; /* Mark as active */
3242
3243 /*
3244 * Allocate and initialize a new URB.
3245 */
3246 PVUSBURB pUrb = VUSBIRhNewUrb(pThisCC->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, VUSB_DEVICE_PORT_INVALID,
3247 enmType, enmDir, cbTotal, cTds, "ohciR3ServiceTdMultiple");
3248 if (!pUrb)
3249 /* retry later... */
3250 return false;
3251 Assert(pUrb->cbData == cbTotal);
3252
3253 pUrb->enmType = enmType;
3254 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3255 pUrb->enmDir = enmDir;
3256 pUrb->fShortNotOk = !(pTail->Td.hwinfo & TD_HWINFO_ROUNDING);
3257 pUrb->enmStatus = VUSBSTATUS_OK;
3258 pUrb->pHci->cTds = cTds;
3259 pUrb->pHci->EdAddr = EdAddr;
3260 pUrb->pHci->fUnlinked = false;
3261 pUrb->pHci->u32FrameNo = pThis->HcFmNumber;
3262
3263 /* Copy data and TD information. */
3264 unsigned iTd = 0;
3265 uint8_t *pb = &pUrb->abData[0];
3266 for (struct OHCITDENTRY *pCur = &Head; pCur; pCur = pCur->pNext, iTd++)
3267 {
3268 /* data */
3269 if ( cbTotal
3270 && enmDir != VUSBDIRECTION_IN
3271 && pCur->Buf.cVecs > 0)
3272 {
3273 ohciR3PhysRead(pDevIns, pCur->Buf.aVecs[0].Addr, pb, pCur->Buf.aVecs[0].cb);
3274 if (pCur->Buf.cVecs > 1)
3275 ohciR3PhysRead(pDevIns, pCur->Buf.aVecs[1].Addr, pb + pCur->Buf.aVecs[0].cb, pCur->Buf.aVecs[1].cb);
3276 }
3277 pb += pCur->Buf.cbTotal;
3278
3279 /* TD info */
3280 pUrb->paTds[iTd].TdAddr = pCur->TdAddr;
3281 AssertCompile(sizeof(pUrb->paTds[iTd].TdCopy) >= sizeof(pCur->Td));
3282 memcpy(pUrb->paTds[iTd].TdCopy, &pCur->Td, sizeof(pCur->Td));
3283 }
3284
3285 /*
3286 * Submit the URB.
3287 */
3288 ohciR3InFlightAddUrb(pThis, pThisCC, pUrb);
3289 Log(("%s: ohciR3ServiceTdMultiple: submitting cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x\n",
3290 pUrb->pszDesc, pUrb->cbData, EdAddr, cTds, TdAddr));
3291 ohciR3Unlock(pThisCC);
3292 int rc = VUSBIRhSubmitUrb(pThisCC->RootHub.pIRhConn, pUrb, &pThisCC->RootHub.Led);
3293 ohciR3Lock(pThisCC);
3294 if (RT_SUCCESS(rc))
3295 return true;
3296
3297 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
3298 Log(("ohciR3ServiceTdMultiple: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x - rc=%Rrc\n",
3299 pUrb, cbTotal, EdAddr, cTds, TdAddr, rc));
3300 /* NB: We cannot call ohciR3InFlightRemoveUrb() because the URB is already gone! */
3301 for (struct OHCITDENTRY *pCur = &Head; pCur; pCur = pCur->pNext, iTd++)
3302 ohciR3InFlightRemove(pThis, pThisCC, pCur->TdAddr);
3303 return false;
3304}
3305
3306
3307/**
3308 * Service the head TD of an endpoint.
3309 */
3310static bool ohciR3ServiceHeadTdMultiple(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, VUSBXFERTYPE enmType,
3311 PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
3312{
3313 /*
3314 * First, check that it's not already in-flight.
3315 */
3316 uint32_t TdAddr = pEd->HeadP & ED_PTR_MASK;
3317 if (ohciR3IsTdInFlight(pThisCC, TdAddr))
3318 return false;
3319# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
3320 ohciR3InDoneQueueCheck(pThisCC, TdAddr);
3321# endif
3322 return ohciR3ServiceTdMultiple(pDevIns, pThis, enmType, pEd, EdAddr, TdAddr, &TdAddr, pszListName);
3323}
3324
3325
3326/**
3327 * A worker for ohciR3ServiceIsochronousEndpoint which unlinks a ITD
3328 * that belongs to the past.
3329 */
3330static bool ohciR3ServiceIsochronousTdUnlink(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, POHCIITD pITd, uint32_t ITdAddr,
3331 uint32_t ITdAddrPrev, PVUSBURB pUrb, POHCIED pEd, uint32_t EdAddr)
3332{
3333 LogFlow(("%s%sohciR3ServiceIsochronousTdUnlink: Unlinking ITD: ITdAddr=%#010x EdAddr=%#010x ITdAddrPrev=%#010x\n",
3334 pUrb ? pUrb->pszDesc : "", pUrb ? ": " : "", ITdAddr, EdAddr, ITdAddrPrev));
3335
3336 /*
3337 * Do the unlinking.
3338 */
3339 const uint32_t ITdAddrNext = pITd->NextTD & ED_PTR_MASK;
3340 if (ITdAddrPrev)
3341 {
3342 /* Get validate the previous TD */
3343 int iInFlightPrev = ohciR3InFlightFind(pThisCC, ITdAddrPrev);
3344 AssertMsgReturn(iInFlightPrev >= 0, ("ITdAddr=%#RX32\n", ITdAddrPrev), false);
3345 PVUSBURB pUrbPrev = pThisCC->aInFlight[iInFlightPrev].pUrb;
3346 if (ohciR3HasUrbBeenCanceled(pDevIns, pThis, pUrbPrev, pEd)) /* ensures the copy is correct. */
3347 return false;
3348
3349 /* Update the copy and write it back. */
3350 POHCIITD pITdPrev = ((POHCIITD)pUrbPrev->paTds[0].TdCopy);
3351 pITdPrev->NextTD = (pITdPrev->NextTD & ~ED_PTR_MASK) | ITdAddrNext;
3352 ohciR3WriteITd(pDevIns, pThis, ITdAddrPrev, pITdPrev, "ohciR3ServiceIsochronousEndpoint");
3353 }
3354 else
3355 {
3356 /* It's the head node. update the copy from the caller and write it back. */
3357 pEd->HeadP = (pEd->HeadP & ~ED_PTR_MASK) | ITdAddrNext;
3358 ohciR3WriteEd(pDevIns, EdAddr, pEd);
3359 }
3360
3361 /*
3362 * If it's in flight, just mark the URB as unlinked (there is only one ITD per URB atm).
3363 * Otherwise, retire it to the done queue with an error and cause a done line interrupt (?).
3364 */
3365 if (pUrb)
3366 {
3367 pUrb->pHci->fUnlinked = true;
3368 if (ohciR3HasUrbBeenCanceled(pDevIns, pThis, pUrb, pEd)) /* ensures the copy is correct (paranoia). */
3369 return false;
3370
3371 POHCIITD pITdCopy = ((POHCIITD)pUrb->paTds[0].TdCopy);
3372 pITd->NextTD = pITdCopy->NextTD &= ~ED_PTR_MASK;
3373 }
3374 else
3375 {
3376 pITd->HwInfo &= ~ITD_HWINFO_CC;
3377 pITd->HwInfo |= OHCI_CC_DATA_OVERRUN;
3378
3379 pITd->NextTD = pThis->done;
3380 pThis->done = ITdAddr;
3381
3382 pThis->dqic = 0;
3383 }
3384
3385 ohciR3WriteITd(pDevIns, pThis, ITdAddr, pITd, "ohciR3ServiceIsochronousTdUnlink");
3386 return true;
3387}
3388
3389
3390/**
3391 * A worker for ohciR3ServiceIsochronousEndpoint which submits the specified TD.
3392 *
3393 * @returns true on success.
3394 * @returns false on failure to submit.
3395 * @param pDevIns The device instance.
3396 * @param pThis The OHCI controller instance data, shared edition.
3397 * @param pThisCC The OHCI controller instance data, ring-3 edition.
3398 * @param pITd The transfer descriptor to service.
3399 * @param ITdAddr The address of the transfer descriptor in gues memory.
3400 * @param R The start packet (frame) relative to the start of frame in HwInfo.
3401 * @param pEd The OHCI endpoint descriptor.
3402 * @param EdAddr The endpoint descriptor address in guest memory.
3403 */
3404static bool ohciR3ServiceIsochronousTd(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC,
3405 POHCIITD pITd, uint32_t ITdAddr, const unsigned R, PCOHCIED pEd, uint32_t EdAddr)
3406{
3407 /*
3408 * Determine the endpoint direction.
3409 */
3410 VUSBDIRECTION enmDir;
3411 switch (pEd->hwinfo & ED_HWINFO_DIR)
3412 {
3413 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3414 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3415 default:
3416 Log(("ohciR3ServiceIsochronousTd: Invalid direction!!!! Ed.hwdinfo=%#x\n", pEd->hwinfo));
3417 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 5);
3418 return false;
3419 }
3420
3421 /*
3422 * Extract the packet sizes and calc the total URB size.
3423 */
3424 struct
3425 {
3426 uint16_t cb;
3427 uint16_t off;
3428 } aPkts[ITD_NUM_PSW];
3429
3430 /* first entry (R) */
3431 uint32_t cbTotal = 0;
3432 if (((uint32_t)pITd->aPSW[R] >> ITD_PSW_CC_SHIFT) < (OHCI_CC_NOT_ACCESSED_0 >> TD_HWINFO_CC_SHIFT))
3433 {
3434 Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, R, pITd->aPSW[R] >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
3435 pThis->intr_status |= OHCI_INTR_UNRECOVERABLE_ERROR;
3436 return false;
3437 }
3438 uint16_t offPrev = aPkts[0].off = (pITd->aPSW[R] & ITD_PSW_OFFSET);
3439
3440 /* R+1..cFrames */
3441 const unsigned cFrames = ((pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
3442 for (unsigned iR = R + 1; iR < cFrames; iR++)
3443 {
3444 const uint16_t PSW = pITd->aPSW[iR];
3445 const uint16_t off = aPkts[iR - R].off = (PSW & ITD_PSW_OFFSET);
3446 cbTotal += aPkts[iR - R - 1].cb = off - offPrev;
3447 if (off < offPrev)
3448 {
3449 Log(("ITdAddr=%RX32 PSW%d.offset=%#x < offPrev=%#x!\n", ITdAddr, iR, off, offPrev)); /* => Unrecoverable Error*/
3450 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 6);
3451 return false;
3452 }
3453 if (((uint32_t)PSW >> ITD_PSW_CC_SHIFT) < (OHCI_CC_NOT_ACCESSED_0 >> TD_HWINFO_CC_SHIFT))
3454 {
3455 Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, iR, PSW >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
3456 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 7);
3457 return false;
3458 }
3459 offPrev = off;
3460 }
3461
3462 /* calc offEnd and figure out the size of the last packet. */
3463 const uint32_t offEnd = (pITd->BE & 0xfff)
3464 + (((pITd->BE & ITD_BP0_MASK) != (pITd->BP0 & ITD_BP0_MASK)) << 12)
3465 + 1 /* BE is inclusive */;
3466 if (offEnd < offPrev)
3467 {
3468 Log(("ITdAddr=%RX32 offEnd=%#x < offPrev=%#x!\n", ITdAddr, offEnd, offPrev)); /* => Unrecoverable Error*/
3469 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 8);
3470 return false;
3471 }
3472 cbTotal += aPkts[cFrames - 1 - R].cb = offEnd - offPrev;
3473 Assert(cbTotal <= 0x2000);
3474
3475 pThis->fIdle = false; /* Mark as active */
3476
3477 /*
3478 * Allocate and initialize a new URB.
3479 */
3480 PVUSBURB pUrb = VUSBIRhNewUrb(pThisCC->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, VUSB_DEVICE_PORT_INVALID,
3481 VUSBXFERTYPE_ISOC, enmDir, cbTotal, 1, NULL);
3482 if (!pUrb)
3483 /* retry later... */
3484 return false;
3485
3486 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3487 pUrb->fShortNotOk = false;
3488 pUrb->enmStatus = VUSBSTATUS_OK;
3489 pUrb->pHci->EdAddr = EdAddr;
3490 pUrb->pHci->cTds = 1;
3491 pUrb->pHci->fUnlinked = false;
3492 pUrb->pHci->u32FrameNo = pThis->HcFmNumber;
3493 pUrb->paTds[0].TdAddr = ITdAddr;
3494 AssertCompile(sizeof(pUrb->paTds[0].TdCopy) >= sizeof(*pITd));
3495 memcpy(pUrb->paTds[0].TdCopy, pITd, sizeof(*pITd));
3496# if 0 /* color the data */
3497 memset(pUrb->abData, 0xfe, cbTotal);
3498# endif
3499
3500 /* copy the data */
3501 if ( cbTotal
3502 && enmDir != VUSBDIRECTION_IN)
3503 {
3504 const uint32_t off0 = pITd->aPSW[R] & ITD_PSW_OFFSET;
3505 if (off0 < 0x1000)
3506 {
3507 if (offEnd > 0x1000)
3508 {
3509 /* both pages. */
3510 const unsigned cb0 = 0x1000 - off0;
3511 ohciR3PhysRead(pDevIns, (pITd->BP0 & ITD_BP0_MASK) + off0, &pUrb->abData[0], cb0);
3512 ohciR3PhysRead(pDevIns, pITd->BE & ITD_BP0_MASK, &pUrb->abData[cb0], offEnd & 0xfff);
3513 }
3514 else /* a portion of the 1st page. */
3515 ohciR3PhysRead(pDevIns, (pITd->BP0 & ITD_BP0_MASK) + off0, pUrb->abData, offEnd - off0);
3516 }
3517 else /* a portion of the 2nd page. */
3518 ohciR3PhysRead(pDevIns, (pITd->BE & UINT32_C(0xfffff000)) + (off0 & 0xfff), pUrb->abData, cbTotal);
3519 }
3520
3521 /* setup the packets */
3522 pUrb->cIsocPkts = cFrames - R;
3523 unsigned off = 0;
3524 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
3525 {
3526 pUrb->aIsocPkts[i].enmStatus = VUSBSTATUS_NOT_ACCESSED;
3527 pUrb->aIsocPkts[i].off = off;
3528 off += pUrb->aIsocPkts[i].cb = aPkts[i].cb;
3529 }
3530 Assert(off == cbTotal);
3531
3532 /*
3533 * Submit the URB.
3534 */
3535 ohciR3InFlightAdd(pThis, pThisCC, ITdAddr, pUrb);
3536 Log(("%s: ohciR3ServiceIsochronousTd: submitting cbData=%#x cIsocPkts=%d EdAddr=%#010x TdAddr=%#010x SF=%#x (%#x)\n",
3537 pUrb->pszDesc, pUrb->cbData, pUrb->cIsocPkts, EdAddr, ITdAddr, pITd->HwInfo & ITD_HWINFO_SF, pThis->HcFmNumber));
3538 ohciR3Unlock(pThisCC);
3539 int rc = VUSBIRhSubmitUrb(pThisCC->RootHub.pIRhConn, pUrb, &pThisCC->RootHub.Led);
3540 ohciR3Lock(pThisCC);
3541 if (RT_SUCCESS(rc))
3542 return true;
3543
3544 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
3545 Log(("ohciR3ServiceIsochronousTd: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d ITdAddr0=%#010x - rc=%Rrc\n",
3546 pUrb, cbTotal, EdAddr, 1, ITdAddr, rc));
3547 ohciR3InFlightRemove(pThis, pThisCC, ITdAddr);
3548 return false;
3549}
3550
3551
3552/**
3553 * Service an isochronous endpoint.
3554 */
3555static void ohciR3ServiceIsochronousEndpoint(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, POHCIED pEd, uint32_t EdAddr)
3556{
3557 /*
3558 * We currently process this as if the guest follows the interrupt end point chaining
3559 * hierarchy described in the documenation. This means that for an isochronous endpoint
3560 * with a 1 ms interval we expect to find in-flight TDs at the head of the list. We will
3561 * skip over all in-flight TDs which timeframe has been exceed. Those which aren't in
3562 * flight but which are too late will be retired (possibly out of order, but, we don't
3563 * care right now).
3564 *
3565 * When we reach a TD which still has a buffer which is due for take off, we will
3566 * stop iterating TDs. If it's in-flight, there isn't anything to be done. Otherwise
3567 * we will push it onto the runway for immediate take off. In this process we
3568 * might have to complete buffers which didn't make it on time, something which
3569 * complicates the kind of status info we need to keep around for the TD.
3570 *
3571 * Note: We're currently not making any attempt at reassembling ITDs into URBs.
3572 * However, this will become necessary because of EMT scheduling and guest
3573 * like linux using one TD for each frame (simple but inefficient for us).
3574 */
3575 OHCIITD ITd;
3576 uint32_t ITdAddr = pEd->HeadP & ED_PTR_MASK;
3577 uint32_t ITdAddrPrev = 0;
3578 uint32_t u32NextFrame = UINT32_MAX;
3579 const uint16_t u16CurFrame = pThis->HcFmNumber;
3580 for (;;)
3581 {
3582 /* check for end-of-chain. */
3583 if ( ITdAddr == (pEd->TailP & ED_PTR_MASK)
3584 || !ITdAddr)
3585 break;
3586
3587 /*
3588 * If isochronous endpoints are around, don't slow down the timer. Getting the timing right
3589 * is difficult enough as it is.
3590 */
3591 pThis->fIdle = false;
3592
3593 /*
3594 * Read the current ITD and check what we're supposed to do about it.
3595 */
3596 ohciR3ReadITd(pDevIns, pThis, ITdAddr, &ITd);
3597 const uint32_t ITdAddrNext = ITd.NextTD & ED_PTR_MASK;
3598 const int16_t R = u16CurFrame - (uint16_t)(ITd.HwInfo & ITD_HWINFO_SF); /* 4.3.2.3 */
3599 const int16_t cFrames = ((ITd.HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
3600
3601 if (R < cFrames)
3602 {
3603 /*
3604 * It's inside the current or a future launch window.
3605 *
3606 * We will try maximize the TD in flight here to deal with EMT scheduling
3607 * issues and similar stuff which will screw up the time. So, we will only
3608 * stop submitting TD when we reach a gap (in time) or end of the list.
3609 */
3610 if ( R < 0 /* (a future frame) */
3611 && (uint16_t)u32NextFrame != (uint16_t)(ITd.HwInfo & ITD_HWINFO_SF))
3612 break;
3613 if (ohciR3InFlightFind(pThisCC, ITdAddr) < 0)
3614 if (!ohciR3ServiceIsochronousTd(pDevIns, pThis, pThisCC, &ITd, ITdAddr, R < 0 ? 0 : R, pEd, EdAddr))
3615 break;
3616
3617 ITdAddrPrev = ITdAddr;
3618 }
3619 else
3620 {
3621# if 1
3622 /*
3623 * Ok, the launch window for this TD has passed.
3624 * If it's not in flight it should be retired with a DataOverrun status (TD).
3625 *
3626 * Don't remove in-flight TDs before they complete.
3627 * Windows will, upon the completion of another ITD it seems, check for if
3628 * any other TDs has been unlinked. If we unlink them before they really
3629 * complete all the packet status codes will be NotAccessed and Windows
3630 * will fail the URB with status USBD_STATUS_ISOCH_REQUEST_FAILED.
3631 *
3632 * I don't know if unlinking TDs out of order could cause similar problems,
3633 * time will show.
3634 */
3635 int iInFlight = ohciR3InFlightFind(pThisCC, ITdAddr);
3636 if (iInFlight >= 0)
3637 ITdAddrPrev = ITdAddr;
3638 else if (!ohciR3ServiceIsochronousTdUnlink(pDevIns, pThis, pThisCC, &ITd, ITdAddr, ITdAddrPrev, NULL, pEd, EdAddr))
3639 {
3640 Log(("ohciR3ServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
3641 break;
3642 }
3643# else /* BAD IDEA: */
3644 /*
3645 * Ok, the launch window for this TD has passed.
3646 * If it's not in flight it should be retired with a DataOverrun status (TD).
3647 *
3648 * If it's in flight we will try unlink it from the list prematurely to
3649 * help the guest to move on and shorten the list we have to walk. We currently
3650 * are successful with the first URB but then it goes too slowly...
3651 */
3652 int iInFlight = ohciR3InFlightFind(pThis, ITdAddr);
3653 if (!ohciR3ServiceIsochronousTdUnlink(pThis, &ITd, ITdAddr, ITdAddrPrev,
3654 iInFlight < 0 ? NULL : pThis->aInFlight[iInFlight].pUrb,
3655 pEd, EdAddr))
3656 {
3657 Log(("ohciR3ServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
3658 break;
3659 }
3660# endif
3661 }
3662
3663 /* advance to the next ITD */
3664 ITdAddr = ITdAddrNext;
3665 u32NextFrame = (ITd.HwInfo & ITD_HWINFO_SF) + cFrames;
3666 }
3667}
3668
3669
3670/**
3671 * Checks if a endpoints has TDs queued and is ready to have them processed.
3672 *
3673 * @returns true if it's ok to process TDs.
3674 * @param pEd The endpoint data.
3675 */
3676DECLINLINE(bool) ohciR3IsEdReady(PCOHCIED pEd)
3677{
3678 return (pEd->HeadP & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3679 && !(pEd->HeadP & ED_HEAD_HALTED)
3680 && !(pEd->hwinfo & ED_HWINFO_SKIP);
3681}
3682
3683
3684/**
3685 * Checks if an endpoint has TDs queued (not necessarily ready to have them processed).
3686 *
3687 * @returns true if endpoint may have TDs queued.
3688 * @param pEd The endpoint data.
3689 */
3690DECLINLINE(bool) ohciR3IsEdPresent(PCOHCIED pEd)
3691{
3692 return (pEd->HeadP & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3693 && !(pEd->HeadP & ED_HEAD_HALTED);
3694}
3695
3696
3697/**
3698 * Services the bulk list.
3699 *
3700 * On the bulk list we must reassemble URBs from multiple TDs using heuristics
3701 * derived from USB tracing done in the guests and guest source code (when available).
3702 */
3703static void ohciR3ServiceBulkList(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
3704{
3705# ifdef LOG_ENABLED
3706 if (g_fLogBulkEPs)
3707 ohciR3DumpEdList(pDevIns, pThisCC, pThis->bulk_head, "Bulk before", true);
3708 if (pThis->bulk_cur)
3709 Log(("ohciR3ServiceBulkList: bulk_cur=%#010x before listprocessing!!! HCD have positioned us!!!\n", pThis->bulk_cur));
3710# endif
3711
3712 /*
3713 * ", HC will start processing the Bulk list and will set BF [BulkListFilled] to 0"
3714 * - We've simplified and are always starting at the head of the list and working
3715 * our way thru to the end each time.
3716 */
3717 pThis->status &= ~OHCI_STATUS_BLF;
3718 pThis->fBulkNeedsCleaning = false;
3719 pThis->bulk_cur = 0;
3720
3721 uint32_t EdAddr = pThis->bulk_head;
3722 uint32_t cIterations = 256;
3723 while (EdAddr
3724 && (pThis->ctl & OHCI_CTL_BLE)
3725 && (cIterations-- > 0))
3726 {
3727 OHCIED Ed;
3728
3729 /* Bail if previous processing ended up in the unrecoverable error state. */
3730 if (pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR)
3731 break;
3732
3733 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
3734 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3735 if (ohciR3IsEdReady(&Ed))
3736 {
3737 pThis->status |= OHCI_STATUS_BLF;
3738 pThis->fBulkNeedsCleaning = true;
3739
3740# if 1
3741 /*
3742
3743 * After we figured out that all the TDs submitted for dealing with MSD
3744 * read/write data really makes up on single URB, and that we must
3745 * reassemble these TDs into an URB before submitting it, there is no
3746 * longer any need for servicing anything other than the head *URB*
3747 * on a bulk endpoint.
3748 */
3749 ohciR3ServiceHeadTdMultiple(pDevIns, pThis, pThisCC, VUSBXFERTYPE_BULK, &Ed, EdAddr, "Bulk");
3750# else
3751 /*
3752 * This alternative code was used before we started reassembling URBs from
3753 * multiple TDs. We keep it handy for debugging.
3754 */
3755 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3756 if (!ohciR3IsTdInFlight(pThis, TdAddr))
3757 {
3758 do
3759 {
3760 if (!ohciR3ServiceTdMultiple(pThis, VUSBXFERTYPE_BULK, &Ed, EdAddr, TdAddr, &TdAddr, "Bulk"))
3761 {
3762 LogFlow(("ohciR3ServiceBulkList: ohciR3ServiceTdMultiple -> false\n"));
3763 break;
3764 }
3765 if ( (TdAddr & ED_PTR_MASK) == (Ed.TailP & ED_PTR_MASK)
3766 || !TdAddr /* paranoia */)
3767 {
3768 LogFlow(("ohciR3ServiceBulkList: TdAddr=%#010RX32 Ed.TailP=%#010RX32\n", TdAddr, Ed.TailP));
3769 break;
3770 }
3771
3772 ohciR3ReadEd(pDevIns, EdAddr, &Ed); /* It might have been updated on URB completion. */
3773 } while (ohciR3IsEdReady(&Ed));
3774 }
3775# endif
3776 }
3777 else
3778 {
3779 if (Ed.hwinfo & ED_HWINFO_SKIP)
3780 {
3781 LogFlow(("ohciR3ServiceBulkList: Ed=%#010RX32 Ed.TailP=%#010RX32 SKIP\n", EdAddr, Ed.TailP));
3782 /* If the ED is in 'skip' state, no transactions on it are allowed and we must
3783 * cancel outstanding URBs, if any.
3784 */
3785 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3786 PVUSBURB pUrb = ohciR3TdInFlightUrb(pThisCC, TdAddr);
3787 if (pUrb)
3788 pThisCC->RootHub.pIRhConn->pfnCancelUrbsEp(pThisCC->RootHub.pIRhConn, pUrb);
3789 }
3790 }
3791
3792 /* Trivial loop detection. */
3793 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
3794 break;
3795 /* Proceed to the next endpoint. */
3796 EdAddr = Ed.NextED & ED_PTR_MASK;
3797 }
3798
3799# ifdef LOG_ENABLED
3800 if (g_fLogBulkEPs)
3801 ohciR3DumpEdList(pDevIns, pThisCC, pThis->bulk_head, "Bulk after ", true);
3802# endif
3803}
3804
3805
3806/**
3807 * Abort outstanding transfers on the bulk list.
3808 *
3809 * If the guest disabled bulk list processing, we must abort any outstanding transfers
3810 * (that is, cancel in-flight URBs associated with the list). This is required because
3811 * there may be outstanding read URBs that will never get a response from the device
3812 * and would block further communication.
3813 */
3814static void ohciR3UndoBulkList(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
3815{
3816# ifdef LOG_ENABLED
3817 if (g_fLogBulkEPs)
3818 ohciR3DumpEdList(pDevIns, pThisCC, pThis->bulk_head, "Bulk before", true);
3819 if (pThis->bulk_cur)
3820 Log(("ohciR3UndoBulkList: bulk_cur=%#010x before list processing!!! HCD has positioned us!!!\n", pThis->bulk_cur));
3821# endif
3822
3823 /* This flag follows OHCI_STATUS_BLF, but BLF doesn't change when list processing is disabled. */
3824 pThis->fBulkNeedsCleaning = false;
3825
3826 uint32_t EdAddr = pThis->bulk_head;
3827 uint32_t cIterations = 256;
3828 while (EdAddr
3829 && (cIterations-- > 0))
3830 {
3831 OHCIED Ed;
3832
3833 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
3834 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3835 if (ohciR3IsEdPresent(&Ed))
3836 {
3837 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3838 if (ohciR3IsTdInFlight(pThisCC, TdAddr))
3839 {
3840 LogFlow(("ohciR3UndoBulkList: Ed=%#010RX32 Ed.TailP=%#010RX32 UNDO\n", EdAddr, Ed.TailP));
3841 PVUSBURB pUrb = ohciR3TdInFlightUrb(pThisCC, TdAddr);
3842 if (pUrb)
3843 pThisCC->RootHub.pIRhConn->pfnCancelUrbsEp(pThisCC->RootHub.pIRhConn, pUrb);
3844 }
3845 }
3846
3847 /* Trivial loop detection. */
3848 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
3849 break;
3850 /* Proceed to the next endpoint. */
3851 EdAddr = Ed.NextED & ED_PTR_MASK;
3852 }
3853}
3854
3855
3856/**
3857 * Services the control list.
3858 *
3859 * The control list has complex URB assembling, but that's taken
3860 * care of at VUSB level (unlike the other transfer types).
3861 */
3862static void ohciR3ServiceCtrlList(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
3863{
3864# ifdef LOG_ENABLED
3865 if (g_fLogControlEPs)
3866 ohciR3DumpEdList(pDevIns, pThisCC, pThis->ctrl_head, "Ctrl before", true);
3867 if (pThis->ctrl_cur)
3868 Log(("ohciR3ServiceCtrlList: ctrl_cur=%010x before list processing!!! HCD have positioned us!!!\n", pThis->ctrl_cur));
3869# endif
3870
3871 /*
3872 * ", HC will start processing the list and will set ControlListFilled to 0"
3873 * - We've simplified and are always starting at the head of the list and working
3874 * our way thru to the end each time.
3875 */
3876 pThis->status &= ~OHCI_STATUS_CLF;
3877 pThis->ctrl_cur = 0;
3878
3879 uint32_t EdAddr = pThis->ctrl_head;
3880 uint32_t cIterations = 256;
3881 while ( EdAddr
3882 && (pThis->ctl & OHCI_CTL_CLE)
3883 && (cIterations-- > 0))
3884 {
3885 OHCIED Ed;
3886
3887 /* Bail if previous processing ended up in the unrecoverable error state. */
3888 if (pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR)
3889 break;
3890
3891 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
3892 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3893 if (ohciR3IsEdReady(&Ed))
3894 {
3895# if 1
3896 /*
3897 * Control TDs depends on order and stage. Only one can be in-flight
3898 * at any given time. OTOH, some stages are completed immediately,
3899 * so we process the list until we've got a head which is in-flight
3900 * or reach the end of the list.
3901 */
3902 do
3903 {
3904 if ( !ohciR3ServiceHeadTd(pDevIns, pThis, pThisCC, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control")
3905 || ohciR3IsTdInFlight(pThisCC, Ed.HeadP & ED_PTR_MASK))
3906 {
3907 pThis->status |= OHCI_STATUS_CLF;
3908 break;
3909 }
3910 ohciR3ReadEd(pDevIns, EdAddr, &Ed); /* It might have been updated on URB completion. */
3911 } while (ohciR3IsEdReady(&Ed));
3912# else
3913 /* Simplistic, for debugging. */
3914 ohciR3ServiceHeadTd(pThis, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control");
3915 pThis->status |= OHCI_STATUS_CLF;
3916# endif
3917 }
3918
3919 /* Trivial loop detection. */
3920 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
3921 break;
3922 /* Proceed to the next endpoint. */
3923 EdAddr = Ed.NextED & ED_PTR_MASK;
3924 }
3925
3926# ifdef LOG_ENABLED
3927 if (g_fLogControlEPs)
3928 ohciR3DumpEdList(pDevIns, pThisCC, pThis->ctrl_head, "Ctrl after ", true);
3929# endif
3930}
3931
3932
3933/**
3934 * Services the periodic list.
3935 *
3936 * On the interrupt portion of the periodic list we must reassemble URBs from multiple
3937 * TDs using heuristics derived from USB tracing done in the guests and guest source
3938 * code (when available).
3939 */
3940static void ohciR3ServicePeriodicList(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
3941{
3942 /*
3943 * Read the list head from the HCCA.
3944 */
3945 const unsigned iList = pThis->HcFmNumber % OHCI_HCCA_NUM_INTR;
3946 uint32_t EdAddr;
3947 ohciR3GetDWords(pDevIns, pThis->hcca + iList * sizeof(EdAddr), &EdAddr, 1);
3948
3949# ifdef LOG_ENABLED
3950 const uint32_t EdAddrHead = EdAddr;
3951 if (g_fLogInterruptEPs)
3952 {
3953 char sz[48];
3954 RTStrPrintf(sz, sizeof(sz), "Int%02x before", iList);
3955 ohciR3DumpEdList(pDevIns, pThisCC, EdAddrHead, sz, true);
3956 }
3957# endif
3958
3959 /*
3960 * Iterate the endpoint list.
3961 */
3962 unsigned cIterations = 128;
3963 while (EdAddr
3964 && (pThis->ctl & OHCI_CTL_PLE)
3965 && (cIterations-- > 0))
3966 {
3967 OHCIED Ed;
3968
3969 /* Bail if previous processing ended up in the unrecoverable error state. */
3970 if (pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR)
3971 break;
3972
3973 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
3974 if (ohciR3IsEdReady(&Ed))
3975 {
3976 /*
3977 * "There is no separate head pointer of isochronous transfers. The first
3978 * isochronous Endpoint Descriptor simply links to the last interrupt
3979 * Endpoint Descriptor."
3980 */
3981 if (!(Ed.hwinfo & ED_HWINFO_ISO))
3982 {
3983 /*
3984 * Presently we will only process the head URB on an interrupt endpoint.
3985 */
3986 ohciR3ServiceHeadTdMultiple(pDevIns, pThis, pThisCC, VUSBXFERTYPE_INTR, &Ed, EdAddr, "Periodic");
3987 }
3988 else if (pThis->ctl & OHCI_CTL_IE)
3989 {
3990 /*
3991 * Presently only the head ITD.
3992 */
3993 ohciR3ServiceIsochronousEndpoint(pDevIns, pThis, pThisCC, &Ed, EdAddr);
3994 }
3995 else
3996 break;
3997 }
3998 else
3999 {
4000 if (Ed.hwinfo & ED_HWINFO_SKIP)
4001 {
4002 Log3(("ohciR3ServicePeriodicList: Ed=%#010RX32 Ed.TailP=%#010RX32 SKIP\n", EdAddr, Ed.TailP));
4003 /* If the ED is in 'skip' state, no transactions on it are allowed and we must
4004 * cancel outstanding URBs, if any.
4005 */
4006 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
4007 PVUSBURB pUrb = ohciR3TdInFlightUrb(pThisCC, TdAddr);
4008 if (pUrb)
4009 pThisCC->RootHub.pIRhConn->pfnCancelUrbsEp(pThisCC->RootHub.pIRhConn, pUrb);
4010 }
4011 }
4012 /* Trivial loop detection. */
4013 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
4014 break;
4015 /* Proceed to the next endpoint. */
4016 EdAddr = Ed.NextED & ED_PTR_MASK;
4017 }
4018
4019# ifdef LOG_ENABLED
4020 if (g_fLogInterruptEPs)
4021 {
4022 char sz[48];
4023 RTStrPrintf(sz, sizeof(sz), "Int%02x after ", iList);
4024 ohciR3DumpEdList(pDevIns, pThisCC, EdAddrHead, sz, true);
4025 }
4026# endif
4027}
4028
4029
4030/**
4031 * Update the HCCA.
4032 *
4033 * @param pDevIns The device instance.
4034 * @param pThis The OHCI controller instance data, shared edition.
4035 * @param pThisCC The OHCI controller instance data, ring-3 edition.
4036 */
4037static void ohciR3UpdateHCCA(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4038{
4039 OCHIHCCA hcca;
4040 ohciR3PhysRead(pDevIns, pThis->hcca + OHCI_HCCA_OFS, &hcca, sizeof(hcca));
4041
4042 hcca.frame = RT_H2LE_U16((uint16_t)pThis->HcFmNumber);
4043 hcca.pad = 0;
4044
4045 bool fWriteDoneHeadInterrupt = false;
4046 if ( pThis->dqic == 0
4047 && (pThis->intr_status & OHCI_INTR_WRITE_DONE_HEAD) == 0)
4048 {
4049 uint32_t done = pThis->done;
4050
4051 if (pThis->intr_status & ~( OHCI_INTR_MASTER_INTERRUPT_ENABLED | OHCI_INTR_OWNERSHIP_CHANGE
4052 | OHCI_INTR_WRITE_DONE_HEAD) )
4053 done |= 0x1;
4054
4055 hcca.done = RT_H2LE_U32(done);
4056 pThis->done = 0;
4057 pThis->dqic = 0x7;
4058
4059 Log(("ohci: Writeback Done (%#010x) on frame %#x (age %#x)\n", hcca.done,
4060 pThis->HcFmNumber, pThis->HcFmNumber - pThisCC->u32FmDoneQueueTail));
4061# ifdef LOG_ENABLED
4062 ohciR3DumpTdQueue(pDevIns, pThisCC, hcca.done & ED_PTR_MASK, "DoneQueue");
4063# endif
4064 Assert(RT_OFFSETOF(OCHIHCCA, done) == 4);
4065# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
4066 ohciR3InDoneQueueZap(pThisCC);
4067# endif
4068 fWriteDoneHeadInterrupt = true;
4069 }
4070
4071 Log3(("ohci: Updating HCCA on frame %#x\n", pThis->HcFmNumber));
4072 ohciR3PhysWriteMeta(pDevIns, pThis->hcca + OHCI_HCCA_OFS, (uint8_t *)&hcca, sizeof(hcca));
4073 if (fWriteDoneHeadInterrupt)
4074 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_WRITE_DONE_HEAD);
4075 RT_NOREF(pThisCC);
4076}
4077
4078
4079/**
4080 * Go over the in-flight URB list and cancel any URBs that are no longer in use.
4081 * This occurs when the host removes EDs or TDs from the lists and we don't notice
4082 * the sKip bit. Such URBs must be promptly canceled, otherwise there is a risk
4083 * they might "steal" data destined for another URB.
4084 */
4085static void ohciR3CancelOrphanedURBs(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4086{
4087 bool fValidHCCA = !( pThis->hcca >= OHCI_HCCA_MASK
4088 || pThis->hcca < ~OHCI_HCCA_MASK);
4089 unsigned i, cLeft;
4090 int j;
4091 uint32_t EdAddr;
4092 PVUSBURB pUrb;
4093
4094 /* If the HCCA is not currently valid, or there are no in-flight URBs,
4095 * there's nothing to do.
4096 */
4097 if (!fValidHCCA || !pThisCC->cInFlight)
4098 return;
4099
4100 /* Initially mark all in-flight URBs as inactive. */
4101 for (i = 0, cLeft = pThisCC->cInFlight; cLeft && i < RT_ELEMENTS(pThisCC->aInFlight); i++)
4102 {
4103 if (pThisCC->aInFlight[i].pUrb)
4104 {
4105 pThisCC->aInFlight[i].fInactive = true;
4106 cLeft--;
4107 }
4108 }
4109 Assert(cLeft == 0);
4110
4111# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
4112 /* Get hcca data to minimize calls to ohciR3GetDWords/PDMDevHlpPCIPhysRead. */
4113 uint32_t au32HCCA[OHCI_HCCA_NUM_INTR];
4114 ohciR3GetDWords(pDevIns, pThis->hcca, au32HCCA, OHCI_HCCA_NUM_INTR);
4115# endif
4116
4117 /* Go over all bulk/control/interrupt endpoint lists; any URB found in these lists
4118 * is marked as active again.
4119 */
4120 for (i = 0; i < OHCI_HCCA_NUM_INTR + 2; i++)
4121 {
4122 switch (i)
4123 {
4124 case OHCI_HCCA_NUM_INTR:
4125 EdAddr = pThis->bulk_head;
4126 break;
4127 case OHCI_HCCA_NUM_INTR + 1:
4128 EdAddr = pThis->ctrl_head;
4129 break;
4130 default:
4131# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
4132 EdAddr = au32HCCA[i];
4133# else
4134 ohciR3GetDWords(pDevIns, pThis->hcca + i * sizeof(EdAddr), &EdAddr, 1);
4135# endif
4136 break;
4137 }
4138
4139 unsigned cIterED = 128;
4140 while ( EdAddr
4141 && (cIterED-- > 0))
4142 {
4143 OHCIED Ed;
4144 OHCITD Td;
4145
4146 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
4147 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
4148 uint32_t TailP = Ed.TailP & ED_PTR_MASK;
4149 unsigned cIterTD = 0;
4150 if ( !(Ed.hwinfo & ED_HWINFO_SKIP)
4151 && (TdAddr != TailP))
4152 {
4153# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
4154 ohciR3PhysReadCacheInvalidate(&pThisCC->CacheTD);
4155# endif
4156 do
4157 {
4158 ohciR3ReadTd(pDevIns, TdAddr, &Td);
4159 j = ohciR3InFlightFind(pThisCC, TdAddr);
4160 if (j > -1)
4161 pThisCC->aInFlight[j].fInactive = false;
4162 TdAddr = Td.NextTD & ED_PTR_MASK;
4163 /* See #8125.
4164 * Sometimes the ED is changed by the guest between ohciR3ReadEd above and here.
4165 * Then the code reads TD pointed by the new TailP, which is not allowed.
4166 * Luckily Windows guests have Td.NextTD = 0 in the tail TD.
4167 * Also having a real TD at 0 is very unlikely.
4168 * So do not continue.
4169 */
4170 if (TdAddr == 0)
4171 break;
4172 /* Failsafe for temporarily looped lists. */
4173 if (++cIterTD == 128)
4174 break;
4175 } while (TdAddr != (Ed.TailP & ED_PTR_MASK));
4176 }
4177 /* Trivial loop detection. */
4178 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
4179 break;
4180 /* Proceed to the next endpoint. */
4181 EdAddr = Ed.NextED & ED_PTR_MASK;
4182 }
4183 }
4184
4185 /* In-flight URBs still marked as inactive are not used anymore and need
4186 * to be canceled.
4187 */
4188 for (i = 0, cLeft = pThisCC->cInFlight; cLeft && i < RT_ELEMENTS(pThisCC->aInFlight); i++)
4189 {
4190 if (pThisCC->aInFlight[i].pUrb)
4191 {
4192 cLeft--;
4193 pUrb = pThisCC->aInFlight[i].pUrb;
4194 if ( pThisCC->aInFlight[i].fInactive
4195 && pUrb->enmState == VUSBURBSTATE_IN_FLIGHT
4196 && pUrb->enmType != VUSBXFERTYPE_CTRL)
4197 pThisCC->RootHub.pIRhConn->pfnCancelUrbsEp(pThisCC->RootHub.pIRhConn, pUrb);
4198 }
4199 }
4200 Assert(cLeft == 0);
4201}
4202
4203
4204/**
4205 * Generate a Start-Of-Frame event, and set a timer for End-Of-Frame.
4206 */
4207static void ohciR3StartOfFrame(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4208{
4209# ifdef LOG_ENABLED
4210 const uint32_t status_old = pThis->status;
4211# endif
4212
4213 /*
4214 * Update HcFmRemaining.FRT and update start of frame time.
4215 */
4216 pThis->frt = pThis->fit;
4217 pThis->SofTime += pThis->cTicksPerFrame;
4218
4219 /*
4220 * Check that the HCCA address isn't bogus. Linux 2.4.x is known to start
4221 * the bus with a hcca of 0 to work around problem with a specific controller.
4222 */
4223 bool fValidHCCA = !( pThis->hcca >= OHCI_HCCA_MASK
4224 || pThis->hcca < ~OHCI_HCCA_MASK);
4225
4226# if 1
4227 /*
4228 * Update the HCCA.
4229 * Should be done after SOF but before HC read first ED in this frame.
4230 */
4231 if (fValidHCCA)
4232 ohciR3UpdateHCCA(pDevIns, pThis, pThisCC);
4233# endif
4234
4235 /* "After writing to HCCA, HC will set SF in HcInterruptStatus" - guest isn't executing, so ignore the order! */
4236 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_START_OF_FRAME);
4237
4238 if (pThis->fno)
4239 {
4240 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_FRAMENUMBER_OVERFLOW);
4241 pThis->fno = 0;
4242 }
4243
4244 /* If the HCCA address is invalid, we're quitting here to avoid doing something which cannot be reported to the HCD. */
4245 if (!fValidHCCA)
4246 {
4247 Log(("ohciR3StartOfFrame: skipping hcca part because hcca=%RX32 (our 'valid' range: %RX32-%RX32)\n",
4248 pThis->hcca, ~OHCI_HCCA_MASK, OHCI_HCCA_MASK));
4249 return;
4250 }
4251
4252 /*
4253 * Periodic EPs.
4254 */
4255 if (pThis->ctl & OHCI_CTL_PLE)
4256 ohciR3ServicePeriodicList(pDevIns, pThis, pThisCC);
4257
4258 /*
4259 * Control EPs.
4260 */
4261 if ( (pThis->ctl & OHCI_CTL_CLE)
4262 && (pThis->status & OHCI_STATUS_CLF) )
4263 ohciR3ServiceCtrlList(pDevIns, pThis, pThisCC);
4264
4265 /*
4266 * Bulk EPs.
4267 */
4268 if ( (pThis->ctl & OHCI_CTL_BLE)
4269 && (pThis->status & OHCI_STATUS_BLF))
4270 ohciR3ServiceBulkList(pDevIns, pThis, pThisCC);
4271 else if ((pThis->status & OHCI_STATUS_BLF)
4272 && pThis->fBulkNeedsCleaning)
4273 ohciR3UndoBulkList(pDevIns, pThis, pThisCC); /* If list disabled but not empty, abort endpoints. */
4274
4275# if 0
4276 /*
4277 * Update the HCCA after processing the lists and everything. A bit experimental.
4278 *
4279 * ASSUME the guest won't be very upset if a TD is completed, retired and handed
4280 * back immediately. The idea is to be able to retire the data and/or status stages
4281 * of a control transfer together with the setup stage, thus saving a frame. This
4282 * behaviour is should be perfectly ok, since the setup (and maybe data) stages
4283 * have already taken at least one frame to complete.
4284 *
4285 * But, when implementing the first synchronous virtual USB devices, we'll have to
4286 * verify that the guest doesn't choke when having a TD returned in the same frame
4287 * as it was submitted.
4288 */
4289 ohciR3UpdateHCCA(pThis);
4290# endif
4291
4292# ifdef LOG_ENABLED
4293 if (pThis->status ^ status_old)
4294 {
4295 uint32_t val = pThis->status;
4296 uint32_t chg = val ^ status_old; NOREF(chg);
4297 Log2(("ohciR3StartOfFrame: HcCommandStatus=%#010x: %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
4298 val,
4299 chg & RT_BIT(0) ? "*" : "", val & 1,
4300 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
4301 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4302 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4303 chg & (3<<16)? "*" : "", (val >> 16) & 3));
4304 }
4305# endif
4306}
4307
4308
4309/**
4310 * Updates the HcFmNumber and FNO registers.
4311 */
4312static void ohciR3BumpFrameNumber(POHCI pThis)
4313{
4314 const uint16_t u16OldFmNumber = pThis->HcFmNumber++;
4315 if ((u16OldFmNumber ^ pThis->HcFmNumber) & RT_BIT(15))
4316 pThis->fno = 1;
4317}
4318
4319
4320/**
4321 * Callback for periodic frame processing.
4322 */
4323static DECLCALLBACK(bool) ohciR3StartFrame(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameNo)
4324{
4325 RT_NOREF(u32FrameNo);
4326 POHCICC pThisCC = VUSBIROOTHUBPORT_2_OHCI(pInterface);
4327 PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
4328 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
4329
4330 ohciR3Lock(pThisCC);
4331
4332 /* Reset idle detection flag */
4333 pThis->fIdle = true;
4334
4335# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
4336 physReadStatsReset(&g_PhysReadState);
4337# endif
4338
4339 if (!(pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR))
4340 {
4341 /* Frame boundary, so do EOF stuff here. */
4342 ohciR3BumpFrameNumber(pThis);
4343 if ( (pThis->dqic != 0x7) && (pThis->dqic != 0))
4344 pThis->dqic--;
4345
4346 /* Clean up any URBs that have been removed. */
4347 ohciR3CancelOrphanedURBs(pDevIns, pThis, pThisCC);
4348
4349 /* Start the next frame. */
4350 ohciR3StartOfFrame(pDevIns, pThis, pThisCC);
4351 }
4352
4353# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
4354 physReadStatsPrint(&g_PhysReadState);
4355# endif
4356
4357 ohciR3Unlock(pThisCC);
4358 return pThis->fIdle;
4359}
4360
4361
4362/**
4363 * @interface_method_impl{VUSBIROOTHUBPORT,pfnFrameRateChanged}
4364 */
4365static DECLCALLBACK(void) ohciR3FrameRateChanged(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameRate)
4366{
4367 POHCICC pThisCC = VUSBIROOTHUBPORT_2_OHCI(pInterface);
4368 PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
4369 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
4370
4371 Assert(u32FrameRate <= OHCI_DEFAULT_TIMER_FREQ);
4372
4373 pThis->cTicksPerFrame = pThis->u64TimerHz / u32FrameRate;
4374 if (!pThis->cTicksPerFrame)
4375 pThis->cTicksPerFrame = 1;
4376 pThis->cTicksPerUsbTick = pThis->u64TimerHz >= VUSB_BUS_HZ ? pThis->u64TimerHz / VUSB_BUS_HZ : 1;
4377}
4378
4379
4380/**
4381 * Start sending SOF tokens across the USB bus, lists are processed in
4382 * next frame
4383 */
4384static void ohciR3BusStart(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4385{
4386 pThisCC->RootHub.pIRhConn->pfnPowerOn(pThisCC->RootHub.pIRhConn);
4387 pThis->dqic = 0x7;
4388
4389 Log(("ohci: Bus started\n"));
4390
4391 pThis->SofTime = PDMDevHlpTMTimeVirtGet(pDevIns);
4392 int rc = pThisCC->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThisCC->RootHub.pIRhConn, OHCI_DEFAULT_TIMER_FREQ);
4393 AssertRC(rc);
4394}
4395
4396
4397/**
4398 * Stop sending SOF tokens on the bus
4399 */
4400static void ohciR3BusStop(POHCICC pThisCC)
4401{
4402 int rc = pThisCC->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThisCC->RootHub.pIRhConn, 0);
4403 AssertRC(rc);
4404 pThisCC->RootHub.pIRhConn->pfnPowerOff(pThisCC->RootHub.pIRhConn);
4405}
4406
4407
4408/**
4409 * Move in to resume state
4410 */
4411static void ohciR3BusResume(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, bool fHardware)
4412{
4413 pThis->ctl &= ~OHCI_CTL_HCFS;
4414 pThis->ctl |= OHCI_USB_RESUME;
4415
4416 LogFunc(("fHardware=%RTbool RWE=%s\n",
4417 fHardware, (pThis->ctl & OHCI_CTL_RWE) ? "on" : "off"));
4418
4419 if (fHardware && (pThis->ctl & OHCI_CTL_RWE))
4420 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_RESUME_DETECT);
4421
4422 ohciR3BusStart(pDevIns, pThis, pThisCC);
4423}
4424
4425
4426/* Power a port up or down */
4427static void ohciR3RhPortPower(POHCIROOTHUBR3 pRh, unsigned iPort, bool fPowerUp)
4428{
4429 POHCIHUBPORT pPort = &pRh->aPorts[iPort];
4430 bool fOldPPS = !!(pPort->fReg & OHCI_PORT_PPS);
4431
4432 LogFlowFunc(("iPort=%u fPowerUp=%RTbool\n", iPort, fPowerUp));
4433
4434 if (fPowerUp)
4435 {
4436 /* power up */
4437 if (pPort->fAttached)
4438 pPort->fReg |= OHCI_PORT_CCS;
4439 if (pPort->fReg & OHCI_PORT_CCS)
4440 pPort->fReg |= OHCI_PORT_PPS;
4441 if (pPort->fAttached && !fOldPPS)
4442 VUSBIRhDevPowerOn(pRh->pIRhConn, OHCI_PORT_2_VUSB_PORT(iPort));
4443 }
4444 else
4445 {
4446 /* power down */
4447 pPort->fReg &= ~(OHCI_PORT_PPS | OHCI_PORT_CCS | OHCI_PORT_PSS | OHCI_PORT_PRS);
4448 if (pPort->fAttached && fOldPPS)
4449 VUSBIRhDevPowerOff(pRh->pIRhConn, OHCI_PORT_2_VUSB_PORT(iPort));
4450 }
4451}
4452
4453#endif /* IN_RING3 */
4454
4455/**
4456 * Read the HcRevision register.
4457 */
4458static VBOXSTRICTRC HcRevision_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4459{
4460 RT_NOREF(pDevIns, pThis, iReg);
4461 Log2(("HcRevision_r() -> 0x10\n"));
4462 *pu32Value = 0x10; /* OHCI revision 1.0, no emulation. */
4463 return VINF_SUCCESS;
4464}
4465
4466/**
4467 * Write to the HcRevision register.
4468 */
4469static VBOXSTRICTRC HcRevision_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t u32Value)
4470{
4471 RT_NOREF(pDevIns, pThis, iReg, u32Value);
4472 Log2(("HcRevision_w(%#010x) - denied\n", u32Value));
4473 ASSERT_GUEST_MSG_FAILED(("Invalid operation!!! u32Value=%#010x\n", u32Value));
4474 return VINF_SUCCESS;
4475}
4476
4477/**
4478 * Read the HcControl register.
4479 */
4480static VBOXSTRICTRC HcControl_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4481{
4482 RT_NOREF(pDevIns, iReg);
4483 uint32_t ctl = pThis->ctl;
4484 Log2(("HcControl_r -> %#010x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
4485 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
4486 (ctl >> 9) & 1, (ctl >> 10) & 1));
4487 *pu32Value = ctl;
4488 return VINF_SUCCESS;
4489}
4490
4491/**
4492 * Write the HcControl register.
4493 */
4494static VBOXSTRICTRC HcControl_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4495{
4496 RT_NOREF(iReg);
4497
4498 /* log it. */
4499 uint32_t chg = pThis->ctl ^ val; NOREF(chg);
4500 Log2(("HcControl_w(%#010x) => %sCBSR=%d %sPLE=%d %sIE=%d %sCLE=%d %sBLE=%d %sHCFS=%#x %sIR=%d %sRWC=%d %sRWE=%d\n",
4501 val,
4502 chg & 3 ? "*" : "", val & 3,
4503 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4504 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4505 chg & RT_BIT(4) ? "*" : "", (val >> 4) & 1,
4506 chg & RT_BIT(5) ? "*" : "", (val >> 5) & 1,
4507 chg & (3 << 6)? "*" : "", (val >> 6) & 3,
4508 chg & RT_BIT(8) ? "*" : "", (val >> 8) & 1,
4509 chg & RT_BIT(9) ? "*" : "", (val >> 9) & 1,
4510 chg & RT_BIT(10) ? "*" : "", (val >> 10) & 1));
4511 if (val & ~0x07ff)
4512 Log2(("Unknown bits %#x are set!!!\n", val & ~0x07ff));
4513
4514 /* see what changed and take action on that. */
4515 uint32_t old_state = pThis->ctl & OHCI_CTL_HCFS;
4516 uint32_t new_state = val & OHCI_CTL_HCFS;
4517
4518#ifdef IN_RING3
4519 pThis->ctl = val;
4520 if (new_state != old_state)
4521 {
4522 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
4523 switch (new_state)
4524 {
4525 case OHCI_USB_OPERATIONAL:
4526 LogRel(("OHCI: USB Operational\n"));
4527 ohciR3BusStart(pDevIns, pThis, pThisCC);
4528 break;
4529 case OHCI_USB_SUSPEND:
4530 ohciR3BusStop(pThisCC);
4531 LogRel(("OHCI: USB Suspended\n"));
4532 break;
4533 case OHCI_USB_RESUME:
4534 LogRel(("OHCI: USB Resume\n"));
4535 ohciR3BusResume(pDevIns, pThis, pThisCC, false /* not hardware */);
4536 break;
4537 case OHCI_USB_RESET:
4538 {
4539 LogRel(("OHCI: USB Reset\n"));
4540 ohciR3BusStop(pThisCC);
4541 /** @todo This should probably do a real reset, but we don't implement
4542 * that correctly in the roothub reset callback yet. check it's
4543 * comments and argument for more details. */
4544 pThisCC->RootHub.pIRhConn->pfnReset(pThisCC->RootHub.pIRhConn, false /* don't do a real reset */);
4545 break;
4546 }
4547 }
4548 }
4549#else /* !IN_RING3 */
4550 RT_NOREF(pDevIns);
4551 if ( new_state != old_state )
4552 {
4553 Log2(("HcControl_w: state changed -> VINF_IOM_R3_MMIO_WRITE\n"));
4554 return VINF_IOM_R3_MMIO_WRITE;
4555 }
4556 pThis->ctl = val;
4557#endif /* !IN_RING3 */
4558
4559 return VINF_SUCCESS;
4560}
4561
4562/**
4563 * Read the HcCommandStatus register.
4564 */
4565static VBOXSTRICTRC HcCommandStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4566{
4567 uint32_t status = pThis->status;
4568 Log2(("HcCommandStatus_r() -> %#010x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
4569 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3));
4570 *pu32Value = status;
4571 RT_NOREF(pDevIns, iReg);
4572 return VINF_SUCCESS;
4573}
4574
4575/**
4576 * Write to the HcCommandStatus register.
4577 */
4578static VBOXSTRICTRC HcCommandStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4579{
4580 RT_NOREF(pDevIns, iReg);
4581
4582 /* log */
4583 uint32_t chg = pThis->status ^ val; NOREF(chg);
4584 Log2(("HcCommandStatus_w(%#010x) => %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
4585 val,
4586 chg & RT_BIT(0) ? "*" : "", val & 1,
4587 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
4588 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4589 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4590 chg & (3<<16)? "!!!":"", (pThis->status >> 16) & 3));
4591 if (val & ~0x0003000f)
4592 Log2(("Unknown bits %#x are set!!!\n", val & ~0x0003000f));
4593
4594 /* SOC is read-only */
4595 val = (val & ~OHCI_STATUS_SOC);
4596
4597#ifdef IN_RING3
4598 /* "bits written as '0' remain unchanged in the register" */
4599 pThis->status |= val;
4600 if (pThis->status & OHCI_STATUS_HCR)
4601 {
4602 LogRel(("OHCI: Software reset\n"));
4603 ohciR3DoReset(pDevIns, pThis, PDMDEVINS_2_DATA_CC(pDevIns, POHCICC), OHCI_USB_SUSPEND, false /* N/A */);
4604 }
4605#else
4606 if ((pThis->status | val) & OHCI_STATUS_HCR)
4607 {
4608 LogFlow(("HcCommandStatus_w: reset -> VINF_IOM_R3_MMIO_WRITE\n"));
4609 return VINF_IOM_R3_MMIO_WRITE;
4610 }
4611 pThis->status |= val;
4612#endif
4613 return VINF_SUCCESS;
4614}
4615
4616/**
4617 * Read the HcInterruptStatus register.
4618 */
4619static VBOXSTRICTRC HcInterruptStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4620{
4621 uint32_t val = pThis->intr_status;
4622 Log2(("HcInterruptStatus_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
4623 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4624 (val >> 6) & 1, (val >> 30) & 1));
4625 *pu32Value = val;
4626 RT_NOREF(pDevIns, iReg);
4627 return VINF_SUCCESS;
4628}
4629
4630/**
4631 * Write to the HcInterruptStatus register.
4632 */
4633static VBOXSTRICTRC HcInterruptStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4634{
4635 RT_NOREF(iReg);
4636
4637 uint32_t res = pThis->intr_status & ~val;
4638 uint32_t chg = pThis->intr_status ^ res; NOREF(chg);
4639
4640 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4641 if (rc != VINF_SUCCESS)
4642 return rc;
4643
4644 Log2(("HcInterruptStatus_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d\n",
4645 val,
4646 chg & RT_BIT(0) ? "*" : "", res & 1,
4647 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4648 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4649 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4650 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4651 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4652 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4653 chg & RT_BIT(30)? "*" : "", (res >> 30) & 1));
4654 if ( (val & ~0xc000007f)
4655 && val != 0xffffffff /* ignore clear-all-like requests from xp. */)
4656 Log2(("Unknown bits %#x are set!!!\n", val & ~0xc000007f));
4657
4658 /* "The Host Controller Driver may clear specific bits in this
4659 * register by writing '1' to bit positions to be cleared"
4660 */
4661 pThis->intr_status &= ~val;
4662 ohciUpdateInterruptLocked(pDevIns, pThis, "HcInterruptStatus_w");
4663 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
4664 return VINF_SUCCESS;
4665}
4666
4667/**
4668 * Read the HcInterruptEnable register
4669 */
4670static VBOXSTRICTRC HcInterruptEnable_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4671{
4672 uint32_t val = pThis->intr;
4673 Log2(("HcInterruptEnable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
4674 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4675 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
4676 *pu32Value = val;
4677 RT_NOREF(pDevIns, iReg);
4678 return VINF_SUCCESS;
4679}
4680
4681/**
4682 * Writes to the HcInterruptEnable register.
4683 */
4684static VBOXSTRICTRC HcInterruptEnable_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4685{
4686 RT_NOREF(iReg);
4687 uint32_t res = pThis->intr | val;
4688 uint32_t chg = pThis->intr ^ res; NOREF(chg);
4689
4690 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4691 if (rc != VINF_SUCCESS)
4692 return rc;
4693
4694 Log2(("HcInterruptEnable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
4695 val,
4696 chg & RT_BIT(0) ? "*" : "", res & 1,
4697 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4698 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4699 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4700 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4701 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4702 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4703 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
4704 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
4705 if (val & ~0xc000007f)
4706 Log2(("Uknown bits %#x are set!!!\n", val & ~0xc000007f));
4707
4708 pThis->intr |= val;
4709 ohciUpdateInterruptLocked(pDevIns, pThis, "HcInterruptEnable_w");
4710 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
4711 return VINF_SUCCESS;
4712}
4713
4714/**
4715 * Reads the HcInterruptDisable register.
4716 */
4717static VBOXSTRICTRC HcInterruptDisable_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4718{
4719#if 1 /** @todo r=bird: "On read, the current value of the HcInterruptEnable register is returned." */
4720 uint32_t val = pThis->intr;
4721#else /* old code. */
4722 uint32_t val = ~pThis->intr;
4723#endif
4724 Log2(("HcInterruptDisable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
4725 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4726 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
4727
4728 *pu32Value = val;
4729 RT_NOREF(pDevIns, iReg);
4730 return VINF_SUCCESS;
4731}
4732
4733/**
4734 * Writes to the HcInterruptDisable register.
4735 */
4736static VBOXSTRICTRC HcInterruptDisable_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4737{
4738 RT_NOREF(iReg);
4739 uint32_t res = pThis->intr & ~val;
4740 uint32_t chg = pThis->intr ^ res; NOREF(chg);
4741
4742 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4743 if (rc != VINF_SUCCESS)
4744 return rc;
4745
4746 Log2(("HcInterruptDisable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
4747 val,
4748 chg & RT_BIT(0) ? "*" : "", res & 1,
4749 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4750 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4751 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4752 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4753 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4754 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4755 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
4756 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
4757 /* Don't bitch about invalid bits here since it makes sense to disable
4758 * interrupts you don't know about. */
4759
4760 pThis->intr &= ~val;
4761 ohciUpdateInterruptLocked(pDevIns, pThis, "HcInterruptDisable_w");
4762 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
4763 return VINF_SUCCESS;
4764}
4765
4766/**
4767 * Read the HcHCCA register (Host Controller Communications Area physical address).
4768 */
4769static VBOXSTRICTRC HcHCCA_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4770{
4771 Log2(("HcHCCA_r() -> %#010x\n", pThis->hcca));
4772 *pu32Value = pThis->hcca;
4773 RT_NOREF(pDevIns, iReg);
4774 return VINF_SUCCESS;
4775}
4776
4777/**
4778 * Write to the HcHCCA register (Host Controller Communications Area physical address).
4779 */
4780static VBOXSTRICTRC HcHCCA_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t Value)
4781{
4782 Log2(("HcHCCA_w(%#010x) - old=%#010x new=%#010x\n", Value, pThis->hcca, Value & OHCI_HCCA_MASK));
4783 pThis->hcca = Value & OHCI_HCCA_MASK;
4784 RT_NOREF(pDevIns, iReg);
4785 return VINF_SUCCESS;
4786}
4787
4788/**
4789 * Read the HcPeriodCurrentED register.
4790 */
4791static VBOXSTRICTRC HcPeriodCurrentED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4792{
4793 Log2(("HcPeriodCurrentED_r() -> %#010x\n", pThis->per_cur));
4794 *pu32Value = pThis->per_cur;
4795 RT_NOREF(pDevIns, iReg);
4796 return VINF_SUCCESS;
4797}
4798
4799/**
4800 * Write to the HcPeriodCurrentED register.
4801 */
4802static VBOXSTRICTRC HcPeriodCurrentED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4803{
4804 Log(("HcPeriodCurrentED_w(%#010x) - old=%#010x new=%#010x (This is a read only register, only the linux guys don't respect that!)\n",
4805 val, pThis->per_cur, val & ~7));
4806 //AssertMsgFailed(("HCD (Host Controller Driver) should not write to HcPeriodCurrentED! val=%#010x (old=%#010x)\n", val, pThis->per_cur));
4807 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4808 pThis->per_cur = val & ~7;
4809 RT_NOREF(pDevIns, iReg);
4810 return VINF_SUCCESS;
4811}
4812
4813/**
4814 * Read the HcControlHeadED register.
4815 */
4816static VBOXSTRICTRC HcControlHeadED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4817{
4818 Log2(("HcControlHeadED_r() -> %#010x\n", pThis->ctrl_head));
4819 *pu32Value = pThis->ctrl_head;
4820 RT_NOREF(pDevIns, iReg);
4821 return VINF_SUCCESS;
4822}
4823
4824/**
4825 * Write to the HcControlHeadED register.
4826 */
4827static VBOXSTRICTRC HcControlHeadED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4828{
4829 Log2(("HcControlHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->ctrl_head, val & ~7));
4830 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4831 pThis->ctrl_head = val & ~7;
4832 RT_NOREF(pDevIns, iReg);
4833 return VINF_SUCCESS;
4834}
4835
4836/**
4837 * Read the HcControlCurrentED register.
4838 */
4839static VBOXSTRICTRC HcControlCurrentED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4840{
4841 Log2(("HcControlCurrentED_r() -> %#010x\n", pThis->ctrl_cur));
4842 *pu32Value = pThis->ctrl_cur;
4843 RT_NOREF(pDevIns, iReg);
4844 return VINF_SUCCESS;
4845}
4846
4847/**
4848 * Write to the HcControlCurrentED register.
4849 */
4850static VBOXSTRICTRC HcControlCurrentED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4851{
4852 Log2(("HcControlCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->ctrl_cur, val & ~7));
4853 AssertMsg(!(pThis->ctl & OHCI_CTL_CLE), ("Illegal write! HcControl.ControlListEnabled is set! val=%#010x\n", val));
4854 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4855 pThis->ctrl_cur = val & ~7;
4856 RT_NOREF(pDevIns, iReg);
4857 return VINF_SUCCESS;
4858}
4859
4860/**
4861 * Read the HcBulkHeadED register.
4862 */
4863static VBOXSTRICTRC HcBulkHeadED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4864{
4865 Log2(("HcBulkHeadED_r() -> %#010x\n", pThis->bulk_head));
4866 *pu32Value = pThis->bulk_head;
4867 RT_NOREF(pDevIns, iReg);
4868 return VINF_SUCCESS;
4869}
4870
4871/**
4872 * Write to the HcBulkHeadED register.
4873 */
4874static VBOXSTRICTRC HcBulkHeadED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4875{
4876 Log2(("HcBulkHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->bulk_head, val & ~7));
4877 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4878 pThis->bulk_head = val & ~7; /** @todo The ATI OHCI controller on my machine enforces 16-byte address alignment. */
4879 RT_NOREF(pDevIns, iReg);
4880 return VINF_SUCCESS;
4881}
4882
4883/**
4884 * Read the HcBulkCurrentED register.
4885 */
4886static VBOXSTRICTRC HcBulkCurrentED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4887{
4888 Log2(("HcBulkCurrentED_r() -> %#010x\n", pThis->bulk_cur));
4889 *pu32Value = pThis->bulk_cur;
4890 RT_NOREF(pDevIns, iReg);
4891 return VINF_SUCCESS;
4892}
4893
4894/**
4895 * Write to the HcBulkCurrentED register.
4896 */
4897static VBOXSTRICTRC HcBulkCurrentED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4898{
4899 Log2(("HcBulkCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->bulk_cur, val & ~7));
4900 AssertMsg(!(pThis->ctl & OHCI_CTL_BLE), ("Illegal write! HcControl.BulkListEnabled is set! val=%#010x\n", val));
4901 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4902 pThis->bulk_cur = val & ~7;
4903 RT_NOREF(pDevIns, iReg);
4904 return VINF_SUCCESS;
4905}
4906
4907
4908/**
4909 * Read the HcDoneHead register.
4910 */
4911static VBOXSTRICTRC HcDoneHead_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4912{
4913 Log2(("HcDoneHead_r() -> 0x%#08x\n", pThis->done));
4914 *pu32Value = pThis->done;
4915 RT_NOREF(pDevIns, iReg);
4916 return VINF_SUCCESS;
4917}
4918
4919/**
4920 * Write to the HcDoneHead register.
4921 */
4922static VBOXSTRICTRC HcDoneHead_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4923{
4924 RT_NOREF(pDevIns, pThis, iReg, val);
4925 Log2(("HcDoneHead_w(0x%#08x) - denied!!!\n", val));
4926 /*AssertMsgFailed(("Illegal operation!!! val=%#010x\n", val)); - OS/2 does this */
4927 return VINF_SUCCESS;
4928}
4929
4930
4931/**
4932 * Read the HcFmInterval (Fm=Frame) register.
4933 */
4934static VBOXSTRICTRC HcFmInterval_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4935{
4936 uint32_t val = (pThis->fit << 31) | (pThis->fsmps << 16) | (pThis->fi);
4937 Log2(("HcFmInterval_r() -> 0x%#08x - FI=%d FSMPS=%d FIT=%d\n",
4938 val, val & 0x3fff, (val >> 16) & 0x7fff, val >> 31));
4939 *pu32Value = val;
4940 RT_NOREF(pDevIns, iReg);
4941 return VINF_SUCCESS;
4942}
4943
4944/**
4945 * Write to the HcFmInterval (Fm = Frame) register.
4946 */
4947static VBOXSTRICTRC HcFmInterval_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4948{
4949 RT_NOREF(pDevIns, iReg);
4950
4951 /* log */
4952 uint32_t chg = val ^ ((pThis->fit << 31) | (pThis->fsmps << 16) | pThis->fi); NOREF(chg);
4953 Log2(("HcFmInterval_w(%#010x) => %sFI=%d %sFSMPS=%d %sFIT=%d\n",
4954 val,
4955 chg & 0x00003fff ? "*" : "", val & 0x3fff,
4956 chg & 0x7fff0000 ? "*" : "", (val >> 16) & 0x7fff,
4957 chg >> 31 ? "*" : "", (val >> 31) & 1));
4958 if (pThis->fi != (val & OHCI_FMI_FI))
4959 {
4960 Log(("ohci: FrameInterval: %#010x -> %#010x\n", pThis->fi, val & OHCI_FMI_FI));
4961 AssertMsg(pThis->fit != ((val >> OHCI_FMI_FIT_SHIFT) & 1), ("HCD didn't toggle the FIT bit!!!\n"));
4962 }
4963
4964 /* update */
4965 pThis->fi = val & OHCI_FMI_FI;
4966 pThis->fit = (val & OHCI_FMI_FIT) >> OHCI_FMI_FIT_SHIFT;
4967 pThis->fsmps = (val & OHCI_FMI_FSMPS) >> OHCI_FMI_FSMPS_SHIFT;
4968 return VINF_SUCCESS;
4969}
4970
4971/**
4972 * Read the HcFmRemaining (Fm = Frame) register.
4973 */
4974static VBOXSTRICTRC HcFmRemaining_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4975{
4976 RT_NOREF(iReg);
4977 uint32_t Value = pThis->frt << 31;
4978 if ((pThis->ctl & OHCI_CTL_HCFS) == OHCI_USB_OPERATIONAL)
4979 {
4980 /*
4981 * Being in USB operational state guarantees SofTime was set already.
4982 */
4983 uint64_t tks = PDMDevHlpTMTimeVirtGet(pDevIns) - pThis->SofTime;
4984 if (tks < pThis->cTicksPerFrame) /* avoid muldiv if possible */
4985 {
4986 uint16_t fr;
4987 tks = ASMMultU64ByU32DivByU32(1, tks, pThis->cTicksPerUsbTick);
4988 fr = (uint16_t)(pThis->fi - tks);
4989 Value |= fr;
4990 }
4991 }
4992
4993 Log2(("HcFmRemaining_r() -> %#010x - FR=%d FRT=%d\n", Value, Value & 0x3fff, Value >> 31));
4994 *pu32Value = Value;
4995 return VINF_SUCCESS;
4996}
4997
4998/**
4999 * Write to the HcFmRemaining (Fm = Frame) register.
5000 */
5001static VBOXSTRICTRC HcFmRemaining_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5002{
5003 RT_NOREF(pDevIns, pThis, iReg, val);
5004 Log2(("HcFmRemaining_w(%#010x) - denied\n", val));
5005 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
5006 return VINF_SUCCESS;
5007}
5008
5009/**
5010 * Read the HcFmNumber (Fm = Frame) register.
5011 */
5012static VBOXSTRICTRC HcFmNumber_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5013{
5014 RT_NOREF(pDevIns, iReg);
5015 uint32_t val = (uint16_t)pThis->HcFmNumber;
5016 Log2(("HcFmNumber_r() -> %#010x - FN=%#x(%d) (32-bit=%#x(%d))\n", val, val, val, pThis->HcFmNumber, pThis->HcFmNumber));
5017 *pu32Value = val;
5018 return VINF_SUCCESS;
5019}
5020
5021/**
5022 * Write to the HcFmNumber (Fm = Frame) register.
5023 */
5024static VBOXSTRICTRC HcFmNumber_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5025{
5026 RT_NOREF(pDevIns, pThis, iReg, val);
5027 Log2(("HcFmNumber_w(%#010x) - denied\n", val));
5028 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
5029 return VINF_SUCCESS;
5030}
5031
5032/**
5033 * Read the HcPeriodicStart register.
5034 * The register determines when in a frame to switch from control&bulk to periodic lists.
5035 */
5036static VBOXSTRICTRC HcPeriodicStart_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5037{
5038 RT_NOREF(pDevIns, iReg);
5039 Log2(("HcPeriodicStart_r() -> %#010x - PS=%d\n", pThis->pstart, pThis->pstart & 0x3fff));
5040 *pu32Value = pThis->pstart;
5041 return VINF_SUCCESS;
5042}
5043
5044/**
5045 * Write to the HcPeriodicStart register.
5046 * The register determines when in a frame to switch from control&bulk to periodic lists.
5047 */
5048static VBOXSTRICTRC HcPeriodicStart_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5049{
5050 RT_NOREF(pDevIns, iReg);
5051 Log2(("HcPeriodicStart_w(%#010x) => PS=%d\n", val, val & 0x3fff));
5052 if (val & ~0x3fff)
5053 Log2(("Unknown bits %#x are set!!!\n", val & ~0x3fff));
5054 pThis->pstart = val; /** @todo r=bird: should we support setting the other bits? */
5055 return VINF_SUCCESS;
5056}
5057
5058/**
5059 * Read the HcLSThreshold register.
5060 */
5061static VBOXSTRICTRC HcLSThreshold_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5062{
5063 RT_NOREF(pDevIns, pThis, iReg);
5064 Log2(("HcLSThreshold_r() -> %#010x\n", OHCI_LS_THRESH));
5065 *pu32Value = OHCI_LS_THRESH;
5066 return VINF_SUCCESS;
5067}
5068
5069/**
5070 * Write to the HcLSThreshold register.
5071 *
5072 * Docs are inconsistent here:
5073 *
5074 * "Neither the Host Controller nor the Host Controller Driver are allowed to change this value."
5075 *
5076 * "This value is calculated by HCD with the consideration of transmission and setup overhead."
5077 *
5078 * The register is marked "R/W" the HCD column.
5079 *
5080 */
5081static VBOXSTRICTRC HcLSThreshold_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5082{
5083 RT_NOREF(pDevIns, pThis, iReg, val);
5084 Log2(("HcLSThreshold_w(%#010x) => LST=0x%03x(%d)\n", val, val & 0x0fff, val & 0x0fff));
5085 AssertMsg(val == OHCI_LS_THRESH,
5086 ("HCD tried to write bad LS threshold: 0x%x (see function header)\n", val));
5087 /** @todo the HCD can change this. */
5088 return VINF_SUCCESS;
5089}
5090
5091/**
5092 * Read the HcRhDescriptorA register.
5093 */
5094static VBOXSTRICTRC HcRhDescriptorA_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5095{
5096 RT_NOREF(pDevIns, iReg);
5097 uint32_t val = pThis->RootHub.desc_a;
5098#if 0 /* annoying */
5099 Log2(("HcRhDescriptorA_r() -> %#010x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTGT=%#x\n",
5100 val, val & 0xff, (val >> 8) & 1, (val >> 9) & 1, (val >> 10) & 1, (val >> 11) & 1,
5101 (val >> 12) & 1, (val >> 24) & 0xff));
5102#endif
5103 *pu32Value = val;
5104 return VINF_SUCCESS;
5105}
5106
5107/**
5108 * Write to the HcRhDescriptorA register.
5109 */
5110static VBOXSTRICTRC HcRhDescriptorA_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5111{
5112 RT_NOREF(pDevIns, iReg);
5113 uint32_t chg = val ^ pThis->RootHub.desc_a; NOREF(chg);
5114 Log2(("HcRhDescriptorA_w(%#010x) => %sNDP=%d %sPSM=%d %sNPS=%d %sDT=%d %sOCPM=%d %sNOCP=%d %sPOTGT=%#x - %sPowerSwitching Set%sPower\n",
5115 val,
5116 chg & 0xff ?"!!!": "", val & 0xff,
5117 (chg >> 8) & 1 ? "*" : "", (val >> 8) & 1,
5118 (chg >> 9) & 1 ? "*" : "", (val >> 9) & 1,
5119 (chg >> 10) & 1 ?"!!!": "", 0,
5120 (chg >> 11) & 1 ? "*" : "", (val >> 11) & 1,
5121 (chg >> 12) & 1 ? "*" : "", (val >> 12) & 1,
5122 (chg >> 24)&0xff? "*" : "", (val >> 24) & 0xff,
5123 val & OHCI_RHA_NPS ? "No" : "",
5124 val & OHCI_RHA_PSM ? "Port" : "Global"));
5125 if (val & ~0xff001fff)
5126 Log2(("Unknown bits %#x are set!!!\n", val & ~0xff001fff));
5127
5128
5129 if ((val & (OHCI_RHA_NDP | OHCI_RHA_DT)) != OHCI_NDP_CFG(pThis))
5130 {
5131 Log(("ohci: invalid write to NDP or DT in roothub descriptor A!!! val=0x%.8x\n", val));
5132 val &= ~(OHCI_RHA_NDP | OHCI_RHA_DT);
5133 val |= OHCI_NDP_CFG(pThis);
5134 }
5135
5136 pThis->RootHub.desc_a = val;
5137 return VINF_SUCCESS;
5138}
5139
5140/**
5141 * Read the HcRhDescriptorB register.
5142 */
5143static VBOXSTRICTRC HcRhDescriptorB_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5144{
5145 uint32_t val = pThis->RootHub.desc_b;
5146 Log2(("HcRhDescriptorB_r() -> %#010x - DR=0x%04x PPCM=0x%04x\n",
5147 val, val & 0xffff, val >> 16));
5148 *pu32Value = val;
5149 RT_NOREF(pDevIns, iReg);
5150 return VINF_SUCCESS;
5151}
5152
5153/**
5154 * Write to the HcRhDescriptorB register.
5155 */
5156static VBOXSTRICTRC HcRhDescriptorB_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5157{
5158 RT_NOREF(pDevIns, iReg);
5159 uint32_t chg = pThis->RootHub.desc_b ^ val; NOREF(chg);
5160 Log2(("HcRhDescriptorB_w(%#010x) => %sDR=0x%04x %sPPCM=0x%04x\n",
5161 val,
5162 chg & 0xffff ? "!!!" : "", val & 0xffff,
5163 chg >> 16 ? "!!!" : "", val >> 16));
5164
5165 if ( pThis->RootHub.desc_b != val )
5166 Log(("ohci: unsupported write to root descriptor B!!! 0x%.8x -> 0x%.8x\n", pThis->RootHub.desc_b, val));
5167 pThis->RootHub.desc_b = val;
5168 return VINF_SUCCESS;
5169}
5170
5171/**
5172 * Read the HcRhStatus (Rh = Root Hub) register.
5173 */
5174static VBOXSTRICTRC HcRhStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5175{
5176 uint32_t val = pThis->RootHub.status;
5177 if (val & (OHCI_RHS_LPSC | OHCI_RHS_OCIC))
5178 Log2(("HcRhStatus_r() -> %#010x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n",
5179 val, val & 1, (val >> 1) & 1, (val >> 15) & 1, (val >> 16) & 1, (val >> 17) & 1, (val >> 31) & 1));
5180 *pu32Value = val;
5181 RT_NOREF(pDevIns, iReg);
5182 return VINF_SUCCESS;
5183}
5184
5185/**
5186 * Write to the HcRhStatus (Rh = Root Hub) register.
5187 */
5188static VBOXSTRICTRC HcRhStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5189{
5190#ifdef IN_RING3
5191 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5192
5193 /* log */
5194 uint32_t old = pThis->RootHub.status;
5195 uint32_t chg;
5196 if (val & ~0x80038003)
5197 Log2(("HcRhStatus_w: Unknown bits %#x are set!!!\n", val & ~0x80038003));
5198 if ( (val & OHCI_RHS_LPSC) && (val & OHCI_RHS_LPS) )
5199 Log2(("HcRhStatus_w: Warning both CGP and SGP are set! (Clear/Set Global Power)\n"));
5200 if ( (val & OHCI_RHS_DRWE) && (val & OHCI_RHS_CRWE) )
5201 Log2(("HcRhStatus_w: Warning both CRWE and SRWE are set! (Clear/Set Remote Wakeup Enable)\n"));
5202
5203
5204 /* write 1 to clear OCIC */
5205 if ( val & OHCI_RHS_OCIC )
5206 pThis->RootHub.status &= ~OHCI_RHS_OCIC;
5207
5208 /* SetGlobalPower */
5209 if ( val & OHCI_RHS_LPSC )
5210 {
5211 unsigned i;
5212 Log2(("ohci: global power up\n"));
5213 for (i = 0; i < OHCI_NDP_CFG(pThis); i++)
5214 ohciR3RhPortPower(&pThisCC->RootHub, i, true /* power up */);
5215 }
5216
5217 /* ClearGlobalPower */
5218 if ( val & OHCI_RHS_LPS )
5219 {
5220 unsigned i;
5221 Log2(("ohci: global power down\n"));
5222 for (i = 0; i < OHCI_NDP_CFG(pThis); i++)
5223 ohciR3RhPortPower(&pThisCC->RootHub, i, false /* power down */);
5224 }
5225
5226 if ( val & OHCI_RHS_DRWE )
5227 pThis->RootHub.status |= OHCI_RHS_DRWE;
5228
5229 if ( val & OHCI_RHS_CRWE )
5230 pThis->RootHub.status &= ~OHCI_RHS_DRWE;
5231
5232 chg = pThis->RootHub.status ^ old;
5233 Log2(("HcRhStatus_w(%#010x) => %sCGP=%d %sOCI=%d %sSRWE=%d %sSGP=%d %sOCIC=%d %sCRWE=%d\n",
5234 val,
5235 chg & 1 ? "*" : "", val & 1,
5236 (chg >> 1) & 1 ?"!!!": "", (val >> 1) & 1,
5237 (chg >> 15) & 1 ? "*" : "", (val >> 15) & 1,
5238 (chg >> 16) & 1 ? "*" : "", (val >> 16) & 1,
5239 (chg >> 17) & 1 ? "*" : "", (val >> 17) & 1,
5240 (chg >> 31) & 1 ? "*" : "", (val >> 31) & 1));
5241 RT_NOREF(pDevIns, iReg);
5242 return VINF_SUCCESS;
5243#else /* !IN_RING3 */
5244 RT_NOREF(pDevIns, pThis, iReg, val);
5245 return VINF_IOM_R3_MMIO_WRITE;
5246#endif /* !IN_RING3 */
5247}
5248
5249/**
5250 * Read the HcRhPortStatus register of a port.
5251 */
5252static VBOXSTRICTRC HcRhPortStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5253{
5254 const unsigned i = iReg - 21;
5255 uint32_t val = pThis->RootHub.aPorts[i].fReg | OHCI_PORT_PPS; /* PortPowerStatus: see todo on power in _w function. */
5256 if (val & OHCI_PORT_PRS)
5257 {
5258#ifdef IN_RING3
5259 RTThreadYield();
5260#else
5261 Log2(("HcRhPortStatus_r: yield -> VINF_IOM_R3_MMIO_READ\n"));
5262 return VINF_IOM_R3_MMIO_READ;
5263#endif
5264 }
5265 if (val & (OHCI_PORT_PRS | OHCI_PORT_CLEAR_CHANGE_MASK))
5266 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",
5267 i, val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 8) & 1, (val >> 9) & 1,
5268 (val >> 16) & 1, (val >> 17) & 1, (val >> 18) & 1, (val >> 19) & 1, (val >> 20) & 1));
5269 *pu32Value = val;
5270 RT_NOREF(pDevIns);
5271 return VINF_SUCCESS;
5272}
5273
5274#ifdef IN_RING3
5275/**
5276 * Completion callback for the vusb_dev_reset() operation.
5277 * @thread EMT.
5278 */
5279static DECLCALLBACK(void) ohciR3PortResetDone(PVUSBIDEVICE pDev, uint32_t uPort, int rc, void *pvUser)
5280{
5281 RT_NOREF(pDev);
5282
5283 Assert(uPort >= 1);
5284 PPDMDEVINS pDevIns = (PPDMDEVINS)pvUser;
5285 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5286 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5287 POHCIHUBPORT pPort = &pThis->RootHub.aPorts[uPort - 1];
5288
5289 if (RT_SUCCESS(rc))
5290 {
5291 /*
5292 * Successful reset.
5293 */
5294 Log2(("ohciR3PortResetDone: Reset completed.\n"));
5295 pPort->fReg &= ~(OHCI_PORT_PRS | OHCI_PORT_PSS | OHCI_PORT_PSSC);
5296 pPort->fReg |= OHCI_PORT_PES | OHCI_PORT_PRSC;
5297 }
5298 else
5299 {
5300 /* desperate measures. */
5301 if ( pPort->fAttached
5302 && VUSBIRhDevGetState(pThisCC->RootHub.pIRhConn, uPort) == VUSB_DEVICE_STATE_ATTACHED)
5303 {
5304 /*
5305 * Damn, something weird happened during reset. We'll pretend the user did an
5306 * incredible fast reconnect or something. (probably not gonna work)
5307 */
5308 Log2(("ohciR3PortResetDone: The reset failed (rc=%Rrc)!!! Pretending reconnect at the speed of light.\n", rc));
5309 pPort->fReg = OHCI_PORT_CCS | OHCI_PORT_CSC;
5310 }
5311 else
5312 {
5313 /*
5314 * The device have / will be disconnected.
5315 */
5316 Log2(("ohciR3PortResetDone: Disconnected (rc=%Rrc)!!!\n", rc));
5317 pPort->fReg &= ~(OHCI_PORT_PRS | OHCI_PORT_PSS | OHCI_PORT_PSSC | OHCI_PORT_PRSC);
5318 pPort->fReg |= OHCI_PORT_CSC;
5319 }
5320 }
5321
5322 /* Raise roothub status change interrupt. */
5323 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5324}
5325
5326/**
5327 * Sets a flag in a port status register but only set it if a device is
5328 * connected, if not set ConnectStatusChange flag to force HCD to reevaluate
5329 * connect status.
5330 *
5331 * @returns true if device was connected and the flag was cleared.
5332 */
5333static bool ohciR3RhPortSetIfConnected(PPDMDEVINS pDevIns, POHCI pThis, int iPort, uint32_t fValue)
5334{
5335 /*
5336 * Writing a 0 has no effect
5337 */
5338 if (fValue == 0)
5339 return false;
5340
5341 /*
5342 * If CurrentConnectStatus is cleared we set ConnectStatusChange.
5343 */
5344 if (!(pThis->RootHub.aPorts[iPort].fReg & OHCI_PORT_CCS))
5345 {
5346 pThis->RootHub.aPorts[iPort].fReg |= OHCI_PORT_CSC;
5347 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5348 return false;
5349 }
5350
5351 bool fRc = !(pThis->RootHub.aPorts[iPort].fReg & fValue);
5352
5353 /* set the bit */
5354 pThis->RootHub.aPorts[iPort].fReg |= fValue;
5355
5356 return fRc;
5357}
5358#endif /* IN_RING3 */
5359
5360/**
5361 * Write to the HcRhPortStatus register of a port.
5362 */
5363static VBOXSTRICTRC HcRhPortStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5364{
5365#ifdef IN_RING3
5366 const unsigned i = iReg - 21;
5367 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5368 POHCIHUBPORT p = &pThis->RootHub.aPorts[i];
5369 uint32_t old_state = p->fReg;
5370
5371# ifdef LOG_ENABLED
5372 /*
5373 * Log it.
5374 */
5375 static const char *apszCmdNames[32] =
5376 {
5377 "ClearPortEnable", "SetPortEnable", "SetPortSuspend", "!!!ClearSuspendStatus",
5378 "SetPortReset", "!!!5", "!!!6", "!!!7",
5379 "SetPortPower", "ClearPortPower", "!!!10", "!!!11",
5380 "!!!12", "!!!13", "!!!14", "!!!15",
5381 "ClearCSC", "ClearPESC", "ClearPSSC", "ClearOCIC",
5382 "ClearPRSC", "!!!21", "!!!22", "!!!23",
5383 "!!!24", "!!!25", "!!!26", "!!!27",
5384 "!!!28", "!!!29", "!!!30", "!!!31"
5385 };
5386 Log2(("HcRhPortStatus_w(%#010x): port %u:", val, i));
5387 for (unsigned j = 0; j < RT_ELEMENTS(apszCmdNames); j++)
5388 if (val & (1 << j))
5389 Log2((" %s", apszCmdNames[j]));
5390 Log2(("\n"));
5391# endif
5392
5393 /* Write to clear any of the change bits: CSC, PESC, PSSC, OCIC and PRSC */
5394 if (val & OHCI_PORT_CLEAR_CHANGE_MASK)
5395 p->fReg &= ~(val & OHCI_PORT_CLEAR_CHANGE_MASK);
5396
5397 if (val & OHCI_PORT_CLRPE)
5398 {
5399 p->fReg &= ~OHCI_PORT_PES;
5400 Log2(("HcRhPortStatus_w(): port %u: DISABLE\n", i));
5401 }
5402
5403 if (ohciR3RhPortSetIfConnected(pDevIns, pThis, i, val & OHCI_PORT_PES))
5404 Log2(("HcRhPortStatus_w(): port %u: ENABLE\n", i));
5405
5406 if (ohciR3RhPortSetIfConnected(pDevIns, pThis, i, val & OHCI_PORT_PSS))
5407 Log2(("HcRhPortStatus_w(): port %u: SUSPEND - not implemented correctly!!!\n", i));
5408
5409 if (val & OHCI_PORT_PRS)
5410 {
5411 if (ohciR3RhPortSetIfConnected(pDevIns, pThis, i, val & OHCI_PORT_PRS))
5412 {
5413 PVM pVM = PDMDevHlpGetVM(pDevIns);
5414 p->fReg &= ~OHCI_PORT_PRSC;
5415 VUSBIRhDevReset(pThisCC->RootHub.pIRhConn, OHCI_PORT_2_VUSB_PORT(i), false /* don't reset on linux */,
5416 ohciR3PortResetDone, pDevIns, pVM);
5417 }
5418 else if (p->fReg & OHCI_PORT_PRS)
5419 {
5420 /* the guest is getting impatient. */
5421 Log2(("HcRhPortStatus_w(): port %u: Impatient guest!\n", i));
5422 RTThreadYield();
5423 }
5424 }
5425
5426 if (!(pThis->RootHub.desc_a & OHCI_RHA_NPS))
5427 {
5428 /** @todo To implement per-device power-switching
5429 * we need to check PortPowerControlMask to make
5430 * sure it isn't gang powered
5431 */
5432 if (val & OHCI_PORT_CLRPP)
5433 ohciR3RhPortPower(&pThisCC->RootHub, i, false /* power down */);
5434 if (val & OHCI_PORT_PPS)
5435 ohciR3RhPortPower(&pThisCC->RootHub, i, true /* power up */);
5436 }
5437
5438 /** @todo r=frank: ClearSuspendStatus. Timing? */
5439 if (val & OHCI_PORT_CLRSS)
5440 {
5441 ohciR3RhPortPower(&pThisCC->RootHub, i, true /* power up */);
5442 pThis->RootHub.aPorts[i].fReg &= ~OHCI_PORT_PSS;
5443 pThis->RootHub.aPorts[i].fReg |= OHCI_PORT_PSSC;
5444 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5445 }
5446
5447 if (p->fReg != old_state)
5448 {
5449 uint32_t res = p->fReg;
5450 uint32_t chg = res ^ old_state; NOREF(chg);
5451 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",
5452 val, i,
5453 chg & 1 ? "*" : "", res & 1,
5454 (chg >> 1) & 1 ? "*" : "", (res >> 1) & 1,
5455 (chg >> 2) & 1 ? "*" : "", (res >> 2) & 1,
5456 (chg >> 3) & 1 ? "*" : "", (res >> 3) & 1,
5457 (chg >> 4) & 1 ? "*" : "", (res >> 4) & 1,
5458 (chg >> 8) & 1 ? "*" : "", (res >> 8) & 1,
5459 (chg >> 9) & 1 ? "*" : "", (res >> 9) & 1,
5460 (chg >> 16) & 1 ? "*" : "", (res >> 16) & 1,
5461 (chg >> 17) & 1 ? "*" : "", (res >> 17) & 1,
5462 (chg >> 18) & 1 ? "*" : "", (res >> 18) & 1,
5463 (chg >> 19) & 1 ? "*" : "", (res >> 19) & 1,
5464 (chg >> 20) & 1 ? "*" : "", (res >> 20) & 1));
5465 }
5466 RT_NOREF(pDevIns);
5467 return VINF_SUCCESS;
5468#else /* !IN_RING3 */
5469 RT_NOREF(pDevIns, pThis, iReg, val);
5470 return VINF_IOM_R3_MMIO_WRITE;
5471#endif /* !IN_RING3 */
5472}
5473
5474/**
5475 * Register descriptor table
5476 */
5477static const OHCIOPREG g_aOpRegs[] =
5478{
5479 { "HcRevision", HcRevision_r, HcRevision_w }, /* 0 */
5480 { "HcControl", HcControl_r, HcControl_w }, /* 1 */
5481 { "HcCommandStatus", HcCommandStatus_r, HcCommandStatus_w }, /* 2 */
5482 { "HcInterruptStatus", HcInterruptStatus_r, HcInterruptStatus_w }, /* 3 */
5483 { "HcInterruptEnable", HcInterruptEnable_r, HcInterruptEnable_w }, /* 4 */
5484 { "HcInterruptDisable", HcInterruptDisable_r, HcInterruptDisable_w }, /* 5 */
5485 { "HcHCCA", HcHCCA_r, HcHCCA_w }, /* 6 */
5486 { "HcPeriodCurrentED", HcPeriodCurrentED_r, HcPeriodCurrentED_w }, /* 7 */
5487 { "HcControlHeadED", HcControlHeadED_r, HcControlHeadED_w }, /* 8 */
5488 { "HcControlCurrentED", HcControlCurrentED_r, HcControlCurrentED_w }, /* 9 */
5489 { "HcBulkHeadED", HcBulkHeadED_r, HcBulkHeadED_w }, /* 10 */
5490 { "HcBulkCurrentED", HcBulkCurrentED_r, HcBulkCurrentED_w }, /* 11 */
5491 { "HcDoneHead", HcDoneHead_r, HcDoneHead_w }, /* 12 */
5492 { "HcFmInterval", HcFmInterval_r, HcFmInterval_w }, /* 13 */
5493 { "HcFmRemaining", HcFmRemaining_r, HcFmRemaining_w }, /* 14 */
5494 { "HcFmNumber", HcFmNumber_r, HcFmNumber_w }, /* 15 */
5495 { "HcPeriodicStart", HcPeriodicStart_r, HcPeriodicStart_w }, /* 16 */
5496 { "HcLSThreshold", HcLSThreshold_r, HcLSThreshold_w }, /* 17 */
5497 { "HcRhDescriptorA", HcRhDescriptorA_r, HcRhDescriptorA_w }, /* 18 */
5498 { "HcRhDescriptorB", HcRhDescriptorB_r, HcRhDescriptorB_w }, /* 19 */
5499 { "HcRhStatus", HcRhStatus_r, HcRhStatus_w }, /* 20 */
5500
5501 /* The number of port status register depends on the definition
5502 * of OHCI_NDP_MAX macro
5503 */
5504 { "HcRhPortStatus[0]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 21 */
5505 { "HcRhPortStatus[1]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 22 */
5506 { "HcRhPortStatus[2]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 23 */
5507 { "HcRhPortStatus[3]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 24 */
5508 { "HcRhPortStatus[4]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 25 */
5509 { "HcRhPortStatus[5]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 26 */
5510 { "HcRhPortStatus[6]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 27 */
5511 { "HcRhPortStatus[7]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 28 */
5512 { "HcRhPortStatus[8]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 29 */
5513 { "HcRhPortStatus[9]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 30 */
5514 { "HcRhPortStatus[10]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 31 */
5515 { "HcRhPortStatus[11]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 32 */
5516 { "HcRhPortStatus[12]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 33 */
5517 { "HcRhPortStatus[13]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 34 */
5518 { "HcRhPortStatus[14]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 35 */
5519};
5520
5521/* Quick way to determine how many op regs are valid. Since at least one port must
5522 * be configured (and no more than 15), there will be between 22 and 36 registers.
5523 */
5524#define NUM_OP_REGS(pohci) (21 + OHCI_NDP_CFG(pohci))
5525
5526AssertCompile(RT_ELEMENTS(g_aOpRegs) > 21);
5527AssertCompile(RT_ELEMENTS(g_aOpRegs) <= 36);
5528
5529/**
5530 * @callback_method_impl{FNIOMMMIONEWREAD}
5531 */
5532static DECLCALLBACK(VBOXSTRICTRC) ohciMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
5533{
5534 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5535 RT_NOREF(pvUser);
5536
5537 /* Paranoia: Assert that IOMMMIO_FLAGS_READ_DWORD works. */
5538 AssertReturn(cb == sizeof(uint32_t), VERR_INTERNAL_ERROR_3);
5539 AssertReturn(!(off & 0x3), VERR_INTERNAL_ERROR_4);
5540
5541 /*
5542 * Validate the register and call the read operator.
5543 */
5544 VBOXSTRICTRC rc;
5545 const uint32_t iReg = off >> 2;
5546 if (iReg < NUM_OP_REGS(pThis))
5547 rc = g_aOpRegs[iReg].pfnRead(pDevIns, pThis, iReg, (uint32_t *)pv);
5548 else
5549 {
5550 Log(("ohci: Trying to read register %u/%u!!!\n", iReg, NUM_OP_REGS(pThis)));
5551 rc = VINF_IOM_MMIO_UNUSED_FF;
5552 }
5553 return rc;
5554}
5555
5556
5557/**
5558 * @callback_method_impl{FNIOMMMIONEWWRITE}
5559 */
5560static DECLCALLBACK(VBOXSTRICTRC) ohciMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
5561{
5562 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5563 RT_NOREF(pvUser);
5564
5565 /* Paranoia: Assert that IOMMMIO_FLAGS_WRITE_DWORD_ZEROED works. */
5566 AssertReturn(cb == sizeof(uint32_t), VERR_INTERNAL_ERROR_3);
5567 AssertReturn(!(off & 0x3), VERR_INTERNAL_ERROR_4);
5568
5569 /*
5570 * Validate the register and call the read operator.
5571 */
5572 VBOXSTRICTRC rc;
5573 const uint32_t iReg = off >> 2;
5574 if (iReg < NUM_OP_REGS(pThis))
5575 rc = g_aOpRegs[iReg].pfnWrite(pDevIns, pThis, iReg, *(uint32_t const *)pv);
5576 else
5577 {
5578 Log(("ohci: Trying to write to register %u/%u!!!\n", iReg, NUM_OP_REGS(pThis)));
5579 rc = VINF_SUCCESS;
5580 }
5581 return rc;
5582}
5583
5584#ifdef IN_RING3
5585
5586/**
5587 * Saves the state of the OHCI device.
5588 *
5589 * @returns VBox status code.
5590 * @param pDevIns The device instance.
5591 * @param pSSM The handle to save the state to.
5592 */
5593static DECLCALLBACK(int) ohciR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5594{
5595 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5596 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5597 LogFlow(("ohciR3SaveExec:\n"));
5598
5599 int rc = pDevIns->pHlpR3->pfnSSMPutStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
5600 AssertRCReturn(rc, rc);
5601
5602 /* Save the periodic frame rate so we can we can tell if the bus was started or not when restoring. */
5603 return pDevIns->pHlpR3->pfnSSMPutU32(pSSM, VUSBIRhGetPeriodicFrameRate(pThisCC->RootHub.pIRhConn));
5604}
5605
5606
5607/**
5608 * Loads the state of the OHCI device.
5609 *
5610 * @returns VBox status code.
5611 * @param pDevIns The device instance.
5612 * @param pSSM The handle to the saved state.
5613 * @param uVersion The data unit version number.
5614 * @param uPass The data pass.
5615 */
5616static DECLCALLBACK(int) ohciR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5617{
5618 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5619 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5620 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5621 int rc;
5622 LogFlow(("ohciR3LoadExec:\n"));
5623
5624 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
5625
5626 if (uVersion >= OHCI_SAVED_STATE_VERSION_EOF_TIMER)
5627 rc = pHlp->pfnSSMGetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
5628 else if (uVersion == OHCI_SAVED_STATE_VERSION_8PORTS)
5629 rc = pHlp->pfnSSMGetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields8Ports[0], NULL);
5630 else
5631 AssertMsgFailedReturn(("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
5632 AssertRCReturn(rc, rc);
5633
5634 /*
5635 * Get the frame rate / started indicator.
5636 *
5637 * For older versions there is a timer saved here. We'll skip it and deduce
5638 * the periodic frame rate from the host controller functional state.
5639 */
5640 if (uVersion > OHCI_SAVED_STATE_VERSION_EOF_TIMER)
5641 {
5642 rc = pHlp->pfnSSMGetU32(pSSM, &pThisCC->uRestoredPeriodicFrameRate);
5643 AssertRCReturn(rc, rc);
5644 }
5645 else
5646 {
5647 rc = pHlp->pfnSSMSkipToEndOfUnit(pSSM);
5648 AssertRCReturn(rc, rc);
5649
5650 uint32_t fHcfs = pThis->ctl & OHCI_CTL_HCFS;
5651 switch (fHcfs)
5652 {
5653 case OHCI_USB_OPERATIONAL:
5654 case OHCI_USB_RESUME:
5655 pThisCC->uRestoredPeriodicFrameRate = OHCI_DEFAULT_TIMER_FREQ;
5656 break;
5657 default:
5658 pThisCC->uRestoredPeriodicFrameRate = 0;
5659 break;
5660 }
5661 }
5662
5663 /** @todo could we restore the frame rate here instead of in ohciR3Resume? */
5664 return VINF_SUCCESS;
5665}
5666
5667
5668/**
5669 * Reset notification.
5670 *
5671 * @returns VBox status code.
5672 * @param pDevIns The device instance data.
5673 */
5674static DECLCALLBACK(void) ohciR3Reset(PPDMDEVINS pDevIns)
5675{
5676 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5677 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5678 LogFlow(("ohciR3Reset:\n"));
5679
5680 /*
5681 * There is no distinction between cold boot, warm reboot and software reboots,
5682 * all of these are treated as cold boots. We are also doing the initialization
5683 * job of a BIOS or SMM driver.
5684 *
5685 * Important: Don't confuse UsbReset with hardware reset. Hardware reset is
5686 * just one way of getting into the UsbReset state.
5687 */
5688 ohciR3DoReset(pDevIns, pThis, pThisCC, OHCI_USB_RESET, true /* reset devices */);
5689}
5690
5691
5692/**
5693 * Resume notification.
5694 *
5695 * @returns VBox status code.
5696 * @param pDevIns The device instance data.
5697 */
5698static DECLCALLBACK(void) ohciR3Resume(PPDMDEVINS pDevIns)
5699{
5700 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5701 LogFlowFunc(("\n"));
5702
5703 /* Restart the frame thread if it was active when the loaded state was saved. */
5704 uint32_t uRestoredPeriodicFR = pThisCC->uRestoredPeriodicFrameRate;
5705 pThisCC->uRestoredPeriodicFrameRate = 0;
5706 if (uRestoredPeriodicFR)
5707 {
5708 LogFlowFunc(("Bus was active, enable periodic frame processing (rate: %u)\n", uRestoredPeriodicFR));
5709 int rc = pThisCC->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThisCC->RootHub.pIRhConn, uRestoredPeriodicFR);
5710 AssertRC(rc);
5711 }
5712}
5713
5714
5715/**
5716 * Info handler, device version. Dumps OHCI control registers.
5717 *
5718 * @param pDevIns Device instance which registered the info.
5719 * @param pHlp Callback functions for doing output.
5720 * @param pszArgs Argument string. Optional and specific to the handler.
5721 */
5722static DECLCALLBACK(void) ohciR3InfoRegs(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
5723{
5724 RT_NOREF(pszArgs);
5725 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5726 uint32_t val, ctl, status;
5727
5728 /* Control register */
5729 ctl = pThis->ctl;
5730 pHlp->pfnPrintf(pHlp, "HcControl: %08x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
5731 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
5732 (ctl >> 9) & 1, (ctl >> 10) & 1);
5733
5734 /* Command status register */
5735 status = pThis->status;
5736 pHlp->pfnPrintf(pHlp, "HcCommandStatus: %08x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
5737 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3);
5738
5739 /* Interrupt status register */
5740 val = pThis->intr_status;
5741 pHlp->pfnPrintf(pHlp, "HcInterruptStatus: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
5742 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5743 (val >> 6) & 1, (val >> 30) & 1);
5744
5745 /* Interrupt enable register */
5746 val = pThis->intr;
5747 pHlp->pfnPrintf(pHlp, "HcInterruptEnable: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
5748 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5749 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1);
5750
5751 /* HCCA address register */
5752 pHlp->pfnPrintf(pHlp, "HcHCCA: %08x\n", pThis->hcca);
5753
5754 /* Current periodic ED register */
5755 pHlp->pfnPrintf(pHlp, "HcPeriodCurrentED: %08x\n", pThis->per_cur);
5756
5757 /* Control ED registers */
5758 pHlp->pfnPrintf(pHlp, "HcControlHeadED: %08x\n", pThis->ctrl_head);
5759 pHlp->pfnPrintf(pHlp, "HcControlCurrentED: %08x\n", pThis->ctrl_cur);
5760
5761 /* Bulk ED registers */
5762 pHlp->pfnPrintf(pHlp, "HcBulkHeadED: %08x\n", pThis->bulk_head);
5763 pHlp->pfnPrintf(pHlp, "HcBulkCurrentED: %08x\n", pThis->bulk_cur);
5764
5765 /* Done head register */
5766 pHlp->pfnPrintf(pHlp, "HcDoneHead: %08x\n", pThis->done);
5767
5768 /* Done head register */
5769 pHlp->pfnPrintf(pHlp, "HcDoneHead: %08x\n", pThis->done);
5770
5771 /* Root hub descriptor A */
5772 val = pThis->RootHub.desc_a;
5773 pHlp->pfnPrintf(pHlp, "HcRhDescriptorA: %08x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTPGT=%d\n",
5774 val, (uint8_t)val, (val >> 8) & 1, (val >> 9) & 1, (val >> 10) & 1, (val >> 11) & 1, (val >> 12) & 1, (uint8_t)(val >> 24));
5775
5776 /* Root hub descriptor B */
5777 val = pThis->RootHub.desc_b;
5778 pHlp->pfnPrintf(pHlp, "HcRhDescriptorB: %08x - DR=%#04x PPCM=%#04x\n", val, (uint16_t)val, (uint16_t)(val >> 16));
5779
5780 /* Root hub status register */
5781 val = pThis->RootHub.status;
5782 pHlp->pfnPrintf(pHlp, "HcRhStatus: %08x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n\n",
5783 val, val & 1, (val >> 1) & 1, (val >> 15) & 1, (val >> 16) & 1, (val >> 17) & 1, (val >> 31) & 1);
5784
5785 /* Port status registers */
5786 for (unsigned i = 0; i < OHCI_NDP_CFG(pThis); ++i)
5787 {
5788 val = pThis->RootHub.aPorts[i].fReg;
5789 pHlp->pfnPrintf(pHlp, "HcRhPortStatus%02d: CCS=%d PES =%d PSS =%d POCI=%d PRS =%d PPS=%d LSDA=%d\n"
5790 " %08x - CSC=%d PESC=%d PSSC=%d OCIC=%d PRSC=%d\n",
5791 i, val & 1, (val >> 1) & 1, (val >> 2) & 1,(val >> 3) & 1, (val >> 4) & 1, (val >> 8) & 1, (val >> 9) & 1,
5792 val, (val >> 16) & 1, (val >> 17) & 1, (val >> 18) & 1, (val >> 19) & 1, (val >> 20) & 1);
5793 }
5794}
5795
5796
5797/**
5798 * Destruct a device instance.
5799 *
5800 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
5801 * resources can be freed correctly.
5802 *
5803 * @returns VBox status code.
5804 * @param pDevIns The device instance data.
5805 */
5806static DECLCALLBACK(int) ohciR3Destruct(PPDMDEVINS pDevIns)
5807{
5808 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5809 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5810 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5811
5812 if (RTCritSectIsInitialized(&pThisCC->CritSect))
5813 RTCritSectDelete(&pThisCC->CritSect);
5814 PDMDevHlpCritSectDelete(pDevIns, &pThis->CsIrq);
5815
5816 /*
5817 * Tear down the per endpoint in-flight tracking...
5818 */
5819
5820 return VINF_SUCCESS;
5821}
5822
5823
5824/**
5825 * @interface_method_impl{PDMDEVREG,pfnConstruct,OHCI constructor}
5826 */
5827static DECLCALLBACK(int) ohciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5828{
5829 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5830 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5831 POHCIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCIR3);
5832
5833 /*
5834 * Init instance data.
5835 */
5836 pThisCC->pDevInsR3 = pDevIns;
5837
5838 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
5839 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
5840
5841 PDMPciDevSetVendorId(pPciDev, 0x106b);
5842 PDMPciDevSetDeviceId(pPciDev, 0x003f);
5843 PDMPciDevSetClassProg(pPciDev, 0x10); /* OHCI */
5844 PDMPciDevSetClassSub(pPciDev, 0x03);
5845 PDMPciDevSetClassBase(pPciDev, 0x0c);
5846 PDMPciDevSetInterruptPin(pPciDev, 0x01);
5847#ifdef VBOX_WITH_MSI_DEVICES
5848 PDMPciDevSetStatus(pPciDev, VBOX_PCI_STATUS_CAP_LIST);
5849 PDMPciDevSetCapabilityList(pPciDev, 0x80);
5850#endif
5851
5852 pThisCC->RootHub.pOhci = pThis;
5853 pThisCC->RootHub.IBase.pfnQueryInterface = ohciR3RhQueryInterface;
5854 pThisCC->RootHub.IRhPort.pfnGetAvailablePorts = ohciR3RhGetAvailablePorts;
5855 pThisCC->RootHub.IRhPort.pfnGetUSBVersions = ohciR3RhGetUSBVersions;
5856 pThisCC->RootHub.IRhPort.pfnAttach = ohciR3RhAttach;
5857 pThisCC->RootHub.IRhPort.pfnDetach = ohciR3RhDetach;
5858 pThisCC->RootHub.IRhPort.pfnReset = ohciR3RhReset;
5859 pThisCC->RootHub.IRhPort.pfnXferCompletion = ohciR3RhXferCompletion;
5860 pThisCC->RootHub.IRhPort.pfnXferError = ohciR3RhXferError;
5861 pThisCC->RootHub.IRhPort.pfnStartFrame = ohciR3StartFrame;
5862 pThisCC->RootHub.IRhPort.pfnFrameRateChanged = ohciR3FrameRateChanged;
5863
5864 /* USB LED */
5865 pThisCC->RootHub.Led.u32Magic = PDMLED_MAGIC;
5866 pThisCC->RootHub.ILeds.pfnQueryStatusLed = ohciR3RhQueryStatusLed;
5867
5868
5869 /*
5870 * Read configuration.
5871 */
5872 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Ports", "");
5873
5874 /* Number of ports option. */
5875 uint32_t cPorts;
5876 int rc = pDevIns->pHlpR3->pfnCFGMQueryU32Def(pCfg, "Ports", &cPorts, OHCI_NDP_DEFAULT);
5877 if (RT_FAILURE(rc))
5878 return PDMDEV_SET_ERROR(pDevIns, rc, N_("OHCI configuration error: failed to read Ports as integer"));
5879 if (cPorts == 0 || cPorts > OHCI_NDP_MAX)
5880 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5881 N_("OHCI configuration error: Ports must be in range [%u,%u]"),
5882 1, OHCI_NDP_MAX);
5883
5884 /* Store the configured NDP; it will be used everywhere else from now on. */
5885 pThis->RootHub.desc_a = cPorts;
5886
5887 /*
5888 * Register PCI device and I/O region.
5889 */
5890 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
5891 if (RT_FAILURE(rc))
5892 return rc;
5893
5894#ifdef VBOX_WITH_MSI_DEVICES
5895 PDMMSIREG MsiReg;
5896 RT_ZERO(MsiReg);
5897 MsiReg.cMsiVectors = 1;
5898 MsiReg.iMsiCapOffset = 0x80;
5899 MsiReg.iMsiNextOffset = 0x00;
5900 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
5901 if (RT_FAILURE(rc))
5902 {
5903 PDMPciDevSetCapabilityList(pPciDev, 0x0);
5904 /* That's OK, we can work without MSI */
5905 }
5906#endif
5907
5908 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 0, 4096, PCI_ADDRESS_SPACE_MEM, ohciMmioWrite, ohciMmioRead, NULL /*pvUser*/,
5909 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED
5910 | IOMMMIO_FLAGS_DBGSTOP_ON_COMPLICATED_WRITE, "USB OHCI", &pThis->hMmio);
5911 AssertRCReturn(rc, rc);
5912
5913 /*
5914 * Register the saved state data unit.
5915 */
5916 rc = PDMDevHlpSSMRegisterEx(pDevIns, OHCI_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
5917 NULL, NULL, NULL,
5918 NULL, ohciR3SaveExec, NULL,
5919 NULL, ohciR3LoadExec, NULL);
5920 AssertRCReturn(rc, rc);
5921
5922 /*
5923 * Attach to the VBox USB RootHub Driver on LUN #0.
5924 */
5925 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->RootHub.IBase, &pThisCC->RootHub.pIBase, "RootHub");
5926 if (RT_FAILURE(rc))
5927 {
5928 AssertMsgFailed(("Configuration error: No roothub driver attached to LUN #0!\n"));
5929 return rc;
5930 }
5931 pThisCC->RootHub.pIRhConn = PDMIBASE_QUERY_INTERFACE(pThisCC->RootHub.pIBase, VUSBIROOTHUBCONNECTOR);
5932 AssertMsgReturn(pThisCC->RootHub.pIRhConn,
5933 ("Configuration error: The driver doesn't provide the VUSBIROOTHUBCONNECTOR interface!\n"),
5934 VERR_PDM_MISSING_INTERFACE);
5935
5936 /*
5937 * Attach status driver (optional).
5938 */
5939 PPDMIBASE pBase;
5940 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->RootHub.IBase, &pBase, "Status Port");
5941 if (RT_SUCCESS(rc))
5942 pThisCC->RootHub.pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5943 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
5944 {
5945 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
5946 return rc;
5947 }
5948
5949 /* Set URB parameters. */
5950 rc = VUSBIRhSetUrbParams(pThisCC->RootHub.pIRhConn, sizeof(VUSBURBHCIINT), sizeof(VUSBURBHCITDINT));
5951 if (RT_FAILURE(rc))
5952 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("OHCI: Failed to set URB parameters"));
5953
5954 /*
5955 * Take down the virtual clock frequence for use in ohciR3FrameRateChanged().
5956 * (Used to be a timer, thus the name.)
5957 */
5958 pThis->u64TimerHz = PDMDevHlpTMTimeVirtGetFreq(pDevIns);
5959
5960 /*
5961 * Critical sections: explain
5962 */
5963 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CsIrq, RT_SRC_POS, "OHCI#%uIrq", iInstance);
5964 if (RT_FAILURE(rc))
5965 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("OHCI: Failed to create critical section"));
5966
5967 rc = RTCritSectInit(&pThisCC->CritSect);
5968 if (RT_FAILURE(rc))
5969 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("OHCI: Failed to create critical section"));
5970
5971 /*
5972 * Do a hardware reset.
5973 */
5974 ohciR3DoReset(pDevIns, pThis, pThisCC, OHCI_USB_RESET, false /* don't reset devices */);
5975
5976# ifdef VBOX_WITH_STATISTICS
5977 /*
5978 * Register statistics.
5979 */
5980 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledIsocUrbs, STAMTYPE_COUNTER, "CanceledIsocUrbs", STAMUNIT_OCCURENCES, "Detected canceled isochronous URBs.");
5981 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledGenUrbs, STAMTYPE_COUNTER, "CanceledGenUrbs", STAMUNIT_OCCURENCES, "Detected canceled general URBs.");
5982 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatDroppedUrbs, STAMTYPE_COUNTER, "DroppedUrbs", STAMUNIT_OCCURENCES, "Dropped URBs (endpoint halted, or URB canceled).");
5983# endif
5984
5985 /*
5986 * Register debugger info callbacks.
5987 */
5988 PDMDevHlpDBGFInfoRegister(pDevIns, "ohci", "OHCI control registers.", ohciR3InfoRegs);
5989
5990# if 0/*def DEBUG_bird*/
5991// g_fLogInterruptEPs = true;
5992 g_fLogControlEPs = true;
5993 g_fLogBulkEPs = true;
5994# endif
5995
5996 return VINF_SUCCESS;
5997}
5998
5999#else /* !IN_RING3 */
6000
6001/**
6002 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
6003 */
6004static DECLCALLBACK(int) ohciRZConstruct(PPDMDEVINS pDevIns)
6005{
6006 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
6007 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
6008
6009 int rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, ohciMmioWrite, ohciMmioRead, NULL /*pvUser*/);
6010 AssertRCReturn(rc, rc);
6011
6012 return VINF_SUCCESS;
6013}
6014
6015#endif /* !IN_RING3 */
6016
6017const PDMDEVREG g_DeviceOHCI =
6018{
6019 /* .u32version = */ PDM_DEVREG_VERSION,
6020 /* .uReserved0 = */ 0,
6021 /* .szName = */ "usb-ohci",
6022 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
6023 /* .fClass = */ PDM_DEVREG_CLASS_BUS_USB,
6024 /* .cMaxInstances = */ ~0U,
6025 /* .uSharedVersion = */ 42,
6026 /* .cbInstanceShared = */ sizeof(OHCI),
6027 /* .cbInstanceCC = */ sizeof(OHCICC),
6028 /* .cbInstanceRC = */ 0,
6029 /* .cMaxPciDevices = */ 1,
6030 /* .cMaxMsixVectors = */ 0,
6031 /* .pszDescription = */ "OHCI USB controller.\n",
6032#if defined(IN_RING3)
6033 /* .pszRCMod = */ "VBoxDDRC.rc",
6034 /* .pszR0Mod = */ "VBoxDDR0.r0",
6035 /* .pfnConstruct = */ ohciR3Construct,
6036 /* .pfnDestruct = */ ohciR3Destruct,
6037 /* .pfnRelocate = */ NULL,
6038 /* .pfnMemSetup = */ NULL,
6039 /* .pfnPowerOn = */ NULL,
6040 /* .pfnReset = */ ohciR3Reset,
6041 /* .pfnSuspend = */ NULL,
6042 /* .pfnResume = */ ohciR3Resume,
6043 /* .pfnAttach = */ NULL,
6044 /* .pfnDetach = */ NULL,
6045 /* .pfnQueryInterface = */ NULL,
6046 /* .pfnInitComplete = */ NULL,
6047 /* .pfnPowerOff = */ NULL,
6048 /* .pfnSoftReset = */ NULL,
6049 /* .pfnReserved0 = */ NULL,
6050 /* .pfnReserved1 = */ NULL,
6051 /* .pfnReserved2 = */ NULL,
6052 /* .pfnReserved3 = */ NULL,
6053 /* .pfnReserved4 = */ NULL,
6054 /* .pfnReserved5 = */ NULL,
6055 /* .pfnReserved6 = */ NULL,
6056 /* .pfnReserved7 = */ NULL,
6057#elif defined(IN_RING0)
6058 /* .pfnEarlyConstruct = */ NULL,
6059 /* .pfnConstruct = */ ohciRZConstruct,
6060 /* .pfnDestruct = */ NULL,
6061 /* .pfnFinalDestruct = */ NULL,
6062 /* .pfnRequest = */ NULL,
6063 /* .pfnReserved0 = */ NULL,
6064 /* .pfnReserved1 = */ NULL,
6065 /* .pfnReserved2 = */ NULL,
6066 /* .pfnReserved3 = */ NULL,
6067 /* .pfnReserved4 = */ NULL,
6068 /* .pfnReserved5 = */ NULL,
6069 /* .pfnReserved6 = */ NULL,
6070 /* .pfnReserved7 = */ NULL,
6071#elif defined(IN_RC)
6072 /* .pfnConstruct = */ ohciRZConstruct,
6073 /* .pfnReserved0 = */ NULL,
6074 /* .pfnReserved1 = */ NULL,
6075 /* .pfnReserved2 = */ NULL,
6076 /* .pfnReserved3 = */ NULL,
6077 /* .pfnReserved4 = */ NULL,
6078 /* .pfnReserved5 = */ NULL,
6079 /* .pfnReserved6 = */ NULL,
6080 /* .pfnReserved7 = */ NULL,
6081#else
6082# error "Not in IN_RING3, IN_RING0 or IN_RC!"
6083#endif
6084 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
6085};
6086
6087#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