VirtualBox

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

Last change on this file since 101556 was 99739, checked in by vboxsync, 20 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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