VirtualBox

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

Last change on this file since 104276 was 104276, checked in by vboxsync, 12 months ago

DevOHCI: Reworked endpoint skipping to use the new cancel by address/endpoint/direction callback (see bugref:10634).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 224.0 KB
Line 
1/* $Id: DevOHCI.cpp 104276 2024-04-10 13:29:23Z 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 - ",erable 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 bool ohciR3GetDirection(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, PCOHCIED pEd, VUSBDIRECTION *pEnmDir)
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 *pEnmDir = VUSBDIRECTION_INVALID;
3105 return false;
3106 }
3107 }
3108 else
3109 {
3110 switch (pEd->hwinfo & ED_HWINFO_DIR)
3111 {
3112 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3113 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3114 default:
3115 /* We must read the TD to determine direction. */
3116 uint32_t TdAddr = pEd->HeadP & ED_PTR_MASK;
3117 OHCITD Td;
3118 ohciR3ReadTd(pDevIns, TdAddr, &Td);
3119 switch (Td.hwinfo & TD_HWINFO_DIR)
3120 {
3121 case TD_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3122 case TD_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3123 case 0: enmDir = VUSBDIRECTION_SETUP; break;
3124 default:
3125 Log(("ohciR3GetDirection: Invalid direction!!!! Td.hwinfo=%#x Ed.hwinfo=%#x\n", Td.hwinfo, pEd->hwinfo));
3126 *pEnmDir = VUSBDIRECTION_INVALID;
3127 return false;
3128 }
3129 }
3130 }
3131
3132 *pEnmDir = enmDir;
3133 return true;
3134}
3135
3136
3137/**
3138 * Service a general transport descriptor.
3139 */
3140static bool ohciR3ServiceTd(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, VUSBXFERTYPE enmType,
3141 PCOHCIED pEd, uint32_t EdAddr, uint32_t TdAddr, uint32_t *pNextTdAddr, const char *pszListName)
3142{
3143 RT_NOREF(pszListName);
3144
3145 /*
3146 * Read the TD and setup the buffer data.
3147 */
3148 OHCITD Td;
3149 ohciR3ReadTd(pDevIns, TdAddr, &Td);
3150 OHCIBUF Buf;
3151 ohciR3BufInit(&Buf, Td.cbp, Td.be);
3152
3153 *pNextTdAddr = Td.NextTD & ED_PTR_MASK;
3154
3155 /*
3156 * Determine the direction.
3157 */
3158 VUSBDIRECTION enmDir;
3159 switch (pEd->hwinfo & ED_HWINFO_DIR)
3160 {
3161 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3162 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3163 default:
3164 switch (Td.hwinfo & TD_HWINFO_DIR)
3165 {
3166 case TD_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3167 case TD_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3168 case 0: enmDir = VUSBDIRECTION_SETUP; break;
3169 default:
3170 Log(("ohciR3ServiceTd: Invalid direction!!!! Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Td.hwinfo, pEd->hwinfo));
3171 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 2);
3172 return false;
3173 }
3174 break;
3175 }
3176
3177 pThis->fIdle = false; /* Mark as active */
3178
3179 /*
3180 * Allocate and initialize a new URB.
3181 */
3182 PVUSBURB pUrb = VUSBIRhNewUrb(pThisCC->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, VUSB_DEVICE_PORT_INVALID,
3183 enmType, enmDir, Buf.cbTotal, 1, NULL);
3184 if (!pUrb)
3185 return false; /* retry later... */
3186
3187 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3188 pUrb->fShortNotOk = !(Td.hwinfo & TD_HWINFO_ROUNDING);
3189 pUrb->enmStatus = VUSBSTATUS_OK;
3190 pUrb->pHci->EdAddr = EdAddr;
3191 pUrb->pHci->fUnlinked = false;
3192 pUrb->pHci->cTds = 1;
3193 pUrb->paTds[0].TdAddr = TdAddr;
3194 pUrb->pHci->u32FrameNo = pThis->HcFmNumber;
3195 AssertCompile(sizeof(pUrb->paTds[0].TdCopy) >= sizeof(Td));
3196 memcpy(pUrb->paTds[0].TdCopy, &Td, sizeof(Td));
3197
3198 /* copy data if out bound transfer. */
3199 pUrb->cbData = Buf.cbTotal;
3200 if ( Buf.cbTotal
3201 && Buf.cVecs > 0
3202 && enmDir != VUSBDIRECTION_IN)
3203 {
3204 /* Be paranoid. */
3205 if ( Buf.aVecs[0].cb > pUrb->cbData
3206 || ( Buf.cVecs > 1
3207 && Buf.aVecs[1].cb > (pUrb->cbData - Buf.aVecs[0].cb)))
3208 {
3209 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 3);
3210 VUSBIRhFreeUrb(pThisCC->RootHub.pIRhConn, pUrb);
3211 return false;
3212 }
3213
3214 ohciR3PhysRead(pDevIns, Buf.aVecs[0].Addr, pUrb->abData, Buf.aVecs[0].cb);
3215 if (Buf.cVecs > 1)
3216 ohciR3PhysRead(pDevIns, Buf.aVecs[1].Addr, &pUrb->abData[Buf.aVecs[0].cb], Buf.aVecs[1].cb);
3217 }
3218
3219 /*
3220 * Submit the URB.
3221 */
3222 ohciR3InFlightAdd(pThis, pThisCC, TdAddr, pUrb);
3223 Log(("%s: ohciR3ServiceTd: submitting TdAddr=%#010x EdAddr=%#010x cbData=%#x\n",
3224 pUrb->pszDesc, TdAddr, EdAddr, pUrb->cbData));
3225
3226 ohciR3Unlock(pThisCC);
3227 int rc = VUSBIRhSubmitUrb(pThisCC->RootHub.pIRhConn, pUrb, &pThisCC->RootHub.Led);
3228 ohciR3Lock(pThisCC);
3229 if (RT_SUCCESS(rc))
3230 return true;
3231
3232 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
3233 Log(("ohciR3ServiceTd: failed submitting TdAddr=%#010x EdAddr=%#010x pUrb=%p!!\n",
3234 TdAddr, EdAddr, pUrb));
3235 ohciR3InFlightRemove(pThis, pThisCC, TdAddr);
3236 return false;
3237}
3238
3239
3240/**
3241 * Service a the head TD of an endpoint.
3242 */
3243static bool ohciR3ServiceHeadTd(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, VUSBXFERTYPE enmType,
3244 PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
3245{
3246 /*
3247 * Read the TD, after first checking if it's already in-flight.
3248 */
3249 uint32_t TdAddr = pEd->HeadP & ED_PTR_MASK;
3250 if (ohciR3IsTdInFlight(pThisCC, TdAddr))
3251 return false;
3252# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
3253 ohciR3InDoneQueueCheck(pThisCC, TdAddr);
3254# endif
3255 return ohciR3ServiceTd(pDevIns, pThis, pThisCC, enmType, pEd, EdAddr, TdAddr, &TdAddr, pszListName);
3256}
3257
3258
3259/**
3260 * Service one or more general transport descriptors (bulk or interrupt).
3261 */
3262static bool ohciR3ServiceTdMultiple(PPDMDEVINS pDevIns, POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr,
3263 uint32_t TdAddr, uint32_t *pNextTdAddr, const char *pszListName)
3264{
3265 RT_NOREF(pszListName);
3266
3267 /*
3268 * Read the TDs involved in this URB.
3269 */
3270 struct OHCITDENTRY
3271 {
3272 /** The TD. */
3273 OHCITD Td;
3274 /** The associated OHCI buffer tracker. */
3275 OHCIBUF Buf;
3276 /** The TD address. */
3277 uint32_t TdAddr;
3278 /** Pointer to the next element in the chain (stack). */
3279 struct OHCITDENTRY *pNext;
3280 } Head;
3281
3282 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
3283# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
3284 ohciR3PhysReadCacheInvalidate(&pThisCC->CacheTD);
3285# endif
3286
3287 /* read the head */
3288 ohciR3ReadTd(pDevIns, TdAddr, &Head.Td);
3289 ohciR3BufInit(&Head.Buf, Head.Td.cbp, Head.Td.be);
3290 Head.TdAddr = TdAddr;
3291 Head.pNext = NULL;
3292
3293 /* combine with more TDs. */
3294 struct OHCITDENTRY *pTail = &Head;
3295 unsigned cbTotal = pTail->Buf.cbTotal;
3296 unsigned cTds = 1;
3297 while ( (pTail->Buf.cbTotal == 0x1000 || pTail->Buf.cbTotal == 0x2000)
3298 && !(pTail->Td.hwinfo & TD_HWINFO_ROUNDING) /* This isn't right for *BSD, but let's not . */
3299 && (pTail->Td.NextTD & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3300 && cTds < 128)
3301 {
3302 struct OHCITDENTRY *pCur = (struct OHCITDENTRY *)alloca(sizeof(*pCur));
3303
3304 pCur->pNext = NULL;
3305 pCur->TdAddr = pTail->Td.NextTD & ED_PTR_MASK;
3306 ohciR3ReadTd(pDevIns, pCur->TdAddr, &pCur->Td);
3307 ohciR3BufInit(&pCur->Buf, pCur->Td.cbp, pCur->Td.be);
3308
3309 /* Don't combine if the direction doesn't match up. There can't actually be
3310 * a mismatch for bulk/interrupt EPs unless the guest is buggy.
3311 */
3312 if ( (pCur->Td.hwinfo & (TD_HWINFO_DIR))
3313 != (Head.Td.hwinfo & (TD_HWINFO_DIR)))
3314 break;
3315
3316 pTail->pNext = pCur;
3317 pTail = pCur;
3318 cbTotal += pCur->Buf.cbTotal;
3319 cTds++;
3320 }
3321
3322 /* calc next TD address */
3323 *pNextTdAddr = pTail->Td.NextTD & ED_PTR_MASK;
3324
3325 /*
3326 * Determine the direction.
3327 */
3328 VUSBDIRECTION enmDir;
3329 switch (pEd->hwinfo & ED_HWINFO_DIR)
3330 {
3331 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3332 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3333 default:
3334 Log(("ohciR3ServiceTdMultiple: WARNING! Ed.hwdinfo=%#x bulk or interrupt EP shouldn't rely on the TD for direction...\n", pEd->hwinfo));
3335 switch (Head.Td.hwinfo & TD_HWINFO_DIR)
3336 {
3337 case TD_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3338 case TD_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3339 default:
3340 Log(("ohciR3ServiceTdMultiple: Invalid direction!!!! Head.Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Head.Td.hwinfo, pEd->hwinfo));
3341 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 4);
3342 return false;
3343 }
3344 break;
3345 }
3346
3347 pThis->fIdle = false; /* Mark as active */
3348
3349 /*
3350 * Allocate and initialize a new URB.
3351 */
3352 PVUSBURB pUrb = VUSBIRhNewUrb(pThisCC->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, VUSB_DEVICE_PORT_INVALID,
3353 enmType, enmDir, cbTotal, cTds, "ohciR3ServiceTdMultiple");
3354 if (!pUrb)
3355 /* retry later... */
3356 return false;
3357 Assert(pUrb->cbData == cbTotal);
3358
3359 pUrb->enmType = enmType;
3360 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3361 pUrb->enmDir = enmDir;
3362 pUrb->fShortNotOk = !(pTail->Td.hwinfo & TD_HWINFO_ROUNDING);
3363 pUrb->enmStatus = VUSBSTATUS_OK;
3364 pUrb->pHci->cTds = cTds;
3365 pUrb->pHci->EdAddr = EdAddr;
3366 pUrb->pHci->fUnlinked = false;
3367 pUrb->pHci->u32FrameNo = pThis->HcFmNumber;
3368
3369 /* Copy data and TD information. */
3370 unsigned iTd = 0;
3371 uint8_t *pb = &pUrb->abData[0];
3372 for (struct OHCITDENTRY *pCur = &Head; pCur; pCur = pCur->pNext, iTd++)
3373 {
3374 /* data */
3375 if ( cbTotal
3376 && enmDir != VUSBDIRECTION_IN
3377 && pCur->Buf.cVecs > 0)
3378 {
3379 ohciR3PhysRead(pDevIns, pCur->Buf.aVecs[0].Addr, pb, pCur->Buf.aVecs[0].cb);
3380 if (pCur->Buf.cVecs > 1)
3381 ohciR3PhysRead(pDevIns, pCur->Buf.aVecs[1].Addr, pb + pCur->Buf.aVecs[0].cb, pCur->Buf.aVecs[1].cb);
3382 }
3383 pb += pCur->Buf.cbTotal;
3384
3385 /* TD info */
3386 pUrb->paTds[iTd].TdAddr = pCur->TdAddr;
3387 AssertCompile(sizeof(pUrb->paTds[iTd].TdCopy) >= sizeof(pCur->Td));
3388 memcpy(pUrb->paTds[iTd].TdCopy, &pCur->Td, sizeof(pCur->Td));
3389 }
3390
3391 /*
3392 * Submit the URB.
3393 */
3394 ohciR3InFlightAddUrb(pThis, pThisCC, pUrb);
3395 Log(("%s: ohciR3ServiceTdMultiple: submitting cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x\n",
3396 pUrb->pszDesc, pUrb->cbData, EdAddr, cTds, TdAddr));
3397 ohciR3Unlock(pThisCC);
3398 int rc = VUSBIRhSubmitUrb(pThisCC->RootHub.pIRhConn, pUrb, &pThisCC->RootHub.Led);
3399 ohciR3Lock(pThisCC);
3400 if (RT_SUCCESS(rc))
3401 return true;
3402
3403 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
3404 Log(("ohciR3ServiceTdMultiple: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x - rc=%Rrc\n",
3405 pUrb, cbTotal, EdAddr, cTds, TdAddr, rc));
3406 /* NB: We cannot call ohciR3InFlightRemoveUrb() because the URB is already gone! */
3407 for (struct OHCITDENTRY *pCur = &Head; pCur; pCur = pCur->pNext, iTd++)
3408 ohciR3InFlightRemove(pThis, pThisCC, pCur->TdAddr);
3409 return false;
3410}
3411
3412
3413/**
3414 * Service the head TD of an endpoint.
3415 */
3416static bool ohciR3ServiceHeadTdMultiple(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, VUSBXFERTYPE enmType,
3417 PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
3418{
3419 /*
3420 * First, check that it's not already in-flight.
3421 */
3422 uint32_t TdAddr = pEd->HeadP & ED_PTR_MASK;
3423 if (ohciR3IsTdInFlight(pThisCC, TdAddr))
3424 return false;
3425# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
3426 ohciR3InDoneQueueCheck(pThisCC, TdAddr);
3427# endif
3428 return ohciR3ServiceTdMultiple(pDevIns, pThis, enmType, pEd, EdAddr, TdAddr, &TdAddr, pszListName);
3429}
3430
3431
3432/**
3433 * A worker for ohciR3ServiceIsochronousEndpoint which unlinks a ITD
3434 * that belongs to the past.
3435 */
3436static bool ohciR3ServiceIsochronousTdUnlink(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, POHCIITD pITd, uint32_t ITdAddr,
3437 uint32_t ITdAddrPrev, PVUSBURB pUrb, POHCIED pEd, uint32_t EdAddr)
3438{
3439 LogFlow(("%s%sohciR3ServiceIsochronousTdUnlink: Unlinking ITD: ITdAddr=%#010x EdAddr=%#010x ITdAddrPrev=%#010x\n",
3440 pUrb ? pUrb->pszDesc : "", pUrb ? ": " : "", ITdAddr, EdAddr, ITdAddrPrev));
3441
3442 /*
3443 * Do the unlinking.
3444 */
3445 const uint32_t ITdAddrNext = pITd->NextTD & ED_PTR_MASK;
3446 if (ITdAddrPrev)
3447 {
3448 /* Get validate the previous TD */
3449 int iInFlightPrev = ohciR3InFlightFind(pThisCC, ITdAddrPrev);
3450 AssertMsgReturn(iInFlightPrev >= 0, ("ITdAddr=%#RX32\n", ITdAddrPrev), false);
3451 PVUSBURB pUrbPrev = pThisCC->aInFlight[iInFlightPrev].pUrb;
3452 if (ohciR3HasUrbBeenCanceled(pDevIns, pThis, pUrbPrev, pEd)) /* ensures the copy is correct. */
3453 return false;
3454
3455 /* Update the copy and write it back. */
3456 POHCIITD pITdPrev = ((POHCIITD)pUrbPrev->paTds[0].TdCopy);
3457 pITdPrev->NextTD = (pITdPrev->NextTD & ~ED_PTR_MASK) | ITdAddrNext;
3458 ohciR3WriteITd(pDevIns, pThis, ITdAddrPrev, pITdPrev, "ohciR3ServiceIsochronousEndpoint");
3459 }
3460 else
3461 {
3462 /* It's the head node. update the copy from the caller and write it back. */
3463 pEd->HeadP = (pEd->HeadP & ~ED_PTR_MASK) | ITdAddrNext;
3464 ohciR3WriteEd(pDevIns, EdAddr, pEd);
3465 }
3466
3467 /*
3468 * If it's in flight, just mark the URB as unlinked (there is only one ITD per URB atm).
3469 * Otherwise, retire it to the done queue with an error and cause a done line interrupt (?).
3470 */
3471 if (pUrb)
3472 {
3473 pUrb->pHci->fUnlinked = true;
3474 if (ohciR3HasUrbBeenCanceled(pDevIns, pThis, pUrb, pEd)) /* ensures the copy is correct (paranoia). */
3475 return false;
3476
3477 POHCIITD pITdCopy = ((POHCIITD)pUrb->paTds[0].TdCopy);
3478 pITd->NextTD = pITdCopy->NextTD &= ~ED_PTR_MASK;
3479 }
3480 else
3481 {
3482 pITd->HwInfo &= ~ITD_HWINFO_CC;
3483 pITd->HwInfo |= OHCI_CC_DATA_OVERRUN;
3484
3485 pITd->NextTD = pThis->done;
3486 pThis->done = ITdAddr;
3487
3488 pThis->dqic = 0;
3489 }
3490
3491 ohciR3WriteITd(pDevIns, pThis, ITdAddr, pITd, "ohciR3ServiceIsochronousTdUnlink");
3492 return true;
3493}
3494
3495
3496/**
3497 * A worker for ohciR3ServiceIsochronousEndpoint which submits the specified TD.
3498 *
3499 * @returns true on success.
3500 * @returns false on failure to submit.
3501 * @param pDevIns The device instance.
3502 * @param pThis The OHCI controller instance data, shared edition.
3503 * @param pThisCC The OHCI controller instance data, ring-3 edition.
3504 * @param pITd The transfer descriptor to service.
3505 * @param ITdAddr The address of the transfer descriptor in gues memory.
3506 * @param R The start packet (frame) relative to the start of frame in HwInfo.
3507 * @param pEd The OHCI endpoint descriptor.
3508 * @param EdAddr The endpoint descriptor address in guest memory.
3509 */
3510static bool ohciR3ServiceIsochronousTd(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC,
3511 POHCIITD pITd, uint32_t ITdAddr, const unsigned R, PCOHCIED pEd, uint32_t EdAddr)
3512{
3513 /*
3514 * Determine the endpoint direction.
3515 */
3516 VUSBDIRECTION enmDir;
3517 switch (pEd->hwinfo & ED_HWINFO_DIR)
3518 {
3519 case ED_HWINFO_OUT: enmDir = VUSBDIRECTION_OUT; break;
3520 case ED_HWINFO_IN: enmDir = VUSBDIRECTION_IN; break;
3521 default:
3522 Log(("ohciR3ServiceIsochronousTd: Invalid direction!!!! Ed.hwdinfo=%#x\n", pEd->hwinfo));
3523 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 5);
3524 return false;
3525 }
3526
3527 /*
3528 * Extract the packet sizes and calc the total URB size.
3529 */
3530 struct
3531 {
3532 uint16_t cb;
3533 uint16_t off;
3534 } aPkts[ITD_NUM_PSW];
3535
3536 /* first entry (R) */
3537 uint32_t cbTotal = 0;
3538 if (((uint32_t)pITd->aPSW[R] >> ITD_PSW_CC_SHIFT) < (OHCI_CC_NOT_ACCESSED_0 >> TD_HWINFO_CC_SHIFT))
3539 {
3540 Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, R, pITd->aPSW[R] >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
3541 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 9);
3542 return false;
3543 }
3544 uint16_t offPrev = aPkts[0].off = (pITd->aPSW[R] & ITD_PSW_OFFSET);
3545
3546 /* R+1..cFrames */
3547 const unsigned cFrames = ((pITd->HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
3548 for (unsigned iR = R + 1; iR < cFrames; iR++)
3549 {
3550 const uint16_t PSW = pITd->aPSW[iR];
3551 const uint16_t off = aPkts[iR - R].off = (PSW & ITD_PSW_OFFSET);
3552 cbTotal += aPkts[iR - R - 1].cb = off - offPrev;
3553 if (off < offPrev)
3554 {
3555 Log(("ITdAddr=%RX32 PSW%d.offset=%#x < offPrev=%#x!\n", ITdAddr, iR, off, offPrev)); /* => Unrecoverable Error*/
3556 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 6);
3557 return false;
3558 }
3559 if (((uint32_t)PSW >> ITD_PSW_CC_SHIFT) < (OHCI_CC_NOT_ACCESSED_0 >> TD_HWINFO_CC_SHIFT))
3560 {
3561 Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, iR, PSW >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
3562 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 7);
3563 return false;
3564 }
3565 offPrev = off;
3566 }
3567
3568 /* calc offEnd and figure out the size of the last packet. */
3569 const uint32_t offEnd = (pITd->BE & 0xfff)
3570 + (((pITd->BE & ITD_BP0_MASK) != (pITd->BP0 & ITD_BP0_MASK)) << 12)
3571 + 1 /* BE is inclusive */;
3572 if (offEnd < offPrev)
3573 {
3574 Log(("ITdAddr=%RX32 offEnd=%#x < offPrev=%#x!\n", ITdAddr, offEnd, offPrev)); /* => Unrecoverable Error*/
3575 ohciR3RaiseUnrecoverableError(pDevIns, pThis, 8);
3576 return false;
3577 }
3578 cbTotal += aPkts[cFrames - 1 - R].cb = offEnd - offPrev;
3579 Assert(cbTotal <= 0x2000);
3580
3581 pThis->fIdle = false; /* Mark as active */
3582
3583 /*
3584 * Allocate and initialize a new URB.
3585 */
3586 PVUSBURB pUrb = VUSBIRhNewUrb(pThisCC->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, VUSB_DEVICE_PORT_INVALID,
3587 VUSBXFERTYPE_ISOC, enmDir, cbTotal, 1, NULL);
3588 if (!pUrb)
3589 /* retry later... */
3590 return false;
3591
3592 pUrb->EndPt = (pEd->hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3593 pUrb->fShortNotOk = false;
3594 pUrb->enmStatus = VUSBSTATUS_OK;
3595 pUrb->pHci->EdAddr = EdAddr;
3596 pUrb->pHci->cTds = 1;
3597 pUrb->pHci->fUnlinked = false;
3598 pUrb->pHci->u32FrameNo = pThis->HcFmNumber;
3599 pUrb->paTds[0].TdAddr = ITdAddr;
3600 AssertCompile(sizeof(pUrb->paTds[0].TdCopy) >= sizeof(*pITd));
3601 memcpy(pUrb->paTds[0].TdCopy, pITd, sizeof(*pITd));
3602# if 0 /* color the data */
3603 memset(pUrb->abData, 0xfe, cbTotal);
3604# endif
3605
3606 /* copy the data */
3607 if ( cbTotal
3608 && enmDir != VUSBDIRECTION_IN)
3609 {
3610 const uint32_t off0 = pITd->aPSW[R] & ITD_PSW_OFFSET;
3611 if (off0 < 0x1000)
3612 {
3613 if (offEnd > 0x1000)
3614 {
3615 /* both pages. */
3616 const unsigned cb0 = 0x1000 - off0;
3617 ohciR3PhysRead(pDevIns, (pITd->BP0 & ITD_BP0_MASK) + off0, &pUrb->abData[0], cb0);
3618 ohciR3PhysRead(pDevIns, pITd->BE & ITD_BP0_MASK, &pUrb->abData[cb0], offEnd & 0xfff);
3619 }
3620 else /* a portion of the 1st page. */
3621 ohciR3PhysRead(pDevIns, (pITd->BP0 & ITD_BP0_MASK) + off0, pUrb->abData, offEnd - off0);
3622 }
3623 else /* a portion of the 2nd page. */
3624 ohciR3PhysRead(pDevIns, (pITd->BE & UINT32_C(0xfffff000)) + (off0 & 0xfff), pUrb->abData, cbTotal);
3625 }
3626
3627 /* setup the packets */
3628 pUrb->cIsocPkts = cFrames - R;
3629 unsigned off = 0;
3630 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
3631 {
3632 pUrb->aIsocPkts[i].enmStatus = VUSBSTATUS_NOT_ACCESSED;
3633 pUrb->aIsocPkts[i].off = off;
3634 off += pUrb->aIsocPkts[i].cb = aPkts[i].cb;
3635 }
3636 Assert(off == cbTotal);
3637
3638 /*
3639 * Submit the URB.
3640 */
3641 ohciR3InFlightAdd(pThis, pThisCC, ITdAddr, pUrb);
3642 Log(("%s: ohciR3ServiceIsochronousTd: submitting cbData=%#x cIsocPkts=%d EdAddr=%#010x TdAddr=%#010x SF=%#x (%#x)\n",
3643 pUrb->pszDesc, pUrb->cbData, pUrb->cIsocPkts, EdAddr, ITdAddr, pITd->HwInfo & ITD_HWINFO_SF, pThis->HcFmNumber));
3644 ohciR3Unlock(pThisCC);
3645 int rc = VUSBIRhSubmitUrb(pThisCC->RootHub.pIRhConn, pUrb, &pThisCC->RootHub.Led);
3646 ohciR3Lock(pThisCC);
3647 if (RT_SUCCESS(rc))
3648 return true;
3649
3650 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
3651 Log(("ohciR3ServiceIsochronousTd: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d ITdAddr0=%#010x - rc=%Rrc\n",
3652 pUrb, cbTotal, EdAddr, 1, ITdAddr, rc));
3653 ohciR3InFlightRemove(pThis, pThisCC, ITdAddr);
3654 return false;
3655}
3656
3657
3658/**
3659 * Service an isochronous endpoint.
3660 */
3661static void ohciR3ServiceIsochronousEndpoint(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, POHCIED pEd, uint32_t EdAddr)
3662{
3663 /*
3664 * We currently process this as if the guest follows the interrupt end point chaining
3665 * hierarchy described in the documenation. This means that for an isochronous endpoint
3666 * with a 1 ms interval we expect to find in-flight TDs at the head of the list. We will
3667 * skip over all in-flight TDs whose timeframe has been exceeded. Those which aren't in
3668 * flight but which are too late will be retired (possibly out of order, but, we don't
3669 * care right now).
3670 *
3671 * When we reach a TD which still has a buffer which is due for take off, we will
3672 * stop iterating TDs. If it's in-flight, there isn't anything to be done. Otherwise
3673 * we will push it onto the runway for immediate take off. In this process we
3674 * might have to complete buffers which didn't make it on time, something which
3675 * complicates the kind of status info we need to keep around for the TD.
3676 *
3677 * Note: We're currently not making any attempt at reassembling ITDs into URBs.
3678 * However, this will become necessary because of EMT scheduling and guest
3679 * like linux using one TD for each frame (simple but inefficient for us).
3680 */
3681 OHCIITD ITd;
3682 uint32_t ITdAddr = pEd->HeadP & ED_PTR_MASK;
3683 uint32_t ITdAddrPrev = 0;
3684 uint32_t u32NextFrame = UINT32_MAX;
3685 const uint16_t u16CurFrame = pThis->HcFmNumber;
3686 for (;;)
3687 {
3688 /* check for end-of-chain. */
3689 if ( ITdAddr == (pEd->TailP & ED_PTR_MASK)
3690 || !ITdAddr)
3691 break;
3692
3693 /*
3694 * If isochronous endpoints are around, don't slow down the timer. Getting the timing right
3695 * is difficult enough as it is.
3696 */
3697 pThis->fIdle = false;
3698
3699 /*
3700 * Read the current ITD and check what we're supposed to do about it.
3701 */
3702 ohciR3ReadITd(pDevIns, pThis, ITdAddr, &ITd);
3703 const uint32_t ITdAddrNext = ITd.NextTD & ED_PTR_MASK;
3704 const int16_t R = u16CurFrame - (uint16_t)(ITd.HwInfo & ITD_HWINFO_SF); /* 4.3.2.3 */
3705 const int16_t cFrames = ((ITd.HwInfo & ITD_HWINFO_FC) >> ITD_HWINFO_FC_SHIFT) + 1;
3706
3707 if (R < cFrames)
3708 {
3709 /*
3710 * It's inside the current or a future launch window.
3711 *
3712 * We will try maximize the TD in flight here to deal with EMT scheduling
3713 * issues and similar stuff which will screw up the time. So, we will only
3714 * stop submitting TD when we reach a gap (in time) or end of the list.
3715 */
3716 if ( R < 0 /* (a future frame) */
3717 && (uint16_t)u32NextFrame != (uint16_t)(ITd.HwInfo & ITD_HWINFO_SF))
3718 break;
3719 if (ohciR3InFlightFind(pThisCC, ITdAddr) < 0)
3720 if (!ohciR3ServiceIsochronousTd(pDevIns, pThis, pThisCC, &ITd, ITdAddr, R < 0 ? 0 : R, pEd, EdAddr))
3721 break;
3722
3723 ITdAddrPrev = ITdAddr;
3724 }
3725 else
3726 {
3727# if 1
3728 /*
3729 * Ok, the launch window for this TD has passed.
3730 * If it's not in flight it should be retired with a DataOverrun status (TD).
3731 *
3732 * Don't remove in-flight TDs before they complete.
3733 * Windows will, upon the completion of another ITD it seems, check for if
3734 * any other TDs has been unlinked. If we unlink them before they really
3735 * complete all the packet status codes will be NotAccessed and Windows
3736 * will fail the URB with status USBD_STATUS_ISOCH_REQUEST_FAILED.
3737 *
3738 * I don't know if unlinking TDs out of order could cause similar problems,
3739 * time will show.
3740 */
3741 int iInFlight = ohciR3InFlightFind(pThisCC, ITdAddr);
3742 if (iInFlight >= 0)
3743 ITdAddrPrev = ITdAddr;
3744 else if (!ohciR3ServiceIsochronousTdUnlink(pDevIns, pThis, pThisCC, &ITd, ITdAddr, ITdAddrPrev, NULL, pEd, EdAddr))
3745 {
3746 Log(("ohciR3ServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
3747 break;
3748 }
3749# else /* BAD IDEA: */
3750 /*
3751 * Ok, the launch window for this TD has passed.
3752 * If it's not in flight it should be retired with a DataOverrun status (TD).
3753 *
3754 * If it's in flight we will try unlink it from the list prematurely to
3755 * help the guest to move on and shorten the list we have to walk. We currently
3756 * are successful with the first URB but then it goes too slowly...
3757 */
3758 int iInFlight = ohciR3InFlightFind(pThis, ITdAddr);
3759 if (!ohciR3ServiceIsochronousTdUnlink(pThis, &ITd, ITdAddr, ITdAddrPrev,
3760 iInFlight < 0 ? NULL : pThis->aInFlight[iInFlight].pUrb,
3761 pEd, EdAddr))
3762 {
3763 Log(("ohciR3ServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
3764 break;
3765 }
3766# endif
3767 }
3768
3769 /* advance to the next ITD */
3770 ITdAddr = ITdAddrNext;
3771 u32NextFrame = (ITd.HwInfo & ITD_HWINFO_SF) + cFrames;
3772 }
3773}
3774
3775
3776/**
3777 * Checks if a endpoints has TDs queued and is ready to have them processed.
3778 *
3779 * @returns true if it's ok to process TDs.
3780 * @param pEd The endpoint data.
3781 */
3782DECLINLINE(bool) ohciR3IsEdReady(PCOHCIED pEd)
3783{
3784 return (pEd->HeadP & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3785 && !(pEd->HeadP & ED_HEAD_HALTED)
3786 && !(pEd->hwinfo & ED_HWINFO_SKIP);
3787}
3788
3789
3790/**
3791 * Checks if an endpoint has TDs queued (not necessarily ready to have them processed).
3792 *
3793 * @returns true if endpoint may have TDs queued.
3794 * @param pEd The endpoint data.
3795 */
3796DECLINLINE(bool) ohciR3IsEdPresent(PCOHCIED pEd)
3797{
3798 return (pEd->HeadP & ED_PTR_MASK) != (pEd->TailP & ED_PTR_MASK)
3799 && !(pEd->HeadP & ED_HEAD_HALTED);
3800}
3801
3802
3803/**
3804 * Services the bulk list.
3805 *
3806 * On the bulk list we must reassemble URBs from multiple TDs using heuristics
3807 * derived from USB tracing done in the guests and guest source code (when available).
3808 */
3809static void ohciR3ServiceBulkList(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
3810{
3811# ifdef LOG_ENABLED
3812 if (g_fLogBulkEPs)
3813 ohciR3DumpEdList(pDevIns, pThisCC, pThis->bulk_head, "Bulk before", true);
3814 if (pThis->bulk_cur)
3815 Log(("ohciR3ServiceBulkList: bulk_cur=%#010x before listprocessing!!! HCD have positioned us!!!\n", pThis->bulk_cur));
3816# endif
3817
3818 /*
3819 * ", HC will start processing the Bulk list and will set BF [BulkListFilled] to 0"
3820 * - We've simplified and are always starting at the head of the list and working
3821 * our way thru to the end each time.
3822 */
3823 pThis->status &= ~OHCI_STATUS_BLF;
3824 pThis->fBulkNeedsCleaning = false;
3825 pThis->bulk_cur = 0;
3826
3827 uint32_t EdAddr = pThis->bulk_head;
3828 uint32_t cIterations = 256;
3829 while (EdAddr
3830 && (pThis->ctl & OHCI_CTL_BLE)
3831 && (cIterations-- > 0))
3832 {
3833 OHCIED Ed;
3834
3835 /* Bail if previous processing ended up in the unrecoverable error state. */
3836 if (pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR)
3837 break;
3838
3839 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
3840 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3841 if (ohciR3IsEdReady(&Ed))
3842 {
3843 pThis->status |= OHCI_STATUS_BLF;
3844 pThis->fBulkNeedsCleaning = true;
3845
3846# if 1
3847 /*
3848 * After we figured out that all the TDs submitted for dealing with MSD
3849 * read/write data really makes up on single URB, and that we must
3850 * reassemble these TDs into an URB before submitting it, there is no
3851 * longer any need for servicing anything other than the head *URB*
3852 * on a bulk endpoint.
3853 */
3854 ohciR3ServiceHeadTdMultiple(pDevIns, pThis, pThisCC, VUSBXFERTYPE_BULK, &Ed, EdAddr, "Bulk");
3855# else
3856 /*
3857 * This alternative code was used before we started reassembling URBs from
3858 * multiple TDs. We keep it handy for debugging.
3859 */
3860 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3861 if (!ohciR3IsTdInFlight(pThis, TdAddr))
3862 {
3863 do
3864 {
3865 if (!ohciR3ServiceTdMultiple(pThis, VUSBXFERTYPE_BULK, &Ed, EdAddr, TdAddr, &TdAddr, "Bulk"))
3866 {
3867 LogFlow(("ohciR3ServiceBulkList: ohciR3ServiceTdMultiple -> false\n"));
3868 break;
3869 }
3870 if ( (TdAddr & ED_PTR_MASK) == (Ed.TailP & ED_PTR_MASK)
3871 || !TdAddr /* paranoia */)
3872 {
3873 LogFlow(("ohciR3ServiceBulkList: TdAddr=%#010RX32 Ed.TailP=%#010RX32\n", TdAddr, Ed.TailP));
3874 break;
3875 }
3876
3877 ohciR3ReadEd(pDevIns, EdAddr, &Ed); /* It might have been updated on URB completion. */
3878 } while (ohciR3IsEdReady(&Ed));
3879 }
3880# endif
3881 }
3882 else
3883 {
3884 if (Ed.hwinfo & ED_HWINFO_SKIP)
3885 {
3886 LogFlow(("ohciR3ServiceBulkList: Ed=%#010RX32 Ed.TailP=%#010RX32 SKIP\n", EdAddr, Ed.TailP));
3887 /* If the ED is in 'skip' state, no transactions on it are allowed and we must
3888 * cancel outstanding URBs, if any.
3889 */
3890 uint8_t uAddr = Ed.hwinfo & ED_HWINFO_FUNCTION;
3891 uint8_t uEndPt = (Ed.hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3892 VUSBDIRECTION enmDir;
3893 if (ohciR3GetDirection(pDevIns, pThis, pThisCC, &Ed, &enmDir))
3894 {
3895 pThisCC->RootHub.pIRhConn->pfnAbortEpByAddr(pThisCC->RootHub.pIRhConn, uAddr, uEndPt, enmDir);
3896 }
3897 }
3898 }
3899
3900 /* Trivial loop detection. */
3901 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
3902 break;
3903 /* Proceed to the next endpoint. */
3904 EdAddr = Ed.NextED & ED_PTR_MASK;
3905 }
3906
3907# ifdef LOG_ENABLED
3908 if (g_fLogBulkEPs)
3909 ohciR3DumpEdList(pDevIns, pThisCC, pThis->bulk_head, "Bulk after ", true);
3910# endif
3911}
3912
3913
3914/**
3915 * Abort outstanding transfers on the bulk list.
3916 *
3917 * If the guest disabled bulk list processing, we must abort any outstanding transfers
3918 * (that is, cancel in-flight URBs associated with the list). This is required because
3919 * there may be outstanding read URBs that will never get a response from the device
3920 * and would block further communication.
3921 */
3922static void ohciR3UndoBulkList(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
3923{
3924# ifdef LOG_ENABLED
3925 if (g_fLogBulkEPs)
3926 ohciR3DumpEdList(pDevIns, pThisCC, pThis->bulk_head, "Bulk before", true);
3927 if (pThis->bulk_cur)
3928 Log(("ohciR3UndoBulkList: bulk_cur=%#010x before list processing!!! HCD has positioned us!!!\n", pThis->bulk_cur));
3929# endif
3930
3931 /* This flag follows OHCI_STATUS_BLF, but BLF doesn't change when list processing is disabled. */
3932 pThis->fBulkNeedsCleaning = false;
3933
3934 uint32_t EdAddr = pThis->bulk_head;
3935 uint32_t cIterations = 256;
3936 while (EdAddr
3937 && (cIterations-- > 0))
3938 {
3939 OHCIED Ed;
3940
3941 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
3942 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
3943 if (ohciR3IsEdPresent(&Ed))
3944 {
3945 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
3946 if (ohciR3IsTdInFlight(pThisCC, TdAddr))
3947 {
3948 LogFlow(("ohciR3UndoBulkList: Ed=%#010RX32 Ed.TailP=%#010RX32 UNDO\n", EdAddr, Ed.TailP));
3949 /* First we need to determine the transfer direction, which may fail(!). */
3950 uint8_t uAddr = Ed.hwinfo & ED_HWINFO_FUNCTION;
3951 uint8_t uEndPt = (Ed.hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
3952 VUSBDIRECTION enmDir;
3953 if (ohciR3GetDirection(pDevIns, pThis, pThisCC, &Ed, &enmDir))
3954 {
3955 pThisCC->RootHub.pIRhConn->pfnAbortEpByAddr(pThisCC->RootHub.pIRhConn, uAddr, uEndPt, enmDir);
3956 }
3957 }
3958 }
3959
3960 /* Trivial loop detection. */
3961 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
3962 break;
3963 /* Proceed to the next endpoint. */
3964 EdAddr = Ed.NextED & ED_PTR_MASK;
3965 }
3966}
3967
3968
3969/**
3970 * Services the control list.
3971 *
3972 * The control list has complex URB assembling, but that's taken
3973 * care of at VUSB level (unlike the other transfer types).
3974 */
3975static void ohciR3ServiceCtrlList(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
3976{
3977# ifdef LOG_ENABLED
3978 if (g_fLogControlEPs)
3979 ohciR3DumpEdList(pDevIns, pThisCC, pThis->ctrl_head, "Ctrl before", true);
3980 if (pThis->ctrl_cur)
3981 Log(("ohciR3ServiceCtrlList: ctrl_cur=%010x before list processing!!! HCD have positioned us!!!\n", pThis->ctrl_cur));
3982# endif
3983
3984 /*
3985 * ", HC will start processing the list and will set ControlListFilled to 0"
3986 * - We've simplified and are always starting at the head of the list and working
3987 * our way thru to the end each time.
3988 */
3989 pThis->status &= ~OHCI_STATUS_CLF;
3990 pThis->ctrl_cur = 0;
3991
3992 uint32_t EdAddr = pThis->ctrl_head;
3993 uint32_t cIterations = 256;
3994 while ( EdAddr
3995 && (pThis->ctl & OHCI_CTL_CLE)
3996 && (cIterations-- > 0))
3997 {
3998 OHCIED Ed;
3999
4000 /* Bail if previous processing ended up in the unrecoverable error state. */
4001 if (pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR)
4002 break;
4003
4004 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
4005 Assert(!(Ed.hwinfo & ED_HWINFO_ISO)); /* the guest is screwing us */
4006 if (ohciR3IsEdReady(&Ed))
4007 {
4008# if 1
4009 /*
4010 * Control TDs depends on order and stage. Only one can be in-flight
4011 * at any given time. OTOH, some stages are completed immediately,
4012 * so we process the list until we've got a head which is in-flight
4013 * or reach the end of the list.
4014 */
4015 do
4016 {
4017 if ( !ohciR3ServiceHeadTd(pDevIns, pThis, pThisCC, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control")
4018 || ohciR3IsTdInFlight(pThisCC, Ed.HeadP & ED_PTR_MASK))
4019 {
4020 pThis->status |= OHCI_STATUS_CLF;
4021 break;
4022 }
4023 ohciR3ReadEd(pDevIns, EdAddr, &Ed); /* It might have been updated on URB completion. */
4024 } while (ohciR3IsEdReady(&Ed));
4025# else
4026 /* Simplistic, for debugging. */
4027 ohciR3ServiceHeadTd(pThis, VUSBXFERTYPE_CTRL, &Ed, EdAddr, "Control");
4028 pThis->status |= OHCI_STATUS_CLF;
4029# endif
4030 }
4031
4032 /* Trivial loop detection. */
4033 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
4034 break;
4035 /* Proceed to the next endpoint. */
4036 EdAddr = Ed.NextED & ED_PTR_MASK;
4037 }
4038
4039# ifdef LOG_ENABLED
4040 if (g_fLogControlEPs)
4041 ohciR3DumpEdList(pDevIns, pThisCC, pThis->ctrl_head, "Ctrl after ", true);
4042# endif
4043}
4044
4045
4046/**
4047 * Services the periodic list.
4048 *
4049 * On the interrupt portion of the periodic list we must reassemble URBs from multiple
4050 * TDs using heuristics derived from USB tracing done in the guests and guest source
4051 * code (when available).
4052 */
4053static void ohciR3ServicePeriodicList(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4054{
4055 /*
4056 * Read the list head from the HCCA.
4057 */
4058 const unsigned iList = pThis->HcFmNumber % OHCI_HCCA_NUM_INTR;
4059 uint32_t EdAddr;
4060 ohciR3GetDWords(pDevIns, pThis->hcca + iList * sizeof(EdAddr), &EdAddr, 1);
4061
4062# ifdef LOG_ENABLED
4063 const uint32_t EdAddrHead = EdAddr;
4064 if (g_fLogInterruptEPs)
4065 {
4066 char sz[48];
4067 RTStrPrintf(sz, sizeof(sz), "Int%02x before", iList);
4068 ohciR3DumpEdList(pDevIns, pThisCC, EdAddrHead, sz, true);
4069 }
4070# endif
4071
4072 /*
4073 * Iterate the endpoint list.
4074 */
4075 unsigned cIterations = 128;
4076 while (EdAddr
4077 && (pThis->ctl & OHCI_CTL_PLE)
4078 && (cIterations-- > 0))
4079 {
4080 OHCIED Ed;
4081
4082 /* Bail if previous processing ended up in the unrecoverable error state. */
4083 if (pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR)
4084 break;
4085
4086 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
4087 if (ohciR3IsEdReady(&Ed))
4088 {
4089 /*
4090 * "There is no separate head pointer of isochronous transfers. The first
4091 * isochronous Endpoint Descriptor simply links to the last interrupt
4092 * Endpoint Descriptor."
4093 */
4094 if (!(Ed.hwinfo & ED_HWINFO_ISO))
4095 {
4096 /*
4097 * Presently we will only process the head URB on an interrupt endpoint.
4098 */
4099 ohciR3ServiceHeadTdMultiple(pDevIns, pThis, pThisCC, VUSBXFERTYPE_INTR, &Ed, EdAddr, "Periodic");
4100 }
4101 else if (pThis->ctl & OHCI_CTL_IE)
4102 {
4103 /*
4104 * Presently only the head ITD.
4105 */
4106 ohciR3ServiceIsochronousEndpoint(pDevIns, pThis, pThisCC, &Ed, EdAddr);
4107 }
4108 else
4109 break;
4110 }
4111 else
4112 {
4113 if (Ed.hwinfo & ED_HWINFO_SKIP)
4114 {
4115 Log3(("ohciR3ServicePeriodicList: Ed=%#010RX32 Ed.TailP=%#010RX32 SKIP\n", EdAddr, Ed.TailP));
4116 /* If the ED is in 'skip' state, no transactions on it are allowed and we must
4117 * cancel outstanding URBs, if any.
4118 * First we need to determine the transfer direction, which may fail(!).
4119 */
4120 uint8_t uAddr = Ed.hwinfo & ED_HWINFO_FUNCTION;
4121 uint8_t uEndPt = (Ed.hwinfo & ED_HWINFO_ENDPOINT) >> ED_HWINFO_ENDPOINT_SHIFT;
4122 VUSBDIRECTION enmDir;
4123 if (ohciR3GetDirection(pDevIns, pThis, pThisCC, &Ed, &enmDir))
4124 {
4125 pThisCC->RootHub.pIRhConn->pfnAbortEpByAddr(pThisCC->RootHub.pIRhConn, uAddr, uEndPt, enmDir);
4126 }
4127 }
4128 }
4129 /* Trivial loop detection. */
4130 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
4131 break;
4132 /* Proceed to the next endpoint. */
4133 EdAddr = Ed.NextED & ED_PTR_MASK;
4134 }
4135
4136# ifdef LOG_ENABLED
4137 if (g_fLogInterruptEPs)
4138 {
4139 char sz[48];
4140 RTStrPrintf(sz, sizeof(sz), "Int%02x after ", iList);
4141 ohciR3DumpEdList(pDevIns, pThisCC, EdAddrHead, sz, true);
4142 }
4143# endif
4144}
4145
4146
4147/**
4148 * Update the HCCA.
4149 *
4150 * @param pDevIns The device instance.
4151 * @param pThis The OHCI controller instance data, shared edition.
4152 * @param pThisCC The OHCI controller instance data, ring-3 edition.
4153 */
4154static void ohciR3UpdateHCCA(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4155{
4156 OCHIHCCA hcca;
4157 ohciR3PhysRead(pDevIns, pThis->hcca + OHCI_HCCA_OFS, &hcca, sizeof(hcca));
4158
4159 hcca.frame = RT_H2LE_U16((uint16_t)pThis->HcFmNumber);
4160 hcca.pad = 0;
4161
4162 bool fWriteDoneHeadInterrupt = false;
4163 if ( pThis->dqic == 0
4164 && (pThis->intr_status & OHCI_INTR_WRITE_DONE_HEAD) == 0)
4165 {
4166 uint32_t done = pThis->done;
4167
4168 if (pThis->intr_status & ~( OHCI_INTR_MASTER_INTERRUPT_ENABLED | OHCI_INTR_OWNERSHIP_CHANGE
4169 | OHCI_INTR_WRITE_DONE_HEAD) )
4170 done |= 0x1;
4171
4172 hcca.done = RT_H2LE_U32(done);
4173 pThis->done = 0;
4174 pThis->dqic = 0x7;
4175
4176 Log(("ohci: Writeback Done (%#010x) on frame %#x (age %#x)\n", hcca.done,
4177 pThis->HcFmNumber, pThis->HcFmNumber - pThisCC->u32FmDoneQueueTail));
4178# ifdef LOG_ENABLED
4179 ohciR3DumpTdQueue(pDevIns, pThisCC, hcca.done & ED_PTR_MASK, "DoneQueue");
4180# endif
4181 Assert(RT_OFFSETOF(OCHIHCCA, done) == 4);
4182# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
4183 ohciR3InDoneQueueZap(pThisCC);
4184# endif
4185 fWriteDoneHeadInterrupt = true;
4186 }
4187
4188 Log3(("ohci: Updating HCCA on frame %#x\n", pThis->HcFmNumber));
4189 ohciR3PhysWriteMeta(pDevIns, pThis->hcca + OHCI_HCCA_OFS, (uint8_t *)&hcca, sizeof(hcca));
4190 if (fWriteDoneHeadInterrupt)
4191 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_WRITE_DONE_HEAD);
4192 RT_NOREF(pThisCC);
4193}
4194
4195
4196/**
4197 * Go over the in-flight URB list and cancel any URBs that are no longer in use.
4198 * This occurs when the host removes EDs or TDs from the lists and we don't notice
4199 * the sKip bit. Such URBs must be promptly canceled, otherwise there is a risk
4200 * they might "steal" data destined for another URB.
4201 */
4202static void ohciR3CancelOrphanedURBs(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4203{
4204 bool fValidHCCA = !( pThis->hcca >= OHCI_HCCA_MASK
4205 || pThis->hcca < ~OHCI_HCCA_MASK);
4206 unsigned i, cLeft;
4207 int j;
4208 uint32_t EdAddr;
4209 PVUSBURB pUrb;
4210
4211 /* If the HCCA is not currently valid, or there are no in-flight URBs,
4212 * there's nothing to do.
4213 */
4214 if (!fValidHCCA || !pThisCC->cInFlight)
4215 return;
4216
4217 /* Initially mark all in-flight URBs as inactive. */
4218 for (i = 0, cLeft = pThisCC->cInFlight; cLeft && i < RT_ELEMENTS(pThisCC->aInFlight); i++)
4219 {
4220 if (pThisCC->aInFlight[i].pUrb)
4221 {
4222 pThisCC->aInFlight[i].fInactive = true;
4223 cLeft--;
4224 }
4225 }
4226 Assert(cLeft == 0);
4227
4228# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
4229 /* Get hcca data to minimize calls to ohciR3GetDWords/PDMDevHlpPCIPhysRead. */
4230 uint32_t au32HCCA[OHCI_HCCA_NUM_INTR];
4231 ohciR3GetDWords(pDevIns, pThis->hcca, au32HCCA, OHCI_HCCA_NUM_INTR);
4232# endif
4233
4234 /* Go over all bulk/control/interrupt endpoint lists; any URB found in these lists
4235 * is marked as active again.
4236 */
4237 for (i = 0; i < OHCI_HCCA_NUM_INTR + 2; i++)
4238 {
4239 switch (i)
4240 {
4241 case OHCI_HCCA_NUM_INTR:
4242 EdAddr = pThis->bulk_head;
4243 break;
4244 case OHCI_HCCA_NUM_INTR + 1:
4245 EdAddr = pThis->ctrl_head;
4246 break;
4247 default:
4248# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
4249 EdAddr = au32HCCA[i];
4250# else
4251 ohciR3GetDWords(pDevIns, pThis->hcca + i * sizeof(EdAddr), &EdAddr, 1);
4252# endif
4253 break;
4254 }
4255
4256 unsigned cIterED = 128;
4257 while ( EdAddr
4258 && (cIterED-- > 0))
4259 {
4260 OHCIED Ed;
4261 OHCITD Td;
4262
4263 ohciR3ReadEd(pDevIns, EdAddr, &Ed);
4264 uint32_t TdAddr = Ed.HeadP & ED_PTR_MASK;
4265 uint32_t TailP = Ed.TailP & ED_PTR_MASK;
4266 unsigned cIterTD = 0;
4267 if ( !(Ed.hwinfo & ED_HWINFO_SKIP)
4268 && (TdAddr != TailP))
4269 {
4270# ifdef VBOX_WITH_OHCI_PHYS_READ_CACHE
4271 ohciR3PhysReadCacheInvalidate(&pThisCC->CacheTD);
4272# endif
4273 do
4274 {
4275 ohciR3ReadTd(pDevIns, TdAddr, &Td);
4276 j = ohciR3InFlightFind(pThisCC, TdAddr);
4277 if (j > -1)
4278 pThisCC->aInFlight[j].fInactive = false;
4279 TdAddr = Td.NextTD & ED_PTR_MASK;
4280 /* See @bugref{8125}.
4281 * Sometimes the ED is changed by the guest between ohciR3ReadEd above and here.
4282 * Then the code reads TD pointed by the new TailP, which is not allowed.
4283 * Luckily Windows guests have Td.NextTD = 0 in the tail TD.
4284 * Also having a real TD at 0 is very unlikely.
4285 * So do not continue.
4286 */
4287 if (TdAddr == 0)
4288 break;
4289 /* Failsafe for temporarily looped lists. */
4290 if (++cIterTD == 128)
4291 break;
4292 } while (TdAddr != (Ed.TailP & ED_PTR_MASK));
4293 }
4294 /* Trivial loop detection. */
4295 if (EdAddr == (Ed.NextED & ED_PTR_MASK))
4296 break;
4297 /* Proceed to the next endpoint. */
4298 EdAddr = Ed.NextED & ED_PTR_MASK;
4299 }
4300 }
4301
4302 /* In-flight URBs still marked as inactive are not used anymore and need
4303 * to be canceled.
4304 */
4305 for (i = 0, cLeft = pThisCC->cInFlight; cLeft && i < RT_ELEMENTS(pThisCC->aInFlight); i++)
4306 {
4307 if (pThisCC->aInFlight[i].pUrb)
4308 {
4309 cLeft--;
4310 pUrb = pThisCC->aInFlight[i].pUrb;
4311 if ( pThisCC->aInFlight[i].fInactive
4312 && pUrb->enmState == VUSBURBSTATE_IN_FLIGHT
4313 && pUrb->enmType != VUSBXFERTYPE_CTRL)
4314 pThisCC->RootHub.pIRhConn->pfnCancelUrbsEp(pThisCC->RootHub.pIRhConn, pUrb);
4315 }
4316 }
4317 Assert(cLeft == 0);
4318}
4319
4320
4321/**
4322 * Generate a Start-Of-Frame event, and set a timer for End-Of-Frame.
4323 */
4324static void ohciR3StartOfFrame(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4325{
4326# ifdef LOG_ENABLED
4327 const uint32_t status_old = pThis->status;
4328# endif
4329
4330 /*
4331 * Update HcFmRemaining.FRT and update start of frame time.
4332 */
4333 pThis->frt = pThis->fit;
4334 pThis->SofTime += pThis->cTicksPerFrame;
4335
4336 /*
4337 * Check that the HCCA address isn't bogus. Linux 2.4.x is known to start
4338 * the bus with a hcca of 0 to work around problem with a specific controller.
4339 */
4340 bool fValidHCCA = !( pThis->hcca >= OHCI_HCCA_MASK
4341 || pThis->hcca < ~OHCI_HCCA_MASK);
4342
4343# if 1
4344 /*
4345 * Update the HCCA.
4346 * Should be done after SOF but before HC read first ED in this frame.
4347 */
4348 if (fValidHCCA)
4349 ohciR3UpdateHCCA(pDevIns, pThis, pThisCC);
4350# endif
4351
4352 /* "After writing to HCCA, HC will set SF in HcInterruptStatus" - guest isn't executing, so ignore the order! */
4353 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_START_OF_FRAME);
4354
4355 if (pThis->fno)
4356 {
4357 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_FRAMENUMBER_OVERFLOW);
4358 pThis->fno = 0;
4359 }
4360
4361 /* If the HCCA address is invalid, we're quitting here to avoid doing something which cannot be reported to the HCD. */
4362 if (!fValidHCCA)
4363 {
4364 Log(("ohciR3StartOfFrame: skipping hcca part because hcca=%RX32 (our 'valid' range: %RX32-%RX32)\n",
4365 pThis->hcca, ~OHCI_HCCA_MASK, OHCI_HCCA_MASK));
4366 return;
4367 }
4368
4369 /*
4370 * Periodic EPs.
4371 */
4372 if (pThis->ctl & OHCI_CTL_PLE)
4373 ohciR3ServicePeriodicList(pDevIns, pThis, pThisCC);
4374
4375 /*
4376 * Control EPs.
4377 */
4378 if ( (pThis->ctl & OHCI_CTL_CLE)
4379 && (pThis->status & OHCI_STATUS_CLF) )
4380 ohciR3ServiceCtrlList(pDevIns, pThis, pThisCC);
4381
4382 /*
4383 * Bulk EPs.
4384 */
4385 if ( (pThis->ctl & OHCI_CTL_BLE)
4386 && (pThis->status & OHCI_STATUS_BLF))
4387 ohciR3ServiceBulkList(pDevIns, pThis, pThisCC);
4388 else if ((pThis->status & OHCI_STATUS_BLF)
4389 && pThis->fBulkNeedsCleaning)
4390 ohciR3UndoBulkList(pDevIns, pThis, pThisCC); /* If list disabled but not empty, abort endpoints. */
4391
4392# if 0
4393 /*
4394 * Update the HCCA after processing the lists and everything. A bit experimental.
4395 *
4396 * ASSUME the guest won't be very upset if a TD is completed, retired and handed
4397 * back immediately. The idea is to be able to retire the data and/or status stages
4398 * of a control transfer together with the setup stage, thus saving a frame. This
4399 * behaviour is should be perfectly ok, since the setup (and maybe data) stages
4400 * have already taken at least one frame to complete.
4401 *
4402 * But, when implementing the first synchronous virtual USB devices, we'll have to
4403 * verify that the guest doesn't choke when having a TD returned in the same frame
4404 * as it was submitted.
4405 */
4406 ohciR3UpdateHCCA(pThis);
4407# endif
4408
4409# ifdef LOG_ENABLED
4410 if (pThis->status ^ status_old)
4411 {
4412 uint32_t val = pThis->status;
4413 uint32_t chg = val ^ status_old; NOREF(chg);
4414 Log2(("ohciR3StartOfFrame: HcCommandStatus=%#010x: %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
4415 val,
4416 chg & RT_BIT(0) ? "*" : "", val & 1,
4417 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
4418 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4419 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4420 chg & (3<<16)? "*" : "", (val >> 16) & 3));
4421 }
4422# endif
4423}
4424
4425
4426/**
4427 * Updates the HcFmNumber and FNO registers.
4428 */
4429static void ohciR3BumpFrameNumber(POHCI pThis)
4430{
4431 const uint16_t u16OldFmNumber = pThis->HcFmNumber++;
4432 if ((u16OldFmNumber ^ pThis->HcFmNumber) & RT_BIT(15))
4433 pThis->fno = 1;
4434}
4435
4436
4437/**
4438 * Callback for periodic frame processing.
4439 */
4440static DECLCALLBACK(bool) ohciR3StartFrame(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameNo)
4441{
4442 RT_NOREF(u32FrameNo);
4443 POHCICC pThisCC = VUSBIROOTHUBPORT_2_OHCI(pInterface);
4444 PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
4445 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
4446
4447 ohciR3Lock(pThisCC);
4448
4449 /* Reset idle detection flag */
4450 pThis->fIdle = true;
4451
4452# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
4453 physReadStatsReset(&g_PhysReadState);
4454# endif
4455
4456 if (!(pThis->intr_status & OHCI_INTR_UNRECOVERABLE_ERROR))
4457 {
4458 /* Frame boundary, so do EOF stuff here. */
4459 ohciR3BumpFrameNumber(pThis);
4460 if ( (pThis->dqic != 0x7) && (pThis->dqic != 0))
4461 pThis->dqic--;
4462
4463 /* Clean up any URBs that have been removed. */
4464 ohciR3CancelOrphanedURBs(pDevIns, pThis, pThisCC);
4465
4466 /* Start the next frame. */
4467 ohciR3StartOfFrame(pDevIns, pThis, pThisCC);
4468 }
4469
4470# ifdef VBOX_WITH_OHCI_PHYS_READ_STATS
4471 physReadStatsPrint(&g_PhysReadState);
4472# endif
4473
4474 ohciR3Unlock(pThisCC);
4475 return pThis->fIdle;
4476}
4477
4478
4479/**
4480 * @interface_method_impl{VUSBIROOTHUBPORT,pfnFrameRateChanged}
4481 */
4482static DECLCALLBACK(void) ohciR3FrameRateChanged(PVUSBIROOTHUBPORT pInterface, uint32_t u32FrameRate)
4483{
4484 POHCICC pThisCC = VUSBIROOTHUBPORT_2_OHCI(pInterface);
4485 PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
4486 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
4487
4488 Assert(u32FrameRate <= OHCI_DEFAULT_TIMER_FREQ);
4489
4490 pThis->cTicksPerFrame = pThis->u64TimerHz / u32FrameRate;
4491 if (!pThis->cTicksPerFrame)
4492 pThis->cTicksPerFrame = 1;
4493 pThis->cTicksPerUsbTick = pThis->u64TimerHz >= VUSB_BUS_HZ ? pThis->u64TimerHz / VUSB_BUS_HZ : 1;
4494}
4495
4496
4497/**
4498 * Start sending SOF tokens across the USB bus, lists are processed in
4499 * next frame
4500 */
4501static void ohciR3BusStart(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC)
4502{
4503 pThisCC->RootHub.pIRhConn->pfnPowerOn(pThisCC->RootHub.pIRhConn);
4504 pThis->dqic = 0x7;
4505
4506 Log(("ohci: Bus started\n"));
4507
4508 pThis->SofTime = PDMDevHlpTMTimeVirtGet(pDevIns);
4509 int rc = pThisCC->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThisCC->RootHub.pIRhConn, OHCI_DEFAULT_TIMER_FREQ);
4510 AssertRC(rc);
4511}
4512
4513
4514/**
4515 * Stop sending SOF tokens on the bus
4516 */
4517static void ohciR3BusStop(POHCICC pThisCC)
4518{
4519 int rc = pThisCC->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThisCC->RootHub.pIRhConn, 0);
4520 AssertRC(rc);
4521 pThisCC->RootHub.pIRhConn->pfnPowerOff(pThisCC->RootHub.pIRhConn);
4522}
4523
4524
4525/**
4526 * Move in to resume state
4527 */
4528static void ohciR3BusResume(PPDMDEVINS pDevIns, POHCI pThis, POHCICC pThisCC, bool fHardware)
4529{
4530 pThis->ctl &= ~OHCI_CTL_HCFS;
4531 pThis->ctl |= OHCI_USB_RESUME;
4532
4533 LogFunc(("fHardware=%RTbool RWE=%s\n",
4534 fHardware, (pThis->ctl & OHCI_CTL_RWE) ? "on" : "off"));
4535
4536 if (fHardware && (pThis->ctl & OHCI_CTL_RWE))
4537 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_RESUME_DETECT);
4538
4539 ohciR3BusStart(pDevIns, pThis, pThisCC);
4540}
4541
4542
4543/* Power a port up or down */
4544static void ohciR3RhPortPower(POHCIROOTHUBR3 pRh, unsigned iPort, bool fPowerUp)
4545{
4546 POHCIHUBPORT pPort = &pRh->aPorts[iPort];
4547 bool fOldPPS = !!(pPort->fReg & OHCI_PORT_PPS);
4548
4549 LogFlowFunc(("iPort=%u fPowerUp=%RTbool\n", iPort, fPowerUp));
4550
4551 if (fPowerUp)
4552 {
4553 /* power up */
4554 if (pPort->fAttached)
4555 pPort->fReg |= OHCI_PORT_CCS;
4556 if (pPort->fReg & OHCI_PORT_CCS)
4557 pPort->fReg |= OHCI_PORT_PPS;
4558 if (pPort->fAttached && !fOldPPS)
4559 VUSBIRhDevPowerOn(pRh->pIRhConn, OHCI_PORT_2_VUSB_PORT(iPort));
4560 }
4561 else
4562 {
4563 /* power down */
4564 pPort->fReg &= ~(OHCI_PORT_PPS | OHCI_PORT_CCS | OHCI_PORT_PSS | OHCI_PORT_PRS);
4565 if (pPort->fAttached && fOldPPS)
4566 VUSBIRhDevPowerOff(pRh->pIRhConn, OHCI_PORT_2_VUSB_PORT(iPort));
4567 }
4568}
4569
4570#endif /* IN_RING3 */
4571
4572/**
4573 * Read the HcRevision register.
4574 */
4575static VBOXSTRICTRC HcRevision_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4576{
4577 RT_NOREF(pDevIns, pThis, iReg);
4578 Log2(("HcRevision_r() -> 0x10\n"));
4579 *pu32Value = 0x10; /* OHCI revision 1.0, no emulation. */
4580 return VINF_SUCCESS;
4581}
4582
4583/**
4584 * Write to the HcRevision register.
4585 */
4586static VBOXSTRICTRC HcRevision_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t u32Value)
4587{
4588 RT_NOREF(pDevIns, pThis, iReg, u32Value);
4589 Log2(("HcRevision_w(%#010x) - denied\n", u32Value));
4590 ASSERT_GUEST_MSG_FAILED(("Invalid operation!!! u32Value=%#010x\n", u32Value));
4591 return VINF_SUCCESS;
4592}
4593
4594/**
4595 * Read the HcControl register.
4596 */
4597static VBOXSTRICTRC HcControl_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4598{
4599 RT_NOREF(pDevIns, iReg);
4600 uint32_t ctl = pThis->ctl;
4601 Log2(("HcControl_r -> %#010x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
4602 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
4603 (ctl >> 9) & 1, (ctl >> 10) & 1));
4604 *pu32Value = ctl;
4605 return VINF_SUCCESS;
4606}
4607
4608/**
4609 * Write the HcControl register.
4610 */
4611static VBOXSTRICTRC HcControl_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4612{
4613 RT_NOREF(iReg);
4614
4615 /* log it. */
4616 uint32_t chg = pThis->ctl ^ val; NOREF(chg);
4617 Log2(("HcControl_w(%#010x) => %sCBSR=%d %sPLE=%d %sIE=%d %sCLE=%d %sBLE=%d %sHCFS=%#x %sIR=%d %sRWC=%d %sRWE=%d\n",
4618 val,
4619 chg & 3 ? "*" : "", val & 3,
4620 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4621 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4622 chg & RT_BIT(4) ? "*" : "", (val >> 4) & 1,
4623 chg & RT_BIT(5) ? "*" : "", (val >> 5) & 1,
4624 chg & (3 << 6)? "*" : "", (val >> 6) & 3,
4625 chg & RT_BIT(8) ? "*" : "", (val >> 8) & 1,
4626 chg & RT_BIT(9) ? "*" : "", (val >> 9) & 1,
4627 chg & RT_BIT(10) ? "*" : "", (val >> 10) & 1));
4628 if (val & ~0x07ff)
4629 Log2(("Unknown bits %#x are set!!!\n", val & ~0x07ff));
4630
4631 /* see what changed and take action on that. */
4632 uint32_t old_state = pThis->ctl & OHCI_CTL_HCFS;
4633 uint32_t new_state = val & OHCI_CTL_HCFS;
4634
4635#ifdef IN_RING3
4636 pThis->ctl = val;
4637 if (new_state != old_state)
4638 {
4639 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
4640 switch (new_state)
4641 {
4642 case OHCI_USB_OPERATIONAL:
4643 LogRel(("OHCI: USB Operational\n"));
4644 ohciR3BusStart(pDevIns, pThis, pThisCC);
4645 break;
4646 case OHCI_USB_SUSPEND:
4647 ohciR3BusStop(pThisCC);
4648 LogRel(("OHCI: USB Suspended\n"));
4649 break;
4650 case OHCI_USB_RESUME:
4651 LogRel(("OHCI: USB Resume\n"));
4652 ohciR3BusResume(pDevIns, pThis, pThisCC, false /* not hardware */);
4653 break;
4654 case OHCI_USB_RESET:
4655 {
4656 LogRel(("OHCI: USB Reset\n"));
4657 ohciR3BusStop(pThisCC);
4658 /** @todo This should probably do a real reset, but we don't implement
4659 * that correctly in the roothub reset callback yet. check it's
4660 * comments and argument for more details. */
4661 pThisCC->RootHub.pIRhConn->pfnReset(pThisCC->RootHub.pIRhConn, false /* don't do a real reset */);
4662 break;
4663 }
4664 }
4665 }
4666#else /* !IN_RING3 */
4667 RT_NOREF(pDevIns);
4668 if ( new_state != old_state )
4669 {
4670 Log2(("HcControl_w: state changed -> VINF_IOM_R3_MMIO_WRITE\n"));
4671 return VINF_IOM_R3_MMIO_WRITE;
4672 }
4673 pThis->ctl = val;
4674#endif /* !IN_RING3 */
4675
4676 return VINF_SUCCESS;
4677}
4678
4679/**
4680 * Read the HcCommandStatus register.
4681 */
4682static VBOXSTRICTRC HcCommandStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4683{
4684 uint32_t status = pThis->status;
4685 Log2(("HcCommandStatus_r() -> %#010x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
4686 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3));
4687 *pu32Value = status;
4688 RT_NOREF(pDevIns, iReg);
4689 return VINF_SUCCESS;
4690}
4691
4692/**
4693 * Write to the HcCommandStatus register.
4694 */
4695static VBOXSTRICTRC HcCommandStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4696{
4697 RT_NOREF(pDevIns, iReg);
4698
4699 /* log */
4700 uint32_t chg = pThis->status ^ val; NOREF(chg);
4701 Log2(("HcCommandStatus_w(%#010x) => %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
4702 val,
4703 chg & RT_BIT(0) ? "*" : "", val & 1,
4704 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
4705 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
4706 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
4707 chg & (3<<16)? "!!!":"", (pThis->status >> 16) & 3));
4708 if (val & ~0x0003000f)
4709 Log2(("Unknown bits %#x are set!!!\n", val & ~0x0003000f));
4710
4711 /* SOC is read-only */
4712 val = (val & ~OHCI_STATUS_SOC);
4713
4714#ifdef IN_RING3
4715 /* "bits written as '0' remain unchanged in the register" */
4716 pThis->status |= val;
4717 if (pThis->status & OHCI_STATUS_HCR)
4718 {
4719 LogRel(("OHCI: Software reset\n"));
4720 ohciR3DoReset(pDevIns, pThis, PDMDEVINS_2_DATA_CC(pDevIns, POHCICC), OHCI_USB_SUSPEND, false /* N/A */);
4721 }
4722#else
4723 if ((pThis->status | val) & OHCI_STATUS_HCR)
4724 {
4725 LogFlow(("HcCommandStatus_w: reset -> VINF_IOM_R3_MMIO_WRITE\n"));
4726 return VINF_IOM_R3_MMIO_WRITE;
4727 }
4728 pThis->status |= val;
4729#endif
4730 return VINF_SUCCESS;
4731}
4732
4733/**
4734 * Read the HcInterruptStatus register.
4735 */
4736static VBOXSTRICTRC HcInterruptStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4737{
4738 uint32_t val = pThis->intr_status;
4739 Log2(("HcInterruptStatus_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
4740 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4741 (val >> 6) & 1, (val >> 30) & 1));
4742 *pu32Value = val;
4743 RT_NOREF(pDevIns, iReg);
4744 return VINF_SUCCESS;
4745}
4746
4747/**
4748 * Write to the HcInterruptStatus register.
4749 */
4750static VBOXSTRICTRC HcInterruptStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4751{
4752 RT_NOREF(iReg);
4753
4754 uint32_t res = pThis->intr_status & ~val;
4755 uint32_t chg = pThis->intr_status ^ res; NOREF(chg);
4756
4757 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4758 if (rc != VINF_SUCCESS)
4759 return rc;
4760
4761 Log2(("HcInterruptStatus_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d\n",
4762 val,
4763 chg & RT_BIT(0) ? "*" : "", res & 1,
4764 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4765 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4766 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4767 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4768 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4769 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4770 chg & RT_BIT(30)? "*" : "", (res >> 30) & 1));
4771 if ( (val & ~0xc000007f)
4772 && val != 0xffffffff /* ignore clear-all-like requests from xp. */)
4773 Log2(("Unknown bits %#x are set!!!\n", val & ~0xc000007f));
4774
4775 /* "The Host Controller Driver may clear specific bits in this
4776 * register by writing '1' to bit positions to be cleared"
4777 */
4778 pThis->intr_status &= ~val;
4779 ohciUpdateInterruptLocked(pDevIns, pThis, "HcInterruptStatus_w");
4780 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
4781 return VINF_SUCCESS;
4782}
4783
4784/**
4785 * Read the HcInterruptEnable register
4786 */
4787static VBOXSTRICTRC HcInterruptEnable_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4788{
4789 uint32_t val = pThis->intr;
4790 Log2(("HcInterruptEnable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
4791 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4792 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
4793 *pu32Value = val;
4794 RT_NOREF(pDevIns, iReg);
4795 return VINF_SUCCESS;
4796}
4797
4798/**
4799 * Writes to the HcInterruptEnable register.
4800 */
4801static VBOXSTRICTRC HcInterruptEnable_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4802{
4803 RT_NOREF(iReg);
4804 uint32_t res = pThis->intr | val;
4805 uint32_t chg = pThis->intr ^ res; NOREF(chg);
4806
4807 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4808 if (rc != VINF_SUCCESS)
4809 return rc;
4810
4811 Log2(("HcInterruptEnable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
4812 val,
4813 chg & RT_BIT(0) ? "*" : "", res & 1,
4814 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4815 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4816 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4817 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4818 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4819 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4820 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
4821 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
4822 if (val & ~0xc000007f)
4823 Log2(("Uknown bits %#x are set!!!\n", val & ~0xc000007f));
4824
4825 pThis->intr |= val;
4826 ohciUpdateInterruptLocked(pDevIns, pThis, "HcInterruptEnable_w");
4827 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
4828 return VINF_SUCCESS;
4829}
4830
4831/**
4832 * Reads the HcInterruptDisable register.
4833 */
4834static VBOXSTRICTRC HcInterruptDisable_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4835{
4836#if 1 /** @todo r=bird: "On read, the current value of the HcInterruptEnable register is returned." */
4837 uint32_t val = pThis->intr;
4838#else /* old code. */
4839 uint32_t val = ~pThis->intr;
4840#endif
4841 Log2(("HcInterruptDisable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
4842 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
4843 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1));
4844
4845 *pu32Value = val;
4846 RT_NOREF(pDevIns, iReg);
4847 return VINF_SUCCESS;
4848}
4849
4850/**
4851 * Writes to the HcInterruptDisable register.
4852 */
4853static VBOXSTRICTRC HcInterruptDisable_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4854{
4855 RT_NOREF(iReg);
4856 uint32_t res = pThis->intr & ~val;
4857 uint32_t chg = pThis->intr ^ res; NOREF(chg);
4858
4859 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4860 if (rc != VINF_SUCCESS)
4861 return rc;
4862
4863 Log2(("HcInterruptDisable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
4864 val,
4865 chg & RT_BIT(0) ? "*" : "", res & 1,
4866 chg & RT_BIT(1) ? "*" : "", (res >> 1) & 1,
4867 chg & RT_BIT(2) ? "*" : "", (res >> 2) & 1,
4868 chg & RT_BIT(3) ? "*" : "", (res >> 3) & 1,
4869 chg & RT_BIT(4) ? "*" : "", (res >> 4) & 1,
4870 chg & RT_BIT(5) ? "*" : "", (res >> 5) & 1,
4871 chg & RT_BIT(6) ? "*" : "", (res >> 6) & 1,
4872 chg & RT_BIT(30) ? "*" : "", (res >> 30) & 1,
4873 chg & RT_BIT(31) ? "*" : "", (res >> 31) & 1));
4874 /* Don't bitch about invalid bits here since it makes sense to disable
4875 * interrupts you don't know about. */
4876
4877 pThis->intr &= ~val;
4878 ohciUpdateInterruptLocked(pDevIns, pThis, "HcInterruptDisable_w");
4879 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
4880 return VINF_SUCCESS;
4881}
4882
4883/**
4884 * Read the HcHCCA register (Host Controller Communications Area physical address).
4885 */
4886static VBOXSTRICTRC HcHCCA_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4887{
4888 Log2(("HcHCCA_r() -> %#010x\n", pThis->hcca));
4889 *pu32Value = pThis->hcca;
4890 RT_NOREF(pDevIns, iReg);
4891 return VINF_SUCCESS;
4892}
4893
4894/**
4895 * Write to the HcHCCA register (Host Controller Communications Area physical address).
4896 */
4897static VBOXSTRICTRC HcHCCA_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t Value)
4898{
4899 Log2(("HcHCCA_w(%#010x) - old=%#010x new=%#010x\n", Value, pThis->hcca, Value & OHCI_HCCA_MASK));
4900 pThis->hcca = Value & OHCI_HCCA_MASK;
4901 RT_NOREF(pDevIns, iReg);
4902 return VINF_SUCCESS;
4903}
4904
4905/**
4906 * Read the HcPeriodCurrentED register.
4907 */
4908static VBOXSTRICTRC HcPeriodCurrentED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4909{
4910 Log2(("HcPeriodCurrentED_r() -> %#010x\n", pThis->per_cur));
4911 *pu32Value = pThis->per_cur;
4912 RT_NOREF(pDevIns, iReg);
4913 return VINF_SUCCESS;
4914}
4915
4916/**
4917 * Write to the HcPeriodCurrentED register.
4918 */
4919static VBOXSTRICTRC HcPeriodCurrentED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4920{
4921 Log(("HcPeriodCurrentED_w(%#010x) - old=%#010x new=%#010x (This is a read only register, only the linux guys don't respect that!)\n",
4922 val, pThis->per_cur, val & ~7));
4923 //AssertMsgFailed(("HCD (Host Controller Driver) should not write to HcPeriodCurrentED! val=%#010x (old=%#010x)\n", val, pThis->per_cur));
4924 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4925 pThis->per_cur = val & ~7;
4926 RT_NOREF(pDevIns, iReg);
4927 return VINF_SUCCESS;
4928}
4929
4930/**
4931 * Read the HcControlHeadED register.
4932 */
4933static VBOXSTRICTRC HcControlHeadED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4934{
4935 Log2(("HcControlHeadED_r() -> %#010x\n", pThis->ctrl_head));
4936 *pu32Value = pThis->ctrl_head;
4937 RT_NOREF(pDevIns, iReg);
4938 return VINF_SUCCESS;
4939}
4940
4941/**
4942 * Write to the HcControlHeadED register.
4943 */
4944static VBOXSTRICTRC HcControlHeadED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4945{
4946 Log2(("HcControlHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->ctrl_head, val & ~7));
4947 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4948 pThis->ctrl_head = val & ~7;
4949 RT_NOREF(pDevIns, iReg);
4950 return VINF_SUCCESS;
4951}
4952
4953/**
4954 * Read the HcControlCurrentED register.
4955 */
4956static VBOXSTRICTRC HcControlCurrentED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4957{
4958 Log2(("HcControlCurrentED_r() -> %#010x\n", pThis->ctrl_cur));
4959 *pu32Value = pThis->ctrl_cur;
4960 RT_NOREF(pDevIns, iReg);
4961 return VINF_SUCCESS;
4962}
4963
4964/**
4965 * Write to the HcControlCurrentED register.
4966 */
4967static VBOXSTRICTRC HcControlCurrentED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4968{
4969 Log2(("HcControlCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->ctrl_cur, val & ~7));
4970 AssertMsg(!(pThis->ctl & OHCI_CTL_CLE), ("Illegal write! HcControl.ControlListEnabled is set! val=%#010x\n", val));
4971 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4972 pThis->ctrl_cur = val & ~7;
4973 RT_NOREF(pDevIns, iReg);
4974 return VINF_SUCCESS;
4975}
4976
4977/**
4978 * Read the HcBulkHeadED register.
4979 */
4980static VBOXSTRICTRC HcBulkHeadED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4981{
4982 Log2(("HcBulkHeadED_r() -> %#010x\n", pThis->bulk_head));
4983 *pu32Value = pThis->bulk_head;
4984 RT_NOREF(pDevIns, iReg);
4985 return VINF_SUCCESS;
4986}
4987
4988/**
4989 * Write to the HcBulkHeadED register.
4990 */
4991static VBOXSTRICTRC HcBulkHeadED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
4992{
4993 Log2(("HcBulkHeadED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->bulk_head, val & ~7));
4994 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
4995 pThis->bulk_head = val & ~7; /** @todo The ATI OHCI controller on my machine enforces 16-byte address alignment. */
4996 RT_NOREF(pDevIns, iReg);
4997 return VINF_SUCCESS;
4998}
4999
5000/**
5001 * Read the HcBulkCurrentED register.
5002 */
5003static VBOXSTRICTRC HcBulkCurrentED_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5004{
5005 Log2(("HcBulkCurrentED_r() -> %#010x\n", pThis->bulk_cur));
5006 *pu32Value = pThis->bulk_cur;
5007 RT_NOREF(pDevIns, iReg);
5008 return VINF_SUCCESS;
5009}
5010
5011/**
5012 * Write to the HcBulkCurrentED register.
5013 */
5014static VBOXSTRICTRC HcBulkCurrentED_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5015{
5016 Log2(("HcBulkCurrentED_w(%#010x) - old=%#010x new=%#010x\n", val, pThis->bulk_cur, val & ~7));
5017 AssertMsg(!(pThis->ctl & OHCI_CTL_BLE), ("Illegal write! HcControl.BulkListEnabled is set! val=%#010x\n", val));
5018 AssertMsg(!(val & 7), ("Invalid alignment, val=%#010x\n", val));
5019 pThis->bulk_cur = val & ~7;
5020 RT_NOREF(pDevIns, iReg);
5021 return VINF_SUCCESS;
5022}
5023
5024
5025/**
5026 * Read the HcDoneHead register.
5027 */
5028static VBOXSTRICTRC HcDoneHead_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5029{
5030 Log2(("HcDoneHead_r() -> 0x%#08x\n", pThis->done));
5031 *pu32Value = pThis->done;
5032 RT_NOREF(pDevIns, iReg);
5033 return VINF_SUCCESS;
5034}
5035
5036/**
5037 * Write to the HcDoneHead register.
5038 */
5039static VBOXSTRICTRC HcDoneHead_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5040{
5041 RT_NOREF(pDevIns, pThis, iReg, val);
5042 Log2(("HcDoneHead_w(0x%#08x) - denied!!!\n", val));
5043 /*AssertMsgFailed(("Illegal operation!!! val=%#010x\n", val)); - OS/2 does this */
5044 return VINF_SUCCESS;
5045}
5046
5047
5048/**
5049 * Read the HcFmInterval (Fm=Frame) register.
5050 */
5051static VBOXSTRICTRC HcFmInterval_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5052{
5053 uint32_t val = (pThis->fit << 31) | (pThis->fsmps << 16) | (pThis->fi);
5054 Log2(("HcFmInterval_r() -> 0x%#08x - FI=%d FSMPS=%d FIT=%d\n",
5055 val, val & 0x3fff, (val >> 16) & 0x7fff, val >> 31));
5056 *pu32Value = val;
5057 RT_NOREF(pDevIns, iReg);
5058 return VINF_SUCCESS;
5059}
5060
5061/**
5062 * Write to the HcFmInterval (Fm = Frame) register.
5063 */
5064static VBOXSTRICTRC HcFmInterval_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5065{
5066 RT_NOREF(pDevIns, iReg);
5067
5068 /* log */
5069 uint32_t chg = val ^ ((pThis->fit << 31) | (pThis->fsmps << 16) | pThis->fi); NOREF(chg);
5070 Log2(("HcFmInterval_w(%#010x) => %sFI=%d %sFSMPS=%d %sFIT=%d\n",
5071 val,
5072 chg & 0x00003fff ? "*" : "", val & 0x3fff,
5073 chg & 0x7fff0000 ? "*" : "", (val >> 16) & 0x7fff,
5074 chg >> 31 ? "*" : "", (val >> 31) & 1));
5075 if (pThis->fi != (val & OHCI_FMI_FI))
5076 {
5077 Log(("ohci: FrameInterval: %#010x -> %#010x\n", pThis->fi, val & OHCI_FMI_FI));
5078 AssertMsg(pThis->fit != ((val >> OHCI_FMI_FIT_SHIFT) & 1), ("HCD didn't toggle the FIT bit!!!\n"));
5079 }
5080
5081 /* update */
5082 pThis->fi = val & OHCI_FMI_FI;
5083 pThis->fit = (val & OHCI_FMI_FIT) >> OHCI_FMI_FIT_SHIFT;
5084 pThis->fsmps = (val & OHCI_FMI_FSMPS) >> OHCI_FMI_FSMPS_SHIFT;
5085 return VINF_SUCCESS;
5086}
5087
5088/**
5089 * Read the HcFmRemaining (Fm = Frame) register.
5090 */
5091static VBOXSTRICTRC HcFmRemaining_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5092{
5093 RT_NOREF(iReg);
5094 uint32_t Value = pThis->frt << 31;
5095 if ((pThis->ctl & OHCI_CTL_HCFS) == OHCI_USB_OPERATIONAL)
5096 {
5097 /*
5098 * Being in USB operational state guarantees SofTime was set already.
5099 */
5100 uint64_t tks = PDMDevHlpTMTimeVirtGet(pDevIns) - pThis->SofTime;
5101 if (tks < pThis->cTicksPerFrame) /* avoid muldiv if possible */
5102 {
5103 uint16_t fr;
5104 tks = ASMMultU64ByU32DivByU32(1, tks, pThis->cTicksPerUsbTick);
5105 fr = (uint16_t)(pThis->fi - tks);
5106 Value |= fr;
5107 }
5108 }
5109
5110 Log2(("HcFmRemaining_r() -> %#010x - FR=%d FRT=%d\n", Value, Value & 0x3fff, Value >> 31));
5111 *pu32Value = Value;
5112 return VINF_SUCCESS;
5113}
5114
5115/**
5116 * Write to the HcFmRemaining (Fm = Frame) register.
5117 */
5118static VBOXSTRICTRC HcFmRemaining_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5119{
5120 RT_NOREF(pDevIns, pThis, iReg, val);
5121 Log2(("HcFmRemaining_w(%#010x) - denied\n", val));
5122 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
5123 return VINF_SUCCESS;
5124}
5125
5126/**
5127 * Read the HcFmNumber (Fm = Frame) register.
5128 */
5129static VBOXSTRICTRC HcFmNumber_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5130{
5131 RT_NOREF(pDevIns, iReg);
5132 uint32_t val = (uint16_t)pThis->HcFmNumber;
5133 Log2(("HcFmNumber_r() -> %#010x - FN=%#x(%d) (32-bit=%#x(%d))\n", val, val, val, pThis->HcFmNumber, pThis->HcFmNumber));
5134 *pu32Value = val;
5135 return VINF_SUCCESS;
5136}
5137
5138/**
5139 * Write to the HcFmNumber (Fm = Frame) register.
5140 */
5141static VBOXSTRICTRC HcFmNumber_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5142{
5143 RT_NOREF(pDevIns, pThis, iReg, val);
5144 Log2(("HcFmNumber_w(%#010x) - denied\n", val));
5145 AssertMsgFailed(("Invalid operation!!! val=%#010x\n", val));
5146 return VINF_SUCCESS;
5147}
5148
5149/**
5150 * Read the HcPeriodicStart register.
5151 * The register determines when in a frame to switch from control&bulk to periodic lists.
5152 */
5153static VBOXSTRICTRC HcPeriodicStart_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5154{
5155 RT_NOREF(pDevIns, iReg);
5156 Log2(("HcPeriodicStart_r() -> %#010x - PS=%d\n", pThis->pstart, pThis->pstart & 0x3fff));
5157 *pu32Value = pThis->pstart;
5158 return VINF_SUCCESS;
5159}
5160
5161/**
5162 * Write to the HcPeriodicStart register.
5163 * The register determines when in a frame to switch from control&bulk to periodic lists.
5164 */
5165static VBOXSTRICTRC HcPeriodicStart_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5166{
5167 RT_NOREF(pDevIns, iReg);
5168 Log2(("HcPeriodicStart_w(%#010x) => PS=%d\n", val, val & 0x3fff));
5169 if (val & ~0x3fff)
5170 Log2(("Unknown bits %#x are set!!!\n", val & ~0x3fff));
5171 pThis->pstart = val; /** @todo r=bird: should we support setting the other bits? */
5172 return VINF_SUCCESS;
5173}
5174
5175/**
5176 * Read the HcLSThreshold register.
5177 */
5178static VBOXSTRICTRC HcLSThreshold_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5179{
5180 RT_NOREF(pDevIns, pThis, iReg);
5181 Log2(("HcLSThreshold_r() -> %#010x\n", OHCI_LS_THRESH));
5182 *pu32Value = OHCI_LS_THRESH;
5183 return VINF_SUCCESS;
5184}
5185
5186/**
5187 * Write to the HcLSThreshold register.
5188 *
5189 * Docs are inconsistent here:
5190 *
5191 * "Neither the Host Controller nor the Host Controller Driver are allowed to change this value."
5192 *
5193 * "This value is calculated by HCD with the consideration of transmission and setup overhead."
5194 *
5195 * The register is marked "R/W" the HCD column.
5196 *
5197 */
5198static VBOXSTRICTRC HcLSThreshold_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5199{
5200 RT_NOREF(pDevIns, pThis, iReg, val);
5201 Log2(("HcLSThreshold_w(%#010x) => LST=0x%03x(%d)\n", val, val & 0x0fff, val & 0x0fff));
5202 AssertMsg(val == OHCI_LS_THRESH,
5203 ("HCD tried to write bad LS threshold: 0x%x (see function header)\n", val));
5204 /** @todo the HCD can change this. */
5205 return VINF_SUCCESS;
5206}
5207
5208/**
5209 * Read the HcRhDescriptorA register.
5210 */
5211static VBOXSTRICTRC HcRhDescriptorA_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5212{
5213 RT_NOREF(pDevIns, iReg);
5214 uint32_t val = pThis->RootHub.desc_a;
5215#if 0 /* annoying */
5216 Log2(("HcRhDescriptorA_r() -> %#010x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTGT=%#x\n",
5217 val, val & 0xff, (val >> 8) & 1, (val >> 9) & 1, (val >> 10) & 1, (val >> 11) & 1,
5218 (val >> 12) & 1, (val >> 24) & 0xff));
5219#endif
5220 *pu32Value = val;
5221 return VINF_SUCCESS;
5222}
5223
5224/**
5225 * Write to the HcRhDescriptorA register.
5226 */
5227static VBOXSTRICTRC HcRhDescriptorA_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5228{
5229 RT_NOREF(pDevIns, iReg);
5230 uint32_t chg = val ^ pThis->RootHub.desc_a; NOREF(chg);
5231 Log2(("HcRhDescriptorA_w(%#010x) => %sNDP=%d %sPSM=%d %sNPS=%d %sDT=%d %sOCPM=%d %sNOCP=%d %sPOTGT=%#x - %sPowerSwitching Set%sPower\n",
5232 val,
5233 chg & 0xff ?"!!!": "", val & 0xff,
5234 (chg >> 8) & 1 ? "*" : "", (val >> 8) & 1,
5235 (chg >> 9) & 1 ? "*" : "", (val >> 9) & 1,
5236 (chg >> 10) & 1 ?"!!!": "", 0,
5237 (chg >> 11) & 1 ? "*" : "", (val >> 11) & 1,
5238 (chg >> 12) & 1 ? "*" : "", (val >> 12) & 1,
5239 (chg >> 24)&0xff? "*" : "", (val >> 24) & 0xff,
5240 val & OHCI_RHA_NPS ? "No" : "",
5241 val & OHCI_RHA_PSM ? "Port" : "Global"));
5242 if (val & ~0xff001fff)
5243 Log2(("Unknown bits %#x are set!!!\n", val & ~0xff001fff));
5244
5245
5246 if ((val & (OHCI_RHA_NDP | OHCI_RHA_DT)) != OHCI_NDP_CFG(pThis))
5247 {
5248 Log(("ohci: invalid write to NDP or DT in roothub descriptor A!!! val=0x%.8x\n", val));
5249 val &= ~(OHCI_RHA_NDP | OHCI_RHA_DT);
5250 val |= OHCI_NDP_CFG(pThis);
5251 }
5252
5253 pThis->RootHub.desc_a = val;
5254 return VINF_SUCCESS;
5255}
5256
5257/**
5258 * Read the HcRhDescriptorB register.
5259 */
5260static VBOXSTRICTRC HcRhDescriptorB_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5261{
5262 uint32_t val = pThis->RootHub.desc_b;
5263 Log2(("HcRhDescriptorB_r() -> %#010x - DR=0x%04x PPCM=0x%04x\n",
5264 val, val & 0xffff, val >> 16));
5265 *pu32Value = val;
5266 RT_NOREF(pDevIns, iReg);
5267 return VINF_SUCCESS;
5268}
5269
5270/**
5271 * Write to the HcRhDescriptorB register.
5272 */
5273static VBOXSTRICTRC HcRhDescriptorB_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5274{
5275 RT_NOREF(pDevIns, iReg);
5276 uint32_t chg = pThis->RootHub.desc_b ^ val; NOREF(chg);
5277 Log2(("HcRhDescriptorB_w(%#010x) => %sDR=0x%04x %sPPCM=0x%04x\n",
5278 val,
5279 chg & 0xffff ? "!!!" : "", val & 0xffff,
5280 chg >> 16 ? "!!!" : "", val >> 16));
5281
5282 if ( pThis->RootHub.desc_b != val )
5283 Log(("ohci: unsupported write to root descriptor B!!! 0x%.8x -> 0x%.8x\n", pThis->RootHub.desc_b, val));
5284 pThis->RootHub.desc_b = val;
5285 return VINF_SUCCESS;
5286}
5287
5288/**
5289 * Read the HcRhStatus (Rh = Root Hub) register.
5290 */
5291static VBOXSTRICTRC HcRhStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5292{
5293 uint32_t val = pThis->RootHub.status;
5294 if (val & (OHCI_RHS_LPSC | OHCI_RHS_OCIC))
5295 Log2(("HcRhStatus_r() -> %#010x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n",
5296 val, val & 1, (val >> 1) & 1, (val >> 15) & 1, (val >> 16) & 1, (val >> 17) & 1, (val >> 31) & 1));
5297 *pu32Value = val;
5298 RT_NOREF(pDevIns, iReg);
5299 return VINF_SUCCESS;
5300}
5301
5302/**
5303 * Write to the HcRhStatus (Rh = Root Hub) register.
5304 */
5305static VBOXSTRICTRC HcRhStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5306{
5307#ifdef IN_RING3
5308 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5309
5310 /* log */
5311 uint32_t old = pThis->RootHub.status;
5312 uint32_t chg;
5313 if (val & ~0x80038003)
5314 Log2(("HcRhStatus_w: Unknown bits %#x are set!!!\n", val & ~0x80038003));
5315 if ( (val & OHCI_RHS_LPSC) && (val & OHCI_RHS_LPS) )
5316 Log2(("HcRhStatus_w: Warning both CGP and SGP are set! (Clear/Set Global Power)\n"));
5317 if ( (val & OHCI_RHS_DRWE) && (val & OHCI_RHS_CRWE) )
5318 Log2(("HcRhStatus_w: Warning both CRWE and SRWE are set! (Clear/Set Remote Wakeup Enable)\n"));
5319
5320
5321 /* write 1 to clear OCIC */
5322 if ( val & OHCI_RHS_OCIC )
5323 pThis->RootHub.status &= ~OHCI_RHS_OCIC;
5324
5325 /* SetGlobalPower */
5326 if ( val & OHCI_RHS_LPSC )
5327 {
5328 unsigned i;
5329 Log2(("ohci: global power up\n"));
5330 for (i = 0; i < OHCI_NDP_CFG(pThis); i++)
5331 ohciR3RhPortPower(&pThisCC->RootHub, i, true /* power up */);
5332 }
5333
5334 /* ClearGlobalPower */
5335 if ( val & OHCI_RHS_LPS )
5336 {
5337 unsigned i;
5338 Log2(("ohci: global power down\n"));
5339 for (i = 0; i < OHCI_NDP_CFG(pThis); i++)
5340 ohciR3RhPortPower(&pThisCC->RootHub, i, false /* power down */);
5341 }
5342
5343 if ( val & OHCI_RHS_DRWE )
5344 pThis->RootHub.status |= OHCI_RHS_DRWE;
5345
5346 if ( val & OHCI_RHS_CRWE )
5347 pThis->RootHub.status &= ~OHCI_RHS_DRWE;
5348
5349 chg = pThis->RootHub.status ^ old;
5350 Log2(("HcRhStatus_w(%#010x) => %sCGP=%d %sOCI=%d %sSRWE=%d %sSGP=%d %sOCIC=%d %sCRWE=%d\n",
5351 val,
5352 chg & 1 ? "*" : "", val & 1,
5353 (chg >> 1) & 1 ?"!!!": "", (val >> 1) & 1,
5354 (chg >> 15) & 1 ? "*" : "", (val >> 15) & 1,
5355 (chg >> 16) & 1 ? "*" : "", (val >> 16) & 1,
5356 (chg >> 17) & 1 ? "*" : "", (val >> 17) & 1,
5357 (chg >> 31) & 1 ? "*" : "", (val >> 31) & 1));
5358 RT_NOREF(pDevIns, iReg);
5359 return VINF_SUCCESS;
5360#else /* !IN_RING3 */
5361 RT_NOREF(pDevIns, pThis, iReg, val);
5362 return VINF_IOM_R3_MMIO_WRITE;
5363#endif /* !IN_RING3 */
5364}
5365
5366/**
5367 * Read the HcRhPortStatus register of a port.
5368 */
5369static VBOXSTRICTRC HcRhPortStatus_r(PPDMDEVINS pDevIns, PCOHCI pThis, uint32_t iReg, uint32_t *pu32Value)
5370{
5371 const unsigned i = iReg - 21;
5372 uint32_t val = pThis->RootHub.aPorts[i].fReg | OHCI_PORT_PPS; /* PortPowerStatus: see todo on power in _w function. */
5373 if (val & OHCI_PORT_PRS)
5374 {
5375#ifdef IN_RING3
5376 RTThreadYield();
5377#else
5378 Log2(("HcRhPortStatus_r: yield -> VINF_IOM_R3_MMIO_READ\n"));
5379 return VINF_IOM_R3_MMIO_READ;
5380#endif
5381 }
5382 if (val & (OHCI_PORT_PRS | OHCI_PORT_CLEAR_CHANGE_MASK))
5383 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",
5384 i, val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 8) & 1, (val >> 9) & 1,
5385 (val >> 16) & 1, (val >> 17) & 1, (val >> 18) & 1, (val >> 19) & 1, (val >> 20) & 1));
5386 *pu32Value = val;
5387 RT_NOREF(pDevIns);
5388 return VINF_SUCCESS;
5389}
5390
5391#ifdef IN_RING3
5392/**
5393 * Completion callback for the vusb_dev_reset() operation.
5394 * @thread EMT.
5395 */
5396static DECLCALLBACK(void) ohciR3PortResetDone(PVUSBIDEVICE pDev, uint32_t uPort, int rc, void *pvUser)
5397{
5398 RT_NOREF(pDev);
5399
5400 Assert(uPort >= 1);
5401 PPDMDEVINS pDevIns = (PPDMDEVINS)pvUser;
5402 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5403 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5404 POHCIHUBPORT pPort = &pThis->RootHub.aPorts[uPort - 1];
5405
5406 if (RT_SUCCESS(rc))
5407 {
5408 /*
5409 * Successful reset.
5410 */
5411 Log2(("ohciR3PortResetDone: Reset completed.\n"));
5412 pPort->fReg &= ~(OHCI_PORT_PRS | OHCI_PORT_PSS | OHCI_PORT_PSSC);
5413 pPort->fReg |= OHCI_PORT_PES | OHCI_PORT_PRSC;
5414 }
5415 else
5416 {
5417 /* desperate measures. */
5418 if ( pPort->fAttached
5419 && VUSBIRhDevGetState(pThisCC->RootHub.pIRhConn, uPort) == VUSB_DEVICE_STATE_ATTACHED)
5420 {
5421 /*
5422 * Damn, something weird happened during reset. We'll pretend the user did an
5423 * incredible fast reconnect or something. (probably not gonna work)
5424 */
5425 Log2(("ohciR3PortResetDone: The reset failed (rc=%Rrc)!!! Pretending reconnect at the speed of light.\n", rc));
5426 pPort->fReg = OHCI_PORT_CCS | OHCI_PORT_CSC;
5427 }
5428 else
5429 {
5430 /*
5431 * The device have / will be disconnected.
5432 */
5433 Log2(("ohciR3PortResetDone: Disconnected (rc=%Rrc)!!!\n", rc));
5434 pPort->fReg &= ~(OHCI_PORT_PRS | OHCI_PORT_PSS | OHCI_PORT_PSSC | OHCI_PORT_PRSC);
5435 pPort->fReg |= OHCI_PORT_CSC;
5436 }
5437 }
5438
5439 /* Raise roothub status change interrupt. */
5440 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5441}
5442
5443/**
5444 * Sets a flag in a port status register but only set it if a device is
5445 * connected, if not set ConnectStatusChange flag to force HCD to reevaluate
5446 * connect status.
5447 *
5448 * @returns true if device was connected and the flag was cleared.
5449 */
5450static bool ohciR3RhPortSetIfConnected(PPDMDEVINS pDevIns, POHCI pThis, int iPort, uint32_t fValue)
5451{
5452 /*
5453 * Writing a 0 has no effect
5454 */
5455 if (fValue == 0)
5456 return false;
5457
5458 /*
5459 * If CurrentConnectStatus is cleared we set ConnectStatusChange.
5460 */
5461 if (!(pThis->RootHub.aPorts[iPort].fReg & OHCI_PORT_CCS))
5462 {
5463 pThis->RootHub.aPorts[iPort].fReg |= OHCI_PORT_CSC;
5464 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5465 return false;
5466 }
5467
5468 bool fRc = !(pThis->RootHub.aPorts[iPort].fReg & fValue);
5469
5470 /* set the bit */
5471 pThis->RootHub.aPorts[iPort].fReg |= fValue;
5472
5473 return fRc;
5474}
5475#endif /* IN_RING3 */
5476
5477/**
5478 * Write to the HcRhPortStatus register of a port.
5479 */
5480static VBOXSTRICTRC HcRhPortStatus_w(PPDMDEVINS pDevIns, POHCI pThis, uint32_t iReg, uint32_t val)
5481{
5482#ifdef IN_RING3
5483 const unsigned i = iReg - 21;
5484 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5485 POHCIHUBPORT p = &pThis->RootHub.aPorts[i];
5486 uint32_t old_state = p->fReg;
5487
5488# ifdef LOG_ENABLED
5489 /*
5490 * Log it.
5491 */
5492 static const char *apszCmdNames[32] =
5493 {
5494 "ClearPortEnable", "SetPortEnable", "SetPortSuspend", "!!!ClearSuspendStatus",
5495 "SetPortReset", "!!!5", "!!!6", "!!!7",
5496 "SetPortPower", "ClearPortPower", "!!!10", "!!!11",
5497 "!!!12", "!!!13", "!!!14", "!!!15",
5498 "ClearCSC", "ClearPESC", "ClearPSSC", "ClearOCIC",
5499 "ClearPRSC", "!!!21", "!!!22", "!!!23",
5500 "!!!24", "!!!25", "!!!26", "!!!27",
5501 "!!!28", "!!!29", "!!!30", "!!!31"
5502 };
5503 Log2(("HcRhPortStatus_w(%#010x): port %u:", val, i));
5504 for (unsigned j = 0; j < RT_ELEMENTS(apszCmdNames); j++)
5505 if (val & (1 << j))
5506 Log2((" %s", apszCmdNames[j]));
5507 Log2(("\n"));
5508# endif
5509
5510 /* Write to clear any of the change bits: CSC, PESC, PSSC, OCIC and PRSC */
5511 if (val & OHCI_PORT_CLEAR_CHANGE_MASK)
5512 p->fReg &= ~(val & OHCI_PORT_CLEAR_CHANGE_MASK);
5513
5514 if (val & OHCI_PORT_CLRPE)
5515 {
5516 p->fReg &= ~OHCI_PORT_PES;
5517 Log2(("HcRhPortStatus_w(): port %u: DISABLE\n", i));
5518 }
5519
5520 if (ohciR3RhPortSetIfConnected(pDevIns, pThis, i, val & OHCI_PORT_PES))
5521 Log2(("HcRhPortStatus_w(): port %u: ENABLE\n", i));
5522
5523 if (ohciR3RhPortSetIfConnected(pDevIns, pThis, i, val & OHCI_PORT_PSS))
5524 Log2(("HcRhPortStatus_w(): port %u: SUSPEND - not implemented correctly!!!\n", i));
5525
5526 if (val & OHCI_PORT_PRS)
5527 {
5528 if (ohciR3RhPortSetIfConnected(pDevIns, pThis, i, val & OHCI_PORT_PRS))
5529 {
5530 PVM pVM = PDMDevHlpGetVM(pDevIns);
5531 p->fReg &= ~OHCI_PORT_PRSC;
5532 VUSBIRhDevReset(pThisCC->RootHub.pIRhConn, OHCI_PORT_2_VUSB_PORT(i), false /* don't reset on linux */,
5533 ohciR3PortResetDone, pDevIns, pVM);
5534 }
5535 else if (p->fReg & OHCI_PORT_PRS)
5536 {
5537 /* the guest is getting impatient. */
5538 Log2(("HcRhPortStatus_w(): port %u: Impatient guest!\n", i));
5539 RTThreadYield();
5540 }
5541 }
5542
5543 if (!(pThis->RootHub.desc_a & OHCI_RHA_NPS))
5544 {
5545 /** @todo To implement per-device power-switching
5546 * we need to check PortPowerControlMask to make
5547 * sure it isn't gang powered
5548 */
5549 if (val & OHCI_PORT_CLRPP)
5550 ohciR3RhPortPower(&pThisCC->RootHub, i, false /* power down */);
5551 if (val & OHCI_PORT_PPS)
5552 ohciR3RhPortPower(&pThisCC->RootHub, i, true /* power up */);
5553 }
5554
5555 /** @todo r=frank: ClearSuspendStatus. Timing? */
5556 if (val & OHCI_PORT_CLRSS)
5557 {
5558 ohciR3RhPortPower(&pThisCC->RootHub, i, true /* power up */);
5559 pThis->RootHub.aPorts[i].fReg &= ~OHCI_PORT_PSS;
5560 pThis->RootHub.aPorts[i].fReg |= OHCI_PORT_PSSC;
5561 ohciR3SetInterrupt(pDevIns, pThis, OHCI_INTR_ROOT_HUB_STATUS_CHANGE);
5562 }
5563
5564 if (p->fReg != old_state)
5565 {
5566 uint32_t res = p->fReg;
5567 uint32_t chg = res ^ old_state; NOREF(chg);
5568 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",
5569 val, i,
5570 chg & 1 ? "*" : "", res & 1,
5571 (chg >> 1) & 1 ? "*" : "", (res >> 1) & 1,
5572 (chg >> 2) & 1 ? "*" : "", (res >> 2) & 1,
5573 (chg >> 3) & 1 ? "*" : "", (res >> 3) & 1,
5574 (chg >> 4) & 1 ? "*" : "", (res >> 4) & 1,
5575 (chg >> 8) & 1 ? "*" : "", (res >> 8) & 1,
5576 (chg >> 9) & 1 ? "*" : "", (res >> 9) & 1,
5577 (chg >> 16) & 1 ? "*" : "", (res >> 16) & 1,
5578 (chg >> 17) & 1 ? "*" : "", (res >> 17) & 1,
5579 (chg >> 18) & 1 ? "*" : "", (res >> 18) & 1,
5580 (chg >> 19) & 1 ? "*" : "", (res >> 19) & 1,
5581 (chg >> 20) & 1 ? "*" : "", (res >> 20) & 1));
5582 }
5583 RT_NOREF(pDevIns);
5584 return VINF_SUCCESS;
5585#else /* !IN_RING3 */
5586 RT_NOREF(pDevIns, pThis, iReg, val);
5587 return VINF_IOM_R3_MMIO_WRITE;
5588#endif /* !IN_RING3 */
5589}
5590
5591/**
5592 * Register descriptor table
5593 */
5594static const OHCIOPREG g_aOpRegs[] =
5595{
5596 { "HcRevision", HcRevision_r, HcRevision_w }, /* 0 */
5597 { "HcControl", HcControl_r, HcControl_w }, /* 1 */
5598 { "HcCommandStatus", HcCommandStatus_r, HcCommandStatus_w }, /* 2 */
5599 { "HcInterruptStatus", HcInterruptStatus_r, HcInterruptStatus_w }, /* 3 */
5600 { "HcInterruptEnable", HcInterruptEnable_r, HcInterruptEnable_w }, /* 4 */
5601 { "HcInterruptDisable", HcInterruptDisable_r, HcInterruptDisable_w }, /* 5 */
5602 { "HcHCCA", HcHCCA_r, HcHCCA_w }, /* 6 */
5603 { "HcPeriodCurrentED", HcPeriodCurrentED_r, HcPeriodCurrentED_w }, /* 7 */
5604 { "HcControlHeadED", HcControlHeadED_r, HcControlHeadED_w }, /* 8 */
5605 { "HcControlCurrentED", HcControlCurrentED_r, HcControlCurrentED_w }, /* 9 */
5606 { "HcBulkHeadED", HcBulkHeadED_r, HcBulkHeadED_w }, /* 10 */
5607 { "HcBulkCurrentED", HcBulkCurrentED_r, HcBulkCurrentED_w }, /* 11 */
5608 { "HcDoneHead", HcDoneHead_r, HcDoneHead_w }, /* 12 */
5609 { "HcFmInterval", HcFmInterval_r, HcFmInterval_w }, /* 13 */
5610 { "HcFmRemaining", HcFmRemaining_r, HcFmRemaining_w }, /* 14 */
5611 { "HcFmNumber", HcFmNumber_r, HcFmNumber_w }, /* 15 */
5612 { "HcPeriodicStart", HcPeriodicStart_r, HcPeriodicStart_w }, /* 16 */
5613 { "HcLSThreshold", HcLSThreshold_r, HcLSThreshold_w }, /* 17 */
5614 { "HcRhDescriptorA", HcRhDescriptorA_r, HcRhDescriptorA_w }, /* 18 */
5615 { "HcRhDescriptorB", HcRhDescriptorB_r, HcRhDescriptorB_w }, /* 19 */
5616 { "HcRhStatus", HcRhStatus_r, HcRhStatus_w }, /* 20 */
5617
5618 /* The number of port status register depends on the definition
5619 * of OHCI_NDP_MAX macro
5620 */
5621 { "HcRhPortStatus[0]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 21 */
5622 { "HcRhPortStatus[1]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 22 */
5623 { "HcRhPortStatus[2]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 23 */
5624 { "HcRhPortStatus[3]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 24 */
5625 { "HcRhPortStatus[4]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 25 */
5626 { "HcRhPortStatus[5]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 26 */
5627 { "HcRhPortStatus[6]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 27 */
5628 { "HcRhPortStatus[7]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 28 */
5629 { "HcRhPortStatus[8]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 29 */
5630 { "HcRhPortStatus[9]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 30 */
5631 { "HcRhPortStatus[10]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 31 */
5632 { "HcRhPortStatus[11]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 32 */
5633 { "HcRhPortStatus[12]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 33 */
5634 { "HcRhPortStatus[13]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 34 */
5635 { "HcRhPortStatus[14]", HcRhPortStatus_r, HcRhPortStatus_w }, /* 35 */
5636};
5637
5638/* Quick way to determine how many op regs are valid. Since at least one port must
5639 * be configured (and no more than 15), there will be between 22 and 36 registers.
5640 */
5641#define NUM_OP_REGS(pohci) (21 + OHCI_NDP_CFG(pohci))
5642
5643AssertCompile(RT_ELEMENTS(g_aOpRegs) > 21);
5644AssertCompile(RT_ELEMENTS(g_aOpRegs) <= 36);
5645
5646/**
5647 * @callback_method_impl{FNIOMMMIONEWREAD}
5648 */
5649static DECLCALLBACK(VBOXSTRICTRC) ohciMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
5650{
5651 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5652 RT_NOREF(pvUser);
5653
5654 /* Paranoia: Assert that IOMMMIO_FLAGS_READ_DWORD works. */
5655 AssertReturn(cb == sizeof(uint32_t), VERR_INTERNAL_ERROR_3);
5656 AssertReturn(!(off & 0x3), VERR_INTERNAL_ERROR_4);
5657
5658 /*
5659 * Validate the register and call the read operator.
5660 */
5661 VBOXSTRICTRC rc;
5662 const uint32_t iReg = off >> 2;
5663 if (iReg < NUM_OP_REGS(pThis))
5664 rc = g_aOpRegs[iReg].pfnRead(pDevIns, pThis, iReg, (uint32_t *)pv);
5665 else
5666 {
5667 Log(("ohci: Trying to read register %u/%u!!!\n", iReg, NUM_OP_REGS(pThis)));
5668 rc = VINF_IOM_MMIO_UNUSED_FF;
5669 }
5670 return rc;
5671}
5672
5673
5674/**
5675 * @callback_method_impl{FNIOMMMIONEWWRITE}
5676 */
5677static DECLCALLBACK(VBOXSTRICTRC) ohciMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
5678{
5679 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5680 RT_NOREF(pvUser);
5681
5682 /* Paranoia: Assert that IOMMMIO_FLAGS_WRITE_DWORD_ZEROED works. */
5683 AssertReturn(cb == sizeof(uint32_t), VERR_INTERNAL_ERROR_3);
5684 AssertReturn(!(off & 0x3), VERR_INTERNAL_ERROR_4);
5685
5686 /*
5687 * Validate the register and call the read operator.
5688 */
5689 VBOXSTRICTRC rc;
5690 const uint32_t iReg = off >> 2;
5691 if (iReg < NUM_OP_REGS(pThis))
5692 rc = g_aOpRegs[iReg].pfnWrite(pDevIns, pThis, iReg, *(uint32_t const *)pv);
5693 else
5694 {
5695 Log(("ohci: Trying to write to register %u/%u!!!\n", iReg, NUM_OP_REGS(pThis)));
5696 rc = VINF_SUCCESS;
5697 }
5698 return rc;
5699}
5700
5701#ifdef IN_RING3
5702
5703/**
5704 * Saves the state of the OHCI device.
5705 *
5706 * @returns VBox status code.
5707 * @param pDevIns The device instance.
5708 * @param pSSM The handle to save the state to.
5709 */
5710static DECLCALLBACK(int) ohciR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5711{
5712 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5713 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5714 LogFlow(("ohciR3SaveExec:\n"));
5715
5716 int rc = pDevIns->pHlpR3->pfnSSMPutStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
5717 AssertRCReturn(rc, rc);
5718
5719 /* Save the periodic frame rate so we can we can tell if the bus was started or not when restoring. */
5720 return pDevIns->pHlpR3->pfnSSMPutU32(pSSM, VUSBIRhGetPeriodicFrameRate(pThisCC->RootHub.pIRhConn));
5721}
5722
5723
5724/**
5725 * Loads the state of the OHCI device.
5726 *
5727 * @returns VBox status code.
5728 * @param pDevIns The device instance.
5729 * @param pSSM The handle to the saved state.
5730 * @param uVersion The data unit version number.
5731 * @param uPass The data pass.
5732 */
5733static DECLCALLBACK(int) ohciR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5734{
5735 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5736 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5737 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5738 int rc;
5739 LogFlow(("ohciR3LoadExec:\n"));
5740
5741 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
5742
5743 if (uVersion >= OHCI_SAVED_STATE_VERSION_EOF_TIMER)
5744 rc = pHlp->pfnSSMGetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields[0], NULL);
5745 else if (uVersion == OHCI_SAVED_STATE_VERSION_8PORTS)
5746 rc = pHlp->pfnSSMGetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aOhciFields8Ports[0], NULL);
5747 else
5748 AssertMsgFailedReturn(("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
5749 AssertRCReturn(rc, rc);
5750
5751 /*
5752 * Get the frame rate / started indicator.
5753 *
5754 * For older versions there is a timer saved here. We'll skip it and deduce
5755 * the periodic frame rate from the host controller functional state.
5756 */
5757 if (uVersion > OHCI_SAVED_STATE_VERSION_EOF_TIMER)
5758 {
5759 rc = pHlp->pfnSSMGetU32(pSSM, &pThisCC->uRestoredPeriodicFrameRate);
5760 AssertRCReturn(rc, rc);
5761 }
5762 else
5763 {
5764 rc = pHlp->pfnSSMSkipToEndOfUnit(pSSM);
5765 AssertRCReturn(rc, rc);
5766
5767 uint32_t fHcfs = pThis->ctl & OHCI_CTL_HCFS;
5768 switch (fHcfs)
5769 {
5770 case OHCI_USB_OPERATIONAL:
5771 case OHCI_USB_RESUME:
5772 pThisCC->uRestoredPeriodicFrameRate = OHCI_DEFAULT_TIMER_FREQ;
5773 break;
5774 default:
5775 pThisCC->uRestoredPeriodicFrameRate = 0;
5776 break;
5777 }
5778 }
5779
5780 /** @todo could we restore the frame rate here instead of in ohciR3Resume? */
5781 return VINF_SUCCESS;
5782}
5783
5784
5785/**
5786 * Reset notification.
5787 *
5788 * @param pDevIns The device instance data.
5789 */
5790static DECLCALLBACK(void) ohciR3Reset(PPDMDEVINS pDevIns)
5791{
5792 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5793 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5794 LogFlow(("ohciR3Reset:\n"));
5795
5796 /*
5797 * There is no distinction between cold boot, warm reboot and software reboots,
5798 * all of these are treated as cold boots. We are also doing the initialization
5799 * job of a BIOS or SMM driver.
5800 *
5801 * Important: Don't confuse UsbReset with hardware reset. Hardware reset is
5802 * just one way of getting into the UsbReset state.
5803 */
5804 ohciR3DoReset(pDevIns, pThis, pThisCC, OHCI_USB_RESET, true /* reset devices */);
5805}
5806
5807
5808/**
5809 * Resume notification.
5810 *
5811 * @param pDevIns The device instance data.
5812 */
5813static DECLCALLBACK(void) ohciR3Resume(PPDMDEVINS pDevIns)
5814{
5815 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5816 LogFlowFunc(("\n"));
5817
5818 /* Restart the frame thread if it was active when the loaded state was saved. */
5819 uint32_t uRestoredPeriodicFR = pThisCC->uRestoredPeriodicFrameRate;
5820 pThisCC->uRestoredPeriodicFrameRate = 0;
5821 if (uRestoredPeriodicFR)
5822 {
5823 LogFlowFunc(("Bus was active, enable periodic frame processing (rate: %u)\n", uRestoredPeriodicFR));
5824 int rc = pThisCC->RootHub.pIRhConn->pfnSetPeriodicFrameProcessing(pThisCC->RootHub.pIRhConn, uRestoredPeriodicFR);
5825 AssertRC(rc);
5826 }
5827}
5828
5829
5830/**
5831 * Info handler, device version. Dumps OHCI control registers.
5832 *
5833 * @param pDevIns Device instance which registered the info.
5834 * @param pHlp Callback functions for doing output.
5835 * @param pszArgs Argument string. Optional and specific to the handler.
5836 */
5837static DECLCALLBACK(void) ohciR3InfoRegs(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
5838{
5839 RT_NOREF(pszArgs);
5840 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5841 uint32_t val, ctl, status;
5842
5843 /* Control register */
5844 ctl = pThis->ctl;
5845 pHlp->pfnPrintf(pHlp, "HcControl: %08x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
5846 ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
5847 (ctl >> 9) & 1, (ctl >> 10) & 1);
5848
5849 /* Command status register */
5850 status = pThis->status;
5851 pHlp->pfnPrintf(pHlp, "HcCommandStatus: %08x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
5852 status, status & 1, (status >> 1) & 1, (status >> 2) & 1, (status >> 3) & 1, (status >> 16) & 3);
5853
5854 /* Interrupt status register */
5855 val = pThis->intr_status;
5856 pHlp->pfnPrintf(pHlp, "HcInterruptStatus: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
5857 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5858 (val >> 6) & 1, (val >> 30) & 1);
5859
5860 /* Interrupt enable register */
5861 val = pThis->intr;
5862 pHlp->pfnPrintf(pHlp, "HcInterruptEnable: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
5863 val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 5) & 1,
5864 (val >> 6) & 1, (val >> 30) & 1, (val >> 31) & 1);
5865
5866 /* HCCA address register */
5867 pHlp->pfnPrintf(pHlp, "HcHCCA: %08x\n", pThis->hcca);
5868
5869 /* Current periodic ED register */
5870 pHlp->pfnPrintf(pHlp, "HcPeriodCurrentED: %08x\n", pThis->per_cur);
5871
5872 /* Control ED registers */
5873 pHlp->pfnPrintf(pHlp, "HcControlHeadED: %08x\n", pThis->ctrl_head);
5874 pHlp->pfnPrintf(pHlp, "HcControlCurrentED: %08x\n", pThis->ctrl_cur);
5875
5876 /* Bulk ED registers */
5877 pHlp->pfnPrintf(pHlp, "HcBulkHeadED: %08x\n", pThis->bulk_head);
5878 pHlp->pfnPrintf(pHlp, "HcBulkCurrentED: %08x\n", pThis->bulk_cur);
5879
5880 /* Done head register */
5881 pHlp->pfnPrintf(pHlp, "HcDoneHead: %08x\n", pThis->done);
5882
5883 /* Done head register */
5884 pHlp->pfnPrintf(pHlp, "HcDoneHead: %08x\n", pThis->done);
5885
5886 /* Root hub descriptor A */
5887 val = pThis->RootHub.desc_a;
5888 pHlp->pfnPrintf(pHlp, "HcRhDescriptorA: %08x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTPGT=%d\n",
5889 val, (uint8_t)val, (val >> 8) & 1, (val >> 9) & 1, (val >> 10) & 1, (val >> 11) & 1, (val >> 12) & 1, (uint8_t)(val >> 24));
5890
5891 /* Root hub descriptor B */
5892 val = pThis->RootHub.desc_b;
5893 pHlp->pfnPrintf(pHlp, "HcRhDescriptorB: %08x - DR=%#04x PPCM=%#04x\n", val, (uint16_t)val, (uint16_t)(val >> 16));
5894
5895 /* Root hub status register */
5896 val = pThis->RootHub.status;
5897 pHlp->pfnPrintf(pHlp, "HcRhStatus: %08x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n\n",
5898 val, val & 1, (val >> 1) & 1, (val >> 15) & 1, (val >> 16) & 1, (val >> 17) & 1, (val >> 31) & 1);
5899
5900 /* Port status registers */
5901 for (unsigned i = 0; i < OHCI_NDP_CFG(pThis); ++i)
5902 {
5903 val = pThis->RootHub.aPorts[i].fReg;
5904 pHlp->pfnPrintf(pHlp, "HcRhPortStatus%02d: CCS=%d PES =%d PSS =%d POCI=%d PRS =%d PPS=%d LSDA=%d\n"
5905 " %08x - CSC=%d PESC=%d PSSC=%d OCIC=%d PRSC=%d\n",
5906 i, val & 1, (val >> 1) & 1, (val >> 2) & 1,(val >> 3) & 1, (val >> 4) & 1, (val >> 8) & 1, (val >> 9) & 1,
5907 val, (val >> 16) & 1, (val >> 17) & 1, (val >> 18) & 1, (val >> 19) & 1, (val >> 20) & 1);
5908 }
5909}
5910
5911
5912/**
5913 * Destruct a device instance.
5914 *
5915 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
5916 * resources can be freed correctly.
5917 *
5918 * @returns VBox status code.
5919 * @param pDevIns The device instance data.
5920 */
5921static DECLCALLBACK(int) ohciR3Destruct(PPDMDEVINS pDevIns)
5922{
5923 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5924 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5925 POHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCICC);
5926
5927 if (RTCritSectIsInitialized(&pThisCC->CritSect))
5928 RTCritSectDelete(&pThisCC->CritSect);
5929 PDMDevHlpCritSectDelete(pDevIns, &pThis->CsIrq);
5930
5931 /*
5932 * Tear down the per endpoint in-flight tracking...
5933 */
5934
5935 return VINF_SUCCESS;
5936}
5937
5938
5939/**
5940 * @interface_method_impl{PDMDEVREG,pfnConstruct,OHCI constructor}
5941 */
5942static DECLCALLBACK(int) ohciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5943{
5944 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5945 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
5946 POHCIR3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, POHCIR3);
5947
5948 /*
5949 * Init instance data.
5950 */
5951 pThisCC->pDevInsR3 = pDevIns;
5952
5953 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
5954 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
5955
5956 PDMPciDevSetVendorId(pPciDev, 0x106b);
5957 PDMPciDevSetDeviceId(pPciDev, 0x003f);
5958 PDMPciDevSetClassProg(pPciDev, 0x10); /* OHCI */
5959 PDMPciDevSetClassSub(pPciDev, 0x03);
5960 PDMPciDevSetClassBase(pPciDev, 0x0c);
5961 PDMPciDevSetInterruptPin(pPciDev, 0x01);
5962#ifdef VBOX_WITH_MSI_DEVICES
5963 PDMPciDevSetStatus(pPciDev, VBOX_PCI_STATUS_CAP_LIST);
5964 PDMPciDevSetCapabilityList(pPciDev, 0x80);
5965#endif
5966
5967 pThisCC->RootHub.pOhci = pThis;
5968 pThisCC->RootHub.IBase.pfnQueryInterface = ohciR3RhQueryInterface;
5969 pThisCC->RootHub.IRhPort.pfnGetAvailablePorts = ohciR3RhGetAvailablePorts;
5970 pThisCC->RootHub.IRhPort.pfnGetUSBVersions = ohciR3RhGetUSBVersions;
5971 pThisCC->RootHub.IRhPort.pfnAttach = ohciR3RhAttach;
5972 pThisCC->RootHub.IRhPort.pfnDetach = ohciR3RhDetach;
5973 pThisCC->RootHub.IRhPort.pfnReset = ohciR3RhReset;
5974 pThisCC->RootHub.IRhPort.pfnXferCompletion = ohciR3RhXferCompletion;
5975 pThisCC->RootHub.IRhPort.pfnXferError = ohciR3RhXferError;
5976 pThisCC->RootHub.IRhPort.pfnStartFrame = ohciR3StartFrame;
5977 pThisCC->RootHub.IRhPort.pfnFrameRateChanged = ohciR3FrameRateChanged;
5978
5979 /* USB LED */
5980 pThisCC->RootHub.Led.u32Magic = PDMLED_MAGIC;
5981 pThisCC->RootHub.ILeds.pfnQueryStatusLed = ohciR3RhQueryStatusLed;
5982
5983
5984 /*
5985 * Read configuration.
5986 */
5987 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Ports", "");
5988
5989 /* Number of ports option. */
5990 uint32_t cPorts;
5991 int rc = pDevIns->pHlpR3->pfnCFGMQueryU32Def(pCfg, "Ports", &cPorts, OHCI_NDP_DEFAULT);
5992 if (RT_FAILURE(rc))
5993 return PDMDEV_SET_ERROR(pDevIns, rc, N_("OHCI configuration error: failed to read Ports as integer"));
5994 if (cPorts == 0 || cPorts > OHCI_NDP_MAX)
5995 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5996 N_("OHCI configuration error: Ports must be in range [%u,%u]"),
5997 1, OHCI_NDP_MAX);
5998
5999 /* Store the configured NDP; it will be used everywhere else from now on. */
6000 pThis->RootHub.desc_a = cPorts;
6001
6002 /*
6003 * Register PCI device and I/O region.
6004 */
6005 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
6006 if (RT_FAILURE(rc))
6007 return rc;
6008
6009#ifdef VBOX_WITH_MSI_DEVICES
6010 PDMMSIREG MsiReg;
6011 RT_ZERO(MsiReg);
6012 MsiReg.cMsiVectors = 1;
6013 MsiReg.iMsiCapOffset = 0x80;
6014 MsiReg.iMsiNextOffset = 0x00;
6015 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
6016 if (RT_FAILURE(rc))
6017 {
6018 PDMPciDevSetCapabilityList(pPciDev, 0x0);
6019 /* That's OK, we can work without MSI */
6020 }
6021#endif
6022
6023 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 0, 4096, PCI_ADDRESS_SPACE_MEM, ohciMmioWrite, ohciMmioRead, NULL /*pvUser*/,
6024 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED
6025 | IOMMMIO_FLAGS_DBGSTOP_ON_COMPLICATED_WRITE, "USB OHCI", &pThis->hMmio);
6026 AssertRCReturn(rc, rc);
6027
6028 /*
6029 * Register the saved state data unit.
6030 */
6031 rc = PDMDevHlpSSMRegisterEx(pDevIns, OHCI_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
6032 NULL, NULL, NULL,
6033 NULL, ohciR3SaveExec, NULL,
6034 NULL, ohciR3LoadExec, NULL);
6035 AssertRCReturn(rc, rc);
6036
6037 /*
6038 * Attach to the VBox USB RootHub Driver on LUN #0.
6039 */
6040 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->RootHub.IBase, &pThisCC->RootHub.pIBase, "RootHub");
6041 if (RT_FAILURE(rc))
6042 {
6043 AssertMsgFailed(("Configuration error: No roothub driver attached to LUN #0!\n"));
6044 return rc;
6045 }
6046 pThisCC->RootHub.pIRhConn = PDMIBASE_QUERY_INTERFACE(pThisCC->RootHub.pIBase, VUSBIROOTHUBCONNECTOR);
6047 AssertMsgReturn(pThisCC->RootHub.pIRhConn,
6048 ("Configuration error: The driver doesn't provide the VUSBIROOTHUBCONNECTOR interface!\n"),
6049 VERR_PDM_MISSING_INTERFACE);
6050
6051 /*
6052 * Attach status driver (optional).
6053 */
6054 PPDMIBASE pBase;
6055 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->RootHub.IBase, &pBase, "Status Port");
6056 if (RT_SUCCESS(rc))
6057 pThisCC->RootHub.pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
6058 else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
6059 {
6060 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
6061 return rc;
6062 }
6063
6064 /* Set URB parameters. */
6065 rc = VUSBIRhSetUrbParams(pThisCC->RootHub.pIRhConn, sizeof(VUSBURBHCIINT), sizeof(VUSBURBHCITDINT));
6066 if (RT_FAILURE(rc))
6067 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("OHCI: Failed to set URB parameters"));
6068
6069 /*
6070 * Take down the virtual clock frequence for use in ohciR3FrameRateChanged().
6071 * (Used to be a timer, thus the name.)
6072 */
6073 pThis->u64TimerHz = PDMDevHlpTMTimeVirtGetFreq(pDevIns);
6074
6075 /*
6076 * Critical sections: explain
6077 */
6078 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CsIrq, RT_SRC_POS, "OHCI#%uIrq", iInstance);
6079 if (RT_FAILURE(rc))
6080 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("OHCI: Failed to create critical section"));
6081
6082 rc = RTCritSectInit(&pThisCC->CritSect);
6083 if (RT_FAILURE(rc))
6084 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("OHCI: Failed to create critical section"));
6085
6086 /*
6087 * Do a hardware reset.
6088 */
6089 ohciR3DoReset(pDevIns, pThis, pThisCC, OHCI_USB_RESET, false /* don't reset devices */);
6090
6091# ifdef VBOX_WITH_STATISTICS
6092 /*
6093 * Register statistics.
6094 */
6095 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledIsocUrbs, STAMTYPE_COUNTER, "CanceledIsocUrbs", STAMUNIT_OCCURENCES, "Detected canceled isochronous URBs.");
6096 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledGenUrbs, STAMTYPE_COUNTER, "CanceledGenUrbs", STAMUNIT_OCCURENCES, "Detected canceled general URBs.");
6097 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatDroppedUrbs, STAMTYPE_COUNTER, "DroppedUrbs", STAMUNIT_OCCURENCES, "Dropped URBs (endpoint halted, or URB canceled).");
6098# endif
6099
6100 /*
6101 * Register debugger info callbacks.
6102 */
6103 PDMDevHlpDBGFInfoRegister(pDevIns, "ohci", "OHCI control registers.", ohciR3InfoRegs);
6104
6105# if 0/*def DEBUG_bird*/
6106// g_fLogInterruptEPs = true;
6107 g_fLogControlEPs = true;
6108 g_fLogBulkEPs = true;
6109# endif
6110
6111 return VINF_SUCCESS;
6112}
6113
6114#else /* !IN_RING3 */
6115
6116/**
6117 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
6118 */
6119static DECLCALLBACK(int) ohciRZConstruct(PPDMDEVINS pDevIns)
6120{
6121 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
6122 POHCI pThis = PDMDEVINS_2_DATA(pDevIns, POHCI);
6123
6124 int rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, ohciMmioWrite, ohciMmioRead, NULL /*pvUser*/);
6125 AssertRCReturn(rc, rc);
6126
6127 return VINF_SUCCESS;
6128}
6129
6130#endif /* !IN_RING3 */
6131
6132const PDMDEVREG g_DeviceOHCI =
6133{
6134 /* .u32version = */ PDM_DEVREG_VERSION,
6135 /* .uReserved0 = */ 0,
6136 /* .szName = */ "usb-ohci",
6137 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
6138 /* .fClass = */ PDM_DEVREG_CLASS_BUS_USB,
6139 /* .cMaxInstances = */ ~0U,
6140 /* .uSharedVersion = */ 42,
6141 /* .cbInstanceShared = */ sizeof(OHCI),
6142 /* .cbInstanceCC = */ sizeof(OHCICC),
6143 /* .cbInstanceRC = */ 0,
6144 /* .cMaxPciDevices = */ 1,
6145 /* .cMaxMsixVectors = */ 0,
6146 /* .pszDescription = */ "OHCI USB controller.\n",
6147#if defined(IN_RING3)
6148 /* .pszRCMod = */ "VBoxDDRC.rc",
6149 /* .pszR0Mod = */ "VBoxDDR0.r0",
6150 /* .pfnConstruct = */ ohciR3Construct,
6151 /* .pfnDestruct = */ ohciR3Destruct,
6152 /* .pfnRelocate = */ NULL,
6153 /* .pfnMemSetup = */ NULL,
6154 /* .pfnPowerOn = */ NULL,
6155 /* .pfnReset = */ ohciR3Reset,
6156 /* .pfnSuspend = */ NULL,
6157 /* .pfnResume = */ ohciR3Resume,
6158 /* .pfnAttach = */ NULL,
6159 /* .pfnDetach = */ NULL,
6160 /* .pfnQueryInterface = */ NULL,
6161 /* .pfnInitComplete = */ NULL,
6162 /* .pfnPowerOff = */ NULL,
6163 /* .pfnSoftReset = */ NULL,
6164 /* .pfnReserved0 = */ NULL,
6165 /* .pfnReserved1 = */ NULL,
6166 /* .pfnReserved2 = */ NULL,
6167 /* .pfnReserved3 = */ NULL,
6168 /* .pfnReserved4 = */ NULL,
6169 /* .pfnReserved5 = */ NULL,
6170 /* .pfnReserved6 = */ NULL,
6171 /* .pfnReserved7 = */ NULL,
6172#elif defined(IN_RING0)
6173 /* .pfnEarlyConstruct = */ NULL,
6174 /* .pfnConstruct = */ ohciRZConstruct,
6175 /* .pfnDestruct = */ NULL,
6176 /* .pfnFinalDestruct = */ NULL,
6177 /* .pfnRequest = */ NULL,
6178 /* .pfnReserved0 = */ NULL,
6179 /* .pfnReserved1 = */ NULL,
6180 /* .pfnReserved2 = */ NULL,
6181 /* .pfnReserved3 = */ NULL,
6182 /* .pfnReserved4 = */ NULL,
6183 /* .pfnReserved5 = */ NULL,
6184 /* .pfnReserved6 = */ NULL,
6185 /* .pfnReserved7 = */ NULL,
6186#elif defined(IN_RC)
6187 /* .pfnConstruct = */ ohciRZConstruct,
6188 /* .pfnReserved0 = */ NULL,
6189 /* .pfnReserved1 = */ NULL,
6190 /* .pfnReserved2 = */ NULL,
6191 /* .pfnReserved3 = */ NULL,
6192 /* .pfnReserved4 = */ NULL,
6193 /* .pfnReserved5 = */ NULL,
6194 /* .pfnReserved6 = */ NULL,
6195 /* .pfnReserved7 = */ NULL,
6196#else
6197# error "Not in IN_RING3, IN_RING0 or IN_RC!"
6198#endif
6199 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
6200};
6201
6202#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

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