Changeset 93955 in vbox for trunk/src/VBox/Devices/USB
- Timestamp:
- Feb 25, 2022 4:13:44 PM (3 years ago)
- Location:
- trunk/src/VBox/Devices/USB
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/USB/DrvVUSBRootHub.cpp
r93939 r93955 294 294 295 295 /** 296 * Attaches a device to a specific hub. 297 * 298 * This function is called by the vusb_add_device() and vusbRhAttachDevice(). 299 * 300 * @returns VBox status code. 301 * @param pHub The hub to attach it to. 302 * @param pDev The device to attach. 303 * @thread EMT 304 */ 305 static int vusbHubAttach(PVUSBHUB pHub, PVUSBDEV pDev) 306 { 307 LogFlow(("vusbHubAttach: pHub=%p[%s] pDev=%p[%s]\n", pHub, pHub->pszName, pDev, pDev->pUsbIns->pszName)); 308 return vusbDevAttach(pDev, pHub); 309 } 310 311 312 /* -=-=-=-=-=- PDMUSBHUBREG methods -=-=-=-=-=- */ 313 314 /** @interface_method_impl{PDMUSBHUBREG,pfnAttachDevice} */ 315 static DECLCALLBACK(int) vusbPDMHubAttachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, const char *pszCaptureFilename, uint32_t *piPort) 316 { 317 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB); 318 319 /* 320 * Allocate a new VUSB device and initialize it. 321 */ 322 PVUSBDEV pDev = (PVUSBDEV)RTMemAllocZ(sizeof(*pDev)); 323 AssertReturn(pDev, VERR_NO_MEMORY); 324 int rc = vusbDevInit(pDev, pUsbIns, pszCaptureFilename); 325 if (RT_SUCCESS(rc)) 326 { 327 pUsbIns->pvVUsbDev2 = pDev; 328 rc = vusbHubAttach(&pThis->Hub, pDev); 329 if (RT_SUCCESS(rc)) 330 { 331 *piPort = UINT32_MAX; /// @todo implement piPort 332 return rc; 333 } 334 335 RTMemFree(pDev->paIfStates); 336 pUsbIns->pvVUsbDev2 = NULL; 337 } 338 vusbDevRelease(pDev); 339 return rc; 340 } 341 342 343 /** @interface_method_impl{PDMUSBHUBREG,pfnDetachDevice} */ 344 static DECLCALLBACK(int) vusbPDMHubDetachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, uint32_t iPort) 345 { 346 RT_NOREF(pDrvIns, iPort); 347 PVUSBDEV pDev = (PVUSBDEV)pUsbIns->pvVUsbDev2; 348 Assert(pDev); 349 350 /* 351 * Deal with pending async reset. 352 * (anything but reset) 353 */ 354 vusbDevSetStateCmp(pDev, VUSB_DEVICE_STATE_DEFAULT, VUSB_DEVICE_STATE_RESET); 355 356 /* 357 * Detach and free resources. 358 */ 359 if (pDev->pHub) 360 vusbDevDetach(pDev); 361 362 vusbDevRelease(pDev); 363 return VINF_SUCCESS; 364 } 365 366 /** 367 * The hub registration structure. 368 */ 369 static const PDMUSBHUBREG g_vusbHubReg = 370 { 371 PDM_USBHUBREG_VERSION, 372 vusbPDMHubAttachDevice, 373 vusbPDMHubDetachDevice, 374 PDM_USBHUBREG_VERSION 375 }; 376 377 378 /* -=-=-=-=-=- VUSBIROOTHUBCONNECTOR methods -=-=-=-=-=- */ 379 380 381 /** 382 * Callback for freeing an URB. 383 * @param pUrb The URB to free. 384 */ 385 static DECLCALLBACK(void) vusbRhFreeUrb(PVUSBURB pUrb) 386 { 387 /* 388 * Assert sanity. 389 */ 390 vusbUrbAssert(pUrb); 391 PVUSBROOTHUB pRh = (PVUSBROOTHUB)pUrb->pVUsb->pvFreeCtx; 392 Assert(pRh); 393 394 Assert(pUrb->enmState != VUSBURBSTATE_FREE); 395 396 /* 397 * Free the URB description (logging builds only). 398 */ 399 if (pUrb->pszDesc) 400 { 401 RTStrFree(pUrb->pszDesc); 402 pUrb->pszDesc = NULL; 403 } 404 405 /* The URB comes from the roothub if there is no device (invalid address). */ 406 if (pUrb->pVUsb->pDev) 407 { 408 PVUSBDEV pDev = pUrb->pVUsb->pDev; 409 410 vusbUrbPoolFree(&pUrb->pVUsb->pDev->UrbPool, pUrb); 411 vusbDevRelease(pDev); 412 } 413 else 414 vusbUrbPoolFree(&pRh->Hub.Dev.UrbPool, pUrb); 415 } 416 417 418 /** 419 * Worker routine for vusbRhConnNewUrb(). 420 */ 421 static PVUSBURB vusbRhNewUrb(PVUSBROOTHUB pRh, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType, 422 VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag) 423 { 424 RT_NOREF(pszTag); 425 PVUSBURBPOOL pUrbPool = &pRh->Hub.Dev.UrbPool; 426 427 if (RT_UNLIKELY(cbData > (32 * _1M))) 428 { 429 LogFunc(("Bad URB size (%u)!\n", cbData)); 430 return NULL; 431 } 432 433 PVUSBDEV pDev; 434 if (uPort == VUSB_DEVICE_PORT_INVALID) 435 pDev = vusbR3RhGetVUsbDevByAddrRetain(pRh, DstAddress); 436 else 437 pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); 438 439 if (pDev) 440 pUrbPool = &pDev->UrbPool; 441 442 PVUSBURB pUrb = vusbUrbPoolAlloc(pUrbPool, enmType, enmDir, cbData, 443 pRh->cbHci, pRh->cbHciTd, cTds); 444 if (RT_LIKELY(pUrb)) 445 { 446 pUrb->pVUsb->pvFreeCtx = pRh; 447 pUrb->pVUsb->pfnFree = vusbRhFreeUrb; 448 pUrb->DstAddress = DstAddress; 449 pUrb->pVUsb->pDev = pDev; 450 451 #ifdef LOG_ENABLED 452 const char *pszType = NULL; 453 454 switch(pUrb->enmType) 455 { 456 case VUSBXFERTYPE_CTRL: 457 pszType = "ctrl"; 458 break; 459 case VUSBXFERTYPE_INTR: 460 pszType = "intr"; 461 break; 462 case VUSBXFERTYPE_BULK: 463 pszType = "bulk"; 464 break; 465 case VUSBXFERTYPE_ISOC: 466 pszType = "isoc"; 467 break; 468 default: 469 pszType = "invld"; 470 break; 471 } 472 473 pRh->iSerial = (pRh->iSerial + 1) % 10000; 474 RTStrAPrintf(&pUrb->pszDesc, "URB %p %s%c%04d (%s)", pUrb, pszType, 475 (pUrb->enmDir == VUSBDIRECTION_IN) ? '<' : ((pUrb->enmDir == VUSBDIRECTION_SETUP) ? 's' : '>'), 476 pRh->iSerial, pszTag ? pszTag : "<none>"); 477 #endif 478 } 479 480 return pUrb; 481 } 482 483 484 /** 485 * Calculate frame timer variables given a frame rate. 486 */ 487 static void vusbRhR3CalcTimerIntervals(PVUSBROOTHUB pThis, uint32_t u32FrameRate) 488 { 489 pThis->nsWait = RT_NS_1SEC / u32FrameRate; 490 pThis->uFrameRate = u32FrameRate; 491 /* Inform the HCD about the new frame rate. */ 492 pThis->pIRhPort->pfnFrameRateChanged(pThis->pIRhPort, u32FrameRate); 493 } 494 495 496 /** 497 * Calculates the new frame rate based on the idle detection and number of idle 498 * cycles. 499 * 500 * @returns nothing. 501 * @param pThis The roothub instance data. 502 * @param fIdle Flag whether the last frame didn't produce any activity. 503 */ 504 static void vusbRhR3FrameRateCalcNew(PVUSBROOTHUB pThis, bool fIdle) 505 { 506 uint32_t uNewFrameRate = pThis->uFrameRate; 507 508 /* 509 * Adjust the frame timer interval based on idle detection. 510 */ 511 if (fIdle) 512 { 513 pThis->cIdleCycles++; 514 /* Set the new frame rate based on how long we've been idle. Tunable. */ 515 switch (pThis->cIdleCycles) 516 { 517 case 4: uNewFrameRate = 500; break; /* 2ms interval */ 518 case 16:uNewFrameRate = 125; break; /* 8ms interval */ 519 case 24:uNewFrameRate = 50; break; /* 20ms interval */ 520 default: break; 521 } 522 /* Avoid overflow. */ 523 if (pThis->cIdleCycles > 60000) 524 pThis->cIdleCycles = 20000; 525 } 526 else 527 { 528 if (pThis->cIdleCycles) 529 { 530 pThis->cIdleCycles = 0; 531 uNewFrameRate = pThis->uFrameRateDefault; 532 } 533 } 534 535 if ( uNewFrameRate != pThis->uFrameRate 536 && uNewFrameRate) 537 { 538 LogFlow(("Frame rate changed from %u to %u\n", pThis->uFrameRate, uNewFrameRate)); 539 vusbRhR3CalcTimerIntervals(pThis, uNewFrameRate); 540 } 541 } 542 543 544 /** 545 * The core frame processing routine keeping track of the elapsed time and calling into 546 * the device emulation above us to do the work. 547 * 548 * @returns Relative timespan when to process the next frame. 549 * @param pThis The roothub instance data. 550 * @param fCallback Flag whether this method is called from the URB completion callback or 551 * from the worker thread (only used for statistics). 552 */ 553 DECLHIDDEN(uint64_t) vusbRhR3ProcessFrame(PVUSBROOTHUB pThis, bool fCallback) 554 { 555 uint64_t tsNext = 0; 556 uint64_t tsNanoStart = RTTimeNanoTS(); 557 558 /* Don't do anything if we are not supposed to process anything (EHCI and XHCI). */ 559 if (!pThis->uFrameRateDefault) 560 return 0; 561 562 if (ASMAtomicXchgBool(&pThis->fFrameProcessing, true)) 563 return pThis->nsWait; 564 565 if ( tsNanoStart > pThis->tsFrameProcessed 566 && tsNanoStart - pThis->tsFrameProcessed >= 750 * RT_NS_1US) 567 { 568 LogFlowFunc(("Starting new frame at ts %llu\n", tsNanoStart)); 569 570 bool fIdle = pThis->pIRhPort->pfnStartFrame(pThis->pIRhPort, 0 /* u32FrameNo */); 571 vusbRhR3FrameRateCalcNew(pThis, fIdle); 572 573 uint64_t tsNow = RTTimeNanoTS(); 574 tsNext = (tsNanoStart + pThis->nsWait) > tsNow ? (tsNanoStart + pThis->nsWait) - tsNow : 0; 575 pThis->tsFrameProcessed = tsNanoStart; 576 LogFlowFunc(("Current frame took %llu nano seconds to process, next frame in %llu ns\n", tsNow - tsNanoStart, tsNext)); 577 if (fCallback) 578 STAM_COUNTER_INC(&pThis->StatFramesProcessedClbk); 579 else 580 STAM_COUNTER_INC(&pThis->StatFramesProcessedThread); 581 } 582 else 583 { 584 tsNext = (pThis->tsFrameProcessed + pThis->nsWait) > tsNanoStart ? (pThis->tsFrameProcessed + pThis->nsWait) - tsNanoStart : 0; 585 LogFlowFunc(("Next frame is too far away in the future, waiting... (tsNanoStart=%llu tsFrameProcessed=%llu)\n", 586 tsNanoStart, pThis->tsFrameProcessed)); 587 } 588 589 ASMAtomicXchgBool(&pThis->fFrameProcessing, false); 590 LogFlowFunc(("returns %llu\n", tsNext)); 591 return tsNext; 592 } 593 594 595 /** 596 * Worker for processing frames periodically. 597 * 598 * @returns VBox status code. 599 * @param pDrvIns The driver instance. 600 * @param pThread The PDM thread structure for the thread this worker runs on. 601 */ 602 static DECLCALLBACK(int) vusbRhR3PeriodFrameWorker(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) 603 { 604 RT_NOREF(pDrvIns); 605 int rc = VINF_SUCCESS; 606 PVUSBROOTHUB pThis = (PVUSBROOTHUB)pThread->pvUser; 607 608 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) 609 return VINF_SUCCESS; 610 611 while (pThread->enmState == PDMTHREADSTATE_RUNNING) 612 { 613 while ( !ASMAtomicReadU32(&pThis->uFrameRateDefault) 614 && pThread->enmState == PDMTHREADSTATE_RUNNING) 615 { 616 /* Signal the waiter that we are stopped now. */ 617 rc = RTSemEventMultiSignal(pThis->hSemEventPeriodFrameStopped); 618 AssertRC(rc); 619 620 rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrame, RT_INDEFINITE_WAIT); 621 RTSemEventMultiReset(pThis->hSemEventPeriodFrame); 622 623 /* 624 * Notify the device above about the frame rate changed if we are supposed to 625 * process frames. 626 */ 627 uint32_t uFrameRate = ASMAtomicReadU32(&pThis->uFrameRateDefault); 628 if (uFrameRate) 629 vusbRhR3CalcTimerIntervals(pThis, uFrameRate); 630 } 631 632 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc); 633 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING)) 634 break; 635 636 uint64_t tsNext = vusbRhR3ProcessFrame(pThis, false /* fCallback */); 637 638 if (tsNext >= 250 * RT_NS_1US) 639 { 640 rc = RTSemEventMultiWaitEx(pThis->hSemEventPeriodFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE, 641 tsNext); 642 AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc)); 643 RTSemEventMultiReset(pThis->hSemEventPeriodFrame); 644 } 645 } 646 647 return VINF_SUCCESS; 648 } 649 650 651 /** 652 * Unblock the periodic frame thread so it can respond to a state change. 653 * 654 * @returns VBox status code. 655 * @param pDrvIns The driver instance. 656 * @param pThread The send thread. 657 */ 658 static DECLCALLBACK(int) vusbRhR3PeriodFrameWorkerWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) 659 { 660 RT_NOREF(pThread); 661 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB); 662 return RTSemEventMultiSignal(pThis->hSemEventPeriodFrame); 663 } 664 665 666 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetUrbParams} */ 667 static DECLCALLBACK(int) vusbRhSetUrbParams(PVUSBIROOTHUBCONNECTOR pInterface, size_t cbHci, size_t cbHciTd) 668 { 669 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 670 671 pRh->cbHci = cbHci; 672 pRh->cbHciTd = cbHciTd; 673 674 return VINF_SUCCESS; 675 } 676 677 678 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReset} */ 679 static DECLCALLBACK(int) vusbR3RhReset(PVUSBIROOTHUBCONNECTOR pInterface, bool fResetOnLinux) 680 { 681 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 682 return pRh->pIRhPort->pfnReset(pRh->pIRhPort, fResetOnLinux); 683 } 684 685 686 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOn} */ 687 static DECLCALLBACK(int) vusbR3RhPowerOn(PVUSBIROOTHUBCONNECTOR pInterface) 688 { 689 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 690 LogFlow(("vusR3bRhPowerOn: pRh=%p\n", pRh)); 691 692 Assert( pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED 693 && pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET); 694 695 if (pRh->Hub.Dev.enmState == VUSB_DEVICE_STATE_ATTACHED) 696 pRh->Hub.Dev.enmState = VUSB_DEVICE_STATE_POWERED; 697 698 return VINF_SUCCESS; 699 } 700 701 702 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOff} */ 703 static DECLCALLBACK(int) vusbR3RhPowerOff(PVUSBIROOTHUBCONNECTOR pInterface) 704 { 705 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 706 LogFlow(("vusbR3RhDevPowerOff: pThis=%p\n", pThis)); 707 708 Assert( pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED 709 && pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET); 710 711 /* 712 * Cancel all URBs and reap them. 713 */ 714 VUSBIRhCancelAllUrbs(&pThis->IRhConnector); 715 for (uint32_t uPort = 0; uPort < RT_ELEMENTS(pThis->apDevByPort); uPort++) 716 VUSBIRhReapAsyncUrbs(&pThis->IRhConnector, uPort, 0); 717 718 pThis->Hub.Dev.enmState = VUSB_DEVICE_STATE_ATTACHED; 719 return VINF_SUCCESS; 720 } 721 722 723 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnNewUrb} */ 724 static DECLCALLBACK(PVUSBURB) vusbRhConnNewUrb(PVUSBIROOTHUBCONNECTOR pInterface, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType, 725 VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag) 726 { 727 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 728 return vusbRhNewUrb(pRh, DstAddress, uPort, enmType, enmDir, cbData, cTds, pszTag); 729 } 730 731 732 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnFreeUrb} */ 733 static DECLCALLBACK(int) vusbRhConnFreeUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb) 734 { 735 RT_NOREF(pInterface); 736 pUrb->pVUsb->pfnFree(pUrb); 737 return VINF_SUCCESS; 738 } 739 740 741 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSubmitUrb} */ 742 static DECLCALLBACK(int) vusbRhSubmitUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb, PPDMLED pLed) 743 { 744 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 745 STAM_PROFILE_START(&pRh->StatSubmitUrb, a); 746 747 #ifdef VBOX_WITH_STATISTICS 748 /* 749 * Total and per-type submit statistics. 750 */ 751 Assert(pUrb->enmType >= 0 && pUrb->enmType < (int)RT_ELEMENTS(pRh->aTypes)); 752 STAM_COUNTER_INC(&pRh->Total.StatUrbsSubmitted); 753 STAM_COUNTER_INC(&pRh->aTypes[pUrb->enmType].StatUrbsSubmitted); 754 755 STAM_COUNTER_ADD(&pRh->Total.StatReqBytes, pUrb->cbData); 756 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqBytes, pUrb->cbData); 757 if (pUrb->enmDir == VUSBDIRECTION_IN) 758 { 759 STAM_COUNTER_ADD(&pRh->Total.StatReqReadBytes, pUrb->cbData); 760 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqReadBytes, pUrb->cbData); 761 } 762 else 763 { 764 STAM_COUNTER_ADD(&pRh->Total.StatReqWriteBytes, pUrb->cbData); 765 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqWriteBytes, pUrb->cbData); 766 } 767 768 if (pUrb->enmType == VUSBXFERTYPE_ISOC) 769 { 770 STAM_COUNTER_ADD(&pRh->StatIsocReqPkts, pUrb->cIsocPkts); 771 if (pUrb->enmDir == VUSBDIRECTION_IN) 772 STAM_COUNTER_ADD(&pRh->StatIsocReqReadPkts, pUrb->cIsocPkts); 773 else 774 STAM_COUNTER_ADD(&pRh->StatIsocReqWritePkts, pUrb->cIsocPkts); 775 } 776 #endif 777 778 /* If there is a sniffer on the roothub record the URB there. */ 779 if (pRh->hSniffer != VUSBSNIFFER_NIL) 780 { 781 int rc = VUSBSnifferRecordEvent(pRh->hSniffer, pUrb, VUSBSNIFFEREVENT_SUBMIT); 782 if (RT_FAILURE(rc)) 783 LogRel(("VUSB: Capturing URB submit event on the root hub failed with %Rrc\n", rc)); 784 } 785 786 /* 787 * The device was resolved when we allocated the URB. 788 * Submit it to the device if we found it, if not fail with device-not-ready. 789 */ 790 int rc; 791 if ( pUrb->pVUsb->pDev 792 && pUrb->pVUsb->pDev->pUsbIns) 793 { 794 switch (pUrb->enmDir) 795 { 796 case VUSBDIRECTION_IN: 797 pLed->Asserted.s.fReading = pLed->Actual.s.fReading = 1; 798 rc = vusbUrbSubmit(pUrb); 799 pLed->Actual.s.fReading = 0; 800 break; 801 case VUSBDIRECTION_OUT: 802 pLed->Asserted.s.fWriting = pLed->Actual.s.fWriting = 1; 803 rc = vusbUrbSubmit(pUrb); 804 pLed->Actual.s.fWriting = 0; 805 break; 806 default: 807 rc = vusbUrbSubmit(pUrb); 808 break; 809 } 810 811 if (RT_FAILURE(rc)) 812 { 813 LogFlow(("vusbRhSubmitUrb: freeing pUrb=%p\n", pUrb)); 814 pUrb->pVUsb->pfnFree(pUrb); 815 } 816 } 817 else 818 { 819 vusbDevRetain(&pRh->Hub.Dev); 820 pUrb->pVUsb->pDev = &pRh->Hub.Dev; 821 Log(("vusb: pRh=%p: SUBMIT: Address %i not found!!!\n", pRh, pUrb->DstAddress)); 822 823 pUrb->enmState = VUSBURBSTATE_REAPED; 824 pUrb->enmStatus = VUSBSTATUS_DNR; 825 vusbUrbCompletionRh(pUrb); 826 rc = VINF_SUCCESS; 827 } 828 829 STAM_PROFILE_STOP(&pRh->StatSubmitUrb, a); 830 return rc; 831 } 832 833 834 static DECLCALLBACK(int) vusbRhReapAsyncUrbsWorker(PVUSBDEV pDev, RTMSINTERVAL cMillies) 835 { 836 if (!cMillies) 837 vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, 0); 838 else 839 { 840 uint64_t u64Start = RTTimeMilliTS(); 841 do 842 { 843 vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, RT_MIN(cMillies >> 8, 10)); 844 } while ( !RTListIsEmpty(&pDev->LstAsyncUrbs) 845 && RTTimeMilliTS() - u64Start < cMillies); 846 } 847 848 return VINF_SUCCESS; 849 } 850 851 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReapAsyncUrbs} */ 852 static DECLCALLBACK(void) vusbRhReapAsyncUrbs(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, RTMSINTERVAL cMillies) 853 { 854 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); NOREF(pRh); 855 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); 856 857 if ( !pDev 858 || RTListIsEmpty(&pDev->LstAsyncUrbs)) 859 return; 860 861 STAM_PROFILE_START(&pRh->StatReapAsyncUrbs, a); 862 int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhReapAsyncUrbsWorker, 2, pDev, cMillies); 863 AssertRC(rc); 864 STAM_PROFILE_STOP(&pRh->StatReapAsyncUrbs, a); 865 866 vusbDevRelease(pDev); 867 } 868 869 870 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelUrbsEp} */ 871 static DECLCALLBACK(int) vusbRhCancelUrbsEp(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb) 872 { 873 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 874 AssertReturn(pRh, VERR_INVALID_PARAMETER); 875 AssertReturn(pUrb, VERR_INVALID_PARAMETER); 876 877 /// @todo This method of URB canceling may not work on non-Linux hosts. 878 /* 879 * Cancel and reap the URB(s) on an endpoint. 880 */ 881 LogFlow(("vusbRhCancelUrbsEp: pRh=%p pUrb=%p\n", pRh, pUrb)); 882 883 vusbUrbCancelAsync(pUrb, CANCELMODE_UNDO); 884 885 /* The reaper thread will take care of completing the URB. */ 886 887 return VINF_SUCCESS; 888 } 889 890 /** 891 * Worker doing the actual cancelling of all outstanding URBs on the device I/O thread. 892 * 893 * @returns VBox status code. 894 * @param pDev USB device instance data. 895 */ 896 static DECLCALLBACK(int) vusbRhCancelAllUrbsWorker(PVUSBDEV pDev) 897 { 898 /* 899 * Cancel the URBS. 900 * 901 * Not using th CritAsyncUrbs critical section here is safe 902 * as the I/O thread is the only thread accessing this struture at the 903 * moment. 904 */ 905 PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext; 906 RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst) 907 { 908 PVUSBURB pUrb = pVUsbUrb->pUrb; 909 /* Call the worker directly. */ 910 vusbUrbCancelWorker(pUrb, CANCELMODE_FAIL); 911 } 912 913 return VINF_SUCCESS; 914 } 915 916 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelAllUrbs} */ 917 static DECLCALLBACK(void) vusbRhCancelAllUrbs(PVUSBIROOTHUBCONNECTOR pInterface) 918 { 919 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 920 921 RTCritSectEnter(&pThis->CritSectDevices); 922 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++) 923 { 924 PVUSBDEV pDev = pThis->apDevByPort[i]; 925 if (pDev) 926 vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhCancelAllUrbsWorker, 1, pDev); 927 } 928 RTCritSectLeave(&pThis->CritSectDevices); 929 } 930 931 /** 932 * Worker doing the actual cancelling of all outstanding per-EP URBs on the 933 * device I/O thread. 934 * 935 * @returns VBox status code. 936 * @param pDev USB device instance data. 937 * @param EndPt Endpoint number. 938 * @param enmDir Endpoint direction. 939 */ 940 static DECLCALLBACK(int) vusbRhAbortEpWorker(PVUSBDEV pDev, int EndPt, VUSBDIRECTION enmDir) 941 { 942 /* 943 * Iterate the URBs, find ones corresponding to given EP, and cancel them. 944 */ 945 PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext; 946 RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst) 947 { 948 PVUSBURB pUrb = pVUsbUrb->pUrb; 949 950 Assert(pUrb->pVUsb->pDev == pDev); 951 952 /* For the default control EP, direction does not matter. */ 953 if (pUrb->EndPt == EndPt && (pUrb->enmDir == enmDir || !EndPt)) 954 { 955 LogFlow(("%s: vusbRhAbortEpWorker: CANCELING URB\n", pUrb->pszDesc)); 956 int rc = vusbUrbCancelWorker(pUrb, CANCELMODE_UNDO); 957 AssertRC(rc); 958 } 959 } 960 961 return VINF_SUCCESS; 962 } 963 964 965 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnAbortEp} */ 966 static DECLCALLBACK(int) vusbRhAbortEp(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, int EndPt, VUSBDIRECTION enmDir) 967 { 968 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 969 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); 970 971 if (&pRh->Hub != pDev->pHub) 972 AssertFailedReturn(VERR_INVALID_PARAMETER); 973 974 vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhAbortEpWorker, 3, pDev, EndPt, enmDir); 975 vusbDevRelease(pDev); 976 977 /* The reaper thread will take care of completing the URB. */ 978 979 return VINF_SUCCESS; 980 } 981 982 983 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetPeriodicFrameProcessing} */ 984 static DECLCALLBACK(int) vusbRhSetFrameProcessing(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate) 985 { 986 int rc = VINF_SUCCESS; 987 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 988 989 /* Create the frame thread lazily. */ 990 if ( !pThis->hThreadPeriodFrame 991 && uFrameRate) 992 { 993 ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate); 994 pThis->uFrameRate = uFrameRate; 995 vusbRhR3CalcTimerIntervals(pThis, uFrameRate); 996 997 rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrame); 998 AssertRCReturn(rc, rc); 999 1000 rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrameStopped); 1001 AssertRCReturn(rc, rc); 1002 1003 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->hThreadPeriodFrame, pThis, vusbRhR3PeriodFrameWorker, 1004 vusbRhR3PeriodFrameWorkerWakeup, 0, RTTHREADTYPE_IO, "VUsbPeriodFrm"); 1005 AssertRCReturn(rc, rc); 1006 1007 VMSTATE enmState = PDMDrvHlpVMState(pThis->pDrvIns); 1008 if ( enmState == VMSTATE_RUNNING 1009 || enmState == VMSTATE_RUNNING_LS) 1010 { 1011 rc = PDMDrvHlpThreadResume(pThis->pDrvIns, pThis->hThreadPeriodFrame); 1012 AssertRCReturn(rc, rc); 1013 } 1014 } 1015 else if ( pThis->hThreadPeriodFrame 1016 && !uFrameRate) 1017 { 1018 /* Stop processing. */ 1019 uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate); 1020 if (uFrameRateOld) 1021 { 1022 rc = RTSemEventMultiReset(pThis->hSemEventPeriodFrameStopped); 1023 AssertRC(rc); 1024 1025 /* Signal the frame thread to stop. */ 1026 RTSemEventMultiSignal(pThis->hSemEventPeriodFrame); 1027 1028 /* Wait for signal from the thread that it stopped. */ 1029 rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrameStopped, RT_INDEFINITE_WAIT); 1030 AssertRC(rc); 1031 } 1032 } 1033 else if ( pThis->hThreadPeriodFrame 1034 && uFrameRate) 1035 { 1036 /* Just switch to the new frame rate and let the periodic frame thread pick it up. */ 1037 uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate); 1038 1039 /* Signal the frame thread to continue if it was stopped. */ 1040 if (!uFrameRateOld) 1041 RTSemEventMultiSignal(pThis->hSemEventPeriodFrame); 1042 } 1043 1044 return rc; 1045 } 1046 1047 1048 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnGetPeriodicFrameRate} */ 1049 static DECLCALLBACK(uint32_t) vusbRhGetPeriodicFrameRate(PVUSBIROOTHUBCONNECTOR pInterface) 1050 { 1051 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1052 1053 return pThis->uFrameRate; 1054 } 1055 1056 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnUpdateIsocFrameDelta} */ 1057 static DECLCALLBACK(uint32_t) vusbRhUpdateIsocFrameDelta(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, 1058 int EndPt, VUSBDIRECTION enmDir, uint16_t uNewFrameID, uint8_t uBits) 1059 { 1060 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1061 AssertReturn(pRh, 0); 1062 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); AssertPtr(pDev); 1063 PVUSBPIPE pPipe = &pDev->aPipes[EndPt]; 1064 uint32_t *puLastFrame; 1065 int32_t uFrameDelta; 1066 uint32_t uMaxVal = 1 << uBits; 1067 1068 puLastFrame = enmDir == VUSBDIRECTION_IN ? &pPipe->uLastFrameIn : &pPipe->uLastFrameOut; 1069 uFrameDelta = uNewFrameID - *puLastFrame; 1070 *puLastFrame = uNewFrameID; 1071 /* Take care of wrap-around. */ 1072 if (uFrameDelta < 0) 1073 uFrameDelta += uMaxVal; 1074 1075 vusbDevRelease(pDev); 1076 return (uint16_t)uFrameDelta; 1077 } 1078 1079 1080 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevReset} */ 1081 static DECLCALLBACK(int) vusbR3RhDevReset(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, bool fResetOnLinux, 1082 PFNVUSBRESETDONE pfnDone, void *pvUser, PVM pVM) 1083 { 1084 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1085 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1086 AssertPtr(pDev); 1087 1088 int rc = VUSBIDevReset(&pDev->IDevice, fResetOnLinux, pfnDone, pvUser, pVM); 1089 vusbDevRelease(pDev); 1090 return rc; 1091 } 1092 1093 1094 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOn} */ 1095 static DECLCALLBACK(int) vusbR3RhDevPowerOn(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort) 1096 { 1097 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1098 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1099 AssertPtr(pDev); 1100 1101 int rc = VUSBIDevPowerOn(&pDev->IDevice); 1102 vusbDevRelease(pDev); 1103 return rc; 1104 } 1105 1106 1107 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOff} */ 1108 static DECLCALLBACK(int) vusbR3RhDevPowerOff(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort) 1109 { 1110 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1111 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1112 AssertPtr(pDev); 1113 1114 int rc = VUSBIDevPowerOff(&pDev->IDevice); 1115 vusbDevRelease(pDev); 1116 return rc; 1117 } 1118 1119 1120 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetState} */ 1121 static DECLCALLBACK(VUSBDEVICESTATE) vusbR3RhDevGetState(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort) 1122 { 1123 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1124 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1125 AssertPtr(pDev); 1126 1127 VUSBDEVICESTATE enmState = VUSBIDevGetState(&pDev->IDevice); 1128 vusbDevRelease(pDev); 1129 return enmState; 1130 } 1131 1132 1133 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevIsSavedStateSupported} */ 1134 static DECLCALLBACK(bool) vusbR3RhDevIsSavedStateSupported(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort) 1135 { 1136 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1137 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1138 AssertPtr(pDev); 1139 1140 bool fSavedStateSupported = VUSBIDevIsSavedStateSupported(&pDev->IDevice); 1141 vusbDevRelease(pDev); 1142 return fSavedStateSupported; 1143 } 1144 1145 1146 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetSpeed} */ 1147 static DECLCALLBACK(VUSBSPEED) vusbR3RhDevGetSpeed(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort) 1148 { 1149 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1150 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1151 AssertPtr(pDev); 1152 1153 VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice); 1154 vusbDevRelease(pDev); 1155 return enmSpeed; 1156 } 1157 1158 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 */ 1159 301 static const char *vusbGetSpeedString(VUSBSPEED enmSpeed) 1160 302 { … … 1190 332 1191 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 */ 343 static 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 /* -=-=-=-=-=- PDMUSBHUBREG methods -=-=-=-=-=- */ 403 404 /** @interface_method_impl{PDMUSBHUBREG,pfnAttachDevice} */ 405 static DECLCALLBACK(int) vusbPDMHubAttachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, const char *pszCaptureFilename, uint32_t *piPort) 406 { 407 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB); 408 409 /* 410 * Allocate a new VUSB device and initialize it. 411 */ 412 PVUSBDEV pDev = (PVUSBDEV)RTMemAllocZ(sizeof(*pDev)); 413 AssertReturn(pDev, VERR_NO_MEMORY); 414 int rc = vusbDevInit(pDev, pUsbIns, pszCaptureFilename); 415 if (RT_SUCCESS(rc)) 416 { 417 pUsbIns->pvVUsbDev2 = pDev; 418 rc = vusbHubAttach(pThis, pDev); 419 if (RT_SUCCESS(rc)) 420 { 421 *piPort = UINT32_MAX; /// @todo implement piPort 422 return rc; 423 } 424 425 RTMemFree(pDev->paIfStates); 426 pUsbIns->pvVUsbDev2 = NULL; 427 } 428 vusbDevRelease(pDev); 429 return rc; 430 } 431 432 433 /** @interface_method_impl{PDMUSBHUBREG,pfnDetachDevice} */ 434 static DECLCALLBACK(int) vusbPDMHubDetachDevice(PPDMDRVINS pDrvIns, PPDMUSBINS pUsbIns, uint32_t iPort) 435 { 436 RT_NOREF(iPort); 437 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB); 438 PVUSBHUB pHub = &pThis->Hub; 439 PVUSBDEV pDev = (PVUSBDEV)pUsbIns->pvVUsbDev2; 440 Assert(pDev); 441 442 /* 443 * Deal with pending async reset. 444 * (anything but reset) 445 */ 446 vusbDevSetStateCmp(pDev, VUSB_DEVICE_STATE_DEFAULT, VUSB_DEVICE_STATE_RESET); 447 Assert(pDev->i16Port != -1); 448 449 /* Cancel all in-flight URBs from this device. */ 450 vusbDevCancelAllUrbs(pDev, true); 451 452 /* 453 * Detach the device and mark the port as available. 454 */ 455 unsigned uPort = pDev->i16Port; 456 pDev->i16Port = -1; 457 pThis->pIRhPort->pfnDetach(pThis->pIRhPort, uPort); 458 ASMBitSet(&pThis->Bitmap, uPort); 459 pHub->cDevices--; 460 461 /* Check that it's attached and remove it. */ 462 RTCritSectEnter(&pThis->CritSectDevices); 463 Assert(pThis->apDevByPort[uPort] == pDev); 464 pThis->apDevByPort[uPort] = NULL; 465 466 if (pDev->u8Address == VUSB_DEFAULT_ADDRESS) 467 { 468 AssertPtr(pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS]); 469 470 if (pDev == pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS]) 471 pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS] = pDev->pNextDefAddr; 472 else 473 { 474 /* Search the list for the device and remove it. */ 475 PVUSBDEV pDevPrev = pThis->apDevByAddr[VUSB_DEFAULT_ADDRESS]; 476 477 while ( pDevPrev 478 && pDevPrev->pNextDefAddr != pDev) 479 pDevPrev = pDevPrev->pNextDefAddr; 480 481 AssertPtr(pDevPrev); 482 pDevPrev->pNextDefAddr = pDev->pNextDefAddr; 483 } 484 485 pDev->pNextDefAddr = NULL; 486 } 487 else 488 { 489 Assert(pThis->apDevByAddr[pDev->u8Address] == pDev); 490 pThis->apDevByAddr[pDev->u8Address] = NULL; 491 } 492 RTCritSectLeave(&pThis->CritSectDevices); 493 494 /* 495 * Detach and free resources. 496 */ 497 vusbDevDetach(pDev); 498 499 LogRel(("VUSB: Detached '%s' from port %u on %s\n", pDev->pUsbIns->pszName, uPort, pHub->pszName)); 500 vusbDevRelease(pDev); 501 return VINF_SUCCESS; 502 } 503 504 /** 505 * The hub registration structure. 506 */ 507 static const PDMUSBHUBREG g_vusbHubReg = 508 { 509 PDM_USBHUBREG_VERSION, 510 vusbPDMHubAttachDevice, 511 vusbPDMHubDetachDevice, 512 PDM_USBHUBREG_VERSION 513 }; 514 515 516 /* -=-=-=-=-=- VUSBIROOTHUBCONNECTOR methods -=-=-=-=-=- */ 517 518 519 /** 520 * Callback for freeing an URB. 521 * @param pUrb The URB to free. 522 */ 523 static DECLCALLBACK(void) vusbRhFreeUrb(PVUSBURB pUrb) 524 { 525 /* 526 * Assert sanity. 527 */ 528 vusbUrbAssert(pUrb); 529 PVUSBROOTHUB pRh = (PVUSBROOTHUB)pUrb->pVUsb->pvFreeCtx; 530 Assert(pRh); 531 532 Assert(pUrb->enmState != VUSBURBSTATE_FREE); 533 534 /* 535 * Free the URB description (logging builds only). 536 */ 537 if (pUrb->pszDesc) 538 { 539 RTStrFree(pUrb->pszDesc); 540 pUrb->pszDesc = NULL; 541 } 542 543 /* The URB comes from the roothub if there is no device (invalid address). */ 544 if (pUrb->pVUsb->pDev) 545 { 546 PVUSBDEV pDev = pUrb->pVUsb->pDev; 547 548 vusbUrbPoolFree(&pUrb->pVUsb->pDev->UrbPool, pUrb); 549 vusbDevRelease(pDev); 550 } 551 else 552 vusbUrbPoolFree(&pRh->Hub.Dev.UrbPool, pUrb); 553 } 554 555 556 /** 557 * Worker routine for vusbRhConnNewUrb(). 558 */ 559 static PVUSBURB vusbRhNewUrb(PVUSBROOTHUB pRh, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType, 560 VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag) 561 { 562 RT_NOREF(pszTag); 563 PVUSBURBPOOL pUrbPool = &pRh->Hub.Dev.UrbPool; 564 565 if (RT_UNLIKELY(cbData > (32 * _1M))) 566 { 567 LogFunc(("Bad URB size (%u)!\n", cbData)); 568 return NULL; 569 } 570 571 PVUSBDEV pDev; 572 if (uPort == VUSB_DEVICE_PORT_INVALID) 573 pDev = vusbR3RhGetVUsbDevByAddrRetain(pRh, DstAddress); 574 else 575 pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); 576 577 if (pDev) 578 pUrbPool = &pDev->UrbPool; 579 580 PVUSBURB pUrb = vusbUrbPoolAlloc(pUrbPool, enmType, enmDir, cbData, 581 pRh->cbHci, pRh->cbHciTd, cTds); 582 if (RT_LIKELY(pUrb)) 583 { 584 pUrb->pVUsb->pvFreeCtx = pRh; 585 pUrb->pVUsb->pfnFree = vusbRhFreeUrb; 586 pUrb->DstAddress = DstAddress; 587 pUrb->pVUsb->pDev = pDev; 588 589 #ifdef LOG_ENABLED 590 const char *pszType = NULL; 591 592 switch(pUrb->enmType) 593 { 594 case VUSBXFERTYPE_CTRL: 595 pszType = "ctrl"; 596 break; 597 case VUSBXFERTYPE_INTR: 598 pszType = "intr"; 599 break; 600 case VUSBXFERTYPE_BULK: 601 pszType = "bulk"; 602 break; 603 case VUSBXFERTYPE_ISOC: 604 pszType = "isoc"; 605 break; 606 default: 607 pszType = "invld"; 608 break; 609 } 610 611 pRh->iSerial = (pRh->iSerial + 1) % 10000; 612 RTStrAPrintf(&pUrb->pszDesc, "URB %p %s%c%04d (%s)", pUrb, pszType, 613 (pUrb->enmDir == VUSBDIRECTION_IN) ? '<' : ((pUrb->enmDir == VUSBDIRECTION_SETUP) ? 's' : '>'), 614 pRh->iSerial, pszTag ? pszTag : "<none>"); 615 #endif 616 } 617 618 return pUrb; 619 } 620 621 622 /** 623 * Calculate frame timer variables given a frame rate. 624 */ 625 static void vusbRhR3CalcTimerIntervals(PVUSBROOTHUB pThis, uint32_t u32FrameRate) 626 { 627 pThis->nsWait = RT_NS_1SEC / u32FrameRate; 628 pThis->uFrameRate = u32FrameRate; 629 /* Inform the HCD about the new frame rate. */ 630 pThis->pIRhPort->pfnFrameRateChanged(pThis->pIRhPort, u32FrameRate); 631 } 632 633 634 /** 635 * Calculates the new frame rate based on the idle detection and number of idle 636 * cycles. 637 * 638 * @returns nothing. 639 * @param pThis The roothub instance data. 640 * @param fIdle Flag whether the last frame didn't produce any activity. 641 */ 642 static void vusbRhR3FrameRateCalcNew(PVUSBROOTHUB pThis, bool fIdle) 643 { 644 uint32_t uNewFrameRate = pThis->uFrameRate; 645 646 /* 647 * Adjust the frame timer interval based on idle detection. 648 */ 649 if (fIdle) 650 { 651 pThis->cIdleCycles++; 652 /* Set the new frame rate based on how long we've been idle. Tunable. */ 653 switch (pThis->cIdleCycles) 654 { 655 case 4: uNewFrameRate = 500; break; /* 2ms interval */ 656 case 16:uNewFrameRate = 125; break; /* 8ms interval */ 657 case 24:uNewFrameRate = 50; break; /* 20ms interval */ 658 default: break; 659 } 660 /* Avoid overflow. */ 661 if (pThis->cIdleCycles > 60000) 662 pThis->cIdleCycles = 20000; 663 } 664 else 665 { 666 if (pThis->cIdleCycles) 667 { 668 pThis->cIdleCycles = 0; 669 uNewFrameRate = pThis->uFrameRateDefault; 670 } 671 } 672 673 if ( uNewFrameRate != pThis->uFrameRate 674 && uNewFrameRate) 675 { 676 LogFlow(("Frame rate changed from %u to %u\n", pThis->uFrameRate, uNewFrameRate)); 677 vusbRhR3CalcTimerIntervals(pThis, uNewFrameRate); 678 } 679 } 680 681 682 /** 683 * The core frame processing routine keeping track of the elapsed time and calling into 684 * the device emulation above us to do the work. 685 * 686 * @returns Relative timespan when to process the next frame. 687 * @param pThis The roothub instance data. 688 * @param fCallback Flag whether this method is called from the URB completion callback or 689 * from the worker thread (only used for statistics). 690 */ 691 DECLHIDDEN(uint64_t) vusbRhR3ProcessFrame(PVUSBROOTHUB pThis, bool fCallback) 692 { 693 uint64_t tsNext = 0; 694 uint64_t tsNanoStart = RTTimeNanoTS(); 695 696 /* Don't do anything if we are not supposed to process anything (EHCI and XHCI). */ 697 if (!pThis->uFrameRateDefault) 698 return 0; 699 700 if (ASMAtomicXchgBool(&pThis->fFrameProcessing, true)) 701 return pThis->nsWait; 702 703 if ( tsNanoStart > pThis->tsFrameProcessed 704 && tsNanoStart - pThis->tsFrameProcessed >= 750 * RT_NS_1US) 705 { 706 LogFlowFunc(("Starting new frame at ts %llu\n", tsNanoStart)); 707 708 bool fIdle = pThis->pIRhPort->pfnStartFrame(pThis->pIRhPort, 0 /* u32FrameNo */); 709 vusbRhR3FrameRateCalcNew(pThis, fIdle); 710 711 uint64_t tsNow = RTTimeNanoTS(); 712 tsNext = (tsNanoStart + pThis->nsWait) > tsNow ? (tsNanoStart + pThis->nsWait) - tsNow : 0; 713 pThis->tsFrameProcessed = tsNanoStart; 714 LogFlowFunc(("Current frame took %llu nano seconds to process, next frame in %llu ns\n", tsNow - tsNanoStart, tsNext)); 715 if (fCallback) 716 STAM_COUNTER_INC(&pThis->StatFramesProcessedClbk); 717 else 718 STAM_COUNTER_INC(&pThis->StatFramesProcessedThread); 719 } 720 else 721 { 722 tsNext = (pThis->tsFrameProcessed + pThis->nsWait) > tsNanoStart ? (pThis->tsFrameProcessed + pThis->nsWait) - tsNanoStart : 0; 723 LogFlowFunc(("Next frame is too far away in the future, waiting... (tsNanoStart=%llu tsFrameProcessed=%llu)\n", 724 tsNanoStart, pThis->tsFrameProcessed)); 725 } 726 727 ASMAtomicXchgBool(&pThis->fFrameProcessing, false); 728 LogFlowFunc(("returns %llu\n", tsNext)); 729 return tsNext; 730 } 731 732 733 /** 734 * Worker for processing frames periodically. 735 * 736 * @returns VBox status code. 737 * @param pDrvIns The driver instance. 738 * @param pThread The PDM thread structure for the thread this worker runs on. 739 */ 740 static DECLCALLBACK(int) vusbRhR3PeriodFrameWorker(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) 741 { 742 RT_NOREF(pDrvIns); 743 int rc = VINF_SUCCESS; 744 PVUSBROOTHUB pThis = (PVUSBROOTHUB)pThread->pvUser; 745 746 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) 747 return VINF_SUCCESS; 748 749 while (pThread->enmState == PDMTHREADSTATE_RUNNING) 750 { 751 while ( !ASMAtomicReadU32(&pThis->uFrameRateDefault) 752 && pThread->enmState == PDMTHREADSTATE_RUNNING) 753 { 754 /* Signal the waiter that we are stopped now. */ 755 rc = RTSemEventMultiSignal(pThis->hSemEventPeriodFrameStopped); 756 AssertRC(rc); 757 758 rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrame, RT_INDEFINITE_WAIT); 759 RTSemEventMultiReset(pThis->hSemEventPeriodFrame); 760 761 /* 762 * Notify the device above about the frame rate changed if we are supposed to 763 * process frames. 764 */ 765 uint32_t uFrameRate = ASMAtomicReadU32(&pThis->uFrameRateDefault); 766 if (uFrameRate) 767 vusbRhR3CalcTimerIntervals(pThis, uFrameRate); 768 } 769 770 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc), rc); 771 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING)) 772 break; 773 774 uint64_t tsNext = vusbRhR3ProcessFrame(pThis, false /* fCallback */); 775 776 if (tsNext >= 250 * RT_NS_1US) 777 { 778 rc = RTSemEventMultiWaitEx(pThis->hSemEventPeriodFrame, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE, 779 tsNext); 780 AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_TIMEOUT, ("%Rrc\n", rc)); 781 RTSemEventMultiReset(pThis->hSemEventPeriodFrame); 782 } 783 } 784 785 return VINF_SUCCESS; 786 } 787 788 789 /** 790 * Unblock the periodic frame thread so it can respond to a state change. 791 * 792 * @returns VBox status code. 793 * @param pDrvIns The driver instance. 794 * @param pThread The send thread. 795 */ 796 static DECLCALLBACK(int) vusbRhR3PeriodFrameWorkerWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) 797 { 798 RT_NOREF(pThread); 799 PVUSBROOTHUB pThis = PDMINS_2_DATA(pDrvIns, PVUSBROOTHUB); 800 return RTSemEventMultiSignal(pThis->hSemEventPeriodFrame); 801 } 802 803 804 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetUrbParams} */ 805 static DECLCALLBACK(int) vusbRhSetUrbParams(PVUSBIROOTHUBCONNECTOR pInterface, size_t cbHci, size_t cbHciTd) 806 { 807 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 808 809 pRh->cbHci = cbHci; 810 pRh->cbHciTd = cbHciTd; 811 812 return VINF_SUCCESS; 813 } 814 815 816 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReset} */ 817 static DECLCALLBACK(int) vusbR3RhReset(PVUSBIROOTHUBCONNECTOR pInterface, bool fResetOnLinux) 818 { 819 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 820 return pRh->pIRhPort->pfnReset(pRh->pIRhPort, fResetOnLinux); 821 } 822 823 824 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOn} */ 825 static DECLCALLBACK(int) vusbR3RhPowerOn(PVUSBIROOTHUBCONNECTOR pInterface) 826 { 827 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 828 LogFlow(("vusR3bRhPowerOn: pRh=%p\n", pRh)); 829 830 Assert( pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED 831 && pRh->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET); 832 833 if (pRh->Hub.Dev.enmState == VUSB_DEVICE_STATE_ATTACHED) 834 pRh->Hub.Dev.enmState = VUSB_DEVICE_STATE_POWERED; 835 836 return VINF_SUCCESS; 837 } 838 839 840 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnPowerOff} */ 841 static DECLCALLBACK(int) vusbR3RhPowerOff(PVUSBIROOTHUBCONNECTOR pInterface) 842 { 843 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 844 LogFlow(("vusbR3RhDevPowerOff: pThis=%p\n", pThis)); 845 846 Assert( pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_DETACHED 847 && pThis->Hub.Dev.enmState != VUSB_DEVICE_STATE_RESET); 848 849 /* 850 * Cancel all URBs and reap them. 851 */ 852 VUSBIRhCancelAllUrbs(&pThis->IRhConnector); 853 for (uint32_t uPort = 0; uPort < RT_ELEMENTS(pThis->apDevByPort); uPort++) 854 VUSBIRhReapAsyncUrbs(&pThis->IRhConnector, uPort, 0); 855 856 pThis->Hub.Dev.enmState = VUSB_DEVICE_STATE_ATTACHED; 857 return VINF_SUCCESS; 858 } 859 860 861 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnNewUrb} */ 862 static DECLCALLBACK(PVUSBURB) vusbRhConnNewUrb(PVUSBIROOTHUBCONNECTOR pInterface, uint8_t DstAddress, uint32_t uPort, VUSBXFERTYPE enmType, 863 VUSBDIRECTION enmDir, uint32_t cbData, uint32_t cTds, const char *pszTag) 864 { 865 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 866 return vusbRhNewUrb(pRh, DstAddress, uPort, enmType, enmDir, cbData, cTds, pszTag); 867 } 868 869 870 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnFreeUrb} */ 871 static DECLCALLBACK(int) vusbRhConnFreeUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb) 872 { 873 RT_NOREF(pInterface); 874 pUrb->pVUsb->pfnFree(pUrb); 875 return VINF_SUCCESS; 876 } 877 878 879 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSubmitUrb} */ 880 static DECLCALLBACK(int) vusbRhSubmitUrb(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb, PPDMLED pLed) 881 { 882 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 883 STAM_PROFILE_START(&pRh->StatSubmitUrb, a); 884 885 #ifdef VBOX_WITH_STATISTICS 886 /* 887 * Total and per-type submit statistics. 888 */ 889 Assert(pUrb->enmType >= 0 && pUrb->enmType < (int)RT_ELEMENTS(pRh->aTypes)); 890 STAM_COUNTER_INC(&pRh->Total.StatUrbsSubmitted); 891 STAM_COUNTER_INC(&pRh->aTypes[pUrb->enmType].StatUrbsSubmitted); 892 893 STAM_COUNTER_ADD(&pRh->Total.StatReqBytes, pUrb->cbData); 894 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqBytes, pUrb->cbData); 895 if (pUrb->enmDir == VUSBDIRECTION_IN) 896 { 897 STAM_COUNTER_ADD(&pRh->Total.StatReqReadBytes, pUrb->cbData); 898 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqReadBytes, pUrb->cbData); 899 } 900 else 901 { 902 STAM_COUNTER_ADD(&pRh->Total.StatReqWriteBytes, pUrb->cbData); 903 STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatReqWriteBytes, pUrb->cbData); 904 } 905 906 if (pUrb->enmType == VUSBXFERTYPE_ISOC) 907 { 908 STAM_COUNTER_ADD(&pRh->StatIsocReqPkts, pUrb->cIsocPkts); 909 if (pUrb->enmDir == VUSBDIRECTION_IN) 910 STAM_COUNTER_ADD(&pRh->StatIsocReqReadPkts, pUrb->cIsocPkts); 911 else 912 STAM_COUNTER_ADD(&pRh->StatIsocReqWritePkts, pUrb->cIsocPkts); 913 } 914 #endif 915 916 /* If there is a sniffer on the roothub record the URB there. */ 917 if (pRh->hSniffer != VUSBSNIFFER_NIL) 918 { 919 int rc = VUSBSnifferRecordEvent(pRh->hSniffer, pUrb, VUSBSNIFFEREVENT_SUBMIT); 920 if (RT_FAILURE(rc)) 921 LogRel(("VUSB: Capturing URB submit event on the root hub failed with %Rrc\n", rc)); 922 } 923 924 /* 925 * The device was resolved when we allocated the URB. 926 * Submit it to the device if we found it, if not fail with device-not-ready. 927 */ 928 int rc; 929 if ( pUrb->pVUsb->pDev 930 && pUrb->pVUsb->pDev->pUsbIns) 931 { 932 switch (pUrb->enmDir) 933 { 934 case VUSBDIRECTION_IN: 935 pLed->Asserted.s.fReading = pLed->Actual.s.fReading = 1; 936 rc = vusbUrbSubmit(pUrb); 937 pLed->Actual.s.fReading = 0; 938 break; 939 case VUSBDIRECTION_OUT: 940 pLed->Asserted.s.fWriting = pLed->Actual.s.fWriting = 1; 941 rc = vusbUrbSubmit(pUrb); 942 pLed->Actual.s.fWriting = 0; 943 break; 944 default: 945 rc = vusbUrbSubmit(pUrb); 946 break; 947 } 948 949 if (RT_FAILURE(rc)) 950 { 951 LogFlow(("vusbRhSubmitUrb: freeing pUrb=%p\n", pUrb)); 952 pUrb->pVUsb->pfnFree(pUrb); 953 } 954 } 955 else 956 { 957 vusbDevRetain(&pRh->Hub.Dev); 958 pUrb->pVUsb->pDev = &pRh->Hub.Dev; 959 Log(("vusb: pRh=%p: SUBMIT: Address %i not found!!!\n", pRh, pUrb->DstAddress)); 960 961 pUrb->enmState = VUSBURBSTATE_REAPED; 962 pUrb->enmStatus = VUSBSTATUS_DNR; 963 vusbUrbCompletionRh(pUrb); 964 rc = VINF_SUCCESS; 965 } 966 967 STAM_PROFILE_STOP(&pRh->StatSubmitUrb, a); 968 return rc; 969 } 970 971 972 static DECLCALLBACK(int) vusbRhReapAsyncUrbsWorker(PVUSBDEV pDev, RTMSINTERVAL cMillies) 973 { 974 if (!cMillies) 975 vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, 0); 976 else 977 { 978 uint64_t u64Start = RTTimeMilliTS(); 979 do 980 { 981 vusbUrbDoReapAsync(&pDev->LstAsyncUrbs, RT_MIN(cMillies >> 8, 10)); 982 } while ( !RTListIsEmpty(&pDev->LstAsyncUrbs) 983 && RTTimeMilliTS() - u64Start < cMillies); 984 } 985 986 return VINF_SUCCESS; 987 } 988 989 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnReapAsyncUrbs} */ 990 static DECLCALLBACK(void) vusbRhReapAsyncUrbs(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, RTMSINTERVAL cMillies) 991 { 992 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); NOREF(pRh); 993 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); 994 995 if ( !pDev 996 || RTListIsEmpty(&pDev->LstAsyncUrbs)) 997 return; 998 999 STAM_PROFILE_START(&pRh->StatReapAsyncUrbs, a); 1000 int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhReapAsyncUrbsWorker, 2, pDev, cMillies); 1001 AssertRC(rc); 1002 STAM_PROFILE_STOP(&pRh->StatReapAsyncUrbs, a); 1003 1004 vusbDevRelease(pDev); 1005 } 1006 1007 1008 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelUrbsEp} */ 1009 static DECLCALLBACK(int) vusbRhCancelUrbsEp(PVUSBIROOTHUBCONNECTOR pInterface, PVUSBURB pUrb) 1010 { 1011 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1012 AssertReturn(pRh, VERR_INVALID_PARAMETER); 1013 AssertReturn(pUrb, VERR_INVALID_PARAMETER); 1014 1015 /// @todo This method of URB canceling may not work on non-Linux hosts. 1016 /* 1017 * Cancel and reap the URB(s) on an endpoint. 1018 */ 1019 LogFlow(("vusbRhCancelUrbsEp: pRh=%p pUrb=%p\n", pRh, pUrb)); 1020 1021 vusbUrbCancelAsync(pUrb, CANCELMODE_UNDO); 1022 1023 /* The reaper thread will take care of completing the URB. */ 1024 1025 return VINF_SUCCESS; 1026 } 1027 1028 /** 1029 * Worker doing the actual cancelling of all outstanding URBs on the device I/O thread. 1030 * 1031 * @returns VBox status code. 1032 * @param pDev USB device instance data. 1033 */ 1034 static DECLCALLBACK(int) vusbRhCancelAllUrbsWorker(PVUSBDEV pDev) 1035 { 1036 /* 1037 * Cancel the URBS. 1038 * 1039 * Not using th CritAsyncUrbs critical section here is safe 1040 * as the I/O thread is the only thread accessing this struture at the 1041 * moment. 1042 */ 1043 PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext; 1044 RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst) 1045 { 1046 PVUSBURB pUrb = pVUsbUrb->pUrb; 1047 /* Call the worker directly. */ 1048 vusbUrbCancelWorker(pUrb, CANCELMODE_FAIL); 1049 } 1050 1051 return VINF_SUCCESS; 1052 } 1053 1054 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnCancelAllUrbs} */ 1055 static DECLCALLBACK(void) vusbRhCancelAllUrbs(PVUSBIROOTHUBCONNECTOR pInterface) 1056 { 1057 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1058 1059 RTCritSectEnter(&pThis->CritSectDevices); 1060 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apDevByPort); i++) 1061 { 1062 PVUSBDEV pDev = pThis->apDevByPort[i]; 1063 if (pDev) 1064 vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhCancelAllUrbsWorker, 1, pDev); 1065 } 1066 RTCritSectLeave(&pThis->CritSectDevices); 1067 } 1068 1069 /** 1070 * Worker doing the actual cancelling of all outstanding per-EP URBs on the 1071 * device I/O thread. 1072 * 1073 * @returns VBox status code. 1074 * @param pDev USB device instance data. 1075 * @param EndPt Endpoint number. 1076 * @param enmDir Endpoint direction. 1077 */ 1078 static DECLCALLBACK(int) vusbRhAbortEpWorker(PVUSBDEV pDev, int EndPt, VUSBDIRECTION enmDir) 1079 { 1080 /* 1081 * Iterate the URBs, find ones corresponding to given EP, and cancel them. 1082 */ 1083 PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext; 1084 RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst) 1085 { 1086 PVUSBURB pUrb = pVUsbUrb->pUrb; 1087 1088 Assert(pUrb->pVUsb->pDev == pDev); 1089 1090 /* For the default control EP, direction does not matter. */ 1091 if (pUrb->EndPt == EndPt && (pUrb->enmDir == enmDir || !EndPt)) 1092 { 1093 LogFlow(("%s: vusbRhAbortEpWorker: CANCELING URB\n", pUrb->pszDesc)); 1094 int rc = vusbUrbCancelWorker(pUrb, CANCELMODE_UNDO); 1095 AssertRC(rc); 1096 } 1097 } 1098 1099 return VINF_SUCCESS; 1100 } 1101 1102 1103 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnAbortEp} */ 1104 static DECLCALLBACK(int) vusbRhAbortEp(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, int EndPt, VUSBDIRECTION enmDir) 1105 { 1106 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1107 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); 1108 1109 if (&pRh->Hub != pDev->pHub) 1110 AssertFailedReturn(VERR_INVALID_PARAMETER); 1111 1112 vusbDevIoThreadExecSync(pDev, (PFNRT)vusbRhAbortEpWorker, 3, pDev, EndPt, enmDir); 1113 vusbDevRelease(pDev); 1114 1115 /* The reaper thread will take care of completing the URB. */ 1116 1117 return VINF_SUCCESS; 1118 } 1119 1120 1121 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnSetPeriodicFrameProcessing} */ 1122 static DECLCALLBACK(int) vusbRhSetFrameProcessing(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uFrameRate) 1123 { 1124 int rc = VINF_SUCCESS; 1125 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1126 1127 /* Create the frame thread lazily. */ 1128 if ( !pThis->hThreadPeriodFrame 1129 && uFrameRate) 1130 { 1131 ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate); 1132 pThis->uFrameRate = uFrameRate; 1133 vusbRhR3CalcTimerIntervals(pThis, uFrameRate); 1134 1135 rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrame); 1136 AssertRCReturn(rc, rc); 1137 1138 rc = RTSemEventMultiCreate(&pThis->hSemEventPeriodFrameStopped); 1139 AssertRCReturn(rc, rc); 1140 1141 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->hThreadPeriodFrame, pThis, vusbRhR3PeriodFrameWorker, 1142 vusbRhR3PeriodFrameWorkerWakeup, 0, RTTHREADTYPE_IO, "VUsbPeriodFrm"); 1143 AssertRCReturn(rc, rc); 1144 1145 VMSTATE enmState = PDMDrvHlpVMState(pThis->pDrvIns); 1146 if ( enmState == VMSTATE_RUNNING 1147 || enmState == VMSTATE_RUNNING_LS) 1148 { 1149 rc = PDMDrvHlpThreadResume(pThis->pDrvIns, pThis->hThreadPeriodFrame); 1150 AssertRCReturn(rc, rc); 1151 } 1152 } 1153 else if ( pThis->hThreadPeriodFrame 1154 && !uFrameRate) 1155 { 1156 /* Stop processing. */ 1157 uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate); 1158 if (uFrameRateOld) 1159 { 1160 rc = RTSemEventMultiReset(pThis->hSemEventPeriodFrameStopped); 1161 AssertRC(rc); 1162 1163 /* Signal the frame thread to stop. */ 1164 RTSemEventMultiSignal(pThis->hSemEventPeriodFrame); 1165 1166 /* Wait for signal from the thread that it stopped. */ 1167 rc = RTSemEventMultiWait(pThis->hSemEventPeriodFrameStopped, RT_INDEFINITE_WAIT); 1168 AssertRC(rc); 1169 } 1170 } 1171 else if ( pThis->hThreadPeriodFrame 1172 && uFrameRate) 1173 { 1174 /* Just switch to the new frame rate and let the periodic frame thread pick it up. */ 1175 uint32_t uFrameRateOld = ASMAtomicXchgU32(&pThis->uFrameRateDefault, uFrameRate); 1176 1177 /* Signal the frame thread to continue if it was stopped. */ 1178 if (!uFrameRateOld) 1179 RTSemEventMultiSignal(pThis->hSemEventPeriodFrame); 1180 } 1181 1182 return rc; 1183 } 1184 1185 1186 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnGetPeriodicFrameRate} */ 1187 static DECLCALLBACK(uint32_t) vusbRhGetPeriodicFrameRate(PVUSBIROOTHUBCONNECTOR pInterface) 1188 { 1189 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1190 1191 return pThis->uFrameRate; 1192 } 1193 1194 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnUpdateIsocFrameDelta} */ 1195 static DECLCALLBACK(uint32_t) vusbRhUpdateIsocFrameDelta(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, 1196 int EndPt, VUSBDIRECTION enmDir, uint16_t uNewFrameID, uint8_t uBits) 1197 { 1198 PVUSBROOTHUB pRh = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1199 AssertReturn(pRh, 0); 1200 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pRh, uPort); AssertPtr(pDev); 1201 PVUSBPIPE pPipe = &pDev->aPipes[EndPt]; 1202 uint32_t *puLastFrame; 1203 int32_t uFrameDelta; 1204 uint32_t uMaxVal = 1 << uBits; 1205 1206 puLastFrame = enmDir == VUSBDIRECTION_IN ? &pPipe->uLastFrameIn : &pPipe->uLastFrameOut; 1207 uFrameDelta = uNewFrameID - *puLastFrame; 1208 *puLastFrame = uNewFrameID; 1209 /* Take care of wrap-around. */ 1210 if (uFrameDelta < 0) 1211 uFrameDelta += uMaxVal; 1212 1213 vusbDevRelease(pDev); 1214 return (uint16_t)uFrameDelta; 1215 } 1216 1217 1218 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevReset} */ 1219 static DECLCALLBACK(int) vusbR3RhDevReset(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort, bool fResetOnLinux, 1220 PFNVUSBRESETDONE pfnDone, void *pvUser, PVM pVM) 1221 { 1222 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1223 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1224 AssertPtr(pDev); 1225 1226 int rc = VUSBIDevReset(&pDev->IDevice, fResetOnLinux, pfnDone, pvUser, pVM); 1227 vusbDevRelease(pDev); 1228 return rc; 1229 } 1230 1231 1232 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOn} */ 1233 static DECLCALLBACK(int) vusbR3RhDevPowerOn(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort) 1234 { 1235 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1236 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1237 AssertPtr(pDev); 1238 1239 int rc = VUSBIDevPowerOn(&pDev->IDevice); 1240 vusbDevRelease(pDev); 1241 return rc; 1242 } 1243 1244 1245 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevPowerOff} */ 1246 static DECLCALLBACK(int) vusbR3RhDevPowerOff(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort) 1247 { 1248 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1249 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1250 AssertPtr(pDev); 1251 1252 int rc = VUSBIDevPowerOff(&pDev->IDevice); 1253 vusbDevRelease(pDev); 1254 return rc; 1255 } 1256 1257 1258 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetState} */ 1259 static DECLCALLBACK(VUSBDEVICESTATE) vusbR3RhDevGetState(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort) 1260 { 1261 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1262 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1263 AssertPtr(pDev); 1264 1265 VUSBDEVICESTATE enmState = VUSBIDevGetState(&pDev->IDevice); 1266 vusbDevRelease(pDev); 1267 return enmState; 1268 } 1269 1270 1271 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevIsSavedStateSupported} */ 1272 static DECLCALLBACK(bool) vusbR3RhDevIsSavedStateSupported(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort) 1273 { 1274 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1275 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1276 AssertPtr(pDev); 1277 1278 bool fSavedStateSupported = VUSBIDevIsSavedStateSupported(&pDev->IDevice); 1279 vusbDevRelease(pDev); 1280 return fSavedStateSupported; 1281 } 1282 1283 1284 /** @interface_method_impl{VUSBIROOTHUBCONNECTOR,pfnDevGetSpeed} */ 1285 static DECLCALLBACK(VUSBSPEED) vusbR3RhDevGetSpeed(PVUSBIROOTHUBCONNECTOR pInterface, uint32_t uPort) 1286 { 1287 PVUSBROOTHUB pThis = VUSBIROOTHUBCONNECTOR_2_VUSBROOTHUB(pInterface); 1288 PVUSBDEV pDev = vusbR3RhGetVUsbDevByPortRetain(pThis, uPort); 1289 AssertPtr(pDev); 1290 1291 VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice); 1292 vusbDevRelease(pDev); 1293 return enmSpeed; 1294 } 1295 1296 1297 /** 1192 1298 * @callback_method_impl{FNSSMDRVSAVEPREP, All URBs needs to be canceled.} 1193 1299 */ … … 1270 1376 PVUSBDEV pDev = aPortsOld[i]; 1271 1377 if (pDev && !VUSBIDevIsSavedStateSupported(&pDev->IDevice)) 1272 vusbHubAttach( &pThis->Hub, pDev);1378 vusbHubAttach(pThis, pDev); 1273 1379 } 1274 1380 … … 1343 1449 */ 1344 1450 for (unsigned i = 0; i < pLoad->cDevs; i++) 1345 vusbHubAttach( &pThis->Hub, pLoad->apDevs[i]);1451 vusbHubAttach(pThis, pLoad->apDevs[i]); 1346 1452 1347 1453 /* … … 1381 1487 1382 1488 1383 /* -=-=-=-=-=- VUSB Hub methods -=-=-=-=-=- */1384 1385 1386 /**1387 * Attach the device to the hub.1388 * Port assignments and all such stuff is up to this routine.1389 *1390 * @returns VBox status code.1391 * @param pHub Pointer to the hub.1392 * @param pDev Pointer to the device.1393 */1394 static int vusbRhHubOpAttach(PVUSBHUB pHub, PVUSBDEV pDev)1395 {1396 PVUSBROOTHUB pRh = (PVUSBROOTHUB)pHub;1397 1398 /*1399 * Assign a port.1400 */1401 int iPort = ASMBitFirstSet(&pRh->Bitmap, sizeof(pRh->Bitmap) * 8);1402 if (iPort < 0)1403 {1404 LogRel(("VUSB: No ports available!\n"));1405 return VERR_VUSB_NO_PORTS;1406 }1407 ASMBitClear(&pRh->Bitmap, iPort);1408 pHub->cDevices++;1409 pDev->i16Port = iPort;1410 1411 /*1412 * Call the HCI attach routine and let it have its say before the device is1413 * linked into the device list of this hub.1414 */1415 VUSBSPEED enmSpeed = pDev->IDevice.pfnGetSpeed(&pDev->IDevice);1416 int rc = pRh->pIRhPort->pfnAttach(pRh->pIRhPort, iPort, enmSpeed);1417 if (RT_SUCCESS(rc))1418 {1419 RTCritSectEnter(&pRh->CritSectDevices);1420 Assert(!pRh->apDevByPort[iPort]);1421 pRh->apDevByPort[iPort] = pDev;1422 1423 RTCritSectLeave(&pRh->CritSectDevices);1424 LogRel(("VUSB: Attached '%s' to port %d on %s (%sSpeed)\n", pDev->pUsbIns->pszName,1425 iPort, pHub->pszName, vusbGetSpeedString(pDev->pUsbIns->enmSpeed)));1426 }1427 else1428 {1429 ASMBitSet(&pRh->Bitmap, iPort);1430 pHub->cDevices--;1431 pDev->i16Port = -1;1432 LogRel(("VUSB: Failed to attach '%s' to port %d, rc=%Rrc\n", pDev->pUsbIns->pszName, iPort, rc));1433 }1434 return rc;1435 }1436 1437 1438 /**1439 * Detach the device from the hub.1440 *1441 * @returns VBox status code.1442 * @param pHub Pointer to the hub.1443 * @param pDev Pointer to the device.1444 */1445 static void vusbRhHubOpDetach(PVUSBHUB pHub, PVUSBDEV pDev)1446 {1447 PVUSBROOTHUB pRh = (PVUSBROOTHUB)pHub;1448 Assert(pDev->i16Port != -1);1449 1450 /* Check that it's attached and remvoe it. */1451 RTCritSectEnter(&pRh->CritSectDevices);1452 Assert(pRh->apDevByPort[pDev->i16Port] == pDev);1453 pRh->apDevByPort[pDev->i16Port] = NULL;1454 1455 if (pDev->u8Address == VUSB_DEFAULT_ADDRESS)1456 {1457 AssertPtr(pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS]);1458 1459 if (pDev == pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS])1460 pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS] = pDev->pNextDefAddr;1461 else1462 {1463 /* Search the list for the device and remove it. */1464 PVUSBDEV pDevPrev = pRh->apDevByAddr[VUSB_DEFAULT_ADDRESS];1465 1466 while ( pDevPrev1467 && pDevPrev->pNextDefAddr != pDev)1468 pDevPrev = pDevPrev->pNextDefAddr;1469 1470 AssertPtr(pDevPrev);1471 pDevPrev->pNextDefAddr = pDev->pNextDefAddr;1472 }1473 1474 pDev->pNextDefAddr = NULL;1475 }1476 else1477 {1478 Assert(pRh->apDevByAddr[pDev->u8Address] == pDev);1479 pRh->apDevByAddr[pDev->u8Address] = NULL;1480 }1481 RTCritSectLeave(&pRh->CritSectDevices);1482 1483 /*1484 * Detach the device and mark the port as available.1485 */1486 unsigned uPort = pDev->i16Port;1487 pRh->pIRhPort->pfnDetach(pRh->pIRhPort, uPort);1488 LogRel(("VUSB: Detached '%s' from port %u on %s\n", pDev->pUsbIns->pszName, uPort, pHub->pszName));1489 ASMBitSet(&pRh->Bitmap, uPort);1490 pHub->cDevices--;1491 }1492 1493 1494 /**1495 * The Hub methods implemented by the root hub.1496 */1497 static const VUSBHUBOPS s_VUsbRhHubOps =1498 {1499 vusbRhHubOpAttach,1500 vusbRhHubOpDetach1501 };1502 1503 1504 1505 1489 /* -=-=-=-=-=- PDM Base interface methods -=-=-=-=-=- */ 1506 1490 … … 1604 1588 pThis->Hub.Dev.cRefs = 1; 1605 1589 /* the hub */ 1606 pThis->Hub.pOps = &s_VUsbRhHubOps;1607 1590 pThis->Hub.pRootHub = pThis; 1608 1591 //pThis->hub.cPorts - later -
trunk/src/VBox/Devices/USB/VUSBDevice.cpp
r93940 r93955 1279 1279 /* Create I/O thread and attach to the hub. */ 1280 1280 int rc = vusbDevUrbIoThreadCreate(pDev); 1281 if (RT_SUCCESS(rc))1282 rc = pHub->pOps->pfnAttach(pHub, pDev);1283 1284 1281 if (RT_FAILURE(rc)) 1285 1282 { … … 1305 1302 VUSBDEV_ASSERT_VALID_STATE(pDev->enmState); 1306 1303 Assert(pDev->enmState != VUSB_DEVICE_STATE_RESET); 1307 1308 vusbDevCancelAllUrbs(pDev, true);1309 1310 PVUSBROOTHUB pRh = vusbDevGetRh(pDev);1311 if (!pRh)1312 AssertMsgFailedReturn(("Not attached!\n"), VERR_VUSB_DEVICE_NOT_ATTACHED);1313 1314 /* The roothub will remove the device from the address to device array. */1315 pDev->pHub->pOps->pfnDetach(pDev->pHub, pDev);1316 pDev->i16Port = -1;1317 1304 1318 1305 /* -
trunk/src/VBox/Devices/USB/VUSBInternal.h
r93939 r93955 320 320 321 321 322 /** Virtual method table for USB hub devices.323 * Hub and roothub drivers need to implement these functions in addition to the324 * vusb_dev_ops.325 */326 typedef struct VUSBHUBOPS327 {328 int (*pfnAttach)(PVUSBHUB pHub, PVUSBDEV pDev);329 void (*pfnDetach)(PVUSBHUB pHub, PVUSBDEV pDev);330 } VUSBHUBOPS;331 /** Pointer to a const HUB method table. */332 typedef const VUSBHUBOPS *PCVUSBHUBOPS;333 334 322 /** A VUSB Hub Device - Hub and roothub drivers need to use this struct 335 323 * @todo eliminate this (PDM / roothubs only). … … 338 326 { 339 327 VUSBDEV Dev; 340 PCVUSBHUBOPS pOps;341 328 PVUSBROOTHUB pRootHub; 342 329 uint16_t cPorts; … … 345 332 char *pszName; 346 333 } VUSBHUB; 347 AssertCompileMemberAlignment(VUSBHUB, pOps, 8);348 334 AssertCompileSizeAlignment(VUSBHUB, 8); 349 335
Note:
See TracChangeset
for help on using the changeset viewer.