VirtualBox

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

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

Devices/USB: Convert the HCI emulations to call into the roothub using the devices port instead of using the VUSBIDEVICE interface directly. This will avoid races when devices will get detached unexpectedly while being in use. Also move the device re-attach logic after a saved state operation down to the roothub in order to avoid code duplication, bugref:10196

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