Changeset 53411 in vbox
- Timestamp:
- Nov 28, 2014 1:53:56 PM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/USB/usbip/USBProxyDevice-usbip.cpp
r53260 r53411 32 32 #include <iprt/socket.h> 33 33 #include <iprt/poll.h> 34 #include <iprt/tcp.h> 35 #include <iprt/pipe.h> 34 36 #include <iprt/list.h> 35 37 #include <iprt/semaphore.h> … … 45 47 #define USBIP_VERSION UINT16_C(0x0100) 46 48 /** Request indicator in the command code. */ 47 #define USBIP_INDICATOR_REQ RT_BIT _16(15)49 #define USBIP_INDICATOR_REQ RT_BIT(15) 48 50 49 51 /** Command/Reply code for OP_REQ/RET_DEVLIST. */ … … 273 275 274 276 /** 277 * Union of possible replies from the server during normal operation. 278 */ 279 #pragma pack(1) 280 typedef union UsbIpRet 281 { 282 /** The header. */ 283 UsbIpReqRetHdr Hdr; 284 /** Submit reply. */ 285 UsbIpRetSubmit RetSubmit; 286 /** Unlink reply. */ 287 UsbIpRetUnlink RetUnlink; 288 /** Byte view. */ 289 uint8_t abReply[1]; 290 } UsbIpRet; 291 /** Pointer to a reply union. */ 292 typedef UsbIpRet *PUsbIpRet; 293 #pragma pack() 294 295 /** 275 296 * USB/IP backend specific data for one URB. 276 297 * Required for tracking in flight and landed URBs. … … 303 324 /** Flag whether the reaper thread was woken up. */ 304 325 volatile bool fWokenUp; 305 /** Flag whether the reaper thread is waiting in the select call. */306 volatile bool fWaiting;307 326 /** Next sequence number to use for identifying submitted URBs. */ 308 327 volatile uint32_t u32SeqNumNext; … … 382 401 DECLINLINE(void) usbProxyUsbIpRetSubmitN2H(PUsbIpRetSubmit pRetSubmit) 383 402 { 384 usbProxyUsbIpReqRetHdrN2H(&pRe qSubmit->Hdr);403 usbProxyUsbIpReqRetHdrN2H(&pRetSubmit->Hdr); 385 404 pRetSubmit->u32Status = RT_N2H_U32(pRetSubmit->u32Status); 386 405 pRetSubmit->u32ActualLength = RT_N2H_U32(pRetSubmit->u32ActualLength); … … 388 407 pRetSubmit->u32NumIsocPkts = RT_N2H_U32(pRetSubmit->u32NumIsocPkts); 389 408 pRetSubmit->u32ErrorCount = RT_N2H_U32(pRetSubmit->u32ErrorCount); 409 } 410 411 /** 412 * Converts a unlink request from host to network endianness. 413 * 414 * @returns nothing. 415 * @param pReqUnlink The unlink request to convert. 416 */ 417 DECLINLINE(void) usbProxyUsbIpReqUnlinkH2N(PUsbIpReqUnlink pReqUnlink) 418 { 419 usbProxyUsbIpReqRetHdrH2N(&pReqUnlink->Hdr); 420 pReqUnlink->u32SeqNum = RT_H2N_U32(pReqUnlink->u32SeqNum); 421 } 422 423 /** 424 * Converts a unlink reply from network to host endianness. 425 * 426 * @returns nothing. 427 * @param pRetUnlink The unlink reply to convert. 428 */ 429 DECLINLINE(void) usbProxyUsbIpRetUnlinkN2H(PUsbIpRetUnlink pRetUnlink) 430 { 431 usbProxyUsbIpReqRetHdrN2H(&pRetUnlink->Hdr); 432 pRetUnlink->u32Status = RT_N2H_U32(pRetUnlink->u32Status); 390 433 } 391 434 … … 432 475 433 476 /** 477 * Links a given URB into the given list. 478 * 479 * @returns nothing. 480 * @param pProxyDevUsbIp The USB/IP proxy device data. 481 * @param pList The list to link the URB into. 482 * @param pUrbUsbIp The URB to link. 483 */ 484 DECLINLINE(void) usbProxyUsbIpLinkUrb(PUSBPROXYDEVUSBIP pProxyDevUsbIp, PRTLISTANCHOR pList, PUSBPROXYURBUSBIP pUrbUsbIp) 485 { 486 int rc = RTSemFastMutexRequest(pProxyDevUsbIp->hMtxLists); 487 AssertRC(rc); 488 RTListAppend(pList, &pUrbUsbIp->NodeList); 489 RTSemFastMutexRelease(pProxyDevUsbIp->hMtxLists); 490 } 491 492 /** 493 * Unlinks a given URB from the current assigned list. 494 * 495 * @returns nothing. 496 * @param pProxyDevUsbIp The USB/IP proxy device data. 497 * @param pUrbUsbIp The URB to unlink. 498 */ 499 DECLINLINE(void) usbProxyUsbIpUnlinkUrb(PUSBPROXYDEVUSBIP pProxyDevUsbIp, PUSBPROXYURBUSBIP pUrbUsbIp) 500 { 501 int rc = RTSemFastMutexRequest(pProxyDevUsbIp->hMtxLists); 502 AssertRC(rc); 503 RTListNodeRemove(&pUrbUsbIp->NodeList); 504 RTSemFastMutexRelease(pProxyDevUsbIp->hMtxLists); 505 } 506 507 /** 508 * Allocates a USB/IP proxy specific URB state. 509 * 510 * @returns Pointer to the USB/IP specific URB data or NULL on failure. 511 * @param pProxyDevUsbIp The USB/IP proxy device data. 512 */ 513 static PUSBPROXYURBUSBIP usbProxyUsbIpUrbAlloc(PUSBPROXYDEVUSBIP pProxyDevUsbIp) 514 { 515 NOREF(pProxyDevUsbIp); 516 return (PUSBPROXYURBUSBIP)RTMemAllocZ(sizeof(USBPROXYURBUSBIP)); 517 } 518 519 /** 520 * Frees the given USB/IP URB state. 521 * 522 * @returns nothing. 523 * @param pProxyDevUsbIp The USB/IP proxy device data. 524 * @param pUrbUsbIp The USB/IP speciic URB data. 525 */ 526 static void usbProxyUsbIpUrbFree(PUSBPROXYDEVUSBIP pProxyDevUsbIp, PUSBPROXYURBUSBIP pUrbUsbIp) 527 { 528 NOREF(pProxyDevUsbIp); 529 RTMemFree(pUrbUsbIp); 530 } 531 532 /** 434 533 * Parse the string representation of the host address. 435 534 * … … 450 549 rc = RTStrToUInt32Ex(pszPortStart, NULL, 10 /* uBase */, &pProxyDevUsbIp->uPort); 451 550 if ( rc == VINF_SUCCESS 452 || 551 || cbHost == 0) 453 552 { 454 rc = RTStrAllocEx(&p DevUsbIp->pszHost, cbHost + 1);553 rc = RTStrAllocEx(&pProxyDevUsbIp->pszHost, cbHost + 1); 455 554 if (RT_SUCCESS(rc)) 456 555 { 457 rc = RTStrCopyEx(p DevUsbIp->pszHost, cbHost + 1, pszAddress, cbHost);556 rc = RTStrCopyEx(pProxyDevUsbIp->pszHost, cbHost + 1, pszAddress, cbHost); 458 557 AssertRC(rc); 459 558 return VINF_SUCCESS; … … 478 577 { 479 578 int rc = VINF_SUCCESS; 480 rc = RTTcpClientConnect(p DevUsbIp->pszHost, pDevUsbIp->uPort, &pDevUsbIp->hSocket);579 rc = RTTcpClientConnect(pProxyDevUsbIp->pszHost, pProxyDevUsbIp->uPort, &pProxyDevUsbIp->hSocket); 481 580 if (RT_SUCCESS(rc)) 482 581 { 483 582 /* Disable send coalescing. */ 484 rc = RTTcpSetSendCoalescing(p DevUsbIp->hSocket, false);583 rc = RTTcpSetSendCoalescing(pProxyDevUsbIp->hSocket, false); 485 584 if (RT_FAILURE(rc)) 486 585 LogRel(("UsbIp: Disabling send coalescing failed (rc=%Rrc), continuing nevertheless but expect reduced performance\n", rc)); … … 489 588 UsbIpReqImport ReqImport; 490 589 ReqImport.u16Version = RT_H2N_U16(USBIP_VERSION); 491 ReqImport.u16Cmd = RT_H2N_U16(USBIP_INDICATOR_REQ | 590 ReqImport.u16Cmd = RT_H2N_U16(USBIP_INDICATOR_REQ | USBIP_REQ_RET_IMPORT); 492 591 ReqImport.u32Status = RT_H2N_U32(0); 493 592 rc = RTStrCopy(&ReqImport.aszBusId[0], sizeof(ReqImport.aszBusId[0]), pProxyDevUsbIp->pszBusId); 494 if (rc = VINF_SUCCESS)593 if (rc == VINF_SUCCESS) 495 594 { 496 rc = RTTcpWrite(p DevUsbIp->hSocket, &ReqImport, sizeof(ReqImport));595 rc = RTTcpWrite(pProxyDevUsbIp->hSocket, &ReqImport, sizeof(ReqImport)); 497 596 if (RT_SUCCESS(rc)) 498 597 { 499 598 /* Read the reply. */ 500 599 UsbIpRetImport RetImport; 501 rc = RTTcpRead(p DevUsbIp->hSocket, &RetImport, sizeof(RetImport));600 rc = RTTcpRead(pProxyDevUsbIp->hSocket, &RetImport, sizeof(RetImport), NULL); 502 601 if (RT_SUCCESS(rc)) 503 602 { … … 511 610 /* Read the device data. */ 512 611 UsbIpExportedDevice Device; 513 rc = RTTcpRead(p DevUsbIp->hSocket, &Device, sizeof(Device));612 rc = RTTcpRead(pProxyDevUsbIp->hSocket, &Device, sizeof(Device), NULL); 514 613 if (RT_SUCCESS(rc)) 515 614 { 516 615 usbProxyUsbIpExportedDeviceN2H(&Device); 517 pProxyDevUsbIp->u32DevId = (Device.u32BusNum << 16) | Device.u32DevNum; 616 pProxyDevUsbIp->u32DevId = (Device.u32BusNum << 16) | Device.u32DevNum; 617 618 rc = RTPollSetAddSocket(pProxyDevUsbIp->hPollSet, pProxyDevUsbIp->hSocket, 619 RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, USBIP_POLL_ID_SOCKET); 518 620 } 519 621 } … … 543 645 544 646 if (RT_FAILURE(rc)) 545 RTTcpClientCloseEx(p DevUsbIp->hSocket, false /*fGracefulShutdown*/);647 RTTcpClientCloseEx(pProxyDevUsbIp->hSocket, false /*fGracefulShutdown*/); 546 648 } 547 649 if (RT_FAILURE(rc)) 548 LogRel(("UsbIp: Connecting to the host %s failed with %Rrc\n", p DevUsbIp->pszHost, rc));650 LogRel(("UsbIp: Connecting to the host %s failed with %Rrc\n", pProxyDevUsbIp->pszHost, rc)); 549 651 return rc; 550 652 } … … 560 662 int rc = VINF_SUCCESS; 561 663 562 rc = RTTcpClientCloseEx(p DevUsbIp->hSocket, false /*fGracefulShutdown*/);664 rc = RTTcpClientCloseEx(pProxyDevUsbIp->hSocket, false /*fGracefulShutdown*/); 563 665 if (RT_SUCCESS(rc)) 564 p DevUsbIp->hSocket = NIL_RTSOCKET;666 pProxyDevUsbIp->hSocket = NIL_RTSOCKET; 565 667 return rc; 566 668 } … … 581 683 { 582 684 int rc = VINF_SUCCESS; 583 AssertMsg(!pProxyDevUsbIp->fWaiting, ("usbProxyUsbIpUrbReap is called on another thread\n"));584 685 585 686 UsbIpReqSubmit ReqSubmit; … … 605 706 /** @todo: Don't wait indefinitely long. */ 606 707 UsbIpRetSubmit RetSubmit; 607 rc = RTTcpRead(pProxyDevUsbIp->hSocket, &RetSubmit, sizeof(RetSubmit) );708 rc = RTTcpRead(pProxyDevUsbIp->hSocket, &RetSubmit, sizeof(RetSubmit), NULL); 608 709 if (RT_SUCCESS(rc)) 609 710 { … … 633 734 pDevUsbIp->hPipeR = NIL_RTPIPE; 634 735 pDevUsbIp->fWokenUp = false; 635 pDevUsbIp->fWaiting = false;636 736 pDevUsbIp->u32SeqNumNext = 0; 637 737 pDevUsbIp->pszHost = NULL; … … 652 752 rc = usbProxyUsbIpParseAddress(pDevUsbIp, pszAddress); 653 753 if (RT_SUCCESS(rc)) 654 rc = usbProxyUsbIpConnect(p ProxyDevUsbIp);754 rc = usbProxyUsbIpConnect(pDevUsbIp); 655 755 } 656 756 … … 677 777 static DECLCALLBACK(void) usbProxyUsbIpClose(PUSBPROXYDEV pProxyDev) 678 778 { 779 int rc = VINF_SUCCESS; 679 780 LogFlowFunc(("pProxyDev = %p\n", pProxyDev)); 680 781 … … 706 807 707 808 /* Clear the URB lists. */ 708 intrc = RTSemFastMutexRequest(pDevUsbIp->hMtxLists);809 rc = RTSemFastMutexRequest(pDevUsbIp->hMtxLists); 709 810 AssertRC(rc); 710 811 PUSBPROXYURBUSBIP pIter = NULL; … … 730 831 731 832 PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); 732 return V ERR_NOT_IMPLEMENTED;833 return VINF_SUCCESS; /* No way to reset the device with the current protocol. */ 733 834 } 734 835 735 836 static DECLCALLBACK(int) usbProxyUsbIpSetConfig(PUSBPROXYDEV pProxyDev, int iCfg) 736 837 { 737 LogFlowFunc(("pProxyDev=%s cfg=%#x\n", pProxyDev->pUsbIns->pszName, cfg));838 LogFlowFunc(("pProxyDev=%s cfg=%#x\n", pProxyDev->pUsbIns->pszName, iCfg)); 738 839 739 840 PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); … … 777 878 static DECLCALLBACK(int) usbProxyUsbIpClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int iEp) 778 879 { 779 LogFlowFunc(("pProxyDev=%s ep=%u\n", pProxyDev->pUsbIns->pszName, ep));880 LogFlowFunc(("pProxyDev=%s ep=%u\n", pProxyDev->pUsbIns->pszName, iEp)); 780 881 781 882 PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); … … 794 895 LogFlowFunc(("pUrb=%p\n", pUrb)); 795 896 897 PUSBPROXYDEVUSBIP pProxyDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); 898 899 /* Allocate a USB/IP Urb. */ 900 PUSBPROXYURBUSBIP pUrbUsbIp = usbProxyUsbIpUrbAlloc(pProxyDevUsbIp); 901 if (!pUrbUsbIp) 902 return VERR_NO_MEMORY; 903 904 pUrbUsbIp->u32SeqNumUrb = usbProxyUsbIpSeqNumGet(pProxyDevUsbIp); 905 906 UsbIpReqSubmit ReqSubmit; 907 ReqSubmit.Hdr.u32ReqRet = USBIP_CMD_SUBMIT; 908 ReqSubmit.Hdr.u32SeqNum = pUrbUsbIp->u32SeqNumUrb; 909 ReqSubmit.Hdr.u32DevId = pProxyDevUsbIp->u32DevId; 910 ReqSubmit.Hdr.u32Endpoint = pUrb->EndPt; 911 ReqSubmit.Hdr.u32Direction = pUrb->enmDir == VUSBDIRECTION_IN ? USBIP_DIR_IN : USBIP_DIR_OUT; 912 ReqSubmit.u32XferFlags = 0; 913 if (pUrb->enmDir == VUSBDIRECTION_IN && pUrb->fShortNotOk) 914 ReqSubmit.u32XferFlags |= USBIP_XFER_FLAGS_SHORT_NOT_OK; 915 916 ReqSubmit.u32TransferBufferLength = pUrb->cbData; 917 ReqSubmit.u32StartFrame = 0; 918 ReqSubmit.u32NumIsocPkts = 0; 919 ReqSubmit.u32Interval = 0; 920 921 switch (pUrb->enmType) 922 { 923 case VUSBXFERTYPE_MSG: 924 memcpy(&ReqSubmit.Setup, &pUrb->abData, sizeof(ReqSubmit.Setup)); 925 LogFlowFunc(("Message (Control) URB\n")); 926 break; 927 case VUSBXFERTYPE_ISOC: 928 ReqSubmit.u32XferFlags |= USBIP_XFER_FLAGS_ISO_ASAP; 929 ReqSubmit.u32NumIsocPkts = pUrb->cIsocPkts; 930 #if 0 931 for (unsigned i = 0; i < pUrb->cIsocPkts; i++) 932 { 933 pUrbLnx->KUrb.iso_frame_desc[i].length = pUrb->aIsocPkts[i].cb; 934 pUrbLnx->KUrb.iso_frame_desc[i].actual_length = 0; 935 pUrbLnx->KUrb.iso_frame_desc[i].status = 0x7fff; 936 } 937 #else /** @todo: Implement isochronous support */ 938 usbProxyUsbIpUrbFree(pProxyDevUsbIp, pUrbUsbIp); 939 return VERR_NOT_SUPPORTED; 940 #endif 941 break; 942 case VUSBXFERTYPE_BULK: 943 case VUSBXFERTYPE_INTR: 944 break; 945 default: 946 usbProxyUsbIpUrbFree(pProxyDevUsbIp, pUrbUsbIp); 947 return VERR_INVALID_PARAMETER; /** @todo: better status code. */ 948 } 949 usbProxyUsbIpReqSubmitH2N(&ReqSubmit); 950 951 /* Send the command. */ 952 RTSGBUF SgBufReq; 953 RTSGSEG aSegReq[2]; 954 aSegReq[0].pvSeg = &ReqSubmit; 955 aSegReq[0].cbSeg = sizeof(ReqSubmit); 956 aSegReq[1].pvSeg = &pUrb->abData[0]; 957 aSegReq[1].cbSeg = pUrb->cbData; 958 RTSgBufInit(&SgBufReq, &aSegReq[0], RT_ELEMENTS(aSegReq)); 959 960 int rc = RTTcpSgWrite(pProxyDevUsbIp->hSocket, &SgBufReq); 961 if (RT_SUCCESS(rc)) 962 { 963 /* Link the URB into the list of in flight URBs. */ 964 pUrb->Dev.pvPrivate = pUrbUsbIp; 965 pUrbUsbIp->pVUsbUrb = pUrb; 966 usbProxyUsbIpLinkUrb(pProxyDevUsbIp, &pProxyDevUsbIp->ListUrbsInFlight, pUrbUsbIp); 967 } 968 else 969 usbProxyUsbIpUrbFree(pProxyDevUsbIp, pUrbUsbIp); 970 971 return rc; 972 } 973 974 static DECLCALLBACK(PVUSBURB) usbProxyUsbIpUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies) 975 { 976 LogFlowFunc(("pProxyDev=%s\n", pProxyDev->pUsbIns->pszName)); 977 796 978 PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); 797 return VERR_NOT_IMPLEMENTED; 798 } 799 800 static DECLCALLBACK(PVUSBURB) usbProxyUsbIpUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies) 801 { 802 LogFlowFunc(("pProxyDev=%s\n", pProxyDev->pUsbIns->pszName)); 803 804 PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); 805 return NULL; 979 PUSBPROXYURBUSBIP pUrbUsbIp = NULL; 980 PVUSBURB pUrb = NULL; 981 982 /* Any URBs pending delivery? */ 983 if (!RTListIsEmpty(&pDev->ListUrbsLanded)) 984 { 985 pUrbUsbIp = RTListGetFirst(&pDev->ListUrbsLanded, USBPROXYURBUSBIP, NodeList); 986 if (pUrbUsbIp) 987 { 988 /* unlink from the pending delivery list */ 989 usbProxyUsbIpUnlinkUrb(pDev, pUrbUsbIp); 990 } 991 } 992 993 if (!pUrbUsbIp) 994 { 995 uint32_t uIdReady = 0; 996 uint32_t fEventsRecv = 0; 997 998 if (!ASMAtomicXchgBool(&pDev->fWokenUp, false)) 999 { 1000 int rc = RTPoll(pDev->hPollSet, cMillies, &fEventsRecv, &uIdReady); 1001 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT); 1002 } 1003 1004 UsbIpRet Reply; 1005 } 1006 1007 if (pUrbUsbIp) 1008 { 1009 1010 } 1011 1012 return pUrb; 806 1013 } 807 1014 … … 810 1017 LogFlowFunc(("pUrb=%p\n", pUrb)); 811 1018 812 PUSBPROXYDEVUSBIP p Dev= USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP);1019 PUSBPROXYDEVUSBIP pProxyDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); 813 1020 PUSBPROXYURBUSBIP pUrbUsbIp = (PUSBPROXYURBUSBIP)pUrb->Dev.pvPrivate; 814 1021 UsbIpReqUnlink ReqUnlink; 815 1022 816 uint32_t u32SeqNum = usbProxyUsbIpSeqNumGet(pDev); 817 ReqSubmit.Hdr.u32ReqRet = USBIP_CMD_SUBMIT; 818 ReqSubmit.Hdr.u32SeqNum = u32SeqNum; 819 ReqSubmit.Hdr.u32DevId = pProxyDevUsbIp->u32DevId; 820 ReqSubmit.Hdr.u32Direction = USBIP_DIR_OUT; 821 ReqSubmit.Hdr.u32Endpoint = 0; /* Only default control endpoint is allowed for these kind of messages. */ 822 return VERR_NOT_IMPLEMENTED; 1023 uint32_t u32SeqNum = usbProxyUsbIpSeqNumGet(pProxyDevUsbIp); 1024 ReqUnlink.Hdr.u32ReqRet = USBIP_CMD_UNLINK; 1025 ReqUnlink.Hdr.u32SeqNum = u32SeqNum; 1026 ReqUnlink.Hdr.u32DevId = pProxyDevUsbIp->u32DevId; 1027 ReqUnlink.Hdr.u32Direction = USBIP_DIR_OUT; 1028 ReqUnlink.Hdr.u32Endpoint = pUrb->EndPt; 1029 ReqUnlink.u32SeqNum = pUrbUsbIp->u32SeqNumUrb; 1030 1031 int rc = RTTcpWrite(pProxyDevUsbIp->hSocket, &ReqUnlink, sizeof(ReqUnlink)); 1032 /* Wait for the reply. */ 1033 return rc; 823 1034 } 824 1035 … … 829 1040 PUSBPROXYDEVUSBIP pDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); 830 1041 int rc = VINF_SUCCESS; 831 size_t cbWritten = 0; 832 833 ASMAtomicXchgBool(&pDevUsbIp->fWokenUp, true); 834 835 if (ASMAtomicReadBool(&pDevUsbIp->fWaiting)) 836 { 1042 1043 if (!ASMAtomicXchgBool(&pDevUsbIp->fWokenUp, true)) 1044 { 1045 size_t cbWritten = 0; 1046 837 1047 rc = RTPipeWrite(pDevUsbIp->hPipeW, "", 1, &cbWritten); 838 1048 Assert(RT_SUCCESS(rc) || cbWritten == 0);
Note:
See TracChangeset
for help on using the changeset viewer.