VirtualBox

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

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

Devices/USB: Fix for regression introduced with r150168 where a guest failed to enumerate USB devices if more than one device was on the bus when the VM was powered on. Simplify the code even more to not require any lists for devices in the default state, bugref:10196

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 83.4 KB
Line 
1/* $Id: DrvVUSBRootHub.cpp 93974 2022-02-28 12:03:21Z 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 *
248 * @note The reference count of the VUSB device structure is retained to prevent it from going away.
249 */
250static PVUSBDEV vusbR3RhGetVUsbDevByPortRetain(PVUSBROOTHUB pThis, uint32_t uPort)
251{
252 PVUSBDEV pDev = NULL;
253
254 AssertReturn(uPort < RT_ELEMENTS(pThis->apDevByPort), NULL);
255
256 RTCritSectEnter(&pThis->CritSectDevices);
257
258 pDev = pThis->apDevByPort[uPort];
259 if (RT_LIKELY(pDev))
260 vusbDevRetain(pDev);
261
262 RTCritSectLeave(&pThis->CritSectDevices);
263
264 return pDev;
265}
266
267
268/**
269 * Returns the attached VUSB device for the given port or NULL if none is attached.
270 *
271 * @returns Pointer to the VUSB device or NULL if not found.
272 * @param pThis The VUSB roothub device instance.
273 * @param u8Address The address to get the device for.
274 *
275 * @note The reference count of the VUSB device structure is retained to prevent it from going away.
276 */
277static PVUSBDEV vusbR3RhGetVUsbDevByAddrRetain(PVUSBROOTHUB pThis, uint8_t u8Address)
278{
279 PVUSBDEV pDev = NULL;
280
281 AssertReturn(u8Address < RT_ELEMENTS(pThis->apDevByAddr), NULL);
282
283 RTCritSectEnter(&pThis->CritSectDevices);
284
285 pDev = pThis->apDevByAddr[u8Address];
286 if (RT_LIKELY(pDev))
287 vusbDevRetain(pDev);
288
289 RTCritSectLeave(&pThis->CritSectDevices);
290
291 return pDev;
292}
293
294
295/**
296 * Returns a human readable string fromthe given USB speed enum.
297 *
298 * @returns Human readable string.
299 * @param enmSpeed The speed to stringify.
300 */
301static const char *vusbGetSpeedString(VUSBSPEED enmSpeed)
302{
303 const char *pszSpeed = NULL;
304
305 switch (enmSpeed)
306 {
307 case VUSB_SPEED_LOW:
308 pszSpeed = "Low";
309 break;
310 case VUSB_SPEED_FULL:
311 pszSpeed = "Full";
312 break;
313 case VUSB_SPEED_HIGH:
314 pszSpeed = "High";
315 break;
316 case VUSB_SPEED_VARIABLE:
317 pszSpeed = "Variable";
318 break;
319 case VUSB_SPEED_SUPER:
320 pszSpeed = "Super";
321 break;
322 case VUSB_SPEED_SUPERPLUS:
323 pszSpeed = "SuperPlus";
324 break;
325 default:
326 pszSpeed = "Unknown";
327 break;
328 }
329 return pszSpeed;
330}
331
332
333/**
334 * Attaches a device to a specific hub.
335 *
336 * This function is called by the vusb_add_device() and vusbRhAttachDevice().
337 *
338 * @returns VBox status code.
339 * @param pThis The roothub to attach it to.
340 * @param pDev The device to attach.
341 * @thread EMT
342 */
343static int vusbHubAttach(PVUSBROOTHUB pThis, PVUSBDEV pDev)
344{
345 LogFlow(("vusbHubAttach: pThis=%p[%s] pDev=%p[%s]\n", pThis, pThis->Hub.pszName, pDev, pDev->pUsbIns->pszName));
346
347 PVUSBHUB pHub = &pThis->Hub;
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 pHub->cDevices++;
360 pDev->i16Port = iPort;
361
362 /* Call the device attach helper, so it can initialize its state. */
363 int rc = vusbDevAttach(pDev, pHub);
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, pHub->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 pHub->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 PVUSBHUB pHub = &pThis->Hub;
412
413 Assert(pDev->i16Port != -1);
414
415 /* Cancel all in-flight URBs from this device. */
416 vusbDevCancelAllUrbs(pDev, true);
417
418 /*
419 * Detach the device and mark the port as available.
420 */
421 unsigned uPort = pDev->i16Port;
422 pDev->i16Port = -1;
423 pThis->pIRhPort->pfnDetach(pThis->pIRhPort, uPort);
424 ASMBitSet(&pThis->Bitmap, uPort);
425 pHub->cDevices--;
426
427 /* Check that it's attached and remove it. */
428 RTCritSectEnter(&pThis->CritSectDevices);
429 Assert(pThis->apDevByPort[uPort] == pDev);
430 pThis->apDevByPort[uPort] = NULL;
431
432 if (pDev->u8Address != VUSB_INVALID_ADDRESS)
433 {
434 Assert(pThis->apDevByAddr[pDev->u8Address] == pDev);
435 pThis->apDevByAddr[pDev->u8Address] = NULL;
436 }
437 RTCritSectLeave(&pThis->CritSectDevices);
438
439 /* Free resources. */
440 vusbDevDetach(pDev);
441 return VINF_SUCCESS;
442}
443
444
445
446/* -=-=-=-=-=- PDMUSBHUBREG methods -=-=-=-=-=- */
447
448/** @interface_method_impl{PDMUSBHUBREG,pfnAttachDevice} */
449static DECLCALLBACK(int) vusbPDMHubAttachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, const char *pszCaptureFilename, uint32_t *piPort)
450{
451 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
452
453 /*
454 * Allocate a new VUSB device and initialize it.
455 */
456 PVUSBDEV pDev = (PVUSBDEV)RTMemAllocZ(sizeof(*pDev));
457 AssertReturn(pDev, VERR_NO_MEMORY);
458 int rc = vusbDevInit(pDev, pUsbIns, pszCaptureFilename);
459 if (RT_SUCCESS(rc))
460 {
461 pUsbIns->pvVUsbDev2 = pDev;
462 rc = vusbHubAttach(pThis, pDev);
463 if (RT_SUCCESS(rc))
464 {
465 *piPort = UINT32_MAX; /// @todo implement piPort
466 return rc;
467 }
468
469 RTMemFree(pDev->paIfStates);
470 pUsbIns->pvVUsbDev2 = NULL;
471 }
472 vusbDevRelease(pDev);
473 return rc;
474}
475
476
477/** @interface_method_impl{PDMUSBHUBREG,pfnDetachDevice} */
478static DECLCALLBACK(int) vusbPDMHubDetachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, uint32_t iPort)
479{
480 RT_NOREF(iPort);
481 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
482 PVUSBHUB pHub = &pThis->Hub;
483 PVUSBDEV pDev = (PVUSBDEV)pUsbIns->pvVUsbDev2;
484 Assert(pDev);
485
486 LogRel(("VUSB: Detached '%s' from port %u on %s\n", pDev->pUsbIns->pszName, pDev->i16Port, pHub->pszName));
487
488 /*
489 * Deal with pending async reset.
490 * (anything but reset)
491 */
492 vusbDevSetStateCmp(pDev, VUSB_DEVICE_STATE_DEFAULT, VUSB_DEVICE_STATE_RESET);
493 vusbHubDetach(pThis, pDev);
494 vusbDevRelease(pDev);
495 return VINF_SUCCESS;
496}
497
498/**
499 * The hub registration structure.
500 */
501static const PDMUSBHUBREG g_vusbHubReg =
502{
503 PDM_USBHUBREG_VERSION,
504 vusbPDMHubAttachDevice,
505 vusbPDMHubDetachDevice,
506 PDM_USBHUBREG_VERSION
507};
508
509
510/* -=-=-=-=-=- VUSBIROOTHUBCONNECTOR methods -=-=-=-=-=- */
511
512
513/**
514 * Callback for freeing an URB.
515 * @param pUrb The URB to free.
516 */
517static DECLCALLBACK(void) vusbRhFreeUrb(PVUSBURB pUrb)
518{
519 /*
520 * Assert sanity.
521 */
522 vusbUrbAssert(pUrb);
523 PVUSBROOTHUB pRh = (PVUSBROOTHUB)pUrb->pVUsb->pvFreeCtx;
524 Assert(pRh);
525
526 Assert(pUrb->enmState != VUSBURBSTATE_FREE);
527
528 /*
529 * Free the URB description (logging builds only).
530 */
531 if (pUrb->pszDesc)
532 {
533 RTStrFree(pUrb->pszDesc);
534 pUrb->pszDesc = NULL;
535 }
536
537 /* The URB comes from the roothub if there is no device (invalid address). */
538 if (pUrb->pVUsb->pDev)
539 {
540 PVUSBDEV pDev = pUrb->pVUsb->pDev;
541
542 vusbUrbPoolFree(&pUrb->pVUsb->pDev->UrbPool, pUrb);
543 vusbDevRelease(pDev);
544 }
545 else
546 vusbUrbPoolFree(&pRh->Hub.Dev.UrbPool, pUrb);
547}
548
549
550/**
551 * Worker routine for vusbRhConnNewUrb().
552 */
553static PVUSBURB vusbRhNewUrb(PVUSBROOTHUB pRh, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
554 VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
555{
556 RT_NOREF(pszTag);
557 PVUSBURBPOOL pUrbPool = &pRh->Hub.Dev.UrbPool;
558
559 if (RT_UNLIKELY(cbData > (32 * _1M)))
560 {
561 LogFunc(("Bad URB size (%u)!\n", cbData));
562 return NULL;
563 }
564
565 PVUSBDEV pDev;
566 if (uPort == VUSB_DEVICE_PORT_INVALID)
567 pDev = vusbR3RhGetVUsbDevByAddrRetain(pRh, DstAddress);
568 else
569 pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
570
571 if (pDev)
572 pUrbPool = &pDev->UrbPool;
573
574 PVUSBURB pUrb = vusbUrbPoolAlloc(pUrbPool, enmType, enmDir, cbData,
575 pRh->cbHci, pRh->cbHciTd, cTds);
576 if (RT_LIKELY(pUrb))
577 {
578 pUrb->pVUsb->pvFreeCtx = pRh;
579 pUrb->pVUsb->pfnFree = vusbRhFreeUrb;
580 pUrb->DstAddress = DstAddress;
581 pUrb->pVUsb->pDev = pDev;
582
583#ifdef LOG_ENABLED
584 const char *pszType = NULL;
585
586 switch(pUrb->enmType)
587 {
588 case VUSBXFERTYPE_CTRL:
589 pszType = "ctrl";
590 break;
591 case VUSBXFERTYPE_INTR:
592 pszType = "intr";
593 break;
594 case VUSBXFERTYPE_BULK:
595 pszType = "bulk";
596 break;
597 case VUSBXFERTYPE_ISOC:
598 pszType = "isoc";
599 break;
600 default:
601 pszType = "invld";
602 break;
603 }
604
605 pRh->iSerial = (pRh->iSerial + 1) % 10000;
606 RTStrAPrintf(&pUrb->pszDesc, "URB %p %s%c%04d (%s)", pUrb, pszType,
607 (pUrb->enmDir == VUSBDIRECTION_IN) ? '<' : ((pUrb->enmDir == VUSBDIRECTION_SETUP) ? 's' : '>'),
608 pRh->iSerial, pszTag ? pszTag : "<none>");
609#endif
610 }
611
612 return pUrb;
613}
614
615
616/**
617 * Calculate frame timer variables given a frame rate.
618 */
619static void vusbRhR3CalcTimerIntervals(PVUSBROOTHUB pThis, uint32_t u32FrameRate)
620{
621 pThis->nsWait = RT_NS_1SEC / u32FrameRate;
622 pThis->uFrameRate = u32FrameRate;
623 /* Inform the HCD about the new frame rate. */
624 pThis->pIRhPort->pfnFrameRateChanged(pThis->pIRhPort, u32FrameRate);
625}
626
627
628/**
629 * Calculates the new frame rate based on the idle detection and number of idle
630 * cycles.
631 *
632 * @returns nothing.
633 * @param pThis The roothub instance data.
634 * @param fIdle Flag whether the last frame didn't produce any activity.
635 */
636static void vusbRhR3FrameRateCalcNew(PVUSBROOTHUB pThis, bool fIdle)
637{
638 uint32_t uNewFrameRate = pThis->uFrameRate;
639
640 /*
641 * Adjust the frame timer interval based on idle detection.
642 */
643 if (fIdle)
644 {
645 pThis->cIdleCycles++;
646 /* Set the new frame rate based on how long we've been idle. Tunable. */
647 switch (pThis->cIdleCycles)
648 {
649 case 4: uNewFrameRate = 500; break; /* 2ms interval */
650 case 16:uNewFrameRate = 125; break; /* 8ms interval */
651 case 24:uNewFrameRate = 50; break; /* 20ms interval */
652 default: break;
653 }
654 /* Avoid overflow. */
655 if (pThis->cIdleCycles > 60000)
656 pThis->cIdleCycles = 20000;
657 }
658 else
659 {
660 if (pThis->cIdleCycles)
661 {
662 pThis->cIdleCycles = 0;
663 uNewFrameRate = pThis->uFrameRateDefault;
664 }
665 }
666
667 if ( uNewFrameRate != pThis->uFrameRate
668 && uNewFrameRate)
669 {
670 LogFlow(("Frame rate changed from %u to %u\n", pThis->uFrameRate, uNewFrameRate));
671 vusbRhR3CalcTimerIntervals(pThis, uNewFrameRate);
672 }
673}
674
675
676/**
677 * The core frame processing routine keeping track of the elapsed time and calling into
678 * the device emulation above us to do the work.
679 *
680 * @returns Relative timespan when to process the next frame.
681 * @param pThis The roothub instance data.
682 * @param fCallback Flag whether this method is called from the URB completion callback or
683 * from the worker thread (only used for statistics).
684 */
685DECLHIDDEN(uint64_t) vusbRhR3ProcessFrame(PVUSBROOTHUB pThis, bool fCallback)
686{
687 uint64_t tsNext = 0;
688 uint64_t tsNanoStart = RTTimeNanoTS();
689
690 /* Don't do anything if we are not supposed to process anything (EHCI and XHCI). */
691 if (!pThis->uFrameRateDefault)
692 return 0;
693
694 if (ASMAtomicXchgBool(&pThis->fFrameProcessing, true))
695 return pThis->nsWait;
696
697 if ( tsNanoStart > pThis->tsFrameProcessed
698 && tsNanoStart - pThis->tsFrameProcessed >= 750 * RT_NS_1US)
699 {
700 LogFlowFunc(("Starting new frame at ts %llu\n", tsNanoStart));
701
702 bool fIdle = pThis->pIRhPort->pfnStartFrame(pThis->pIRhPort, 0 /* u32FrameNo */);
703 vusbRhR3FrameRateCalcNew(pThis, fIdle);
704
705 uint64_t tsNow = RTTimeNanoTS();
706 tsNext = (tsNanoStart + pThis->nsWait) > tsNow ? (tsNanoStart + pThis->nsWait) - tsNow : 0;
707 pThis->tsFrameProcessed = tsNanoStart;
708 LogFlowFunc(("Current frame took %llu nano seconds to process, next frame in %llu ns\n", tsNow - tsNanoStart, tsNext));
709 if (fCallback)
710 STAM_COUNTER_INC(&pThis->StatFramesProcessedClbk);
711 else
712 STAM_COUNTER_INC(&pThis->StatFramesProcessedThread);
713 }
714 else
715 {
716 tsNext = (pThis->tsFrameProcessed + pThis->nsWait) > tsNanoStart ? (pThis->tsFrameProcessed + pThis->nsWait) - tsNanoStart : 0;
717 LogFlowFunc(("Next frame is too far away in the future, waiting... (tsNanoStart=%llu tsFrameProcessed=%llu)\n",
718 tsNanoStart, pThis->tsFrameProcessed));
719 }
720
721 ASMAtomicXchgBool(&pThis->fFrameProcessing, false);
722 LogFlowFunc(("returns %llu\n", tsNext));
723 return tsNext;
724}
725
726
727/**
728 * Worker for processing frames periodically.
729 *
730 * @returns VBox status code.
731 * @param pDrvIns The driver instance.
732 * @param pThread The PDM thread structure for the thread this worker runs on.
733 */
734static DECLCALLBACK(int) vusbRhR3PeriodFrameWorker(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
735{
736 RT_NOREF(pDrvIns);
737 int rc = VINF_SUCCESS;
738 PVUSBROOTHUB pThis = (PVUSBROOTHUB)pThread->pvUser;
739
740 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
741 return VINF_SUCCESS;
742
743 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
744 {
745 while ( !ASMAtomicReadU32(&pThis->uFrameRateDefault)
746 && pThread->enmState == PDMTHREADSTATE_RUNNING)
747 {
748 /* Signal the waiter that we are stopped now. */
749 rc = RTSemEventMultiSignal(pThis->hSemEventPeriodFrameStopped);
750 AssertRC(rc);
751
752 rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrame, RT_INDEFINITE_WAIT);
753 RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
754
755 /*
756 * Notify the device above about the frame rate changed if we are supposed to
757 * process frames.
758 */
759 uint32_t uFrameRate = ASMAtomicReadU32(&pThis->uFrameRateDefault);
760 if (uFrameRate)
761 vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
762 }
763
764 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc);
765 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
766 break;
767
768 uint64_t tsNext = vusbRhR3ProcessFrame(pThis, false /* fCallback */);
769
770 if (tsNext >= 250 * RT_NS_1US)
771 {
772 rc = RTSemEventMultiWaitEx(pThis->hSemEventPeriodFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE,
773 tsNext);
774 AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc));
775 RTSemEventMultiReset(pThis->hSemEventPeriodFrame);
776 }
777 }
778
779 return VINF_SUCCESS;
780}
781
782
783/**
784 * Unblock the periodic frame thread so it can respond to a state change.
785 *
786 * @returns VBox status code.
787 * @param pDrvIns The driver instance.
788 * @param pThread The send thread.
789 */
790static DECLCALLBACK(int) vusbRhR3PeriodFrameWorkerWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
791{
792 RT_NOREF(pThread);
793 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
794 return RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
795}
796
797
798/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetUrbParams} */
799static DECLCALLBACK(int) vusbRhSetUrbParams(PVUSBIROOTHUBCONNECTOR pInterface, size_t cbHci, size_t cbHciTd)
800{
801 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
802
803 pRh->cbHci = cbHci;
804 pRh->cbHciTd = cbHciTd;
805
806 return VINF_SUCCESS;
807}
808
809
810/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReset} */
811static DECLCALLBACK(int) vusbR3RhReset(PVUSBIROOTHUBCONNECTOR pInterface, bool fResetOnLinux)
812{
813 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
814 return pRh->pIRhPort->pfnReset(pRh->pIRhPort, fResetOnLinux);
815}
816
817
818/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOn} */
819static DECLCALLBACK(int) vusbR3RhPowerOn(PVUSBIROOTHUBCONNECTOR pInterface)
820{
821 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
822 LogFlow(("vusR3bRhPowerOn: pRh=%p\n", pRh));
823
824 Assert( pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED
825 && pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET);
826
827 if (pRh->Hub.Dev.enmState == VUSB_DEVICE_STATE_ATTACHED)
828 pRh->Hub.Dev.enmState = VUSB_DEVICE_STATE_POWERED;
829
830 return VINF_SUCCESS;
831}
832
833
834/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOff} */
835static DECLCALLBACK(int) vusbR3RhPowerOff(PVUSBIROOTHUBCONNECTOR pInterface)
836{
837 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
838 LogFlow(("vusbR3RhDevPowerOff: pThis=%p\n", pThis));
839
840 Assert( pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED
841 && pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET);
842
843 /*
844 * Cancel all URBs and reap them.
845 */
846 VUSBIRhCancelAllUrbs(&pThis->IRhConnector);
847 for (uint32_t uPort = 0; uPort < RT_ELEMENTS(pThis->apDevByPort); uPort++)
848 VUSBIRhReapAsyncUrbs(&pThis->IRhConnector, uPort, 0);
849
850 pThis->Hub.Dev.enmState = VUSB_DEVICE_STATE_ATTACHED;
851 return VINF_SUCCESS;
852}
853
854
855/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnNewUrb} */
856static DECLCALLBACK(PVUSBURB) vusbRhConnNewUrb(PVUSBIROOTHUBCONNECTOR pInterface, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType,
857 VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag)
858{
859 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
860 return vusbRhNewUrb(pRh, DstAddress, uPort, enmType, enmDir, cbData, cTds, pszTag);
861}
862
863
864/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnFreeUrb} */
865static DECLCALLBACK(int) vusbRhConnFreeUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
866{
867 RT_NOREF(pInterface);
868 pUrb->pVUsb->pfnFree(pUrb);
869 return VINF_SUCCESS;
870}
871
872
873/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSubmitUrb} */
874static DECLCALLBACK(int) vusbRhSubmitUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb, PPDMLED pLed)
875{
876 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
877 STAM_PROFILE_START(&pRh->StatSubmitUrb, a);
878
879#ifdef VBOX_WITH_STATISTICS
880 /*
881 * Total and per-type submit statistics.
882 */
883 Assert(pUrb->enmType >= 0 && pUrb->enmType < (int)RT_ELEMENTS(pRh->aTypes));
884 STAM_COUNTER_INC(&pRh->Total.StatUrbsSubmitted);
885 STAM_COUNTER_INC(&pRh->aTypes[pUrb->enmType].StatUrbsSubmitted);
886
887 STAM_COUNTER_ADD(&pRh->Total.StatReqBytes, pUrb->cbData);
888 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqBytes, pUrb->cbData);
889 if (pUrb->enmDir == VUSBDIRECTION_IN)
890 {
891 STAM_COUNTER_ADD(&pRh->Total.StatReqReadBytes, pUrb->cbData);
892 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqReadBytes, pUrb->cbData);
893 }
894 else
895 {
896 STAM_COUNTER_ADD(&pRh->Total.StatReqWriteBytes, pUrb->cbData);
897 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqWriteBytes, pUrb->cbData);
898 }
899
900 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
901 {
902 STAM_COUNTER_ADD(&pRh->StatIsocReqPkts, pUrb->cIsocPkts);
903 if (pUrb->enmDir == VUSBDIRECTION_IN)
904 STAM_COUNTER_ADD(&pRh->StatIsocReqReadPkts, pUrb->cIsocPkts);
905 else
906 STAM_COUNTER_ADD(&pRh->StatIsocReqWritePkts, pUrb->cIsocPkts);
907 }
908#endif
909
910 /* If there is a sniffer on the roothub record the URB there. */
911 if (pRh->hSniffer != VUSBSNIFFER_NIL)
912 {
913 int rc = VUSBSnifferRecordEvent(pRh->hSniffer, pUrb, VUSBSNIFFEREVENT_SUBMIT);
914 if (RT_FAILURE(rc))
915 LogRel(("VUSB: Capturing URB submit event on the root hub failed with %Rrc\n", rc));
916 }
917
918 /*
919 * The device was resolved when we allocated the URB.
920 * Submit it to the device if we found it, if not fail with device-not-ready.
921 */
922 int rc;
923 if ( pUrb->pVUsb->pDev
924 && pUrb->pVUsb->pDev->pUsbIns)
925 {
926 switch (pUrb->enmDir)
927 {
928 case VUSBDIRECTION_IN:
929 pLed->Asserted.s.fReading = pLed->Actual.s.fReading = 1;
930 rc = vusbUrbSubmit(pUrb);
931 pLed->Actual.s.fReading = 0;
932 break;
933 case VUSBDIRECTION_OUT:
934 pLed->Asserted.s.fWriting = pLed->Actual.s.fWriting = 1;
935 rc = vusbUrbSubmit(pUrb);
936 pLed->Actual.s.fWriting = 0;
937 break;
938 default:
939 rc = vusbUrbSubmit(pUrb);
940 break;
941 }
942
943 if (RT_FAILURE(rc))
944 {
945 LogFlow(("vusbRhSubmitUrb: freeing pUrb=%p\n", pUrb));
946 pUrb->pVUsb->pfnFree(pUrb);
947 }
948 }
949 else
950 {
951 vusbDevRetain(&pRh->Hub.Dev);
952 pUrb->pVUsb->pDev = &pRh->Hub.Dev;
953 Log(("vusb: pRh=%p: SUBMIT: Address %i not found!!!\n", pRh, pUrb->DstAddress));
954
955 pUrb->enmState = VUSBURBSTATE_REAPED;
956 pUrb->enmStatus = VUSBSTATUS_DNR;
957 vusbUrbCompletionRh(pUrb);
958 rc = VINF_SUCCESS;
959 }
960
961 STAM_PROFILE_STOP(&pRh->StatSubmitUrb, a);
962 return rc;
963}
964
965
966static DECLCALLBACK(int) vusbRhReapAsyncUrbsWorker(PVUSBDEV pDev, RTMSINTERVAL cMillies)
967{
968 if (!cMillies)
969 vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, 0);
970 else
971 {
972 uint64_t u64Start = RTTimeMilliTS();
973 do
974 {
975 vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, RT_MIN(cMillies >> 8, 10));
976 } while ( !RTListIsEmpty(&pDev->LstAsyncUrbs)
977 && RTTimeMilliTS() - u64Start < cMillies);
978 }
979
980 return VINF_SUCCESS;
981}
982
983/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReapAsyncUrbs} */
984static DECLCALLBACK(void) vusbRhReapAsyncUrbs(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, RTMSINTERVAL cMillies)
985{
986 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); NOREF(pRh);
987 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
988
989 if ( !pDev
990 || RTListIsEmpty(&pDev->LstAsyncUrbs))
991 return;
992
993 STAM_PROFILE_START(&pRh->StatReapAsyncUrbs, a);
994 int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhReapAsyncUrbsWorker, 2, pDev, cMillies);
995 AssertRC(rc);
996 STAM_PROFILE_STOP(&pRh->StatReapAsyncUrbs, a);
997
998 vusbDevRelease(pDev);
999}
1000
1001
1002/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelUrbsEp} */
1003static DECLCALLBACK(int) vusbRhCancelUrbsEp(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb)
1004{
1005 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1006 AssertReturn(pRh, VERR_INVALID_PARAMETER);
1007 AssertReturn(pUrb, VERR_INVALID_PARAMETER);
1008
1009 /// @todo This method of URB canceling may not work on non-Linux hosts.
1010 /*
1011 * Cancel and reap the URB(s) on an endpoint.
1012 */
1013 LogFlow(("vusbRhCancelUrbsEp: pRh=%p pUrb=%p\n", pRh, pUrb));
1014
1015 vusbUrbCancelAsync(pUrb, CANCELMODE_UNDO);
1016
1017 /* The reaper thread will take care of completing the URB. */
1018
1019 return VINF_SUCCESS;
1020}
1021
1022/**
1023 * Worker doing the actual cancelling of all outstanding URBs on the device I/O thread.
1024 *
1025 * @returns VBox status code.
1026 * @param pDev USB device instance data.
1027 */
1028static DECLCALLBACK(int) vusbRhCancelAllUrbsWorker(PVUSBDEV pDev)
1029{
1030 /*
1031 * Cancel the URBS.
1032 *
1033 * Not using th CritAsyncUrbs critical section here is safe
1034 * as the I/O thread is the only thread accessing this struture at the
1035 * moment.
1036 */
1037 PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
1038 RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
1039 {
1040 PVUSBURB pUrb = pVUsbUrb->pUrb;
1041 /* Call the worker directly. */
1042 vusbUrbCancelWorker(pUrb, CANCELMODE_FAIL);
1043 }
1044
1045 return VINF_SUCCESS;
1046}
1047
1048/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelAllUrbs} */
1049static DECLCALLBACK(void) vusbRhCancelAllUrbs(PVUSBIROOTHUBCONNECTOR pInterface)
1050{
1051 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1052
1053 RTCritSectEnter(&pThis->CritSectDevices);
1054 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
1055 {
1056 PVUSBDEV pDev = pThis->apDevByPort[i];
1057 if (pDev)
1058 vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhCancelAllUrbsWorker, 1, pDev);
1059 }
1060 RTCritSectLeave(&pThis->CritSectDevices);
1061}
1062
1063/**
1064 * Worker doing the actual cancelling of all outstanding per-EP URBs on the
1065 * device I/O thread.
1066 *
1067 * @returns VBox status code.
1068 * @param pDev USB device instance data.
1069 * @param EndPt Endpoint number.
1070 * @param enmDir Endpoint direction.
1071 */
1072static DECLCALLBACK(int) vusbRhAbortEpWorker(PVUSBDEV pDev, int EndPt, VUSBDIRECTION enmDir)
1073{
1074 /*
1075 * Iterate the URBs, find ones corresponding to given EP, and cancel them.
1076 */
1077 PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
1078 RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
1079 {
1080 PVUSBURB pUrb = pVUsbUrb->pUrb;
1081
1082 Assert(pUrb->pVUsb->pDev == pDev);
1083
1084 /* For the default control EP, direction does not matter. */
1085 if (pUrb->EndPt == EndPt && (pUrb->enmDir == enmDir || !EndPt))
1086 {
1087 LogFlow(("%s: vusbRhAbortEpWorker: CANCELING URB\n", pUrb->pszDesc));
1088 int rc = vusbUrbCancelWorker(pUrb, CANCELMODE_UNDO);
1089 AssertRC(rc);
1090 }
1091 }
1092
1093 return VINF_SUCCESS;
1094}
1095
1096
1097/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnAbortEp} */
1098static DECLCALLBACK(int) vusbRhAbortEp(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, int EndPt, VUSBDIRECTION enmDir)
1099{
1100 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1101 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort);
1102
1103 if (&pRh->Hub != pDev->pHub)
1104 AssertFailedReturn(VERR_INVALID_PARAMETER);
1105
1106 vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhAbortEpWorker, 3, pDev, EndPt, enmDir);
1107 vusbDevRelease(pDev);
1108
1109 /* The reaper thread will take care of completing the URB. */
1110
1111 return VINF_SUCCESS;
1112}
1113
1114
1115/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetPeriodicFrameProcessing} */
1116static DECLCALLBACK(int) vusbRhSetFrameProcessing(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate)
1117{
1118 int rc = VINF_SUCCESS;
1119 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1120
1121 /* Create the frame thread lazily. */
1122 if ( !pThis->hThreadPeriodFrame
1123 && uFrameRate)
1124 {
1125 ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
1126 pThis->uFrameRate = uFrameRate;
1127 vusbRhR3CalcTimerIntervals(pThis, uFrameRate);
1128
1129 rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrame);
1130 AssertRCReturn(rc, rc);
1131
1132 rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrameStopped);
1133 AssertRCReturn(rc, rc);
1134
1135 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->hThreadPeriodFrame, pThis, vusbRhR3PeriodFrameWorker,
1136 vusbRhR3PeriodFrameWorkerWakeup, 0, RTTHREADTYPE_IO, "VUsbPeriodFrm");
1137 AssertRCReturn(rc, rc);
1138
1139 VMSTATE enmState = PDMDrvHlpVMState(pThis->pDrvIns);
1140 if ( enmState == VMSTATE_RUNNING
1141 || enmState == VMSTATE_RUNNING_LS)
1142 {
1143 rc = PDMDrvHlpThreadResume(pThis->pDrvIns, pThis->hThreadPeriodFrame);
1144 AssertRCReturn(rc, rc);
1145 }
1146 }
1147 else if ( pThis->hThreadPeriodFrame
1148 && !uFrameRate)
1149 {
1150 /* Stop processing. */
1151 uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
1152 if (uFrameRateOld)
1153 {
1154 rc = RTSemEventMultiReset(pThis->hSemEventPeriodFrameStopped);
1155 AssertRC(rc);
1156
1157 /* Signal the frame thread to stop. */
1158 RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
1159
1160 /* Wait for signal from the thread that it stopped. */
1161 rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrameStopped, RT_INDEFINITE_WAIT);
1162 AssertRC(rc);
1163 }
1164 }
1165 else if ( pThis->hThreadPeriodFrame
1166 && uFrameRate)
1167 {
1168 /* Just switch to the new frame rate and let the periodic frame thread pick it up. */
1169 uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate);
1170
1171 /* Signal the frame thread to continue if it was stopped. */
1172 if (!uFrameRateOld)
1173 RTSemEventMultiSignal(pThis->hSemEventPeriodFrame);
1174 }
1175
1176 return rc;
1177}
1178
1179
1180/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnGetPeriodicFrameRate} */
1181static DECLCALLBACK(uint32_t) vusbRhGetPeriodicFrameRate(PVUSBIROOTHUBCONNECTOR pInterface)
1182{
1183 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1184
1185 return pThis->uFrameRate;
1186}
1187
1188/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnUpdateIsocFrameDelta} */
1189static DECLCALLBACK(uint32_t) vusbRhUpdateIsocFrameDelta(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort,
1190 int EndPt, VUSBDIRECTION enmDir, uint16_t uNewFrameID, uint8_t uBits)
1191{
1192 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1193 AssertReturn(pRh, 0);
1194 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); AssertPtr(pDev);
1195 PVUSBPIPE pPipe = &pDev->aPipes[EndPt];
1196 uint32_t *puLastFrame;
1197 int32_t uFrameDelta;
1198 uint32_t uMaxVal = 1 << uBits;
1199
1200 puLastFrame = enmDir == VUSBDIRECTION_IN ? &pPipe->uLastFrameIn : &pPipe->uLastFrameOut;
1201 uFrameDelta = uNewFrameID - *puLastFrame;
1202 *puLastFrame = uNewFrameID;
1203 /* Take care of wrap-around. */
1204 if (uFrameDelta < 0)
1205 uFrameDelta += uMaxVal;
1206
1207 vusbDevRelease(pDev);
1208 return (uint16_t)uFrameDelta;
1209}
1210
1211
1212/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevReset} */
1213static DECLCALLBACK(int) vusbR3RhDevReset(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, bool fResetOnLinux,
1214 PFNVUSBRESETDONE pfnDone, void *pvUser, PVM pVM)
1215{
1216 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1217 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
1218 AssertPtr(pDev);
1219
1220 int rc = VUSBIDevReset(&pDev->IDevice, fResetOnLinux, pfnDone, pvUser, pVM);
1221 vusbDevRelease(pDev);
1222 return rc;
1223}
1224
1225
1226/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOn} */
1227static DECLCALLBACK(int) vusbR3RhDevPowerOn(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
1228{
1229 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1230 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
1231 AssertPtr(pDev);
1232
1233 int rc = VUSBIDevPowerOn(&pDev->IDevice);
1234 vusbDevRelease(pDev);
1235 return rc;
1236}
1237
1238
1239/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOff} */
1240static DECLCALLBACK(int) vusbR3RhDevPowerOff(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
1241{
1242 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1243 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
1244 AssertPtr(pDev);
1245
1246 int rc = VUSBIDevPowerOff(&pDev->IDevice);
1247 vusbDevRelease(pDev);
1248 return rc;
1249}
1250
1251
1252/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetState} */
1253static DECLCALLBACK(VUSBDEVICESTATE) vusbR3RhDevGetState(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
1254{
1255 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1256 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
1257 AssertPtr(pDev);
1258
1259 VUSBDEVICESTATE enmState = VUSBIDevGetState(&pDev->IDevice);
1260 vusbDevRelease(pDev);
1261 return enmState;
1262}
1263
1264
1265/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevIsSavedStateSupported} */
1266static DECLCALLBACK(bool) vusbR3RhDevIsSavedStateSupported(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
1267{
1268 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1269 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
1270 AssertPtr(pDev);
1271
1272 bool fSavedStateSupported = VUSBIDevIsSavedStateSupported(&pDev->IDevice);
1273 vusbDevRelease(pDev);
1274 return fSavedStateSupported;
1275}
1276
1277
1278/** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetSpeed} */
1279static DECLCALLBACK(VUSBSPEED) vusbR3RhDevGetSpeed(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort)
1280{
1281 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface);
1282 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort);
1283 AssertPtr(pDev);
1284
1285 VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);
1286 vusbDevRelease(pDev);
1287 return enmSpeed;
1288}
1289
1290
1291/**
1292 * @callback_method_impl{FNSSMDRVSAVEPREP, All URBs needs to be canceled.}
1293 */
1294static DECLCALLBACK(int) vusbR3RhSavePrep(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1295{
1296 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1297 LogFlow(("vusbR3RhSavePrep:\n"));
1298 RT_NOREF(pSSM);
1299
1300 /*
1301 * Detach all proxied devices.
1302 */
1303 RTCritSectEnter(&pThis->CritSectDevices);
1304
1305 /** @todo we a) can't tell which are proxied, and b) this won't work well when continuing after saving! */
1306 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
1307 {
1308 PVUSBDEV pDev = pThis->apDevByPort[i];
1309 if (pDev)
1310 {
1311 if (!VUSBIDevIsSavedStateSupported(&pDev->IDevice))
1312 {
1313 int rc = vusbHubDetach(pThis, pDev);
1314 AssertRC(rc);
1315
1316 /*
1317 * Save the device pointers here so we can reattach them afterwards.
1318 * This will work fine even if the save fails since the Done handler is
1319 * called unconditionally if the Prep handler was called.
1320 */
1321 pThis->apDevByPort[i] = pDev;
1322 }
1323 }
1324 }
1325
1326 RTCritSectLeave(&pThis->CritSectDevices);
1327
1328 /*
1329 * Kill old load data which might be hanging around.
1330 */
1331 if (pThis->pLoad)
1332 {
1333 PDMDrvHlpTimerDestroy(pDrvIns, pThis->pLoad->hTimer);
1334 pThis->pLoad->hTimer = NIL_TMTIMERHANDLE;
1335 PDMDrvHlpMMHeapFree(pDrvIns, pThis->pLoad);
1336 pThis->pLoad = NULL;
1337 }
1338
1339 return VINF_SUCCESS;
1340}
1341
1342
1343/**
1344 * @callback_method_impl{FNSSMDRVSAVEDONE}
1345 */
1346static DECLCALLBACK(int) vusbR3RhSaveDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1347{
1348 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1349 PVUSBDEV aPortsOld[VUSB_DEVICES_MAX];
1350 unsigned i;
1351 LogFlow(("vusbR3RhSaveDone:\n"));
1352 RT_NOREF(pSSM);
1353
1354 /* Save the current data. */
1355 memcpy(aPortsOld, pThis->apDevByPort, sizeof(aPortsOld));
1356 AssertCompile(sizeof(aPortsOld) == sizeof(pThis->apDevByPort));
1357
1358 /*
1359 * NULL the dev pointers.
1360 */
1361 for (i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
1362 if (pThis->apDevByPort[i] && !VUSBIDevIsSavedStateSupported(&pThis->apDevByPort[i]->IDevice))
1363 pThis->apDevByPort[i] = NULL;
1364
1365 /*
1366 * Attach the devices.
1367 */
1368 for (i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
1369 {
1370 PVUSBDEV pDev = aPortsOld[i];
1371 if (pDev && !VUSBIDevIsSavedStateSupported(&pDev->IDevice))
1372 vusbHubAttach(pThis, pDev);
1373 }
1374
1375 return VINF_SUCCESS;
1376}
1377
1378
1379/**
1380 * @callback_method_impl{FNSSMDRVLOADPREP, This must detach the devices
1381 * currently attached and save them for reconnect after the state load has been
1382 * completed.}
1383 */
1384static DECLCALLBACK(int) vusbR3RhLoadPrep(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1385{
1386 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1387 int rc = VINF_SUCCESS;
1388 LogFlow(("vusbR3RhLoadPrep:\n"));
1389 RT_NOREF(pSSM);
1390
1391 if (!pThis->pLoad)
1392 {
1393 VUSBROOTHUBLOAD Load;
1394 unsigned i;
1395
1396 /// @todo This is all bogus.
1397 /*
1398 * Detach all devices which are present in this session. Save them in the load
1399 * structure so we can reattach them after restoring the guest.
1400 */
1401 Load.hTimer = NIL_TMTIMERHANDLE;
1402 Load.cDevs = 0;
1403 for (i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++)
1404 {
1405 PVUSBDEV pDev = pThis->apDevByPort[i];
1406 if (pDev && !VUSBIDevIsSavedStateSupported(&pDev->IDevice))
1407 {
1408 Load.apDevs[Load.cDevs++] = pDev;
1409 vusbHubDetach(pThis, pDev);
1410 Assert(!pThis->apDevByPort[i]);
1411 }
1412 }
1413
1414 /*
1415 * Any devices to reattach? If so, duplicate the Load struct.
1416 */
1417 if (Load.cDevs)
1418 {
1419 pThis->pLoad = (PVUSBROOTHUBLOAD)RTMemAllocZ(sizeof(Load));
1420 if (!pThis->pLoad)
1421 return VERR_NO_MEMORY;
1422 *pThis->pLoad = Load;
1423 }
1424 }
1425 /* else: we ASSUME no device can be attached or detached in the time
1426 * between a state load and the pLoad stuff processing. */
1427 return rc;
1428}
1429
1430
1431/**
1432 * Reattaches devices after a saved state load.
1433 */
1434static DECLCALLBACK(void) vusbR3RhLoadReattachDevices(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, void *pvUser)
1435{
1436 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1437 PVUSBROOTHUBLOAD pLoad = pThis->pLoad;
1438 LogFlow(("vusbR3RhLoadReattachDevices:\n"));
1439 Assert(hTimer == pLoad->hTimer); RT_NOREF(pvUser);
1440
1441 /*
1442 * Reattach devices.
1443 */
1444 for (unsigned i = 0; i < pLoad->cDevs; i++)
1445 vusbHubAttach(pThis, pLoad->apDevs[i]);
1446
1447 /*
1448 * Cleanup.
1449 */
1450 PDMDrvHlpTimerDestroy(pDrvIns, hTimer);
1451 pLoad->hTimer = NIL_TMTIMERHANDLE;
1452 RTMemFree(pLoad);
1453 pThis->pLoad = NULL;
1454}
1455
1456
1457/**
1458 * @callback_method_impl{FNSSMDRVLOADDONE}
1459 */
1460static DECLCALLBACK(int) vusbR3RhLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1461{
1462 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1463 LogFlow(("vusbR3RhLoadDone:\n"));
1464 RT_NOREF(pSSM);
1465
1466 /*
1467 * Start a timer if we've got devices to reattach
1468 */
1469 if (pThis->pLoad)
1470 {
1471 int rc = PDMDrvHlpTMTimerCreate(pDrvIns, TMCLOCK_VIRTUAL, vusbR3RhLoadReattachDevices, NULL,
1472 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
1473 "VUSB reattach on load", &pThis->pLoad->hTimer);
1474 if (RT_SUCCESS(rc))
1475 rc = PDMDrvHlpTimerSetMillies(pDrvIns, pThis->pLoad->hTimer, 250);
1476 return rc;
1477 }
1478
1479 return VINF_SUCCESS;
1480}
1481
1482
1483/* -=-=-=-=-=- PDM Base interface methods -=-=-=-=-=- */
1484
1485
1486/**
1487 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1488 */
1489static DECLCALLBACK(void *) vusbRhQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1490{
1491 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1492 PVUSBROOTHUB pRh = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1493
1494 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1495 PDMIBASE_RETURN_INTERFACE(pszIID, VUSBIROOTHUBCONNECTOR, &pRh->IRhConnector);
1496 PDMIBASE_RETURN_INTERFACE(pszIID, VUSBIDEVICE, &pRh->Hub.Dev.IDevice);
1497 return NULL;
1498}
1499
1500
1501/* -=-=-=-=-=- PDM Driver methods -=-=-=-=-=- */
1502
1503
1504/**
1505 * Destruct a driver instance.
1506 *
1507 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1508 * resources can be freed correctly.
1509 *
1510 * @param pDrvIns The driver instance data.
1511 */
1512static DECLCALLBACK(void) vusbRhDestruct(PPDMDRVINS pDrvIns)
1513{
1514 PVUSBROOTHUB pRh = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1515 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1516
1517 vusbUrbPoolDestroy(&pRh->Hub.Dev.UrbPool);
1518 if (pRh->Hub.pszName)
1519 {
1520 RTStrFree(pRh->Hub.pszName);
1521 pRh->Hub.pszName = NULL;
1522 }
1523 if (pRh->hSniffer != VUSBSNIFFER_NIL)
1524 VUSBSnifferDestroy(pRh->hSniffer);
1525
1526 if (pRh->hSemEventPeriodFrame)
1527 RTSemEventMultiDestroy(pRh->hSemEventPeriodFrame);
1528
1529 if (pRh->hSemEventPeriodFrameStopped)
1530 RTSemEventMultiDestroy(pRh->hSemEventPeriodFrameStopped);
1531
1532 RTCritSectDelete(&pRh->CritSectDevices);
1533}
1534
1535
1536/**
1537 * Construct a root hub driver instance.
1538 *
1539 * @copydoc FNPDMDRVCONSTRUCT
1540 */
1541static DECLCALLBACK(int) vusbRhConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1542{
1543 RT_NOREF(fFlags);
1544 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1545 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB);
1546 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1547
1548 LogFlow(("vusbRhConstruct: Instance %d\n", pDrvIns->iInstance));
1549
1550 /*
1551 * Validate configuration.
1552 */
1553 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "CaptureFilename", "");
1554
1555 /*
1556 * Check that there are no drivers below us.
1557 */
1558 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1559 ("Configuration error: Not possible to attach anything to this driver!\n"),
1560 VERR_PDM_DRVINS_NO_ATTACH);
1561
1562 /*
1563 * Initialize the critical sections.
1564 */
1565 int rc = RTCritSectInit(&pThis->CritSectDevices);
1566 if (RT_FAILURE(rc))
1567 return rc;
1568
1569 char *pszCaptureFilename = NULL;
1570 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "CaptureFilename", &pszCaptureFilename);
1571 if ( RT_FAILURE(rc)
1572 && rc != VERR_CFGM_VALUE_NOT_FOUND)
1573 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1574 N_("Configuration error: Failed to query value of \"CaptureFilename\""));
1575
1576 /*
1577 * Initialize the data members.
1578 */
1579 pDrvIns->IBase.pfnQueryInterface = vusbRhQueryInterface;
1580 /* the usb device */
1581 pThis->Hub.Dev.enmState = VUSB_DEVICE_STATE_ATTACHED;
1582 pThis->Hub.Dev.cRefs = 1;
1583 /* the hub */
1584 pThis->Hub.pRootHub = pThis;
1585 //pThis->hub.cPorts - later
1586 pThis->Hub.cDevices = 0;
1587 pThis->Hub.Dev.pHub = &pThis->Hub;
1588 RTStrAPrintf(&pThis->Hub.pszName, "RootHub#%d", pDrvIns->iInstance);
1589 /* misc */
1590 pThis->pDrvIns = pDrvIns;
1591 /* the connector */
1592 pThis->IRhConnector.pfnSetUrbParams = vusbRhSetUrbParams;
1593 pThis->IRhConnector.pfnReset = vusbR3RhReset;
1594 pThis->IRhConnector.pfnPowerOn = vusbR3RhPowerOn;
1595 pThis->IRhConnector.pfnPowerOff = vusbR3RhPowerOff;
1596 pThis->IRhConnector.pfnNewUrb = vusbRhConnNewUrb;
1597 pThis->IRhConnector.pfnFreeUrb = vusbRhConnFreeUrb;
1598 pThis->IRhConnector.pfnSubmitUrb = vusbRhSubmitUrb;
1599 pThis->IRhConnector.pfnReapAsyncUrbs = vusbRhReapAsyncUrbs;
1600 pThis->IRhConnector.pfnCancelUrbsEp = vusbRhCancelUrbsEp;
1601 pThis->IRhConnector.pfnCancelAllUrbs = vusbRhCancelAllUrbs;
1602 pThis->IRhConnector.pfnAbortEp = vusbRhAbortEp;
1603 pThis->IRhConnector.pfnSetPeriodicFrameProcessing = vusbRhSetFrameProcessing;
1604 pThis->IRhConnector.pfnGetPeriodicFrameRate = vusbRhGetPeriodicFrameRate;
1605 pThis->IRhConnector.pfnUpdateIsocFrameDelta = vusbRhUpdateIsocFrameDelta;
1606 pThis->IRhConnector.pfnDevReset = vusbR3RhDevReset;
1607 pThis->IRhConnector.pfnDevPowerOn = vusbR3RhDevPowerOn;
1608 pThis->IRhConnector.pfnDevPowerOff = vusbR3RhDevPowerOff;
1609 pThis->IRhConnector.pfnDevGetState = vusbR3RhDevGetState;
1610 pThis->IRhConnector.pfnDevIsSavedStateSupported = vusbR3RhDevIsSavedStateSupported;
1611 pThis->IRhConnector.pfnDevGetSpeed = vusbR3RhDevGetSpeed;
1612 pThis->hSniffer = VUSBSNIFFER_NIL;
1613 pThis->cbHci = 0;
1614 pThis->cbHciTd = 0;
1615 pThis->fFrameProcessing = false;
1616#ifdef LOG_ENABLED
1617 pThis->iSerial = 0;
1618#endif
1619 /*
1620 * Resolve interface(s).
1621 */
1622 pThis->pIRhPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, VUSBIROOTHUBPORT);
1623 AssertMsgReturn(pThis->pIRhPort, ("Configuration error: the device/driver above us doesn't expose any VUSBIROOTHUBPORT interface!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
1624
1625 /*
1626 * Get number of ports and the availability bitmap.
1627 * ASSUME that the number of ports reported now at creation time is the max number.
1628 */
1629 pThis->Hub.cPorts = pThis->pIRhPort->pfnGetAvailablePorts(pThis->pIRhPort, &pThis->Bitmap);
1630 Log(("vusbRhConstruct: cPorts=%d\n", pThis->Hub.cPorts));
1631
1632 /*
1633 * Get the USB version of the attached HC.
1634 * ASSUME that version 2.0 implies high-speed.
1635 */
1636 pThis->fHcVersions = pThis->pIRhPort->pfnGetUSBVersions(pThis->pIRhPort);
1637 Log(("vusbRhConstruct: fHcVersions=%u\n", pThis->fHcVersions));
1638
1639 rc = vusbUrbPoolInit(&pThis->Hub.Dev.UrbPool);
1640 if (RT_FAILURE(rc))
1641 return rc;
1642
1643 if (pszCaptureFilename)
1644 {
1645 rc = VUSBSnifferCreate(&pThis->hSniffer, 0, pszCaptureFilename, NULL, NULL);
1646 if (RT_FAILURE(rc))
1647 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1648 N_("VUSBSniffer cannot open '%s' for writing. The directory must exist and it must be writable for the current user"),
1649 pszCaptureFilename);
1650
1651 PDMDrvHlpMMHeapFree(pDrvIns, pszCaptureFilename);
1652 }
1653
1654 /*
1655 * Register ourselves as a USB hub.
1656 * The current implementation uses the VUSBIRHCONFIG interface for communication.
1657 */
1658 PCPDMUSBHUBHLP pHlpUsb; /* not used currently */
1659 rc = PDMDrvHlpUSBRegisterHub(pDrvIns, pThis->fHcVersions, pThis->Hub.cPorts, &g_vusbHubReg, &pHlpUsb);
1660 if (RT_FAILURE(rc))
1661 return rc;
1662
1663 /*
1664 * Register the saved state data unit for attaching devices.
1665 */
1666 rc = PDMDrvHlpSSMRegisterEx(pDrvIns, VUSB_ROOTHUB_SAVED_STATE_VERSION, 0,
1667 NULL, NULL, NULL,
1668 vusbR3RhSavePrep, NULL, vusbR3RhSaveDone,
1669 vusbR3RhLoadPrep, NULL, vusbR3RhLoadDone);
1670 AssertRCReturn(rc, rc);
1671
1672 /*
1673 * Statistics. (It requires a 30" monitor or extremely tiny fonts to edit this "table".)
1674 */
1675#ifdef VBOX_WITH_STATISTICS
1676 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "The number of URBs submitted.", "/VUSB/%d/UrbsSubmitted", pDrvIns->iInstance);
1677 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Bulk transfer.", "/VUSB/%d/UrbsSubmitted/Bulk", pDrvIns->iInstance);
1678 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Control transfer.", "/VUSB/%d/UrbsSubmitted/Ctrl", pDrvIns->iInstance);
1679 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Interrupt transfer.", "/VUSB/%d/UrbsSubmitted/Intr", pDrvIns->iInstance);
1680 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Isochronous transfer.", "/VUSB/%d/UrbsSubmitted/Isoc", pDrvIns->iInstance);
1681
1682 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "The number of URBs cancelled. (included in failed)", "/VUSB/%d/UrbsCancelled", pDrvIns->iInstance);
1683 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Bulk transfer.", "/VUSB/%d/UrbsCancelled/Bulk", pDrvIns->iInstance);
1684 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Control transfer.", "/VUSB/%d/UrbsCancelled/Ctrl", pDrvIns->iInstance);
1685 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Interrupt transfer.", "/VUSB/%d/UrbsCancelled/Intr", pDrvIns->iInstance);
1686 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Isochronous transfer.", "/VUSB/%d/UrbsCancelled/Isoc", pDrvIns->iInstance);
1687
1688 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "The number of URBs failing.", "/VUSB/%d/UrbsFailed", pDrvIns->iInstance);
1689 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Bulk transfer.", "/VUSB/%d/UrbsFailed/Bulk", pDrvIns->iInstance);
1690 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Control transfer.", "/VUSB/%d/UrbsFailed/Ctrl", pDrvIns->iInstance);
1691 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Interrupt transfer.", "/VUSB/%d/UrbsFailed/Intr", pDrvIns->iInstance);
1692 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Isochronous transfer.", "/VUSB/%d/UrbsFailed/Isoc", pDrvIns->iInstance);
1693
1694 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Total requested transfer.", "/VUSB/%d/ReqBytes", pDrvIns->iInstance);
1695 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ReqBytes/Bulk", pDrvIns->iInstance);
1696 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ReqBytes/Ctrl", pDrvIns->iInstance);
1697 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ReqBytes/Intr", pDrvIns->iInstance);
1698 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ReqBytes/Isoc", pDrvIns->iInstance);
1699
1700 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Total requested read transfer.", "/VUSB/%d/ReqReadBytes", pDrvIns->iInstance);
1701 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ReqReadBytes/Bulk", pDrvIns->iInstance);
1702 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ReqReadBytes/Ctrl", pDrvIns->iInstance);
1703 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ReqReadBytes/Intr", pDrvIns->iInstance);
1704 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ReqReadBytes/Isoc", pDrvIns->iInstance);
1705
1706 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Total requested write transfer.", "/VUSB/%d/ReqWriteBytes", pDrvIns->iInstance);
1707 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ReqWriteBytes/Bulk", pDrvIns->iInstance);
1708 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ReqWriteBytes/Ctrl", pDrvIns->iInstance);
1709 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ReqWriteBytes/Intr", pDrvIns->iInstance);
1710 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ReqWriteBytes/Isoc", pDrvIns->iInstance);
1711
1712 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Actual total transfer.", "/VUSB/%d/ActBytes", pDrvIns->iInstance);
1713 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ActBytes/Bulk", pDrvIns->iInstance);
1714 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ActBytes/Ctrl", pDrvIns->iInstance);
1715 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ActBytes/Intr", pDrvIns->iInstance);
1716 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ActBytes/Isoc", pDrvIns->iInstance);
1717
1718 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Actual total read transfer.", "/VUSB/%d/ActReadBytes", pDrvIns->iInstance);
1719 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ActReadBytes/Bulk", pDrvIns->iInstance);
1720 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ActReadBytes/Ctrl", pDrvIns->iInstance);
1721 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ActReadBytes/Intr", pDrvIns->iInstance);
1722 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ActReadBytes/Isoc", pDrvIns->iInstance);
1723
1724 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->Total.StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Actual total write transfer.", "/VUSB/%d/ActWriteBytes", pDrvIns->iInstance);
1725 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Bulk transfer.", "/VUSB/%d/ActWriteBytes/Bulk", pDrvIns->iInstance);
1726 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Control transfer.", "/VUSB/%d/ActWriteBytes/Ctrl", pDrvIns->iInstance);
1727 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Interrupt transfer.", "/VUSB/%d/ActWriteBytes/Intr", pDrvIns->iInstance);
1728 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Isochronous transfer.", "/VUSB/%d/ActWriteBytes/Isoc", pDrvIns->iInstance);
1729
1730 /* bulk */
1731 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of submitted URBs.", "/VUSB/%d/Bulk/Urbs", pDrvIns->iInstance);
1732 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of failed URBs.", "/VUSB/%d/Bulk/UrbsFailed", pDrvIns->iInstance);
1733 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of cancelled URBs.", "/VUSB/%d/Bulk/UrbsFailed/Cancelled", pDrvIns->iInstance);
1734 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of bytes transferred.", "/VUSB/%d/Bulk/ActBytes", pDrvIns->iInstance);
1735 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Bulk/ActBytes/Read", pDrvIns->iInstance);
1736 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Bulk/ActBytes/Write", pDrvIns->iInstance);
1737 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Requested number of bytes.", "/VUSB/%d/Bulk/ReqBytes", pDrvIns->iInstance);
1738 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Bulk/ReqBytes/Read", pDrvIns->iInstance);
1739 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_BULK].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Bulk/ReqBytes/Write", pDrvIns->iInstance);
1740
1741 /* control */
1742 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of submitted URBs.", "/VUSB/%d/Ctrl/Urbs", pDrvIns->iInstance);
1743 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of failed URBs.", "/VUSB/%d/Ctrl/UrbsFailed", pDrvIns->iInstance);
1744 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of cancelled URBs.", "/VUSB/%d/Ctrl/UrbsFailed/Cancelled", pDrvIns->iInstance);
1745 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of bytes transferred.", "/VUSB/%d/Ctrl/ActBytes", pDrvIns->iInstance);
1746 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Ctrl/ActBytes/Read", pDrvIns->iInstance);
1747 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Ctrl/ActBytes/Write", pDrvIns->iInstance);
1748 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Requested number of bytes.", "/VUSB/%d/Ctrl/ReqBytes", pDrvIns->iInstance);
1749 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Ctrl/ReqBytes/Read", pDrvIns->iInstance);
1750 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_CTRL].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Ctrl/ReqBytes/Write", pDrvIns->iInstance);
1751
1752 /* interrupt */
1753 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of submitted URBs.", "/VUSB/%d/Intr/Urbs", pDrvIns->iInstance);
1754 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of failed URBs.", "/VUSB/%d/Intr/UrbsFailed", pDrvIns->iInstance);
1755 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of cancelled URBs.", "/VUSB/%d/Intr/UrbsFailed/Cancelled", pDrvIns->iInstance);
1756 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of bytes transferred.", "/VUSB/%d/Intr/ActBytes", pDrvIns->iInstance);
1757 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Intr/ActBytes/Read", pDrvIns->iInstance);
1758 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Intr/ActBytes/Write", pDrvIns->iInstance);
1759 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Requested number of bytes.", "/VUSB/%d/Intr/ReqBytes", pDrvIns->iInstance);
1760 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Intr/ReqBytes/Read", pDrvIns->iInstance);
1761 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_INTR].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Intr/ReqBytes/Write", pDrvIns->iInstance);
1762
1763 /* isochronous */
1764 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of submitted URBs.", "/VUSB/%d/Isoc/Urbs", pDrvIns->iInstance);
1765 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of failed URBs.", "/VUSB/%d/Isoc/UrbsFailed", pDrvIns->iInstance);
1766 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatUrbsCancelled, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of cancelled URBs.", "/VUSB/%d/Isoc/UrbsFailed/Cancelled", pDrvIns->iInstance);
1767 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of bytes transferred.", "/VUSB/%d/Isoc/ActBytes", pDrvIns->iInstance);
1768 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Isoc/ActBytes/Read", pDrvIns->iInstance);
1769 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatActWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Isoc/ActBytes/Write", pDrvIns->iInstance);
1770 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Requested number of bytes.", "/VUSB/%d/Isoc/ReqBytes", pDrvIns->iInstance);
1771 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqReadBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Read.", "/VUSB/%d/Isoc/ReqBytes/Read", pDrvIns->iInstance);
1772 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aTypes[VUSBXFERTYPE_ISOC].StatReqWriteBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Write.", "/VUSB/%d/Isoc/ReqBytes/Write", pDrvIns->iInstance);
1773 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocActPkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of isochronous packets returning data.", "/VUSB/%d/Isoc/ActPkts", pDrvIns->iInstance);
1774 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocActReadPkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Read.", "/VUSB/%d/Isoc/ActPkts/Read", pDrvIns->iInstance);
1775 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocActWritePkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Write.", "/VUSB/%d/Isoc/ActPkts/Write", pDrvIns->iInstance);
1776 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocReqPkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Requested number of isochronous packets.", "/VUSB/%d/Isoc/ReqPkts", pDrvIns->iInstance);
1777 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocReqReadPkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Read.", "/VUSB/%d/Isoc/ReqPkts/Read", pDrvIns->iInstance);
1778 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatIsocReqWritePkts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Write.", "/VUSB/%d/Isoc/ReqPkts/Write", pDrvIns->iInstance);
1779
1780 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aStatIsocDetails); i++)
1781 {
1782 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].Pkts, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d", pDrvIns->iInstance, i);
1783 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].Ok, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/Ok", pDrvIns->iInstance, i);
1784 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].Ok0, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/Ok0", pDrvIns->iInstance, i);
1785 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].DataUnderrun, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/DataUnderrun", pDrvIns->iInstance, i);
1786 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].DataUnderrun0, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/DataUnderrun0", pDrvIns->iInstance, i);
1787 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].DataOverrun, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/DataOverrun", pDrvIns->iInstance, i);
1788 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].NotAccessed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/NotAccessed", pDrvIns->iInstance, i);
1789 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].Misc, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT, ".", "/VUSB/%d/Isoc/%d/Misc", pDrvIns->iInstance, i);
1790 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->aStatIsocDetails[i].Bytes, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, ".", "/VUSB/%d/Isoc/%d/Bytes", pDrvIns->iInstance, i);
1791 }
1792
1793 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReapAsyncUrbs, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling the vusbRhReapAsyncUrbs body (omitting calls when nothing is in-flight).",
1794 "/VUSB/%d/ReapAsyncUrbs", pDrvIns->iInstance);
1795 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSubmitUrb, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling the vusbRhSubmitUrb body.",
1796 "/VUSB/%d/SubmitUrb", pDrvIns->iInstance);
1797 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatFramesProcessedThread, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Processed frames in the dedicated thread",
1798 "/VUSB/%d/FramesProcessedThread", pDrvIns->iInstance);
1799 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatFramesProcessedClbk, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Processed frames in the URB completion callback",
1800 "/VUSB/%d/FramesProcessedClbk", pDrvIns->iInstance);
1801#endif
1802 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->Hub.Dev.UrbPool.cUrbsInPool, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "The number of URBs in the pool.",
1803 "/VUSB/%d/cUrbsInPool", pDrvIns->iInstance);
1804
1805 return VINF_SUCCESS;
1806}
1807
1808
1809/**
1810 * VUSB Root Hub driver registration record.
1811 */
1812const PDMDRVREG g_DrvVUSBRootHub =
1813{
1814 /* u32Version */
1815 PDM_DRVREG_VERSION,
1816 /* szName */
1817 "VUSBRootHub",
1818 /* szRCMod */
1819 "",
1820 /* szR0Mod */
1821 "",
1822 /* pszDescription */
1823 "VUSB Root Hub Driver.",
1824 /* fFlags */
1825 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1826 /* fClass. */
1827 PDM_DRVREG_CLASS_USB,
1828 /* cMaxInstances */
1829 ~0U,
1830 /* cbInstance */
1831 sizeof(VUSBROOTHUB),
1832 /* pfnConstruct */
1833 vusbRhConstruct,
1834 /* pfnDestruct */
1835 vusbRhDestruct,
1836 /* pfnRelocate */
1837 NULL,
1838 /* pfnIOCtl */
1839 NULL,
1840 /* pfnPowerOn */
1841 NULL,
1842 /* pfnReset */
1843 NULL,
1844 /* pfnSuspend */
1845 NULL,
1846 /* pfnResume */
1847 NULL,
1848 /* pfnAttach */
1849 NULL,
1850 /* pfnDetach */
1851 NULL,
1852 /* pfnPowerOff */
1853 NULL,
1854 /* pfnSoftReset */
1855 NULL,
1856 /* u32EndVersion */
1857 PDM_DRVREG_VERSION
1858};
1859
1860/*
1861 * Local Variables:
1862 * mode: c
1863 * c-file-style: "bsd"
1864 * c-basic-offset: 4
1865 * tab-width: 4
1866 * indent-tabs-mode: s
1867 * End:
1868 */
1869
Note: See TracBrowser for help on using the repository browser.

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