VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/DevEHCI.cpp

Last change on this file was 105209, checked in by vboxsync, 2 months ago

Devices/USB/DevEHCI: Adjust padding for the ITD, bugref:10709

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 187.6 KB
Line 
1/* $Id: DevEHCI.cpp 105209 2024-07-09 07:19:57Z vboxsync $ */
2/** @file
3 * DevEHCI - Enhanced 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_ehci EHCI - Enhanced Host Controller Interface Emulation.
29 *
30 * This component implements an EHCI USB controller. It is split roughly
31 * into two main parts, the first part implements the register level
32 * specification of USB EHCI and the second part maintains the root hub (which
33 * is an integrated component of the device).
34 *
35 * The EHCI 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.
39 *
40 * Note that all processing is currently done on a frame boundary and
41 * no attempt is made to emulate events with micro-frame granularity.
42 *
43 * The actual USB transfers are stored in main memory (along with endpoint and
44 * transfer descriptors). The ED's for all the control and bulk endpoints are
45 * found by consulting the HcAsyncListAddr register (ASYNCLISTADDR).
46 * Interrupt and isochronous ED's are found by looking at the HcPeriodicListBase
47 * (PERIODICLISTBASE) register.
48 *
49 * At the start of every frame (in function ehciR3StartOfFrame) we traverse all
50 * enabled ED lists and queue up as many transfers as possible. No attention
51 * is paid to control/bulk service ratios or bandwidth requirements since our
52 * USB could conceivably contain a dozen high speed busses and this would
53 * artificially limit the performance.
54 *
55 * Once we have a transfer ready to go (in the appropriate ehciServiceXxx function)
56 * we allocate an URB on the stack, fill in all the relevant fields and submit
57 * it using the VUSBIRhSubmitUrb function. The roothub device and the virtual
58 * USB core code coordinates everything else from this point onwards.
59 *
60 * When the URB has been successfully handed to the lower level driver, our
61 * prepare callback gets called and we can remove the TD from the ED transfer
62 * list. This stops us queueing it twice while it completes.
63 * bird: no, we don't remove it because that confuses the guest! (=> crashes)
64 *
65 * Completed URBs are reaped at the end of every frame (in function
66 * ehciR3FrameBoundaryTimer). Our completion routine makes use of the ED and TD
67 * fields in the URB to store the physical addresses of the descriptors so
68 * that they may be modified in the roothub callbacks. Our completion
69 * routine (ehciRhXferCompleteXxx) carries out a number of tasks:
70 * -# Retires the TD associated with the transfer, setting the
71 * relevant error code etc.
72 * -# Updates done-queue interrupt timer and potentially causes
73 * a writeback of the done-queue.
74 * -# If the transfer was device-to-host, we copy the data into
75 * the host memory.
76 *
77 * As for error handling EHCI allows for 3 retries before failing a transfer,
78 * an error count is stored in each transfer descriptor. A halt flag is also
79 * stored in the transfer descriptor. That allows for ED's to be disabled
80 * without stopping the bus and de-queuing them.
81 *
82 * When the bus is started and stopped, we call VUSBIDevPowerOn/Off() on our
83 * roothub to indicate it's powering up and powering down. Whenever we power
84 * down, the USB core makes sure to synchronously complete all outstanding
85 * requests so that the EHCI is never seen in an inconsistent state by the
86 * guest OS (Transfers are not meant to be unlinked until they've actually
87 * completed, but we can't do that unless we work synchronously, so we just
88 * have to fake it).
89 * bird: we do work synchronously now, anything causes guest crashes.
90 *
91 * The number of ports is configurable. The architectural maximum is 15, but
92 * some guests (e.g. OS/2) crash if they see more than 12 or so ports. Saved
93 * states always include the data for all 15 ports but HCSPARAMS determines
94 * the actual number visible to the guest.
95 */
96
97
98/*********************************************************************************************************************************
99* Header Files *
100*********************************************************************************************************************************/
101#define LOG_GROUP LOG_GROUP_DEV_EHCI
102#include <VBox/pci.h>
103#include <VBox/vmm/pdm.h>
104#include <VBox/vmm/mm.h>
105#include <VBox/err.h>
106#include <VBox/log.h>
107#include <iprt/assert.h>
108#include <iprt/string.h>
109#include <iprt/asm.h>
110#include <iprt/param.h>
111#include <iprt/thread.h>
112#include <iprt/semaphore.h>
113#include <iprt/critsect.h>
114#ifdef IN_RING3
115# include <iprt/thread.h>
116# include <iprt/mem.h>
117# include <iprt/uuid.h>
118#endif
119#include <VBox/vusb.h>
120#ifdef VBOX_IN_EXTPACK_R3
121# include <VBox/version.h>
122#elif defined(VBOX_IN_EXTPACK)
123# include <VBox/sup.h>
124#endif
125#ifndef VBOX_IN_EXTPACK
126# include "VBoxDD.h"
127#endif
128
129
130/** The saved state version. */
131#define EHCI_SAVED_STATE_VERSION 7
132/** The saved state version before the EOF timers were removed. */
133#define EHCI_SAVED_STATE_VERSION_PRE_TIMER_REMOVAL 6 /* Introduced in 5.2. */
134/** The saved state with support of 8 ports. */
135#define EHCI_SAVED_STATE_VERSION_8PORTS 5 /* Introduced in 3.1 or so. */
136
137/** Number of Downstream Ports on the root hub; 15 is the maximum
138 * the EHCI specification provides for. */
139#define EHCI_NDP_MAX 15
140
141/** The default Number of Downstream Ports reported to guests. */
142#define EHCI_NDP_DEFAULT 12
143
144/* Macro to query the number of currently configured ports. */
145#define EHCI_NDP_CFG(pehci) ((pehci)->hcs_params & EHCI_HCS_PARAMS_NDP_MASK)
146/** Macro to convert a EHCI port index (zero based) to a VUSB roothub port ID (one based). */
147#define EHCI_PORT_2_VUSB_PORT(a_uPort) ((a_uPort) + 1)
148
149/** Size of the capability part of the MMIO page. */
150#define EHCI_CAPS_REG_SIZE 0x20
151
152
153#ifndef VBOX_DEVICE_STRUCT_TESTCASE
154/**
155 * Host controller Transfer Descriptor data.
156 */
157typedef struct VUSBURBHCITDINT
158{
159 /** Type of TD. */
160 uint32_t TdType;
161 /** The address of the TD. */
162 RTGCPHYS TdAddr;
163 /** A copy of the TD. */
164 uint32_t TdCopy[18];
165} VUSBURBHCITDINT;
166
167/**
168 * The host controller data associated with each URB.
169 */
170typedef struct VUSBURBHCIINT
171{
172 /** The endpoint descriptor address. */
173 RTGCPHYS EdAddr;
174 /** Number of Tds in the array. */
175 uint32_t cTds;
176 /** When this URB was created.
177 * (Used for isochronous frames and for logging.) */
178 uint32_t u32FrameNo;
179 /** Flag indicating that the TDs have been unlinked. */
180 bool fUnlinked;
181} VUSBURBHCIINT;
182#endif
183
184/**
185 * An EHCI root hub port, shared.
186 */
187typedef struct EHCIHUBPORT
188{
189 /** The port register. */
190 uint32_t fReg;
191} EHCIHUBPORT;
192/** Pointer to a shared EHCI root hub port. */
193typedef EHCIHUBPORT *PEHCIHUBPORT;
194
195/**
196 * An EHCI root hub port, ring-3.
197 */
198typedef struct EHCIHUBPORTR3
199{
200 /** Flag whether there is a device attached to the port. */
201 bool fAttached;
202} EHCIHUBPORTR3;
203/** Pointer to a ring-3 EHCI root hub port. */
204typedef EHCIHUBPORTR3 *PEHCIHUBPORTR3;
205
206
207/**
208 * The EHCI root hub, shared.
209 */
210typedef struct EHCIROOTHUB
211{
212 /** Per-port state. */
213 EHCIHUBPORT aPorts[EHCI_NDP_MAX];
214 /** Unused, only needed for saved state compatibility. */
215 uint32_t unused;
216} EHCIROOTHUB;
217/** Pointer to the EHCI root hub. */
218typedef EHCIROOTHUB *PEHCIROOTHUB;
219
220
221/**
222 * The EHCI root hub, ring-3 edition.
223 *
224 * @implements PDMIBASE
225 * @implements VUSBIROOTHUBPORT
226 * @implements PDMILEDPORTS
227 */
228typedef struct EHCIROOTHUBR3
229{
230 /** Pointer to the base interface of the VUSB RootHub. */
231 R3PTRTYPE(PPDMIBASE) pIBase;
232 /** Pointer to the connector interface of the VUSB RootHub. */
233 R3PTRTYPE(PVUSBIROOTHUBCONNECTOR) pIRhConn;
234 /** The base interface exposed to the roothub driver. */
235 PDMIBASE IBase;
236 /** The roothub port interface exposed to the roothub driver. */
237 VUSBIROOTHUBPORT IRhPort;
238
239 /** The LED. */
240 PDMLED Led;
241 /** The LED ports. */
242 PDMILEDPORTS ILeds;
243 /** Partner of ILeds. */
244 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
245
246 EHCIHUBPORTR3 aPorts[EHCI_NDP_MAX];
247} EHCIROOTHUBR3;
248/** Pointer to the ring-3 EHCI root hub state. */
249typedef EHCIROOTHUBR3 *PEHCIROOTHUBR3;
250
251
252/**
253 * EHCI device data, shared.
254 */
255typedef struct EHCI
256{
257 /** Async scheduler sleeping; triggered by empty list detection */
258 bool fAsyncTraversalTimerActive;
259
260 bool afAlignment0[7];
261
262 /** Start of current frame. */
263 uint64_t SofTime;
264 /** Root hub device */
265 EHCIROOTHUB RootHub;
266
267 /** @name Host Controller Capability Registers (R/O)
268 * @{ */
269 /** CAPLENGTH: base + cap_length = operational register start */
270 uint32_t cap_length;
271 /** HCIVERSION: host controller interface version */
272 uint32_t hci_version;
273 /** HCSPARAMS: Structural parameters */
274 uint32_t hcs_params;
275 /** HCCPARAMS: Capability parameters */
276 uint32_t hcc_params;
277 /** @} */
278
279 /** @name Host Controller Operational Registers (R/W)
280 * @{ */
281 /** USB command register */
282 uint32_t cmd;
283 /** USB status register */
284 uint32_t intr_status;
285 /** USB interrupt enable register */
286 uint32_t intr;
287 /** Frame index register; actually it's micro-frame number */
288 uint32_t frame_idx;
289 /** Control Data Structure Segment Register */
290 uint32_t ds_segment;
291 /** Periodic Frame List Base Address Register */
292 uint32_t periodic_list_base;
293 /** Current Asynchronous List Address Register */
294 uint32_t async_list_base;
295 /** Configure Flag Register */
296 uint32_t config;
297 /** @} */
298
299 /** @name Control partition (registers)
300 * @{ */
301 /** Interrupt interval; see interrupt threshold in the command register */
302 uint32_t uIrqInterval;
303 /** @} */
304
305 /** @name Frame counter partition (registers)
306 * @{ */
307 /** HcFmNumber.
308 * @remark The register size is 16-bit, but for debugging and performance
309 * reasons we maintain a 32-bit counter. */
310 uint32_t HcFmNumber;
311 /** Number of micro-frames per timer call */
312 uint32_t uFramesPerTimerCall;
313 /** @} */
314
315 /** Flag whether the framer thread should processing frames. */
316 volatile bool fBusStarted;
317
318 bool afAlignment1[3]; /**< Align CsIrq correctly. */
319
320 /* The following members are not part of saved state. */
321
322 /** Critical section synchronising interrupt handling. */
323 PDMCRITSECT CsIrq;
324
325 /** The MMIO region. */
326 IOMMMIOHANDLE hMmio;
327} EHCI;
328AssertCompileMemberAlignment(EHCI, CsIrq, 8);
329/** Pointer to shared EHCI device data. */
330typedef struct EHCI *PEHCI;
331
332
333/**
334 * EHCI device data, ring-3 edition.
335 */
336typedef struct EHCIR3
337{
338 /** Root hub device. */
339 EHCIROOTHUBR3 RootHub;
340
341 /** The number of virtual time ticks per frame. */
342 uint64_t cTicksPerFrame;
343 /** The number of virtual time ticks per USB bus tick. */
344 uint64_t cTicksPerUsbTick;
345
346 /** Pointer to the device instance. */
347 PPDMDEVINSR3 pDevIns;
348
349 /** Number of in-flight TDs. */
350 unsigned cInFlight;
351 unsigned Alignment2; /**< Align aInFlight on a 8 byte boundary. */
352 /** Array of in-flight TDs. */
353 struct ehci_td_in_flight
354 {
355 /** Address of the transport descriptor. */
356 RTGCPHYS GCPhysTD;
357 /** Pointer to the URB. */
358 R3PTRTYPE(PVUSBURB) pUrb;
359 } aInFlight[257];
360
361 /** Detected canceled isochronous URBs. */
362 STAMCOUNTER StatCanceledIsocUrbs;
363 /** Detected canceled general URBs. */
364 STAMCOUNTER StatCanceledGenUrbs;
365 /** Dropped URBs (endpoint halted, or URB canceled). */
366 STAMCOUNTER StatDroppedUrbs;
367
368 /* The following members are not part of saved state. */
369
370 /** VM timer frequency used for frame timer calculations. */
371 uint64_t u64TimerHz;
372 /** Number of USB work cycles with no transfers. */
373 uint32_t cIdleCycles;
374 /** Current frame timer rate (default 1000). */
375 uint32_t uFrameRate;
376 /** Idle detection flag; must be cleared at start of frame */
377 bool fIdle;
378 bool afAlignment4[3];
379
380 /** Default frequency of the frame timer. */
381 uint32_t uFrameRateDefault;
382 /** How long to wait until the next frame. */
383 uint64_t nsWait;
384 /** The framer thread. */
385 R3PTRTYPE(PPDMTHREAD) hThreadFrame;
386 /** Event semaphore to interact with the framer thread. */
387 R3PTRTYPE(RTSEMEVENTMULTI) hSemEventFrame;
388 /** Event semaphore to release the thread waiting for the framer thread to stop. */
389 R3PTRTYPE(RTSEMEVENTMULTI) hSemEventFrameStopped;
390 /** Critical section to synchronize the framer and URB completion handler. */
391 RTCRITSECT CritSect;
392} EHCIR3;
393/** Pointer to ring-3 EHCI device data. */
394typedef struct EHCIR3 *PEHCIR3;
395
396
397/**
398 * EHCI device data, ring-0 edition.
399 */
400typedef struct EHCIR0
401{
402 uint32_t uUnused;
403} EHCIR0;
404/** Pointer to ring-0 EHCI device data. */
405typedef struct EHCIR0 *PEHCIR0;
406
407
408/**
409 * EHCI device data, raw-mode edition.
410 */
411typedef struct EHCIRC
412{
413 uint32_t uUnused;
414} EHCIRC;
415/** Pointer to raw-mode EHCI device data. */
416typedef struct EHCIRC *PEHCIRC;
417
418
419/** @typedef EHCICC
420 * The EHCI device data for the current context. */
421typedef CTX_SUFF(EHCI) EHCICC;
422/** @typedef PEHCICC
423 * Pointer to the EHCI device for the current context. */
424typedef CTX_SUFF(PEHCI) PEHCICC;
425
426
427/** @@name EHCI Transfer Descriptor Types
428 * @{ */
429/** Isochronous Transfer Descriptor */
430#define EHCI_DESCRIPTOR_ITD 0
431/** Queue Head */
432#define EHCI_DESCRIPTOR_QH 1
433/** Split Transaction Isochronous Transfer Descriptor */
434#define EHCI_DESCRIPTOR_SITD 2
435/** Frame Span Traversal Node */
436#define EHCI_DESCRIPTOR_FSTN 3
437/** @} */
438
439/** @@name EHCI Transfer service type
440 * @{ */
441typedef enum
442{
443 EHCI_SERVICE_PERIODIC = 0,
444 EHCI_SERVICE_ASYNC = 1
445} EHCI_SERVICE_TYPE;
446/** @} */
447
448/** @@name EHCI Frame List Element Pointer
449 * @{ */
450#define EHCI_FRAME_LIST_NEXTPTR_SHIFT 5
451
452typedef struct
453{
454 uint32_t Terminate : 1;
455 uint32_t Type : 2;
456 uint32_t Reserved : 2;
457 uint32_t FrameAddr : 27;
458} EHCI_FRAME_LIST_PTR;
459AssertCompileSize(EHCI_FRAME_LIST_PTR, 4);
460/** @} */
461
462/** @@name EHCI Isochronous Transfer Descriptor (iTD)
463 * @{ */
464#define EHCI_TD_PTR_SHIFT 5
465
466typedef struct
467{
468 uint32_t Terminate : 1;
469 uint32_t Type : 2;
470 uint32_t Reserved : 2;
471 uint32_t Pointer : 27;
472} EHCI_TD_PTR;
473AssertCompileSize(EHCI_TD_PTR, 4);
474
475typedef struct
476{
477 uint32_t Offset : 12;
478 uint32_t PG : 3;
479 uint32_t IOC : 1;
480 uint32_t Length : 12;
481 uint32_t TransactError : 1;
482 uint32_t Babble : 1;
483 uint32_t DataBufError : 1;
484 uint32_t Active : 1;
485} EHCI_ITD_TRANSACTION;
486AssertCompileSize(EHCI_ITD_TRANSACTION, 4);
487
488typedef struct
489{
490 uint32_t DeviceAddress : 7;
491 uint32_t Reserved1 : 1;
492 uint32_t EndPt : 4;
493 uint32_t Ignore1 : 20;
494 uint32_t MaxPacket : 11;
495 uint32_t DirectionIn : 1;
496 uint32_t Ignore2 : 20;
497 uint32_t Multi : 2;
498 uint32_t Reserved10 : 10;
499 uint32_t Ignore3 : 20;
500} EHCI_ITD_MISC;
501AssertCompileSize(EHCI_ITD_MISC, 12);
502
503#define EHCI_BUFFER_PTR_SHIFT 12
504
505typedef struct
506{
507 uint32_t Reserved : 12;
508 uint32_t Pointer : 20; /* 4k aligned */
509} EHCI_BUFFER_PTR;
510AssertCompileSize(EHCI_BUFFER_PTR, 4);
511
512#define EHCI_NUM_ITD_TRANSACTIONS 8
513#define EHCI_NUM_ITD_PAGES 7
514
515typedef struct
516{
517 EHCI_TD_PTR Next;
518 EHCI_ITD_TRANSACTION Transaction[EHCI_NUM_ITD_TRANSACTIONS];
519 union
520 {
521 EHCI_ITD_MISC Misc;
522 EHCI_BUFFER_PTR Buffer[EHCI_NUM_ITD_PAGES];
523 } Buffer;
524} EHCI_ITD, *PEHCI_ITD;
525typedef const EHCI_ITD *PEHCI_CITD;
526AssertCompileSize(EHCI_ITD, 0x40);
527/** @} */
528
529/* ITD with extra padding to add 8th and 9th 'Buffer' entry. The PG member of
530 * EHCI_ITD_TRANSACTION can contain values in the 0-7 range, but only values
531 * 0-6 are valid. The extra padding is added to avoid cluttering the code
532 * with range checks; ehciR3ReadItd() initializes the pad with a safe value.
533 * The EHCI 1.0 specification explicitly says using PG value of 7 yields
534 * undefined behavior. Two pad entries are needed because initial PG value
535 * of 7 (already 'wrong') can cross to the next page (8).
536 */
537typedef struct
538{
539 EHCI_TD_PTR Next;
540 EHCI_ITD_TRANSACTION Transaction[EHCI_NUM_ITD_TRANSACTIONS];
541 union
542 {
543 EHCI_ITD_MISC Misc;
544 EHCI_BUFFER_PTR Buffer[EHCI_NUM_ITD_PAGES + 2];
545 } Buffer;
546} EHCI_ITD_PAD, *PEHCI_ITD_PAD;
547AssertCompileSize(EHCI_ITD_PAD, 0x48);
548
549/** @name Split Transaction Isochronous Transfer Descriptor (siTD)
550 * @{ */
551typedef struct
552{
553 uint32_t DeviceAddress : 7;
554 uint32_t Reserved : 1;
555 uint32_t EndPt : 4;
556 uint32_t Reserved2 : 4;
557 uint32_t HubAddress : 7;
558 uint32_t Reserved3 : 1;
559 uint32_t Port : 7;
560 uint32_t DirectionIn : 1;
561} EHCI_SITD_ADDR;
562AssertCompileSize(EHCI_SITD_ADDR, 4);
563
564typedef struct
565{
566 uint32_t SMask : 8;
567 uint32_t CMask : 8;
568 uint32_t Reserved : 16;
569} EHCI_SITD_SCHEDCTRL;
570AssertCompileSize(EHCI_SITD_SCHEDCTRL, 4);
571
572typedef struct
573{
574 /* 8 Status flags */
575 uint32_t Reserved : 1;
576 uint32_t SplitXState : 1;
577 uint32_t MisseduFrame : 1;
578 uint32_t TransactError : 1;
579 uint32_t Babble : 1;
580 uint32_t DataBufError : 1;
581 uint32_t Error : 1;
582 uint32_t Active : 1;
583 uint32_t CPMask : 8;
584 uint32_t Length : 10;
585 uint32_t Reserved4 : 4;
586 uint32_t PageSelect : 1;
587 uint32_t IOC : 1;
588} EHCI_SITD_TRANSFER;
589AssertCompileSize(EHCI_SITD_TRANSFER, 4);
590
591typedef struct
592{
593 uint32_t Offset : 12;
594 uint32_t Pointer : 20; /**< 4k aligned */
595} EHCI_SITD_BUFFER0;
596AssertCompileSize(EHCI_SITD_BUFFER0, 4);
597
598typedef struct
599{
600 uint32_t TCount : 3;
601 uint32_t TPosition : 2;
602 uint32_t Reserved : 7;
603 uint32_t Pointer : 20; /**< 4k aligned */
604} EHCI_SITD_BUFFER1;
605AssertCompileSize(EHCI_SITD_BUFFER1, 4);
606
607typedef struct
608{
609 uint32_t Terminate : 1;
610 uint32_t Reserved : 4;
611 uint32_t Pointer : 27;
612} EHCI_SITD_BACKPTR;
613AssertCompileSize(EHCI_SITD_BACKPTR, 4);
614
615typedef struct
616{
617 EHCI_TD_PTR NextSITD;
618 EHCI_SITD_ADDR Address;
619 EHCI_SITD_SCHEDCTRL ScheduleCtrl;
620 EHCI_SITD_TRANSFER Transfer;
621 EHCI_SITD_BUFFER0 Buffer0;
622 EHCI_SITD_BUFFER1 Buffer1;
623 EHCI_SITD_BACKPTR BackPtr;
624} EHCI_SITD, *PEHCI_SITD;
625typedef const EHCI_SITD *PEHCI_CSITD;
626AssertCompileSize(EHCI_SITD, 0x1C);
627/** @} */
628
629
630/** @name Queue Element Transfer Descriptor (qTD)
631 * @{ */
632typedef struct
633{
634 uint32_t Terminate : 1;
635 uint32_t Reserved : 4;
636 uint32_t Pointer : 27;
637} EHCI_QTD_NEXTPTR;
638AssertCompileSize(EHCI_QTD_NEXTPTR, 4);
639
640typedef struct
641{
642 uint32_t Terminate : 1;
643 uint32_t Reserved : 4;
644 uint32_t Pointer : 27;
645} EHCI_QTD_ALTNEXTPTR;
646AssertCompileSize(EHCI_QTD_ALTNEXTPTR, 4);
647
648#define EHCI_QTD_PID_OUT 0
649#define EHCI_QTD_PID_IN 1
650#define EHCI_QTD_PID_SETUP 2
651
652typedef struct
653{
654 /* 8 Status flags */
655 uint32_t PingState : 1;
656 uint32_t SplitXState : 1;
657 uint32_t MisseduFrame : 1;
658 uint32_t TransactError : 1;
659 uint32_t Babble : 1;
660 uint32_t DataBufError : 1;
661 uint32_t Halted : 1;
662 uint32_t Active : 1;
663 uint32_t PID : 2;
664 uint32_t ErrorCount : 2;
665 uint32_t CurrentPage : 3;
666 uint32_t IOC : 1;
667 uint32_t Length : 15;
668 uint32_t DataToggle : 1;
669} EHCI_QTD_TOKEN;
670AssertCompileSize(EHCI_QTD_TOKEN, 4);
671
672#define EHCI_QTD_HAS_ERROR(pQtdToken) (*((uint32_t *)pQtdToken) & 0x7F)
673
674typedef struct
675{
676 uint32_t Offset : 12;
677 uint32_t Reserved : 20;
678 uint32_t Ignore[4];
679} EHCI_QTD_OFFSET;
680AssertCompileSize(EHCI_QTD_OFFSET, 20);
681
682typedef struct
683{
684 EHCI_QTD_NEXTPTR Next;
685 EHCI_QTD_ALTNEXTPTR AltNext;
686 union
687 {
688 EHCI_QTD_TOKEN Bits;
689 uint32_t u32;
690 } Token;
691 union
692 {
693 EHCI_QTD_OFFSET Offset;
694 EHCI_BUFFER_PTR Buffer[5];
695 } Buffer;
696} EHCI_QTD, *PEHCI_QTD;
697typedef const EHCI_QTD *PEHCI_CQTD;
698AssertCompileSize(EHCI_QTD, 0x20);
699/** @} */
700
701
702/** @name Queue Head Descriptor (QHD)
703 * @{ */
704
705#define EHCI_QHD_EPT_SPEED_FULL 0 /**< 12 Mbps */
706#define EHCI_QHD_EPT_SPEED_LOW 1 /**< 1.5 Mbps */
707#define EHCI_QHD_EPT_SPEED_HIGH 2 /**< 480 Mbps */
708#define EHCI_QHD_EPT_SPEED_RESERVED 3
709
710typedef struct
711{
712 uint32_t DeviceAddress : 7;
713 uint32_t InActiveNext : 1;
714 uint32_t EndPt : 4;
715 uint32_t EndPtSpeed : 2;
716 uint32_t DataToggle : 1;
717 uint32_t HeadReclamation : 1;
718 uint32_t MaxLength : 11;
719 uint32_t ControlEPFlag : 1;
720 uint32_t NakCountReload : 4;
721} EHCI_QHD_EPCHARS;
722AssertCompileSize(EHCI_QHD_EPCHARS, 4);
723
724typedef struct
725{
726 uint32_t SMask : 8;
727 uint32_t CMask : 8;
728 uint32_t HubAddress : 7;
729 uint32_t Port : 7;
730 uint32_t Mult : 2;
731} EHCI_QHD_EPCAPS;
732AssertCompileSize(EHCI_QHD_EPCAPS, 4);
733
734typedef struct
735{
736 uint32_t Reserved : 5;
737 uint32_t Pointer : 27;
738} EHCI_QHD_CURRPTR;
739AssertCompileSize(EHCI_QHD_CURRPTR, 4);
740
741typedef struct
742{
743 uint32_t Terminate : 1;
744 uint32_t NakCnt : 4;
745 uint32_t Pointer : 27;
746} EHCI_QHD_ALTNEXT;
747AssertCompileSize(EHCI_QHD_ALTNEXT, 4);
748
749typedef struct
750{
751 uint32_t CProgMask : 8;
752 uint32_t Reserved : 4;
753 uint32_t Pointer : 20; /**< 4k aligned */
754} EHCI_QHD_BUFFER1;
755AssertCompileSize(EHCI_QHD_BUFFER1, 4);
756
757typedef struct
758{
759 uint32_t FrameTag : 5;
760 uint32_t SBytes : 7;
761 uint32_t Pointer : 20; /**< 4k aligned */
762} EHCI_QHD_BUFFER2;
763AssertCompileSize(EHCI_QHD_BUFFER2, 4);
764
765typedef struct
766{
767 EHCI_TD_PTR Next;
768 EHCI_QHD_EPCHARS Characteristics;
769 EHCI_QHD_EPCAPS Caps;
770 EHCI_QHD_CURRPTR CurrQTD;
771 union
772 {
773 EHCI_QTD OrgQTD;
774 struct
775 {
776 uint32_t Identical1[2];
777 EHCI_QHD_ALTNEXT AltNextQTD;
778 uint32_t Identical2;
779 EHCI_QHD_BUFFER1 Buffer1;
780 EHCI_QHD_BUFFER2 Buffer2;
781 uint32_t Identical3[2];
782 } Status;
783 } Overlay;
784} EHCI_QHD, *PEHCI_QHD;
785typedef const EHCI_QHD *PEHCI_CQHD;
786AssertCompileSize(EHCI_QHD, 0x30);
787/** @} */
788
789/** @name Periodic Frame Span Traversal Node (FSTN)
790 * @{ */
791
792typedef struct
793{
794 uint32_t Terminate : 1;
795 uint32_t Type : 2;
796 uint32_t Reserved : 2;
797 uint32_t Ptr : 27;
798} EHCI_FSTN_PTR;
799AssertCompileSize(EHCI_FSTN_PTR, 4);
800
801typedef struct
802{
803 EHCI_FSTN_PTR NormalPtr;
804 EHCI_FSTN_PTR BackPtr;
805} EHCI_FSTN, *PEHCI_FSTN;
806typedef const EHCI_FSTN *PEHCI_CFSTN;
807AssertCompileSize(EHCI_FSTN, 8);
808
809/** @} */
810
811
812/**
813 * EHCI register operator.
814 */
815typedef struct EHCIOPREG
816{
817 const char *pszName;
818 VBOXSTRICTRC (*pfnRead )(PPDMDEVINS pDevIns, PEHCI ehci, uint32_t iReg, uint32_t *pu32Value);
819 VBOXSTRICTRC (*pfnWrite)(PPDMDEVINS pDevIns, PEHCI ehci, uint32_t iReg, uint32_t u32Value);
820} EHCIOPREG;
821
822
823/* EHCI Local stuff */
824#define EHCI_HCS_PARAMS_PORT_ROUTING_RULES RT_BIT(7)
825#define EHCI_HCS_PARAMS_PORT_POWER_CONTROL RT_BIT(4)
826#define EHCI_HCS_PARAMS_NDP_MASK (RT_BIT(0) | RT_BIT(1) | RT_BIT(2) | RT_BIT(3))
827
828/* controller may cache an isochronous data structure for an entire frame */
829#define EHCI_HCC_PARAMS_ISOCHRONOUS_CACHING RT_BIT(7)
830#define EHCI_HCC_PARAMS_ASYNC_SCHEDULE_PARKING RT_BIT(2)
831#define EHCI_HCC_PARAMS_PROGRAMMABLE_FRAME_LIST RT_BIT(1)
832#define EHCI_HCC_PARAMS_64BITS_ADDRESSING RT_BIT(0)
833
834/** @name Interrupt Enable Register bits (USBINTR)
835 * @{ */
836#define EHCI_INTR_ENABLE_THRESHOLD RT_BIT(0)
837#define EHCI_INTR_ENABLE_ERROR RT_BIT(1)
838#define EHCI_INTR_ENABLE_PORT_CHANGE RT_BIT(2)
839#define EHCI_INTR_ENABLE_FRAME_LIST_ROLLOVER RT_BIT(3)
840#define EHCI_INTR_ENABLE_HOST_SYSTEM_ERROR RT_BIT(4)
841#define EHCI_INTR_ENABLE_ASYNC_ADVANCE RT_BIT(5)
842#define EHCI_INTR_ENABLE_MASK (EHCI_INTR_ENABLE_ASYNC_ADVANCE|EHCI_INTR_ENABLE_HOST_SYSTEM_ERROR|EHCI_INTR_ENABLE_FRAME_LIST_ROLLOVER|EHCI_INTR_ENABLE_PORT_CHANGE|EHCI_INTR_ENABLE_ERROR|EHCI_INTR_ENABLE_THRESHOLD)
843/** @} */
844
845/** @name Configure Flag Register (CONFIGFLAG)
846 * @{ */
847#define EHCI_CONFIGFLAG_ROUTING RT_BIT(0)
848#define EHCI_CONFIGFLAG_MASK EHCI_CONFIGFLAG_ROUTING
849/** @} */
850
851/** @name Status Register (USBSTS)
852 * @{ */
853#define EHCI_STATUS_ASYNC_SCHED RT_BIT(15) /* RO */
854#define EHCI_STATUS_PERIOD_SCHED RT_BIT(14) /* RO */
855#define EHCI_STATUS_RECLAMATION RT_BIT(13) /* RO */
856#define EHCI_STATUS_HCHALTED RT_BIT(12) /* RO */
857#define EHCI_STATUS_INT_ON_ASYNC_ADV RT_BIT(5)
858#define EHCI_STATUS_HOST_SYSTEM_ERROR RT_BIT(4)
859#define EHCI_STATUS_FRAME_LIST_ROLLOVER RT_BIT(3)
860#define EHCI_STATUS_PORT_CHANGE_DETECT RT_BIT(2)
861#define EHCI_STATUS_ERROR_INT RT_BIT(1)
862#define EHCI_STATUS_THRESHOLD_INT RT_BIT(0)
863#define EHCI_STATUS_INTERRUPT_MASK (EHCI_STATUS_THRESHOLD_INT|EHCI_STATUS_ERROR_INT|EHCI_STATUS_PORT_CHANGE_DETECT|EHCI_STATUS_FRAME_LIST_ROLLOVER|EHCI_STATUS_HOST_SYSTEM_ERROR|EHCI_STATUS_INT_ON_ASYNC_ADV)
864/** @} */
865
866#define EHCI_PERIODIC_LIST_MASK UINT32_C(0xFFFFF000) /**< 4kb aligned */
867#define EHCI_ASYNC_LIST_MASK UINT32_C(0xFFFFFFE0) /**< 32-byte aligned */
868
869
870/** @name Port Status and Control Register bits (PORTSC)
871 * @{ */
872#define EHCI_PORT_CURRENT_CONNECT RT_BIT(0) /**< RO */
873#define EHCI_PORT_CONNECT_CHANGE RT_BIT(1)
874#define EHCI_PORT_PORT_ENABLED RT_BIT(2)
875#define EHCI_PORT_PORT_CHANGE RT_BIT(3)
876#define EHCI_PORT_OVER_CURRENT_ACTIVE RT_BIT(4) /**< RO */
877#define EHCI_PORT_OVER_CURRENT_CHANGE RT_BIT(5)
878#define EHCI_PORT_FORCE_PORT_RESUME RT_BIT(6)
879#define EHCI_PORT_SUSPEND RT_BIT(7)
880#define EHCI_PORT_RESET RT_BIT(8)
881#define EHCI_PORT_LINE_STATUS_MASK (RT_BIT(10) | RT_BIT(11)) /**< RO */
882#define EHCI_PORT_LINE_STATUS_SHIFT 10
883#define EHCI_PORT_POWER RT_BIT(12)
884#define EHCI_PORT_OWNER RT_BIT(13)
885#define EHCI_PORT_INDICATOR (RT_BIT(14) | RT_BIT(15))
886#define EHCI_PORT_TEST_CONTROL_MASK (RT_BIT(16) | RT_BIT(17) | RT_BIT(18) | RT_BIT(19))
887#define EHCI_PORT_TEST_CONTROL_SHIFT 16
888#define EHCI_PORT_WAKE_ON_CONNECT_ENABLE RT_BIT(20)
889#define EHCI_PORT_WAKE_ON_DISCONNECT_ENABLE RT_BIT(21)
890#define EHCI_PORT_WAKE_OVER_CURRENT_ENABLE RT_BIT(22)
891#define EHCI_PORT_RESERVED (RT_BIT(9)|RT_BIT(23)|RT_BIT(24)|RT_BIT(25)|RT_BIT(26)|RT_BIT(27)|RT_BIT(28)|RT_BIT(29)|RT_BIT(30)|RT_BIT(31))
892
893#define EHCI_PORT_WAKE_MASK (EHCI_PORT_WAKE_ON_CONNECT_ENABLE|EHCI_PORT_WAKE_ON_DISCONNECT_ENABLE|EHCI_PORT_WAKE_OVER_CURRENT_ENABLE)
894#define EHCI_PORT_CHANGE_MASK (EHCI_PORT_CONNECT_CHANGE|EHCI_PORT_PORT_CHANGE|EHCI_PORT_OVER_CURRENT_CHANGE)
895/** @} */
896
897/** @name Command Register bits (USBCMD)
898 * @{ */
899#define EHCI_CMD_RUN RT_BIT(0)
900#define EHCI_CMD_RESET RT_BIT(1)
901#define EHCI_CMD_FRAME_LIST_SIZE_MASK (RT_BIT(2) | RT_BIT(3))
902#define EHCI_CMD_FRAME_LIST_SIZE_SHIFT 2
903#define EHCI_CMD_PERIODIC_SCHED_ENABLE RT_BIT(4)
904#define EHCI_CMD_ASYNC_SCHED_ENABLE RT_BIT(5)
905#define EHCI_CMD_INT_ON_ADVANCE_DOORBELL RT_BIT(6)
906#define EHCI_CMD_SOFT_RESET RT_BIT(7) /**< optional */
907#define EHCI_CMD_ASYNC_SCHED_PARK_MODE_COUNT_MASK (RT_BIT(8) | RT_BIT(9)) /**< optional */
908#define EHCI_CMD_ASYNC_SCHED_PARK_MODE_COUNT_SHIFT 8
909#define EHCI_CMD_RESERVED RT_BIT(10)
910#define EHCI_CMD_ASYNC_SCHED_PARK_ENABLE RT_BIT(11) /**< optional */
911#define EHCI_CMD_RESERVED2 (RT_BIT(12) | RT_BIT(13) | RT_BIT(14) | RT_BIT(15))
912#define EHCI_CMD_INTERRUPT_THRESHOLD_MASK (RT_BIT(16) | RT_BIT(17) | RT_BIT(18) | RT_BIT(19) | RT_BIT(20) | RT_BIT(21) | RT_BIT(22) | RT_BIT(23))
913#define EHCI_CMD_INTERRUPT_THRESHOLD_SHIFT 16
914#define EHCI_CMD_MASK (EHCI_CMD_INTERRUPT_THRESHOLD_MASK|EHCI_CMD_ASYNC_SCHED_PARK_ENABLE|EHCI_CMD_ASYNC_SCHED_PARK_MODE_COUNT_MASK|EHCI_CMD_SOFT_RESET|EHCI_CMD_INT_ON_ADVANCE_DOORBELL|EHCI_CMD_ASYNC_SCHED_ENABLE|EHCI_CMD_PERIODIC_SCHED_ENABLE|EHCI_CMD_FRAME_LIST_SIZE_MASK|EHCI_CMD_RESET|EHCI_CMD_RUN)
915
916#define EHCI_DEFAULT_PERIODIC_LIST_SIZE 1024
917#define EHCI_DEFAULT_PERIODIC_LIST_MASK 0x3ff
918
919#define EHCI_FRINDEX_UFRAME_COUNT_MASK 0x7
920#define EHCI_FRINDEX_FRAME_INDEX_MASK EHCI_DEFAULT_PERIODIC_LIST_MASK
921#define EHCI_FRINDEX_FRAME_INDEX_SHIFT 3
922
923/** @} */
924
925/* Local EHCI definitions */
926#define EHCI_USB_RESET 0x00
927#define EHCI_USB_RESUME 0x40
928#define EHCI_USB_OPERATIONAL 0x80
929#define EHCI_USB_SUSPEND 0xc0
930
931#define EHCI_HARDWARE_TIMER_FREQ 8000 /**< 8000 hz = every 125 usec */
932#define EHCI_DEFAULT_TIMER_FREQ 1000
933#define EHCI_UFRAMES_PER_FRAME 8
934
935#ifndef VBOX_DEVICE_STRUCT_TESTCASE
936
937
938/*********************************************************************************************************************************
939* Global Variables *
940*********************************************************************************************************************************/
941#if defined(VBOX_IN_EXTPACK_R0) && defined(RT_OS_SOLARIS)
942/* Dependency information for the native solaris loader. */
943extern "C" { char _depends_on[] = "vboxdrv VMMR0.r0"; }
944#endif
945
946#if defined(LOG_ENABLED) && defined(IN_RING3)
947static bool g_fLogControlEPs = false;
948static bool g_fLogInterruptEPs = false;
949#endif
950
951#ifdef IN_RING3
952/**
953 * SSM descriptor table for the EHCI structure.
954 */
955static SSMFIELD const g_aEhciFields[] =
956{
957 SSMFIELD_ENTRY( EHCI, fAsyncTraversalTimerActive),
958 SSMFIELD_ENTRY( EHCI, SofTime),
959 SSMFIELD_ENTRY( EHCI, RootHub.unused),
960 SSMFIELD_ENTRY( EHCI, RootHub.unused),
961 SSMFIELD_ENTRY( EHCI, RootHub.unused),
962 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[0].fReg),
963 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[1].fReg),
964 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[2].fReg),
965 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[3].fReg),
966 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[4].fReg),
967 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[5].fReg),
968 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[6].fReg),
969 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[7].fReg),
970 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[8].fReg),
971 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[9].fReg),
972 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[10].fReg),
973 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[11].fReg),
974 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[12].fReg),
975 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[13].fReg),
976 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[14].fReg),
977 SSMFIELD_ENTRY( EHCI, cap_length),
978 SSMFIELD_ENTRY( EHCI, hci_version),
979 SSMFIELD_ENTRY( EHCI, hcs_params),
980 SSMFIELD_ENTRY( EHCI, hcc_params),
981 SSMFIELD_ENTRY( EHCI, cmd),
982 SSMFIELD_ENTRY( EHCI, intr_status),
983 SSMFIELD_ENTRY( EHCI, intr),
984 SSMFIELD_ENTRY( EHCI, frame_idx),
985 SSMFIELD_ENTRY( EHCI, ds_segment),
986 SSMFIELD_ENTRY( EHCI, periodic_list_base),
987 SSMFIELD_ENTRY( EHCI, async_list_base),
988 SSMFIELD_ENTRY( EHCI, config),
989 SSMFIELD_ENTRY( EHCI, uIrqInterval),
990 SSMFIELD_ENTRY( EHCI, HcFmNumber),
991 SSMFIELD_ENTRY( EHCI, uFramesPerTimerCall),
992 SSMFIELD_ENTRY( EHCI, fBusStarted),
993 SSMFIELD_ENTRY_TERM()
994};
995#endif
996
997
998/*********************************************************************************************************************************
999* Internal Functions *
1000*********************************************************************************************************************************/
1001RT_C_DECLS_BEGIN
1002#ifdef IN_RING3
1003/* Update host controller state to reflect a device attach */
1004static void ehciR3PortPower(PEHCI pThis, PEHCICC pThisCC, unsigned iPort, bool fPowerUp);
1005
1006static void ehciR3QHUpdateOverlay(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC,
1007 PEHCI_QHD pQhd, RTGCPHYS GCPhysQHD, PEHCI_QTD pQtd);
1008static void ehciR3CalcTimerIntervals(PEHCI pThis, PEHCICC pThisCC, uint32_t u32FrameRate);
1009#endif /* IN_RING3 */
1010RT_C_DECLS_END
1011
1012/**
1013 * Update PCI IRQ levels
1014 */
1015static void ehciUpdateInterruptLocked(PPDMDEVINS pDevIns, PEHCI pThis, const char *msg)
1016{
1017 int level = 0;
1018
1019 if (pThis->intr_status & pThis->intr)
1020 level = 1;
1021
1022 PDMDevHlpPCISetIrq(pDevIns, 0, level);
1023 if (level)
1024 {
1025 uint32_t val = pThis->intr_status & pThis->intr;
1026
1027 Log2Func(("Fired off interrupt %#010x - INT=%d ERR=%d PCD=%d FLR=%d HSE=%d IAA=%d - %s\n",
1028 val,
1029 !!(val & EHCI_STATUS_THRESHOLD_INT),
1030 !!(val & EHCI_STATUS_ERROR_INT),
1031 !!(val & EHCI_STATUS_PORT_CHANGE_DETECT),
1032 !!(val & EHCI_STATUS_FRAME_LIST_ROLLOVER),
1033 !!(val & EHCI_STATUS_HOST_SYSTEM_ERROR),
1034 !!(val & EHCI_STATUS_INT_ON_ASYNC_ADV),
1035 msg));
1036 RT_NOREF(val, msg);
1037
1038 /* host controller must clear the EHCI_CMD_INT_ON_ADVANCE_DOORBELL bit after setting it in the status register */
1039 if (pThis->intr_status & EHCI_STATUS_INT_ON_ASYNC_ADV)
1040 ASMAtomicAndU32(&pThis->cmd, ~EHCI_CMD_INT_ON_ADVANCE_DOORBELL);
1041
1042 }
1043 else
1044 Log2Func(("cleared interrupt\n"));
1045}
1046
1047/**
1048 * Set an interrupt, use the wrapper ehciSetInterrupt.
1049 */
1050DECLINLINE(int) ehciSetInterruptInt(PPDMDEVINS pDevIns, PEHCI pThis, int rcBusy, uint32_t intr, const char *msg)
1051{
1052 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, rcBusy);
1053 if (rc != VINF_SUCCESS)
1054 return rc;
1055
1056 if ( (pThis->intr_status & intr) != intr )
1057 {
1058 ASMAtomicOrU32(&pThis->intr_status, intr);
1059 ehciUpdateInterruptLocked(pDevIns, pThis, msg);
1060 }
1061
1062 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
1063 return rc;
1064}
1065
1066/**
1067 * Set an interrupt wrapper macro for logging purposes.
1068 */
1069#define ehciSetInterrupt(a_pDevIns, a_pEhci, a_rcBusy, a_fIntr) \
1070 ehciSetInterruptInt(a_pDevIns, a_pEhci, a_rcBusy, a_fIntr, #a_fIntr)
1071#define ehciR3SetInterrupt(a_pDevIns, a_pEhci, a_fIntr) \
1072 ehciSetInterruptInt(a_pDevIns, a_pEhci, VERR_IGNORED, a_fIntr, #a_fIntr)
1073
1074#ifdef IN_RING3
1075
1076/**
1077 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1078 */
1079static DECLCALLBACK(void *) ehciR3RhQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1080{
1081 PEHCICC pThisCC = RT_FROM_MEMBER(pInterface, EHCICC, RootHub.IBase);
1082 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->RootHub.IBase);
1083 PDMIBASE_RETURN_INTERFACE(pszIID, VUSBIROOTHUBPORT, &pThisCC->RootHub.IRhPort);
1084 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->RootHub.ILeds);
1085 return NULL;
1086}
1087
1088/**
1089 * Gets the pointer to the status LED of a unit.
1090 *
1091 * @returns VBox status code.
1092 * @param pInterface Pointer to the interface structure containing the called function pointer.
1093 * @param iLUN The unit which status LED we desire.
1094 * @param ppLed Where to store the LED pointer.
1095 */
1096static DECLCALLBACK(int) ehciR3RhQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
1097{
1098 PEHCICC pThisCC = RT_FROM_MEMBER(pInterface, EHCICC, RootHub.ILeds);
1099 if (iLUN == 0)
1100 {
1101 *ppLed = &pThisCC->RootHub.Led;
1102 return VINF_SUCCESS;
1103 }
1104 return VERR_PDM_LUN_NOT_FOUND;
1105}
1106
1107
1108/**
1109 * Get the number of avialable ports in the hub.
1110 *
1111 * @returns The number of ports available.
1112 * @param pInterface Pointer to this structure.
1113 * @param pAvailable Bitmap indicating the available ports. Set bit == available port.
1114 */
1115static DECLCALLBACK(unsigned) ehciR3RhGetAvailablePorts(PVUSBIROOTHUBPORT pInterface, PVUSBPORTBITMAP pAvailable)
1116{
1117 PEHCICC pThisCC = RT_FROM_MEMBER(pInterface, EHCICC, RootHub.IRhPort);
1118 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1119 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
1120
1121 memset(pAvailable, 0, sizeof(*pAvailable));
1122
1123 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
1124 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
1125
1126 unsigned cPorts = 0;
1127 for (unsigned iPort = 0; iPort < EHCI_NDP_CFG(pThis); iPort++)
1128 {
1129 if (!pThisCC->RootHub.aPorts[iPort].fAttached)
1130 {
1131 cPorts++;
1132 ASMBitSet(pAvailable, iPort + 1);
1133 }
1134 }
1135
1136 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
1137 return cPorts;
1138}
1139
1140
1141/**
1142 * Gets the supported USB versions.
1143 *
1144 * @returns The mask of supported USB versions.
1145 * @param pInterface Pointer to this structure.
1146 */
1147static DECLCALLBACK(uint32_t) ehciR3RhGetUSBVersions(PVUSBIROOTHUBPORT pInterface)
1148{
1149 RT_NOREF(pInterface);
1150 return VUSB_STDVER_20;
1151}
1152
1153
1154/** @interface_method_impl{VUSBIROOTHUBPORT,pfnAttach} */
1155static DECLCALLBACK(int) ehciR3RhAttach(PVUSBIROOTHUBPORT pInterface, uint32_t uPort, VUSBSPEED enmSpeed)
1156{
1157 PEHCICC pThisCC = RT_FROM_MEMBER(pInterface, EHCICC, RootHub.IRhPort);
1158 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1159 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
1160 LogFlowFunc(("uPort=%u\n", uPort));
1161 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
1162 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
1163
1164 /*
1165 * Validate and adjust input.
1166 */
1167 Assert(uPort >= 1 && uPort <= EHCI_NDP_CFG(pThis));
1168 uPort--;
1169 Assert(!pThisCC->RootHub.aPorts[uPort].fAttached);
1170 Assert(enmSpeed == VUSB_SPEED_HIGH); RT_NOREF(enmSpeed); /* Only HS devices should end up here! */
1171
1172 /*
1173 * Attach it.
1174 */
1175 ASMAtomicAndU32(&pThis->RootHub.aPorts[uPort].fReg, ~EHCI_PORT_OWNER); /* not attached to a companion controller */
1176 ASMAtomicOrU32(&pThis->RootHub.aPorts[uPort].fReg, EHCI_PORT_CURRENT_CONNECT | EHCI_PORT_CONNECT_CHANGE);
1177 pThisCC->RootHub.aPorts[uPort].fAttached = true;
1178 ehciR3PortPower(pThis, pThisCC, uPort, 1 /* power on */);
1179
1180 ehciR3SetInterrupt(pDevIns, pThis, EHCI_STATUS_PORT_CHANGE_DETECT);
1181
1182 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
1183 return VINF_SUCCESS;
1184}
1185
1186
1187/**
1188 * A device is being detached from a port in the roothub.
1189 *
1190 * @param pInterface Pointer to this structure.
1191 * @param uPort The port number assigned to the device.
1192 */
1193static DECLCALLBACK(void) ehciR3RhDetach(PVUSBIROOTHUBPORT pInterface, uint32_t uPort)
1194{
1195 PEHCICC pThisCC = RT_FROM_MEMBER(pInterface, EHCICC, RootHub.IRhPort);
1196 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1197 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
1198 LogFlowFunc(("uPort=%u\n", uPort));
1199 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
1200 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
1201
1202 /*
1203 * Validate and adjust input.
1204 */
1205 Assert(uPort >= 1 && uPort <= EHCI_NDP_CFG(pThis));
1206 uPort--;
1207 Assert(pThisCC->RootHub.aPorts[uPort].fAttached);
1208
1209 /*
1210 * Detach it.
1211 */
1212 pThisCC->RootHub.aPorts[uPort].fAttached = false;
1213 ASMAtomicAndU32(&pThis->RootHub.aPorts[uPort].fReg, ~EHCI_PORT_CURRENT_CONNECT);
1214 if (pThis->RootHub.aPorts[uPort].fReg & EHCI_PORT_PORT_ENABLED)
1215 {
1216 ASMAtomicAndU32(&pThis->RootHub.aPorts[uPort].fReg, ~EHCI_PORT_PORT_ENABLED);
1217 ASMAtomicOrU32(&pThis->RootHub.aPorts[uPort].fReg, EHCI_PORT_CONNECT_CHANGE | EHCI_PORT_PORT_CHANGE);
1218 }
1219 else
1220 ASMAtomicOrU32(&pThis->RootHub.aPorts[uPort].fReg, EHCI_PORT_CONNECT_CHANGE);
1221
1222 ehciR3SetInterrupt(pDevIns, pThis, EHCI_STATUS_PORT_CHANGE_DETECT);
1223
1224 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
1225}
1226
1227
1228/**
1229 * One of the roothub devices has completed its reset operation.
1230 *
1231 * Currently, we don't think anything is required to be done here
1232 * so it's just a stub for forcing async resetting of the devices
1233 * during a root hub reset.
1234 *
1235 * @param pDev The root hub device.
1236 * @param uPort The port number of the device on the roothub being resetted.
1237 * @param rc The result of the operation.
1238 * @param pvUser Pointer to the controller.
1239 */
1240static DECLCALLBACK(void) ehciR3RhResetDoneOneDev(PVUSBIDEVICE pDev, uint32_t uPort, int rc, void *pvUser)
1241{
1242 LogRel(("EHCI: root hub reset completed with %Rrc\n", rc));
1243 RT_NOREF(pDev, uPort, rc, pvUser);
1244}
1245
1246
1247/**
1248 * Does a software or hardware reset of the controller.
1249 *
1250 * This is called in response to setting HcCommandStatus.HCR, hardware reset,
1251 * and device construction.
1252 *
1253 * @param pDevIns The device instance.
1254 * @param pThis The shared EHCI instance data.
1255 * @param pThisCC The ring-3 EHCI instance data.
1256 * @param fNewMode The new mode of operation. This is UsbSuspend if it's a
1257 * software reset, and UsbReset if it's a hardware reset / cold boot.
1258 * @param fResetOnLinux Set if we can do a real reset of the devices attached to the root hub.
1259 * This is really a just a hack for the non-working linux device reset.
1260 * Linux has this feature called 'logical disconnect' if device reset fails
1261 * which prevents us from doing resets when the guest asks for it - the guest
1262 * will get confused when the device seems to be reconnected everytime it tries
1263 * to reset it. But if we're at hardware reset time, we can allow a device to
1264 * be 'reconnected' without upsetting the guest.
1265 *
1266 * @remark This hasn't got anything to do with software setting the mode to UsbReset.
1267 */
1268static void ehciR3DoReset(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC, uint32_t fNewMode, bool fResetOnLinux)
1269{
1270 LogFunc(("%s reset%s\n", fNewMode == EHCI_USB_RESET ? "hardware" : "software",
1271 fResetOnLinux ? " (reset on linux)" : ""));
1272
1273 /*
1274 * Cancel all outstanding URBs.
1275 *
1276 * We can't, and won't, deal with URBs until we're moved out of the
1277 * suspend/reset state. Also, a real HC isn't going to send anything
1278 * any more when a reset has been signaled.
1279 *
1280 * This must be done on the framer thread to avoid race conditions.
1281 */
1282 pThisCC->RootHub.pIRhConn->pfnCancelAllUrbs(pThisCC->RootHub.pIRhConn);
1283
1284 /*
1285 * Reset the hardware registers.
1286 */
1287 /** @todo other differences between hardware reset and VM reset? */
1288
1289 if (pThis->hcc_params & EHCI_HCC_PARAMS_ASYNC_SCHEDULE_PARKING)
1290 pThis->cmd = 0x80000 | EHCI_CMD_ASYNC_SCHED_PARK_ENABLE | (3 << EHCI_CMD_ASYNC_SCHED_PARK_MODE_COUNT_SHIFT);
1291 else
1292 pThis->cmd = 0x80000;
1293
1294 pThis->intr_status = EHCI_STATUS_HCHALTED;
1295 pThis->intr = 0;
1296 pThis->frame_idx = 0;
1297 pThis->ds_segment = 0;
1298 pThis->periodic_list_base = 0; /* undefined */
1299 pThis->async_list_base = 0; /* undefined */
1300 pThis->config = 0;
1301 pThis->uIrqInterval = (pThis->intr_status & EHCI_CMD_INTERRUPT_THRESHOLD_MASK) >> EHCI_CMD_INTERRUPT_THRESHOLD_SHIFT;
1302
1303 /* We have to update interrupts as the IRQ may need to be cleared. */
1304 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VERR_IGNORED);
1305 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CsIrq, rcLock);
1306
1307 ehciUpdateInterruptLocked(pDevIns, pThis, "ehciR3DoReset");
1308
1309 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
1310
1311 ehciR3CalcTimerIntervals(pThis, pThisCC, pThisCC->uFrameRateDefault);
1312
1313 if (fNewMode == EHCI_USB_RESET)
1314 {
1315 /* Only a hardware reset reinits the port registers */
1316 for (unsigned i = 0; i < EHCI_NDP_CFG(pThis); i++)
1317 {
1318 if (pThis->hcs_params & EHCI_HCS_PARAMS_PORT_POWER_CONTROL)
1319 pThis->RootHub.aPorts[i].fReg = EHCI_PORT_OWNER;
1320 else
1321 pThis->RootHub.aPorts[i].fReg = EHCI_PORT_POWER | EHCI_PORT_OWNER;
1322 }
1323 }
1324/** @todo Shouldn't we stop the SOF timer at this point? */
1325
1326 /*
1327 * If this is a hardware reset, we will initialize the root hub too.
1328 * Software resets doesn't do this according to the specs.
1329 * (It's not possible to have device connected at the time of the
1330 * device construction, so nothing to worry about there.)
1331 */
1332 if (fNewMode == EHCI_USB_RESET)
1333 {
1334 pThisCC->RootHub.pIRhConn->pfnReset(pThisCC->RootHub.pIRhConn, fResetOnLinux);
1335
1336 /*
1337 * Reattach the devices.
1338 */
1339 for (unsigned i = 0; i < EHCI_NDP_CFG(pThis); i++)
1340 {
1341 bool fAttached = pThisCC->RootHub.aPorts[i].fAttached;
1342 pThisCC->RootHub.aPorts[i].fAttached = false;
1343
1344 if (fAttached)
1345 ehciR3RhAttach(&pThisCC->RootHub.IRhPort, i+1, VUSB_SPEED_HIGH);
1346 }
1347 }
1348}
1349
1350/**
1351 * Reset the root hub.
1352 *
1353 * @returns VBox status code.
1354 * @param pInterface Pointer to this structure.
1355 * @param fResetOnLinux This is used to indicate whether we're at VM reset time and
1356 * can do real resets or if we're at any other time where that
1357 * isn't such a good idea.
1358 * @remark Do NOT call VUSBIDevReset on the root hub in an async fashion!
1359 * @thread EMT
1360 */
1361static DECLCALLBACK(int) ehciR3RhReset(PVUSBIROOTHUBPORT pInterface, bool fResetOnLinux)
1362{
1363 PEHCICC pThisCC = RT_FROM_MEMBER(pInterface, EHCICC, RootHub.IRhPort);
1364 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1365 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
1366 LogFunc(("fResetOnLinux=%d\n", fResetOnLinux));
1367
1368 /* Soft reset first */
1369 ehciR3DoReset(pDevIns, pThis, pThisCC, EHCI_USB_SUSPEND, false /* N/A */);
1370
1371 /*
1372 * We're pretending to _reattach_ the devices without resetting them.
1373 * Except, during VM reset where we use the opportunity to do a proper
1374 * reset before the guest comes along and expects things.
1375 *
1376 * However, it's very very likely that we're not doing the right thing
1377 * here when end up here on request from the guest (USB Reset state).
1378 * The docs talks about root hub resetting, however what exact behaviour
1379 * in terms of root hub status and changed bits, and HC interrupts aren't
1380 * stated clearly. IF we get trouble and see the guest doing "USB Resets"
1381 * we will have to look into this. For the time being we stick with simple.
1382 */
1383 for (unsigned iPort = 0; iPort < EHCI_NDP_CFG(pThis); iPort++)
1384 {
1385 if (pThisCC->RootHub.aPorts[iPort].fAttached)
1386 {
1387 ASMAtomicOrU32(&pThis->RootHub.aPorts[iPort].fReg, EHCI_PORT_CURRENT_CONNECT | EHCI_PORT_CONNECT_CHANGE);
1388 if (fResetOnLinux)
1389 {
1390 PVM pVM = PDMDevHlpGetVM(pDevIns);
1391 VUSBIRhDevReset(pThisCC->RootHub.pIRhConn, EHCI_PORT_2_VUSB_PORT(iPort), fResetOnLinux,
1392 ehciR3RhResetDoneOneDev, pThis, pVM);
1393 }
1394 }
1395 }
1396
1397 return VINF_SUCCESS;
1398}
1399
1400
1401/**
1402 * Reads physical memory.
1403 */
1404DECLINLINE(void) ehciPhysRead(PPDMDEVINS pDevIns, RTGCPHYS Addr, void *pvBuf, size_t cbBuf)
1405{
1406 PDMDevHlpPCIPhysReadUser(pDevIns, Addr, pvBuf, cbBuf);
1407}
1408
1409
1410/**
1411 * Reads physical memory - metadata.
1412 */
1413DECLINLINE(void) ehciPhysReadMeta(PPDMDEVINS pDevIns, RTGCPHYS Addr, void *pvBuf, size_t cbBuf)
1414{
1415 PDMDevHlpPCIPhysReadMeta(pDevIns, Addr, pvBuf, cbBuf);
1416}
1417
1418
1419/**
1420 * Writes physical memory.
1421 */
1422DECLINLINE(void) ehciPhysWrite(PPDMDEVINS pDevIns, RTGCPHYS Addr, const void *pvBuf, size_t cbBuf)
1423{
1424 PDMDevHlpPCIPhysWriteUser(pDevIns, Addr, pvBuf, cbBuf);
1425}
1426
1427
1428/**
1429 * Writes physical memory.
1430 */
1431DECLINLINE(void) ehciPhysWriteMeta(PPDMDEVINS pDevIns, RTGCPHYS Addr, const void *pvBuf, size_t cbBuf)
1432{
1433 PDMDevHlpPCIPhysWriteMeta(pDevIns, Addr, pvBuf, cbBuf);
1434}
1435
1436
1437/**
1438 * Read an array of dwords from physical memory and correct endianness.
1439 */
1440DECLINLINE(void) ehciGetDWords(PPDMDEVINS pDevIns, RTGCPHYS Addr, uint32_t *pau32s, int c32s)
1441{
1442 ehciPhysReadMeta(pDevIns, Addr, pau32s, c32s * sizeof(uint32_t));
1443# ifndef RT_LITTLE_ENDIAN
1444 for(int i = 0; i < c32s; i++)
1445 pau32s[i] = RT_H2LE_U32(pau32s[i]);
1446# endif
1447}
1448
1449
1450/**
1451 * Write an array of dwords from physical memory and correct endianness.
1452 */
1453DECLINLINE(void) ehciPutDWords(PPDMDEVINS pDevIns, RTGCPHYS Addr, const uint32_t *pau32s, int cu32s)
1454{
1455# ifdef RT_LITTLE_ENDIAN
1456 ehciPhysWriteMeta(pDevIns, Addr, pau32s, cu32s << 2);
1457# else
1458 for (int i = 0; i < c32s; i++, pau32s++, Addr += sizeof(*pau32s))
1459 {
1460 uint32_t u32Tmp = RT_H2LE_U32(*pau32s);
1461 ehciPhysWriteMeta(pDevIns, Addr, (uint8_t *)&u32Tmp, sizeof(u32Tmp));
1462 }
1463# endif
1464}
1465
1466
1467DECLINLINE(void) ehciR3ReadFrameListPtr(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, EHCI_FRAME_LIST_PTR *pFrameList)
1468{
1469 ehciGetDWords(pDevIns, GCPhys, (uint32_t *)pFrameList, sizeof(*pFrameList) >> 2);
1470}
1471
1472DECLINLINE(void) ehciR3ReadTDPtr(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, EHCI_TD_PTR *pTD)
1473{
1474 ehciGetDWords(pDevIns, GCPhys, (uint32_t *)pTD, sizeof(*pTD) >> 2);
1475}
1476
1477DECLINLINE(void) ehciR3ReadItd(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PEHCI_ITD_PAD pPItd)
1478{
1479 ehciGetDWords(pDevIns, GCPhys, (uint32_t *)pPItd, sizeof(EHCI_ITD) >> 2);
1480 pPItd->Buffer.Buffer[7].Pointer = 0xFFFFF; /* Direct ill-defined accesses to the last page under 4GB (ROM). */
1481 pPItd->Buffer.Buffer[8].Pointer = 0xFFFFF;
1482}
1483
1484DECLINLINE(void) ehciR3ReadSitd(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PEHCI_SITD pSitd)
1485{
1486 ehciGetDWords(pDevIns, GCPhys, (uint32_t *)pSitd, sizeof(*pSitd) >> 2);
1487}
1488
1489DECLINLINE(void) ehciR3WriteItd(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PEHCI_ITD_PAD pItd)
1490{
1491 /** @todo might need to be careful about write order in async io thread */
1492 /*
1493 * Only write to the fields the controller is allowed to write to,
1494 * namely the eight double words coming after the next link pointer.
1495 */
1496 uint32_t offWrite = RT_OFFSETOF(EHCI_ITD, Transaction[0]);
1497 uint32_t offDWordsWrite = offWrite / sizeof(uint32_t);
1498 Assert(!(offWrite % sizeof(uint32_t)));
1499
1500 ehciPutDWords(pDevIns, GCPhys + offWrite, (uint32_t *)pItd + offDWordsWrite, (sizeof(EHCI_ITD) >> 2) - offDWordsWrite);
1501}
1502
1503DECLINLINE(void) ehciR3ReadQHD(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PEHCI_QHD pQHD)
1504{
1505 ehciGetDWords(pDevIns, GCPhys, (uint32_t *)pQHD, sizeof(*pQHD) >> 2);
1506}
1507
1508DECLINLINE(void) ehciR3ReadQTD(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PEHCI_QTD pQTD)
1509{
1510 ehciGetDWords(pDevIns, GCPhys, (uint32_t *)pQTD, sizeof(*pQTD) >> 2);
1511}
1512
1513DECLINLINE(void) ehciR3WriteQTD(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PEHCI_QTD pQTD)
1514{
1515 /** @todo might need to be careful about write order in async io thread */
1516 /*
1517 * Only write to the fields the controller is allowed to write to,
1518 * namely the two double words coming after the alternate next QTD pointer.
1519 */
1520 uint32_t offWrite = RT_OFFSETOF(EHCI_QTD, Token.u32);
1521 uint32_t offDWordsWrite = offWrite / sizeof(uint32_t);
1522 Assert(!(offWrite % sizeof(uint32_t)));
1523
1524 ehciPutDWords(pDevIns, GCPhys + offWrite, (uint32_t *)pQTD + offDWordsWrite, (sizeof(*pQTD) >> 2) - offDWordsWrite);
1525}
1526
1527
1528/**
1529 * Updates the QHD in guest memory only updating portions of the QHD the controller
1530 * is allowed to write to.
1531 *
1532 * @param pDevIns The device instance.
1533 * @param GCPhys Physical guest address of the QHD.
1534 * @param pQHD The QHD to update the guest memory with.
1535 */
1536DECLINLINE(void) ehciR3UpdateQHD(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PEHCI_QHD pQHD)
1537{
1538 /*
1539 * Only update members starting from the current QTD pointer, everything
1540 * before is readonly for the controller and the guest might have updated it
1541 * behind our backs already.
1542 */
1543 uint32_t offWrite = RT_OFFSETOF(EHCI_QHD, CurrQTD);
1544 ehciPhysWriteMeta(pDevIns, GCPhys + offWrite, (uint8_t *)pQHD + offWrite, sizeof(EHCI_QHD) - offWrite);
1545}
1546
1547#ifdef LOG_ENABLED
1548
1549# if 0 /* unused */
1550/**
1551 * Dumps a TD queue. LOG_ENABLED builds only.
1552 */
1553DECLINLINE(void) ehciR3DumpTdQueue(PEHCI pThis, RTGCPHYS GCPhysHead, const char *pszMsg)
1554{
1555 RT_NOREF(pThis, GCPhysHead, pszMsg);
1556 AssertFailed();
1557}
1558# endif /* unused */
1559
1560/**
1561 * Dumps an SITD list. LOG_ENABLED builds only.
1562 */
1563DECLINLINE(void) ehciR3DumpSITD(PPDMDEVINS pDevIns, RTGCPHYS GCPhysHead, bool fList)
1564{
1565 RT_NOREF(pDevIns, GCPhysHead, fList);
1566 AssertFailed();
1567}
1568
1569/**
1570 * Dumps an FSTN list. LOG_ENABLED builds only.
1571 */
1572DECLINLINE(void) ehciR3DumpFSTN(PPDMDEVINS pDevIns, RTGCPHYS GCPhysHead, bool fList)
1573{
1574 RT_NOREF(pDevIns, GCPhysHead, fList);
1575 AssertFailed();
1576}
1577
1578#ifdef LOG_ENABLED
1579static const char *ehciPID2Str(uint32_t PID)
1580{
1581 switch(PID)
1582 {
1583 case EHCI_QTD_PID_OUT:
1584 return "OUT";
1585 case EHCI_QTD_PID_IN:
1586 return "IN";
1587 case EHCI_QTD_PID_SETUP:
1588 return "SETUP";
1589 default:
1590 return "Invalid PID!";
1591 }
1592}
1593#endif
1594
1595DECLINLINE(void) ehciR3DumpSingleQTD(RTGCPHYS GCPhys, PEHCI_QTD pQtd, const char *pszPrefix)
1596{
1597 if (pQtd->Token.Bits.Active)
1598 {
1599 Log2((" QTD%s: %RGp={", pszPrefix, GCPhys));
1600 Log2((" Length=%x IOC=%d DT=%d CErr=%d C_Page=%d Status=%x PID=%s}\n", pQtd->Token.Bits.Length, pQtd->Token.Bits.IOC, pQtd->Token.Bits.DataToggle, pQtd->Token.Bits.ErrorCount, pQtd->Token.Bits.CurrentPage, pQtd->Token.u32 & 0xff, ehciPID2Str(pQtd->Token.Bits.PID)));
1601 Log2((" QTD: %RGp={", GCPhys));
1602 Log2((" Buf0=%x Offset=%x Buf1=%x Buf2=%x Buf3=%x Buf4=%x}\n", pQtd->Buffer.Buffer[0].Pointer, pQtd->Buffer.Offset.Offset, pQtd->Buffer.Buffer[1].Pointer, pQtd->Buffer.Buffer[2].Pointer, pQtd->Buffer.Buffer[3].Pointer, pQtd->Buffer.Buffer[4].Pointer));
1603 Log2((" QTD: %RGp={", GCPhys));
1604 Log2((" Next=%RGp T=%d AltNext=%RGp AltT=%d\n", (RTGCPHYS)pQtd->Next.Pointer << EHCI_TD_PTR_SHIFT, pQtd->Next.Terminate, (RTGCPHYS)pQtd->AltNext.Pointer << EHCI_TD_PTR_SHIFT, pQtd->AltNext.Terminate));
1605 }
1606 else
1607 Log2((" QTD%s: %RGp={Not Active}\n", pszPrefix, GCPhys));
1608}
1609
1610/**
1611 * Dumps a QTD list. LOG_ENABLED builds only.
1612 */
1613DECLINLINE(void) ehciR3DumpQTD(PPDMDEVINS pDevIns, RTGCPHYS GCPhysHead, bool fList)
1614{
1615 RTGCPHYS GCPhys = GCPhysHead;
1616 unsigned iterations = 0;
1617
1618 for (;;)
1619 {
1620 EHCI_QTD qtd;
1621
1622 /* Read the whole QHD */
1623 ehciR3ReadQTD(pDevIns, GCPhys, &qtd);
1624 ehciR3DumpSingleQTD(GCPhys, &qtd, "");
1625
1626 if (!fList || qtd.Next.Terminate || !qtd.Next.Pointer || qtd.Token.Bits.Halted || !qtd.Token.Bits.Active)
1627 break;
1628
1629 /* next */
1630 if (GCPhys == ((RTGCPHYS)qtd.Next.Pointer << EHCI_TD_PTR_SHIFT))
1631 break; /* detect if list item is self-cycled. */
1632
1633 GCPhys = (RTGCPHYS)qtd.Next.Pointer << EHCI_TD_PTR_SHIFT;
1634
1635 if (GCPhys == GCPhysHead)
1636 break;
1637
1638 /* If we ran too many iterations, the list must be looping in on itself.
1639 * On a real controller loops wouldn't be fatal, as it will eventually
1640 * run out of time in the micro-frame.
1641 */
1642 if (++iterations == 128)
1643 {
1644 LogFunc(("Too many iterations, exiting!\n"));
1645 break;
1646 }
1647 }
1648
1649 /* alternative pointers */
1650 GCPhys = GCPhysHead;
1651 iterations = 0;
1652
1653 for (;;)
1654 {
1655 EHCI_QTD qtd;
1656
1657 /* Read the whole QHD */
1658 ehciR3ReadQTD(pDevIns, GCPhys, &qtd);
1659 if (GCPhys != GCPhysHead)
1660 ehciR3DumpSingleQTD(GCPhys, &qtd, "-A");
1661
1662 if (!fList || qtd.AltNext.Terminate || !qtd.AltNext.Pointer || qtd.Token.Bits.Halted || !qtd.Token.Bits.Active)
1663 break;
1664
1665 /* next */
1666 if (GCPhys == ((RTGCPHYS)qtd.AltNext.Pointer << EHCI_TD_PTR_SHIFT))
1667 break; /* detect if list item is self-cycled. */
1668
1669 GCPhys = (RTGCPHYS)qtd.AltNext.Pointer << EHCI_TD_PTR_SHIFT;
1670
1671 if (GCPhys == GCPhysHead)
1672 break;
1673
1674 /* If we ran too many iterations, the list must be looping in on itself.
1675 * On a real controller loops wouldn't be fatal, as it will eventually
1676 * run out of time in the micro-frame.
1677 */
1678 if (++iterations == 128)
1679 {
1680 LogFunc(("Too many iterations, exiting!\n"));
1681 break;
1682 }
1683 }
1684}
1685
1686/**
1687 * Dumps a QHD list. LOG_ENABLED builds only.
1688 */
1689DECLINLINE(void) ehciR3DumpQH(PPDMDEVINS pDevIns, RTGCPHYS GCPhysHead, bool fList)
1690{
1691 EHCI_QHD qhd;
1692 RTGCPHYS GCPhys = GCPhysHead;
1693 unsigned iterations = 0;
1694
1695 Log2((" QH: %RGp={", GCPhys));
1696
1697 /* Read the whole QHD */
1698 ehciR3ReadQHD(pDevIns, GCPhys, &qhd);
1699
1700 Log2(("HorzLnk=%RGp Typ=%u T=%u Addr=%x EndPt=%x Speed=%x MaxSize=%x NAK=%d C=%d RH=%d I=%d}\n",
1701 ((RTGCPHYS)qhd.Next.Pointer << EHCI_TD_PTR_SHIFT), qhd.Next.Type, qhd.Next.Terminate,
1702 qhd.Characteristics.DeviceAddress, qhd.Characteristics.EndPt, qhd.Characteristics.EndPtSpeed,
1703 qhd.Characteristics.MaxLength, qhd.Characteristics.NakCountReload, qhd.Characteristics.ControlEPFlag,
1704 qhd.Characteristics.HeadReclamation, qhd.Characteristics.InActiveNext));
1705 Log2((" Caps: Port=%x Hub=%x Multi=%x CMask=%x SMask=%x\n", qhd.Caps.Port, qhd.Caps.HubAddress,
1706 qhd.Caps.Mult, qhd.Caps.CMask, qhd.Caps.SMask));
1707 Log2((" CurrPtr=%RGp Next=%RGp T=%d AltNext=%RGp T=%d\n",
1708 ((RTGCPHYS)qhd.CurrQTD.Pointer << EHCI_TD_PTR_SHIFT),
1709 ((RTGCPHYS)qhd.Overlay.OrgQTD.Next.Pointer << EHCI_TD_PTR_SHIFT), qhd.Overlay.OrgQTD.Next.Terminate,
1710 ((RTGCPHYS)qhd.Overlay.OrgQTD.AltNext.Pointer << EHCI_TD_PTR_SHIFT), qhd.Overlay.OrgQTD.AltNext.Terminate));
1711 ehciR3DumpSingleQTD((RTGCPHYS)qhd.CurrQTD.Pointer << EHCI_TD_PTR_SHIFT, &qhd.Overlay.OrgQTD, "");
1712 ehciR3DumpQTD(pDevIns, (RTGCPHYS)qhd.Overlay.OrgQTD.Next.Pointer << EHCI_TD_PTR_SHIFT, true);
1713
1714 Assert(qhd.Next.Pointer || qhd.Next.Terminate);
1715 if ( !fList
1716 || qhd.Next.Terminate
1717 || !qhd.Next.Pointer)
1718 return;
1719
1720 for (;;)
1721 {
1722 /* Read the next pointer */
1723 EHCI_TD_PTR ptr;
1724 ehciR3ReadTDPtr(pDevIns, GCPhys, &ptr);
1725
1726 AssertMsg(ptr.Type == EHCI_DESCRIPTOR_QH, ("Unexpected pointer to type %d\n", ptr.Type));
1727 Assert(ptr.Pointer || ptr.Terminate);
1728 if ( ptr.Terminate
1729 || !ptr.Pointer
1730 || ptr.Type != EHCI_DESCRIPTOR_QH)
1731 break;
1732
1733 /* next */
1734 if (GCPhys == ((RTGCPHYS)ptr.Pointer << EHCI_TD_PTR_SHIFT))
1735 break; /* Looping on itself. Bad guest! */
1736
1737 GCPhys = (RTGCPHYS)ptr.Pointer << EHCI_TD_PTR_SHIFT;
1738 if (GCPhys == GCPhysHead)
1739 break; /* break the loop */
1740
1741 ehciR3DumpQH(pDevIns, GCPhys, false);
1742
1743 /* And again, if we ran too many iterations, the list must be looping on itself.
1744 * Just quit.
1745 */
1746 if (++iterations == 64)
1747 {
1748 LogFunc(("Too many iterations, exiting!\n"));
1749 break;
1750 }
1751 }
1752}
1753
1754/**
1755 * Dumps an ITD list. LOG_ENABLED builds only.
1756 */
1757DECLINLINE(void) ehciR3DumpITD(PPDMDEVINS pDevIns, RTGCPHYS GCPhysHead, bool fList)
1758{
1759 RTGCPHYS GCPhys = GCPhysHead;
1760 unsigned iterations = 0;
1761
1762 for (;;)
1763 {
1764 Log2((" ITD: %RGp={", GCPhys));
1765
1766 /* Read the whole ITD */
1767 EHCI_ITD_PAD PaddedItd;
1768 PEHCI_ITD_PAD pItd = &PaddedItd;
1769 ehciR3ReadItd(pDevIns, GCPhys, &PaddedItd);
1770
1771 Log2(("Addr=%x EndPt=%x Dir=%s MaxSize=%x Mult=%d}\n", pItd->Buffer.Misc.DeviceAddress, pItd->Buffer.Misc.EndPt, (pItd->Buffer.Misc.DirectionIn) ? "in" : "out", pItd->Buffer.Misc.MaxPacket, pItd->Buffer.Misc.Multi));
1772 for (unsigned i=0;i<RT_ELEMENTS(pItd->Transaction);i++)
1773 {
1774 if (pItd->Transaction[i].Active)
1775 {
1776 Log2(("T%d Len=%x Offset=%x PG=%d IOC=%d Buffer=%x\n", i, pItd->Transaction[i].Length, pItd->Transaction[i].Offset, pItd->Transaction[i].PG, pItd->Transaction[i].IOC,
1777 (RTGCPHYS)pItd->Buffer.Buffer[pItd->Transaction[i].PG].Pointer << EHCI_BUFFER_PTR_SHIFT));
1778 }
1779 }
1780 Assert(pItd->Next.Pointer || pItd->Next.Terminate);
1781 if (!fList || pItd->Next.Terminate || !pItd->Next.Pointer)
1782 break;
1783
1784 /* And again, if we ran too many iterations, the list must be looping on itself.
1785 * Just quit.
1786 */
1787 if (++iterations == 128)
1788 {
1789 LogFunc(("Too many iterations, exiting!\n"));
1790 break;
1791 }
1792
1793 /* next */
1794 GCPhys = (RTGCPHYS)pItd->Next.Pointer << EHCI_TD_PTR_SHIFT;
1795 }
1796}
1797
1798/**
1799 * Dumps a periodic list. LOG_ENABLED builds only.
1800 */
1801DECLINLINE(void) ehciR3DumpPeriodicList(PPDMDEVINS pDevIns, RTGCPHYS GCPhysHead, const char *pszMsg, bool fTDs)
1802{
1803 RT_NOREF(fTDs);
1804 RTGCPHYS GCPhys = GCPhysHead;
1805 unsigned iterations = 0;
1806
1807 if (pszMsg)
1808 Log2(("%s:", pszMsg));
1809
1810 for (;;)
1811 {
1812 EHCI_FRAME_LIST_PTR FramePtr;
1813
1814 /* ED */
1815 Log2((" %RGp={", GCPhys));
1816 if (!GCPhys)
1817 {
1818 Log2(("END}\n"));
1819 return;
1820 }
1821
1822 /* Frame list pointer */
1823 ehciR3ReadFrameListPtr(pDevIns, GCPhys, &FramePtr);
1824 if (FramePtr.Terminate)
1825 {
1826 Log2(("[Terminate]}\n"));
1827 }
1828 else
1829 {
1830 RTGCPHYS GCPhys1 = (RTGCPHYS)FramePtr.FrameAddr << EHCI_FRAME_LIST_NEXTPTR_SHIFT;
1831 switch (FramePtr.Type)
1832 {
1833 case EHCI_DESCRIPTOR_ITD:
1834 Log2(("[ITD]}\n"));
1835 ehciR3DumpITD(pDevIns, GCPhys1, false);
1836 break;
1837 case EHCI_DESCRIPTOR_SITD:
1838 Log2(("[SITD]}\n"));
1839 ehciR3DumpSITD(pDevIns, GCPhys1, false);
1840 break;
1841 case EHCI_DESCRIPTOR_QH:
1842 Log2(("[QH]}\n"));
1843 ehciR3DumpQH(pDevIns, GCPhys1, false);
1844 break;
1845 case EHCI_DESCRIPTOR_FSTN:
1846 Log2(("[FSTN]}\n"));
1847 ehciR3DumpFSTN(pDevIns, GCPhys1, false);
1848 break;
1849 }
1850 }
1851
1852 /* Same old. If we ran too many iterations, the list must be looping on itself.
1853 * Just quit.
1854 */
1855 if (++iterations == 128)
1856 {
1857 LogFunc(("Too many iterations, exiting!\n"));
1858 break;
1859 }
1860
1861 /* next */
1862 GCPhys = GCPhys + sizeof(FramePtr);
1863 }
1864}
1865
1866#endif /* LOG_ENABLED */
1867
1868
1869DECLINLINE(int) ehciR3InFlightFindFree(PEHCICC pThisCC, const int iStart)
1870{
1871 unsigned i = iStart;
1872 while (i < RT_ELEMENTS(pThisCC->aInFlight)) {
1873 if (pThisCC->aInFlight[i].pUrb == NULL)
1874 return i;
1875 i++;
1876 }
1877 i = iStart;
1878 while (i-- > 0) {
1879 if (pThisCC->aInFlight[i].pUrb == NULL)
1880 return i;
1881 }
1882 return -1;
1883}
1884
1885
1886/**
1887 * Record an in-flight TD.
1888 *
1889 * @param pThis EHCI instance data, shared edition.
1890 * @param pThisCC EHCI instance data, ring-3 edition.
1891 * @param GCPhysTD Physical address of the TD.
1892 * @param pUrb The URB.
1893 */
1894static void ehciR3InFlightAdd(PEHCI pThis, PEHCICC pThisCC, RTGCPHYS GCPhysTD, PVUSBURB pUrb)
1895{
1896 int i = ehciR3InFlightFindFree(pThisCC, (GCPhysTD >> 4) % RT_ELEMENTS(pThisCC->aInFlight));
1897 if (i >= 0)
1898 {
1899#ifdef LOG_ENABLED
1900 pUrb->pHci->u32FrameNo = pThis->HcFmNumber;
1901#endif
1902 pThisCC->aInFlight[i].GCPhysTD = GCPhysTD;
1903 pThisCC->aInFlight[i].pUrb = pUrb;
1904 pThisCC->cInFlight++;
1905 return;
1906 }
1907 AssertMsgFailed(("Out of space cInFlight=%d!\n", pThisCC->cInFlight));
1908 RT_NOREF(pThis);
1909}
1910
1911
1912/**
1913 * Record in-flight TDs for an URB.
1914 *
1915 * @param pThis EHCI instance data, shared edition.
1916 * @param pThisCC EHCI instance data, ring-3 edition.
1917 * @param pUrb The URB.
1918 */
1919static void ehciR3InFlightAddUrb(PEHCI pThis, PEHCICC pThisCC, PVUSBURB pUrb)
1920{
1921 for (unsigned iTd = 0; iTd < pUrb->pHci->cTds; iTd++)
1922 ehciR3InFlightAdd(pThis, pThisCC, pUrb->paTds[iTd].TdAddr, pUrb);
1923}
1924
1925
1926/**
1927 * Finds a in-flight TD.
1928 *
1929 * @returns Index of the record.
1930 * @returns -1 if not found.
1931 * @param pThisCC EHCI instance data, ring-3 edition.
1932 * @param GCPhysTD Physical address of the TD.
1933 * @remark This has to be fast.
1934 */
1935static int ehciR3InFlightFind(PEHCICC pThisCC, RTGCPHYS GCPhysTD)
1936{
1937 unsigned cLeft = pThisCC->cInFlight;
1938 unsigned i = (GCPhysTD >> 4) % RT_ELEMENTS(pThisCC->aInFlight);
1939 const int iLast = i;
1940 while (i < RT_ELEMENTS(pThisCC->aInFlight)) {
1941 if (pThisCC->aInFlight[i].GCPhysTD == GCPhysTD && pThisCC->aInFlight[i].pUrb)
1942 return i;
1943 if (pThisCC->aInFlight[i].pUrb)
1944 if (cLeft-- <= 1)
1945 return -1;
1946 i++;
1947 }
1948 i = iLast;
1949 while (i-- > 0) {
1950 if (pThisCC->aInFlight[i].GCPhysTD == GCPhysTD && pThisCC->aInFlight[i].pUrb)
1951 return i;
1952 if (pThisCC->aInFlight[i].pUrb)
1953 if (cLeft-- <= 1)
1954 return -1;
1955 }
1956 return -1;
1957}
1958
1959
1960/**
1961 * Checks if a TD is in-flight.
1962 *
1963 * @returns true if in flight, false if not.
1964 * @param pThisCC EHCI instance data, ring-3 edition.
1965 * @param GCPhysTD Physical address of the TD.
1966 */
1967static bool ehciR3IsTdInFlight(PEHCICC pThisCC, RTGCPHYS GCPhysTD)
1968{
1969 return ehciR3InFlightFind(pThisCC, GCPhysTD) >= 0;
1970}
1971
1972
1973/**
1974 * Removes a in-flight TD.
1975 *
1976 * @returns 0 if found. For logged builds this is the number of frames the TD has been in-flight.
1977 * @returns -1 if not found.
1978 * @param pThis EHCI instance data, shared edition.
1979 * @param pThisCC EHCI instance data, ring-3 edition.
1980 * @param GCPhysTD Physical address of the TD.
1981 */
1982static int ehciR3InFlightRemove(PEHCI pThis, PEHCICC pThisCC, RTGCPHYS GCPhysTD)
1983{
1984 int i = ehciR3InFlightFind(pThisCC, GCPhysTD);
1985 if (i >= 0)
1986 {
1987#ifdef LOG_ENABLED
1988 const int cFramesInFlight = pThis->HcFmNumber - pThisCC->aInFlight[i].pUrb->pHci->u32FrameNo;
1989#else
1990 const int cFramesInFlight = 0;
1991#endif
1992 Log2Func(("reaping TD=%RGp %d frames (%#010x-%#010x)\n",
1993 GCPhysTD, cFramesInFlight, pThisCC->aInFlight[i].pUrb->pHci->u32FrameNo, pThis->HcFmNumber));
1994 pThisCC->aInFlight[i].GCPhysTD = 0;
1995 pThisCC->aInFlight[i].pUrb = NULL;
1996 pThisCC->cInFlight--;
1997 return cFramesInFlight;
1998 }
1999 AssertMsgFailed(("TD %RGp is not in flight\n", GCPhysTD));
2000 RT_NOREF(pThis);
2001 return -1;
2002}
2003
2004
2005/**
2006 * Removes all TDs associated with a URB from the in-flight tracking.
2007 *
2008 * @returns 0 if found. For logged builds this is the number of frames the TD has been in-flight.
2009 * @returns -1 if not found.
2010 * @param pThis EHCI instance data, shared edition.
2011 * @param pThisCC EHCI instance data, ring-3 edition.
2012 * @param pUrb The URB.
2013 */
2014static int ehciR3InFlightRemoveUrb(PEHCI pThis, PEHCICC pThisCC, PVUSBURB pUrb)
2015{
2016 int cFramesInFlight = ehciR3InFlightRemove(pThis, pThisCC, pUrb->paTds[0].TdAddr);
2017 if (pUrb->pHci->cTds > 1)
2018 {
2019 for (unsigned iTd = 1; iTd < pUrb->pHci->cTds; iTd++)
2020 if (ehciR3InFlightRemove(pThis, pThisCC, pUrb->paTds[iTd].TdAddr) < 0)
2021 cFramesInFlight = -1;
2022 }
2023 return cFramesInFlight;
2024}
2025
2026
2027/**
2028 * Checks that the transport descriptors associated with the URB
2029 * hasn't been changed in any way indicating that they may have been canceled.
2030 *
2031 * This rountine also updates the TD copies contained within the URB.
2032 *
2033 * @returns true if the URB has been canceled, otherwise false.
2034 * @param pThisCC EHCI instance data, ring-3 edition. (For stats.)
2035 * @param pUrb The URB in question.
2036 * @param pItd The ITD pointer.
2037 */
2038static bool ehciR3ItdHasUrbBeenCanceled(PEHCICC pThisCC, PVUSBURB pUrb, PEHCI_ITD_PAD pItd)
2039{
2040 RT_NOREF(pThisCC);
2041 Assert(pItd);
2042 if (!pUrb)
2043 return true;
2044
2045 PEHCI_ITD pItdCopy = (PEHCI_ITD)pUrb->paTds[0].TdCopy;
2046
2047 /* Check transactions */
2048 for (unsigned i = 0; i < RT_ELEMENTS(pItd->Transaction); i++)
2049 {
2050 if ( pItd->Transaction[i].Length != pItdCopy->Transaction[i].Length
2051 || pItd->Transaction[i].Offset != pItdCopy->Transaction[i].Offset
2052 || pItd->Transaction[i].PG != pItdCopy->Transaction[i].PG
2053 || pItd->Transaction[i].Active != pItdCopy->Transaction[i].Active)
2054 {
2055 Log(("%s: ehciR3ItdHasUrbBeenCanceled: TdAddr=%RGp canceled! [iso]\n",
2056 pUrb->pszDesc, pUrb->paTds[0].TdAddr));
2057 Log2((" %.*Rhxs (cur)\n"
2058 "!= %.*Rhxs (copy)\n",
2059 sizeof(*pItd), pItd, sizeof(*pItd), &pUrb->paTds[0].TdCopy[0]));
2060 STAM_COUNTER_INC(&pThisCC->StatCanceledIsocUrbs);
2061 return true;
2062 }
2063 }
2064
2065 /* Check misc characteristics */
2066 if ( pItd->Buffer.Misc.DeviceAddress != pItdCopy->Buffer.Misc.DeviceAddress
2067 || pItd->Buffer.Misc.DirectionIn != pItdCopy->Buffer.Misc.DirectionIn
2068 || pItd->Buffer.Misc.EndPt != pItdCopy->Buffer.Misc.EndPt)
2069 {
2070 Log(("%s: ehciR3ItdHasUrbBeenCanceled (misc): TdAddr=%RGp canceled! [iso]\n",
2071 pUrb->pszDesc, pUrb->paTds[0].TdAddr));
2072 Log2((" %.*Rhxs (cur)\n"
2073 "!= %.*Rhxs (copy)\n",
2074 sizeof(*pItd), pItd, sizeof(*pItd), &pUrb->paTds[0].TdCopy[0]));
2075 STAM_COUNTER_INC(&pThisCC->StatCanceledIsocUrbs);
2076 return true;
2077 }
2078
2079 /* Check buffer pointers */
2080 for (unsigned i = 0; i < RT_ELEMENTS(pItd->Buffer.Buffer); i++)
2081 {
2082 if (pItd->Buffer.Buffer[i].Pointer != pItdCopy->Buffer.Buffer[i].Pointer)
2083 {
2084 Log(("%s: ehciR3ItdHasUrbBeenCanceled (buf): TdAddr=%RGp canceled! [iso]\n",
2085 pUrb->pszDesc, pUrb->paTds[0].TdAddr));
2086 Log2((" %.*Rhxs (cur)\n"
2087 "!= %.*Rhxs (copy)\n",
2088 sizeof(*pItd), pItd, sizeof(*pItd), &pUrb->paTds[0].TdCopy[0]));
2089 STAM_COUNTER_INC(&pThisCC->StatCanceledIsocUrbs);
2090 return true;
2091 }
2092 }
2093 return false;
2094}
2095
2096/**
2097 * Checks that the transport descriptors associated with the URB
2098 * hasn't been changed in any way indicating that they may have been canceled.
2099 *
2100 * This rountine also updates the TD copies contained within the URB.
2101 *
2102 * @returns true if the URB has been canceled, otherwise false.
2103 * @param pThisCC EHCI instance data, ring-3 edition. (For stats.)
2104 * @param pUrb The URB in question.
2105 * @param pQhd The QHD pointer
2106 * @param pQtd The QTD pointer
2107 */
2108static bool ehciR3QhdHasUrbBeenCanceled(PEHCICC pThisCC, PVUSBURB pUrb, PEHCI_QHD pQhd, PEHCI_QTD pQtd)
2109{
2110 RT_NOREF(pQhd, pThisCC);
2111 Assert(pQhd && pQtd);
2112 if ( !pUrb
2113 || !ehciR3IsTdInFlight(pThisCC, pUrb->paTds[0].TdAddr))
2114 return true;
2115
2116 PEHCI_QTD pQtdCopy = (PEHCI_QTD)pUrb->paTds[0].TdCopy;
2117
2118 if ( pQtd->Token.Bits.Length != pQtdCopy->Token.Bits.Length
2119 || pQtd->Token.Bits.Active != pQtdCopy->Token.Bits.Active
2120 || pQtd->Token.Bits.DataToggle != pQtdCopy->Token.Bits.DataToggle
2121 || pQtd->Token.Bits.CurrentPage != pQtdCopy->Token.Bits.CurrentPage
2122 || pQtd->Token.Bits.PID != pQtdCopy->Token.Bits.PID
2123 || pQtd->Buffer.Offset.Offset != pQtdCopy->Buffer.Offset.Offset)
2124 {
2125 Log(("%s: ehciQtdHasUrbBeenCanceled: TdAddr=%RGp canceled! [iso]\n",
2126 pUrb->pszDesc, pUrb->paTds[0].TdAddr));
2127 Log2((" %.*Rhxs (cur)\n"
2128 "!= %.*Rhxs (copy)\n",
2129 sizeof(*pQtd), pQtd, sizeof(*pQtd), &pUrb->paTds[0].TdCopy[0]));
2130 STAM_COUNTER_INC(&pThisCC->StatCanceledGenUrbs);
2131 return true;
2132 }
2133
2134
2135 /* Check buffer pointers */
2136 for (unsigned i = 0; i < RT_ELEMENTS(pQtd->Buffer.Buffer); i++)
2137 {
2138 if (pQtd->Buffer.Buffer[i].Pointer != pQtdCopy->Buffer.Buffer[i].Pointer)
2139 {
2140 Log(("%s: ehciQtdHasUrbBeenCanceled (buf): TdAddr=%RGp canceled! [iso]\n",
2141 pUrb->pszDesc, pUrb->paTds[0].TdAddr));
2142 Log2((" %.*Rhxs (cur)\n"
2143 "!= %.*Rhxs (copy)\n",
2144 sizeof(*pQtd), pQtd, sizeof(*pQtd), &pUrb->paTds[0].TdCopy[0]));
2145 STAM_COUNTER_INC(&pThisCC->StatCanceledGenUrbs);
2146 return true;
2147 }
2148 }
2149
2150 return false;
2151}
2152
2153/**
2154 * Set the ITD status bits acorresponding to the VUSB status code.
2155 *
2156 * @param enmStatus The VUSB status code.
2157 * @param pItdStatus ITD status pointer
2158 */
2159static void ehciR3VUsbStatus2ItdStatus(VUSBSTATUS enmStatus, EHCI_ITD_TRANSACTION *pItdStatus)
2160{
2161 switch (enmStatus)
2162 {
2163 case VUSBSTATUS_OK:
2164 pItdStatus->TransactError = 0;
2165 pItdStatus->DataBufError = 0;
2166 break; /* make sure error bits are cleared */
2167 case VUSBSTATUS_STALL:
2168 case VUSBSTATUS_DNR:
2169 case VUSBSTATUS_CRC:
2170 pItdStatus->TransactError = 1;
2171 break;
2172 case VUSBSTATUS_DATA_UNDERRUN:
2173 case VUSBSTATUS_DATA_OVERRUN:
2174 pItdStatus->DataBufError = 1;
2175 break;
2176 case VUSBSTATUS_NOT_ACCESSED:
2177 Log(("pUrb->enmStatus=VUSBSTATUS_NOT_ACCESSED!!!\n"));
2178 break; /* can't signal this other than setting the length to 0 */
2179 default:
2180 Log(("pUrb->enmStatus=%#x!!!\n", enmStatus));
2181 break;;
2182 }
2183}
2184
2185/**
2186 * Set the QTD status bits acorresponding to the VUSB status code.
2187 *
2188 * @param enmStatus The VUSB status code.
2189 * @param pQtdStatus QTD status pointer
2190 */
2191static void ehciR3VUsbStatus2QtdStatus(VUSBSTATUS enmStatus, EHCI_QTD_TOKEN *pQtdStatus)
2192{
2193 /** @todo CERR */
2194 switch (enmStatus)
2195 {
2196 case VUSBSTATUS_OK:
2197 break; /* nothing to do */
2198 case VUSBSTATUS_STALL:
2199 pQtdStatus->Halted = 1;
2200 pQtdStatus->Active = 0;
2201 break; /* not an error! */
2202 case VUSBSTATUS_DNR:
2203 case VUSBSTATUS_CRC:
2204 pQtdStatus->TransactError = 1;
2205 break;
2206 case VUSBSTATUS_DATA_UNDERRUN:
2207 case VUSBSTATUS_DATA_OVERRUN:
2208 pQtdStatus->DataBufError = 1;
2209 break;
2210 case VUSBSTATUS_NOT_ACCESSED:
2211 Log(("pUrb->enmStatus=VUSBSTATUS_NOT_ACCESSED!!!\n"));
2212 break; /* can't signal this */
2213 default:
2214 Log(("pUrb->enmStatus=%#x!!!\n", enmStatus));
2215 break;;
2216 }
2217}
2218
2219
2220/**
2221 * Heuristic to determine the transfer type
2222 *
2223 * @returns transfer type
2224 * @param pQhd Queue head pointer
2225 */
2226static VUSBXFERTYPE ehciR3QueryTransferType(PEHCI_QHD pQhd)
2227{
2228 /* If it's EP0, we know what it is. */
2229 if (!pQhd->Characteristics.EndPt)
2230 return VUSBXFERTYPE_CTRL;
2231
2232 /* Non-zero SMask implies interrupt transfer. */
2233 if (pQhd->Caps.SMask)
2234 return VUSBXFERTYPE_INTR;
2235
2236 /* For non-HS EPs, control endpoints are clearly marked. */
2237 if ( pQhd->Characteristics.ControlEPFlag
2238 && pQhd->Characteristics.EndPtSpeed != EHCI_QHD_EPT_SPEED_HIGH)
2239 return VUSBXFERTYPE_CTRL;
2240
2241 /* If we still don't know, it's guesswork from now on. */
2242
2243 /* 64 likely indicates an interrupt transfer (see @bugref{8314})*/
2244 if (pQhd->Characteristics.MaxLength == 64)
2245 return VUSBXFERTYPE_INTR;
2246
2247 /* At this point we hope it's a bulk transfer with max packet size of 512. */
2248 Assert(pQhd->Characteristics.MaxLength == 512);
2249 return VUSBXFERTYPE_BULK;
2250}
2251
2252/**
2253 * Worker for ehciR3RhXferCompletion that handles the completion of
2254 * a URB made up of isochronous TDs.
2255 *
2256 * In general, all URBs should have status OK.
2257 */
2258static void ehciR3RhXferCompleteITD(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC, PVUSBURB pUrb)
2259{
2260 /* Read the whole ITD */
2261 EHCI_ITD_PAD PaddedItd;
2262 PEHCI_ITD_PAD pItd = &PaddedItd;
2263 ehciR3ReadItd(pDevIns, pUrb->paTds[0].TdAddr, &PaddedItd);
2264
2265 /*
2266 * Check that the URB hasn't been canceled and then try unlink the TDs.
2267 *
2268 * We drop the URB if the ED is marked halted/skip ASSUMING that this
2269 * means the HCD has canceled the URB.
2270 *
2271 * If we succeed here (i.e. not dropping the URB), the TdCopy members will
2272 * be updated but not yet written. We will delay the writing till we're done
2273 * with the data copying, buffer pointer advancing and error handling.
2274 */
2275 bool fHasBeenCanceled = false;
2276 int cFmAge = ehciR3InFlightRemoveUrb(pThis, pThisCC, pUrb);
2277 if ( cFmAge < 0
2278 || (fHasBeenCanceled = ehciR3ItdHasUrbBeenCanceled(pThisCC, pUrb, pItd))
2279 )
2280 {
2281 Log(("%s: ehciR3RhXferCompleteITD: DROPPED {ITD=%RGp cTds=%d TD0=%RGp age %d} because:%s%s!!!\n",
2282 pUrb->pszDesc, pUrb->pHci->EdAddr, pUrb->pHci->cTds, pUrb->paTds[0].TdAddr, cFmAge,
2283 cFmAge < 0 ? " td not-in-flight" : "",
2284 fHasBeenCanceled ? " td canceled" : ""));
2285 NOREF(fHasBeenCanceled);
2286 STAM_COUNTER_INC(&pThisCC->StatDroppedUrbs);
2287 return;
2288 }
2289
2290 bool fIOC = false, fError = false;
2291
2292 /*
2293 * Copy the data back (if IN operation) and update the TDs.
2294 */
2295 if (pUrb->enmStatus == VUSBSTATUS_OK)
2296 {
2297 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
2298 {
2299 ehciR3VUsbStatus2ItdStatus(pUrb->aIsocPkts[i].enmStatus, &pItd->Transaction[i]);
2300 if (pItd->Transaction[i].IOC)
2301 fIOC = true;
2302
2303 if ( pUrb->enmDir == VUSBDIRECTION_IN
2304 && ( pUrb->aIsocPkts[i].enmStatus == VUSBSTATUS_OK
2305 || pUrb->aIsocPkts[i].enmStatus == VUSBSTATUS_DATA_UNDERRUN
2306 || pUrb->aIsocPkts[i].enmStatus == VUSBSTATUS_DATA_OVERRUN))
2307 {
2308 Assert(pItd->Transaction[i].Active);
2309
2310 if (pItd->Transaction[i].Active)
2311 {
2312 const unsigned pg = pItd->Transaction[i].PG;
2313 const unsigned cb = pUrb->aIsocPkts[i].cb;
2314 pItd->Transaction[i].Length = cb; /* Set the actual size. */
2315 /* Copy data. */
2316 if (cb)
2317 {
2318 uint8_t *pb = &pUrb->abData[pUrb->aIsocPkts[i].off];
2319
2320 RTGCPHYS GCPhysBuf = (RTGCPHYS)pItd->Buffer.Buffer[pg].Pointer << EHCI_BUFFER_PTR_SHIFT;
2321 GCPhysBuf += pItd->Transaction[i].Offset;
2322
2323 /* If the transfer would cross page boundary, use the next sequential PG pointer
2324 * for the second part (section 4.7.1).
2325 */
2326 if (pItd->Transaction[i].Offset + pItd->Transaction[i].Length > GUEST_PAGE_SIZE)
2327 {
2328 unsigned cb1 = GUEST_PAGE_SIZE - pItd->Transaction[i].Offset;
2329 unsigned cb2 = cb - cb1;
2330
2331 ehciPhysWrite(pDevIns, GCPhysBuf, pb, cb1);
2332 if ((pg + 1) >= EHCI_NUM_ITD_PAGES)
2333 LogRelMax(10, ("EHCI: Crossing to nonstandard page %d in iTD at %RGp on completion.\n", pg + 1, pUrb->paTds[0].TdAddr));
2334
2335 GCPhysBuf = (RTGCPHYS)pItd->Buffer.Buffer[pg + 1].Pointer << EHCI_BUFFER_PTR_SHIFT;
2336 ehciPhysWrite(pDevIns, GCPhysBuf, pb + cb1, cb2);
2337 }
2338 else
2339 ehciPhysWrite(pDevIns, GCPhysBuf, pb, cb);
2340
2341 Log5(("packet %d: off=%#x cb=%#x pb=%p (%#x)\n"
2342 "%.*Rhxd\n",
2343 i, pUrb->aIsocPkts[i].off, cb, pb, pb - &pUrb->abData[0], cb, pb));
2344 }
2345 }
2346 }
2347 pItd->Transaction[i].Active = 0; /* transfer is now officially finished */
2348 } /* for */
2349 }
2350 else
2351 {
2352 LogFunc(("Taking untested code path at line %d...\n", __LINE__));
2353 /*
2354 * Most status codes only apply to the individual packets.
2355 *
2356 * If we get a URB level error code of this kind, we'll distribute
2357 * it to all the packages unless some other status is available for
2358 * a package. This is a bit fuzzy, and we will get rid of this code
2359 * before long!
2360 */
2361 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
2362 {
2363 if (pItd->Transaction[i].Active)
2364 {
2365 ehciR3VUsbStatus2ItdStatus(pUrb->aIsocPkts[i].enmStatus, &pItd->Transaction[i]);
2366 if (pItd->Transaction[i].IOC)
2367 fIOC = true;
2368
2369 pItd->Transaction[i].Active = 0; /* transfer is now officially finished */
2370 }
2371 }
2372 fError = true;
2373 }
2374
2375 /*
2376 * Write back the modified TD.
2377 */
2378
2379 Log(("%s: ehciR3RhXferCompleteITD: pUrb->paTds[0].TdAddr=%RGp EdAddr=%RGp "
2380 "psw0=%x:%x psw1=%x:%x psw2=%x:%x psw3=%x:%x psw4=%x:%x psw5=%x:%x psw6=%x:%x psw7=%x:%x\n",
2381 pUrb->pszDesc, pUrb->paTds[0].TdAddr,
2382 pUrb->pHci->EdAddr,
2383 pItd->Buffer.Buffer[pItd->Transaction[0].PG].Pointer << EHCI_BUFFER_PTR_SHIFT, pItd->Transaction[0].Length,
2384 pItd->Buffer.Buffer[pItd->Transaction[1].PG].Pointer << EHCI_BUFFER_PTR_SHIFT, pItd->Transaction[1].Length,
2385 pItd->Buffer.Buffer[pItd->Transaction[2].PG].Pointer << EHCI_BUFFER_PTR_SHIFT, pItd->Transaction[2].Length,
2386 pItd->Buffer.Buffer[pItd->Transaction[3].PG].Pointer << EHCI_BUFFER_PTR_SHIFT, pItd->Transaction[3].Length,
2387 pItd->Buffer.Buffer[pItd->Transaction[4].PG].Pointer << EHCI_BUFFER_PTR_SHIFT, pItd->Transaction[4].Length,
2388 pItd->Buffer.Buffer[pItd->Transaction[5].PG].Pointer << EHCI_BUFFER_PTR_SHIFT, pItd->Transaction[5].Length,
2389 pItd->Buffer.Buffer[pItd->Transaction[6].PG].Pointer << EHCI_BUFFER_PTR_SHIFT, pItd->Transaction[6].Length,
2390 pItd->Buffer.Buffer[pItd->Transaction[7].PG].Pointer << EHCI_BUFFER_PTR_SHIFT, pItd->Transaction[7].Length
2391 ));
2392 ehciR3WriteItd(pDevIns, pUrb->paTds[0].TdAddr, pItd);
2393
2394 /*
2395 * Signal an interrupt on the next interrupt threshold when IOC was set for any transaction.
2396 * Both error and completion interrupts may be signaled at the same time (see Table 2.10).
2397 */
2398 if (fError)
2399 ehciR3SetInterrupt(pDevIns, pThis, EHCI_STATUS_ERROR_INT);
2400 if (fIOC)
2401 ehciR3SetInterrupt(pDevIns, pThis, EHCI_STATUS_THRESHOLD_INT);
2402}
2403
2404
2405/**
2406 * Worker for ehciR3RhXferCompletion that handles the completion of
2407 * a URB made up of queue heads/descriptors
2408 */
2409static void ehciR3RhXferCompleteQH(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC, PVUSBURB pUrb)
2410{
2411 EHCI_QHD qhd;
2412 EHCI_QTD qtd;
2413
2414 /* Read the whole QHD & QTD */
2415 ehciR3ReadQHD(pDevIns, pUrb->pHci->EdAddr, &qhd);
2416 AssertMsg(pUrb->paTds[0].TdAddr == ((RTGCPHYS)qhd.CurrQTD.Pointer << EHCI_TD_PTR_SHIFT),
2417 ("Out of order completion %RGp != %RGp Endpoint=%#x\n", pUrb->paTds[0].TdAddr,
2418 ((RTGCPHYS)qhd.CurrQTD.Pointer << EHCI_TD_PTR_SHIFT), pUrb->EndPt));
2419 ehciR3ReadQTD(pDevIns, (RTGCPHYS)qhd.CurrQTD.Pointer << EHCI_TD_PTR_SHIFT, &qtd);
2420
2421 /*
2422 * Check that the URB hasn't been canceled and then try unlink the TDs.
2423 *
2424 * We drop the URB if the ED is marked halted/skip ASSUMING that this
2425 * means the HCD has canceled the URB.
2426 *
2427 * If we succeed here (i.e. not dropping the URB), the TdCopy members will
2428 * be updated but not yet written. We will delay the writing till we're done
2429 * with the data copying, buffer pointer advancing and error handling.
2430 */
2431 bool fHasBeenCanceled = false;
2432 if ((fHasBeenCanceled = ehciR3QhdHasUrbBeenCanceled(pThisCC, pUrb, &qhd, &qtd)))
2433 {
2434 Log(("%s: ehciRhXferCompletionQH: DROPPED {qTD=%RGp cTds=%d TD0=%RGp} because:%s%s!!!\n",
2435 pUrb->pszDesc, pUrb->pHci->EdAddr, pUrb->pHci->cTds, pUrb->paTds[0].TdAddr,
2436 (pUrb->paTds[0].TdAddr != ((RTGCPHYS)qhd.CurrQTD.Pointer << EHCI_TD_PTR_SHIFT)) ? " curptr changed" : "",
2437 fHasBeenCanceled ? " td canceled" : ""));
2438 NOREF(fHasBeenCanceled);
2439 STAM_COUNTER_INC(&pThisCC->StatDroppedUrbs);
2440
2441 ehciR3InFlightRemoveUrb(pThis, pThisCC, pUrb);
2442 qtd.Token.Bits.Active = 0;
2443 ehciR3QHUpdateOverlay(pDevIns, pThis, pThisCC, &qhd, pUrb->pHci->EdAddr, &qtd);
2444 return;
2445 }
2446 ehciR3InFlightRemoveUrb(pThis, pThisCC, pUrb);
2447
2448 /* Update the status/error bits */
2449 ehciR3VUsbStatus2QtdStatus(pUrb->enmStatus, &qtd.Token.Bits);
2450
2451 /*
2452 * Write back IN buffers.
2453 */
2454 if ( pUrb->enmDir == VUSBDIRECTION_IN
2455 && pUrb->cbData
2456 && ( pUrb->enmStatus == VUSBSTATUS_OK
2457 || pUrb->enmStatus == VUSBSTATUS_DATA_OVERRUN
2458 || pUrb->enmStatus == VUSBSTATUS_DATA_UNDERRUN
2459 )
2460 )
2461 {
2462 unsigned curOffset = 0;
2463 unsigned cbLeft = pUrb->cbData;
2464
2465 for (unsigned i=qtd.Token.Bits.CurrentPage;i<RT_ELEMENTS(qtd.Buffer.Buffer);i++)
2466 {
2467 RTGCPHYS GCPhysBuf;
2468 unsigned cbCurTransfer;
2469
2470 GCPhysBuf = (RTGCPHYS)qtd.Buffer.Buffer[i].Pointer << EHCI_BUFFER_PTR_SHIFT;
2471 if (i == 0)
2472 GCPhysBuf += qtd.Buffer.Offset.Offset;
2473
2474 cbCurTransfer = GUEST_PAGE_SIZE - (GCPhysBuf & GUEST_PAGE_OFFSET_MASK);
2475 cbCurTransfer = RT_MIN(cbCurTransfer, cbLeft);
2476
2477 Log3Func(("packet data for page %d:\n"
2478 "%.*Rhxd\n",
2479 i,
2480 cbCurTransfer, &pUrb->abData[curOffset]));
2481
2482 ehciPhysWrite(pDevIns, GCPhysBuf, &pUrb->abData[curOffset], cbCurTransfer);
2483 curOffset += cbCurTransfer;
2484 cbLeft -= cbCurTransfer;
2485
2486 if (cbLeft == 0)
2487 break;
2488 Assert(cbLeft < qtd.Token.Bits.Length);
2489 }
2490 }
2491
2492 if ( pUrb->cbData
2493 && ( pUrb->enmStatus == VUSBSTATUS_OK
2494 || pUrb->enmStatus == VUSBSTATUS_DATA_OVERRUN
2495 || pUrb->enmStatus == VUSBSTATUS_DATA_UNDERRUN
2496 )
2497 )
2498 {
2499 /* 3.5.3:
2500 * This field specifies the total number of bytes to be moved
2501 * with this transfer descriptor. This field is decremented by the number of bytes actually
2502 * moved during the transaction, only on the successful completion of the transaction
2503 */
2504 Assert(qtd.Token.Bits.Length >= pUrb->cbData);
2505 qtd.Token.Bits.Length -= pUrb->cbData;
2506
2507 /* Data was moved; toggle data toggle bit */
2508 qtd.Token.Bits.DataToggle ^= 1;
2509 }
2510
2511#ifdef LOG_ENABLED
2512 ehciR3DumpSingleQTD(pUrb->paTds[0].TdAddr, &qtd, "");
2513#endif
2514 qtd.Token.Bits.Active = 0; /* transfer is now officially finished */
2515
2516 /*
2517 * Write back the modified TD.
2518 */
2519 Log(("%s: ehciR3RhXferCompleteQH: pUrb->paTds[0].TdAddr=%RGp EdAddr=%RGp\n",
2520 pUrb->pszDesc, pUrb->paTds[0].TdAddr,
2521 pUrb->pHci->EdAddr));
2522
2523 ehciR3WriteQTD(pDevIns, pUrb->paTds[0].TdAddr, &qtd);
2524
2525 ehciR3QHUpdateOverlay(pDevIns, pThis, pThisCC, &qhd, pUrb->pHci->EdAddr, &qtd);
2526
2527 /*
2528 * Signal an interrupt on the next interrupt threshold when IOC was set for any transaction.
2529 * Both error and completion interrupts may be signaled at the same time (see Table 2.10).
2530 */
2531 if (EHCI_QTD_HAS_ERROR(&qtd.Token.Bits))
2532 ehciR3SetInterrupt(pDevIns, pThis, EHCI_STATUS_ERROR_INT);
2533
2534 bool fIOC = false;
2535 if (qtd.Token.Bits.IOC) {
2536 fIOC = true;
2537 Log2Func(("Interrupting, IOC set\n"));
2538 } else if (qtd.Token.Bits.Length && (qtd.Token.Bits.PID == EHCI_QTD_PID_IN)) {
2539 fIOC = true; /* See 4.10.8 */
2540 Log2Func(("Interrupting, short IN packet\n"));
2541 }
2542 if (fIOC)
2543 ehciR3SetInterrupt(pDevIns, pThis, EHCI_STATUS_THRESHOLD_INT);
2544}
2545
2546
2547/**
2548 * Transfer completion callback routine.
2549 *
2550 * VUSB will call this when a transfer have been completed
2551 * in a one or another way.
2552 *
2553 * @param pInterface Pointer to EHCI::ROOTHUB::IRhPort.
2554 * @param pUrb Pointer to the URB in question.
2555 */
2556static DECLCALLBACK(void) ehciR3RhXferCompletion(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb)
2557{
2558 PEHCICC pThisCC = RT_FROM_MEMBER(pInterface, EHCICC, RootHub.IRhPort);
2559 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2560 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
2561
2562 LogFlow(("%s: ehciR3RhXferCompletion: EdAddr=%RGp cTds=%d TdAddr0=%RGp\n",
2563 pUrb->pszDesc, pUrb->pHci->EdAddr, pUrb->pHci->cTds, pUrb->paTds[0].TdAddr));
2564 LogFlow(("%s: ehciR3RhXferCompletion: cbData=%x status=%x\n", pUrb->pszDesc, pUrb->cbData, pUrb->enmStatus));
2565
2566 Assert(pUrb->pHci->cTds == 1);
2567
2568 RTCritSectEnter(&pThisCC->CritSect);
2569 pThisCC->fIdle = false; /* Mark as active */
2570
2571 switch (pUrb->paTds[0].TdType)
2572 {
2573 case EHCI_DESCRIPTOR_QH:
2574 ehciR3RhXferCompleteQH(pDevIns, pThis, pThisCC, pUrb);
2575 break;
2576
2577 case EHCI_DESCRIPTOR_ITD:
2578 ehciR3RhXferCompleteITD(pDevIns, pThis, pThisCC, pUrb);
2579 break;
2580
2581 case EHCI_DESCRIPTOR_SITD:
2582 case EHCI_DESCRIPTOR_FSTN:
2583 AssertFailed();
2584 break;
2585 }
2586
2587 ehciR3CalcTimerIntervals(pThis, pThisCC, pThisCC->uFrameRateDefault);
2588 RTCritSectLeave(&pThisCC->CritSect);
2589 RTSemEventMultiSignal(pThisCC->hSemEventFrame);
2590}
2591
2592/**
2593 * Worker for ehciR3RhXferError that handles the error case of
2594 * a URB made up of queue heads/descriptors
2595 *
2596 * @returns true if the URB should be retired.
2597 * @returns false if the URB should be retried.
2598 * @param pDevIns The device instance.
2599 * @param pThisCC EHCI instance data, ring-3 edition. (For stats.)
2600 * @param pUrb Pointer to the URB in question.
2601 */
2602static bool ehciR3RhXferErrorQH(PPDMDEVINS pDevIns, PEHCICC pThisCC, PVUSBURB pUrb)
2603{
2604 EHCI_QHD qhd;
2605 EHCI_QTD qtd;
2606
2607 /* Read the whole QHD & QTD */
2608 ehciR3ReadQHD(pDevIns, pUrb->pHci->EdAddr, &qhd);
2609 Assert(pUrb->paTds[0].TdAddr == ((RTGCPHYS)qhd.CurrQTD.Pointer << EHCI_TD_PTR_SHIFT));
2610 ehciR3ReadQTD(pDevIns, (RTGCPHYS)qhd.CurrQTD.Pointer << EHCI_TD_PTR_SHIFT, &qtd);
2611
2612 /*
2613 * Check if the TDs still are valid.
2614 * This will make sure the TdCopy is up to date.
2615 */
2616 /** @todo IMPORTANT! we must check if the ED is still valid at this point!!! */
2617 if (ehciR3QhdHasUrbBeenCanceled(pThisCC, pUrb, &qhd, &qtd))
2618 {
2619 Log(("%s: ehciR3RhXferError: TdAddr0=%RGp canceled!\n", pUrb->pszDesc, pUrb->paTds[0].TdAddr));
2620 return true;
2621 }
2622 return true;
2623}
2624
2625/**
2626 * Handle transfer errors.
2627 *
2628 * VUSB calls this when a transfer attempt failed. This function will respond
2629 * indicating whether to retry or complete the URB with failure.
2630 *
2631 * @returns true if the URB should be retired.
2632 * @returns false if the URB should be retried.
2633 * @param pInterface Pointer to EHCI::ROOTHUB::IRhPort.
2634 * @param pUrb Pointer to the URB in question.
2635 */
2636static DECLCALLBACK(bool) ehciR3RhXferError(PVUSBIROOTHUBPORT pInterface, PVUSBURB pUrb)
2637{
2638 PEHCICC pThisCC = RT_FROM_MEMBER(pInterface, EHCICC, RootHub.IRhPort);
2639 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2640 bool fRetire = false;
2641
2642 RTCritSectEnter(&pThisCC->CritSect);
2643 /*
2644 * Don't retry on stall.
2645 */
2646 if (pUrb->enmStatus == VUSBSTATUS_STALL)
2647 {
2648 Log2(("%s: ehciR3RhXferError: STALL, giving up.\n", pUrb->pszDesc));
2649 fRetire = true;
2650 }
2651 else
2652 {
2653 switch (pUrb->paTds[0].TdType)
2654 {
2655 case EHCI_DESCRIPTOR_QH:
2656 {
2657 fRetire = ehciR3RhXferErrorQH(pDevIns, pThisCC, pUrb);
2658 break;
2659 }
2660
2661 /*
2662 * Isochronous URBs can't be retried.
2663 */
2664 case EHCI_DESCRIPTOR_ITD:
2665 case EHCI_DESCRIPTOR_SITD:
2666 case EHCI_DESCRIPTOR_FSTN:
2667 default:
2668 fRetire = true;
2669 break;
2670 }
2671 }
2672
2673 RTCritSectLeave(&pThisCC->CritSect);
2674 return fRetire;
2675}
2676
2677/**
2678 * A worker for ehciR3ServiceQTD which submits the specified TD.
2679 *
2680 * @returns true on success.
2681 * @returns false on failure to submit.
2682 */
2683static bool ehciR3SubmitQTD(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC, RTGCPHYS GCPhysQHD,
2684 PEHCI_QHD pQhd, RTGCPHYS GCPhysQTD, PEHCI_QTD pQtd, const unsigned iFrame)
2685{
2686 /*
2687 * Determine the endpoint direction.
2688 */
2689 VUSBDIRECTION enmDir;
2690 switch(pQtd->Token.Bits.PID)
2691 {
2692 case EHCI_QTD_PID_OUT:
2693 enmDir = VUSBDIRECTION_OUT;
2694 break;
2695 case EHCI_QTD_PID_IN:
2696 enmDir = VUSBDIRECTION_IN;
2697 break;
2698 case EHCI_QTD_PID_SETUP:
2699 enmDir = VUSBDIRECTION_SETUP;
2700 break;
2701 default:
2702 return false;
2703 }
2704
2705 VUSBXFERTYPE enmType;
2706
2707 enmType = ehciR3QueryTransferType(pQhd);
2708
2709 pThisCC->fIdle = false; /* Mark as active */
2710
2711 /*
2712 * Allocate and initialize the URB.
2713 */
2714 PVUSBURB pUrb = VUSBIRhNewUrb(pThisCC->RootHub.pIRhConn, pQhd->Characteristics.DeviceAddress, VUSB_DEVICE_PORT_INVALID,
2715 enmType, enmDir, pQtd->Token.Bits.Length, 1, NULL);
2716 if (!pUrb)
2717 /* retry later... */
2718 return false;
2719
2720 pUrb->EndPt = pQhd->Characteristics.EndPt;
2721 pUrb->fShortNotOk = (enmDir != VUSBDIRECTION_IN); /** @todo ??? */
2722 pUrb->enmStatus = VUSBSTATUS_OK;
2723 pUrb->pHci->cTds = 1;
2724 pUrb->pHci->EdAddr = GCPhysQHD;
2725 pUrb->pHci->fUnlinked = false;
2726 pUrb->pHci->u32FrameNo = iFrame;
2727 pUrb->paTds[0].TdAddr = GCPhysQTD;
2728 pUrb->paTds[0].TdType = EHCI_DESCRIPTOR_QH;
2729 AssertCompile(sizeof(pUrb->paTds[0].TdCopy) >= sizeof(*pQtd));
2730 memcpy(pUrb->paTds[0].TdCopy, pQtd, sizeof(*pQtd));
2731#if 0 /* color the data */
2732 memset(pUrb->abData, 0xfe, cbTotal);
2733#endif
2734
2735 /* copy the data */
2736 if ( pQtd->Token.Bits.Length
2737 && enmDir != VUSBDIRECTION_IN)
2738 {
2739 unsigned curOffset = 0;
2740 unsigned cbTransfer = pQtd->Token.Bits.Length;
2741
2742 for (unsigned i=pQtd->Token.Bits.CurrentPage;i<RT_ELEMENTS(pQtd->Buffer.Buffer);i++)
2743 {
2744 RTGCPHYS GCPhysBuf;
2745 unsigned cbCurTransfer;
2746
2747 GCPhysBuf = (RTGCPHYS)pQtd->Buffer.Buffer[i].Pointer << EHCI_BUFFER_PTR_SHIFT;
2748 if (i == 0)
2749 GCPhysBuf += pQtd->Buffer.Offset.Offset;
2750
2751 cbCurTransfer = GUEST_PAGE_SIZE - (GCPhysBuf & GUEST_PAGE_OFFSET_MASK);
2752 cbCurTransfer = RT_MIN(cbCurTransfer, cbTransfer);
2753
2754 ehciPhysRead(pDevIns, GCPhysBuf, &pUrb->abData[curOffset], cbCurTransfer);
2755
2756 Log3Func(("packet data:\n"
2757 "%.*Rhxd\n",
2758 cbCurTransfer, &pUrb->abData[curOffset]));
2759
2760 curOffset += cbCurTransfer;
2761 cbTransfer -= cbCurTransfer;
2762
2763 if (cbTransfer == 0)
2764 break;
2765 Assert(cbTransfer < pQtd->Token.Bits.Length);
2766 }
2767 }
2768
2769 /*
2770 * Submit the URB.
2771 */
2772 ehciR3InFlightAddUrb(pThis, pThisCC, pUrb);
2773 Log(("%s: ehciSubmitQtd: QtdAddr=%RGp GCPhysQHD=%RGp cbData=%#x\n",
2774 pUrb->pszDesc, GCPhysQTD, GCPhysQHD, pUrb->cbData));
2775 RTCritSectLeave(&pThisCC->CritSect);
2776 int rc = VUSBIRhSubmitUrb(pThisCC->RootHub.pIRhConn, pUrb, &pThisCC->RootHub.Led);
2777 RTCritSectEnter(&pThisCC->CritSect);
2778 if (RT_SUCCESS(rc))
2779 return true;
2780
2781 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
2782 LogFunc(("failed GCPhysQtd=%RGp GCPhysQHD=%RGp pUrb=%p!!\n",
2783 GCPhysQTD, GCPhysQHD, pUrb));
2784 ehciR3InFlightRemove(pThis, pThisCC, GCPhysQTD);
2785
2786 /* Also mark the QH as halted and inactive and write back the changes. */
2787 pQhd->Overlay.OrgQTD.Token.Bits.Active = 0;
2788 pQhd->Overlay.OrgQTD.Token.Bits.Halted = 1;
2789 ehciR3UpdateQHD(pDevIns, GCPhysQHD, pQhd);
2790 return false;
2791}
2792
2793/**
2794 * A worker for ehciR3ServiceITD which submits the specified TD.
2795 *
2796 * @returns true on success.
2797 * @returns false on failure to submit.
2798 */
2799static bool ehciR3SubmitITD(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC,
2800 PEHCI_ITD_PAD pItd, RTGCPHYS ITdAddr, const unsigned iFrame)
2801{
2802 /*
2803 * Determine the endpoint direction.
2804 */
2805 VUSBDIRECTION enmDir;
2806 if(pItd->Buffer.Misc.DirectionIn)
2807 enmDir = VUSBDIRECTION_IN;
2808 else
2809 enmDir = VUSBDIRECTION_OUT;
2810
2811 /*
2812 * Extract the packet sizes and calc the total URB size.
2813 */
2814 struct
2815 {
2816 uint16_t cb;
2817 } aPkts[EHCI_NUM_ITD_TRANSACTIONS];
2818
2819 unsigned cPackets = 0;
2820 uint32_t cbTotal = 0;
2821 for (unsigned i=0;i<RT_ELEMENTS(pItd->Transaction);i++)
2822 {
2823 if (pItd->Transaction[i].Active)
2824 {
2825 aPkts[cPackets].cb = pItd->Transaction[i].Length;
2826 cbTotal += pItd->Transaction[i].Length;
2827 cPackets++;
2828 }
2829 }
2830 Assert(cbTotal <= 24576);
2831
2832 pThisCC->fIdle = false; /* Mark as active */
2833
2834 /*
2835 * Allocate and initialize the URB.
2836 */
2837 PVUSBURB pUrb = VUSBIRhNewUrb(pThisCC->RootHub.pIRhConn, pItd->Buffer.Misc.DeviceAddress, VUSB_DEVICE_PORT_INVALID,
2838 VUSBXFERTYPE_ISOC, enmDir, cbTotal, 1, NULL);
2839 if (!pUrb)
2840 /* retry later... */
2841 return false;
2842
2843 pUrb->EndPt = pItd->Buffer.Misc.EndPt;
2844 pUrb->fShortNotOk = false;
2845 pUrb->enmStatus = VUSBSTATUS_OK;
2846 pUrb->pHci->cTds = 1;
2847 pUrb->pHci->EdAddr = ITdAddr;
2848 pUrb->pHci->fUnlinked = false;
2849 pUrb->pHci->u32FrameNo = iFrame;
2850 pUrb->paTds[0].TdAddr = ITdAddr;
2851 pUrb->paTds[0].TdType = EHCI_DESCRIPTOR_ITD;
2852 AssertCompile(sizeof(pUrb->paTds[0].TdCopy) >= sizeof(*pItd));
2853 memcpy(pUrb->paTds[0].TdCopy, pItd, sizeof(*pItd));
2854#if 0 /* color the data */
2855 memset(pUrb->abData, 0xfe, cbTotal);
2856#endif
2857
2858 /* copy the data */
2859 if ( cbTotal
2860 && enmDir != VUSBDIRECTION_IN)
2861 {
2862 unsigned curOffset = 0;
2863
2864 for (unsigned i=0;i<RT_ELEMENTS(pItd->Transaction);i++)
2865 {
2866 RTGCPHYS GCPhysBuf;
2867
2868 if (pItd->Transaction[i].Active)
2869 {
2870 const unsigned pg = pItd->Transaction[i].PG;
2871
2872 GCPhysBuf = (RTGCPHYS)pItd->Buffer.Buffer[pg].Pointer << EHCI_BUFFER_PTR_SHIFT;
2873 GCPhysBuf += pItd->Transaction[i].Offset;
2874
2875 /* If the transfer would cross page boundary, use the next sequential PG pointer
2876 * for the second part (section 4.7.1).
2877 */
2878 if (pItd->Transaction[i].Offset + pItd->Transaction[i].Length > GUEST_PAGE_SIZE)
2879 {
2880 unsigned cb1 = GUEST_PAGE_SIZE - pItd->Transaction[i].Offset;
2881 unsigned cb2 = pItd->Transaction[i].Length - cb1;
2882
2883 ehciPhysRead(pDevIns, GCPhysBuf, &pUrb->abData[curOffset], cb1);
2884 if ((pg + 1) >= EHCI_NUM_ITD_PAGES)
2885 LogRelMax(10, ("EHCI: Crossing to nonstandard page %d in iTD at %RGp on submit.\n", pg + 1, pUrb->paTds[0].TdAddr));
2886
2887 GCPhysBuf = (RTGCPHYS)pItd->Buffer.Buffer[pg + 1].Pointer << EHCI_BUFFER_PTR_SHIFT;
2888 ehciPhysRead(pDevIns, GCPhysBuf, &pUrb->abData[curOffset + cb1], cb2);
2889 }
2890 else
2891 ehciPhysRead(pDevIns, GCPhysBuf, &pUrb->abData[curOffset], pItd->Transaction[i].Length);
2892
2893 curOffset += pItd->Transaction[i].Length;
2894 }
2895 }
2896 }
2897
2898 /* setup the packets */
2899 pUrb->cIsocPkts = cPackets;
2900 unsigned off = 0;
2901 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
2902 {
2903 pUrb->aIsocPkts[i].enmStatus = VUSBSTATUS_NOT_ACCESSED;
2904 pUrb->aIsocPkts[i].off = off;
2905 off += pUrb->aIsocPkts[i].cb = aPkts[i].cb;
2906 }
2907 Assert(off == cbTotal);
2908
2909 /*
2910 * Submit the URB.
2911 */
2912 ehciR3InFlightAddUrb(pThis, pThisCC, pUrb);
2913 Log(("%s: ehciR3SubmitITD: cbData=%#x cIsocPkts=%d TdAddr=%RGp (%#x)\n",
2914 pUrb->pszDesc, pUrb->cbData, pUrb->cIsocPkts, ITdAddr, iFrame));
2915 RTCritSectLeave(&pThisCC->CritSect);
2916 int rc = VUSBIRhSubmitUrb(pThisCC->RootHub.pIRhConn, pUrb, &pThisCC->RootHub.Led);
2917 RTCritSectEnter(&pThisCC->CritSect);
2918 if (RT_SUCCESS(rc))
2919 return true;
2920
2921 /* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
2922 LogFunc(("failed pUrb=%p cbData=%#x cTds=%d ITdAddr0=%RGp - rc=%Rrc\n",
2923 pUrb, cbTotal, 1, ITdAddr, rc));
2924 ehciR3InFlightRemove(pThis, pThisCC, ITdAddr);
2925 return false;
2926}
2927
2928
2929/**
2930 * Services an ITD list (only for high-speed isochronous endpoints; all others use queues)
2931 *
2932 * An ITD can contain up to 8 transactions, which are all processed within a single frame.
2933 * Note that FRINDEX includes the micro-frame number, but only bits [12:3] are used as an
2934 * index into the periodic frame list (see 4.7.1).
2935 */
2936static void ehciR3ServiceITD(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC,
2937 RTGCPHYS GCPhys, EHCI_SERVICE_TYPE enmServiceType, const unsigned iFrame)
2938{
2939 RT_NOREF(enmServiceType);
2940 bool fAnyActive = false;
2941 EHCI_ITD_PAD PaddedItd;
2942 PEHCI_ITD_PAD pItd = &PaddedItd;
2943
2944 if (ehciR3IsTdInFlight(pThisCC, GCPhys))
2945 return;
2946
2947 /* Read the whole ITD */
2948 ehciR3ReadItd(pDevIns, GCPhys, &PaddedItd);
2949
2950 Log2((" ITD: %RGp={Addr=%x EndPt=%x Dir=%s MaxSize=%x Mult=%d}\n", GCPhys, pItd->Buffer.Misc.DeviceAddress, pItd->Buffer.Misc.EndPt, (pItd->Buffer.Misc.DirectionIn) ? "in" : "out", pItd->Buffer.Misc.MaxPacket, pItd->Buffer.Misc.Multi));
2951
2952 /* Some basic checks */
2953 for (unsigned i = 0; i < RT_ELEMENTS(pItd->Transaction); i++)
2954 {
2955 if (pItd->Transaction[i].Active)
2956 {
2957 fAnyActive = true;
2958 if (pItd->Transaction[i].PG >= EHCI_NUM_ITD_PAGES)
2959 {
2960 /* Using out of range PG value (7) yields undefined behavior. We will attempt
2961 * the last page below 4GB (which is ROM, not writable).
2962 */
2963 LogRelMax(10, ("EHCI: Nonstandard page value %d in iTD at %RGp.\n", pItd->Transaction[i].PG, (RTGCPHYS)GCPhys));
2964 }
2965
2966 Log2((" T%d Len=%x Offset=%x PG=%d IOC=%d Buffer=%x\n", i, pItd->Transaction[i].Length, pItd->Transaction[i].Offset, pItd->Transaction[i].PG, pItd->Transaction[i].IOC,
2967 pItd->Buffer.Buffer[pItd->Transaction[i].PG].Pointer));
2968 }
2969 }
2970 /* We can't service one transaction every 125 usec, so we'll handle all 8 of them at once. */
2971 if (fAnyActive)
2972 ehciR3SubmitITD(pDevIns, pThis, pThisCC, pItd, GCPhys, iFrame);
2973 else
2974 Log2((" ITD not active, skipping.\n"));
2975}
2976
2977/**
2978 * Services an SITD list
2979 */
2980static void ehciR3ServiceSITD(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC,
2981 RTGCPHYS GCPhys, EHCI_SERVICE_TYPE enmServiceType, const unsigned iFrame)
2982{
2983 RT_NOREF(pThis, pThisCC, enmServiceType, iFrame);
2984
2985 /* Read the whole SITD */
2986 EHCI_SITD sitd;
2987 ehciR3ReadSitd(pDevIns, GCPhys, &sitd);
2988
2989 Log2((" SITD: %RGp={Addr=%x EndPt=%x Dir=%s MaxSize=%x}\n", GCPhys, sitd.Address.DeviceAddress, sitd.Address.EndPt, (sitd.Address.DirectionIn) ? "in" : "out", sitd.Transfer.Length));
2990
2991 if (sitd.Transfer.Active)
2992 AssertMsgFailed(("SITD lists not implemented; active SITD should never occur!\n"));
2993 else
2994 Log2((" SITD not active, skipping.\n"));
2995}
2996
2997/**
2998 * Copies the currently active QTD to the QH overlay area
2999 */
3000static void ehciR3QHSetupOverlay(PPDMDEVINS pDevIns, PEHCI_QHD pQhd, RTGCPHYS GCPhysQHD, PEHCI_QTD pQtd, RTGCPHYS GCPhysQTD)
3001{
3002 bool fDataToggle = pQhd->Overlay.OrgQTD.Token.Bits.DataToggle;
3003
3004 Assert(pQtd->Token.Bits.Active);
3005
3006 Log2Func(("current pointer %RGp old %RGp\n", GCPhysQTD, ((RTGCPHYS)pQhd->CurrQTD.Pointer << EHCI_TD_PTR_SHIFT)));
3007 pQhd->CurrQTD.Pointer = GCPhysQTD >> EHCI_TD_PTR_SHIFT;
3008 pQhd->CurrQTD.Reserved = 0;
3009 pQhd->Overlay.OrgQTD = *pQtd;
3010 /* All fields except those below are copied from the QTD; see 4.10.2 */
3011 if (pQhd->Characteristics.DataToggle)
3012 pQhd->Overlay.OrgQTD.Token.Bits.DataToggle = fDataToggle; /* Preserve data toggle bit in the queue head */
3013
3014 pQhd->Overlay.Status.Buffer1.CProgMask = 0;
3015 pQhd->Overlay.Status.Buffer2.FrameTag = 0;
3016 pQhd->Overlay.Status.AltNextQTD.NakCnt = pQhd->Characteristics.NakCountReload;
3017 /* Note: ping state not changed if it's a high-speed device */
3018
3019 /* Save the current QTD to the overlay area */
3020 ehciR3UpdateQHD(pDevIns, GCPhysQHD, pQhd);
3021}
3022
3023/**
3024 * Updates the currently active QTD to the QH overlay area
3025 */
3026static void ehciR3QHUpdateOverlay(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC,
3027 PEHCI_QHD pQhd, RTGCPHYS GCPhysQHD, PEHCI_QTD pQtd)
3028{
3029 Assert(!pQtd->Token.Bits.Active);
3030 pQhd->Overlay.OrgQTD = *pQtd;
3031 if (!pQhd->Overlay.OrgQTD.Next.Terminate)
3032 {
3033 EHCI_QTD qtdNext;
3034 RTGCPHYS GCPhysNextQTD = (RTGCPHYS)pQhd->Overlay.OrgQTD.Next.Pointer << EHCI_TD_PTR_SHIFT;
3035
3036 if (ehciR3IsTdInFlight(pThisCC, GCPhysNextQTD))
3037 {
3038 /* Read the whole QTD */
3039 ehciR3ReadQTD(pDevIns, GCPhysNextQTD, &qtdNext);
3040 if (qtdNext.Token.Bits.Active)
3041 {
3042 ehciR3QHSetupOverlay(pDevIns, pQhd, GCPhysQHD, &qtdNext, GCPhysNextQTD);
3043 return;
3044 }
3045 else
3046 {
3047 /* Td has been cancelled! */
3048 LogFunc(("in-flight qTD %RGp has been cancelled! (active=%d T=%d)\n", GCPhysNextQTD, qtdNext.Token.Bits.Active, pQhd->Overlay.OrgQTD.Next.Terminate));
3049 /** @todo we don't properly cancel the URB; it will remain active on the host.... */
3050 ehciR3InFlightRemove(pThis, pThisCC, GCPhysNextQTD);
3051 }
3052 }
3053 }
3054 /* Save the current QTD to the overlay area. */
3055 ehciR3UpdateQHD(pDevIns, GCPhysQHD, pQhd);
3056}
3057
3058/**
3059 * Services a QTD list
3060 */
3061static RTGCPHYS ehciR3ServiceQTD(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC, PEHCI_QHD pQhd, RTGCPHYS GCPhysQHD,
3062 RTGCPHYS GCPhysQTD, EHCI_SERVICE_TYPE enmServiceType, const unsigned iFrame)
3063{
3064 EHCI_QTD qtd;
3065
3066 /* Read the whole QTD */
3067 ehciR3ReadQTD(pDevIns, GCPhysQTD, &qtd);
3068
3069 if (qtd.Token.Bits.Active)
3070 {
3071 if (!ehciR3IsTdInFlight(pThisCC, GCPhysQTD))
3072 {
3073 /* Don't queue more than one non-bulk transfer at a time */
3074 if ( ehciR3QueryTransferType(pQhd) != VUSBXFERTYPE_BULK
3075 && pQhd->Overlay.OrgQTD.Token.Bits.Active)
3076 return 0;
3077
3078 Log2((" Length=%x IOC=%d DT=%d PID=%s}\n", qtd.Token.Bits.Length, qtd.Token.Bits.IOC, qtd.Token.Bits.DataToggle, ehciPID2Str(qtd.Token.Bits.PID)));
3079 if ( !pQhd->Overlay.OrgQTD.Token.Bits.Active
3080 || GCPhysQTD == (RTGCPHYS)pQhd->CurrQTD.Pointer << EHCI_TD_PTR_SHIFT)
3081 ehciR3QHSetupOverlay(pDevIns, pQhd, GCPhysQHD, &qtd, GCPhysQTD);
3082 else
3083 Log2Func(("transfer %RGp in progress -> don't update the overlay\n", (RTGCPHYS)pQhd->CurrQTD.Pointer << EHCI_TD_PTR_SHIFT));
3084
3085 ehciR3SubmitQTD(pDevIns, pThis, pThisCC, GCPhysQHD, pQhd, GCPhysQTD, &qtd, iFrame);
3086
3087 /* Set the Reclamation bit in USBSTS (4.10.3) */
3088 if (enmServiceType == EHCI_SERVICE_ASYNC)
3089 {
3090 Log2Func(("activity detected, set EHCI_STATUS_RECLAMATION\n"));
3091 ASMAtomicOrU32(&pThis->intr_status, EHCI_STATUS_RECLAMATION);
3092 }
3093
3094 /* Reread the whole QTD; it might have been completed already and therefore changed */
3095 ehciR3ReadQTD(pDevIns, GCPhysQTD, &qtd);
3096 }
3097 /* Table 4-10: any transfer with zero size: queue only one */
3098 if (qtd.Token.Bits.Length == 0)
3099 {
3100 LogFunc(("queue only one: transfer with zero size\n"));
3101 return 0;
3102 }
3103
3104 /* We can't queue more than one TD if we can't decide here and now which TD we should take next */
3105 if ( qtd.Token.Bits.Active /* only check if this urb is in-flight */
3106 && qtd.Token.Bits.PID == EHCI_QTD_PID_IN
3107 && !qtd.AltNext.Terminate
3108 && !qtd.Next.Terminate
3109 && qtd.Next.Pointer != qtd.AltNext.Pointer)
3110 {
3111 Log2Func(("Can't decide which pointer to take next; don't queue more than one!\n"));
3112 return 0;
3113 }
3114 }
3115 else
3116 {
3117 Log2((" Not active}\n"));
3118 return 0;
3119 }
3120
3121 /* If the 'Bytes to Transfer' field is not zero and the T-bit in the AltNext pointer is zero, then use this pointer. (4.10.2) */
3122 if ( !qtd.Token.Bits.Active /* only check if no urbs are in-flight */
3123 && qtd.Token.Bits.PID == EHCI_QTD_PID_IN /* short packets only apply to incoming tds */
3124 && !qtd.AltNext.Terminate
3125 && qtd.Token.Bits.Length)
3126 {
3127 Assert(qtd.AltNext.Pointer);
3128 Log2(("Taking alternate pointer %RGp\n", (RTGCPHYS)(qtd.AltNext.Pointer << EHCI_TD_PTR_SHIFT)));
3129 return (RTGCPHYS)qtd.AltNext.Pointer << EHCI_TD_PTR_SHIFT;
3130 }
3131 else
3132 {
3133 Assert(qtd.Next.Pointer || qtd.Next.Terminate);
3134 if (qtd.Next.Terminate || !qtd.Next.Pointer)
3135 return 0;
3136 return (RTGCPHYS)qtd.Next.Pointer << EHCI_TD_PTR_SHIFT;
3137 }
3138}
3139
3140/**
3141 * Services a QHD list
3142 */
3143static bool ehciR3ServiceQHD(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC,
3144 RTGCPHYS GCPhys, EHCI_SERVICE_TYPE enmServiceType, const unsigned iFrame)
3145{
3146 EHCI_QHD qhd;
3147
3148 Log2Func(("%RGp={", GCPhys));
3149
3150 /* Read the whole QHD */ /** @todo reading too much */
3151 ehciR3ReadQHD(pDevIns, GCPhys, &qhd);
3152
3153 /* Only interrupt qHDs should be linked from the periodic list; the S-mask field description
3154 * in table 3-20 clearly says a zero S-mask on the periodic list yields undefined results. In reality,
3155 * the Windows HCD links dummy qHDs at the start of the interrupt queue and these have an empty S-mask.
3156 * If we're servicing the periodic list, check the S-mask first; that takes care of the dummy qHDs.
3157 */
3158 if (enmServiceType == EHCI_SERVICE_PERIODIC)
3159 {
3160 // If iFrame was a micro-frame number, we should check the S-mask against it. But
3161 // we're processing all micro-frames at once, so we'll look at any qHD with non-zero S-mask
3162 if (qhd.Caps.SMask == 0) {
3163 Log2Func(("periodic qHD not scheduled for current frame -> next\n"));
3164 return true;
3165 }
3166 else
3167 Log2Func(("periodic qHD scheduled for current frame, processing\n"));
3168 }
3169 else
3170 {
3171 Assert(enmServiceType == EHCI_SERVICE_ASYNC);
3172 /* Empty schedule detection (4.10.1), for async schedule only */
3173 if (qhd.Characteristics.HeadReclamation) /* H-bit set but not an interrupt qHD */
3174 {
3175 if (pThis->intr_status & EHCI_STATUS_RECLAMATION)
3176 {
3177 Log2Func(("clear EHCI_STATUS_RECLAMATION\n"));
3178 ASMAtomicAndU32(&pThis->intr_status, ~EHCI_STATUS_RECLAMATION);
3179 }
3180 else
3181 {
3182 Log2Func(("empty schedule -> bail out\n"));
3183 pThis->fAsyncTraversalTimerActive = true;
3184 return false; /** stop traversing the list */
3185 }
3186 }
3187 }
3188
3189 /* no active qTD here or in the next queue element -> skip to next horizontal pointer (Figure 4.14 & 4.10.2) */
3190 if ( !qhd.Overlay.OrgQTD.Token.Bits.Active
3191 && qhd.Characteristics.InActiveNext)
3192 {
3193 Log2Func(("skip to next pointer (active)\n"));
3194 return true;
3195 }
3196 /* we are ignoring the Inactivate on Next Transaction bit; only applies to periodic lists & low or full speed devices (table 3.9) */
3197
3198 /** We are not allowed to handle multiple TDs unless async park is enabled (and only for high-speed devices), but we can cheat a bit. */
3199 unsigned PMCount = 1;
3200 if ( (pThis->cmd & EHCI_CMD_ASYNC_SCHED_PARK_ENABLE)
3201 && qhd.Characteristics.EndPtSpeed == EHCI_QHD_EPT_SPEED_HIGH
3202 && enmServiceType == EHCI_SERVICE_ASYNC)
3203 {
3204 PMCount = (pThis->cmd & EHCI_CMD_ASYNC_SCHED_PARK_MODE_COUNT_MASK) >> EHCI_CMD_ASYNC_SCHED_PARK_MODE_COUNT_SHIFT;
3205 Log2Func(("PM Count=%d\n", PMCount));
3206
3207 /* We will attempt to queue a bit more if we're allowed to queue more than one TD. */
3208 if (PMCount != 1)
3209 PMCount = 16;
3210 }
3211
3212 /* Queue as many transfer descriptors as possible */
3213 RTGCPHYS GCPhysQTD;
3214 if (qhd.Overlay.OrgQTD.Token.Bits.Active)
3215 {
3216 Assert(ehciR3IsTdInFlight(pThisCC, (RTGCPHYS)qhd.CurrQTD.Pointer << EHCI_TD_PTR_SHIFT));
3217 GCPhysQTD = (RTGCPHYS)qhd.CurrQTD.Pointer << EHCI_TD_PTR_SHIFT;
3218 }
3219 else
3220 {
3221 /* If the 'Bytes to Transfer' field is not zero and the T-bit in the AltNext pointer is zero, then use this pointer. (4.10.2) */
3222 if ( !qhd.Overlay.OrgQTD.AltNext.Terminate
3223 && qhd.Overlay.OrgQTD.Token.Bits.Length)
3224 {
3225 Assert(qhd.Overlay.OrgQTD.AltNext.Pointer);
3226 Log2(("Taking alternate pointer %RGp\n", (RTGCPHYS)qhd.Overlay.OrgQTD.AltNext.Pointer << EHCI_TD_PTR_SHIFT));
3227 GCPhysQTD = (RTGCPHYS)qhd.Overlay.OrgQTD.AltNext.Pointer << EHCI_TD_PTR_SHIFT;
3228 }
3229 else
3230 {
3231 Assert(qhd.Overlay.OrgQTD.Next.Pointer || qhd.Overlay.OrgQTD.Next.Terminate || qhd.Overlay.OrgQTD.Token.Bits.Halted);
3232 if (qhd.Overlay.OrgQTD.Next.Terminate || !qhd.Overlay.OrgQTD.Next.Pointer || qhd.Overlay.OrgQTD.Token.Bits.Halted)
3233 GCPhysQTD = 0;
3234 else
3235 GCPhysQTD = (RTGCPHYS)qhd.Overlay.OrgQTD.Next.Pointer << EHCI_TD_PTR_SHIFT;
3236 }
3237 }
3238
3239 while (GCPhysQTD && PMCount--)
3240 {
3241 GCPhysQTD = ehciR3ServiceQTD(pDevIns, pThis, pThisCC, &qhd, GCPhys, GCPhysQTD, enmServiceType, iFrame);
3242
3243 /* Reread the whole QHD; urb submit can call us right back which causes QH changes */ /** @todo reading too much */
3244 ehciR3ReadQHD(pDevIns, GCPhys, &qhd);
3245 }
3246 return true;
3247}
3248
3249/**
3250 * Services a FSTN list
3251 */
3252static void ehciR3ServiceFSTN(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC,
3253 RTGCPHYS GCPhys, EHCI_SERVICE_TYPE enmServiceType, const unsigned iFrame)
3254{
3255 RT_NOREF(pDevIns, pThis, pThisCC, GCPhys, enmServiceType, iFrame);
3256 AssertMsgFailed(("FSTN lists not implemented; should never occur!\n"));
3257}
3258
3259/**
3260 * Services the async list.
3261 *
3262 * The async list has complex URB assembling, but that's taken
3263 * care of at VUSB level (unlike the other transfer types).
3264 */
3265static void ehciR3ServiceAsyncList(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC, const unsigned iFrame)
3266{
3267 RTGCPHYS GCPhysHead = pThis->async_list_base;
3268 RTGCPHYS GCPhys = GCPhysHead;
3269 EHCI_TD_PTR ptr;
3270 unsigned cIterations = 0;
3271
3272 Assert(!(pThis->async_list_base & 0x1f));
3273 Assert(pThis->cmd & EHCI_CMD_ASYNC_SCHED_ENABLE);
3274 Assert(pThis->cmd & EHCI_CMD_RUN);
3275
3276 Log2Func(("%RGp\n", GCPhysHead));
3277#ifdef LOG_ENABLED
3278 ehciR3DumpQH(pDevIns, GCPhysHead, true);
3279#endif
3280
3281 /* Signal the async advance doorbell interrupt (if required) */
3282 if ( (pThis->cmd & EHCI_CMD_INT_ON_ADVANCE_DOORBELL))
3283// && !pThis->cInFlight)
3284 ehciR3SetInterrupt(pDevIns, pThis, EHCI_STATUS_INT_ON_ASYNC_ADV);
3285
3286 /* Process the list of qHDs */
3287 for (;;)
3288 {
3289 /* Process the qHD */
3290 if (!ehciR3ServiceQHD(pDevIns, pThis, pThisCC, GCPhys, EHCI_SERVICE_ASYNC, iFrame))
3291 break;
3292
3293 /* Read the next pointer */
3294 RTGCPHYS GCPhysLast = GCPhys;
3295 ehciR3ReadTDPtr(pDevIns, GCPhys, &ptr);
3296
3297 /* Detect obvious loops. */
3298 if (GCPhys == ((RTGCPHYS)ptr.Pointer << EHCI_TD_PTR_SHIFT))
3299 break;
3300
3301 /* Technically a zero address could be valid, but that's extremely unlikely! */
3302 Assert(ptr.Pointer || ptr.Terminate);
3303 if (ptr.Terminate || !ptr.Pointer)
3304 break;
3305
3306 /* Not clear what we should do if this *is* something other than a qHD. */
3307 AssertMsg(ptr.Type == EHCI_DESCRIPTOR_QH, ("Unexpected pointer to type %d\n", ptr.Type));
3308 if (ptr.Type != EHCI_DESCRIPTOR_QH)
3309 break;
3310
3311 /* If we ran too many iterations, the list must be looping in on itself.
3312 * On a real controller loops wouldn't be fatal, as it will eventually
3313 * run out of time in the micro-frame.
3314 */
3315 AssertMsgBreak(++cIterations < 128, ("Too many iterations, exiting\n"));
3316
3317 /* next */
3318 GCPhys = (RTGCPHYS)ptr.Pointer << EHCI_TD_PTR_SHIFT;
3319 Assert(!(GCPhys & 0x1f));
3320 if ( GCPhys == GCPhysHead
3321 || GCPhys == GCPhysLast)
3322 break; /* break the loop */
3323 }
3324
3325#ifdef LOG_ENABLED
3326 if (g_fLogControlEPs)
3327 ehciR3DumpQH(pDevIns, GCPhysHead, true);
3328#endif
3329}
3330
3331
3332/**
3333 * Services the periodic list.
3334 *
3335 * On the interrupt portion of the periodic list we must reassemble URBs from multiple
3336 * TDs using heuristics derived from USB tracing done in the guests and guest source
3337 * code (when available).
3338 */
3339static void ehciR3ServicePeriodicList(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC, const unsigned iFrame)
3340{
3341 Assert(pThis->cmd & EHCI_CMD_PERIODIC_SCHED_ENABLE);
3342
3343#ifdef LOG_ENABLED
3344 RTGCPHYS pFramePtrHead = 0;
3345 if (g_fLogInterruptEPs)
3346 {
3347 RTGCPHYS pFramePtr = pThis->periodic_list_base + iFrame * sizeof(EHCI_FRAME_LIST_PTR);
3348
3349 pFramePtrHead = pFramePtr;
3350
3351 char sz[48];
3352 RTStrPrintf(sz, sizeof(sz), "Int%02x before", iFrame);
3353 ehciR3DumpPeriodicList(pDevIns, pFramePtrHead, sz, true);
3354 }
3355#endif
3356
3357 /*
3358 * Iterate the periodic list.
3359 */
3360 EHCI_FRAME_LIST_PTR FramePtr;
3361 RTGCPHYS GCPhys = pThis->periodic_list_base + iFrame * sizeof(FramePtr);
3362 unsigned iterations = 0;
3363
3364 ehciR3ReadFrameListPtr(pDevIns, GCPhys, &FramePtr);
3365 while (!FramePtr.Terminate && (pThis->cmd & EHCI_CMD_RUN))
3366 {
3367 GCPhys = (RTGCPHYS)FramePtr.FrameAddr << EHCI_FRAME_LIST_NEXTPTR_SHIFT;
3368 /* Process the descriptor based on its type. Note that on the periodic
3369 * list, HCDs may (and do) mix iTDs and qHDs more or less freely.
3370 */
3371 switch(FramePtr.Type)
3372 {
3373 case EHCI_DESCRIPTOR_ITD:
3374 ehciR3ServiceITD(pDevIns, pThis, pThisCC, GCPhys, EHCI_SERVICE_PERIODIC, iFrame);
3375 break;
3376 case EHCI_DESCRIPTOR_SITD:
3377 ehciR3ServiceSITD(pDevIns, pThis, pThisCC, GCPhys, EHCI_SERVICE_PERIODIC, iFrame);
3378 break;
3379 case EHCI_DESCRIPTOR_QH:
3380 ehciR3ServiceQHD(pDevIns, pThis, pThisCC, GCPhys, EHCI_SERVICE_PERIODIC, iFrame);
3381 break;
3382 case EHCI_DESCRIPTOR_FSTN:
3383 ehciR3ServiceFSTN(pDevIns, pThis, pThisCC, GCPhys, EHCI_SERVICE_PERIODIC, iFrame);
3384 break;
3385 }
3386
3387 /* If we ran too many iterations, the list must be looping in on itself.
3388 * On a real controller loops wouldn't be fatal, as it will eventually
3389 * run out of time in the micro-frame.
3390 */
3391 if (++iterations == 2048)
3392 {
3393 AssertMsgFailed(("ehciR3ServicePeriodicList: Too many iterations, exiting\n"));
3394 break;
3395 }
3396 /* Read the next link */
3397 ehciR3ReadFrameListPtr(pDevIns, GCPhys, &FramePtr);
3398
3399 /* Detect obvious loops. */
3400 if (GCPhys == ((RTGCPHYS)FramePtr.FrameAddr << EHCI_FRAME_LIST_NEXTPTR_SHIFT))
3401 break;
3402 }
3403
3404#ifdef LOG_ENABLED
3405 if (g_fLogInterruptEPs)
3406 {
3407 char sz[48];
3408 RTStrPrintf(sz, sizeof(sz), "Int%02x after ", iFrame);
3409 ehciR3DumpPeriodicList(pDevIns, pFramePtrHead, sz, true);
3410 }
3411#endif
3412}
3413
3414
3415/**
3416 * Calculate frame timer variables given a frame rate (1,000 Hz is the full speed).
3417 */
3418static void ehciR3CalcTimerIntervals(PEHCI pThis, PEHCICC pThisCC, uint32_t u32FrameRate)
3419{
3420 Assert(u32FrameRate <= EHCI_HARDWARE_TIMER_FREQ);
3421
3422 pThis->uFramesPerTimerCall = EHCI_HARDWARE_TIMER_FREQ / u32FrameRate;
3423 pThisCC->nsWait = RT_NS_1SEC / u32FrameRate;
3424 pThisCC->cTicksPerFrame = pThisCC->u64TimerHz / u32FrameRate;
3425 if (!pThisCC->cTicksPerFrame)
3426 pThisCC->cTicksPerFrame = 1;
3427 pThisCC->uFrameRate = u32FrameRate;
3428}
3429
3430/**
3431 * Generate a Start-Of-Frame event, and set a timer for End-Of-Frame.
3432 */
3433static void ehciR3StartOfFrame(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC)
3434{
3435 uint32_t uNewFrameRate = pThisCC->uFrameRate;
3436#ifdef LOG_ENABLED
3437 const uint32_t status_old = pThis->intr_status;
3438#endif
3439
3440 pThis->SofTime += pThisCC->cTicksPerFrame;
3441 unsigned iFrame = (pThis->frame_idx >> EHCI_FRINDEX_FRAME_INDEX_SHIFT) & EHCI_FRINDEX_FRAME_INDEX_MASK;
3442
3443 if (pThis->uIrqInterval < pThis->uFramesPerTimerCall)
3444 pThis->uIrqInterval = 0;
3445 else
3446 pThis->uIrqInterval -= pThis->uFramesPerTimerCall;
3447
3448 /* Empty async list detection halted the async schedule */
3449 if (pThis->fAsyncTraversalTimerActive)
3450 {
3451 /* Table 4.7 in 4.8.4.1 */
3452 Log2Func(("setting STATUS_RECLAMATION after empty list detection\n"));
3453 ASMAtomicOrU32(&pThis->intr_status, EHCI_STATUS_RECLAMATION);
3454 pThis->fAsyncTraversalTimerActive = false;
3455 }
3456
3457 /*
3458 * Periodic EPs (Isochronous & Interrupt)
3459 */
3460 if (pThis->cmd & EHCI_CMD_PERIODIC_SCHED_ENABLE)
3461 {
3462 int num_frames = RT_MAX(1, pThis->uFramesPerTimerCall >> EHCI_FRINDEX_FRAME_INDEX_SHIFT);
3463 Assert(num_frames > 0 && num_frames < 1024);
3464
3465 ASMAtomicOrU32(&pThis->intr_status, EHCI_STATUS_PERIOD_SCHED);
3466
3467 if (pThis->cmd & EHCI_CMD_RUN)
3468 {
3469 /* If we're running the frame timer at a reduced rate, we still need to process
3470 * all frames. Otherwise we risk completely missing newly scheduled periodic transfers.
3471 */
3472 for (int i = 0; i < num_frames; ++i)
3473 ehciR3ServicePeriodicList(pDevIns, pThis, pThisCC, (iFrame + i) & EHCI_FRINDEX_FRAME_INDEX_MASK);
3474 }
3475 }
3476 else
3477 ASMAtomicAndU32(&pThis->intr_status, ~EHCI_STATUS_PERIOD_SCHED);
3478
3479 /*
3480 * Async EPs (Control and Bulk)
3481 */
3482 if (pThis->cmd & EHCI_CMD_ASYNC_SCHED_ENABLE)
3483 {
3484 ASMAtomicOrU32(&pThis->intr_status, EHCI_STATUS_ASYNC_SCHED);
3485 if (pThis->cmd & EHCI_CMD_RUN)
3486 ehciR3ServiceAsyncList(pDevIns, pThis, pThisCC, iFrame);
3487 }
3488 else
3489 ASMAtomicAndU32(&pThis->intr_status, ~EHCI_STATUS_ASYNC_SCHED);
3490
3491 /*
3492 * ASSUME the guest won't be very upset if a TD is completed, retired and handed
3493 * back immediately. The idea is to be able to retire the data and/or status stages
3494 * of a control transfer together with the setup stage, thus saving a frame. This
3495 * behaviour is should be perfectly ok, since the setup (and maybe data) stages
3496 * have already taken at least one frame to complete.
3497 *
3498 * But, when implementing the first synchronous virtual USB devices, we'll have to
3499 * verify that the guest doesn't choke when having a TD returned in the same frame
3500 * as it was submitted.
3501 */
3502
3503#ifdef LOG_ENABLED
3504 if (pThis->intr_status ^ status_old)
3505 {
3506 uint32_t val = pThis->intr_status;
3507 uint32_t chg = val ^ status_old; NOREF(chg);
3508 Log2Func(("HcCommandStatus=%#010x: %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
3509 val,
3510 chg & RT_BIT(0) ? "*" : "", val & 1,
3511 chg & RT_BIT(1) ? "*" : "", (val >> 1) & 1,
3512 chg & RT_BIT(2) ? "*" : "", (val >> 2) & 1,
3513 chg & RT_BIT(3) ? "*" : "", (val >> 3) & 1,
3514 chg & (3<<16)? "*" : "", (val >> 16) & 3));
3515 }
3516#endif
3517
3518 /*
3519 * Adjust the frame timer interval based on idle detection.
3520 */
3521 if (pThisCC->fIdle)
3522 {
3523 pThisCC->cIdleCycles++;
3524
3525 /*
3526 * Set the new frame rate based on how long we've been idle.
3527 * Don't remain more than 2 seconds in each frame rate (except for lowest one).
3528 */
3529 /** @todo Experiment with these values. */
3530 if (pThisCC->cIdleCycles == 2 * pThisCC->uFrameRate)
3531 {
3532 if (pThisCC->uFrameRate > 500)
3533 uNewFrameRate = pThisCC->uFrameRate - 500;
3534 else
3535 uNewFrameRate = 50; /* Absolute minimum is 50 Hertz, i.e 20ms interval. */
3536
3537 pThisCC->cIdleCycles = 1;
3538 }
3539 }
3540 else
3541 {
3542 if (pThisCC->cIdleCycles)
3543 {
3544 pThisCC->cIdleCycles = 0;
3545 uNewFrameRate = pThisCC->uFrameRateDefault;
3546 }
3547 }
3548 if (uNewFrameRate != pThisCC->uFrameRate)
3549 ehciR3CalcTimerIntervals(pThis, pThisCC, uNewFrameRate);
3550}
3551
3552/**
3553 * Updates the HcFmNumber and frame_index values. HcFmNumber contains the current USB
3554 * frame number, frame_idx is the current micro-frame. In other words,
3555 *
3556 * HcFmNumber == frame_idx << EHCI_FRAME_INDEX_SHIFT
3557 */
3558static void ehciR3BumpFrameNumber(PPDMDEVINS pDevIns, PEHCI pThis)
3559{
3560 pThis->HcFmNumber = pThis->frame_idx;
3561
3562 const uint32_t u32OldFmNumber = pThis->HcFmNumber;
3563
3564 pThis->HcFmNumber += pThis->uFramesPerTimerCall;
3565
3566 if ((u32OldFmNumber ^ pThis->HcFmNumber) & ~EHCI_FRINDEX_FRAME_INDEX_MASK)
3567 {
3568 Log2Func(("rollover!\n"));
3569 ehciR3SetInterrupt(pDevIns, pThis, EHCI_STATUS_FRAME_LIST_ROLLOVER);
3570 }
3571
3572 pThis->frame_idx = pThis->HcFmNumber;
3573
3574}
3575
3576/**
3577 * @callback_method_impl{PFNPDMTHREADDEV, EHCI Frame Thread}
3578 */
3579static DECLCALLBACK(int) ehciR3ThreadFrame(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
3580{
3581 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
3582 PEHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PEHCICC);
3583
3584 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
3585 return VINF_SUCCESS;
3586
3587 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
3588 {
3589 int rc = VINF_SUCCESS;
3590 while ( !ASMAtomicReadBool(&pThis->fBusStarted)
3591 && pThread->enmState == PDMTHREADSTATE_RUNNING)
3592 {
3593 /* Make sure the SCHED status bits are clear. */
3594 ASMAtomicAndU32(&pThis->intr_status, ~EHCI_STATUS_PERIOD_SCHED);
3595 ASMAtomicAndU32(&pThis->intr_status, ~EHCI_STATUS_ASYNC_SCHED);
3596
3597 /* Signal the waiter that we are stopped now. */
3598 rc = RTSemEventMultiSignal(pThisCC->hSemEventFrameStopped);
3599 AssertRC(rc);
3600
3601 rc = RTSemEventMultiWait(pThisCC->hSemEventFrame, RT_INDEFINITE_WAIT);
3602 RTSemEventMultiReset(pThisCC->hSemEventFrame);
3603 }
3604
3605 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc);
3606 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
3607 break;
3608
3609 uint64_t const tsNanoStart = RTTimeNanoTS();
3610
3611 RTCritSectEnter(&pThisCC->CritSect);
3612
3613 /* Reset idle detection flag */
3614 pThisCC->fIdle = true;
3615
3616 /* Frame boundary, so do EOF stuff here */
3617 ehciR3StartOfFrame(pDevIns, pThis, pThisCC);
3618
3619 /* Start the next frame */
3620 ehciR3BumpFrameNumber(pDevIns, pThis);
3621
3622 RTCritSectLeave(&pThisCC->CritSect);
3623
3624 /* Wait for the next round. */
3625 uint64_t nsWait = (RTTimeNanoTS() + pThisCC->nsWait) - tsNanoStart;
3626
3627 rc = RTSemEventMultiWaitEx(pThisCC->hSemEventFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE,
3628 nsWait);
3629 AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc));
3630 RTSemEventMultiReset(pThisCC->hSemEventFrame);
3631 }
3632
3633 return VINF_SUCCESS;
3634}
3635
3636/**
3637 * Unblock the framer thread so it can respond to a state change.
3638 *
3639 * @returns VBox status code.
3640 * @param pDevIns The device instance.
3641 * @param pThread The send thread.
3642 */
3643static DECLCALLBACK(int) ehciR3ThreadFrameWakeup(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
3644{
3645 PEHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PEHCICC);
3646 RT_NOREF(pThread);
3647 return RTSemEventMultiSignal(pThisCC->hSemEventFrame);
3648}
3649
3650/**
3651 * Start sending SOF tokens across the USB bus, lists are processed in next frame.
3652 */
3653static void ehciR3BusStart(PPDMDEVINS pDevIns, PEHCI pThis, PEHCICC pThisCC)
3654{
3655 pThisCC->RootHub.pIRhConn->pfnPowerOn(pThisCC->RootHub.pIRhConn);
3656 ehciR3BumpFrameNumber(pDevIns, pThis);
3657
3658 LogFunc(("Bus started\n"));
3659
3660 ASMAtomicAndU32(&pThis->intr_status, ~EHCI_STATUS_HCHALTED);
3661 pThis->SofTime = PDMDevHlpTMTimeVirtGet(pDevIns) - pThisCC->cTicksPerFrame;
3662 bool fBusActive = ASMAtomicXchgBool(&pThis->fBusStarted, true);
3663 if (!fBusActive)
3664 RTSemEventMultiSignal(pThisCC->hSemEventFrame);
3665}
3666
3667/**
3668 * Stop sending SOF tokens on the bus
3669 */
3670static void ehciR3BusStop(PEHCI pThis, PEHCICC pThisCC)
3671{
3672 LogFunc(("\n"));
3673 bool fBusActive = ASMAtomicXchgBool(&pThis->fBusStarted, false);
3674 if (fBusActive)
3675 {
3676 int rc = RTSemEventMultiReset(pThisCC->hSemEventFrameStopped);
3677 AssertRC(rc);
3678
3679 /* Signal the frame thread to stop. */
3680 RTSemEventMultiSignal(pThisCC->hSemEventFrame);
3681
3682 /* Wait for signal from the thread that it stopped. */
3683 rc = RTSemEventMultiWait(pThisCC->hSemEventFrameStopped, RT_INDEFINITE_WAIT);
3684 AssertRC(rc);
3685 }
3686 pThisCC->RootHub.pIRhConn->pfnPowerOff(pThisCC->RootHub.pIRhConn);
3687 ASMAtomicOrU32(&pThis->intr_status, EHCI_STATUS_HCHALTED);
3688}
3689
3690/**
3691 * Power a port up or down
3692 */
3693static void ehciR3PortPower(PEHCI pThis, PEHCICC pThisCC, unsigned iPort, bool fPowerUp)
3694{
3695 bool fOldPPS = !!(pThis->RootHub.aPorts[iPort].fReg & EHCI_PORT_POWER);
3696 if (fPowerUp)
3697 {
3698 Log2Func(("port %d UP\n", iPort));
3699 /* power up */
3700 if (pThisCC->RootHub.aPorts[iPort].fAttached)
3701 ASMAtomicOrU32(&pThis->RootHub.aPorts[iPort].fReg, EHCI_PORT_CURRENT_CONNECT);
3702 if (pThis->RootHub.aPorts[iPort].fReg & EHCI_PORT_CURRENT_CONNECT)
3703 ASMAtomicOrU32(&pThis->RootHub.aPorts[iPort].fReg, EHCI_PORT_POWER);
3704 if (pThisCC->RootHub.aPorts[iPort].fAttached && !fOldPPS)
3705 VUSBIRhDevPowerOn(pThisCC->RootHub.pIRhConn, EHCI_PORT_2_VUSB_PORT(iPort));
3706 }
3707 else
3708 {
3709 Log2(("Func port %d DOWN\n", iPort));
3710 /* power down */
3711 ASMAtomicAndU32(&pThis->RootHub.aPorts[iPort].fReg, ~(EHCI_PORT_POWER|EHCI_PORT_CURRENT_CONNECT));
3712 if (pThisCC->RootHub.aPorts[iPort].fAttached && fOldPPS)
3713 VUSBIRhDevPowerOff(pThisCC->RootHub.pIRhConn, EHCI_PORT_2_VUSB_PORT(iPort));
3714 }
3715}
3716
3717/**
3718 * Completion callback for the VUSBIDevReset() operation.
3719 * @thread EMT.
3720 */
3721static void ehciR3PortResetDone(PEHCI pThis, PEHCICC pThisCC, uint32_t uPort, int rc)
3722{
3723 Log2Func(("rc=%Rrc\n", rc));
3724 Assert(uPort >= 1);
3725 unsigned iPort = uPort - 1;
3726
3727 if (RT_SUCCESS(rc))
3728 {
3729 /*
3730 * Successful reset.
3731 */
3732 Log2Func(("Reset completed.\n"));
3733 /* Note: XP relies on us clearing EHCI_PORT_CONNECT_CHANGE */
3734 ASMAtomicAndU32(&pThis->RootHub.aPorts[iPort].fReg, ~(EHCI_PORT_RESET | EHCI_PORT_SUSPEND | EHCI_PORT_CONNECT_CHANGE));
3735 ASMAtomicOrU32(&pThis->RootHub.aPorts[iPort].fReg, EHCI_PORT_PORT_ENABLED);
3736 }
3737 else
3738 {
3739 /* desperate measures. */
3740 if ( pThisCC->RootHub.aPorts[iPort].fAttached
3741 && VUSBIRhDevGetState(pThisCC->RootHub.pIRhConn, uPort) == VUSB_DEVICE_STATE_ATTACHED)
3742 {
3743 /*
3744 * Damn, something weird happend during reset. We'll pretend the user did an
3745 * incredible fast reconnect or something. (prolly not gonna work)
3746 */
3747 Log2Func(("The reset failed (rc=%Rrc)!!! Pretending reconnect at the speed of light.\n", rc));
3748 ASMAtomicOrU32(&pThis->RootHub.aPorts[iPort].fReg, EHCI_PORT_CURRENT_CONNECT | EHCI_PORT_CONNECT_CHANGE);
3749 }
3750 else
3751 {
3752 /*
3753 * The device has / will be disconnected.
3754 */
3755 Log2Func(("Disconnected (rc=%Rrc)!!!\n", rc));
3756 ASMAtomicAndU32(&pThis->RootHub.aPorts[iPort].fReg, ~(EHCI_PORT_RESET | EHCI_PORT_SUSPEND));
3757 ASMAtomicOrU32(&pThis->RootHub.aPorts[iPort].fReg, EHCI_PORT_CONNECT_CHANGE);
3758 }
3759 }
3760}
3761
3762/**
3763 * Sets a flag in a port status register but only set it if a device is
3764 * connected, if not set ConnectStatusChange flag to force HCD to reevaluate
3765 * connect status.
3766 *
3767 * @returns true if device was connected and the flag was cleared.
3768 */
3769static bool ehciR3RhPortSetIfConnected(PEHCIROOTHUB pRh, int iPort, uint32_t fValue)
3770{
3771 /*
3772 * Writing a 0 has no effect
3773 */
3774 if (fValue == 0)
3775 return false;
3776
3777 /*
3778 * The port might be still/already disconnected.
3779 */
3780 if (!(pRh->aPorts[iPort].fReg & EHCI_PORT_CURRENT_CONNECT))
3781 return false;
3782
3783 bool fRc = !(pRh->aPorts[iPort].fReg & fValue);
3784
3785 /* set the bit */
3786 ASMAtomicOrU32(&pRh->aPorts[iPort].fReg, fValue);
3787
3788 return fRc;
3789}
3790
3791#endif /* IN_RING3 */
3792
3793
3794/**
3795 * Read the USBCMD register of the host controller.
3796 */
3797static VBOXSTRICTRC HcCommand_r(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t *pu32Value)
3798{
3799 /* Signal the async advance doorbell interrupt (if required)
3800 * XP polls the command register to see when it can queue up more TDs.
3801 */
3802 if ( (pThis->cmd & EHCI_CMD_INT_ON_ADVANCE_DOORBELL))
3803// && !pThis->cInFlight)
3804 {
3805 int rc = ehciSetInterrupt(pDevIns, pThis, VINF_IOM_R3_MMIO_READ, EHCI_STATUS_INT_ON_ASYNC_ADV);
3806 if (rc != VINF_SUCCESS)
3807 return rc;
3808 }
3809
3810 *pu32Value = pThis->cmd;
3811 RT_NOREF(iReg);
3812 return VINF_SUCCESS;
3813}
3814
3815/**
3816 * Write to the USBCMD register of the host controller.
3817 */
3818static VBOXSTRICTRC HcCommand_w(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t val)
3819{
3820#ifdef IN_RING3
3821 PEHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PEHCICC);
3822#endif
3823 RT_NOREF(pDevIns, iReg);
3824
3825#ifdef LOG_ENABLED
3826 Log(("HcCommand_w old=%x new=%x\n", pThis->cmd, val));
3827 if (val & EHCI_CMD_RUN)
3828 Log((" CMD_RUN\n"));
3829 if (val & EHCI_CMD_RESET)
3830 Log((" CMD_RESET\n"));
3831 if (val & EHCI_CMD_PERIODIC_SCHED_ENABLE)
3832 Log((" CMD_PERIODIC_SCHED_ENABLE\n"));
3833 if (val & EHCI_CMD_ASYNC_SCHED_ENABLE)
3834 Log((" CMD_ASYNC_SCHED_ENABLE\n"));
3835 if (val & EHCI_CMD_INT_ON_ADVANCE_DOORBELL)
3836 Log((" CMD_INT_ON_ADVANCE_DOORBELL\n"));
3837 if (val & EHCI_CMD_SOFT_RESET)
3838 Log((" CMD_SOFT_RESET\n"));
3839 if (val & EHCI_CMD_ASYNC_SCHED_PARK_ENABLE)
3840 Log((" CMD_ASYNC_SCHED_PARK_ENABLE\n"));
3841
3842 Log((" CMD_FRAME_LIST_SIZE %d\n", (val & EHCI_CMD_FRAME_LIST_SIZE_MASK) >> EHCI_CMD_FRAME_LIST_SIZE_SHIFT));
3843 Log((" CMD_ASYNC_SCHED_PARK_MODE_COUNT %d\n", (val & EHCI_CMD_ASYNC_SCHED_PARK_MODE_COUNT_MASK) >> EHCI_CMD_ASYNC_SCHED_PARK_MODE_COUNT_SHIFT));
3844 Log((" CMD_INTERRUPT_THRESHOLD %d\n", (val & EHCI_CMD_INTERRUPT_THRESHOLD_MASK) >> EHCI_CMD_INTERRUPT_THRESHOLD_SHIFT));
3845#endif
3846
3847 Assert(!(pThis->hcc_params & EHCI_HCC_PARAMS_PROGRAMMABLE_FRAME_LIST)); /* hardcoded assumptions about list size */
3848 if (!(pThis->hcc_params & EHCI_HCC_PARAMS_PROGRAMMABLE_FRAME_LIST))
3849 {
3850 if (val & EHCI_CMD_FRAME_LIST_SIZE_MASK)
3851 Log(("Trying to change the frame list size to %d even though it's hardcoded at 1024 elements!!\n", (val & EHCI_CMD_FRAME_LIST_SIZE_MASK) >> EHCI_CMD_FRAME_LIST_SIZE_SHIFT));
3852
3853 val &= ~EHCI_CMD_FRAME_LIST_SIZE_MASK; /* 00 = 1024 */
3854 }
3855 if (val & ~EHCI_CMD_MASK)
3856 Log(("Unknown bits %#x are set!!!\n", val & ~0x0003000f));
3857
3858 uint32_t old_cmd = pThis->cmd;
3859#ifdef IN_RING3
3860 pThis->cmd = val;
3861#endif
3862
3863 if (val & EHCI_CMD_RESET)
3864 {
3865#ifdef IN_RING3
3866 LogRel(("EHCI: Hardware reset\n"));
3867 ehciR3DoReset(pDevIns, pThis, pThisCC, EHCI_USB_RESET, true /* reset devices */);
3868#else
3869 return VINF_IOM_R3_MMIO_WRITE;
3870#endif
3871 }
3872 else if (val & EHCI_CMD_SOFT_RESET)
3873 {
3874#ifdef IN_RING3
3875 LogRel(("EHCI: Software reset\n"));
3876 ehciR3DoReset(pDevIns, pThis, pThisCC, EHCI_USB_SUSPEND, false /* N/A */);
3877#else
3878 return VINF_IOM_R3_MMIO_WRITE;
3879#endif
3880 }
3881 else
3882 {
3883 /* see what changed and take action on that. */
3884 uint32_t old_state = old_cmd & EHCI_CMD_RUN;
3885 uint32_t new_state = val & EHCI_CMD_RUN;
3886
3887 if (old_state != new_state)
3888 {
3889#ifdef IN_RING3
3890 switch (new_state)
3891 {
3892 case EHCI_CMD_RUN:
3893 LogRel(("EHCI: USB Operational\n"));
3894 ehciR3BusStart(pDevIns, pThis, pThisCC);
3895 break;
3896 case 0:
3897 ehciR3BusStop(pThis, pThisCC);
3898 LogRel(("EHCI: USB Suspended\n"));
3899 break;
3900 }
3901#else
3902 return VINF_IOM_R3_MMIO_WRITE;
3903#endif
3904 }
3905 }
3906#ifndef IN_RING3
3907 pThis->cmd = val;
3908#endif
3909 return VINF_SUCCESS;
3910}
3911
3912/**
3913 * Read the USBSTS register of the host controller.
3914 */
3915static VBOXSTRICTRC HcStatus_r(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t *pu32Value)
3916{
3917 RT_NOREF(pDevIns, iReg);
3918#ifdef LOG_ENABLED
3919 Log(("HcStatus_r current value %x\n", pThis->intr_status));
3920 if (pThis->intr_status & EHCI_STATUS_ASYNC_SCHED)
3921 Log((" STATUS_ASYNC_SCHED\n"));
3922 if (pThis->intr_status & EHCI_STATUS_PERIOD_SCHED)
3923 Log((" STATUS_PERIOD_SCHED\n"));
3924 if (pThis->intr_status & EHCI_STATUS_RECLAMATION)
3925 Log((" STATUS_RECLAMATION\n"));
3926 if (pThis->intr_status & EHCI_STATUS_HCHALTED)
3927 Log((" STATUS_HCHALTED\n"));
3928 if (pThis->intr_status & EHCI_STATUS_INT_ON_ASYNC_ADV)
3929 Log((" STATUS_INT_ON_ASYNC_ADV\n"));
3930 if (pThis->intr_status & EHCI_STATUS_HOST_SYSTEM_ERROR)
3931 Log((" STATUS_HOST_SYSTEM_ERROR\n"));
3932 if (pThis->intr_status & EHCI_STATUS_FRAME_LIST_ROLLOVER)
3933 Log((" STATUS_FRAME_LIST_ROLLOVER\n"));
3934 if (pThis->intr_status & EHCI_STATUS_PORT_CHANGE_DETECT)
3935 Log((" STATUS_PORT_CHANGE_DETECT\n"));
3936 if (pThis->intr_status & EHCI_STATUS_ERROR_INT)
3937 Log((" STATUS_ERROR_INT\n"));
3938 if (pThis->intr_status & EHCI_STATUS_THRESHOLD_INT)
3939 Log((" STATUS_THRESHOLD_INT\n"));
3940#endif
3941 *pu32Value = pThis->intr_status;
3942 return VINF_SUCCESS;
3943}
3944
3945/**
3946 * Write to the USBSTS register of the host controller.
3947 */
3948static VBOXSTRICTRC HcStatus_w(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t val)
3949{
3950 RT_NOREF(iReg);
3951 VBOXSTRICTRC rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
3952 if (rc != VINF_SUCCESS)
3953 return rc;
3954
3955#ifdef LOG_ENABLED
3956 Log(("HcStatus_w current value %x; new %x\n", pThis->intr_status, val));
3957 if (val & EHCI_STATUS_ASYNC_SCHED)
3958 Log((" STATUS_ASYNC_SCHED\n"));
3959 if (val & EHCI_STATUS_PERIOD_SCHED)
3960 Log((" STATUS_PERIOD_SCHED\n"));
3961 if (val & EHCI_STATUS_RECLAMATION)
3962 Log((" STATUS_RECLAMATION\n"));
3963 if (val & EHCI_STATUS_HCHALTED)
3964 Log((" STATUS_HCHALTED\n"));
3965 if (val & EHCI_STATUS_INT_ON_ASYNC_ADV)
3966 Log((" STATUS_INT_ON_ASYNC_ADV\n"));
3967 if (val & EHCI_STATUS_HOST_SYSTEM_ERROR)
3968 Log((" STATUS_HOST_SYSTEM_ERROR\n"));
3969 if (val & EHCI_STATUS_FRAME_LIST_ROLLOVER)
3970 Log((" STATUS_FRAME_LIST_ROLLOVER\n"));
3971 if (val & EHCI_STATUS_PORT_CHANGE_DETECT)
3972 Log((" STATUS_PORT_CHANGE_DETECT\n"));
3973 if (val & EHCI_STATUS_ERROR_INT)
3974 Log((" STATUS_ERROR_INT\n"));
3975 if (val & EHCI_STATUS_THRESHOLD_INT)
3976 Log((" STATUS_THRESHOLD_INT\n"));
3977#endif
3978 if ( (val & ~EHCI_STATUS_INTERRUPT_MASK)
3979 && val != 0xffffffff /* ignore clear-all-like requests from xp. */)
3980 Log(("Unknown bits %#x are set!!!\n", val & ~EHCI_STATUS_INTERRUPT_MASK));
3981
3982 /* Some bits are read-only */
3983 val &= EHCI_STATUS_INTERRUPT_MASK;
3984
3985 /* "The Host Controller Driver may clear specific bits in this
3986 * register by writing '1' to bit positions to be cleared"
3987 */
3988 ASMAtomicAndU32(&pThis->intr_status, ~val);
3989 ehciUpdateInterruptLocked(pDevIns, pThis, "HcStatus_w");
3990
3991 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
3992 return VINF_SUCCESS;
3993}
3994
3995/**
3996 * Read the USBINTR register of the host controller.
3997 */
3998static VBOXSTRICTRC HcInterruptEnable_r(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t *pu32Value)
3999{
4000 RT_NOREF(pDevIns, iReg);
4001 *pu32Value = pThis->intr;
4002 return VINF_SUCCESS;
4003}
4004
4005/**
4006 * Write to the USBINTR register of the host controller.
4007 */
4008static VBOXSTRICTRC HcInterruptEnable_w(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t val)
4009{
4010 RT_NOREF(iReg);
4011#ifdef LOG_ENABLED
4012 Log(("HcInterruptEnable_w -> new value %x\n", val));
4013 if (val & EHCI_INTR_ENABLE_THRESHOLD)
4014 Log((" INTR_ENABLE_THRESHOLD\n"));
4015 if (val & EHCI_INTR_ENABLE_ERROR)
4016 Log((" INTR_ENABLE_ERROR\n"));
4017 if (val & EHCI_INTR_ENABLE_PORT_CHANGE)
4018 Log((" INTR_ENABLE_PORT_CHANGE\n"));
4019 if (val & EHCI_INTR_ENABLE_FRAME_LIST_ROLLOVER)
4020 Log((" INTR_ENABLE_FRAME_LIST_ROLLOVER\n"));
4021 if (val & EHCI_INTR_ENABLE_HOST_SYSTEM_ERROR)
4022 Log((" INTR_ENABLE_HOST_SYSTEM_ERROR\n"));
4023 if (val & EHCI_INTR_ENABLE_ASYNC_ADVANCE)
4024 Log((" INTR_ENABLE_ASYNC_ADVANCE\n"));
4025 if (val & ~EHCI_INTR_ENABLE_MASK)
4026 Log((" Illegal bits set %x!!\n", val & ~EHCI_INTR_ENABLE_MASK));
4027#endif
4028 VBOXSTRICTRC rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CsIrq, VINF_IOM_R3_MMIO_WRITE);
4029 if (rc == VINF_SUCCESS)
4030 {
4031 pThis->intr = val & EHCI_INTR_ENABLE_MASK;
4032 ehciUpdateInterruptLocked(pDevIns, pThis, "HcInterruptEnable_w");
4033 PDMDevHlpCritSectLeave(pDevIns, &pThis->CsIrq);
4034 }
4035 return rc;
4036}
4037
4038/**
4039 * Read the FRINDEX register of the host controller.
4040 */
4041static VBOXSTRICTRC HcFrameIndex_r(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4042{
4043 RT_NOREF(pDevIns, iReg);
4044 Log2Func(("current frame %x\n", pThis->frame_idx));
4045 *pu32Value = pThis->frame_idx;
4046 return VINF_SUCCESS;
4047}
4048
4049/**
4050 * Write to the FRINDEX register of the host controller.
4051 */
4052static VBOXSTRICTRC HcFrameIndex_w(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t val)
4053{
4054 RT_NOREF(pDevIns, iReg);
4055 LogFunc(("pThis->frame_idx new index=%x\n", val));
4056 if (!(pThis->intr_status & EHCI_STATUS_HCHALTED))
4057 Log(("->>Updating the frame index while the controller is running!!!\n"));
4058
4059 ASMAtomicXchgU32(&pThis->frame_idx, val);
4060 return VINF_SUCCESS;
4061}
4062
4063/**
4064 * Read the CTRLDSSEGMENT register of the host controller.
4065 */
4066static VBOXSTRICTRC HcControlDSSeg_r(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4067{
4068 RT_NOREF(pDevIns, iReg);
4069 if (pThis->hcc_params & EHCI_HCC_PARAMS_64BITS_ADDRESSING)
4070 *pu32Value = pThis->ds_segment;
4071 else
4072 *pu32Value = 0;
4073
4074 return VINF_SUCCESS;
4075}
4076
4077/**
4078 * Write to the CTRLDSSEGMENT register of the host controller.
4079 */
4080static VBOXSTRICTRC HcControlDSSeg_w(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t val)
4081{
4082 RT_NOREF(pDevIns, iReg);
4083 LogFunc(("new base %x\n", val));
4084 if (pThis->hcc_params & EHCI_HCC_PARAMS_64BITS_ADDRESSING)
4085 ASMAtomicXchgU32(&pThis->ds_segment, val);
4086
4087 return VINF_SUCCESS;
4088}
4089
4090/**
4091 * Read the PERIODICLISTBASE register of the host controller.
4092 */
4093static VBOXSTRICTRC HcPeriodicListBase_r(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4094{
4095 RT_NOREF(pDevIns, iReg);
4096 Log2Func(("current base %x\n", pThis->periodic_list_base));
4097 *pu32Value = pThis->periodic_list_base;
4098 return VINF_SUCCESS;
4099}
4100
4101/**
4102 * Write to the PERIODICLISTBASE register of the host controller.
4103 */
4104static VBOXSTRICTRC HcPeriodicListBase_w(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t val)
4105{
4106 RT_NOREF(pDevIns, iReg);
4107 LogFunc(("new base %x\n", val));
4108 if (val & ~EHCI_PERIODIC_LIST_MASK)
4109 Log(("->> Base not aligned on a 4kb boundary!!!!\n"));
4110 if ( !(pThis->intr_status & EHCI_STATUS_HCHALTED)
4111 && (pThis->cmd & EHCI_CMD_PERIODIC_SCHED_ENABLE))
4112 Log(("->>Updating the periodic list base while the controller is running!!!\n"));
4113
4114 ASMAtomicXchgU32(&pThis->periodic_list_base, val & EHCI_PERIODIC_LIST_MASK);
4115 return VINF_SUCCESS;
4116}
4117
4118/**
4119 * Read the ASYNCLISTADDR register of the host controller.
4120 */
4121static VBOXSTRICTRC HcAsyncListAddr_r(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4122{
4123 RT_NOREF(pDevIns, iReg);
4124 Log2Func(("current base %x\n", pThis->async_list_base));
4125 *pu32Value = pThis->async_list_base;
4126 return VINF_SUCCESS;
4127}
4128
4129/**
4130 * Write to the ASYNCLISTADDR register of the host controller.
4131 */
4132static VBOXSTRICTRC HcAsyncListAddr_w(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t val)
4133{
4134 RT_NOREF(pDevIns, iReg);
4135 LogFunc(("new address %x\n", val));
4136 if (val & ~EHCI_ASYNC_LIST_MASK)
4137 Log(("->> Base not aligned on a 32-byte boundary!!!!\n"));
4138 if ( !(pThis->intr_status & EHCI_STATUS_HCHALTED)
4139 && (pThis->cmd & EHCI_CMD_ASYNC_SCHED_ENABLE))
4140 Log(("->>Updating the asynchronous list address while the controller is running!!!\n"));
4141
4142 ASMAtomicXchgU32(&pThis->async_list_base, val & EHCI_ASYNC_LIST_MASK);
4143 return VINF_SUCCESS;
4144}
4145
4146/**
4147 * Read the CONFIGFLAG register of the host controller.
4148 */
4149static VBOXSTRICTRC HcConfigFlag_r(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4150{
4151 RT_NOREF(pDevIns, iReg);
4152 Log2Func(("current config=%x\n", pThis->config));
4153 *pu32Value = pThis->config;
4154 return VINF_SUCCESS;
4155}
4156
4157/**
4158 * Write to the CONFIGFLAG register of the host controller.
4159 */
4160static VBOXSTRICTRC HcConfigFlag_w(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t val)
4161{
4162 RT_NOREF(pDevIns, iReg);
4163 LogFunc(("new configuration routing %x\n", val & EHCI_CONFIGFLAG_ROUTING));
4164 pThis->config = val & EHCI_CONFIGFLAG_MASK;
4165 return VINF_SUCCESS;
4166}
4167
4168/**
4169 * Read the PORTSC register of a port
4170 */
4171static VBOXSTRICTRC HcPortStatusCtrl_r(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t *pu32Value)
4172{
4173 const unsigned i = iReg - 1;
4174 PEHCIHUBPORT p = &pThis->RootHub.aPorts[i];
4175 RT_NOREF(pDevIns);
4176
4177 Assert(!(pThis->hcs_params & EHCI_HCS_PARAMS_PORT_POWER_CONTROL));
4178
4179 if (p->fReg & EHCI_PORT_RESET)
4180 {
4181#ifdef IN_RING3
4182 Log2Func(("port %u: Impatient guest!\n", i));
4183 RTThreadYield();
4184#else
4185 Log2Func(("yield -> VINF_IOM_R3_MMIO_READ\n"));
4186 return VINF_IOM_R3_MMIO_READ;
4187#endif
4188 }
4189
4190 *pu32Value = p->fReg;
4191 return VINF_SUCCESS;
4192}
4193
4194/**
4195 * Write to the PORTSC register of a port
4196 */
4197static VBOXSTRICTRC HcPortStatusCtrl_w(PPDMDEVINS pDevIns, PEHCI pThis, uint32_t iReg, uint32_t val)
4198{
4199 const unsigned i = iReg - 1;
4200 PEHCIHUBPORT pPort = &pThis->RootHub.aPorts[i];
4201
4202 if ( pPort->fReg == val
4203 && !(val & EHCI_PORT_CHANGE_MASK))
4204 return VINF_SUCCESS;
4205
4206#ifdef IN_RING3
4207 PEHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PEHCICC);
4208
4209 LogFunc(("port %d: old=%x new=%x\n", i, pPort->fReg, val));
4210 Assert(!(pThis->hcs_params & EHCI_HCS_PARAMS_PORT_POWER_CONTROL));
4211 Assert(pPort->fReg & EHCI_PORT_POWER);
4212
4213 if (val & EHCI_PORT_RESERVED)
4214 Log(("Invalid bits set %x!!!\n", val & EHCI_PORT_RESERVED));
4215
4216 /* Write to clear any of the change bits: EHCI_PORT_CONNECT_CHANGE, EHCI_PORT_PORT_CHANGE and EHCI_PORT_OVER_CURRENT_CHANGE */
4217 if (val & EHCI_PORT_CHANGE_MASK)
4218 {
4219 ASMAtomicAndU32(&pPort->fReg, ~(val & EHCI_PORT_CHANGE_MASK));
4220 /* XP seems to need this after device detach */
4221 if (!(pPort->fReg & EHCI_PORT_CURRENT_CONNECT))
4222 ASMAtomicAndU32(&pPort->fReg, ~EHCI_PORT_CONNECT_CHANGE);
4223 }
4224
4225 /* Writing the Port Enable/Disable bit as 1 has no effect; software cannot enable
4226 * the port that way. Writing the bit as zero does disable the port, but does not
4227 * set the corresponding 'changed' bit or trigger an interrupt.
4228 */
4229 if (!(val & EHCI_PORT_PORT_ENABLED) && (pPort->fReg & EHCI_PORT_PORT_ENABLED))
4230 {
4231 ASMAtomicAndU32(&pPort->fReg, ~EHCI_PORT_PORT_ENABLED);
4232 LogFunc(("port %u: DISABLE\n", i));
4233 }
4234
4235 if (val & EHCI_PORT_SUSPEND)
4236 LogFunc(("port %u: SUSPEND - not implemented correctly!!!\n", i));
4237
4238 if (val & EHCI_PORT_RESET)
4239 {
4240 Log2Func(("Reset port\n"));
4241 if ( ehciR3RhPortSetIfConnected(&pThis->RootHub, i, val & EHCI_PORT_RESET) )
4242 {
4243 PVM pVM = PDMDevHlpGetVM(pDevIns);
4244 VUSBIRhDevReset(pThisCC->RootHub.pIRhConn, EHCI_PORT_2_VUSB_PORT(i), false /* don't reset on linux */, NULL /* sync */, pThis, pVM);
4245 ehciR3PortResetDone(pThis, pThisCC, EHCI_PORT_2_VUSB_PORT(i), VINF_SUCCESS);
4246 }
4247 else if (pPort->fReg & EHCI_PORT_RESET)
4248 {
4249 /* the guest is getting impatient. */
4250 Log2Func(("port %u: Impatient guest!\n", i));
4251 RTThreadYield();
4252 }
4253 }
4254
4255 /* EHCI_PORT_POWER ignored as we don't support this in HCS_PARAMS */
4256 /* EHCI_PORT_INDICATOR ignored as we don't support this in HCS_PARAMS */
4257 /* EHCI_PORT_TEST_CONTROL_MASK ignored */
4258 ASMAtomicAndU32(&pPort->fReg, ~EHCI_PORT_WAKE_MASK);
4259 ASMAtomicOrU32(&pPort->fReg, (val & EHCI_PORT_WAKE_MASK));
4260 return VINF_SUCCESS;
4261
4262#else /* !IN_RING3 */
4263 RT_NOREF(pDevIns);
4264 return VINF_IOM_R3_MMIO_WRITE;
4265#endif /* !IN_RING3 */
4266}
4267
4268/**
4269 * Register descriptor table
4270 */
4271static const EHCIOPREG g_aOpRegs[] =
4272{
4273 {"HcCommand" , HcCommand_r, HcCommand_w},
4274 {"HcStatus", HcStatus_r, HcStatus_w},
4275 {"HcInterruptEnable", HcInterruptEnable_r, HcInterruptEnable_w},
4276 {"HcFrameIndex", HcFrameIndex_r, HcFrameIndex_w},
4277 {"HcControlDSSeg", HcControlDSSeg_r, HcControlDSSeg_w},
4278 {"HcPeriodicListBase", HcPeriodicListBase_r, HcPeriodicListBase_w},
4279 {"HcAsyncListAddr", HcAsyncListAddr_r, HcAsyncListAddr_w}
4280};
4281
4282/**
4283 * Register descriptor table 2
4284 * (Starting at offset 0x40)
4285 */
4286static const EHCIOPREG g_aOpRegs2[] =
4287{
4288 {"HcConfigFlag", HcConfigFlag_r, HcConfigFlag_w},
4289
4290 /* The number of port status register depends on the definition
4291 * of EHCI_NDP_MAX macro
4292 */
4293 {"HcPortStatusCtrl[0]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4294 {"HcPortStatusCtrl[1]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4295 {"HcPortStatusCtrl[2]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4296 {"HcPortStatusCtrl[3]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4297 {"HcPortStatusCtrl[4]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4298 {"HcPortStatusCtrl[5]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4299 {"HcPortStatusCtrl[6]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4300 {"HcPortStatusCtrl[7]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4301 {"HcPortStatusCtrl[8]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4302 {"HcPortStatusCtrl[9]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4303 {"HcPortStatusCtrl[10]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4304 {"HcPortStatusCtrl[11]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4305 {"HcPortStatusCtrl[12]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4306 {"HcPortStatusCtrl[13]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4307 {"HcPortStatusCtrl[14]", HcPortStatusCtrl_r, HcPortStatusCtrl_w},
4308};
4309
4310/* Quick way to determine how many op regs are valid. Since at least one port must
4311 * be configured (and no more than 15), there will be between 2 and 16 registers.
4312 */
4313#define NUM_OP_REGS2(pehci) (1 + EHCI_NDP_CFG(pehci))
4314
4315AssertCompile(RT_ELEMENTS(g_aOpRegs2) > 1);
4316AssertCompile(RT_ELEMENTS(g_aOpRegs2) <= 16);
4317
4318
4319/**
4320 * @callback_method_impl{FNIOMMMIONEWWRITE, Write to a MMIO register. }
4321 *
4322 * @note We only accept 32-bit reads that are 32-bit aligned.
4323 */
4324static DECLCALLBACK(VBOXSTRICTRC) ehciMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
4325{
4326 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
4327 RT_NOREF(pvUser);
4328
4329 Log2Func(("%RGp size=%d\n", off, cb));
4330
4331 if (off < EHCI_CAPS_REG_SIZE)
4332 {
4333 switch (off)
4334 {
4335 case 0x0: /* CAPLENGTH */
4336 /* read CAPLENGTH + HCIVERSION in one go */
4337 if (cb == 4)
4338 {
4339 *(uint32_t *)pv = (pThis->hci_version << 16) | pThis->cap_length;
4340 return VINF_SUCCESS;
4341 }
4342
4343 AssertReturn(cb == 1, VINF_IOM_MMIO_UNUSED_FF);
4344 *(uint8_t *)pv = pThis->cap_length;
4345 break;
4346
4347 case 0x2: /* HCIVERSION */
4348 AssertReturn(cb == 2, VINF_IOM_MMIO_UNUSED_FF);
4349 *(uint16_t *)pv = pThis->hci_version;
4350 break;
4351
4352 case 0x4: /* HCSPARAMS (structural) */
4353 AssertReturn(cb == 4, VINF_IOM_MMIO_UNUSED_FF);
4354 *(uint32_t *)pv = pThis->hcs_params;
4355 break;
4356
4357 case 0x8: /* HCCPARAMS (caps) */
4358 AssertReturn(cb == 4, VINF_IOM_MMIO_UNUSED_FF);
4359 *(uint32_t *)pv = pThis->hcc_params;
4360 break;
4361
4362 case 0x9: /* one byte HCIPARAMS read (XP; EHCI extended capability offset) */
4363 AssertReturn(cb == 1, VINF_IOM_MMIO_UNUSED_FF);
4364 *(uint8_t *)pv = (uint8_t)(pThis->hcc_params >> 8);
4365 break;
4366
4367 case 0xC: /* HCSP-PORTROUTE (60 bits) */
4368 case 0x10:
4369 AssertReturn(cb == 4, VINF_IOM_MMIO_UNUSED_FF);
4370 *(uint32_t *)pv = 0;
4371 break;
4372
4373 default:
4374 LogFunc(("Trying to read register %#x!!!\n", off));
4375 return VINF_IOM_MMIO_UNUSED_FF;
4376 }
4377 Log2Func(("%RGp size=%d -> val=%x\n", off, cb, *(uint32_t *)pv));
4378 return VINF_SUCCESS;
4379 }
4380
4381 /*
4382 * Validate the access.
4383 */
4384 if (cb != sizeof(uint32_t))
4385 {
4386 Log2Func(("Bad read size!!! off=%RGp cb=%d\n", off, cb));
4387 return VINF_IOM_MMIO_UNUSED_FF; /* No idea what really would happen... */
4388 }
4389 if (off & 0x3)
4390 {
4391 Log2Func(("Unaligned read!!! off=%RGp cb=%d\n", off, cb));
4392 return VINF_IOM_MMIO_UNUSED_FF;
4393 }
4394
4395 /*
4396 * Validate the register and call the read operator.
4397 */
4398 VBOXSTRICTRC rc;
4399 uint32_t iReg = (off - pThis->cap_length) >> 2;
4400 if (iReg < RT_ELEMENTS(g_aOpRegs))
4401 {
4402 const EHCIOPREG *pReg = &g_aOpRegs[iReg];
4403 rc = pReg->pfnRead(pDevIns, pThis, iReg, (uint32_t *)pv);
4404 Log2Func(("%RGp size=%d -> val=%x (rc=%d)\n", off, cb, *(uint32_t *)pv, VBOXSTRICTRC_VAL(rc)));
4405 }
4406 else if (iReg >= 0x10) /* 0x40 */
4407 {
4408 iReg -= 0x10;
4409 if (iReg < NUM_OP_REGS2(pThis))
4410 {
4411 const EHCIOPREG *pReg = &g_aOpRegs2[iReg];
4412 rc = pReg->pfnRead(pDevIns, pThis, iReg, (uint32_t *)pv);
4413 Log2Func(("%RGp size=%d -> val=%x (rc=%d)*\n", off, cb, *(uint32_t *)pv, VBOXSTRICTRC_VAL(rc)));
4414 }
4415 else
4416 {
4417 LogFunc(("Trying to read register %u/%u!!!\n", iReg, NUM_OP_REGS2(pThis)));
4418 rc = VINF_IOM_MMIO_UNUSED_FF;
4419 }
4420 }
4421 else
4422 {
4423 LogFunc(("Trying to read register %u/%u (2)!!!\n", iReg, RT_ELEMENTS(g_aOpRegs)));
4424 rc = VINF_IOM_MMIO_UNUSED_FF;
4425 }
4426 return rc;
4427}
4428
4429
4430/**
4431 * @callback_method_impl{FNIOMMMIONEWWRITE, Write to a MMIO register. }
4432 *
4433 * @note We only accept 32-bit writes that are 32-bit aligned.
4434 */
4435static DECLCALLBACK(VBOXSTRICTRC) ehciMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
4436{
4437 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
4438 RT_NOREF(pvUser);
4439
4440 Log2Func(("%RGp %x size=%d\n", off, *(uint32_t *)pv, cb));
4441
4442 if (off < EHCI_CAPS_REG_SIZE)
4443 {
4444 /* These are read-only */
4445 LogFunc(("Trying to write to register %#x!!!\n", off));
4446 return VINF_SUCCESS;
4447 }
4448
4449 /*
4450 * Validate the access.
4451 */
4452 if (cb != sizeof(uint32_t))
4453 {
4454 Log2Func(("Bad write size!!! off=%RGp cb=%d\n", off, cb));
4455 return VINF_SUCCESS;
4456 }
4457 if (off & 0x3)
4458 {
4459 Log2Func(("Unaligned write!!! off=%RGp cb=%d\n", off, cb));
4460 return VINF_SUCCESS;
4461 }
4462
4463 /*
4464 * Validate the register and call the read operator.
4465 */
4466 VBOXSTRICTRC rc;
4467 uint32_t iReg = (off - pThis->cap_length) >> 2;
4468 if (iReg < RT_ELEMENTS(g_aOpRegs))
4469 {
4470 const EHCIOPREG *pReg = &g_aOpRegs[iReg];
4471 rc = pReg->pfnWrite(pDevIns, pThis, iReg, *(uint32_t *)pv);
4472 }
4473 else if (iReg >= 0x10) /* 0x40 */
4474 {
4475 iReg -= 0x10;
4476 if (iReg < NUM_OP_REGS2(pThis))
4477 {
4478 const EHCIOPREG *pReg = &g_aOpRegs2[iReg];
4479 rc = pReg->pfnWrite(pDevIns, pThis, iReg, *(uint32_t *)pv);
4480 }
4481 else
4482 {
4483 LogFunc(("Trying to write to register %u/%u!!!\n", iReg, NUM_OP_REGS2(pThis)));
4484 rc = VINF_SUCCESS; /* ignore the invalid write */
4485 }
4486 }
4487 else
4488 {
4489 LogFunc(("Trying to write to register %u/%u!!! (2)\n", iReg, RT_ELEMENTS(g_aOpRegs)));
4490 rc = VINF_SUCCESS; /* ignore the invalid write */
4491 }
4492 return rc;
4493}
4494
4495#ifdef IN_RING3
4496
4497/**
4498 * @callback_method_impl{FNSSMDEVSAVEEXEC}
4499 */
4500static DECLCALLBACK(int) ehciR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
4501{
4502 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
4503 LogFlowFunc(("\n"));
4504 return pDevIns->pHlpR3->pfnSSMPutStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aEhciFields[0], NULL);
4505}
4506
4507
4508/**
4509 * @callback_method_impl{FNSSMDEVLOADEXEC}
4510 */
4511static DECLCALLBACK(int) ehciLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
4512{
4513 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
4514 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4515 int rc;
4516 LogFlowFunc(("\n"));
4517 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
4518
4519 if (uVersion == EHCI_SAVED_STATE_VERSION)
4520 {
4521 rc = pHlp->pfnSSMGetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aEhciFields[0], NULL);
4522 if (RT_FAILURE(rc))
4523 return rc;
4524 }
4525 else if (uVersion == EHCI_SAVED_STATE_VERSION_PRE_TIMER_REMOVAL)
4526 {
4527 static SSMFIELD const g_aEhciFieldsPreTimerRemoval[] =
4528 {
4529 SSMFIELD_ENTRY( EHCI, fAsyncTraversalTimerActive),
4530 SSMFIELD_ENTRY( EHCI, SofTime),
4531 SSMFIELD_ENTRY( EHCI, RootHub.unused),
4532 SSMFIELD_ENTRY( EHCI, RootHub.unused),
4533 SSMFIELD_ENTRY( EHCI, RootHub.unused),
4534 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[0].fReg),
4535 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[1].fReg),
4536 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[2].fReg),
4537 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[3].fReg),
4538 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[4].fReg),
4539 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[5].fReg),
4540 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[6].fReg),
4541 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[7].fReg),
4542 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[8].fReg),
4543 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[9].fReg),
4544 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[10].fReg),
4545 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[11].fReg),
4546 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[12].fReg),
4547 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[13].fReg),
4548 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[14].fReg),
4549 SSMFIELD_ENTRY( EHCI, cap_length),
4550 SSMFIELD_ENTRY( EHCI, hci_version),
4551 SSMFIELD_ENTRY( EHCI, hcs_params),
4552 SSMFIELD_ENTRY( EHCI, hcc_params),
4553 SSMFIELD_ENTRY( EHCI, cmd),
4554 SSMFIELD_ENTRY( EHCI, intr_status),
4555 SSMFIELD_ENTRY( EHCI, intr),
4556 SSMFIELD_ENTRY( EHCI, frame_idx),
4557 SSMFIELD_ENTRY( EHCI, ds_segment),
4558 SSMFIELD_ENTRY( EHCI, periodic_list_base),
4559 SSMFIELD_ENTRY( EHCI, async_list_base),
4560 SSMFIELD_ENTRY( EHCI, config),
4561 SSMFIELD_ENTRY( EHCI, uIrqInterval),
4562 SSMFIELD_ENTRY( EHCI, HcFmNumber),
4563 SSMFIELD_ENTRY( EHCI, uFramesPerTimerCall),
4564 SSMFIELD_ENTRY_TERM()
4565 };
4566
4567 rc = pHlp->pfnSSMGetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &g_aEhciFieldsPreTimerRemoval[0], NULL);
4568 if (RT_FAILURE(rc))
4569 return rc;
4570 AssertReturn(EHCI_NDP_CFG(pThis) <= EHCI_NDP_MAX, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
4571 }
4572 else if (uVersion == EHCI_SAVED_STATE_VERSION_8PORTS)
4573 {
4574 static SSMFIELD const s_aEhciFields8Ports[] =
4575 {
4576 SSMFIELD_ENTRY( EHCI, fAsyncTraversalTimerActive),
4577 SSMFIELD_ENTRY( EHCI, SofTime),
4578 SSMFIELD_ENTRY( EHCI, RootHub.unused),
4579 SSMFIELD_ENTRY( EHCI, RootHub.unused),
4580 SSMFIELD_ENTRY( EHCI, RootHub.unused),
4581 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[0].fReg),
4582 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[1].fReg),
4583 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[2].fReg),
4584 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[3].fReg),
4585 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[4].fReg),
4586 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[5].fReg),
4587 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[6].fReg),
4588 SSMFIELD_ENTRY( EHCI, RootHub.aPorts[7].fReg),
4589 SSMFIELD_ENTRY( EHCI, cap_length),
4590 SSMFIELD_ENTRY( EHCI, hci_version),
4591 SSMFIELD_ENTRY( EHCI, hcs_params),
4592 SSMFIELD_ENTRY( EHCI, hcc_params),
4593 SSMFIELD_ENTRY( EHCI, cmd),
4594 SSMFIELD_ENTRY( EHCI, intr_status),
4595 SSMFIELD_ENTRY( EHCI, intr),
4596 SSMFIELD_ENTRY( EHCI, frame_idx),
4597 SSMFIELD_ENTRY( EHCI, ds_segment),
4598 SSMFIELD_ENTRY( EHCI, periodic_list_base),
4599 SSMFIELD_ENTRY( EHCI, async_list_base),
4600 SSMFIELD_ENTRY( EHCI, config),
4601 SSMFIELD_ENTRY( EHCI, uIrqInterval),
4602 SSMFIELD_ENTRY( EHCI, HcFmNumber),
4603 SSMFIELD_ENTRY( EHCI, uFramesPerTimerCall),
4604 SSMFIELD_ENTRY_TERM()
4605 };
4606
4607 rc = pHlp->pfnSSMGetStructEx(pSSM, pThis, sizeof(*pThis), 0 /*fFlags*/, &s_aEhciFields8Ports[0], NULL);
4608 if (RT_FAILURE(rc))
4609 return rc;
4610 AssertReturn(EHCI_NDP_CFG(pThis) == 8, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
4611 }
4612 else
4613 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4614
4615 /*
4616 * The EOF timer changed from one to two in version 4 of the saved state,
4617 * then was dropped entirely in version 7.
4618 *
4619 * Note! Looks like someone remove the code that dealt with versions 1 thru 4,
4620 * without adjust the above comment.
4621 */
4622 if (uVersion <= EHCI_SAVED_STATE_VERSION_PRE_TIMER_REMOVAL)
4623 {
4624 bool fActive1 = false;
4625 pHlp->pfnTimerSkipLoad(pSSM, &fActive1);
4626 bool fActive2 = false;
4627 pHlp->pfnTimerSkipLoad(pSSM, &fActive2);
4628 bool fNoSync = false;
4629 rc = pHlp->pfnSSMGetBool(pSSM, &fNoSync);
4630 if ( RT_SUCCESS(rc)
4631 && (fActive1 || fActive2))
4632 pThis->fBusStarted = true;
4633 else
4634 pThis->fBusStarted = false;
4635 }
4636 return rc;
4637}
4638
4639
4640/**
4641 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps EHCI control registers.}
4642 */
4643static DECLCALLBACK(void) ehciR3InfoRegs(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4644{
4645 RT_NOREF(pszArgs);
4646 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
4647 unsigned uPort;
4648
4649 /* Command register */
4650 pHlp->pfnPrintf(pHlp, "USBCMD: %x\n", pThis->cmd);
4651 if (pThis->cmd & EHCI_CMD_RUN)
4652 pHlp->pfnPrintf(pHlp, " CMD_RUN\n");
4653 if (pThis->cmd & EHCI_CMD_RESET)
4654 pHlp->pfnPrintf(pHlp, " CMD_RESET\n");
4655 if (pThis->cmd & EHCI_CMD_PERIODIC_SCHED_ENABLE)
4656 pHlp->pfnPrintf(pHlp, " CMD_PERIODIC_SCHED_ENABLE\n");
4657 if (pThis->cmd & EHCI_CMD_ASYNC_SCHED_ENABLE)
4658 pHlp->pfnPrintf(pHlp, " CMD_ASYNC_SCHED_ENABLE\n");
4659 if (pThis->cmd & EHCI_CMD_INT_ON_ADVANCE_DOORBELL)
4660 pHlp->pfnPrintf(pHlp, " CMD_INT_ON_ADVANCE_DOORBELL\n");
4661 if (pThis->cmd & EHCI_CMD_SOFT_RESET)
4662 pHlp->pfnPrintf(pHlp, " CMD_SOFT_RESET\n");
4663 if (pThis->cmd & EHCI_CMD_ASYNC_SCHED_PARK_ENABLE)
4664 pHlp->pfnPrintf(pHlp, " CMD_ASYNC_SCHED_PARK_ENABLE\n");
4665
4666 pHlp->pfnPrintf(pHlp, " CMD_FRAME_LIST_SIZE %d\n", (pThis->cmd & EHCI_CMD_FRAME_LIST_SIZE_MASK) >> EHCI_CMD_FRAME_LIST_SIZE_SHIFT);
4667 pHlp->pfnPrintf(pHlp, " CMD_ASYNC_SCHED_PARK_MODE_COUNT %d\n", (pThis->cmd & EHCI_CMD_ASYNC_SCHED_PARK_MODE_COUNT_MASK) >> EHCI_CMD_ASYNC_SCHED_PARK_MODE_COUNT_SHIFT);
4668 pHlp->pfnPrintf(pHlp, " CMD_INTERRUPT_THRESHOLD %d\n", (pThis->cmd & EHCI_CMD_INTERRUPT_THRESHOLD_MASK) >> EHCI_CMD_INTERRUPT_THRESHOLD_SHIFT);
4669
4670 /* Status register */
4671 pHlp->pfnPrintf(pHlp, "USBSTS: %x\n", pThis->intr_status);
4672 if (pThis->intr_status & EHCI_STATUS_ASYNC_SCHED)
4673 pHlp->pfnPrintf(pHlp, " STATUS_ASYNC_SCHED\n");
4674 if (pThis->intr_status & EHCI_STATUS_PERIOD_SCHED)
4675 pHlp->pfnPrintf(pHlp, " STATUS_PERIOD_SCHED\n");
4676 if (pThis->intr_status & EHCI_STATUS_RECLAMATION)
4677 pHlp->pfnPrintf(pHlp, " STATUS_RECLAMATION\n");
4678 if (pThis->intr_status & EHCI_STATUS_HCHALTED)
4679 pHlp->pfnPrintf(pHlp, " STATUS_HCHALTED\n");
4680 if (pThis->intr_status & EHCI_STATUS_INT_ON_ASYNC_ADV)
4681 pHlp->pfnPrintf(pHlp, " STATUS_INT_ON_ASYNC_ADV\n");
4682 if (pThis->intr_status & EHCI_STATUS_HOST_SYSTEM_ERROR)
4683 pHlp->pfnPrintf(pHlp, " STATUS_HOST_SYSTEM_ERROR\n");
4684 if (pThis->intr_status & EHCI_STATUS_FRAME_LIST_ROLLOVER)
4685 pHlp->pfnPrintf(pHlp, " STATUS_FRAME_LIST_ROLLOVER\n");
4686 if (pThis->intr_status & EHCI_STATUS_PORT_CHANGE_DETECT)
4687 pHlp->pfnPrintf(pHlp, " STATUS_PORT_CHANGE_DETECT\n");
4688 if (pThis->intr_status & EHCI_STATUS_ERROR_INT)
4689 pHlp->pfnPrintf(pHlp, " STATUS_ERROR_INT\n");
4690 if (pThis->intr_status & EHCI_STATUS_THRESHOLD_INT)
4691 pHlp->pfnPrintf(pHlp, " STATUS_THRESHOLD_INT\n");
4692
4693 /* Interrupt enable register */
4694 pHlp->pfnPrintf(pHlp, "USBINTR: %x\n", pThis->intr);
4695 if (pThis->intr & EHCI_INTR_ENABLE_THRESHOLD)
4696 pHlp->pfnPrintf(pHlp, " INTR_ENABLE_THRESHOLD\n");
4697 if (pThis->intr & EHCI_INTR_ENABLE_ERROR)
4698 pHlp->pfnPrintf(pHlp, " INTR_ENABLE_ERROR\n");
4699 if (pThis->intr & EHCI_INTR_ENABLE_PORT_CHANGE)
4700 pHlp->pfnPrintf(pHlp, " INTR_ENABLE_PORT_CHANGE\n");
4701 if (pThis->intr & EHCI_INTR_ENABLE_FRAME_LIST_ROLLOVER)
4702 pHlp->pfnPrintf(pHlp, " INTR_ENABLE_FRAME_LIST_ROLLOVER\n");
4703 if (pThis->intr & EHCI_INTR_ENABLE_HOST_SYSTEM_ERROR)
4704 pHlp->pfnPrintf(pHlp, " INTR_ENABLE_HOST_SYSTEM_ERROR\n");
4705 if (pThis->intr & EHCI_INTR_ENABLE_ASYNC_ADVANCE)
4706 pHlp->pfnPrintf(pHlp, " INTR_ENABLE_ASYNC_ADVANCE\n");
4707 if (pThis->intr & ~EHCI_INTR_ENABLE_MASK)
4708 pHlp->pfnPrintf(pHlp, " Illegal bits set %x!!\n", pThis->intr & ~EHCI_INTR_ENABLE_MASK);
4709
4710 /* Frame index register */
4711 pHlp->pfnPrintf(pHlp, "FRINDEX: %x\n", pThis->frame_idx);
4712
4713 /* Control data structure segment */
4714 pHlp->pfnPrintf(pHlp, "CTRLDSSEGMENT: %RX32\n", pThis->ds_segment);
4715
4716 /* Periodic frame list base address register */
4717 pHlp->pfnPrintf(pHlp, "PERIODICLISTBASE: %RX32\n", pThis->periodic_list_base);
4718
4719 /* Current asynchronous list address register */
4720 pHlp->pfnPrintf(pHlp, "ASYNCLISTADDR: %RX32\n", pThis->async_list_base);
4721
4722 pHlp->pfnPrintf(pHlp, "\n");
4723
4724 for (uPort = 0; uPort < EHCI_NDP_CFG(pThis); ++uPort)
4725 {
4726 PEHCIHUBPORT pPort = &pThis->RootHub.aPorts[uPort];
4727 pHlp->pfnPrintf(pHlp, "PORTSC for port %u:\n", uPort);
4728 if (pPort->fReg & EHCI_PORT_CURRENT_CONNECT)
4729 pHlp->pfnPrintf(pHlp, " PORT_CURRENT_CONNECT\n");
4730 if (pPort->fReg & EHCI_PORT_CONNECT_CHANGE)
4731 pHlp->pfnPrintf(pHlp, " PORT_CONNECT_CHANGE\n");
4732 if (pPort->fReg & EHCI_PORT_PORT_ENABLED)
4733 pHlp->pfnPrintf(pHlp, " PORT_PORT_ENABLED\n");
4734 if (pPort->fReg & EHCI_PORT_PORT_CHANGE)
4735 pHlp->pfnPrintf(pHlp, " PORT_PORT_CHANGE\n");
4736 if (pPort->fReg & EHCI_PORT_OVER_CURRENT_ACTIVE)
4737 pHlp->pfnPrintf(pHlp, " PORT_OVER_CURRENT_ACTIVE\n");
4738 if (pPort->fReg & EHCI_PORT_OVER_CURRENT_CHANGE)
4739 pHlp->pfnPrintf(pHlp, " PORT_OVER_CURRENT_CHANGE\n");
4740 if (pPort->fReg & EHCI_PORT_FORCE_PORT_RESUME)
4741 pHlp->pfnPrintf(pHlp, " PORT_FORCE_PORT_RESUME\n");
4742 if (pPort->fReg & EHCI_PORT_SUSPEND)
4743 pHlp->pfnPrintf(pHlp, " PORT_SUSPEND\n");
4744 if (pPort->fReg & EHCI_PORT_RESET)
4745 pHlp->pfnPrintf(pHlp, " PORT_RESET\n");
4746 pHlp->pfnPrintf(pHlp, " LINE_STATUS: ");
4747 switch ((pPort->fReg & EHCI_PORT_LINE_STATUS_MASK) >> EHCI_PORT_LINE_STATUS_SHIFT)
4748 {
4749 case 0:
4750 pHlp->pfnPrintf(pHlp, " SE0 (0), not low-speed\n");
4751 break;
4752 case 1:
4753 pHlp->pfnPrintf(pHlp, " K-state (1), low-speed device\n");
4754 break;
4755 case 2:
4756 pHlp->pfnPrintf(pHlp, " J-state (2), not low-speed\n");
4757 break;
4758 default:
4759 case 3:
4760 pHlp->pfnPrintf(pHlp, " Undefined (3)\n");
4761 break;
4762 }
4763 if (pPort->fReg & EHCI_PORT_POWER)
4764 pHlp->pfnPrintf(pHlp, " PORT_POWER\n");
4765 if (pPort->fReg & EHCI_PORT_OWNER)
4766 pHlp->pfnPrintf(pHlp, " PORT_OWNER (1 = owned by companion HC)\n");
4767 if (pPort->fReg & EHCI_PORT_WAKE_ON_CONNECT_ENABLE)
4768 pHlp->pfnPrintf(pHlp, " PORT_WAKE_ON_CONNECT_ENABLE\n");
4769 if (pPort->fReg & EHCI_PORT_WAKE_ON_DISCONNECT_ENABLE)
4770 pHlp->pfnPrintf(pHlp, " PORT_WAKE_ON_DISCONNECT_ENABLE\n");
4771 if (pPort->fReg & EHCI_PORT_WAKE_OVER_CURRENT_ENABLE)
4772 pHlp->pfnPrintf(pHlp, " PORT_WAKE_OVER_CURRENT_ENABLE\n");
4773 }
4774}
4775
4776
4777/**
4778 * @interface_method_impl{PDMDEVREG,pfnReset}
4779 */
4780static DECLCALLBACK(void) ehciR3Reset(PPDMDEVINS pDevIns)
4781{
4782 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
4783 PEHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PEHCICC);
4784 LogFlowFunc(("\n"));
4785
4786 /*
4787 * There is no distinction between cold boot, warm reboot and software reboots,
4788 * all of these are treated as cold boots. We are also doing the initialization
4789 * job of a BIOS or SMM driver.
4790 *
4791 * Important: Don't confuse UsbReset with hardware reset. Hardware reset is
4792 * just one way of getting into the UsbReset state.
4793 */
4794 ehciR3BusStop(pThis, pThisCC);
4795 ehciR3DoReset(pDevIns, pThis, pThisCC, EHCI_USB_RESET, true /* reset devices */);
4796}
4797
4798
4799/**
4800 * Reset notification.
4801 *
4802 * @param pDevIns The device instance data.
4803 */
4804static DECLCALLBACK(void) ehciR3Resume(PPDMDEVINS pDevIns)
4805{
4806 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
4807 PEHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PEHCICC);
4808 LogFlowFunc(("\n"));
4809
4810 /* Restart the frame thread if the timer is active. */
4811 if (pThis->fBusStarted)
4812 {
4813 LogFlowFunc(("Bus was active, restart frame thread\n"));
4814 RTSemEventMultiSignal(pThisCC->hSemEventFrame);
4815 }
4816}
4817
4818
4819/**
4820 * @interface_method_impl{PDMDEVREG,pfnDestruct}
4821 */
4822static DECLCALLBACK(int) ehciR3Destruct(PPDMDEVINS pDevIns)
4823{
4824 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
4825 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
4826 PEHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PEHCICC);
4827 LogFlowFunc(("\n"));
4828
4829 if (pThisCC->hSemEventFrame != NIL_RTSEMEVENTMULTI)
4830 {
4831 RTSemEventMultiDestroy(pThisCC->hSemEventFrame);
4832 pThisCC->hSemEventFrame = NIL_RTSEMEVENTMULTI;
4833 }
4834
4835 if (pThisCC->hSemEventFrameStopped != NIL_RTSEMEVENTMULTI)
4836 {
4837 RTSemEventMultiDestroy(pThisCC->hSemEventFrameStopped);
4838 pThisCC->hSemEventFrameStopped = NIL_RTSEMEVENTMULTI;
4839 }
4840
4841 if (RTCritSectIsInitialized(&pThisCC->CritSect))
4842 RTCritSectDelete(&pThisCC->CritSect);
4843 PDMDevHlpCritSectDelete(pDevIns, &pThis->CsIrq);
4844
4845 /*
4846 * Tear down the per endpoint in-flight tracking...
4847 */
4848
4849 return VINF_SUCCESS;
4850}
4851
4852
4853/**
4854 * @interface_method_impl{PDMDEVREG,pfnConstruct,EHCI constructor}
4855 */
4856static DECLCALLBACK(int) ehciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
4857{
4858 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
4859 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
4860 PEHCICC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PEHCICC);
4861 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
4862 LogFlowFunc(("\n"));
4863
4864 /*
4865 * Read configuration.
4866 */
4867 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DefaultFrameRateKHz|Ports", "");
4868
4869 /* Frame rate option. */
4870 int rc = pHlp->pfnCFGMQueryU32Def(pCfg, "DefaultFrameRateKHz", &pThisCC->uFrameRateDefault, EHCI_DEFAULT_TIMER_FREQ / 1000);
4871 if (RT_FAILURE(rc))
4872 return PDMDEV_SET_ERROR(pDevIns, rc,
4873 N_("EHCI configuration error: failed to read DefaultFrameRateKHz as integer"));
4874
4875 if ( pThisCC->uFrameRateDefault > EHCI_HARDWARE_TIMER_FREQ / 1000
4876 || pThisCC->uFrameRateDefault == 0)
4877 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
4878 N_("EHCI configuration error: DefaultFrameRateKHz must be in range [%u,%u]"),
4879 1, EHCI_HARDWARE_TIMER_FREQ / 1000);
4880
4881 /* Convert to Hertz. */
4882 pThisCC->uFrameRateDefault *= 1000;
4883
4884 /* Number of ports option. */
4885 uint32_t cPorts;
4886 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Ports", &cPorts, EHCI_NDP_DEFAULT);
4887 if (RT_FAILURE(rc))
4888 return PDMDEV_SET_ERROR(pDevIns, rc, N_("EHCI configuration error: failed to read Ports as integer"));
4889
4890 if (cPorts == 0 || cPorts > EHCI_NDP_MAX)
4891 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
4892 N_("EHCI configuration error: Ports must be in range [%u,%u]"),
4893 1, EHCI_NDP_MAX);
4894
4895 /*
4896 * Init instance data.
4897 */
4898 pThisCC->pDevIns = pDevIns;
4899
4900 /* Intel 82801FB/FBM USB2 controller */
4901 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
4902 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
4903
4904 PDMPciDevSetVendorId(pPciDev, 0x8086);
4905 PDMPciDevSetDeviceId(pPciDev, 0x265C);
4906 PDMPciDevSetClassProg(pPciDev, 0x20); /* EHCI */
4907 PDMPciDevSetClassSub(pPciDev, 0x03);
4908 PDMPciDevSetClassBase(pPciDev, 0x0c);
4909 PDMPciDevSetInterruptPin(pPciDev, 0x01);
4910#ifdef VBOX_WITH_MSI_DEVICES
4911 PDMPciDevSetStatus(pPciDev, VBOX_PCI_STATUS_CAP_LIST);
4912 PDMPciDevSetCapabilityList(pPciDev, 0x80);
4913#endif
4914 PDMPciDevSetByte(pPciDev, 0x60, 0x20); /* serial bus release number register; 0x20 = USB 2.0 */
4915 /** @todo USBLEGSUP & USBLEGCTLSTS? Legacy interface for the BIOS (0xEECP+0 & 0xEECP+4) */
4916
4917 pThisCC->RootHub.IBase.pfnQueryInterface = ehciR3RhQueryInterface;
4918 pThisCC->RootHub.IRhPort.pfnGetAvailablePorts = ehciR3RhGetAvailablePorts;
4919 pThisCC->RootHub.IRhPort.pfnGetUSBVersions = ehciR3RhGetUSBVersions;
4920 pThisCC->RootHub.IRhPort.pfnAttach = ehciR3RhAttach;
4921 pThisCC->RootHub.IRhPort.pfnDetach = ehciR3RhDetach;
4922 pThisCC->RootHub.IRhPort.pfnReset = ehciR3RhReset;
4923 pThisCC->RootHub.IRhPort.pfnXferCompletion = ehciR3RhXferCompletion;
4924 pThisCC->RootHub.IRhPort.pfnXferError = ehciR3RhXferError;
4925
4926 /* USB LED */
4927 pThisCC->RootHub.Led.u32Magic = PDMLED_MAGIC;
4928 pThisCC->RootHub.ILeds.pfnQueryStatusLed = ehciR3RhQueryStatusLed;
4929
4930 /*
4931 * Register PCI device and I/O region.
4932 */
4933 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
4934 if (RT_FAILURE(rc))
4935 return rc;
4936
4937#ifdef VBOX_WITH_MSI_DEVICES
4938 PDMMSIREG MsiReg;
4939 RT_ZERO(MsiReg);
4940 MsiReg.cMsiVectors = 1;
4941 MsiReg.iMsiCapOffset = 0x80;
4942 MsiReg.iMsiNextOffset = 0x00;
4943 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
4944 if (RT_FAILURE(rc))
4945 {
4946 PDMPciDevSetCapabilityList(pPciDev, 0x0);
4947 /* That's OK, we can work without MSI */
4948 }
4949#endif
4950
4951 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 0 /*iPciRegion*/, 4096 /*cbRegion*/, PCI_ADDRESS_SPACE_MEM,
4952 ehciMmioWrite, ehciMmioRead, NULL /*pvUser*/,
4953 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED
4954 | IOMMMIO_FLAGS_DBGSTOP_ON_COMPLICATED_WRITE,
4955 "USB EHCI", &pThis->hMmio);
4956 AssertRCReturn(rc, rc);
4957
4958 /* Initialize capability registers */
4959 pThis->cap_length = EHCI_CAPS_REG_SIZE;
4960 pThis->hci_version = 0x100;
4961 /* 31:24 Reserved
4962 * 23:20 Debug Port Number
4963 * 19:17 Reserved
4964 * 16 Port indicators (P_INDICATOR) enabled/disabled
4965 * 15:12 Number of companion controllers (N_CC)
4966 * 11:8 Number of ports per companion controller (N_PCC)
4967 * 7 Port routing controls enabled/disabled
4968 * 6:5 Reserved
4969 * 4 Port power control enabled/disabled -> disabled to simplify matters!
4970 * 3:0 N_PORTS; number of ports
4971 */
4972 /* Currently only number of ports specified */
4973 pThis->hcs_params = cPorts;
4974
4975 /* 31:16 Reserved
4976 * 15:8 EHCI extended capabilities pointer (EECP) (0x40 or greater)
4977 * 7:4 Isochronous scheduling threshold
4978 * 3 Reserved
4979 * 2 Asynchronous schedule park capability (allow several TDs to be handled per async queue head)
4980 * 1 Programmable frame list flag (0=1024 frames fixed)
4981 * 0 64 bits addressability
4982 */
4983 pThis->hcc_params = EHCI_HCC_PARAMS_ISOCHRONOUS_CACHING | EHCI_HCC_PARAMS_ASYNC_SCHEDULE_PARKING;
4984
4985 /*
4986 * Register the saved state data unit.
4987 */
4988 rc = PDMDevHlpSSMRegisterEx(pDevIns, EHCI_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
4989 NULL, NULL, NULL,
4990 NULL, ehciR3SaveExec, NULL,
4991 NULL, ehciLoadExec, NULL);
4992 if (RT_FAILURE(rc))
4993 return rc;
4994
4995 /*
4996 * Attach to the VBox USB RootHub Driver on LUN #0.
4997 */
4998 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->RootHub.IBase, &pThisCC->RootHub.pIBase, "RootHub");
4999 if (RT_FAILURE(rc))
5000 {
5001 AssertMsgFailed(("Configuration error: No roothub driver attached to LUN #0!\n"));
5002 return rc;
5003 }
5004 pThisCC->RootHub.pIRhConn = PDMIBASE_QUERY_INTERFACE(pThisCC->RootHub.pIBase, VUSBIROOTHUBCONNECTOR);
5005 AssertMsgReturn(pThisCC->RootHub.pIRhConn,
5006 ("Configuration error: The driver doesn't provide the VUSBIROOTHUBCONNECTOR interface!\n"),
5007 VERR_PDM_MISSING_INTERFACE);
5008
5009 /*
5010 * Attach status driver (optional).
5011 */
5012 PPDMIBASE pBase;
5013 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->RootHub.IBase, &pBase, "Status Port");
5014 if (RT_SUCCESS(rc))
5015 pThisCC->RootHub.pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
5016 else
5017 AssertLogRelMsgReturn(rc == VERR_PDM_NO_ATTACHED_DRIVER, ("Failed to attach to status driver. rc=%Rrc\n", rc), rc);
5018
5019 /* Set URB parameters. */
5020 rc = VUSBIRhSetUrbParams(pThisCC->RootHub.pIRhConn, sizeof(VUSBURBHCIINT), sizeof(VUSBURBHCITDINT));
5021 if (RT_FAILURE(rc))
5022 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("EHCI: Failed to set URB parameters"));
5023
5024 /*
5025 * Calculate the timer intervals.
5026 * This ASSUMES that the VM timer doesn't change frequency during the run.
5027 */
5028 pThisCC->u64TimerHz = PDMDevHlpTMTimeVirtGetFreq(pDevIns);
5029 ehciR3CalcTimerIntervals(pThis, pThisCC, pThisCC->uFrameRateDefault);
5030 LogFunc(("cTicksPerFrame=%RU64 cTicksPerUsbTick=%RU64\n", pThisCC->cTicksPerFrame, pThisCC->cTicksPerUsbTick));
5031
5032 pThis->fBusStarted = false;
5033
5034 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CsIrq, RT_SRC_POS, "EHCI#%uIrq", iInstance);
5035 if (RT_FAILURE(rc))
5036 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
5037 N_("EHCI: Failed to create critical section"));
5038
5039 rc = RTSemEventMultiCreate(&pThisCC->hSemEventFrame);
5040 AssertRCReturn(rc, rc);
5041
5042 rc = RTSemEventMultiCreate(&pThisCC->hSemEventFrameStopped);
5043 AssertRCReturn(rc, rc);
5044
5045 rc = RTCritSectInit(&pThisCC->CritSect);
5046 if (RT_FAILURE(rc))
5047 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("EHCI: Failed to create critical section"));
5048
5049 rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->hThreadFrame, pThisCC, ehciR3ThreadFrame,
5050 ehciR3ThreadFrameWakeup, 0, RTTHREADTYPE_IO, "EhciFramer");
5051 if (RT_FAILURE(rc))
5052 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("EHCI: Failed to create worker thread"));
5053
5054 /*
5055 * Do a hardware reset.
5056 */
5057 ehciR3DoReset(pDevIns, pThis, pThisCC, EHCI_USB_RESET, false /* don't reset devices */);
5058
5059#ifdef VBOX_WITH_STATISTICS
5060 /*
5061 * Register statistics.
5062 */
5063 PDMDevHlpSTAMRegister(pDevIns, &pThisCC->StatCanceledIsocUrbs, STAMTYPE_COUNTER, "CanceledIsocUrbs", STAMUNIT_OCCURENCES, "Detected canceled isochronous URBs.");
5064 PDMDevHlpSTAMRegister(pDevIns, &pThisCC->StatCanceledGenUrbs, STAMTYPE_COUNTER, "CanceledGenUrbs", STAMUNIT_OCCURENCES, "Detected canceled general URBs.");
5065 PDMDevHlpSTAMRegister(pDevIns, &pThisCC->StatDroppedUrbs, STAMTYPE_COUNTER, "DroppedUrbs", STAMUNIT_OCCURENCES, "Dropped URBs (endpoint halted, or URB canceled).");
5066#endif
5067
5068 /*
5069 * Register debugger info callbacks.
5070 */
5071 PDMDevHlpDBGFInfoRegister(pDevIns, "ehci", "EHCI control registers.", ehciR3InfoRegs);
5072
5073#ifdef DEBUG_sandervl
5074// g_fLogInterruptEPs = true;
5075 g_fLogControlEPs = true;
5076#endif
5077
5078 return VINF_SUCCESS;
5079}
5080
5081#else /* !IN_RING3 */
5082
5083/**
5084 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
5085 */
5086static DECLCALLBACK(int) ehciRZConstruct(PPDMDEVINS pDevIns)
5087{
5088 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5089 PEHCI pThis = PDMDEVINS_2_DATA(pDevIns, PEHCI);
5090
5091 int rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, ehciMmioWrite, ehciMmioRead, NULL /*pvUser*/);
5092 AssertRCReturn(rc, rc);
5093
5094 return VINF_SUCCESS;
5095}
5096
5097#endif /* !IN_RING3 */
5098
5099const PDMDEVREG g_DeviceEHCI =
5100{
5101 /* .u32version = */ PDM_DEVREG_VERSION,
5102 /* .uReserved0 = */ 0,
5103 /* .szName = */ "usb-ehci",
5104 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
5105 /* .fClass = */ PDM_DEVREG_CLASS_BUS_USB,
5106 /* .cMaxInstances = */ ~0U,
5107 /* .uSharedVersion = */ 42,
5108 /* .cbInstanceShared = */ sizeof(EHCI),
5109 /* .cbInstanceCC = */ sizeof(EHCICC),
5110 /* .cbInstanceRC = */ sizeof(EHCIRC),
5111 /* .cMaxPciDevices = */ 1,
5112 /* .cMaxMsixVectors = */ 0,
5113 /* .pszDescription = */ "EHCI USB controller.\n",
5114#if defined(IN_RING3)
5115# ifdef VBOX_IN_EXTPACK
5116 /* .pszRCMod = */ "VBoxEhciRC.rc",
5117 /* .pszR0Mod = */ "VBoxEhciR0.r0",
5118# else
5119 /* .pszRCMod = */ "VBoxDDRC.rc",
5120 /* .pszR0Mod = */ "VBoxDDR0.r0",
5121# endif
5122 /* .pfnConstruct = */ ehciR3Construct,
5123 /* .pfnDestruct = */ ehciR3Destruct,
5124 /* .pfnRelocate = */ NULL,
5125 /* .pfnMemSetup = */ NULL,
5126 /* .pfnPowerOn = */ NULL,
5127 /* .pfnReset = */ ehciR3Reset,
5128 /* .pfnSuspend = */ NULL,
5129 /* .pfnResume = */ ehciR3Resume,
5130 /* .pfnAttach = */ NULL,
5131 /* .pfnDetach = */ NULL,
5132 /* .pfnQueryInterface = */ NULL,
5133 /* .pfnInitComplete = */ NULL,
5134 /* .pfnPowerOff = */ NULL,
5135 /* .pfnSoftReset = */ NULL,
5136 /* .pfnReserved0 = */ NULL,
5137 /* .pfnReserved1 = */ NULL,
5138 /* .pfnReserved2 = */ NULL,
5139 /* .pfnReserved3 = */ NULL,
5140 /* .pfnReserved4 = */ NULL,
5141 /* .pfnReserved5 = */ NULL,
5142 /* .pfnReserved6 = */ NULL,
5143 /* .pfnReserved7 = */ NULL,
5144#elif defined(IN_RING0)
5145 /* .pfnEarlyConstruct = */ NULL,
5146 /* .pfnConstruct = */ ehciRZConstruct,
5147 /* .pfnDestruct = */ NULL,
5148 /* .pfnFinalDestruct = */ NULL,
5149 /* .pfnRequest = */ NULL,
5150 /* .pfnReserved0 = */ NULL,
5151 /* .pfnReserved1 = */ NULL,
5152 /* .pfnReserved2 = */ NULL,
5153 /* .pfnReserved3 = */ NULL,
5154 /* .pfnReserved4 = */ NULL,
5155 /* .pfnReserved5 = */ NULL,
5156 /* .pfnReserved6 = */ NULL,
5157 /* .pfnReserved7 = */ NULL,
5158#elif defined(IN_RC)
5159 /* .pfnConstruct = */ ehciRZConstruct,
5160 /* .pfnReserved0 = */ NULL,
5161 /* .pfnReserved1 = */ NULL,
5162 /* .pfnReserved2 = */ NULL,
5163 /* .pfnReserved3 = */ NULL,
5164 /* .pfnReserved4 = */ NULL,
5165 /* .pfnReserved5 = */ NULL,
5166 /* .pfnReserved6 = */ NULL,
5167 /* .pfnReserved7 = */ NULL,
5168#else
5169# error "Not in IN_RING3, IN_RING0 or IN_RC!"
5170#endif
5171 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
5172};
5173
5174#ifdef VBOX_IN_EXTPACK
5175extern "C" const PDMDEVREG g_DeviceXHCI;
5176
5177# ifdef VBOX_IN_EXTPACK_R3
5178
5179/**
5180 * @callback_method_impl{FNPDMVBOXDEVICESREGISTER}
5181 */
5182extern "C" DECLEXPORT(int) VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t u32Version)
5183{
5184 AssertLogRelMsgReturn(u32Version >= VBOX_VERSION,
5185 ("u32Version=%#x VBOX_VERSION=%#x\n", u32Version, VBOX_VERSION),
5186 VERR_EXTPACK_VBOX_VERSION_MISMATCH);
5187 AssertLogRelMsgReturn(pCallbacks->u32Version == PDM_DEVREG_CB_VERSION,
5188 ("pCallbacks->u32Version=%#x PDM_DEVREG_CB_VERSION=%#x\n", pCallbacks->u32Version, PDM_DEVREG_CB_VERSION),
5189 VERR_VERSION_MISMATCH);
5190
5191 int rc = pCallbacks->pfnRegister(pCallbacks, &g_DeviceEHCI);
5192
5193 /* EHCI and xHCI devices live in the same module. */
5194 extern const PDMDEVREG g_DeviceXHCI;
5195 if (RT_SUCCESS(rc))
5196 rc = pCallbacks->pfnRegister(pCallbacks, &g_DeviceXHCI);
5197
5198 return rc;
5199}
5200
5201# else
5202
5203/** Pointer to the ring-0 device registrations for VBoxEhciR0/RC. */
5204static PCPDMDEVREGR0 g_apDevRegs[] =
5205{
5206 &g_DeviceEHCI,
5207 &g_DeviceXHCI,
5208};
5209
5210/** Module device registration record for VBoxEhciR0/RC. */
5211static PDMDEVMODREGR0 g_ModDevReg =
5212{
5213 /* .u32Version = */ PDM_DEVMODREGR0_VERSION,
5214 /* .cDevRegs = */ RT_ELEMENTS(g_apDevRegs),
5215 /* .papDevRegs = */ &g_apDevRegs[0],
5216 /* .hMod = */ NULL,
5217 /* .ListEntry = */ { NULL, NULL },
5218};
5219
5220
5221DECLEXPORT(int) ModuleInit(void *hMod)
5222{
5223 LogFlow(("VBoxEhciRZ/ModuleInit: %p\n", hMod));
5224 return PDMR0DeviceRegisterModule(hMod, &g_ModDevReg);
5225}
5226
5227
5228DECLEXPORT(void) ModuleTerm(void *hMod)
5229{
5230 LogFlow(("VBoxEhciRZ/ModuleTerm: %p\n", hMod));
5231 PDMR0DeviceDeregisterModule(hMod, &g_ModDevReg);
5232}
5233
5234# endif
5235#endif /* VBOX_IN_EXTPACK */
5236
5237#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
5238
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle
ContactPrivacy/Do Not Sell My InfoTerms of Use