VirtualBox

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

Last change on this file since 104932 was 104604, checked in by vboxsync, 7 months ago

Devices/USB/DevOHCI.cpp: Fix parfait warning about unused chg variable which is only used in LOG_ENABLED builds, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 223.9 KB
Line 
1/* $Id: DevOHCI.cpp 104604 2024-05-13 15:20:33Z vboxsync $ */
2/** @file
3 * DevOHCI - Open Host Controller Interface for USB.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/** @page pg_dev_ohci OHCI - Open Host Controller Interface Emulation.
29 *
30 * This component implements an OHCI USB controller. It is split roughly in
31 * to two main parts, the first part implements the register level
32 * specification of USB OHCI and the second part maintains the root hub (which
33 * is an integrated component of the device).
34 *
35 * The OHCI registers are used for the usual stuff like enabling and disabling
36 * interrupts. Since the USB time is divided in to 1ms frames and various
37 * interrupts may need to be triggered at frame boundary time, a timer-based
38 * approach was taken. Whenever the bus is enabled ohci->eof_timer will be set.
39 *
40 * The actual USB transfers are stored in main memory (along with endpoint and
41 * transfer descriptors). The ED's for all the control and bulk endpoints are
42 * found by consulting the HcControlHeadED and HcBulkHeadED registers
43 * respectively. Interrupt ED's are different, they are found by looking
44 * in the HCCA (another communication area in main memory).
45 *
46 * At the start of every frame (in function ohci_sof) we traverse all enabled
47 * ED lists and queue up as many transfers as possible. No attention is paid
48 * to control/bulk service ratios or bandwidth requirements since our USB
49 * could conceivably contain a dozen high speed busses so this would
50 * artificially limit the performance.
51 *
52 * Once we have a transfer ready to go (in function ohciR3ServiceTd) we
53 * allocate an URB on the stack, fill in all the relevant fields and submit
54 * it using the VUSBIRhSubmitUrb function. The roothub device and the virtual
55 * USB core code (vusb.c) coordinates everything else from this point onwards.
56 *
57 * When the URB has been successfully handed to the lower level driver, our
58 * prepare callback gets called and we can remove the TD from the ED transfer
59 * list. This stops us queueing it twice while it completes.
60 * bird: no, we don't remove it because that confuses the guest! (=> crashes)
61 *
62 * Completed URBs are reaped at the end of every frame (in function
63 * ohci_frame_boundary). Our completion routine makes use of the ED and TD
64 * fields in the URB to store the physical addresses of the descriptors so
65 * that they may be modified in the roothub callbacks. Our completion
66 * routine (ohciR3RhXferCompletion) carries out a number of tasks:
67 * -# Retires the TD associated with the transfer, setting the
68 * relevant error code etc.
69 * -# Updates done-queue interrupt timer and potentially causes
70 * a writeback of the done-queue.
71 * -# If the transfer was device-to-host, we copy the data in to
72 * the host memory.
73 *
74 * 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 pThisCC->RootHub.pIRhConn->pfnAbortEpByAddr(pThisCC->RootHub.pIRhConn, uAddr, uEndPt, enmDir);
3891 }
3892 }
3893 }
3894
3895 /* Trivial loop detection. */
3896 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
3897 break;
3898 /* Proceed to the next endpoint. */
3899 EdAddr = Ed.NextED & ED_PTR_MASK;
3900 }
3901
3902# ifdef LOG_ENABLED
3903 if (g_fLogBulkEPs)
3904 ohciR3DumpEdList(pDevIns, pThisCC, pThis->bulk_head, "Bulk after ", true);
3905# endif
3906}
3907
3908
3909/**
3910 * Abort outstanding transfers on the bulk list.
3911 *
3912 * If the guest disabled bulk list processing, we must abort any outstanding transfers
3913 * (that is, cancel in-flight URBs associated with the list). This is required because
3914 * there may be outstanding read URBs that will never get a response from the device
3915 * and would block further communication.
3916 */
3917static void ohciR3UndoBulkList(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
3918{
3919# ifdef LOG_ENABLED
3920 if (g_fLogBulkEPs)
3921 ohciR3DumpEdList(pDevIns, pThisCC, pThis->bulk_head, "Bulk before", true);
3922 if (pThis->bulk_cur)
3923 Log(("ohciR3UndoBulkList: bulk_cur=%#010x before list processing!!! HCD has positioned us!!!\n", pThis->bulk_cur));
3924# endif
3925
3926 /* This flag follows OHCI_STATUS_BLF, but BLF doesn't change when list processing is disabled. */
3927 pThis->fBulkNeedsCleaning = false;
3928
3929 uint32_t EdAddr = pThis->bulk_head;
3930 uint32_t cIterations = 256;
3931 while (EdAddr
3932 && (cIterations-- > 0))
3933 {
3934 OHCIED Ed;
3935
3936 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
3937 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3938 if (ohciR3IsEdPresent(&Ed))
3939 {
3940 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3941 if (ohciR3IsTdInFlight(pThisCC, TdAddr))
3942 {
3943 LogFlow(("ohciR3UndoBulkList: Ed=%#010RX32 Ed.TailP=%#010RX32 UNDO\n", EdAddr, Ed.TailP));
3944 /* First we need to determine the transfer direction, which may fail(!). */
3945 uint8_t uAddr = Ed.hwinfo & ED_HWINFO_FUNCTION;
3946 uint8_t uEndPt = (Ed.hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3947 VUSBDIRECTION enmDir = ohciR3GetDirection(pDevIns, pThis, pThisCC, &Ed);
3948 if (enmDir != VUSBDIRECTION_INVALID)
3949 {
3950 pThisCC->RootHub.pIRhConn->pfnAbortEpByAddr(pThisCC->RootHub.pIRhConn, uAddr, uEndPt, enmDir);
3951 }
3952 }
3953 }
3954
3955 /* Trivial loop detection. */
3956 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
3957 break;
3958 /* Proceed to the next endpoint. */
3959 EdAddr = Ed.NextED & ED_PTR_MASK;
3960 }
3961}
3962
3963
3964/**
3965 * Services the control list.
3966 *
3967 * The control list has complex URB assembling, but that's taken
3968 * care of at VUSB level (unlike the other transfer types).
3969 */
3970static void ohciR3ServiceCtrlList(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
3971{
3972# ifdef LOG_ENABLED
3973 if (g_fLogControlEPs)
3974 ohciR3DumpEdList(pDevIns, pThisCC, pThis->ctrl_head, "Ctrl before", true);
3975 if (pThis->ctrl_cur)
3976 Log(("ohciR3ServiceCtrlList: ctrl_cur=%010x before list processing!!! HCD have positioned us!!!\n", pThis->ctrl_cur));
3977# endif
3978
3979 /*
3980 * ", HC will start processing the list and will set ControlListFilled to 0"
3981 * - We've simplified and are always starting at the head of the list and working
3982 * our way thru to the end each time.
3983 */
3984 pThis->status &= ~OHCI_STATUS_CLF;
3985 pThis->ctrl_cur = 0;
3986
3987 uint32_t EdAddr = pThis->ctrl_head;
3988 uint32_t cIterations = 256;
3989 while ( EdAddr
3990 && (pThis->ctl & OHCI_CTL_CLE)
3991 && (cIterations-- > 0))
3992 {
3993 OHCIED Ed;
3994
3995 /* Bail if previous processing ended up in the unrecoverable error state. */
3996 if (pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR)
3997 break;
3998
3999 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
4000 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
4001 if (ohciR3IsEdReady(&Ed))
4002 {
4003# if 1
4004 /*
4005 * Control TDs depends on order and stage. Only one can be in-flight
4006 * at any given time. OTOH, some stages are completed immediately,
4007 * so we process the list until we've got a head which is in-flight
4008 * or reach the end of the list.
4009 */
4010 do
4011 {
4012 if ( !ohciR3ServiceHeadTd(pDevIns, pThis, pThisCC, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control")
4013 || ohciR3IsTdInFlight(pThisCC, Ed.HeadP & ED_PTR_MASK))
4014 {
4015 pThis->status |= OHCI_STATUS_CLF;
4016 break;
4017 }
4018 ohciR3ReadEd(pDevIns, EdAddr, &Ed); /* It might have been updated on URB completion. */
4019 } while (ohciR3IsEdReady(&Ed));
4020# else
4021 /* Simplistic, for debugging. */
4022 ohciR3ServiceHeadTd(pThis, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control");
4023 pThis->status |= OHCI_STATUS_CLF;
4024# endif
4025 }
4026
4027 /* Trivial loop detection. */
4028 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
4029 break;
4030 /* Proceed to the next endpoint. */
4031 EdAddr = Ed.NextED & ED_PTR_MASK;
4032 }
4033
4034# ifdef LOG_ENABLED
4035 if (g_fLogControlEPs)
4036 ohciR3DumpEdList(pDevIns, pThisCC, pThis->ctrl_head, "Ctrl after ", true);
4037# endif
4038}
4039
4040
4041/**
4042 * Services the periodic list.
4043 *
4044 * On the interrupt portion of the periodic list we must reassemble URBs from multiple
4045 * TDs using heuristics derived from USB tracing done in the guests and guest source
4046 * code (when available).
4047 */
4048static void ohciR3ServicePeriodicList(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4049{
4050 /*
4051 * Read the list head from the HCCA.
4052 */
4053 const unsigned iList = pThis->HcFmNumber % OHCI_HCCA_NUM_INTR;
4054 uint32_t EdAddr;
4055 ohciR3GetDWords(pDevIns, pThis->hcca + iList * sizeof(EdAddr), &EdAddr, 1);
4056
4057# ifdef LOG_ENABLED
4058 const uint32_t EdAddrHead = EdAddr;
4059 if (g_fLogInterruptEPs)
4060 {
4061 char sz[48];
4062 RTStrPrintf(sz, sizeof(sz), "Int%02x before", iList);
4063 ohciR3DumpEdList(pDevIns, pThisCC, EdAddrHead, sz, true);
4064 }
4065# endif
4066
4067 /*
4068 * Iterate the endpoint list.
4069 */
4070 unsigned cIterations = 128;
4071 while (EdAddr
4072 && (pThis->ctl & OHCI_CTL_PLE)
4073 && (cIterations-- > 0))
4074 {
4075 OHCIED Ed;
4076
4077 /* Bail if previous processing ended up in the unrecoverable error state. */
4078 if (pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR)
4079 break;
4080
4081 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
4082 if (ohciR3IsEdReady(&Ed))
4083 {
4084 /*
4085 * "There is no separate head pointer of isochronous transfers. The first
4086 * isochronous Endpoint Descriptor simply links to the last interrupt
4087 * Endpoint Descriptor."
4088 */
4089 if (!(Ed.hwinfo & ED_HWINFO_ISO))
4090 {
4091 /*
4092 * Presently we will only process the head URB on an interrupt endpoint.
4093 */
4094 ohciR3ServiceHeadTdMultiple(pDevIns, pThis, pThisCC, VUSBXFERTYPE_INTR, &Ed, EdAddr, "Periodic");
4095 }
4096 else if (pThis->ctl & OHCI_CTL_IE)
4097 {
4098 /*
4099 * Presently only the head ITD.
4100 */
4101 ohciR3ServiceIsochronousEndpoint(pDevIns, pThis, pThisCC, &Ed, EdAddr);
4102 }
4103 else
4104 break;
4105 }
4106 else
4107 {
4108 if (Ed.hwinfo & ED_HWINFO_SKIP)
4109 {
4110 Log3(("ohciR3ServicePeriodicList: Ed=%#010RX32 Ed.TailP=%#010RX32 SKIP\n", EdAddr, Ed.TailP));
4111 /* If the ED is in 'skip' state, no transactions on it are allowed and we must
4112 * cancel outstanding URBs, if any.
4113 * First we need to determine the transfer direction, which may fail(!).
4114 */
4115 uint8_t uAddr = Ed.hwinfo & ED_HWINFO_FUNCTION;
4116 uint8_t uEndPt = (Ed.hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
4117 VUSBDIRECTION enmDir = ohciR3GetDirection(pDevIns, pThis, pThisCC, &Ed);
4118 if (enmDir != VUSBDIRECTION_INVALID)
4119 {
4120 pThisCC->RootHub.pIRhConn->pfnAbortEpByAddr(pThisCC->RootHub.pIRhConn, uAddr, uEndPt, enmDir);
4121 }
4122 }
4123 }
4124 /* Trivial loop detection. */
4125 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
4126 break;
4127 /* Proceed to the next endpoint. */
4128 EdAddr = Ed.NextED & ED_PTR_MASK;
4129 }
4130
4131# ifdef LOG_ENABLED
4132 if (g_fLogInterruptEPs)
4133 {
4134 char sz[48];
4135 RTStrPrintf(sz, sizeof(sz), "Int%02x after ", iList);
4136 ohciR3DumpEdList(pDevIns, pThisCC, EdAddrHead, sz, true);
4137 }
4138# endif
4139}
4140
4141
4142/**
4143 * Update the HCCA.
4144 *
4145 * @param pDevIns The device instance.
4146 * @param pThis The OHCI controller instance data, shared edition.
4147 * @param pThisCC The OHCI controller instance data, ring-3 edition.
4148 */
4149static void ohciR3UpdateHCCA(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4150{
4151 OCHIHCCA hcca;
4152 ohciR3PhysRead(pDevIns, pThis->hcca + OHCI_HCCA_OFS, &hcca, sizeof(hcca));
4153
4154 hcca.frame = RT_H2LE_U16((uint16_t)pThis->HcFmNumber);
4155 hcca.pad = 0;
4156
4157 bool fWriteDoneHeadInterrupt = false;
4158 if ( pThis->dqic == 0
4159 && (pThis->intr_status & OHCI_INTR_WRITE_DONE_HEAD) == 0)
4160 {
4161 uint32_t done = pThis->done;
4162
4163 if (pThis->intr_status & ~( OHCI_INTR_MASTER_INTERRUPT_ENABLED | OHCI_INTR_OWNERSHIP_CHANGE
4164 | OHCI_INTR_WRITE_DONE_HEAD) )
4165 done |= 0x1;
4166
4167 hcca.done = RT_H2LE_U32(done);
4168 pThis->done = 0;
4169 pThis->dqic = 0x7;
4170
4171 Log(("ohci: Writeback Done (%#010x) on frame %#x (age %#x)\n", hcca.done,
4172 pThis->HcFmNumber, pThis->HcFmNumber - pThisCC->u32FmDoneQueueTail));
4173# ifdef LOG_ENABLED
4174 ohciR3DumpTdQueue(pDevIns, pThisCC, hcca.done & ED_PTR_MASK, "DoneQueue");
4175# endif
4176 Assert(RT_OFFSETOF(OCHIHCCA, done) == 4);
4177# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
4178 ohciR3InDoneQueueZap(pThisCC);
4179# endif
4180 fWriteDoneHeadInterrupt = true;
4181 }
4182
4183 Log3(("ohci: Updating HCCA on frame %#x\n", pThis->HcFmNumber));
4184 ohciR3PhysWriteMeta(pDevIns, pThis->hcca + OHCI_HCCA_OFS, (uint8_t *)&hcca, sizeof(hcca));
4185 if (fWriteDoneHeadInterrupt)
4186 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_WRITE_DONE_HEAD);
4187 RT_NOREF(pThisCC);
4188}
4189
4190
4191/**
4192 * Go over the in-flight URB list and cancel any URBs that are no longer in use.
4193 * This occurs when the host removes EDs or TDs from the lists and we don't notice
4194 * the sKip bit. Such URBs must be promptly canceled, otherwise there is a risk
4195 * they might "steal" data destined for another URB.
4196 */
4197static void ohciR3CancelOrphanedURBs(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4198{
4199 bool fValidHCCA = !( pThis->hcca >= OHCI_HCCA_MASK
4200 || pThis->hcca < ~OHCI_HCCA_MASK);
4201 unsigned i, cLeft;
4202 int j;
4203 uint32_t EdAddr;
4204 PVUSBURB pUrb;
4205
4206 /* If the HCCA is not currently valid, or there are no in-flight URBs,
4207 * there's nothing to do.
4208 */
4209 if (!fValidHCCA || !pThisCC->cInFlight)
4210 return;
4211
4212 /* Initially mark all in-flight URBs as inactive. */
4213 for (i = 0, cLeft = pThisCC->cInFlight; cLeft && i < RT_ELEMENTS(pThisCC->aInFlight); i++)
4214 {
4215 if (pThisCC->aInFlight[i].pUrb)
4216 {
4217 pThisCC->aInFlight[i].fInactive = true;
4218 cLeft--;
4219 }
4220 }
4221 Assert(cLeft == 0);
4222
4223# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
4224 /* Get hcca data to minimize calls to ohciR3GetDWords/PDMDevHlpPCIPhysRead. */
4225 uint32_t au32HCCA[OHCI_HCCA_NUM_INTR];
4226 ohciR3GetDWords(pDevIns, pThis->hcca, au32HCCA, OHCI_HCCA_NUM_INTR);
4227# endif
4228
4229 /* Go over all bulk/control/interrupt endpoint lists; any URB found in these lists
4230 * is marked as active again.
4231 */
4232 for (i = 0; i < OHCI_HCCA_NUM_INTR + 2; i++)
4233 {
4234 switch (i)
4235 {
4236 case OHCI_HCCA_NUM_INTR:
4237 EdAddr = pThis->bulk_head;
4238 break;
4239 case OHCI_HCCA_NUM_INTR + 1:
4240 EdAddr = pThis->ctrl_head;
4241 break;
4242 default:
4243# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
4244 EdAddr = au32HCCA[i];
4245# else
4246 ohciR3GetDWords(pDevIns, pThis->hcca + i * sizeof(EdAddr), &EdAddr, 1);
4247# endif
4248 break;
4249 }
4250
4251 unsigned cIterED = 128;
4252 while ( EdAddr
4253 && (cIterED-- > 0))
4254 {
4255 OHCIED Ed;
4256 OHCITD Td;
4257
4258 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
4259 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
4260 uint32_t TailP = Ed.TailP & ED_PTR_MASK;
4261 unsigned cIterTD = 0;
4262 if ( !(Ed.hwinfo & ED_HWINFO_SKIP)
4263 && (TdAddr != TailP))
4264 {
4265# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
4266 ohciR3PhysReadCacheInvalidate(&pThisCC->CacheTD);
4267# endif
4268 do
4269 {
4270 ohciR3ReadTd(pDevIns, TdAddr, &Td);
4271 j = ohciR3InFlightFind(pThisCC, TdAddr);
4272 if (j > -1)
4273 pThisCC->aInFlight[j].fInactive = false;
4274 TdAddr = Td.NextTD & ED_PTR_MASK;
4275 /* See @bugref{8125}.
4276 * Sometimes the ED is changed by the guest between ohciR3ReadEd above and here.
4277 * Then the code reads TD pointed by the new TailP, which is not allowed.
4278 * Luckily Windows guests have Td.NextTD = 0 in the tail TD.
4279 * Also having a real TD at 0 is very unlikely.
4280 * So do not continue.
4281 */
4282 if (TdAddr == 0)
4283 break;
4284 /* Failsafe for temporarily looped lists. */
4285 if (++cIterTD == 128)
4286 break;
4287 } while (TdAddr != (Ed.TailP & ED_PTR_MASK));
4288 }
4289 /* Trivial loop detection. */
4290 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
4291 break;
4292 /* Proceed to the next endpoint. */
4293 EdAddr = Ed.NextED & ED_PTR_MASK;
4294 }
4295 }
4296
4297 /* In-flight URBs still marked as inactive are not used anymore and need
4298 * to be canceled.
4299 */
4300 for (i = 0, cLeft = pThisCC->cInFlight; cLeft && i < RT_ELEMENTS(pThisCC->aInFlight); i++)
4301 {
4302 if (pThisCC->aInFlight[i].pUrb)
4303 {
4304 cLeft--;
4305 pUrb = pThisCC->aInFlight[i].pUrb;
4306 if ( pThisCC->aInFlight[i].fInactive
4307 && pUrb->enmState == VUSBURBSTATE_IN_FLIGHT
4308 && pUrb->enmType != VUSBXFERTYPE_CTRL)
4309 pThisCC->RootHub.pIRhConn->pfnCancelUrbsEp(pThisCC->RootHub.pIRhConn, pUrb);
4310 }
4311 }
4312 Assert(cLeft == 0);
4313}
4314
4315
4316/**
4317 * Generate a Start-Of-Frame event, and set a timer for End-Of-Frame.
4318 */
4319static void ohciR3StartOfFrame(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4320{
4321# ifdef LOG_ENABLED
4322 const uint32_t status_old = pThis->status;
4323# endif
4324
4325 /*
4326 * Update HcFmRemaining.FRT and update start of frame time.
4327 */
4328 pThis->frt = pThis->fit;
4329 pThis->SofTime += pThis->cTicksPerFrame;
4330
4331 /*
4332 * Check that the HCCA address isn't bogus. Linux 2.4.x is known to start
4333 * the bus with a hcca of 0 to work around problem with a specific controller.
4334 */
4335 bool fValidHCCA = !( pThis->hcca >= OHCI_HCCA_MASK
4336 || pThis->hcca < ~OHCI_HCCA_MASK);
4337
4338# if 1
4339 /*
4340 * Update the HCCA.
4341 * Should be done after SOF but before HC read first ED in this frame.
4342 */
4343 if (fValidHCCA)
4344 ohciR3UpdateHCCA(pDevIns, pThis, pThisCC);
4345# endif
4346
4347 /* "After writing to HCCA, HC will set SF in HcInterruptStatus" - guest isn't executing, so ignore the order! */
4348 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_START_OF_FRAME);
4349
4350 if (pThis->fno)
4351 {
4352 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_FRAMENUMBER_OVERFLOW);
4353 pThis->fno = 0;
4354 }
4355
4356 /* If the HCCA address is invalid, we're quitting here to avoid doing something which cannot be reported to the HCD. */
4357 if (!fValidHCCA)
4358 {
4359 Log(("ohciR3StartOfFrame: skipping hcca part because hcca=%RX32 (our 'valid' range: %RX32-%RX32)\n",
4360 pThis->hcca, ~OHCI_HCCA_MASK, OHCI_HCCA_MASK));
4361 return;
4362 }
4363
4364 /*
4365 * Periodic EPs.
4366 */
4367 if (pThis->ctl & OHCI_CTL_PLE)
4368 ohciR3ServicePeriodicList(pDevIns, pThis, pThisCC);
4369
4370 /*
4371 * Control EPs.
4372 */
4373 if ( (pThis->ctl & OHCI_CTL_CLE)
4374 && (pThis->status & OHCI_STATUS_CLF) )
4375 ohciR3ServiceCtrlList(pDevIns, pThis, pThisCC);
4376
4377 /*
4378 * Bulk EPs.
4379 */
4380 if ( (pThis->ctl & OHCI_CTL_BLE)
4381 && (pThis->status & OHCI_STATUS_BLF))
4382 ohciR3ServiceBulkList(pDevIns, pThis, pThisCC);
4383 else if ((pThis->status & OHCI_STATUS_BLF)
4384 && pThis->fBulkNeedsCleaning)
4385 ohciR3UndoBulkList(pDevIns, pThis, pThisCC); /* If list disabled but not empty, abort endpoints. */
4386
4387# if 0
4388 /*
4389 * Update the HCCA after processing the lists and everything. A bit experimental.
4390 *
4391 * ASSUME the guest won't be very upset if a TD is completed, retired and handed
4392 * back immediately. The idea is to be able to retire the data and/or status stages
4393 * of a control transfer together with the setup stage, thus saving a frame. This
4394 * behaviour is should be perfectly ok, since the setup (and maybe data) stages
4395 * have already taken at least one frame to complete.
4396 *
4397 * But, when implementing the first synchronous virtual USB devices, we'll have to
4398 * verify that the guest doesn't choke when having a TD returned in the same frame
4399 * as it was submitted.
4400 */
4401 ohciR3UpdateHCCA(pThis);
4402# endif
4403
4404# ifdef LOG_ENABLED
4405 if (pThis->status ^ status_old)
4406 {
4407 uint32_t val = pThis->status;
4408 uint32_t chg = val ^ status_old; NOREF(chg);
4409 Log2(("ohciR3StartOfFrame: HcCommandStatus=%#010x: %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
4410 val,
4411 chg & RT_BIT(0) ? "*" : "", val & 1,
4412 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
4413 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4414 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4415 chg & (3<<16)? "*" : "", (val >> 16) & 3));
4416 }
4417# endif
4418}
4419
4420
4421/**
4422 * Updates the HcFmNumber and FNO registers.
4423 */
4424static void ohciR3BumpFrameNumber(POHCI pThis)
4425{
4426 const uint16_t u16OldFmNumber = pThis->HcFmNumber++;
4427 if ((u16OldFmNumber ^ pThis->HcFmNumber) & RT_BIT(15))
4428 pThis->fno = 1;
4429}
4430
4431
4432/**
4433 * Callback for periodic frame processing.
4434 */
4435static DECLCALLBACK(bool) ohciR3StartFrame(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameNo)
4436{
4437 RT_NOREF(u32FrameNo);
4438 POHCICC pThisCC = VUSBIROOTHUBPORT_2_OHCI(pInterface);
4439 PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
4440 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
4441
4442 ohciR3Lock(pThisCC);
4443
4444 /* Reset idle detection flag */
4445 pThis->fIdle = true;
4446
4447# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
4448 physReadStatsReset(&g_PhysReadState);
4449# endif
4450
4451 if (!(pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR))
4452 {
4453 /* Frame boundary, so do EOF stuff here. */
4454 ohciR3BumpFrameNumber(pThis);
4455 if ( (pThis->dqic != 0x7) && (pThis->dqic != 0))
4456 pThis->dqic--;
4457
4458 /* Clean up any URBs that have been removed. */
4459 ohciR3CancelOrphanedURBs(pDevIns, pThis, pThisCC);
4460
4461 /* Start the next frame. */
4462 ohciR3StartOfFrame(pDevIns, pThis, pThisCC);
4463 }
4464
4465# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
4466 physReadStatsPrint(&g_PhysReadState);
4467# endif
4468
4469 ohciR3Unlock(pThisCC);
4470 return pThis->fIdle;
4471}
4472
4473
4474/**
4475 * @interface_method_impl{VUSBIROOTHUBPORT,pfnFrameRateChanged}
4476 */
4477static DECLCALLBACK(void) ohciR3FrameRateChanged(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameRate)
4478{
4479 POHCICC pThisCC = VUSBIROOTHUBPORT_2_OHCI(pInterface);
4480 PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
4481 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
4482
4483 Assert(u32FrameRate <= OHCI_DEFAULT_TIMER_FREQ);
4484
4485 pThis->cTicksPerFrame = pThis->u64TimerHz / u32FrameRate;
4486 if (!pThis->cTicksPerFrame)
4487 pThis->cTicksPerFrame = 1;
4488 pThis->cTicksPerUsbTick = pThis->u64TimerHz >= VUSB_BUS_HZ ? pThis->u64TimerHz / VUSB_BUS_HZ : 1;
4489}
4490
4491
4492/**
4493 * Start sending SOF tokens across the USB bus, lists are processed in
4494 * next frame
4495 */
4496static void ohciR3BusStart(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4497{
4498 pThisCC->RootHub.pIRhConn->pfnPowerOn(pThisCC->RootHub.pIRhConn);
4499 pThis->dqic = 0x7;
4500
4501 Log(("ohci: Bus started\n"));
4502
4503 pThis->SofTime = PDMDevHlpTMTimeVirtGet(pDevIns);
4504 int rc = pThisCC->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThisCC->RootHub.pIRhConn, OHCI_DEFAULT_TIMER_FREQ);
4505 AssertRC(rc);
4506}
4507
4508
4509/**
4510 * Stop sending SOF tokens on the bus
4511 */
4512static void ohciR3BusStop(POHCICC pThisCC)
4513{
4514 int rc = pThisCC->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThisCC->RootHub.pIRhConn, 0);
4515 AssertRC(rc);
4516 pThisCC->RootHub.pIRhConn->pfnPowerOff(pThisCC->RootHub.pIRhConn);
4517}
4518
4519
4520/**
4521 * Move in to resume state
4522 */
4523static void ohciR3BusResume(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, bool fHardware)
4524{
4525 pThis->ctl &= ~OHCI_CTL_HCFS;
4526 pThis->ctl |= OHCI_USB_RESUME;
4527
4528 LogFunc(("fHardware=%RTbool RWE=%s\n",
4529 fHardware, (pThis->ctl & OHCI_CTL_RWE) ? "on" : "off"));
4530
4531 if (fHardware && (pThis->ctl & OHCI_CTL_RWE))
4532 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_RESUME_DETECT);
4533
4534 ohciR3BusStart(pDevIns, pThis, pThisCC);
4535}
4536
4537
4538/* Power a port up or down */
4539static void ohciR3RhPortPower(POHCIROOTHUBR3 pRh, unsigned iPort, bool fPowerUp)
4540{
4541 POHCIHUBPORT pPort = &pRh->aPorts[iPort];
4542 bool fOldPPS = !!(pPort->fReg & OHCI_PORT_PPS);
4543
4544 LogFlowFunc(("iPort=%u fPowerUp=%RTbool\n", iPort, fPowerUp));
4545
4546 if (fPowerUp)
4547 {
4548 /* power up */
4549 if (pPort->fAttached)
4550 pPort->fReg |= OHCI_PORT_CCS;
4551 if (pPort->fReg & OHCI_PORT_CCS)
4552 pPort->fReg |= OHCI_PORT_PPS;
4553 if (pPort->fAttached && !fOldPPS)
4554 VUSBIRhDevPowerOn(pRh->pIRhConn, OHCI_PORT_2_VUSB_PORT(iPort));
4555 }
4556 else
4557 {
4558 /* power down */
4559 pPort->fReg &= ~(OHCI_PORT_PPS | OHCI_PORT_CCS | OHCI_PORT_PSS | OHCI_PORT_PRS);
4560 if (pPort->fAttached && fOldPPS)
4561 VUSBIRhDevPowerOff(pRh->pIRhConn, OHCI_PORT_2_VUSB_PORT(iPort));
4562 }
4563}
4564
4565#endif /* IN_RING3 */
4566
4567/**
4568 * Read the HcRevision register.
4569 */
4570static VBOXSTRICTRC HcRevision_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4571{
4572 RT_NOREF(pDevIns, pThis, iReg);
4573 Log2(("HcRevision_r() -> 0x10\n"));
4574 *pu32Value = 0x10; /* OHCI revision 1.0, no emulation. */
4575 return VINF_SUCCESS;
4576}
4577
4578/**
4579 * Write to the HcRevision register.
4580 */
4581static VBOXSTRICTRC HcRevision_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t u32Value)
4582{
4583 RT_NOREF(pDevIns, pThis, iReg, u32Value);
4584 Log2(("HcRevision_w(%#010x) - denied\n", u32Value));
4585 ASSERT_GUEST_MSG_FAILED(("Invalid operation!!! u32Value=%#010x\n", u32Value));
4586 return VINF_SUCCESS;
4587}
4588
4589/**
4590 * Read the HcControl register.
4591 */
4592static VBOXSTRICTRC HcControl_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4593{
4594 RT_NOREF(pDevIns, iReg);
4595 uint32_t ctl = pThis->ctl;
4596 Log2(("HcControl_r -> %#010x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
4597 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
4598 (ctl >> 9) & 1, (ctl >> 10) & 1));
4599 *pu32Value = ctl;
4600 return VINF_SUCCESS;
4601}
4602
4603/**
4604 * Write the HcControl register.
4605 */
4606static VBOXSTRICTRC HcControl_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4607{
4608 RT_NOREF(iReg);
4609
4610 /* log it. */
4611 uint32_t chg = pThis->ctl ^ val; NOREF(chg);
4612 Log2(("HcControl_w(%#010x) => %sCBSR=%d %sPLE=%d %sIE=%d %sCLE=%d %sBLE=%d %sHCFS=%#x %sIR=%d %sRWC=%d %sRWE=%d\n",
4613 val,
4614 chg & 3 ? "*" : "", val & 3,
4615 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4616 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4617 chg & RT_BIT(4) ? "*" : "", (val >> 4) & 1,
4618 chg & RT_BIT(5) ? "*" : "", (val >> 5) & 1,
4619 chg & (3 << 6)? "*" : "", (val >> 6) & 3,
4620 chg & RT_BIT(8) ? "*" : "", (val >> 8) & 1,
4621 chg & RT_BIT(9) ? "*" : "", (val >> 9) & 1,
4622 chg & RT_BIT(10) ? "*" : "", (val >> 10) & 1));
4623 if (val & ~0x07ff)
4624 Log2(("Unknown bits %#x are set!!!\n", val & ~0x07ff));
4625
4626 /* see what changed and take action on that. */
4627 uint32_t old_state = pThis->ctl & OHCI_CTL_HCFS;
4628 uint32_t new_state = val & OHCI_CTL_HCFS;
4629
4630#ifdef IN_RING3
4631 pThis->ctl = val;
4632 if (new_state != old_state)
4633 {
4634 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
4635 switch (new_state)
4636 {
4637 case OHCI_USB_OPERATIONAL:
4638 LogRel(("OHCI: USB Operational\n"));
4639 ohciR3BusStart(pDevIns, pThis, pThisCC);
4640 break;
4641 case OHCI_USB_SUSPEND:
4642 ohciR3BusStop(pThisCC);
4643 LogRel(("OHCI: USB Suspended\n"));
4644 break;
4645 case OHCI_USB_RESUME:
4646 LogRel(("OHCI: USB Resume\n"));
4647 ohciR3BusResume(pDevIns, pThis, pThisCC, false /* not hardware */);
4648 break;
4649 case OHCI_USB_RESET:
4650 {
4651 LogRel(("OHCI: USB Reset\n"));
4652 ohciR3BusStop(pThisCC);
4653 /** @todo This should probably do a real reset, but we don't implement
4654 * that correctly in the roothub reset callback yet. check it's
4655 * comments and argument for more details. */
4656 pThisCC->RootHub.pIRhConn->pfnReset(pThisCC->RootHub.pIRhConn, false /* don't do a real reset */);
4657 break;
4658 }
4659 }
4660 }
4661#else /* !IN_RING3 */
4662 RT_NOREF(pDevIns);
4663 if ( new_state != old_state )
4664 {
4665 Log2(("HcControl_w: state changed -> VINF_IOM_R3_MMIO_WRITE\n"));
4666 return VINF_IOM_R3_MMIO_WRITE;
4667 }
4668 pThis->ctl = val;
4669#endif /* !IN_RING3 */
4670
4671 return VINF_SUCCESS;
4672}
4673
4674/**
4675 * Read the HcCommandStatus register.
4676 */
4677static VBOXSTRICTRC HcCommandStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4678{
4679 uint32_t status = pThis->status;
4680 Log2(("HcCommandStatus_r() -> %#010x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
4681 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3));
4682 *pu32Value = status;
4683 RT_NOREF(pDevIns, iReg);
4684 return VINF_SUCCESS;
4685}
4686
4687/**
4688 * Write to the HcCommandStatus register.
4689 */
4690static VBOXSTRICTRC HcCommandStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4691{
4692 RT_NOREF(pDevIns, iReg);
4693
4694 /* log */
4695 uint32_t chg = pThis->status ^ val; NOREF(chg);
4696 Log2(("HcCommandStatus_w(%#010x) => %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
4697 val,
4698 chg & RT_BIT(0) ? "*" : "", val & 1,
4699 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
4700 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4701 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4702 chg & (3<<16)? "!!!":"", (pThis->status >> 16) & 3));
4703 if (val & ~0x0003000f)
4704 Log2(("Unknown bits %#x are set!!!\n", val & ~0x0003000f));
4705
4706 /* SOC is read-only */
4707 val = (val & ~OHCI_STATUS_SOC);
4708
4709#ifdef IN_RING3
4710 /* "bits written as '0' remain unchanged in the register" */
4711 pThis->status |= val;
4712 if (pThis->status & OHCI_STATUS_HCR)
4713 {
4714 LogRel(("OHCI: Software reset\n"));
4715 ohciR3DoReset(pDevIns, pThis, PDMDEVINS_2_DATA_CC(pDevIns, POHCICC), OHCI_USB_SUSPEND, false /* N/A */);
4716 }
4717#else
4718 if ((pThis->status | val) & OHCI_STATUS_HCR)
4719 {
4720 LogFlow(("HcCommandStatus_w: reset -> VINF_IOM_R3_MMIO_WRITE\n"));
4721 return VINF_IOM_R3_MMIO_WRITE;
4722 }
4723 pThis->status |= val;
4724#endif
4725 return VINF_SUCCESS;
4726}
4727
4728/**
4729 * Read the HcInterruptStatus register.
4730 */
4731static VBOXSTRICTRC HcInterruptStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4732{
4733 uint32_t val = pThis->intr_status;
4734 Log2(("HcInterruptStatus_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
4735 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4736 (val >> 6) & 1, (val >> 30) & 1));
4737 *pu32Value = val;
4738 RT_NOREF(pDevIns, iReg);
4739 return VINF_SUCCESS;
4740}
4741
4742/**
4743 * Write to the HcInterruptStatus register.
4744 */
4745static VBOXSTRICTRC HcInterruptStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4746{
4747 RT_NOREF(iReg);
4748
4749 uint32_t res = pThis->intr_status & ~val;
4750 uint32_t chg = pThis->intr_status ^ res; NOREF(chg);
4751
4752 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4753 if (rc != VINF_SUCCESS)
4754 return rc;
4755
4756 Log2(("HcInterruptStatus_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d\n",
4757 val,
4758 chg & RT_BIT(0) ? "*" : "", res & 1,
4759 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4760 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4761 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4762 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4763 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4764 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4765 chg & RT_BIT(30)? "*" : "", (res >> 30) & 1));
4766 if ( (val & ~0xc000007f)
4767 && val != 0xffffffff /* ignore clear-all-like requests from xp. */)
4768 Log2(("Unknown bits %#x are set!!!\n", val & ~0xc000007f));
4769
4770 /* "The Host Controller Driver may clear specific bits in this
4771 * register by writing '1' to bit positions to be cleared"
4772 */
4773 pThis->intr_status &= ~val;
4774 ohciUpdateInterruptLocked(pDevIns, pThis, "HcInterruptStatus_w");
4775 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
4776 return VINF_SUCCESS;
4777}
4778
4779/**
4780 * Read the HcInterruptEnable register
4781 */
4782static VBOXSTRICTRC HcInterruptEnable_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4783{
4784 uint32_t val = pThis->intr;
4785 Log2(("HcInterruptEnable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
4786 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4787 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
4788 *pu32Value = val;
4789 RT_NOREF(pDevIns, iReg);
4790 return VINF_SUCCESS;
4791}
4792
4793/**
4794 * Writes to the HcInterruptEnable register.
4795 */
4796static VBOXSTRICTRC HcInterruptEnable_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4797{
4798 RT_NOREF(iReg);
4799 uint32_t res = pThis->intr | val;
4800 uint32_t chg = pThis->intr ^ res; NOREF(chg);
4801
4802 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4803 if (rc != VINF_SUCCESS)
4804 return rc;
4805
4806 Log2(("HcInterruptEnable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
4807 val,
4808 chg & RT_BIT(0) ? "*" : "", res & 1,
4809 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4810 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4811 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4812 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4813 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4814 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4815 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
4816 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
4817 if (val & ~0xc000007f)
4818 Log2(("Uknown bits %#x are set!!!\n", val & ~0xc000007f));
4819
4820 pThis->intr |= val;
4821 ohciUpdateInterruptLocked(pDevIns, pThis, "HcInterruptEnable_w");
4822 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
4823 return VINF_SUCCESS;
4824}
4825
4826/**
4827 * Reads the HcInterruptDisable register.
4828 */
4829static VBOXSTRICTRC HcInterruptDisable_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4830{
4831#if 1 /** @todo r=bird: "On read, the current value of the HcInterruptEnable register is returned." */
4832 uint32_t val = pThis->intr;
4833#else /* old code. */
4834 uint32_t val = ~pThis->intr;
4835#endif
4836 Log2(("HcInterruptDisable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
4837 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4838 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
4839
4840 *pu32Value = val;
4841 RT_NOREF(pDevIns, iReg);
4842 return VINF_SUCCESS;
4843}
4844
4845/**
4846 * Writes to the HcInterruptDisable register.
4847 */
4848static VBOXSTRICTRC HcInterruptDisable_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4849{
4850 RT_NOREF(iReg);
4851 uint32_t res = pThis->intr & ~val;
4852 uint32_t chg = pThis->intr ^ res; NOREF(chg);
4853
4854 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4855 if (rc != VINF_SUCCESS)
4856 return rc;
4857
4858 Log2(("HcInterruptDisable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
4859 val,
4860 chg & RT_BIT(0) ? "*" : "", res & 1,
4861 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4862 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4863 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4864 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4865 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4866 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4867 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
4868 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
4869 /* Don't bitch about invalid bits here since it makes sense to disable
4870 * interrupts you don't know about. */
4871
4872 pThis->intr &= ~val;
4873 ohciUpdateInterruptLocked(pDevIns, pThis, "HcInterruptDisable_w");
4874 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
4875 return VINF_SUCCESS;
4876}
4877
4878/**
4879 * Read the HcHCCA register (Host Controller Communications Area physical address).
4880 */
4881static VBOXSTRICTRC HcHCCA_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4882{
4883 Log2(("HcHCCA_r() -> %#010x\n", pThis->hcca));
4884 *pu32Value = pThis->hcca;
4885 RT_NOREF(pDevIns, iReg);
4886 return VINF_SUCCESS;
4887}
4888
4889/**
4890 * Write to the HcHCCA register (Host Controller Communications Area physical address).
4891 */
4892static VBOXSTRICTRC HcHCCA_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t Value)
4893{
4894 Log2(("HcHCCA_w(%#010x) - old=%#010x new=%#010x\n", Value, pThis->hcca, Value & OHCI_HCCA_MASK));
4895 pThis->hcca = Value & OHCI_HCCA_MASK;
4896 RT_NOREF(pDevIns, iReg);
4897 return VINF_SUCCESS;
4898}
4899
4900/**
4901 * Read the HcPeriodCurrentED register.
4902 */
4903static VBOXSTRICTRC HcPeriodCurrentED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4904{
4905 Log2(("HcPeriodCurrentED_r() -> %#010x\n", pThis->per_cur));
4906 *pu32Value = pThis->per_cur;
4907 RT_NOREF(pDevIns, iReg);
4908 return VINF_SUCCESS;
4909}
4910
4911/**
4912 * Write to the HcPeriodCurrentED register.
4913 */
4914static VBOXSTRICTRC HcPeriodCurrentED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4915{
4916 Log(("HcPeriodCurrentED_w(%#010x) - old=%#010x new=%#010x (This is a read only register, only the linux guys don't respect that!)\n",
4917 val, pThis->per_cur, val & ~7));
4918 //AssertMsgFailed(("HCD (Host Controller Driver) should not write to HcPeriodCurrentED! val=%#010x (old=%#010x)\n", val, pThis->per_cur));
4919 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4920 pThis->per_cur = val & ~7;
4921 RT_NOREF(pDevIns, iReg);
4922 return VINF_SUCCESS;
4923}
4924
4925/**
4926 * Read the HcControlHeadED register.
4927 */
4928static VBOXSTRICTRC HcControlHeadED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4929{
4930 Log2(("HcControlHeadED_r() -> %#010x\n", pThis->ctrl_head));
4931 *pu32Value = pThis->ctrl_head;
4932 RT_NOREF(pDevIns, iReg);
4933 return VINF_SUCCESS;
4934}
4935
4936/**
4937 * Write to the HcControlHeadED register.
4938 */
4939static VBOXSTRICTRC HcControlHeadED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4940{
4941 Log2(("HcControlHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->ctrl_head, val & ~7));
4942 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4943 pThis->ctrl_head = val & ~7;
4944 RT_NOREF(pDevIns, iReg);
4945 return VINF_SUCCESS;
4946}
4947
4948/**
4949 * Read the HcControlCurrentED register.
4950 */
4951static VBOXSTRICTRC HcControlCurrentED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4952{
4953 Log2(("HcControlCurrentED_r() -> %#010x\n", pThis->ctrl_cur));
4954 *pu32Value = pThis->ctrl_cur;
4955 RT_NOREF(pDevIns, iReg);
4956 return VINF_SUCCESS;
4957}
4958
4959/**
4960 * Write to the HcControlCurrentED register.
4961 */
4962static VBOXSTRICTRC HcControlCurrentED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4963{
4964 Log2(("HcControlCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->ctrl_cur, val & ~7));
4965 AssertMsg(!(pThis->ctl & OHCI_CTL_CLE), ("Illegal write! HcControl.ControlListEnabled is set! val=%#010x\n", val));
4966 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4967 pThis->ctrl_cur = val & ~7;
4968 RT_NOREF(pDevIns, iReg);
4969 return VINF_SUCCESS;
4970}
4971
4972/**
4973 * Read the HcBulkHeadED register.
4974 */
4975static VBOXSTRICTRC HcBulkHeadED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4976{
4977 Log2(("HcBulkHeadED_r() -> %#010x\n", pThis->bulk_head));
4978 *pu32Value = pThis->bulk_head;
4979 RT_NOREF(pDevIns, iReg);
4980 return VINF_SUCCESS;
4981}
4982
4983/**
4984 * Write to the HcBulkHeadED register.
4985 */
4986static VBOXSTRICTRC HcBulkHeadED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4987{
4988 Log2(("HcBulkHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->bulk_head, val & ~7));
4989 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4990 pThis->bulk_head = val & ~7; /** @todo The ATI OHCI controller on my machine enforces 16-byte address alignment. */
4991 RT_NOREF(pDevIns, iReg);
4992 return VINF_SUCCESS;
4993}
4994
4995/**
4996 * Read the HcBulkCurrentED register.
4997 */
4998static VBOXSTRICTRC HcBulkCurrentED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4999{
5000 Log2(("HcBulkCurrentED_r() -> %#010x\n", pThis->bulk_cur));
5001 *pu32Value = pThis->bulk_cur;
5002 RT_NOREF(pDevIns, iReg);
5003 return VINF_SUCCESS;
5004}
5005
5006/**
5007 * Write to the HcBulkCurrentED register.
5008 */
5009static VBOXSTRICTRC HcBulkCurrentED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5010{
5011 Log2(("HcBulkCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->bulk_cur, val & ~7));
5012 AssertMsg(!(pThis->ctl & OHCI_CTL_BLE), ("Illegal write! HcControl.BulkListEnabled is set! val=%#010x\n", val));
5013 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
5014 pThis->bulk_cur = val & ~7;
5015 RT_NOREF(pDevIns, iReg);
5016 return VINF_SUCCESS;
5017}
5018
5019
5020/**
5021 * Read the HcDoneHead register.
5022 */
5023static VBOXSTRICTRC HcDoneHead_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5024{
5025 Log2(("HcDoneHead_r() -> 0x%#08x\n", pThis->done));
5026 *pu32Value = pThis->done;
5027 RT_NOREF(pDevIns, iReg);
5028 return VINF_SUCCESS;
5029}
5030
5031/**
5032 * Write to the HcDoneHead register.
5033 */
5034static VBOXSTRICTRC HcDoneHead_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5035{
5036 RT_NOREF(pDevIns, pThis, iReg, val);
5037 Log2(("HcDoneHead_w(0x%#08x) - denied!!!\n", val));
5038 /*AssertMsgFailed(("Illegal operation!!! val=%#010x\n", val)); - OS/2 does this */
5039 return VINF_SUCCESS;
5040}
5041
5042
5043/**
5044 * Read the HcFmInterval (Fm=Frame) register.
5045 */
5046static VBOXSTRICTRC HcFmInterval_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5047{
5048 uint32_t val = (pThis->fit << 31) | (pThis->fsmps << 16) | (pThis->fi);
5049 Log2(("HcFmInterval_r() -> 0x%#08x - FI=%d FSMPS=%d FIT=%d\n",
5050 val, val & 0x3fff, (val >> 16) & 0x7fff, val >> 31));
5051 *pu32Value = val;
5052 RT_NOREF(pDevIns, iReg);
5053 return VINF_SUCCESS;
5054}
5055
5056/**
5057 * Write to the HcFmInterval (Fm = Frame) register.
5058 */
5059static VBOXSTRICTRC HcFmInterval_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5060{
5061 RT_NOREF(pDevIns, iReg);
5062
5063 /* log */
5064 uint32_t chg = val ^ ((pThis->fit << 31) | (pThis->fsmps << 16) | pThis->fi); NOREF(chg);
5065 Log2(("HcFmInterval_w(%#010x) => %sFI=%d %sFSMPS=%d %sFIT=%d\n",
5066 val,
5067 chg & 0x00003fff ? "*" : "", val & 0x3fff,
5068 chg & 0x7fff0000 ? "*" : "", (val >> 16) & 0x7fff,
5069 chg >> 31 ? "*" : "", (val >> 31) & 1));
5070 if (pThis->fi != (val & OHCI_FMI_FI))
5071 {
5072 Log(("ohci: FrameInterval: %#010x -> %#010x\n", pThis->fi, val & OHCI_FMI_FI));
5073 AssertMsg(pThis->fit != ((val >> OHCI_FMI_FIT_SHIFT) & 1), ("HCD didn't toggle the FIT bit!!!\n"));
5074 }
5075
5076 /* update */
5077 pThis->fi = val & OHCI_FMI_FI;
5078 pThis->fit = (val & OHCI_FMI_FIT) >> OHCI_FMI_FIT_SHIFT;
5079 pThis->fsmps = (val & OHCI_FMI_FSMPS) >> OHCI_FMI_FSMPS_SHIFT;
5080 return VINF_SUCCESS;
5081}
5082
5083/**
5084 * Read the HcFmRemaining (Fm = Frame) register.
5085 */
5086static VBOXSTRICTRC HcFmRemaining_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5087{
5088 RT_NOREF(iReg);
5089 uint32_t Value = pThis->frt << 31;
5090 if ((pThis->ctl & OHCI_CTL_HCFS) == OHCI_USB_OPERATIONAL)
5091 {
5092 /*
5093 * Being in USB operational state guarantees SofTime was set already.
5094 */
5095 uint64_t tks = PDMDevHlpTMTimeVirtGet(pDevIns) - pThis->SofTime;
5096 if (tks < pThis->cTicksPerFrame) /* avoid muldiv if possible */
5097 {
5098 uint16_t fr;
5099 tks = ASMMultU64ByU32DivByU32(1, tks, pThis->cTicksPerUsbTick);
5100 fr = (uint16_t)(pThis->fi - tks);
5101 Value |= fr;
5102 }
5103 }
5104
5105 Log2(("HcFmRemaining_r() -> %#010x - FR=%d FRT=%d\n", Value, Value & 0x3fff, Value >> 31));
5106 *pu32Value = Value;
5107 return VINF_SUCCESS;
5108}
5109
5110/**
5111 * Write to the HcFmRemaining (Fm = Frame) register.
5112 */
5113static VBOXSTRICTRC HcFmRemaining_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5114{
5115 RT_NOREF(pDevIns, pThis, iReg, val);
5116 Log2(("HcFmRemaining_w(%#010x) - denied\n", val));
5117 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
5118 return VINF_SUCCESS;
5119}
5120
5121/**
5122 * Read the HcFmNumber (Fm = Frame) register.
5123 */
5124static VBOXSTRICTRC HcFmNumber_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5125{
5126 RT_NOREF(pDevIns, iReg);
5127 uint32_t val = (uint16_t)pThis->HcFmNumber;
5128 Log2(("HcFmNumber_r() -> %#010x - FN=%#x(%d) (32-bit=%#x(%d))\n", val, val, val, pThis->HcFmNumber, pThis->HcFmNumber));
5129 *pu32Value = val;
5130 return VINF_SUCCESS;
5131}
5132
5133/**
5134 * Write to the HcFmNumber (Fm = Frame) register.
5135 */
5136static VBOXSTRICTRC HcFmNumber_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5137{
5138 RT_NOREF(pDevIns, pThis, iReg, val);
5139 Log2(("HcFmNumber_w(%#010x) - denied\n", val));
5140 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
5141 return VINF_SUCCESS;
5142}
5143
5144/**
5145 * Read the HcPeriodicStart register.
5146 * The register determines when in a frame to switch from control&bulk to periodic lists.
5147 */
5148static VBOXSTRICTRC HcPeriodicStart_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5149{
5150 RT_NOREF(pDevIns, iReg);
5151 Log2(("HcPeriodicStart_r() -> %#010x - PS=%d\n", pThis->pstart, pThis->pstart & 0x3fff));
5152 *pu32Value = pThis->pstart;
5153 return VINF_SUCCESS;
5154}
5155
5156/**
5157 * Write to the HcPeriodicStart register.
5158 * The register determines when in a frame to switch from control&bulk to periodic lists.
5159 */
5160static VBOXSTRICTRC HcPeriodicStart_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5161{
5162 RT_NOREF(pDevIns, iReg);
5163 Log2(("HcPeriodicStart_w(%#010x) => PS=%d\n", val, val & 0x3fff));
5164 if (val & ~0x3fff)
5165 Log2(("Unknown bits %#x are set!!!\n", val & ~0x3fff));
5166 pThis->pstart = val; /** @todo r=bird: should we support setting the other bits? */
5167 return VINF_SUCCESS;
5168}
5169
5170/**
5171 * Read the HcLSThreshold register.
5172 */
5173static VBOXSTRICTRC HcLSThreshold_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5174{
5175 RT_NOREF(pDevIns, pThis, iReg);
5176 Log2(("HcLSThreshold_r() -> %#010x\n", OHCI_LS_THRESH));
5177 *pu32Value = OHCI_LS_THRESH;
5178 return VINF_SUCCESS;
5179}
5180
5181/**
5182 * Write to the HcLSThreshold register.
5183 *
5184 * Docs are inconsistent here:
5185 *
5186 * "Neither the Host Controller nor the Host Controller Driver are allowed to change this value."
5187 *
5188 * "This value is calculated by HCD with the consideration of transmission and setup overhead."
5189 *
5190 * The register is marked "R/W" the HCD column.
5191 *
5192 */
5193static VBOXSTRICTRC HcLSThreshold_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5194{
5195 RT_NOREF(pDevIns, pThis, iReg, val);
5196 Log2(("HcLSThreshold_w(%#010x) => LST=0x%03x(%d)\n", val, val & 0x0fff, val & 0x0fff));
5197 AssertMsg(val == OHCI_LS_THRESH,
5198 ("HCD tried to write bad LS threshold: 0x%x (see function header)\n", val));
5199 /** @todo the HCD can change this. */
5200 return VINF_SUCCESS;
5201}
5202
5203/**
5204 * Read the HcRhDescriptorA register.
5205 */
5206static VBOXSTRICTRC HcRhDescriptorA_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5207{
5208 RT_NOREF(pDevIns, iReg);
5209 uint32_t val = pThis->RootHub.desc_a;
5210#if 0 /* annoying */
5211 Log2(("HcRhDescriptorA_r() -> %#010x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTGT=%#x\n",
5212 val, val & 0xff, (val >> 8) & 1, (val >> 9) & 1, (val >> 10) & 1, (val >> 11) & 1,
5213 (val >> 12) & 1, (val >> 24) & 0xff));
5214#endif
5215 *pu32Value = val;
5216 return VINF_SUCCESS;
5217}
5218
5219/**
5220 * Write to the HcRhDescriptorA register.
5221 */
5222static VBOXSTRICTRC HcRhDescriptorA_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5223{
5224 RT_NOREF(pDevIns, iReg);
5225 uint32_t chg = val ^ pThis->RootHub.desc_a; NOREF(chg);
5226 Log2(("HcRhDescriptorA_w(%#010x) => %sNDP=%d %sPSM=%d %sNPS=%d %sDT=%d %sOCPM=%d %sNOCP=%d %sPOTGT=%#x - %sPowerSwitching Set%sPower\n",
5227 val,
5228 chg & 0xff ?"!!!": "", val & 0xff,
5229 (chg >> 8) & 1 ? "*" : "", (val >> 8) & 1,
5230 (chg >> 9) & 1 ? "*" : "", (val >> 9) & 1,
5231 (chg >> 10) & 1 ?"!!!": "", 0,
5232 (chg >> 11) & 1 ? "*" : "", (val >> 11) & 1,
5233 (chg >> 12) & 1 ? "*" : "", (val >> 12) & 1,
5234 (chg >> 24)&0xff? "*" : "", (val >> 24) & 0xff,
5235 val & OHCI_RHA_NPS ? "No" : "",
5236 val & OHCI_RHA_PSM ? "Port" : "Global"));
5237 if (val & ~0xff001fff)
5238 Log2(("Unknown bits %#x are set!!!\n", val & ~0xff001fff));
5239
5240
5241 if ((val & (OHCI_RHA_NDP | OHCI_RHA_DT)) != OHCI_NDP_CFG(pThis))
5242 {
5243 Log(("ohci: invalid write to NDP or DT in roothub descriptor A!!! val=0x%.8x\n", val));
5244 val &= ~(OHCI_RHA_NDP | OHCI_RHA_DT);
5245 val |= OHCI_NDP_CFG(pThis);
5246 }
5247
5248 pThis->RootHub.desc_a = val;
5249 return VINF_SUCCESS;
5250}
5251
5252/**
5253 * Read the HcRhDescriptorB register.
5254 */
5255static VBOXSTRICTRC HcRhDescriptorB_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5256{
5257 uint32_t val = pThis->RootHub.desc_b;
5258 Log2(("HcRhDescriptorB_r() -> %#010x - DR=0x%04x PPCM=0x%04x\n",
5259 val, val & 0xffff, val >> 16));
5260 *pu32Value = val;
5261 RT_NOREF(pDevIns, iReg);
5262 return VINF_SUCCESS;
5263}
5264
5265/**
5266 * Write to the HcRhDescriptorB register.
5267 */
5268static VBOXSTRICTRC HcRhDescriptorB_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5269{
5270 RT_NOREF(pDevIns, iReg);
5271 uint32_t chg = pThis->RootHub.desc_b ^ val; NOREF(chg);
5272 Log2(("HcRhDescriptorB_w(%#010x) => %sDR=0x%04x %sPPCM=0x%04x\n",
5273 val,
5274 chg & 0xffff ? "!!!" : "", val & 0xffff,
5275 chg >> 16 ? "!!!" : "", val >> 16));
5276
5277 if ( pThis->RootHub.desc_b != val )
5278 Log(("ohci: unsupported write to root descriptor B!!! 0x%.8x -> 0x%.8x\n", pThis->RootHub.desc_b, val));
5279 pThis->RootHub.desc_b = val;
5280 return VINF_SUCCESS;
5281}
5282
5283/**
5284 * Read the HcRhStatus (Rh = Root Hub) register.
5285 */
5286static VBOXSTRICTRC HcRhStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5287{
5288 uint32_t val = pThis->RootHub.status;
5289 if (val & (OHCI_RHS_LPSC | OHCI_RHS_OCIC))
5290 Log2(("HcRhStatus_r() -> %#010x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n",
5291 val, val & 1, (val >> 1) & 1, (val >> 15) & 1, (val >> 16) & 1, (val >> 17) & 1, (val >> 31) & 1));
5292 *pu32Value = val;
5293 RT_NOREF(pDevIns, iReg);
5294 return VINF_SUCCESS;
5295}
5296
5297/**
5298 * Write to the HcRhStatus (Rh = Root Hub) register.
5299 */
5300static VBOXSTRICTRC HcRhStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5301{
5302#ifdef IN_RING3
5303 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5304
5305#ifdef LOG_ENABLED
5306 /* log */
5307 uint32_t old = pThis->RootHub.status;
5308 if (val & ~0x80038003)
5309 Log2(("HcRhStatus_w: Unknown bits %#x are set!!!\n", val & ~0x80038003));
5310 if ( (val & OHCI_RHS_LPSC) && (val & OHCI_RHS_LPS) )
5311 Log2(("HcRhStatus_w: Warning both CGP and SGP are set! (Clear/Set Global Power)\n"));
5312 if ( (val & OHCI_RHS_DRWE) && (val & OHCI_RHS_CRWE) )
5313 Log2(("HcRhStatus_w: Warning both CRWE and SRWE are set! (Clear/Set Remote Wakeup Enable)\n"));
5314#endif
5315
5316 /* write 1 to clear OCIC */
5317 if ( val & OHCI_RHS_OCIC )
5318 pThis->RootHub.status &= ~OHCI_RHS_OCIC;
5319
5320 /* SetGlobalPower */
5321 if ( val & OHCI_RHS_LPSC )
5322 {
5323 unsigned i;
5324 Log2(("ohci: global power up\n"));
5325 for (i = 0; i < OHCI_NDP_CFG(pThis); i++)
5326 ohciR3RhPortPower(&pThisCC->RootHub, i, true /* power up */);
5327 }
5328
5329 /* ClearGlobalPower */
5330 if ( val & OHCI_RHS_LPS )
5331 {
5332 unsigned i;
5333 Log2(("ohci: global power down\n"));
5334 for (i = 0; i < OHCI_NDP_CFG(pThis); i++)
5335 ohciR3RhPortPower(&pThisCC->RootHub, i, false /* power down */);
5336 }
5337
5338 if ( val & OHCI_RHS_DRWE )
5339 pThis->RootHub.status |= OHCI_RHS_DRWE;
5340
5341 if ( val & OHCI_RHS_CRWE )
5342 pThis->RootHub.status &= ~OHCI_RHS_DRWE;
5343
5344#ifdef LOG_ENABLED
5345 uint32_t chg = pThis->RootHub.status ^ old;
5346 Log2(("HcRhStatus_w(%#010x) => %sCGP=%d %sOCI=%d %sSRWE=%d %sSGP=%d %sOCIC=%d %sCRWE=%d\n",
5347 val,
5348 chg & 1 ? "*" : "", val & 1,
5349 (chg >> 1) & 1 ?"!!!": "", (val >> 1) & 1,
5350 (chg >> 15) & 1 ? "*" : "", (val >> 15) & 1,
5351 (chg >> 16) & 1 ? "*" : "", (val >> 16) & 1,
5352 (chg >> 17) & 1 ? "*" : "", (val >> 17) & 1,
5353 (chg >> 31) & 1 ? "*" : "", (val >> 31) & 1));
5354#endif
5355 RT_NOREF(pDevIns, iReg);
5356 return VINF_SUCCESS;
5357#else /* !IN_RING3 */
5358 RT_NOREF(pDevIns, pThis, iReg, val);
5359 return VINF_IOM_R3_MMIO_WRITE;
5360#endif /* !IN_RING3 */
5361}
5362
5363/**
5364 * Read the HcRhPortStatus register of a port.
5365 */
5366static VBOXSTRICTRC HcRhPortStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5367{
5368 const unsigned i = iReg - 21;
5369 uint32_t val = pThis->RootHub.aPorts[i].fReg | OHCI_PORT_PPS; /* PortPowerStatus: see todo on power in _w function. */
5370 if (val & OHCI_PORT_PRS)
5371 {
5372#ifdef IN_RING3
5373 RTThreadYield();
5374#else
5375 Log2(("HcRhPortStatus_r: yield -> VINF_IOM_R3_MMIO_READ\n"));
5376 return VINF_IOM_R3_MMIO_READ;
5377#endif
5378 }
5379 if (val & (OHCI_PORT_PRS | OHCI_PORT_CLEAR_CHANGE_MASK))
5380 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",
5381 i, val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 8) & 1, (val >> 9) & 1,
5382 (val >> 16) & 1, (val >> 17) & 1, (val >> 18) & 1, (val >> 19) & 1, (val >> 20) & 1));
5383 *pu32Value = val;
5384 RT_NOREF(pDevIns);
5385 return VINF_SUCCESS;
5386}
5387
5388#ifdef IN_RING3
5389/**
5390 * Completion callback for the vusb_dev_reset() operation.
5391 * @thread EMT.
5392 */
5393static DECLCALLBACK(void) ohciR3PortResetDone(PVUSBIDEVICE pDev, uint32_t uPort, int rc, void *pvUser)
5394{
5395 RT_NOREF(pDev);
5396
5397 Assert(uPort >= 1);
5398 PPDMDEVINS pDevIns = (PPDMDEVINS)pvUser;
5399 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5400 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5401 POHCIHUBPORT pPort = &pThis->RootHub.aPorts[uPort - 1];
5402
5403 if (RT_SUCCESS(rc))
5404 {
5405 /*
5406 * Successful reset.
5407 */
5408 Log2(("ohciR3PortResetDone: Reset completed.\n"));
5409 pPort->fReg &= ~(OHCI_PORT_PRS | OHCI_PORT_PSS | OHCI_PORT_PSSC);
5410 pPort->fReg |= OHCI_PORT_PES | OHCI_PORT_PRSC;
5411 }
5412 else
5413 {
5414 /* desperate measures. */
5415 if ( pPort->fAttached
5416 && VUSBIRhDevGetState(pThisCC->RootHub.pIRhConn, uPort) == VUSB_DEVICE_STATE_ATTACHED)
5417 {
5418 /*
5419 * Damn, something weird happened during reset. We'll pretend the user did an
5420 * incredible fast reconnect or something. (probably not gonna work)
5421 */
5422 Log2(("ohciR3PortResetDone: The reset failed (rc=%Rrc)!!! Pretending reconnect at the speed of light.\n", rc));
5423 pPort->fReg = OHCI_PORT_CCS | OHCI_PORT_CSC;
5424 }
5425 else
5426 {
5427 /*
5428 * The device have / will be disconnected.
5429 */
5430 Log2(("ohciR3PortResetDone: Disconnected (rc=%Rrc)!!!\n", rc));
5431 pPort->fReg &= ~(OHCI_PORT_PRS | OHCI_PORT_PSS | OHCI_PORT_PSSC | OHCI_PORT_PRSC);
5432 pPort->fReg |= OHCI_PORT_CSC;
5433 }
5434 }
5435
5436 /* Raise roothub status change interrupt. */
5437 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5438}
5439
5440/**
5441 * Sets a flag in a port status register but only set it if a device is
5442 * connected, if not set ConnectStatusChange flag to force HCD to reevaluate
5443 * connect status.
5444 *
5445 * @returns true if device was connected and the flag was cleared.
5446 */
5447static bool ohciR3RhPortSetIfConnected(PPDMDEVINS pDevIns, POHCI pThis, int iPort, uint32_t fValue)
5448{
5449 /*
5450 * Writing a 0 has no effect
5451 */
5452 if (fValue == 0)
5453 return false;
5454
5455 /*
5456 * If CurrentConnectStatus is cleared we set ConnectStatusChange.
5457 */
5458 if (!(pThis->RootHub.aPorts[iPort].fReg & OHCI_PORT_CCS))
5459 {
5460 pThis->RootHub.aPorts[iPort].fReg |= OHCI_PORT_CSC;
5461 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5462 return false;
5463 }
5464
5465 bool fRc = !(pThis->RootHub.aPorts[iPort].fReg & fValue);
5466
5467 /* set the bit */
5468 pThis->RootHub.aPorts[iPort].fReg |= fValue;
5469
5470 return fRc;
5471}
5472#endif /* IN_RING3 */
5473
5474/**
5475 * Write to the HcRhPortStatus register of a port.
5476 */
5477static VBOXSTRICTRC HcRhPortStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5478{
5479#ifdef IN_RING3
5480 const unsigned i = iReg - 21;
5481 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5482 POHCIHUBPORT p = &pThis->RootHub.aPorts[i];
5483 uint32_t old_state = p->fReg;
5484
5485# ifdef LOG_ENABLED
5486 /*
5487 * Log it.
5488 */
5489 static const char *apszCmdNames[32] =
5490 {
5491 "ClearPortEnable", "SetPortEnable", "SetPortSuspend", "!!!ClearSuspendStatus",
5492 "SetPortReset", "!!!5", "!!!6", "!!!7",
5493 "SetPortPower", "ClearPortPower", "!!!10", "!!!11",
5494 "!!!12", "!!!13", "!!!14", "!!!15",
5495 "ClearCSC", "ClearPESC", "ClearPSSC", "ClearOCIC",
5496 "ClearPRSC", "!!!21", "!!!22", "!!!23",
5497 "!!!24", "!!!25", "!!!26", "!!!27",
5498 "!!!28", "!!!29", "!!!30", "!!!31"
5499 };
5500 Log2(("HcRhPortStatus_w(%#010x): port %u:", val, i));
5501 for (unsigned j = 0; j < RT_ELEMENTS(apszCmdNames); j++)
5502 if (val & (1 << j))
5503 Log2((" %s", apszCmdNames[j]));
5504 Log2(("\n"));
5505# endif
5506
5507 /* Write to clear any of the change bits: CSC, PESC, PSSC, OCIC and PRSC */
5508 if (val & OHCI_PORT_CLEAR_CHANGE_MASK)
5509 p->fReg &= ~(val & OHCI_PORT_CLEAR_CHANGE_MASK);
5510
5511 if (val & OHCI_PORT_CLRPE)
5512 {
5513 p->fReg &= ~OHCI_PORT_PES;
5514 Log2(("HcRhPortStatus_w(): port %u: DISABLE\n", i));
5515 }
5516
5517 if (ohciR3RhPortSetIfConnected(pDevIns, pThis, i, val & OHCI_PORT_PES))
5518 Log2(("HcRhPortStatus_w(): port %u: ENABLE\n", i));
5519
5520 if (ohciR3RhPortSetIfConnected(pDevIns, pThis, i, val & OHCI_PORT_PSS))
5521 Log2(("HcRhPortStatus_w(): port %u: SUSPEND - not implemented correctly!!!\n", i));
5522
5523 if (val & OHCI_PORT_PRS)
5524 {
5525 if (ohciR3RhPortSetIfConnected(pDevIns, pThis, i, val & OHCI_PORT_PRS))
5526 {
5527 PVM pVM = PDMDevHlpGetVM(pDevIns);
5528 p->fReg &= ~OHCI_PORT_PRSC;
5529 VUSBIRhDevReset(pThisCC->RootHub.pIRhConn, OHCI_PORT_2_VUSB_PORT(i), false /* don't reset on linux */,
5530 ohciR3PortResetDone, pDevIns, pVM);
5531 }
5532 else if (p->fReg & OHCI_PORT_PRS)
5533 {
5534 /* the guest is getting impatient. */
5535 Log2(("HcRhPortStatus_w(): port %u: Impatient guest!\n", i));
5536 RTThreadYield();
5537 }
5538 }
5539
5540 if (!(pThis->RootHub.desc_a & OHCI_RHA_NPS))
5541 {
5542 /** @todo To implement per-device power-switching
5543 * we need to check PortPowerControlMask to make
5544 * sure it isn't gang powered
5545 */
5546 if (val & OHCI_PORT_CLRPP)
5547 ohciR3RhPortPower(&pThisCC->RootHub, i, false /* power down */);
5548 if (val & OHCI_PORT_PPS)
5549 ohciR3RhPortPower(&pThisCC->RootHub, i, true /* power up */);
5550 }
5551
5552 /** @todo r=frank: ClearSuspendStatus. Timing? */
5553 if (val & OHCI_PORT_CLRSS)
5554 {
5555 ohciR3RhPortPower(&pThisCC->RootHub, i, true /* power up */);
5556 pThis->RootHub.aPorts[i].fReg &= ~OHCI_PORT_PSS;
5557 pThis->RootHub.aPorts[i].fReg |= OHCI_PORT_PSSC;
5558 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5559 }
5560
5561 if (p->fReg != old_state)
5562 {
5563 uint32_t res = p->fReg;
5564 uint32_t chg = res ^ old_state; NOREF(chg);
5565 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",
5566 val, i,
5567 chg & 1 ? "*" : "", res & 1,
5568 (chg >> 1) & 1 ? "*" : "", (res >> 1) & 1,
5569 (chg >> 2) & 1 ? "*" : "", (res >> 2) & 1,
5570 (chg >> 3) & 1 ? "*" : "", (res >> 3) & 1,
5571 (chg >> 4) & 1 ? "*" : "", (res >> 4) & 1,
5572 (chg >> 8) & 1 ? "*" : "", (res >> 8) & 1,
5573 (chg >> 9) & 1 ? "*" : "", (res >> 9) & 1,
5574 (chg >> 16) & 1 ? "*" : "", (res >> 16) & 1,
5575 (chg >> 17) & 1 ? "*" : "", (res >> 17) & 1,
5576 (chg >> 18) & 1 ? "*" : "", (res >> 18) & 1,
5577 (chg >> 19) & 1 ? "*" : "", (res >> 19) & 1,
5578 (chg >> 20) & 1 ? "*" : "", (res >> 20) & 1));
5579 }
5580 RT_NOREF(pDevIns);
5581 return VINF_SUCCESS;
5582#else /* !IN_RING3 */
5583 RT_NOREF(pDevIns, pThis, iReg, val);
5584 return VINF_IOM_R3_MMIO_WRITE;
5585#endif /* !IN_RING3 */
5586}
5587
5588/**
5589 * Register descriptor table
5590 */
5591static const OHCIOPREG g_aOpRegs[] =
5592{
5593 { "HcRevision", HcRevision_r, HcRevision_w }, /* 0 */
5594 { "HcControl", HcControl_r, HcControl_w }, /* 1 */
5595 { "HcCommandStatus", HcCommandStatus_r, HcCommandStatus_w }, /* 2 */
5596 { "HcInterruptStatus", HcInterruptStatus_r, HcInterruptStatus_w }, /* 3 */
5597 { "HcInterruptEnable", HcInterruptEnable_r, HcInterruptEnable_w }, /* 4 */
5598 { "HcInterruptDisable", HcInterruptDisable_r, HcInterruptDisable_w }, /* 5 */
5599 { "HcHCCA", HcHCCA_r, HcHCCA_w }, /* 6 */
5600 { "HcPeriodCurrentED", HcPeriodCurrentED_r, HcPeriodCurrentED_w }, /* 7 */
5601 { "HcControlHeadED", HcControlHeadED_r, HcControlHeadED_w }, /* 8 */
5602 { "HcControlCurrentED", HcControlCurrentED_r, HcControlCurrentED_w }, /* 9 */
5603 { "HcBulkHeadED", HcBulkHeadED_r, HcBulkHeadED_w }, /* 10 */
5604 { "HcBulkCurrentED", HcBulkCurrentED_r, HcBulkCurrentED_w }, /* 11 */
5605 { "HcDoneHead", HcDoneHead_r, HcDoneHead_w }, /* 12 */
5606 { "HcFmInterval", HcFmInterval_r, HcFmInterval_w }, /* 13 */
5607 { "HcFmRemaining", HcFmRemaining_r, HcFmRemaining_w }, /* 14 */
5608 { "HcFmNumber", HcFmNumber_r, HcFmNumber_w }, /* 15 */
5609 { "HcPeriodicStart", HcPeriodicStart_r, HcPeriodicStart_w }, /* 16 */
5610 { "HcLSThreshold", HcLSThreshold_r, HcLSThreshold_w }, /* 17 */
5611 { "HcRhDescriptorA", HcRhDescriptorA_r, HcRhDescriptorA_w }, /* 18 */
5612 { "HcRhDescriptorB", HcRhDescriptorB_r, HcRhDescriptorB_w }, /* 19 */
5613 { "HcRhStatus", HcRhStatus_r, HcRhStatus_w }, /* 20 */
5614
5615 /* The number of port status register depends on the definition
5616 * of OHCI_NDP_MAX macro
5617 */
5618 { "HcRhPortStatus[0]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 21 */
5619 { "HcRhPortStatus[1]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 22 */
5620 { "HcRhPortStatus[2]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 23 */
5621 { "HcRhPortStatus[3]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 24 */
5622 { "HcRhPortStatus[4]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 25 */
5623 { "HcRhPortStatus[5]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 26 */
5624 { "HcRhPortStatus[6]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 27 */
5625 { "HcRhPortStatus[7]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 28 */
5626 { "HcRhPortStatus[8]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 29 */
5627 { "HcRhPortStatus[9]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 30 */
5628 { "HcRhPortStatus[10]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 31 */
5629 { "HcRhPortStatus[11]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 32 */
5630 { "HcRhPortStatus[12]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 33 */
5631 { "HcRhPortStatus[13]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 34 */
5632 { "HcRhPortStatus[14]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 35 */
5633};
5634
5635/* Quick way to determine how many op regs are valid. Since at least one port must
5636 * be configured (and no more than 15), there will be between 22 and 36 registers.
5637 */
5638#define NUM_OP_REGS(pohci) (21 + OHCI_NDP_CFG(pohci))
5639
5640AssertCompile(RT_ELEMENTS(g_aOpRegs) > 21);
5641AssertCompile(RT_ELEMENTS(g_aOpRegs) <= 36);
5642
5643/**
5644 * @callback_method_impl{FNIOMMMIONEWREAD}
5645 */
5646static DECLCALLBACK(VBOXSTRICTRC) ohciMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
5647{
5648 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5649 RT_NOREF(pvUser);
5650
5651 /* Paranoia: Assert that IOMMMIO_FLAGS_READ_DWORD works. */
5652 AssertReturn(cb == sizeof(uint32_t), VERR_INTERNAL_ERROR_3);
5653 AssertReturn(!(off & 0x3), VERR_INTERNAL_ERROR_4);
5654
5655 /*
5656 * Validate the register and call the read operator.
5657 */
5658 VBOXSTRICTRC rc;
5659 const uint32_t iReg = off >> 2;
5660 if (iReg < NUM_OP_REGS(pThis))
5661 rc = g_aOpRegs[iReg].pfnRead(pDevIns, pThis, iReg, (uint32_t *)pv);
5662 else
5663 {
5664 Log(("ohci: Trying to read register %u/%u!!!\n", iReg, NUM_OP_REGS(pThis)));
5665 rc = VINF_IOM_MMIO_UNUSED_FF;
5666 }
5667 return rc;
5668}
5669
5670
5671/**
5672 * @callback_method_impl{FNIOMMMIONEWWRITE}
5673 */
5674static DECLCALLBACK(VBOXSTRICTRC) ohciMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
5675{
5676 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5677 RT_NOREF(pvUser);
5678
5679 /* Paranoia: Assert that IOMMMIO_FLAGS_WRITE_DWORD_ZEROED works. */
5680 AssertReturn(cb == sizeof(uint32_t), VERR_INTERNAL_ERROR_3);
5681 AssertReturn(!(off & 0x3), VERR_INTERNAL_ERROR_4);
5682
5683 /*
5684 * Validate the register and call the read operator.
5685 */
5686 VBOXSTRICTRC rc;
5687 const uint32_t iReg = off >> 2;
5688 if (iReg < NUM_OP_REGS(pThis))
5689 rc = g_aOpRegs[iReg].pfnWrite(pDevIns, pThis, iReg, *(uint32_t const *)pv);
5690 else
5691 {
5692 Log(("ohci: Trying to write to register %u/%u!!!\n", iReg, NUM_OP_REGS(pThis)));
5693 rc = VINF_SUCCESS;
5694 }
5695 return rc;
5696}
5697
5698#ifdef IN_RING3
5699
5700/**
5701 * Saves the state of the OHCI device.
5702 *
5703 * @returns VBox status code.
5704 * @param pDevIns The device instance.
5705 * @param pSSM The handle to save the state to.
5706 */
5707static DECLCALLBACK(int) ohciR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5708{
5709 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5710 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5711 LogFlow(("ohciR3SaveExec:\n"));
5712
5713 int rc = pDevIns->pHlpR3->pfnSSMPutStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
5714 AssertRCReturn(rc, rc);
5715
5716 /* Save the periodic frame rate so we can we can tell if the bus was started or not when restoring. */
5717 return pDevIns->pHlpR3->pfnSSMPutU32(pSSM, VUSBIRhGetPeriodicFrameRate(pThisCC->RootHub.pIRhConn));
5718}
5719
5720
5721/**
5722 * Loads the state of the OHCI device.
5723 *
5724 * @returns VBox status code.
5725 * @param pDevIns The device instance.
5726 * @param pSSM The handle to the saved state.
5727 * @param uVersion The data unit version number.
5728 * @param uPass The data pass.
5729 */
5730static DECLCALLBACK(int) ohciR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5731{
5732 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5733 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5734 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5735 int rc;
5736 LogFlow(("ohciR3LoadExec:\n"));
5737
5738 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
5739
5740 if (uVersion >= OHCI_SAVED_STATE_VERSION_EOF_TIMER)
5741 rc = pHlp->pfnSSMGetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
5742 else if (uVersion == OHCI_SAVED_STATE_VERSION_8PORTS)
5743 rc = pHlp->pfnSSMGetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields8Ports[0], NULL);
5744 else
5745 AssertMsgFailedReturn(("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
5746 AssertRCReturn(rc, rc);
5747
5748 /*
5749 * Get the frame rate / started indicator.
5750 *
5751 * For older versions there is a timer saved here. We'll skip it and deduce
5752 * the periodic frame rate from the host controller functional state.
5753 */
5754 if (uVersion > OHCI_SAVED_STATE_VERSION_EOF_TIMER)
5755 {
5756 rc = pHlp->pfnSSMGetU32(pSSM, &pThisCC->uRestoredPeriodicFrameRate);
5757 AssertRCReturn(rc, rc);
5758 }
5759 else
5760 {
5761 rc = pHlp->pfnSSMSkipToEndOfUnit(pSSM);
5762 AssertRCReturn(rc, rc);
5763
5764 uint32_t fHcfs = pThis->ctl & OHCI_CTL_HCFS;
5765 switch (fHcfs)
5766 {
5767 case OHCI_USB_OPERATIONAL:
5768 case OHCI_USB_RESUME:
5769 pThisCC->uRestoredPeriodicFrameRate = OHCI_DEFAULT_TIMER_FREQ;
5770 break;
5771 default:
5772 pThisCC->uRestoredPeriodicFrameRate = 0;
5773 break;
5774 }
5775 }
5776
5777 /** @todo could we restore the frame rate here instead of in ohciR3Resume? */
5778 return VINF_SUCCESS;
5779}
5780
5781
5782/**
5783 * Reset notification.
5784 *
5785 * @param pDevIns The device instance data.
5786 */
5787static DECLCALLBACK(void) ohciR3Reset(PPDMDEVINS pDevIns)
5788{
5789 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5790 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5791 LogFlow(("ohciR3Reset:\n"));
5792
5793 /*
5794 * There is no distinction between cold boot, warm reboot and software reboots,
5795 * all of these are treated as cold boots. We are also doing the initialization
5796 * job of a BIOS or SMM driver.
5797 *
5798 * Important: Don't confuse UsbReset with hardware reset. Hardware reset is
5799 * just one way of getting into the UsbReset state.
5800 */
5801 ohciR3DoReset(pDevIns, pThis, pThisCC, OHCI_USB_RESET, true /* reset devices */);
5802}
5803
5804
5805/**
5806 * Resume notification.
5807 *
5808 * @param pDevIns The device instance data.
5809 */
5810static DECLCALLBACK(void) ohciR3Resume(PPDMDEVINS pDevIns)
5811{
5812 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5813 LogFlowFunc(("\n"));
5814
5815 /* Restart the frame thread if it was active when the loaded state was saved. */
5816 uint32_t uRestoredPeriodicFR = pThisCC->uRestoredPeriodicFrameRate;
5817 pThisCC->uRestoredPeriodicFrameRate = 0;
5818 if (uRestoredPeriodicFR)
5819 {
5820 LogFlowFunc(("Bus was active, enable periodic frame processing (rate: %u)\n", uRestoredPeriodicFR));
5821 int rc = pThisCC->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThisCC->RootHub.pIRhConn, uRestoredPeriodicFR);
5822 AssertRC(rc);
5823 }
5824}
5825
5826
5827/**
5828 * Info handler, device version. Dumps OHCI control registers.
5829 *
5830 * @param pDevIns Device instance which registered the info.
5831 * @param pHlp Callback functions for doing output.
5832 * @param pszArgs Argument string. Optional and specific to the handler.
5833 */
5834static DECLCALLBACK(void) ohciR3InfoRegs(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
5835{
5836 RT_NOREF(pszArgs);
5837 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5838 uint32_t val, ctl, status;
5839
5840 /* Control register */
5841 ctl = pThis->ctl;
5842 pHlp->pfnPrintf(pHlp, "HcControl: %08x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
5843 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
5844 (ctl >> 9) & 1, (ctl >> 10) & 1);
5845
5846 /* Command status register */
5847 status = pThis->status;
5848 pHlp->pfnPrintf(pHlp, "HcCommandStatus: %08x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
5849 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3);
5850
5851 /* Interrupt status register */
5852 val = pThis->intr_status;
5853 pHlp->pfnPrintf(pHlp, "HcInterruptStatus: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
5854 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5855 (val >> 6) & 1, (val >> 30) & 1);
5856
5857 /* Interrupt enable register */
5858 val = pThis->intr;
5859 pHlp->pfnPrintf(pHlp, "HcInterruptEnable: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
5860 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5861 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1);
5862
5863 /* HCCA address register */
5864 pHlp->pfnPrintf(pHlp, "HcHCCA: %08x\n", pThis->hcca);
5865
5866 /* Current periodic ED register */
5867 pHlp->pfnPrintf(pHlp, "HcPeriodCurrentED: %08x\n", pThis->per_cur);
5868
5869 /* Control ED registers */
5870 pHlp->pfnPrintf(pHlp, "HcControlHeadED: %08x\n", pThis->ctrl_head);
5871 pHlp->pfnPrintf(pHlp, "HcControlCurrentED: %08x\n", pThis->ctrl_cur);
5872
5873 /* Bulk ED registers */
5874 pHlp->pfnPrintf(pHlp, "HcBulkHeadED: %08x\n", pThis->bulk_head);
5875 pHlp->pfnPrintf(pHlp, "HcBulkCurrentED: %08x\n", pThis->bulk_cur);
5876
5877 /* Done head register */
5878 pHlp->pfnPrintf(pHlp, "HcDoneHead: %08x\n", pThis->done);
5879
5880 /* Done head register */
5881 pHlp->pfnPrintf(pHlp, "HcDoneHead: %08x\n", pThis->done);
5882
5883 /* Root hub descriptor A */
5884 val = pThis->RootHub.desc_a;
5885 pHlp->pfnPrintf(pHlp, "HcRhDescriptorA: %08x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTPGT=%d\n",
5886 val, (uint8_t)val, (val >> 8) & 1, (val >> 9) & 1, (val >> 10) & 1, (val >> 11) & 1, (val >> 12) & 1, (uint8_t)(val >> 24));
5887
5888 /* Root hub descriptor B */
5889 val = pThis->RootHub.desc_b;
5890 pHlp->pfnPrintf(pHlp, "HcRhDescriptorB: %08x - DR=%#04x PPCM=%#04x\n", val, (uint16_t)val, (uint16_t)(val >> 16));
5891
5892 /* Root hub status register */
5893 val = pThis->RootHub.status;
5894 pHlp->pfnPrintf(pHlp, "HcRhStatus: %08x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n\n",
5895 val, val & 1, (val >> 1) & 1, (val >> 15) & 1, (val >> 16) & 1, (val >> 17) & 1, (val >> 31) & 1);
5896
5897 /* Port status registers */
5898 for (unsigned i = 0; i < OHCI_NDP_CFG(pThis); ++i)
5899 {
5900 val = pThis->RootHub.aPorts[i].fReg;
5901 pHlp->pfnPrintf(pHlp, "HcRhPortStatus%02d: CCS=%d PES =%d PSS =%d POCI=%d PRS =%d PPS=%d LSDA=%d\n"
5902 " %08x - CSC=%d PESC=%d PSSC=%d OCIC=%d PRSC=%d\n",
5903 i, val & 1, (val >> 1) & 1, (val >> 2) & 1,(val >> 3) & 1, (val >> 4) & 1, (val >> 8) & 1, (val >> 9) & 1,
5904 val, (val >> 16) & 1, (val >> 17) & 1, (val >> 18) & 1, (val >> 19) & 1, (val >> 20) & 1);
5905 }
5906}
5907
5908
5909/**
5910 * Destruct a device instance.
5911 *
5912 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
5913 * resources can be freed correctly.
5914 *
5915 * @returns VBox status code.
5916 * @param pDevIns The device instance data.
5917 */
5918static DECLCALLBACK(int) ohciR3Destruct(PPDMDEVINS pDevIns)
5919{
5920 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5921 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5922 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5923
5924 if (RTCritSectIsInitialized(&pThisCC->CritSect))
5925 RTCritSectDelete(&pThisCC->CritSect);
5926 PDMDevHlpCritSectDelete(pDevIns, &pThis->CsIrq);
5927
5928 /*
5929 * Tear down the per endpoint in-flight tracking...
5930 */
5931
5932 return VINF_SUCCESS;
5933}
5934
5935
5936/**
5937 * @interface_method_impl{PDMDEVREG,pfnConstruct,OHCI constructor}
5938 */
5939static DECLCALLBACK(int) ohciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5940{
5941 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5942 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5943 POHCIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCIR3);
5944
5945 /*
5946 * Init instance data.
5947 */
5948 pThisCC->pDevInsR3 = pDevIns;
5949
5950 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
5951 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
5952
5953 PDMPciDevSetVendorId(pPciDev, 0x106b);
5954 PDMPciDevSetDeviceId(pPciDev, 0x003f);
5955 PDMPciDevSetClassProg(pPciDev, 0x10); /* OHCI */
5956 PDMPciDevSetClassSub(pPciDev, 0x03);
5957 PDMPciDevSetClassBase(pPciDev, 0x0c);
5958 PDMPciDevSetInterruptPin(pPciDev, 0x01);
5959#ifdef VBOX_WITH_MSI_DEVICES
5960 PDMPciDevSetStatus(pPciDev, VBOX_PCI_STATUS_CAP_LIST);
5961 PDMPciDevSetCapabilityList(pPciDev, 0x80);
5962#endif
5963
5964 pThisCC->RootHub.pOhci = pThis;
5965 pThisCC->RootHub.IBase.pfnQueryInterface = ohciR3RhQueryInterface;
5966 pThisCC->RootHub.IRhPort.pfnGetAvailablePorts = ohciR3RhGetAvailablePorts;
5967 pThisCC->RootHub.IRhPort.pfnGetUSBVersions = ohciR3RhGetUSBVersions;
5968 pThisCC->RootHub.IRhPort.pfnAttach = ohciR3RhAttach;
5969 pThisCC->RootHub.IRhPort.pfnDetach = ohciR3RhDetach;
5970 pThisCC->RootHub.IRhPort.pfnReset = ohciR3RhReset;
5971 pThisCC->RootHub.IRhPort.pfnXferCompletion = ohciR3RhXferCompletion;
5972 pThisCC->RootHub.IRhPort.pfnXferError = ohciR3RhXferError;
5973 pThisCC->RootHub.IRhPort.pfnStartFrame = ohciR3StartFrame;
5974 pThisCC->RootHub.IRhPort.pfnFrameRateChanged = ohciR3FrameRateChanged;
5975
5976 /* USB LED */
5977 pThisCC->RootHub.Led.u32Magic = PDMLED_MAGIC;
5978 pThisCC->RootHub.ILeds.pfnQueryStatusLed = ohciR3RhQueryStatusLed;
5979
5980
5981 /*
5982 * Read configuration.
5983 */
5984 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Ports", "");
5985
5986 /* Number of ports option. */
5987 uint32_t cPorts;
5988 int rc = pDevIns->pHlpR3->pfnCFGMQueryU32Def(pCfg, "Ports", &cPorts, OHCI_NDP_DEFAULT);
5989 if (RT_FAILURE(rc))
5990 return PDMDEV_SET_ERROR(pDevIns, rc, N_("OHCI configuration error: failed to read Ports as integer"));
5991 if (cPorts == 0 || cPorts > OHCI_NDP_MAX)
5992 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5993 N_("OHCI configuration error: Ports must be in range [%u,%u]"),
5994 1, OHCI_NDP_MAX);
5995
5996 /* Store the configured NDP; it will be used everywhere else from now on. */
5997 pThis->RootHub.desc_a = cPorts;
5998
5999 /*
6000 * Register PCI device and I/O region.
6001 */
6002 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
6003 if (RT_FAILURE(rc))
6004 return rc;
6005
6006#ifdef VBOX_WITH_MSI_DEVICES
6007 PDMMSIREG MsiReg;
6008 RT_ZERO(MsiReg);
6009 MsiReg.cMsiVectors = 1;
6010 MsiReg.iMsiCapOffset = 0x80;
6011 MsiReg.iMsiNextOffset = 0x00;
6012 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
6013 if (RT_FAILURE(rc))
6014 {
6015 PDMPciDevSetCapabilityList(pPciDev, 0x0);
6016 /* That's OK, we can work without MSI */
6017 }
6018#endif
6019
6020 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 0, 4096, PCI_ADDRESS_SPACE_MEM, ohciMmioWrite, ohciMmioRead, NULL /*pvUser*/,
6021 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED
6022 | IOMMMIO_FLAGS_DBGSTOP_ON_COMPLICATED_WRITE, "USB OHCI", &pThis->hMmio);
6023 AssertRCReturn(rc, rc);
6024
6025 /*
6026 * Register the saved state data unit.
6027 */
6028 rc = PDMDevHlpSSMRegisterEx(pDevIns, OHCI_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
6029 NULL, NULL, NULL,
6030 NULL, ohciR3SaveExec, NULL,
6031 NULL, ohciR3LoadExec, NULL);
6032 AssertRCReturn(rc, rc);
6033
6034 /*
6035 * Attach to the VBox USB RootHub Driver on LUN #0.
6036 */
6037 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->RootHub.IBase, &pThisCC->RootHub.pIBase, "RootHub");
6038 if (RT_FAILURE(rc))
6039 {
6040 AssertMsgFailed(("Configuration error: No roothub driver attached to LUN #0!\n"));
6041 return rc;
6042 }
6043 pThisCC->RootHub.pIRhConn = PDMIBASE_QUERY_INTERFACE(pThisCC->RootHub.pIBase, VUSBIROOTHUBCONNECTOR);
6044 AssertMsgReturn(pThisCC->RootHub.pIRhConn,
6045 ("Configuration error: The driver doesn't provide the VUSBIROOTHUBCONNECTOR interface!\n"),
6046 VERR_PDM_MISSING_INTERFACE);
6047
6048 /*
6049 * Attach status driver (optional).
6050 */
6051 PPDMIBASE pBase;
6052 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->RootHub.IBase, &pBase, "Status Port");
6053 if (RT_SUCCESS(rc))
6054 pThisCC->RootHub.pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
6055 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
6056 {
6057 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
6058 return rc;
6059 }
6060
6061 /* Set URB parameters. */
6062 rc = VUSBIRhSetUrbParams(pThisCC->RootHub.pIRhConn, sizeof(VUSBURBHCIINT), sizeof(VUSBURBHCITDINT));
6063 if (RT_FAILURE(rc))
6064 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("OHCI: Failed to set URB parameters"));
6065
6066 /*
6067 * Take down the virtual clock frequence for use in ohciR3FrameRateChanged().
6068 * (Used to be a timer, thus the name.)
6069 */
6070 pThis->u64TimerHz = PDMDevHlpTMTimeVirtGetFreq(pDevIns);
6071
6072 /*
6073 * Critical sections: explain
6074 */
6075 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CsIrq, RT_SRC_POS, "OHCI#%uIrq", iInstance);
6076 if (RT_FAILURE(rc))
6077 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("OHCI: Failed to create critical section"));
6078
6079 rc = RTCritSectInit(&pThisCC->CritSect);
6080 if (RT_FAILURE(rc))
6081 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("OHCI: Failed to create critical section"));
6082
6083 /*
6084 * Do a hardware reset.
6085 */
6086 ohciR3DoReset(pDevIns, pThis, pThisCC, OHCI_USB_RESET, false /* don't reset devices */);
6087
6088# ifdef VBOX_WITH_STATISTICS
6089 /*
6090 * Register statistics.
6091 */
6092 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledIsocUrbs, STAMTYPE_COUNTER, "CanceledIsocUrbs", STAMUNIT_OCCURENCES, "Detected canceled isochronous URBs.");
6093 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledGenUrbs, STAMTYPE_COUNTER, "CanceledGenUrbs", STAMUNIT_OCCURENCES, "Detected canceled general URBs.");
6094 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatDroppedUrbs, STAMTYPE_COUNTER, "DroppedUrbs", STAMUNIT_OCCURENCES, "Dropped URBs (endpoint halted, or URB canceled).");
6095# endif
6096
6097 /*
6098 * Register debugger info callbacks.
6099 */
6100 PDMDevHlpDBGFInfoRegister(pDevIns, "ohci", "OHCI control registers.", ohciR3InfoRegs);
6101
6102# if 0/*def DEBUG_bird*/
6103// g_fLogInterruptEPs = true;
6104 g_fLogControlEPs = true;
6105 g_fLogBulkEPs = true;
6106# endif
6107
6108 return VINF_SUCCESS;
6109}
6110
6111#else /* !IN_RING3 */
6112
6113/**
6114 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
6115 */
6116static DECLCALLBACK(int) ohciRZConstruct(PPDMDEVINS pDevIns)
6117{
6118 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
6119 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
6120
6121 int rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, ohciMmioWrite, ohciMmioRead, NULL /*pvUser*/);
6122 AssertRCReturn(rc, rc);
6123
6124 return VINF_SUCCESS;
6125}
6126
6127#endif /* !IN_RING3 */
6128
6129const PDMDEVREG g_DeviceOHCI =
6130{
6131 /* .u32version = */ PDM_DEVREG_VERSION,
6132 /* .uReserved0 = */ 0,
6133 /* .szName = */ "usb-ohci",
6134 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
6135 /* .fClass = */ PDM_DEVREG_CLASS_BUS_USB,
6136 /* .cMaxInstances = */ ~0U,
6137 /* .uSharedVersion = */ 42,
6138 /* .cbInstanceShared = */ sizeof(OHCI),
6139 /* .cbInstanceCC = */ sizeof(OHCICC),
6140 /* .cbInstanceRC = */ 0,
6141 /* .cMaxPciDevices = */ 1,
6142 /* .cMaxMsixVectors = */ 0,
6143 /* .pszDescription = */ "OHCI USB controller.\n",
6144#if defined(IN_RING3)
6145 /* .pszRCMod = */ "VBoxDDRC.rc",
6146 /* .pszR0Mod = */ "VBoxDDR0.r0",
6147 /* .pfnConstruct = */ ohciR3Construct,
6148 /* .pfnDestruct = */ ohciR3Destruct,
6149 /* .pfnRelocate = */ NULL,
6150 /* .pfnMemSetup = */ NULL,
6151 /* .pfnPowerOn = */ NULL,
6152 /* .pfnReset = */ ohciR3Reset,
6153 /* .pfnSuspend = */ NULL,
6154 /* .pfnResume = */ ohciR3Resume,
6155 /* .pfnAttach = */ NULL,
6156 /* .pfnDetach = */ NULL,
6157 /* .pfnQueryInterface = */ NULL,
6158 /* .pfnInitComplete = */ NULL,
6159 /* .pfnPowerOff = */ NULL,
6160 /* .pfnSoftReset = */ NULL,
6161 /* .pfnReserved0 = */ NULL,
6162 /* .pfnReserved1 = */ NULL,
6163 /* .pfnReserved2 = */ NULL,
6164 /* .pfnReserved3 = */ NULL,
6165 /* .pfnReserved4 = */ NULL,
6166 /* .pfnReserved5 = */ NULL,
6167 /* .pfnReserved6 = */ NULL,
6168 /* .pfnReserved7 = */ NULL,
6169#elif defined(IN_RING0)
6170 /* .pfnEarlyConstruct = */ NULL,
6171 /* .pfnConstruct = */ ohciRZConstruct,
6172 /* .pfnDestruct = */ NULL,
6173 /* .pfnFinalDestruct = */ NULL,
6174 /* .pfnRequest = */ NULL,
6175 /* .pfnReserved0 = */ NULL,
6176 /* .pfnReserved1 = */ NULL,
6177 /* .pfnReserved2 = */ NULL,
6178 /* .pfnReserved3 = */ NULL,
6179 /* .pfnReserved4 = */ NULL,
6180 /* .pfnReserved5 = */ NULL,
6181 /* .pfnReserved6 = */ NULL,
6182 /* .pfnReserved7 = */ NULL,
6183#elif defined(IN_RC)
6184 /* .pfnConstruct = */ ohciRZConstruct,
6185 /* .pfnReserved0 = */ NULL,
6186 /* .pfnReserved1 = */ NULL,
6187 /* .pfnReserved2 = */ NULL,
6188 /* .pfnReserved3 = */ NULL,
6189 /* .pfnReserved4 = */ NULL,
6190 /* .pfnReserved5 = */ NULL,
6191 /* .pfnReserved6 = */ NULL,
6192 /* .pfnReserved7 = */ NULL,
6193#else
6194# error "Not in IN_RING3, IN_RING0 or IN_RC!"
6195#endif
6196 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
6197};
6198
6199#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