VirtualBox

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

Last change on this file since 81534 was 81534, checked in by vboxsync, 5 years ago

DevOHCI: Style nits. bugref:9218

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

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