VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/DrvVUSBRootHub.cpp@ 93993

Last change on this file since 93993 was 93993, checked in by vboxsync, 3 years ago

Devices/USB: Fix crash introduced with r150219 when a USB device is detached while the roothub tries to queue an URB but can't find the device. Also cancel all in flight URBs after the device was removed from the lookup tables in order to prevent the roothub from queueing URBs simultaneously, bugref:10196

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 83.7 KB
Line 
1/* $Id: DrvVUSBRootHub.cpp 93993 2022-02-28 18:28:04Z vboxsync $ */
2/** @file
3 * Virtual USB - Root Hub Driver.
4 */
5
6/*
7 * Copyright (C) 2005-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/** @page pg_dev_vusb VUSB - Virtual USB
20 *
21 * @todo read thru this and correct typos. Merge with old docs.
22 *
23 *
24 * The Virtual USB component glues USB devices and host controllers together.
25 * The VUSB takes the form of a PDM driver which is attached to the HCI. USB
26 * devices are created by, attached to, and managed by the VUSB roothub. The
27 * VUSB also exposes an interface which is used by Main to attach and detach
28 * proxied USB devices.
29 *
30 *
31 * @section sec_dev_vusb_urb The Life of an URB
32 *
33 * The URB is created when the HCI calls the roothub (VUSB) method pfnNewUrb.
34 * VUSB has a pool of URBs, if no free URBs are available a new one is
35 * allocated. The returned URB starts life in the ALLOCATED state and all
36 * fields are initialized with sensible defaults.
37 *
38 * The HCI then copies any request data into the URB if it's an host2dev
39 * transfer. It then submits the URB by calling the pfnSubmitUrb roothub
40 * method.
41 *
42 * pfnSubmitUrb will start by checking if it knows the device address, and if
43 * it doesn't the URB is completed with a device-not-ready error. When the
44 * device address is known to it, action is taken based on the kind of
45 * transfer it is. There are four kinds of transfers: 1. control, 2. bulk,
46 * 3. interrupt, and 4. isochronous. In either case something eventually ends
47 * up being submitted to the device.
48 *
49 *
50 * If an URB fails submitting, may or may not be completed. This depends on
51 * heuristics in some cases and on the kind of failure in others. If
52 * pfnSubmitUrb returns a failure, the HCI should retry submitting it at a
53 * later time. If pfnSubmitUrb returns success the URB is submitted, and it
54 * can even been completed.
55 *
56 * The URB is in the IN_FLIGHT state from the time it's successfully submitted
57 * and till it's reaped or cancelled.
58 *
59 * When an URB transfer or in some case submit failure occurs, the pfnXferError
60 * callback of the HCI is consulted about what to do. If pfnXferError indicates
61 * that the URB should be retried, pfnSubmitUrb will fail. If it indicates that
62 * it should fail, the URB will be completed.
63 *
64 * Completing an URB means that the URB status is set and the HCI
65 * pfnXferCompletion callback is invoked with the URB. The HCI is the supposed
66 * to report the transfer status to the guest OS. After completion the URB
67 * is freed and returned to the pool, unless it was cancelled. If it was
68 * cancelled it will have to await reaping before it's actually freed.
69 *
70 *
71 * @subsection subsec_dev_vusb_urb_ctrl Control
72 *
73 * The control transfer is the most complex one, from VUSB's point of view,
74 * with its three stages and being bi-directional. A control transfer starts
75 * with a SETUP packet containing the request description and two basic
76 * parameters. It is followed by zero or more DATA packets which either picks
77 * up incoming data (dev2host) or supplies the request data (host2dev). This
78 * can then be followed by a STATUS packet which gets the status of the whole
79 * transfer.
80 *
81 * What makes the control transfer complicated is that for a host2dev request
82 * the URB is assembled from the SETUP and DATA stage, and for a dev2host
83 * request the returned data must be kept around for the DATA stage. For both
84 * transfer directions the status of the transfer has to be kept around for
85 * the STATUS stage.
86 *
87 * To complicate matters further, VUSB must intercept and in some cases emulate
88 * some of the standard requests in order to keep the virtual device state
89 * correct and provide the correct virtualization of a device.
90 *
91 * @subsection subsec_dev_vusb_urb_bulk Bulk and Interrupt
92 *
93 * The bulk and interrupt transfer types are relativly simple compared to the
94 * control transfer. VUSB is not inspecting the request content or anything,
95 * but passes it down the device.
96 *
97 * @subsection subsec_dev_vusb_urb_isoc Isochronous
98 *
99 * This kind of transfers hasn't yet been implemented.
100 *
101 */
102
103
104/** @page pg_dev_vusb_old VUSB - Virtual USB Core
105 *
106 * The virtual USB core is controlled by the roothub and the underlying HCI
107 * emulator, it is responsible for device addressing, managing configurations,
108 * interfaces and endpoints, assembling and splitting multi-part control
109 * messages and in general acts as a middle layer between the USB device
110 * emulation code and USB HCI emulation code.
111 *
112 * All USB devices are represented by a struct vusb_dev. This structure
113 * contains things like the device state, device address, all the configuration
114 * descriptors, the currently selected configuration and a mapping between
115 * endpoint addresses and endpoint descriptors.
116 *
117 * Each vusb_dev also has a pointer to a vusb_dev_ops structure which serves as
118 * the virtual method table and includes a virtual constructor and destructor.
119 * After a vusb_dev is created it may be attached to a hub device such as a
120 * roothub (using vusbHubAttach). Although each hub structure has cPorts
121 * and cDevices fields, it is the responsibility of the hub device to allocate
122 * a free port for the new device.
123 *
124 * Devices can chose one of two interfaces for dealing with requests, the
125 * synchronous interface or the asynchronous interface. The synchronous
126 * interface is much simpler and ought to be used for devices which are
127 * unlikely to sleep for long periods in order to serve requests. The
128 * asynchronous interface on the other hand is more difficult to use but is
129 * useful for the USB proxy or if one were to write a mass storage device
130 * emulator. Currently the synchronous interface only supports control and bulk
131 * endpoints and is no longer used by anything.
132 *
133 * In order to use the asynchronous interface, the queue_urb, cancel_urb and
134 * pfnUrbReap fields must be set in the devices vusb_dev_ops structure. The
135 * queue_urb method is used to submit a request to a device without blocking,
136 * it returns 1 if successful and 0 on any kind of failure. A successfully
137 * queued URB is completed when the pfnUrbReap method returns it. Each function
138 * address is reference counted so that pfnUrbReap will only be called if there
139 * are URBs outstanding. For a roothub to reap an URB from any one of it's
140 * devices, the vusbRhReapAsyncUrbs() function is used.
141 *
142 * There are four types of messages an URB may contain:
143 * -# Control - represents a single packet of a multi-packet control
144 * transfer, these are only really used by the host controller to
145 * submit the parts to the usb core.
146 * -# Message - the usb core assembles multiple control transfers in
147 * to single message transfers. In this case the data buffer
148 * contains the setup packet in little endian followed by the full
149 * buffer. In the case of an host-to-device control message, the
150 * message packet is created when the STATUS transfer is seen. In
151 * the case of device-to-host messages, the message packet is
152 * created after the SETUP transfer is seen. Also, certain control
153 * requests never go the real device and get handled synchronously.
154 * -# Bulk - Currently the only endpoint type that does error checking
155 * and endpoint halting.
156 * -# Interrupt - The only non-periodic type supported.
157 *
158 * Hubs are special cases of devices, they have a number of downstream ports
159 * that other devices can be attached to and removed from.
160 *
161 * After a device has been attached (vusbHubAttach):
162 * -# The hub attach method is called, which sends a hub status
163 * change message to the OS.
164 * -# The OS resets the device, and it appears on the default
165 * address with it's config 0 selected (a pseudo-config that
166 * contains only 1 interface with 1 endpoint - the default
167 * message pipe).
168 * -# The OS assigns the device a new address and selects an
169 * appropriate config.
170 * -# The device is ready.
171 *
172 * After a device has been detached (vusbDevDetach):
173 * -# All pending URBs are cancelled.
174 * -# The devices address is unassigned.
175 * -# The hub detach method is called which signals the OS
176 * of the status change.
177 * -# The OS unlinks the ED's for that device.
178 *
179 * A device can also request detachment from within its own methods by
180 * calling vusbDevUnplugged().
181 *
182 * Roothubs are responsible for driving the whole system, they are special
183 * cases of hubs and as such implement attach and detach methods, each one
184 * is described by a struct vusb_roothub. Once a roothub has submitted an
185 * URB to the USB core, a number of callbacks to the roothub are required
186 * for when the URB completes, since the roothub typically wants to inform
187 * the OS when transfers are completed.
188 *
189 * There are four callbacks to be concerned with:
190 * -# prepare - This is called after the URB is successfully queued.
191 * -# completion - This is called after the URB completed.
192 * -# error - This is called if the URB errored, some systems have
193 * automatic resubmission of failed requests, so this callback
194 * should keep track of the error count and return 1 if the count
195 * is above the number of allowed resubmissions.
196 * -# halt_ep - This is called after errors on bulk pipes in order
197 * to halt the pipe.
198 *
199 */
200
201
202/*********************************************************************************************************************************
203* Header Files *
204*********************************************************************************************************************************/
205#define LOG_GROUP LOG_GROUP_DRV_VUSB
206#include <VBox/vmm/pdm.h>
207#include <VBox/vmm/vmapi.h>
208#include <VBox/err.h>
209#include <iprt/alloc.h>
210#include <VBox/log.h>
211#include <iprt/time.h>
212#include <iprt/thread.h>
213#include <iprt/semaphore.h>
214#include <iprt/string.h>
215#include <iprt/assert.h>
216#include <iprt/asm.h>
217#include <iprt/uuid.h>
218#include "VUSBInternal.h"
219#include "VBoxDD.h"
220
221
222#define VUSB_ROOTHUB_SAVED_STATE_VERSION 1
223
224
225/**
226 * Data used for reattaching devices on a state load.
227 */
228typedef struct VUSBROOTHUBLOAD
229{
230 /** Timer used once after state load to inform the guest about new devices.
231 * We do this to be sure the guest get any disconnect / reconnect on the
232 * same port. */
233 TMTIMERHANDLE hTimer;
234 /** Number of detached devices. */
235 unsigned cDevs;
236 /** Array of devices which were detached. */
237 PVUSBDEV apDevs[VUSB_DEVICES_MAX];
238} VUSBROOTHUBLOAD;
239
240
241/**
242 * Returns the attached VUSB device for the given port or NULL if none is attached.
243 *
244 * @returns Pointer to the VUSB device or NULL if not found.
245 * @param pThis The VUSB roothub device instance.
246 * @param uPort The port to get the device for.
247 * @param pszWho Caller of this method.
248 *
249 * @note The reference count of the VUSB device structure is retained to prevent it from going away.
250 */
251static PVUSBDEV vusbR3RhGetVUsbDevByPortRetain(PVUSBROOTHUB pThis, uint32_t uPort, const char *pszWho)
252{
253 PVUSBDEV pDev = NULL;
254
255 AssertReturn(uPort < RT_ELEMENTS(pThis->apDevByPort), NULL);
256
257 RTCritSectEnter(&pThis->CritSectDevices);
258
259 pDev = pThis->apDevByPort[uPort];
260 if (RT_LIKELY(pDev))
261 vusbDevRetain(pDev, pszWho);
262
263 RTCritSectLeave(&pThis->CritSectDevices);
264
265 return pDev;
266}
267
268
269/**
270 * Returns the attached VUSB device for the given port or NULL if none is attached.
271 *
272 * @returns Pointer to the VUSB device or NULL if not found.
273 * @param pThis The VUSB roothub device instance.
274 * @param u8Address The address to get the device for.
275 * @param pszWho Caller of this method.
276 *
277 * @note The reference count of the VUSB device structure is retained to prevent it from going away.
278 */
279static PVUSBDEV vusbR3RhGetVUsbDevByAddrRetain(PVUSBROOTHUB pThis, uint8_t u8Address, const char *pszWho)
280{
281 PVUSBDEV pDev = NULL;
282
283 AssertReturn(u8Address < RT_ELEMENTS(pThis->apDevByAddr), NULL);
284
285 RTCritSectEnter(&pThis->CritSectDevices);
286
287 pDev = pThis->apDevByAddr[u8Address];
288 if (RT_LIKELY(pDev))
289 vusbDevRetain(pDev, pszWho);
290
291 RTCritSectLeave(&pThis->CritSectDevices);
292
293 return pDev;
294}
295
296
297/**
298 * Returns a human readable string fromthe given USB speed enum.
299 *
300 * @returns Human readable string.
301 * @param enmSpeed The speed to stringify.
302 */
303static const char *vusbGetSpeedString(VUSBSPEED enmSpeed)
304{
305 const char *pszSpeed = NULL;
306
307 switch (enmSpeed)
308 {
309 case VUSB_SPEED_LOW:
310 pszSpeed = "Low";
311 break;
312 case VUSB_SPEED_FULL:
313 pszSpeed = "Full";
314 break;
315 case VUSB_SPEED_HIGH:
316 pszSpeed = "High";
317 break;
318 case VUSB_SPEED_VARIABLE:
319 pszSpeed = "Variable";
320 break;
321 case VUSB_SPEED_SUPER:
322 pszSpeed = "Super";
323 break;
324 case VUSB_SPEED_SUPERPLUS:
325 pszSpeed = "SuperPlus";
326 break;
327 default:
328 pszSpeed = "Unknown";
329 break;
330 }
331 return pszSpeed;
332}
333
334
335/**
336 * Attaches a device to a specific hub.
337 *
338 * This function is called by the vusb_add_device() and vusbRhAttachDevice().
339 *
340 * @returns VBox status code.
341 * @param pThis The roothub to attach it to.
342 * @param pDev The device to attach.
343 * @thread EMT
344 */
345static int vusbHubAttach(PVUSBROOTHUB pThis, PVUSBDEV pDev)
346{
347 LogFlow(("vusbHubAttach: pThis=%p[%s] pDev=%p[%s]\n", pThis, pThis->pszName, pDev, pDev->pUsbIns->pszName));
348
349 /*
350 * Assign a port.
351 */
352 int iPort = ASMBitFirstSet(&pThis->Bitmap, sizeof(pThis->Bitmap) * 8);
353 if (iPort < 0)
354 {
355 LogRel(("VUSB: No ports available!\n"));
356 return VERR_VUSB_NO_PORTS;
357 }
358 ASMBitClear(&pThis->Bitmap, iPort);
359 pThis->cDevices++;
360 pDev->i16Port = iPort;
361
362 /* Call the device attach helper, so it can initialize its state. */
363 int rc = vusbDevAttach(pDev, pThis);
364 if (RT_SUCCESS(rc))
365 {
366 RTCritSectEnter(&pThis->CritSectDevices);
367 Assert(!pThis->apDevByPort[iPort]);
368 pThis->apDevByPort[iPort] = pDev;
369 RTCritSectLeave(&pThis->CritSectDevices);
370
371 /*
372 * Call the HCI attach routine and let it have its say before the device is
373 * linked into the device list of this hub.
374 */
375 VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);
376 rc = pThis->pIRhPort->pfnAttach(pThis->pIRhPort, iPort, enmSpeed);
377 if (RT_SUCCESS(rc))
378 {
379 LogRel(("VUSB: Attached '%s' to port %d on %s (%sSpeed)\n", pDev->pUsbIns->pszName,
380 iPort, pThis->pszName, vusbGetSpeedString(pDev->pUsbIns->enmSpeed)));
381 return VINF_SUCCESS;
382 }
383
384 /* Remove from the port in case of failure. */
385 RTCritSectEnter(&pThis->CritSectDevices);
386 Assert(!pThis->apDevByPort[iPort]);
387 pThis->apDevByPort[iPort] = NULL;
388 RTCritSectLeave(&pThis->CritSectDevices);
389
390 vusbDevDetach(pDev);
391 }
392
393 ASMBitSet(&pThis->Bitmap, iPort);
394 pThis->cDevices--;
395 pDev->i16Port = -1;
396 LogRel(("VUSB: Failed to attach '%s' to port %d, rc=%Rrc\n", pDev->pUsbIns->pszName, iPort, rc));
397
398 return rc;
399}
400
401
402/**
403 * Detaches the given device from the given roothub.
404 *
405 * @returns VBox status code.
406 * @param pThis The roothub to detach the device from.
407 * @param pDev The device to detach.
408 */
409static int vusbHubDetach(PVUSBROOTHUB pThis, PVUSBDEV pDev)
410{
411 Assert(pDev->i16Port != -1);
412
413 /*
414 * Detach the device and mark the port as available.
415 */
416 unsigned uPort = pDev->i16Port;
417 pDev->i16Port = -1;
418 pThis->pIRhPort->pfnDetach(pThis->pIRhPort, uPort);
419 ASMBitSet(&pThis->Bitmap, uPort);
420 pThis->cDevices--;
421
422 /* Check that it's attached and remove it. */
423 RTCritSectEnter(&pThis->CritSectDevices);
424 Assert(pThis->apDevByPort[uPort] == pDev);
425 pThis->apDevByPort[uPort] = NULL;
426
427 if (pDev->u8Address != VUSB_INVALID_ADDRESS)
428 {
429 Assert(pThis->apDevByAddr[pDev->u8Address] == pDev);
430 pThis->apDevByAddr[pDev->u8Address] = NULL;
431 }
432 RTCritSectLeave(&pThis->CritSectDevices);
433
434 /* Cancel all in-flight URBs from this device. */
435 vusbDevCancelAllUrbs(pDev, true);
436
437 /* Free resources. */
438 vusbDevDetach(pDev);
439 return VINF_SUCCESS;
440}
441
442
443
444/* -=-=-=-=-=- PDMUSBHUBREG methods -=-=-=-=-=- */
445
446/** @interface_method_impl{PDMUSBHUBREG,pfnAttachDevice} */
447static DECLCALLBACK(int) vusbPDMHubAttachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, const char *pszCaptureFilename, uint32_t *piPort)
448{
449 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
450
451 /*
452 * Allocate a new VUSB device and initialize it.
453 */
454 PVUSBDEV pDev = (PVUSBDEV)RTMemAllocZ(sizeof(*pDev));
455 AssertReturn(pDev, VERR_NO_MEMORY);
456 int rc = vusbDevInit(pDev, pUsbIns, pszCaptureFilename);
457 if (RT_SUCCESS(rc))
458 {
459 pUsbIns->pvVUsbDev2 = pDev;
460 rc = vusbHubAttach(pThis, pDev);
461 if (RT_SUCCESS(rc))
462 {
463 *piPort = UINT32_MAX; /// @todo implement piPort
464 return rc;
465 }
466
467 RTMemFree(pDev->paIfStates);
468 pUsbIns->pvVUsbDev2 = NULL;
469 }
470 vusbDevRelease(pDev, "vusbPDMHubAttachDevice");
471 return rc;
472}
473
474
475/** @interface_method_impl{PDMUSBHUBREG,pfnDetachDevice} */
476static DECLCALLBACK(int) vusbPDMHubDetachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, uint32_t iPort)
477{
478 RT_NOREF(iPort);
479 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
480 PVUSBDEV pDev = (PVUSBDEV)pUsbIns->pvVUsbDev2;
481 Assert(pDev);
482
483 LogRel(("VUSB: Detached '%s' from port %u on %s\n", pDev->pUsbIns->pszName, pDev->i16Port, pThis->pszName));
484
485 /*
486 * Deal with pending async reset.
487 * (anything but reset)
488 */
489 vusbDevSetStateCmp(pDev, VUSB_DEVICE_STATE_DEFAULT, VUSB_DEVICE_STATE_RESET);
490 vusbHubDetach(pThis, pDev);
491 vusbDevRelease(pDev, "vusbPDMHubDetachDevice");
492 return VINF_SUCCESS;
493}
494
495/**
496 * The hub registration structure.
497 */
498static const PDMUSBHUBREG g_vusbHubReg =
499{
500 PDM_USBHUBREG_VERSION,
501 vusbPDMHubAttachDevice,
502 vusbPDMHubDetachDevice,
503 PDM_USBHUBREG_VERSION
504};
505
506
507/* -=-=-=-=-=- VUSBIROOTHUBCONNECTOR methods -=-=-=-=-=- */
508
509
510/**
511 * Callback for freeing an URB.
512 * @param pUrb The URB to free.
513 */
514static DECLCALLBACK(void) vusbRhFreeUrb(PVUSBURB pUrb)
515{
516 /*
517 * Assert sanity.
518 */
519 vusbUrbAssert(pUrb);
520 PVUSBROOTHUB pRh = (PVUSBROOTHUB)pUrb->pVUsb->pvFreeCtx;
521 Assert(pRh);
522
523 Assert(pUrb->enmState != VUSBURBSTATE_FREE);
524
525#ifdef LOG_ENABLED
526 vusbUrbTrace(pUrb, "vusbRhFreeUrb", true);
527#endif
528
529 /*
530 * Free the URB description (logging builds only).
531 */
532 if (pUrb->pszDesc)
533 {
534 RTStrFree(pUrb->pszDesc);
535 pUrb->pszDesc = NULL;
536 }
537
538 /* The URB comes from the roothub if there is no device (invalid address). */
539 if (pUrb->pVUsb->pDev)
540 {
541 PVUSBDEV pDev = pUrb->pVUsb->pDev;
542
543 vusbUrbPoolFree(&pUrb->pVUsb->pDev->UrbPool, pUrb);
544 vusbDevRelease(pDev, "vusbRhFreeUrb");
545 }
546 else
547 vusbUrbPoolFree(&pRh->UrbPool, pUrb);
548}
549
550
551/**
552 * Worker routine for vusbRhConnNewUrb().
553 */
554static PVUSBURB vusbRhNewUrb(PVUSBROOTHUB pRh, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
555 VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
556{
557 RT_NOREF(pszTag);
558 PVUSBURBPOOL pUrbPool = &pRh->UrbPool;
559
560 if (RT_UNLIKELY(cbData > (32 * _1M)))
561 {
562 LogFunc(("Bad URB size (%u)!\n", cbData));
563 return NULL;
564 }
565
566 PVUSBDEV pDev;
567 if (uPort == VUSB_DEVICE_PORT_INVALID)
568 pDev = vusbR3RhGetVUsbDevByAddrRetain(pRh, DstAddress, "vusbRhNewUrb");
569 else
570 pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort, "vusbRhNewUrb");
571
572 if (pDev)
573 pUrbPool = &pDev->UrbPool;
574
575 PVUSBURB pUrb = vusbUrbPoolAlloc(pUrbPool, enmType, enmDir, cbData,
576 pRh->cbHci, pRh->cbHciTd, cTds);
577 if (RT_LIKELY(pUrb))
578 {
579 pUrb->pVUsb->pvFreeCtx = pRh;
580 pUrb->pVUsb->pfnFree = vusbRhFreeUrb;
581 pUrb->DstAddress = DstAddress;
582 pUrb->pVUsb->pDev = pDev;
583
584#ifdef LOG_ENABLED
585 const char *pszType = NULL;
586
587 switch(pUrb->enmType)
588 {
589 case VUSBXFERTYPE_CTRL:
590 pszType = "ctrl";
591 break;
592 case VUSBXFERTYPE_INTR:
593 pszType = "intr";
594 break;
595 case VUSBXFERTYPE_BULK:
596 pszType = "bulk";
597 break;
598 case VUSBXFERTYPE_ISOC:
599 pszType = "isoc";
600 break;
601 default:
602 pszType = "invld";
603 break;
604 }
605
606 pRh->iSerial = (pRh->iSerial + 1) % 10000;
607 RTStrAPrintf(&pUrb->pszDesc, "URB %p %s%c%04d (%s)", pUrb, pszType,
608 (pUrb->enmDir == VUSBDIRECTION_IN) ? '<' : ((pUrb->enmDir == VUSBDIRECTION_SETUP) ? 's' : '>'),
609 pRh->iSerial, pszTag ? pszTag : "<none>");
610
611 vusbUrbTrace(pUrb, "vusbRhNewUrb", false);
612#endif
613 }
614
615 return pUrb;
616}
617
618
619/**
620 * Calculate frame timer variables given a frame rate.
621 */
622static void vusbRhR3CalcTimerIntervals(PVUSBROOTHUB pThis, uint32_t u32FrameRate)
623{
624 pThis->nsWait = RT_NS_1SEC / u32FrameRate;
625 pThis->uFrameRate = u32FrameRate;
626 /* Inform the HCD about the new frame rate. */
627 pThis->pIRhPort->pfnFrameRateChanged(pThis->pIRhPort, u32FrameRate);
628}
629
630
631/**
632 * Calculates the new frame rate based on the idle detection and number of idle
633 * cycles.
634 *
635 * @returns nothing.
636 * @param pThis The roothub instance data.
637 * @param fIdle Flag whether the last frame didn't produce any activity.
638 */
639static void vusbRhR3FrameRateCalcNew(PVUSBROOTHUB pThis, bool fIdle)
640{
641 uint32_t uNewFrameRate = pThis->uFrameRate;
642
643 /*
644 * Adjust the frame timer interval based on idle detection.
645 */
646 if (fIdle)
647 {
648 pThis->cIdleCycles++;
649 /* Set the new frame rate based on how long we've been idle. Tunable. */
650 switch (pThis->cIdleCycles)
651 {
652 case 4: uNewFrameRate = 500; break; /* 2ms interval */
653 case 16:uNewFrameRate = 125; break; /* 8ms interval */
654 case 24:uNewFrameRate = 50; break; /* 20ms interval */
655 default: break;
656 }
657 /* Avoid overflow. */
658 if (pThis->cIdleCycles > 60000)
659 pThis->cIdleCycles = 20000;
660 }
661 else
662 {
663 if (pThis->cIdleCycles)
664 {
665 pThis->cIdleCycles = 0;
666 uNewFrameRate = pThis->uFrameRateDefault;
667 }
668 }
669
670 if ( uNewFrameRate != pThis->uFrameRate
671 && uNewFrameRate)
672 {
673 LogFlow(("Frame rate changed from %u to %u\n", pThis->uFrameRate, uNewFrameRate));
674 vusbRhR3CalcTimerIntervals(pThis, uNewFrameRate);
675 }
676}
677
678
679/**
680 * The core frame processing routine keeping track of the elapsed time and calling into
681 * the device emulation above us to do the work.
682 *
683 * @returns Relative timespan when to process the next frame.
684 * @param pThis The roothub instance data.
685 * @param fCallback Flag whether this method is called from the URB completion callback or
686 * from the worker thread (only used for statistics).
687 */
688DECLHIDDEN(uint64_t) vusbRhR3ProcessFrame(PVUSBROOTHUB pThis, bool fCallback)
689{
690 uint64_t tsNext = 0;
691 uint64_t tsNanoStart = RTTimeNanoTS();
692
693 /* Don't do anything if we are not supposed to process anything (EHCI and XHCI). */
694 if (!pThis->uFrameRateDefault)
695 return 0;
696
697 if (ASMAtomicXchgBool(&pThis->fFrameProcessing, true))
698 return pThis->nsWait;
699
700 if ( tsNanoStart > pThis->tsFrameProcessed
701 && tsNanoStart - pThis->tsFrameProcessed >= 750 * RT_NS_1US)
702 {
703 LogFlowFunc(("Starting new frame at ts %llu\n", tsNanoStart));
704
705 bool fIdle = pThis->pIRhPort->pfnStartFrame(pThis->pIRhPort, 0 /* u32FrameNo */);
706 vusbRhR3FrameRateCalcNew(pThis, fIdle);
707
708 uint64_t tsNow = RTTimeNanoTS();
709 tsNext = (tsNanoStart + pThis->nsWait) > tsNow ? (tsNanoStart + pThis->nsWait) - tsNow : 0;
710 pThis->tsFrameProcessed = tsNanoStart;
711 LogFlowFunc(("Current frame took %llu nano seconds to process, next frame in %llu ns\n", tsNow - tsNanoStart, tsNext));
712 if (fCallback)
713 STAM_COUNTER_INC(&pThis->StatFramesProcessedClbk);
714 else
715 STAM_COUNTER_INC(&pThis->StatFramesProcessedThread);
716 }
717 else
718 {
719 tsNext = (pThis->tsFrameProcessed + pThis->nsWait) > tsNanoStart ? (pThis->tsFrameProcessed + pThis->nsWait) - tsNanoStart : 0;
720 LogFlowFunc(("Next frame is too far away in the future, waiting... (tsNanoStart=%llu tsFrameProcessed=%llu)\n",
721 tsNanoStart, pThis->tsFrameProcessed));
722 }
723
724 ASMAtomicXchgBool(&pThis->fFrameProcessing, false);
725 LogFlowFunc(("returns %llu\n", tsNext));
726 return tsNext;
727}
728
729
730/**
731 * Worker for processing frames periodically.
732 *
733 * @returns VBox status code.
734 * @param pDrvIns The driver instance.
735 * @param pThread The PDM thread structure for the thread this worker runs on.
736 */
737static DECLCALLBACK(int) vusbRhR3PeriodFrameWorker(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
738{
739 RT_NOREF(pDrvIns);
740 int rc = VINF_SUCCESS;
741 PVUSBROOTHUB pThis = (PVUSBROOTHUB)pThread->pvUser;
742
743 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
744 return VINF_SUCCESS;
745
746 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
747 {
748 while ( !ASMAtomicReadU32(&pThis->uFrameRateDefault)
749 && pThread->enmState == PDMTHREADSTATE_RUNNING)
750 {
751 /* Signal the waiter that we are stopped now. */
752 rc = RTSemEventMultiSignal(pThis->hSemEventPeriodFrameStopped);
753 AssertRC(rc);
754
755 rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrame, RT_INDEFINITE_WAIT);
756 RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
757
758 /*
759 * Notify the device above about the frame rate changed if we are supposed to
760 * process frames.
761 */
762 uint32_t uFrameRate = ASMAtomicReadU32(&pThis->uFrameRateDefault);
763 if (uFrameRate)
764 vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
765 }
766
767 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc);
768 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
769 break;
770
771 uint64_t tsNext = vusbRhR3ProcessFrame(pThis, false /* fCallback */);
772
773 if (tsNext >= 250 * RT_NS_1US)
774 {
775 rc = RTSemEventMultiWaitEx(pThis->hSemEventPeriodFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE,
776 tsNext);
777 AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc));
778 RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
779 }
780 }
781
782 return VINF_SUCCESS;
783}
784
785
786/**
787 * Unblock the periodic frame thread so it can respond to a state change.
788 *
789 * @returns VBox status code.
790 * @param pDrvIns The driver instance.
791 * @param pThread The send thread.
792 */
793static DECLCALLBACK(int) vusbRhR3PeriodFrameWorkerWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
794{
795 RT_NOREF(pThread);
796 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
797 return RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
798}
799
800
801/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetUrbParams} */
802static DECLCALLBACK(int) vusbRhSetUrbParams(PVUSBIROOTHUBCONNECTOR pInterface, size_t cbHci, size_t cbHciTd)
803{
804 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
805
806 pRh->cbHci = cbHci;
807 pRh->cbHciTd = cbHciTd;
808
809 return VINF_SUCCESS;
810}
811
812
813/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReset} */
814static DECLCALLBACK(int) vusbR3RhReset(PVUSBIROOTHUBCONNECTOR pInterface, bool fResetOnLinux)
815{
816 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
817 return pRh->pIRhPort->pfnReset(pRh->pIRhPort, fResetOnLinux);
818}
819
820
821/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOn} */
822static DECLCALLBACK(int) vusbR3RhPowerOn(PVUSBIROOTHUBCONNECTOR pInterface)
823{
824 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
825 LogFlow(("vusR3bRhPowerOn: pRh=%p\n", pRh));
826
827 Assert( pRh->enmState != VUSB_DEVICE_STATE_DETACHED
828 && pRh->enmState != VUSB_DEVICE_STATE_RESET);
829
830 if (pRh->enmState == VUSB_DEVICE_STATE_ATTACHED)
831 pRh->enmState = VUSB_DEVICE_STATE_POWERED;
832
833 return VINF_SUCCESS;
834}
835
836
837/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOff} */
838static DECLCALLBACK(int) vusbR3RhPowerOff(PVUSBIROOTHUBCONNECTOR pInterface)
839{
840 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
841 LogFlow(("vusbR3RhDevPowerOff: pThis=%p\n", pThis));
842
843 Assert( pThis->enmState != VUSB_DEVICE_STATE_DETACHED
844 && pThis->enmState != VUSB_DEVICE_STATE_RESET);
845
846 /*
847 * Cancel all URBs and reap them.
848 */
849 VUSBIRhCancelAllUrbs(&pThis->IRhConnector);
850 for (uint32_t uPort = 0; uPort < RT_ELEMENTS(pThis->apDevByPort); uPort++)
851 VUSBIRhReapAsyncUrbs(&pThis->IRhConnector, uPort, 0);
852
853 pThis->enmState = VUSB_DEVICE_STATE_ATTACHED;
854 return VINF_SUCCESS;
855}
856
857
858/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnNewUrb} */
859static DECLCALLBACK(PVUSBURB) vusbRhConnNewUrb(PVUSBIROOTHUBCONNECTOR pInterface, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
860 VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
861{
862 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
863 return vusbRhNewUrb(pRh, DstAddress, uPort, enmType, enmDir, cbData, cTds, pszTag);
864}
865
866
867/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnFreeUrb} */
868static DECLCALLBACK(int) vusbRhConnFreeUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
869{
870 RT_NOREF(pInterface);
871 pUrb->pVUsb->pfnFree(pUrb);
872 return VINF_SUCCESS;
873}
874
875
876/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSubmitUrb} */
877static DECLCALLBACK(int) vusbRhSubmitUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb, PPDMLED pLed)
878{
879 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
880 STAM_PROFILE_START(&pRh->StatSubmitUrb, a);
881
882#ifdef VBOX_WITH_STATISTICS
883 /*
884 * Total and per-type submit statistics.
885 */
886 Assert(pUrb->enmType >= 0 && pUrb->enmType < (int)RT_ELEMENTS(pRh->aTypes));
887 STAM_COUNTER_INC(&pRh->Total.StatUrbsSubmitted);
888 STAM_COUNTER_INC(&pRh->aTypes[pUrb->enmType].StatUrbsSubmitted);
889
890 STAM_COUNTER_ADD(&pRh->Total.StatReqBytes, pUrb->cbData);
891 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqBytes, pUrb->cbData);
892 if (pUrb->enmDir == VUSBDIRECTION_IN)
893 {
894 STAM_COUNTER_ADD(&pRh->Total.StatReqReadBytes, pUrb->cbData);
895 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqReadBytes, pUrb->cbData);
896 }
897 else
898 {
899 STAM_COUNTER_ADD(&pRh->Total.StatReqWriteBytes, pUrb->cbData);
900 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqWriteBytes, pUrb->cbData);
901 }
902
903 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
904 {
905 STAM_COUNTER_ADD(&pRh->StatIsocReqPkts, pUrb->cIsocPkts);
906 if (pUrb->enmDir == VUSBDIRECTION_IN)
907 STAM_COUNTER_ADD(&pRh->StatIsocReqReadPkts, pUrb->cIsocPkts);
908 else
909 STAM_COUNTER_ADD(&pRh->StatIsocReqWritePkts, pUrb->cIsocPkts);
910 }
911#endif
912
913 /* If there is a sniffer on the roothub record the URB there. */
914 if (pRh->hSniffer != VUSBSNIFFER_NIL)
915 {
916 int rc = VUSBSnifferRecordEvent(pRh->hSniffer, pUrb, VUSBSNIFFEREVENT_SUBMIT);
917 if (RT_FAILURE(rc))
918 LogRel(("VUSB: Capturing URB submit event on the root hub failed with %Rrc\n", rc));
919 }
920
921 /*
922 * The device was resolved when we allocated the URB.
923 * Submit it to the device if we found it, if not fail with device-not-ready.
924 */
925 int rc;
926 if ( pUrb->pVUsb->pDev
927 && pUrb->pVUsb->pDev->pUsbIns)
928 {
929 switch (pUrb->enmDir)
930 {
931 case VUSBDIRECTION_IN:
932 pLed->Asserted.s.fReading = pLed->Actual.s.fReading = 1;
933 rc = vusbUrbSubmit(pUrb);
934 pLed->Actual.s.fReading = 0;
935 break;
936 case VUSBDIRECTION_OUT:
937 pLed->Asserted.s.fWriting = pLed->Actual.s.fWriting = 1;
938 rc = vusbUrbSubmit(pUrb);
939 pLed->Actual.s.fWriting = 0;
940 break;
941 default:
942 rc = vusbUrbSubmit(pUrb);
943 break;
944 }
945
946 if (RT_FAILURE(rc))
947 {
948 LogFlow(("vusbRhSubmitUrb: freeing pUrb=%p\n", pUrb));
949 pUrb->pVUsb->pfnFree(pUrb);
950 }
951 }
952 else
953 {
954 Log(("vusb: pRh=%p: SUBMIT: Address %i not found!!!\n", pRh, pUrb->DstAddress));
955
956 pUrb->enmState = VUSBURBSTATE_REAPED;
957 pUrb->enmStatus = VUSBSTATUS_DNR;
958 vusbUrbCompletionRhEx(pRh, pUrb);
959 rc = VINF_SUCCESS;
960 }
961
962 STAM_PROFILE_STOP(&pRh->StatSubmitUrb, a);
963 return rc;
964}
965
966
967static DECLCALLBACK(int) vusbRhReapAsyncUrbsWorker(PVUSBDEV pDev, RTMSINTERVAL cMillies)
968{
969 if (!cMillies)
970 vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, 0);
971 else
972 {
973 uint64_t u64Start = RTTimeMilliTS();
974 do
975 {
976 vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, RT_MIN(cMillies >> 8, 10));
977 } while ( !RTListIsEmpty(&pDev->LstAsyncUrbs)
978 && RTTimeMilliTS() - u64Start < cMillies);
979 }
980
981 return VINF_SUCCESS;
982}
983
984/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReapAsyncUrbs} */
985static DECLCALLBACK(void) vusbRhReapAsyncUrbs(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, RTMSINTERVAL cMillies)
986{
987 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); NOREF(pRh);
988 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort, "vusbRhReapAsyncUrbs");
989
990 if (!pDev)
991 return;
992
993 if (!RTListIsEmpty(&pDev->LstAsyncUrbs))
994 {
995 STAM_PROFILE_START(&pRh->StatReapAsyncUrbs, a);
996 int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhReapAsyncUrbsWorker, 2, pDev, cMillies);
997 AssertRC(rc);
998 STAM_PROFILE_STOP(&pRh->StatReapAsyncUrbs, a);
999 }
1000
1001 vusbDevRelease(pDev, "vusbRhReapAsyncUrbs");
1002}
1003
1004
1005/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelUrbsEp} */
1006static DECLCALLBACK(int) vusbRhCancelUrbsEp(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
1007{
1008 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1009 AssertReturn(pRh, VERR_INVALID_PARAMETER);
1010 AssertReturn(pUrb, VERR_INVALID_PARAMETER);
1011
1012 /// @todo This method of URB canceling may not work on non-Linux hosts.
1013 /*
1014 * Cancel and reap the URB(s) on an endpoint.
1015 */
1016 LogFlow(("vusbRhCancelUrbsEp: pRh=%p pUrb=%p\n", pRh, pUrb));
1017
1018 vusbUrbCancelAsync(pUrb, CANCELMODE_UNDO);
1019
1020 /* The reaper thread will take care of completing the URB. */
1021
1022 return VINF_SUCCESS;
1023}
1024
1025/**
1026 * Worker doing the actual cancelling of all outstanding URBs on the device I/O thread.
1027 *
1028 * @returns VBox status code.
1029 * @param pDev USB device instance data.
1030 */
1031static DECLCALLBACK(int) vusbRhCancelAllUrbsWorker(PVUSBDEV pDev)
1032{
1033 /*
1034 * Cancel the URBS.
1035 *
1036 * Not using th CritAsyncUrbs critical section here is safe
1037 * as the I/O thread is the only thread accessing this struture at the
1038 * moment.
1039 */
1040 PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
1041 RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
1042 {
1043 PVUSBURB pUrb = pVUsbUrb->pUrb;
1044 /* Call the worker directly. */
1045 vusbUrbCancelWorker(pUrb, CANCELMODE_FAIL);
1046 }
1047
1048 return VINF_SUCCESS;
1049}
1050
1051/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelAllUrbs} */
1052static DECLCALLBACK(void) vusbRhCancelAllUrbs(PVUSBIROOTHUBCONNECTOR pInterface)
1053{
1054 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1055
1056 RTCritSectEnter(&pThis->CritSectDevices);
1057 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
1058 {
1059 PVUSBDEV pDev = pThis->apDevByPort[i];
1060 if (pDev)
1061 vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhCancelAllUrbsWorker, 1, pDev);
1062 }
1063 RTCritSectLeave(&pThis->CritSectDevices);
1064}
1065
1066/**
1067 * Worker doing the actual cancelling of all outstanding per-EP URBs on the
1068 * device I/O thread.
1069 *
1070 * @returns VBox status code.
1071 * @param pDev USB device instance data.
1072 * @param EndPt Endpoint number.
1073 * @param enmDir Endpoint direction.
1074 */
1075static DECLCALLBACK(int) vusbRhAbortEpWorker(PVUSBDEV pDev, int EndPt, VUSBDIRECTION enmDir)
1076{
1077 /*
1078 * Iterate the URBs, find ones corresponding to given EP, and cancel them.
1079 */
1080 PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
1081 RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
1082 {
1083 PVUSBURB pUrb = pVUsbUrb->pUrb;
1084
1085 Assert(pUrb->pVUsb->pDev == pDev);
1086
1087 /* For the default control EP, direction does not matter. */
1088 if (pUrb->EndPt == EndPt && (pUrb->enmDir == enmDir || !EndPt))
1089 {
1090 LogFlow(("%s: vusbRhAbortEpWorker: CANCELING URB\n", pUrb->pszDesc));
1091 int rc = vusbUrbCancelWorker(pUrb, CANCELMODE_UNDO);
1092 AssertRC(rc);
1093 }
1094 }
1095
1096 return VINF_SUCCESS;
1097}
1098
1099
1100/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnAbortEp} */
1101static DECLCALLBACK(int) vusbRhAbortEp(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, int EndPt, VUSBDIRECTION enmDir)
1102{
1103 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1104 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort, "vusbRhAbortEp");
1105
1106 if (pDev->pHub != pRh)
1107 AssertFailedReturn(VERR_INVALID_PARAMETER);
1108
1109 vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhAbortEpWorker, 3, pDev, EndPt, enmDir);
1110 vusbDevRelease(pDev, "vusbRhAbortEp");
1111
1112 /* The reaper thread will take care of completing the URB. */
1113
1114 return VINF_SUCCESS;
1115}
1116
1117
1118/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetPeriodicFrameProcessing} */
1119static DECLCALLBACK(int) vusbRhSetFrameProcessing(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate)
1120{
1121 int rc = VINF_SUCCESS;
1122 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1123
1124 /* Create the frame thread lazily. */
1125 if ( !pThis->hThreadPeriodFrame
1126 && uFrameRate)
1127 {
1128 ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
1129 pThis->uFrameRate = uFrameRate;
1130 vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
1131
1132 rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrame);
1133 AssertRCReturn(rc, rc);
1134
1135 rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrameStopped);
1136 AssertRCReturn(rc, rc);
1137
1138 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->hThreadPeriodFrame, pThis, vusbRhR3PeriodFrameWorker,
1139 vusbRhR3PeriodFrameWorkerWakeup, 0, RTTHREADTYPE_IO, "VUsbPeriodFrm");
1140 AssertRCReturn(rc, rc);
1141
1142 VMSTATE enmState = PDMDrvHlpVMState(pThis->pDrvIns);
1143 if ( enmState == VMSTATE_RUNNING
1144 || enmState == VMSTATE_RUNNING_LS)
1145 {
1146 rc = PDMDrvHlpThreadResume(pThis->pDrvIns, pThis->hThreadPeriodFrame);
1147 AssertRCReturn(rc, rc);
1148 }
1149 }
1150 else if ( pThis->hThreadPeriodFrame
1151 && !uFrameRate)
1152 {
1153 /* Stop processing. */
1154 uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
1155 if (uFrameRateOld)
1156 {
1157 rc = RTSemEventMultiReset(pThis->hSemEventPeriodFrameStopped);
1158 AssertRC(rc);
1159
1160 /* Signal the frame thread to stop. */
1161 RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
1162
1163 /* Wait for signal from the thread that it stopped. */
1164 rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrameStopped, RT_INDEFINITE_WAIT);
1165 AssertRC(rc);
1166 }
1167 }
1168 else if ( pThis->hThreadPeriodFrame
1169 && uFrameRate)
1170 {
1171 /* Just switch to the new frame rate and let the periodic frame thread pick it up. */
1172 uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
1173
1174 /* Signal the frame thread to continue if it was stopped. */
1175 if (!uFrameRateOld)
1176 RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
1177 }
1178
1179 return rc;
1180}
1181
1182
1183/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnGetPeriodicFrameRate} */
1184static DECLCALLBACK(uint32_t) vusbRhGetPeriodicFrameRate(PVUSBIROOTHUBCONNECTOR pInterface)
1185{
1186 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1187
1188 return pThis->uFrameRate;
1189}
1190
1191/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnUpdateIsocFrameDelta} */
1192static DECLCALLBACK(uint32_t) vusbRhUpdateIsocFrameDelta(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort,
1193 int EndPt, VUSBDIRECTION enmDir, uint16_t uNewFrameID, uint8_t uBits)
1194{
1195 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1196 AssertReturn(pRh, 0);
1197 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort, "vusbRhUpdateIsocFrameDelta"); AssertPtr(pDev);
1198 PVUSBPIPE pPipe = &pDev->aPipes[EndPt];
1199 uint32_t *puLastFrame;
1200 int32_t uFrameDelta;
1201 uint32_t uMaxVal = 1 << uBits;
1202
1203 puLastFrame = enmDir == VUSBDIRECTION_IN ? &pPipe->uLastFrameIn : &pPipe->uLastFrameOut;
1204 uFrameDelta = uNewFrameID - *puLastFrame;
1205 *puLastFrame = uNewFrameID;
1206 /* Take care of wrap-around. */
1207 if (uFrameDelta < 0)
1208 uFrameDelta += uMaxVal;
1209
1210 vusbDevRelease(pDev, "vusbRhUpdateIsocFrameDelta");
1211 return (uint16_t)uFrameDelta;
1212}
1213
1214
1215/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevReset} */
1216static DECLCALLBACK(int) vusbR3RhDevReset(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, bool fResetOnLinux,
1217 PFNVUSBRESETDONE pfnDone, void *pvUser, PVM pVM)
1218{
1219 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1220 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort, "vusbR3RhDevReset");
1221 AssertPtr(pDev);
1222
1223 int rc = VUSBIDevReset(&pDev->IDevice, fResetOnLinux, pfnDone, pvUser, pVM);
1224 vusbDevRelease(pDev, "vusbR3RhDevReset");
1225 return rc;
1226}
1227
1228
1229/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOn} */
1230static DECLCALLBACK(int) vusbR3RhDevPowerOn(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
1231{
1232 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1233 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort, "vusbR3RhDevPowerOn");
1234 AssertPtr(pDev);
1235
1236 int rc = VUSBIDevPowerOn(&pDev->IDevice);
1237 vusbDevRelease(pDev, "vusbR3RhDevPowerOn");
1238 return rc;
1239}
1240
1241
1242/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOff} */
1243static DECLCALLBACK(int) vusbR3RhDevPowerOff(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
1244{
1245 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1246 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort, "vusbR3RhDevPowerOff");
1247 AssertPtr(pDev);
1248
1249 int rc = VUSBIDevPowerOff(&pDev->IDevice);
1250 vusbDevRelease(pDev, "vusbR3RhDevPowerOff");
1251 return rc;
1252}
1253
1254
1255/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetState} */
1256static DECLCALLBACK(VUSBDEVICESTATE) vusbR3RhDevGetState(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
1257{
1258 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1259 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort, "vusbR3RhDevGetState");
1260 AssertPtr(pDev);
1261
1262 VUSBDEVICESTATE enmState = VUSBIDevGetState(&pDev->IDevice);
1263 vusbDevRelease(pDev, "vusbR3RhDevGetState");
1264 return enmState;
1265}
1266
1267
1268/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevIsSavedStateSupported} */
1269static DECLCALLBACK(bool) vusbR3RhDevIsSavedStateSupported(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
1270{
1271 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1272 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort, "vusbR3RhDevIsSavedStateSupported");
1273 AssertPtr(pDev);
1274
1275 bool fSavedStateSupported = VUSBIDevIsSavedStateSupported(&pDev->IDevice);
1276 vusbDevRelease(pDev, "vusbR3RhDevIsSavedStateSupported");
1277 return fSavedStateSupported;
1278}
1279
1280
1281/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetSpeed} */
1282static DECLCALLBACK(VUSBSPEED) vusbR3RhDevGetSpeed(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
1283{
1284 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1285 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort, "vusbR3RhDevGetSpeed");
1286 AssertPtr(pDev);
1287
1288 VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);
1289 vusbDevRelease(pDev, "vusbR3RhDevGetSpeed");
1290 return enmSpeed;
1291}
1292
1293
1294/**
1295 * @callback_method_impl{FNSSMDRVSAVEPREP, All URBs needs to be canceled.}
1296 */
1297static DECLCALLBACK(int) vusbR3RhSavePrep(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1298{
1299 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1300 LogFlow(("vusbR3RhSavePrep:\n"));
1301 RT_NOREF(pSSM);
1302
1303 /*
1304 * Detach all proxied devices.
1305 */
1306 RTCritSectEnter(&pThis->CritSectDevices);
1307
1308 /** @todo we a) can't tell which are proxied, and b) this won't work well when continuing after saving! */
1309 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
1310 {
1311 PVUSBDEV pDev = pThis->apDevByPort[i];
1312 if (pDev)
1313 {
1314 if (!VUSBIDevIsSavedStateSupported(&pDev->IDevice))
1315 {
1316 int rc = vusbHubDetach(pThis, pDev);
1317 AssertRC(rc);
1318
1319 /*
1320 * Save the device pointers here so we can reattach them afterwards.
1321 * This will work fine even if the save fails since the Done handler is
1322 * called unconditionally if the Prep handler was called.
1323 */
1324 pThis->apDevByPort[i] = pDev;
1325 }
1326 }
1327 }
1328
1329 RTCritSectLeave(&pThis->CritSectDevices);
1330
1331 /*
1332 * Kill old load data which might be hanging around.
1333 */
1334 if (pThis->pLoad)
1335 {
1336 PDMDrvHlpTimerDestroy(pDrvIns, pThis->pLoad->hTimer);
1337 pThis->pLoad->hTimer = NIL_TMTIMERHANDLE;
1338 PDMDrvHlpMMHeapFree(pDrvIns, pThis->pLoad);
1339 pThis->pLoad = NULL;
1340 }
1341
1342 return VINF_SUCCESS;
1343}
1344
1345
1346/**
1347 * @callback_method_impl{FNSSMDRVSAVEDONE}
1348 */
1349static DECLCALLBACK(int) vusbR3RhSaveDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1350{
1351 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1352 PVUSBDEV aPortsOld[VUSB_DEVICES_MAX];
1353 unsigned i;
1354 LogFlow(("vusbR3RhSaveDone:\n"));
1355 RT_NOREF(pSSM);
1356
1357 /* Save the current data. */
1358 memcpy(aPortsOld, pThis->apDevByPort, sizeof(aPortsOld));
1359 AssertCompile(sizeof(aPortsOld) == sizeof(pThis->apDevByPort));
1360
1361 /*
1362 * NULL the dev pointers.
1363 */
1364 for (i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
1365 if (pThis->apDevByPort[i] && !VUSBIDevIsSavedStateSupported(&pThis->apDevByPort[i]->IDevice))
1366 pThis->apDevByPort[i] = NULL;
1367
1368 /*
1369 * Attach the devices.
1370 */
1371 for (i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
1372 {
1373 PVUSBDEV pDev = aPortsOld[i];
1374 if (pDev && !VUSBIDevIsSavedStateSupported(&pDev->IDevice))
1375 vusbHubAttach(pThis, pDev);
1376 }
1377
1378 return VINF_SUCCESS;
1379}
1380
1381
1382/**
1383 * @callback_method_impl{FNSSMDRVLOADPREP, This must detach the devices
1384 * currently attached and save them for reconnect after the state load has been
1385 * completed.}
1386 */
1387static DECLCALLBACK(int) vusbR3RhLoadPrep(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1388{
1389 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1390 int rc = VINF_SUCCESS;
1391 LogFlow(("vusbR3RhLoadPrep:\n"));
1392 RT_NOREF(pSSM);
1393
1394 if (!pThis->pLoad)
1395 {
1396 VUSBROOTHUBLOAD Load;
1397 unsigned i;
1398
1399 /// @todo This is all bogus.
1400 /*
1401 * Detach all devices which are present in this session. Save them in the load
1402 * structure so we can reattach them after restoring the guest.
1403 */
1404 Load.hTimer = NIL_TMTIMERHANDLE;
1405 Load.cDevs = 0;
1406 for (i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
1407 {
1408 PVUSBDEV pDev = pThis->apDevByPort[i];
1409 if (pDev && !VUSBIDevIsSavedStateSupported(&pDev->IDevice))
1410 {
1411 Load.apDevs[Load.cDevs++] = pDev;
1412 vusbHubDetach(pThis, pDev);
1413 Assert(!pThis->apDevByPort[i]);
1414 }
1415 }
1416
1417 /*
1418 * Any devices to reattach? If so, duplicate the Load struct.
1419 */
1420 if (Load.cDevs)
1421 {
1422 pThis->pLoad = (PVUSBROOTHUBLOAD)RTMemAllocZ(sizeof(Load));
1423 if (!pThis->pLoad)
1424 return VERR_NO_MEMORY;
1425 *pThis->pLoad = Load;
1426 }
1427 }
1428 /* else: we ASSUME no device can be attached or detached in the time
1429 * between a state load and the pLoad stuff processing. */
1430 return rc;
1431}
1432
1433
1434/**
1435 * Reattaches devices after a saved state load.
1436 */
1437static DECLCALLBACK(void) vusbR3RhLoadReattachDevices(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
1438{
1439 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1440 PVUSBROOTHUBLOAD pLoad = pThis->pLoad;
1441 LogFlow(("vusbR3RhLoadReattachDevices:\n"));
1442 Assert(hTimer == pLoad->hTimer); RT_NOREF(pvUser);
1443
1444 /*
1445 * Reattach devices.
1446 */
1447 for (unsigned i = 0; i < pLoad->cDevs; i++)
1448 vusbHubAttach(pThis, pLoad->apDevs[i]);
1449
1450 /*
1451 * Cleanup.
1452 */
1453 PDMDrvHlpTimerDestroy(pDrvIns, hTimer);
1454 pLoad->hTimer = NIL_TMTIMERHANDLE;
1455 RTMemFree(pLoad);
1456 pThis->pLoad = NULL;
1457}
1458
1459
1460/**
1461 * @callback_method_impl{FNSSMDRVLOADDONE}
1462 */
1463static DECLCALLBACK(int) vusbR3RhLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1464{
1465 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1466 LogFlow(("vusbR3RhLoadDone:\n"));
1467 RT_NOREF(pSSM);
1468
1469 /*
1470 * Start a timer if we've got devices to reattach
1471 */
1472 if (pThis->pLoad)
1473 {
1474 int rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_VIRTUAL, vusbR3RhLoadReattachDevices, NULL,
1475 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
1476 "VUSB reattach on load", &pThis->pLoad->hTimer);
1477 if (RT_SUCCESS(rc))
1478 rc = PDMDrvHlpTimerSetMillies(pDrvIns, pThis->pLoad->hTimer, 250);
1479 return rc;
1480 }
1481
1482 return VINF_SUCCESS;
1483}
1484
1485
1486/* -=-=-=-=-=- PDM Base interface methods -=-=-=-=-=- */
1487
1488
1489/**
1490 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1491 */
1492static DECLCALLBACK(void *) vusbRhQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1493{
1494 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1495 PVUSBROOTHUB pRh = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1496
1497 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1498 PDMIBASE_RETURN_INTERFACE(pszIID, VUSBIROOTHUBCONNECTOR, &pRh->IRhConnector);
1499 return NULL;
1500}
1501
1502
1503/* -=-=-=-=-=- PDM Driver methods -=-=-=-=-=- */
1504
1505
1506/**
1507 * Destruct a driver instance.
1508 *
1509 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1510 * resources can be freed correctly.
1511 *
1512 * @param pDrvIns The driver instance data.
1513 */
1514static DECLCALLBACK(void) vusbRhDestruct(PPDMDRVINS pDrvIns)
1515{
1516 PVUSBROOTHUB pRh = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1517 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1518
1519 vusbUrbPoolDestroy(&pRh->UrbPool);
1520 if (pRh->pszName)
1521 {
1522 RTStrFree(pRh->pszName);
1523 pRh->pszName = NULL;
1524 }
1525 if (pRh->hSniffer != VUSBSNIFFER_NIL)
1526 VUSBSnifferDestroy(pRh->hSniffer);
1527
1528 if (pRh->hSemEventPeriodFrame)
1529 RTSemEventMultiDestroy(pRh->hSemEventPeriodFrame);
1530
1531 if (pRh->hSemEventPeriodFrameStopped)
1532 RTSemEventMultiDestroy(pRh->hSemEventPeriodFrameStopped);
1533
1534 RTCritSectDelete(&pRh->CritSectDevices);
1535}
1536
1537
1538/**
1539 * Construct a root hub driver instance.
1540 *
1541 * @copydoc FNPDMDRVCONSTRUCT
1542 */
1543static DECLCALLBACK(int) vusbRhConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1544{
1545 RT_NOREF(fFlags);
1546 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1547 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1548 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1549
1550 LogFlow(("vusbRhConstruct: Instance %d\n", pDrvIns->iInstance));
1551
1552 /*
1553 * Validate configuration.
1554 */
1555 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "CaptureFilename", "");
1556
1557 /*
1558 * Check that there are no drivers below us.
1559 */
1560 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1561 ("Configuration error: Not possible to attach anything to this driver!\n"),
1562 VERR_PDM_DRVINS_NO_ATTACH);
1563
1564 /*
1565 * Initialize the critical sections.
1566 */
1567 int rc = RTCritSectInit(&pThis->CritSectDevices);
1568 if (RT_FAILURE(rc))
1569 return rc;
1570
1571 char *pszCaptureFilename = NULL;
1572 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "CaptureFilename", &pszCaptureFilename);
1573 if ( RT_FAILURE(rc)
1574 && rc != VERR_CFGM_VALUE_NOT_FOUND)
1575 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1576 N_("Configuration error: Failed to query value of \"CaptureFilename\""));
1577
1578 /*
1579 * Initialize the data members.
1580 */
1581 pDrvIns->IBase.pfnQueryInterface = vusbRhQueryInterface;
1582 /* the usb device */
1583 pThis->enmState = VUSB_DEVICE_STATE_ATTACHED;
1584 //pThis->hub.cPorts - later
1585 pThis->cDevices = 0;
1586 RTStrAPrintf(&pThis->pszName, "RootHub#%d", pDrvIns->iInstance);
1587 /* misc */
1588 pThis->pDrvIns = pDrvIns;
1589 /* the connector */
1590 pThis->IRhConnector.pfnSetUrbParams = vusbRhSetUrbParams;
1591 pThis->IRhConnector.pfnReset = vusbR3RhReset;
1592 pThis->IRhConnector.pfnPowerOn = vusbR3RhPowerOn;
1593 pThis->IRhConnector.pfnPowerOff = vusbR3RhPowerOff;
1594 pThis->IRhConnector.pfnNewUrb = vusbRhConnNewUrb;
1595 pThis->IRhConnector.pfnFreeUrb = vusbRhConnFreeUrb;
1596 pThis->IRhConnector.pfnSubmitUrb = vusbRhSubmitUrb;
1597 pThis->IRhConnector.pfnReapAsyncUrbs = vusbRhReapAsyncUrbs;
1598 pThis->IRhConnector.pfnCancelUrbsEp = vusbRhCancelUrbsEp;
1599 pThis->IRhConnector.pfnCancelAllUrbs = vusbRhCancelAllUrbs;
1600 pThis->IRhConnector.pfnAbortEp = vusbRhAbortEp;
1601 pThis->IRhConnector.pfnSetPeriodicFrameProcessing = vusbRhSetFrameProcessing;
1602 pThis->IRhConnector.pfnGetPeriodicFrameRate = vusbRhGetPeriodicFrameRate;
1603 pThis->IRhConnector.pfnUpdateIsocFrameDelta = vusbRhUpdateIsocFrameDelta;
1604 pThis->IRhConnector.pfnDevReset = vusbR3RhDevReset;
1605 pThis->IRhConnector.pfnDevPowerOn = vusbR3RhDevPowerOn;
1606 pThis->IRhConnector.pfnDevPowerOff = vusbR3RhDevPowerOff;
1607 pThis->IRhConnector.pfnDevGetState = vusbR3RhDevGetState;
1608 pThis->IRhConnector.pfnDevIsSavedStateSupported = vusbR3RhDevIsSavedStateSupported;
1609 pThis->IRhConnector.pfnDevGetSpeed = vusbR3RhDevGetSpeed;
1610 pThis->hSniffer = VUSBSNIFFER_NIL;
1611 pThis->cbHci = 0;
1612 pThis->cbHciTd = 0;
1613 pThis->fFrameProcessing = false;
1614#ifdef LOG_ENABLED
1615 pThis->iSerial = 0;
1616#endif
1617 /*
1618 * Resolve interface(s).
1619 */
1620 pThis->pIRhPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, VUSBIROOTHUBPORT);
1621 AssertMsgReturn(pThis->pIRhPort, ("Configuration error: the device/driver above us doesn't expose any VUSBIROOTHUBPORT interface!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
1622
1623 /*
1624 * Get number of ports and the availability bitmap.
1625 * ASSUME that the number of ports reported now at creation time is the max number.
1626 */
1627 pThis->cPorts = pThis->pIRhPort->pfnGetAvailablePorts(pThis->pIRhPort, &pThis->Bitmap);
1628 Log(("vusbRhConstruct: cPorts=%d\n", pThis->cPorts));
1629
1630 /*
1631 * Get the USB version of the attached HC.
1632 * ASSUME that version 2.0 implies high-speed.
1633 */
1634 pThis->fHcVersions = pThis->pIRhPort->pfnGetUSBVersions(pThis->pIRhPort);
1635 Log(("vusbRhConstruct: fHcVersions=%u\n", pThis->fHcVersions));
1636
1637 rc = vusbUrbPoolInit(&pThis->UrbPool);
1638 if (RT_FAILURE(rc))
1639 return rc;
1640
1641 if (pszCaptureFilename)
1642 {
1643 rc = VUSBSnifferCreate(&pThis->hSniffer, 0, pszCaptureFilename, NULL, NULL);
1644 if (RT_FAILURE(rc))
1645 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1646 N_("VUSBSniffer cannot open '%s' for writing. The directory must exist and it must be writable for the current user"),
1647 pszCaptureFilename);
1648
1649 PDMDrvHlpMMHeapFree(pDrvIns, pszCaptureFilename);
1650 }
1651
1652 /*
1653 * Register ourselves as a USB hub.
1654 * The current implementation uses the VUSBIRHCONFIG interface for communication.
1655 */
1656 PCPDMUSBHUBHLP pHlpUsb; /* not used currently */
1657 rc = PDMDrvHlpUSBRegisterHub(pDrvIns, pThis->fHcVersions, pThis->cPorts, &g_vusbHubReg, &pHlpUsb);
1658 if (RT_FAILURE(rc))
1659 return rc;
1660
1661 /*
1662 * Register the saved state data unit for attaching devices.
1663 */
1664 rc = PDMDrvHlpSSMRegisterEx(pDrvIns, VUSB_ROOTHUB_SAVED_STATE_VERSION, 0,
1665 NULL, NULL, NULL,
1666 vusbR3RhSavePrep, NULL, vusbR3RhSaveDone,
1667 vusbR3RhLoadPrep, NULL, vusbR3RhLoadDone);
1668 AssertRCReturn(rc, rc);
1669
1670 /*
1671 * Statistics. (It requires a 30" monitor or extremely tiny fonts to edit this "table".)
1672 */
1673#ifdef VBOX_WITH_STATISTICS
1674 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "The number of URBs submitted.", "/VUSB/%d/UrbsSubmitted", pDrvIns->iInstance);
1675 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Bulk transfer.", "/VUSB/%d/UrbsSubmitted/Bulk", pDrvIns->iInstance);
1676 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Control transfer.", "/VUSB/%d/UrbsSubmitted/Ctrl", pDrvIns->iInstance);
1677 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Interrupt transfer.", "/VUSB/%d/UrbsSubmitted/Intr", pDrvIns->iInstance);
1678 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Isochronous transfer.", "/VUSB/%d/UrbsSubmitted/Isoc", pDrvIns->iInstance);
1679
1680 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "The number of URBs cancelled. (included in failed)", "/VUSB/%d/UrbsCancelled", pDrvIns->iInstance);
1681 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Bulk transfer.", "/VUSB/%d/UrbsCancelled/Bulk", pDrvIns->iInstance);
1682 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Control transfer.", "/VUSB/%d/UrbsCancelled/Ctrl", pDrvIns->iInstance);
1683 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Interrupt transfer.", "/VUSB/%d/UrbsCancelled/Intr", pDrvIns->iInstance);
1684 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Isochronous transfer.", "/VUSB/%d/UrbsCancelled/Isoc", pDrvIns->iInstance);
1685
1686 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "The number of URBs failing.", "/VUSB/%d/UrbsFailed", pDrvIns->iInstance);
1687 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Bulk transfer.", "/VUSB/%d/UrbsFailed/Bulk", pDrvIns->iInstance);
1688 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Control transfer.", "/VUSB/%d/UrbsFailed/Ctrl", pDrvIns->iInstance);
1689 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Interrupt transfer.", "/VUSB/%d/UrbsFailed/Intr", pDrvIns->iInstance);
1690 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Isochronous transfer.", "/VUSB/%d/UrbsFailed/Isoc", pDrvIns->iInstance);
1691
1692 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Total requested transfer.", "/VUSB/%d/ReqBytes", pDrvIns->iInstance);
1693 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ReqBytes/Bulk", pDrvIns->iInstance);
1694 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ReqBytes/Ctrl", pDrvIns->iInstance);
1695 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ReqBytes/Intr", pDrvIns->iInstance);
1696 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ReqBytes/Isoc", pDrvIns->iInstance);
1697
1698 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Total requested read transfer.", "/VUSB/%d/ReqReadBytes", pDrvIns->iInstance);
1699 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ReqReadBytes/Bulk", pDrvIns->iInstance);
1700 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ReqReadBytes/Ctrl", pDrvIns->iInstance);
1701 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ReqReadBytes/Intr", pDrvIns->iInstance);
1702 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ReqReadBytes/Isoc", pDrvIns->iInstance);
1703
1704 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Total requested write transfer.", "/VUSB/%d/ReqWriteBytes", pDrvIns->iInstance);
1705 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ReqWriteBytes/Bulk", pDrvIns->iInstance);
1706 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ReqWriteBytes/Ctrl", pDrvIns->iInstance);
1707 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ReqWriteBytes/Intr", pDrvIns->iInstance);
1708 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ReqWriteBytes/Isoc", pDrvIns->iInstance);
1709
1710 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Actual total transfer.", "/VUSB/%d/ActBytes", pDrvIns->iInstance);
1711 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ActBytes/Bulk", pDrvIns->iInstance);
1712 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ActBytes/Ctrl", pDrvIns->iInstance);
1713 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ActBytes/Intr", pDrvIns->iInstance);
1714 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ActBytes/Isoc", pDrvIns->iInstance);
1715
1716 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Actual total read transfer.", "/VUSB/%d/ActReadBytes", pDrvIns->iInstance);
1717 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ActReadBytes/Bulk", pDrvIns->iInstance);
1718 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ActReadBytes/Ctrl", pDrvIns->iInstance);
1719 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ActReadBytes/Intr", pDrvIns->iInstance);
1720 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ActReadBytes/Isoc", pDrvIns->iInstance);
1721
1722 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Actual total write transfer.", "/VUSB/%d/ActWriteBytes", pDrvIns->iInstance);
1723 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ActWriteBytes/Bulk", pDrvIns->iInstance);
1724 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ActWriteBytes/Ctrl", pDrvIns->iInstance);
1725 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ActWriteBytes/Intr", pDrvIns->iInstance);
1726 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ActWriteBytes/Isoc", pDrvIns->iInstance);
1727
1728 /* bulk */
1729 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of submitted URBs.", "/VUSB/%d/Bulk/Urbs", pDrvIns->iInstance);
1730 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of failed URBs.", "/VUSB/%d/Bulk/UrbsFailed", pDrvIns->iInstance);
1731 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of cancelled URBs.", "/VUSB/%d/Bulk/UrbsFailed/Cancelled", pDrvIns->iInstance);
1732 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of bytes transferred.", "/VUSB/%d/Bulk/ActBytes", pDrvIns->iInstance);
1733 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Bulk/ActBytes/Read", pDrvIns->iInstance);
1734 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Bulk/ActBytes/Write", pDrvIns->iInstance);
1735 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Requested number of bytes.", "/VUSB/%d/Bulk/ReqBytes", pDrvIns->iInstance);
1736 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Bulk/ReqBytes/Read", pDrvIns->iInstance);
1737 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Bulk/ReqBytes/Write", pDrvIns->iInstance);
1738
1739 /* control */
1740 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of submitted URBs.", "/VUSB/%d/Ctrl/Urbs", pDrvIns->iInstance);
1741 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of failed URBs.", "/VUSB/%d/Ctrl/UrbsFailed", pDrvIns->iInstance);
1742 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of cancelled URBs.", "/VUSB/%d/Ctrl/UrbsFailed/Cancelled", pDrvIns->iInstance);
1743 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of bytes transferred.", "/VUSB/%d/Ctrl/ActBytes", pDrvIns->iInstance);
1744 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Ctrl/ActBytes/Read", pDrvIns->iInstance);
1745 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Ctrl/ActBytes/Write", pDrvIns->iInstance);
1746 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Requested number of bytes.", "/VUSB/%d/Ctrl/ReqBytes", pDrvIns->iInstance);
1747 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Ctrl/ReqBytes/Read", pDrvIns->iInstance);
1748 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Ctrl/ReqBytes/Write", pDrvIns->iInstance);
1749
1750 /* interrupt */
1751 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of submitted URBs.", "/VUSB/%d/Intr/Urbs", pDrvIns->iInstance);
1752 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of failed URBs.", "/VUSB/%d/Intr/UrbsFailed", pDrvIns->iInstance);
1753 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of cancelled URBs.", "/VUSB/%d/Intr/UrbsFailed/Cancelled", pDrvIns->iInstance);
1754 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of bytes transferred.", "/VUSB/%d/Intr/ActBytes", pDrvIns->iInstance);
1755 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Intr/ActBytes/Read", pDrvIns->iInstance);
1756 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Intr/ActBytes/Write", pDrvIns->iInstance);
1757 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Requested number of bytes.", "/VUSB/%d/Intr/ReqBytes", pDrvIns->iInstance);
1758 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Intr/ReqBytes/Read", pDrvIns->iInstance);
1759 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Intr/ReqBytes/Write", pDrvIns->iInstance);
1760
1761 /* isochronous */
1762 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of submitted URBs.", "/VUSB/%d/Isoc/Urbs", pDrvIns->iInstance);
1763 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of failed URBs.", "/VUSB/%d/Isoc/UrbsFailed", pDrvIns->iInstance);
1764 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of cancelled URBs.", "/VUSB/%d/Isoc/UrbsFailed/Cancelled", pDrvIns->iInstance);
1765 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of bytes transferred.", "/VUSB/%d/Isoc/ActBytes", pDrvIns->iInstance);
1766 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Isoc/ActBytes/Read", pDrvIns->iInstance);
1767 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Isoc/ActBytes/Write", pDrvIns->iInstance);
1768 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Requested number of bytes.", "/VUSB/%d/Isoc/ReqBytes", pDrvIns->iInstance);
1769 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Isoc/ReqBytes/Read", pDrvIns->iInstance);
1770 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Isoc/ReqBytes/Write", pDrvIns->iInstance);
1771 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocActPkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of isochronous packets returning data.", "/VUSB/%d/Isoc/ActPkts", pDrvIns->iInstance);
1772 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocActReadPkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Read.", "/VUSB/%d/Isoc/ActPkts/Read", pDrvIns->iInstance);
1773 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocActWritePkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Write.", "/VUSB/%d/Isoc/ActPkts/Write", pDrvIns->iInstance);
1774 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocReqPkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Requested number of isochronous packets.", "/VUSB/%d/Isoc/ReqPkts", pDrvIns->iInstance);
1775 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocReqReadPkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Read.", "/VUSB/%d/Isoc/ReqPkts/Read", pDrvIns->iInstance);
1776 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocReqWritePkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Write.", "/VUSB/%d/Isoc/ReqPkts/Write", pDrvIns->iInstance);
1777
1778 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aStatIsocDetails); i++)
1779 {
1780 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].Pkts, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d", pDrvIns->iInstance, i);
1781 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].Ok, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/Ok", pDrvIns->iInstance, i);
1782 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].Ok0, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/Ok0", pDrvIns->iInstance, i);
1783 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].DataUnderrun, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/DataUnderrun", pDrvIns->iInstance, i);
1784 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].DataUnderrun0, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/DataUnderrun0", pDrvIns->iInstance, i);
1785 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].DataOverrun, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/DataOverrun", pDrvIns->iInstance, i);
1786 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].NotAccessed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/NotAccessed", pDrvIns->iInstance, i);
1787 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].Misc, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/Misc", pDrvIns->iInstance, i);
1788 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].Bytes, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, ".", "/VUSB/%d/Isoc/%d/Bytes", pDrvIns->iInstance, i);
1789 }
1790
1791 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReapAsyncUrbs, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling the vusbRhReapAsyncUrbs body (omitting calls when nothing is in-flight).",
1792 "/VUSB/%d/ReapAsyncUrbs", pDrvIns->iInstance);
1793 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSubmitUrb, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling the vusbRhSubmitUrb body.",
1794 "/VUSB/%d/SubmitUrb", pDrvIns->iInstance);
1795 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatFramesProcessedThread, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Processed frames in the dedicated thread",
1796 "/VUSB/%d/FramesProcessedThread", pDrvIns->iInstance);
1797 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatFramesProcessedClbk, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Processed frames in the URB completion callback",
1798 "/VUSB/%d/FramesProcessedClbk", pDrvIns->iInstance);
1799#endif
1800 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->UrbPool.cUrbsInPool, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "The number of URBs in the pool.",
1801 "/VUSB/%d/cUrbsInPool", pDrvIns->iInstance);
1802
1803 return VINF_SUCCESS;
1804}
1805
1806
1807/**
1808 * VUSB Root Hub driver registration record.
1809 */
1810const PDMDRVREG g_DrvVUSBRootHub =
1811{
1812 /* u32Version */
1813 PDM_DRVREG_VERSION,
1814 /* szName */
1815 "VUSBRootHub",
1816 /* szRCMod */
1817 "",
1818 /* szR0Mod */
1819 "",
1820 /* pszDescription */
1821 "VUSB Root Hub Driver.",
1822 /* fFlags */
1823 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1824 /* fClass. */
1825 PDM_DRVREG_CLASS_USB,
1826 /* cMaxInstances */
1827 ~0U,
1828 /* cbInstance */
1829 sizeof(VUSBROOTHUB),
1830 /* pfnConstruct */
1831 vusbRhConstruct,
1832 /* pfnDestruct */
1833 vusbRhDestruct,
1834 /* pfnRelocate */
1835 NULL,
1836 /* pfnIOCtl */
1837 NULL,
1838 /* pfnPowerOn */
1839 NULL,
1840 /* pfnReset */
1841 NULL,
1842 /* pfnSuspend */
1843 NULL,
1844 /* pfnResume */
1845 NULL,
1846 /* pfnAttach */
1847 NULL,
1848 /* pfnDetach */
1849 NULL,
1850 /* pfnPowerOff */
1851 NULL,
1852 /* pfnSoftReset */
1853 NULL,
1854 /* u32EndVersion */
1855 PDM_DRVREG_VERSION
1856};
1857
1858/*
1859 * Local Variables:
1860 * mode: c
1861 * c-file-style: "bsd"
1862 * c-basic-offset: 4
1863 * tab-width: 4
1864 * indent-tabs-mode: s
1865 * End:
1866 */
1867
Note: See TracBrowser for help on using the repository browser.

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