VirtualBox

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

Last change on this file since 106842 was 106281, checked in by vboxsync, 3 months ago

DevOHCI: Quick fix for deadlock when pfnAbortEpByAddr is called. Ran into it while windows 2000 was shutting down (almost finished, screen all blue, but never powering off). There is a question whether we should re-reade the endpoint descriptor after regaining the lock, but the guest is free to change it regardless of whether we're holding the lock or not, so I guess it makes little difference.

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

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