VirtualBox

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

Last change on this file since 104124 was 104124, checked in by vboxsync, 6 months ago

OHCI: Comments, typos.

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

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