/* $Id: USBProxyDevice-usbip.cpp 53260 2014-11-06 22:02:32Z vboxsync $ */ /** @file * USB device proxy - USB/IP backend. */ /* * Copyright (C) 2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /******************************************************************************* * Header Files * *******************************************************************************/ #define LOG_GROUP LOG_GROUP_DRV_USBPROXY #include #include #include #include #include #include #include #include #include #include #include #include "../USBProxyDevice.h" /******************************************************************************* * Constants And Macros, Structures and Typedefs * *******************************************************************************/ /** The USB version number used for the protocol. */ #define USBIP_VERSION UINT16_C(0x0100) /** Request indicator in the command code. */ #define USBIP_INDICATOR_REQ RT_BIT_16(15) /** Command/Reply code for OP_REQ/RET_DEVLIST. */ #define USBIP_REQ_RET_DEVLIST UINT16_C(5) /** Command/Reply code for OP_REQ/REP_IMPORT. */ #define USBIP_REQ_RET_IMPORT UINT16_C(3) /** USB submit command identifier. */ #define USBIP_CMD_SUBMIT UINT32_C(1) /** USB submit status identifier. */ #define USBIP_RET_SUBMIT UINT32_C(2) /** URB unlink (cancel) command identifier. */ #define USBIP_CMD_UNLINK UINT32_C(3) /** URB unlink (cancel) reply identifier. */ #define USBIP_RET_UNLINK UINT32_C(4) /** Short read is not okay for the specified URB. */ #define USBIP_XFER_FLAGS_SHORT_NOT_OK RT_BIT_32(0) /** Queue the isochronous URB as soon as possible. */ #define USBIP_XFER_FLAGS_ISO_ASAP RT_BIT_32(1) /** Don't use DMA mappings for this URB. */ #define USBIP_XFER_FLAGS_NO_TRANSFER_DMA_MAP RT_BIT_32(2) /** Explain - only applies to UHCI. */ #define USBIP_XFER_FLAGS_FSBR RT_BIT_32(4) /** URB direction - input. */ #define USBIP_DIR_IN UINT32_C(0) /** URB direction - output. */ #define USBIP_DIR_OUT UINT32_C(1) /** * Exported device entry in the OP_RET_DEVLIST reply. */ #pragma pack(1) typedef struct UsbIpExportedDevice { /** Path of the device, zero terminated string. */ char szPath[256]; /** Bus ID of the exported device, zero terminated string. */ char szBusId[32]; /** Bus number. */ uint32_t u32BusNum; /** Device number. */ uint32_t u32DevNum; /** Speed indicator of the device. */ uint32_t u32Speed; /** Vendor ID of the device. */ uint16_t u16VendorId; /** Product ID of the device. */ uint16_t u16ProductId; /** Device release number. */ uint16_t u16BcdDevice; /** Device class. */ uint8_t bDeviceClass; /** Device Subclass. */ uint8_t bDeviceSubClass; /** Device protocol. */ uint8_t bDeviceProtocol; /** Current configuration value of the device. */ uint8_t bNumConfigurations; /** Number of interfaces for the device. */ uint8_t bNumInterfaces; } UsbIpExportedDevice; /** Pointer to a exported device entry. */ typedef UsbIpExportedDevice *PUsbIpExportedDevice; #pragma pack() /** * Interface descriptor entry for an exported device. */ #pragma pack(1) typedef struct UsbIpDeviceInterface { /** Intefrace class. */ uint8_t bInterfaceClass; /** Interface sub class. */ uint8_t bInterfaceSubClass; /** Interface protocol identifier. */ uint8_t bInterfaceProtocol; /** Padding byte for alignment. */ uint8_t bPadding; } UsbIpDeviceInterface; /** Pointer to an interface descriptor entry. */ typedef UsbIpDeviceInterface *PUsbIpDeviceInterface; #pragma pack() /** * USB/IP Import request. */ #pragma pack(1) typedef struct UsbIpReqImport { /** Protocol version number. */ uint16_t u16Version; /** Command code. */ uint16_t u16Cmd; /** Status field, unused. */ uint32_t u32Status; /** Bus Id of the device as zero terminated string. */ char aszBusId[32]; } UsbIpReqImport; /** Pointer to a import request. */ typedef UsbIpReqImport *PUsbIpReqImport; #pragma pack() /** * USB/IP Import reply. * * This is only the header, for successful * imports the device details are sent to as * defined in UsbIpExportedDevice. */ #pragma pack(1) typedef struct UsbIpRetImport { /** Protocol version number. */ uint16_t u16Version; /** Command code. */ uint16_t u16Cmd; /** Status field, unused. */ uint32_t u32Status; } UsbIpRetImport; /** Pointer to a import reply. */ typedef UsbIpRetImport *PUsbIpRetImport; #pragma pack() /** * Command/Reply header common to the submit and unlink commands * replies. */ #pragma pack(1) typedef struct UsbIpReqRetHdr { /** Request/Return code. */ uint32_t u32ReqRet; /** Sequence number to identify the URB. */ uint32_t u32SeqNum; /** Device id. */ uint32_t u32DevId; /** Direction of the endpoint (host->device, device->host). */ uint32_t u32Direction; /** Endpoint number. */ uint32_t u32Endpoint; } UsbIpReqRetHdr; /** Pointer to a request/reply header. */ typedef UsbIpReqRetHdr *PUsbIpReqRetHdr; #pragma pack() /** * USB/IP Submit request. */ #pragma pack(1) typedef struct UsbIpReqSubmit { /** The request header. */ UsbIpReqRetHdr Hdr; /** Transfer flags for the URB. */ uint32_t u32XferFlags; /** Transfer buffer length. */ uint32_t u32TransferBufferLength; /** Frame to transmit an ISO frame. */ uint32_t u32StartFrame; /** Number of isochronous packets. */ uint32_t u32NumIsocPkts; /** Maximum time for the request on the server side host controller. */ uint32_t u32Interval; /** Setup data for a control URB. */ VUSBSETUP Setup; } UsbIpReqSubmit; /** Pointer to a submit request. */ typedef UsbIpReqSubmit *PUsbIpReqSubmit; #pragma pack() /** * USB/IP Submit reply. */ #pragma pack(1) typedef struct UsbIpRetSubmit { /** The reply header. */ UsbIpReqRetHdr Hdr; /** Status code. */ uint32_t u32Status; /** Actual length of the reply buffer. */ uint32_t u32ActualLength; /** The actual selected frame for a isochronous transmit. */ uint32_t u32StartFrame; /** Number of isochronous packets. */ uint32_t u32NumIsocPkts; /** Number of failed isochronous packets. */ uint32_t u32ErrorCount; /** Setup data for a control URB. */ VUSBSETUP Setup; } UsbIpRetSubmit; /** Pointer to a submit reply. */ typedef UsbIpRetSubmit *PUsbIpRetSubmit; #pragma pack() /** * Unlink URB request. */ #pragma pack(1) typedef struct UsbIpReqUnlink { /** The request header. */ UsbIpReqRetHdr Hdr; /** The sequence number to unlink. */ uint32_t u32SeqNum; } UsbIpReqUnlink; /** Pointer to a URB unlink request. */ typedef UsbIpReqUnlink *PUsbIpReqUnlink; #pragma pack() /** * Unlink URB reply. */ #pragma pack(1) typedef struct UsbIpRetUnlink { /** The reply header. */ UsbIpReqRetHdr Hdr; /** Status of the request. */ uint32_t u32Status; } UsbIpRetUnlink; /** Pointer to a URB unlink request. */ typedef UsbIpRetUnlink *PUsbIpRetUnlink; #pragma pack() /** * USB/IP backend specific data for one URB. * Required for tracking in flight and landed URBs. */ typedef struct USBPROXYURBUSBIP { /** List node for the in flight or landed URB list. */ RTLISTNODE NodeList; /** Sequence number the assigned URB is identified by. */ uint32_t u32SeqNumUrb; /** Pointer to the VUSB URB. */ PVUSBURB pVUsbUrb; } USBPROXYURBUSBIP; /** Pointer to a USB/IP URB. */ typedef USBPROXYURBUSBIP *PUSBPROXYURBUSBIP; /** * Backend data for the USB/IP USB Proxy device backend. */ typedef struct USBPROXYDEVUSBIP { /** IPRT socket handle. */ RTSOCKET hSocket; /** Pollset with the wakeup pipe and socket. */ RTPOLLSET hPollSet; /** Pipe endpoint - read (in the pollset). */ RTPIPE hPipeR; /** Pipe endpoint - write. */ RTPIPE hPipeW; /** Flag whether the reaper thread was woken up. */ volatile bool fWokenUp; /** Flag whether the reaper thread is waiting in the select call. */ volatile bool fWaiting; /** Next sequence number to use for identifying submitted URBs. */ volatile uint32_t u32SeqNumNext; /** Fast mutex protecting the lists below against concurrent access. */ RTSEMFASTMUTEX hMtxLists; /** List of in flight URBs. */ RTLISTANCHOR ListUrbsInFlight; /** List of landed URBs. */ RTLISTANCHOR ListUrbsLanded; /** Port of the USB/IP host to connect to. */ uint32_t uPort; /** USB/IP host address. */ char *pszHost; /** USB Bus ID of the device to capture. */ char *pszBusId; /** The device ID to use to identify the device. */ uint32_t u32DevId; } USBPROXYDEVUSBIP, *PUSBPROXYDEVUSBIP; /** Pollset id of the socket. */ #define USBIP_POLL_ID_SOCKET 0 /** Pollset id of the pipe. */ #define USBIP_POLL_ID_PIPE 1 /** * Converts a request/reply header from network to host endianness. * * @returns nothing. * @param pHdr The header to convert. */ DECLINLINE(void) usbProxyUsbIpReqRetHdrN2H(PUsbIpReqRetHdr pHdr) { pHdr->u32ReqRet = RT_H2N_U32(pHdr->u32ReqRet); pHdr->u32SeqNum = RT_H2N_U32(pHdr->u32SeqNum); pHdr->u32DevId = RT_H2N_U32(pHdr->u32DevId); pHdr->u32Direction = RT_H2N_U32(pHdr->u32Direction); pHdr->u32Endpoint = RT_H2N_U32(pHdr->u32Endpoint); } /** * Converts a request/reply header from host to network endianness. * * @returns nothing. * @param pHdr The header to convert. */ DECLINLINE(void) usbProxyUsbIpReqRetHdrH2N(PUsbIpReqRetHdr pHdr) { pHdr->u32ReqRet = RT_N2H_U32(pHdr->u32ReqRet); pHdr->u32SeqNum = RT_N2H_U32(pHdr->u32SeqNum); pHdr->u32DevId = RT_N2H_U32(pHdr->u32DevId); pHdr->u32Direction = RT_N2H_U32(pHdr->u32Direction); pHdr->u32Endpoint = RT_N2H_U32(pHdr->u32Endpoint); } /** * Converts a submit request from host to network endianness. * * @returns nothing. * @param pReqSubmit The submit request to convert. */ DECLINLINE(void) usbProxyUsbIpReqSubmitH2N(PUsbIpReqSubmit pReqSubmit) { usbProxyUsbIpReqRetHdrH2N(&pReqSubmit->Hdr); pReqSubmit->u32XferFlags = RT_H2N_U32(pReqSubmit->u32XferFlags); pReqSubmit->u32TransferBufferLength = RT_H2N_U32(pReqSubmit->u32TransferBufferLength); pReqSubmit->u32StartFrame = RT_H2N_U32(pReqSubmit->u32StartFrame); pReqSubmit->u32NumIsocPkts = RT_H2N_U32(pReqSubmit->u32NumIsocPkts); pReqSubmit->u32Interval = RT_H2N_U32(pReqSubmit->u32Interval); } /** * Converts a submit reply from network to host endianness. * * @returns nothing. * @param pReqSubmit The submit reply to convert. */ DECLINLINE(void) usbProxyUsbIpRetSubmitN2H(PUsbIpRetSubmit pRetSubmit) { usbProxyUsbIpReqRetHdrN2H(&pReqSubmit->Hdr); pRetSubmit->u32Status = RT_N2H_U32(pRetSubmit->u32Status); pRetSubmit->u32ActualLength = RT_N2H_U32(pRetSubmit->u32ActualLength); pRetSubmit->u32StartFrame = RT_N2H_U32(pRetSubmit->u32StartFrame); pRetSubmit->u32NumIsocPkts = RT_N2H_U32(pRetSubmit->u32NumIsocPkts); pRetSubmit->u32ErrorCount = RT_N2H_U32(pRetSubmit->u32ErrorCount); } /** * Convert the given exported device structure from host to network byte order. * * @returns nothing. * @param pDevice The device structure to convert. */ DECLINLINE(void) usbProxyUsbIpExportedDeviceN2H(PUsbIpExportedDevice pDevice) { pDevice->u32BusNum = RT_N2H_U32(pDevice->u32BusNum); pDevice->u32DevNum = RT_N2H_U32(pDevice->u32DevNum); pDevice->u32Speed = RT_N2H_U16(pDevice->u32Speed); pDevice->u16VendorId = RT_N2H_U16(pDevice->u16VendorId); pDevice->u16ProductId = RT_N2H_U16(pDevice->u16ProductId); pDevice->u16BcdDevice = RT_N2H_U16(pDevice->u16BcdDevice); } /** * Converts a USB/IP status code to VBox status code. * * @returns VBox status code. * @param u32Status The USB/IP status code from the reply. */ DECLINLINE(int) usbProxyUsbIpErrConvertFromStatus(uint32_t u32Status) { if (RT_LIKELY(u32Status == 0)) return VINF_SUCCESS; return VERR_NOT_IMPLEMENTED; } /** * Gets the next free sequence number. * * @returns Next free sequence number. * @param pProxyDevUsbIp The USB/IP proxy device data. */ DECLINLINE(uint32_t) usbProxyUsbIpSeqNumGet(PUSBPROXYDEVUSBIP pProxyDevUsbIp) { return ASMAtomicIncU32(&pProxyDevUsbIp->u32SeqNumNext); } /** * Parse the string representation of the host address. * * @returns VBox status code. * @param pProxyDevUsbIp The USB/IP proxy device data to parse the address for. * @param pszAddress The address string to parse. */ static int usbProxyUsbIpParseAddress(PUSBPROXYDEVUSBIP pProxyDevUsbIp, const char *pszAddress) { int rc = VINF_SUCCESS; uint32_t uPort; char *pszPortStart = RTStrStr(pszAddress, ":"); char *pszHost = NULL; if (pszPortStart) { size_t cbHost = pszPortStart - pszAddress; pszPortStart++; rc = RTStrToUInt32Ex(pszPortStart, NULL, 10 /* uBase */, &pProxyDevUsbIp->uPort); if ( rc == VINF_SUCCESS || cbHost == 0) { rc = RTStrAllocEx(&pDevUsbIp->pszHost, cbHost + 1); if (RT_SUCCESS(rc)) { rc = RTStrCopyEx(pDevUsbIp->pszHost, cbHost + 1, pszAddress, cbHost); AssertRC(rc); return VINF_SUCCESS; } } else rc = VERR_INVALID_PARAMETER; } else rc = VERR_INVALID_PARAMETER; return rc; } /** * Connects to the USB/IP host and claims the device given in the proxy device data. * * @returns VBox status. * @param pProxyDevUsbIp The USB/IP proxy device data. */ static int usbProxyUsbIpConnect(PUSBPROXYDEVUSBIP pProxyDevUsbIp) { int rc = VINF_SUCCESS; rc = RTTcpClientConnect(pDevUsbIp->pszHost, pDevUsbIp->uPort, &pDevUsbIp->hSocket); if (RT_SUCCESS(rc)) { /* Disable send coalescing. */ rc = RTTcpSetSendCoalescing(pDevUsbIp->hSocket, false); if (RT_FAILURE(rc)) LogRel(("UsbIp: Disabling send coalescing failed (rc=%Rrc), continuing nevertheless but expect reduced performance\n", rc)); /* Import the device, i.e. claim it for our use. */ UsbIpReqImport ReqImport; ReqImport.u16Version = RT_H2N_U16(USBIP_VERSION); ReqImport.u16Cmd = RT_H2N_U16(USBIP_INDICATOR_REQ | USBIP_REQ_RET_IMPORT); ReqImport.u32Status = RT_H2N_U32(0); rc = RTStrCopy(&ReqImport.aszBusId[0], sizeof(ReqImport.aszBusId[0]), pProxyDevUsbIp->pszBusId); if (rc = VINF_SUCCESS) { rc = RTTcpWrite(pDevUsbIp->hSocket, &ReqImport, sizeof(ReqImport)); if (RT_SUCCESS(rc)) { /* Read the reply. */ UsbIpRetImport RetImport; rc = RTTcpRead(pDevUsbIp->hSocket, &RetImport, sizeof(RetImport)); if (RT_SUCCESS(rc)) { RetImport.u16Version = RT_N2H_U16(RetImport.u16Version); RetImport.u16Cmd = RT_N2H_U16(RetImport.u16Cmd); RetImport.u32Status = RT_N2H_U16(RetImport.u32Status); if ( RetImport.u16Version == USBIP_VERSION && RetImport.u16Cmd == USBIP_REQ_RET_IMPORT && RetImport.u32Status == 0) { /* Read the device data. */ UsbIpExportedDevice Device; rc = RTTcpRead(pDevUsbIp->hSocket, &Device, sizeof(Device)); if (RT_SUCCESS(rc)) { usbProxyUsbIpExportedDeviceN2H(&Device); pProxyDevUsbIp->u32DevId = (Device.u32BusNum << 16) | Device.u32DevNum; } } else { /* Check what went wrong and leave a meaningful error message in the log. */ if (RetImport.u16Version != USBIP_VERSION) LogRel(("UsbIp: Unexpected protocol version received from host (%#x vs. %#x)\n", RetImport.u16Version, USBIP_VERSION)); else if (RetImport.u16Cmd != USBIP_REQ_RET_IMPORT) LogRel(("UsbIp: Unexpected reply code received from host (%#x vs. %#x)\n", RetImport.u16Cmd, USBIP_REQ_RET_IMPORT)); else if (RetImport.u32Status != 0) LogRel(("UsbIp: Claiming the device has failed on the host with an unspecified error\n")); else AssertMsgFailed(("Something went wrong with if condition\n")); } } } } else { LogRel(("UsbIp: Given bus ID is exceeds permitted protocol length: %u vs %u\n", strlen(pProxyDevUsbIp->pszBusId) + 1, sizeof(ReqImport.aszBusId[0]))); rc = VERR_INVALID_PARAMETER; } if (RT_FAILURE(rc)) RTTcpClientCloseEx(pDevUsbIp->hSocket, false /*fGracefulShutdown*/); } if (RT_FAILURE(rc)) LogRel(("UsbIp: Connecting to the host %s failed with %Rrc\n", pDevUsbIp->pszHost, rc)); return rc; } /** * Disconnects from the USB/IP host releasing the device given in the proxy device data. * * @returns VBox status code. * @param pProxyDevUsbIp The USB/IP proxy device data. */ static int usbProxyUsbIpDisconnect(PUSBPROXYDEVUSBIP pProxyDevUsbIp) { int rc = VINF_SUCCESS; rc = RTTcpClientCloseEx(pDevUsbIp->hSocket, false /*fGracefulShutdown*/); if (RT_SUCCESS(rc)) pDevUsbIp->hSocket = NIL_RTSOCKET; return rc; } /** * Synchronously exchange a given control message with the remote device. * * @eturns VBox status code. * @param pProxyDevUsbIp The USB/IP proxy device data. * @param pSetup The setup message. * * @note This method is only used to implement the *SetConfig, *SetInterface and *ClearHaltedEp * callbacks because the USB/IP protocol lacks dedicated requests for these. * @remark It is assumed that this method is never called while usbProxyUsbIpUrbReap is called * on another thread. */ static int usbProxyUsbIpCtrlUrbExchangeSync(PUSBPROXYDEVUSBIP pProxyDevUsbIp, PVUSBSETUP pSetup) { int rc = VINF_SUCCESS; AssertMsg(!pProxyDevUsbIp->fWaiting, ("usbProxyUsbIpUrbReap is called on another thread\n")); UsbIpReqSubmit ReqSubmit; uint32_t u32SeqNum = usbProxyUsbIpSeqNumGet(pProxyDevUsbIp); ReqSubmit.Hdr.u32ReqRet = USBIP_CMD_SUBMIT; ReqSubmit.Hdr.u32SeqNum = u32SeqNum; ReqSubmit.Hdr.u32DevId = pProxyDevUsbIp->u32DevId; ReqSubmit.Hdr.u32Direction = USBIP_DIR_OUT; ReqSubmit.Hdr.u32Endpoint = 0; /* Only default control endpoint is allowed for these kind of messages. */ ReqSubmit.u32XferFlags = 0; ReqSubmit.u32TransferBufferLength = 0; ReqSubmit.u32StartFrame = 0; ReqSubmit.u32NumIsocPkts = 0; ReqSubmit.u32Interval = 0; memcpy(&ReqSubmit.Setup, pSetup, sizeof(ReqSubmit.Setup)); usbProxyUsbIpReqSubmitH2N(&ReqSubmit); /* Send the command. */ rc = RTTcpWrite(pProxyDevUsbIp->hSocket, &ReqSubmit, sizeof(ReqSubmit)); if (RT_SUCCESS(rc)) { /* Wait for the response. */ /** @todo: Don't wait indefinitely long. */ UsbIpRetSubmit RetSubmit; rc = RTTcpRead(pProxyDevUsbIp->hSocket, &RetSubmit, sizeof(RetSubmit)); if (RT_SUCCESS(rc)) { usbProxyUsbIpRetSubmitN2H(&RetSubmit); rc = usbProxyUsbIpErrConvertFromStatus(RetSubmit.u32Status); } } return rc; } /* * The USB proxy device functions. */ static DECLCALLBACK(int) usbProxyUsbIpOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress, void *pvBackend) { LogFlowFunc(("pProxyDev=%p pszAddress=%s, pvBackend=%p\n", pProxyDev, pszAddress, pvBackend)); PUSBPROXYDEVUSBIP pDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); int rc = VINF_SUCCESS; RTListInit(&pDevUsbIp->ListUrbsInFlight); RTListInit(&pDevUsbIp->ListUrbsLanded); pDevUsbIp->hSocket = NIL_RTSOCKET; pDevUsbIp->hPollSet = NIL_RTPOLLSET; pDevUsbIp->hPipeW = NIL_RTPIPE; pDevUsbIp->hPipeR = NIL_RTPIPE; pDevUsbIp->fWokenUp = false; pDevUsbIp->fWaiting = false; pDevUsbIp->u32SeqNumNext = 0; pDevUsbIp->pszHost = NULL; pDevUsbIp->pszBusId = NULL; /* Setup wakeup pipe and poll set first. */ rc = RTPipeCreate(&pDevUsbIp->hPipeR, &pDevUsbIp->hPipeW, 0); if (RT_SUCCESS(rc)) { rc = RTPollSetCreate(&pDevUsbIp->hPollSet); if (RT_SUCCESS(rc)) { rc = RTPollSetAddPipe(pDevUsbIp->hPollSet, pDevUsbIp->hPipeR, RTPOLL_EVT_READ, USBIP_POLL_ID_PIPE); if (RT_SUCCESS(rc)) { /* Connect to the USB/IP host. */ rc = usbProxyUsbIpParseAddress(pDevUsbIp, pszAddress); if (RT_SUCCESS(rc)) rc = usbProxyUsbIpConnect(pProxyDevUsbIp); } if (RT_FAILURE(rc)) { RTPollSetRemove(pDevUsbIp->hPollSet, USBIP_POLL_ID_PIPE); int rc2 = RTPollSetDestroy(pDevUsbIp->hPollSet); AssertRC(rc2); } } if (RT_FAILURE(rc)) { int rc2 = RTPipeClose(pDevUsbIp->hPipeR); AssertRC(rc2); rc2 = RTPipeClose(pDevUsbIp->hPipeW); AssertRC(rc2); } } return rc; } static DECLCALLBACK(void) usbProxyUsbIpClose(PUSBPROXYDEV pProxyDev) { LogFlowFunc(("pProxyDev = %p\n", pProxyDev)); PUSBPROXYDEVUSBIP pDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); /* Destroy the pipe and pollset if necessary. */ if (pDevUsbIp->hPollSet != NIL_RTPOLLSET) { if (pDevUsbIp->hSocket != NIL_RTSOCKET) { rc = RTPollSetRemove(pDevUsbIp->hPollSet, USBIP_POLL_ID_SOCKET); Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND); } rc = RTPollSetRemove(pDevUsbIp->hPollSet, USBIP_POLL_ID_PIPE); AssertRC(rc); rc = RTPollSetDestroy(pDevUsbIp->hPollSet); AssertRC(rc); rc = RTPipeClose(pDevUsbIp->hPipeR); AssertRC(rc); rc = RTPipeClose(pDevUsbIp->hPipeW); AssertRC(rc); } if (pDevUsbIp->hSocket != NIL_RTSOCKET) usbProxyUsbIpDisconnect(pDevUsbIp); if (pDevUsbIp->pszHost) RTStrFree(pDevUsbIp->pszHost); if (pDevUsbIp->pszBusId) RTStrFree(pDevUsbIp->pszBusId); /* Clear the URB lists. */ int rc = RTSemFastMutexRequest(pDevUsbIp->hMtxLists); AssertRC(rc); PUSBPROXYURBUSBIP pIter = NULL; PUSBPROXYURBUSBIP pIterNext = NULL; RTListForEachSafe(&pDevUsbIp->ListUrbsInFlight, pIter, pIterNext, USBPROXYURBUSBIP, NodeList) { RTListNodeRemove(&pIter->NodeList); RTMemFree(pIter); } RTListForEachSafe(&pDevUsbIp->ListUrbsLanded, pIter, pIterNext, USBPROXYURBUSBIP, NodeList) { RTListNodeRemove(&pIter->NodeList); RTMemFree(pIter); } RTSemFastMutexRelease(pDevUsbIp->hMtxLists); RTSemFastMutexDestroy(pDevUsbIp->hMtxLists); } static DECLCALLBACK(int) usbProxyUsbIpReset(PUSBPROXYDEV pProxyDev, bool fResetOnLinux) { LogFlowFunc(("pProxyDev = %p\n", pProxyDev)); PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); return VERR_NOT_IMPLEMENTED; } static DECLCALLBACK(int) usbProxyUsbIpSetConfig(PUSBPROXYDEV pProxyDev, int iCfg) { LogFlowFunc(("pProxyDev=%s cfg=%#x\n", pProxyDev->pUsbIns->pszName, cfg)); PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); VUSBSETUP Setup; Setup.bmRequestType = 0; Setup.bRequest = 0x09; Setup.wValue = iCfg; Setup.wIndex = 0; Setup.wLength = 0; return usbProxyUsbIpCtrlUrbExchangeSync(pDev, &Setup); } static DECLCALLBACK(int) usbProxyUsbIpClaimInterface(PUSBPROXYDEV pProxyDev, int ifnum) { LogFlowFunc(("pProxyDev=%s ifnum=%#x\n", pProxyDev->pUsbIns->pszName, ifnum)); return VINF_SUCCESS; } static DECLCALLBACK(int) usbProxyUsbIpReleaseInterface(PUSBPROXYDEV pProxyDev, int ifnum) { LogFlowFunc(("pProxyDev=%s ifnum=%#x\n", pProxyDev->pUsbIns->pszName, ifnum)); return VINF_SUCCESS; } static DECLCALLBACK(int) usbProxyUsbIpSetInterface(PUSBPROXYDEV pProxyDev, int ifnum, int setting) { LogFlowFunc(("pProxyDev=%p ifnum=%#x setting=%#x\n", pProxyDev, ifnum, setting)); PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); VUSBSETUP Setup; Setup.bmRequestType = 0x1; Setup.bRequest = 0x11; /* SET_INTERFACE */ Setup.wValue = setting; Setup.wIndex = ifnum; Setup.wLength = 0; return usbProxyUsbIpCtrlUrbExchangeSync(pDev, &Setup); } static DECLCALLBACK(int) usbProxyUsbIpClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int iEp) { LogFlowFunc(("pProxyDev=%s ep=%u\n", pProxyDev->pUsbIns->pszName, ep)); PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); VUSBSETUP Setup; Setup.bmRequestType = 0x2; Setup.bRequest = 0x01; /* CLEAR_FEATURE */ Setup.wValue = 0x00; /* ENDPOINT_HALT */ Setup.wIndex = iEp; Setup.wLength = 0; return usbProxyUsbIpCtrlUrbExchangeSync(pDev, &Setup); } static DECLCALLBACK(int) usbProxyUsbIpUrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb) { LogFlowFunc(("pUrb=%p\n", pUrb)); PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); return VERR_NOT_IMPLEMENTED; } static DECLCALLBACK(PVUSBURB) usbProxyUsbIpUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies) { LogFlowFunc(("pProxyDev=%s\n", pProxyDev->pUsbIns->pszName)); PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); return NULL; } static DECLCALLBACK(int) usbProxyUsbIpUrbCancel(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb) { LogFlowFunc(("pUrb=%p\n", pUrb)); PUSBPROXYDEVUSBIP pDev = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); PUSBPROXYURBUSBIP pUrbUsbIp = (PUSBPROXYURBUSBIP)pUrb->Dev.pvPrivate; UsbIpReqUnlink ReqUnlink; uint32_t u32SeqNum = usbProxyUsbIpSeqNumGet(pDev); ReqSubmit.Hdr.u32ReqRet = USBIP_CMD_SUBMIT; ReqSubmit.Hdr.u32SeqNum = u32SeqNum; ReqSubmit.Hdr.u32DevId = pProxyDevUsbIp->u32DevId; ReqSubmit.Hdr.u32Direction = USBIP_DIR_OUT; ReqSubmit.Hdr.u32Endpoint = 0; /* Only default control endpoint is allowed for these kind of messages. */ return VERR_NOT_IMPLEMENTED; } static DECLCALLBACK(int) usbProxyUsbIpWakeup(PUSBPROXYDEV pProxyDev) { LogFlowFunc(("pProxyDev=%s\n", pProxyDev->pUsbIns->pszName)); PUSBPROXYDEVUSBIP pDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); int rc = VINF_SUCCESS; size_t cbWritten = 0; ASMAtomicXchgBool(&pDevUsbIp->fWokenUp, true); if (ASMAtomicReadBool(&pDevUsbIp->fWaiting)) { rc = RTPipeWrite(pDevUsbIp->hPipeW, "", 1, &cbWritten); Assert(RT_SUCCESS(rc) || cbWritten == 0); } return rc; } /** * The USB/IP USB Proxy Backend operations. */ extern const USBPROXYBACK g_USBProxyDeviceUsbIp = { /* pszName */ "usbip", /* cbBackend */ sizeof(USBPROXYDEVUSBIP), usbProxyUsbIpOpen, NULL, usbProxyUsbIpClose, usbProxyUsbIpReset, usbProxyUsbIpSetConfig, usbProxyUsbIpClaimInterface, usbProxyUsbIpReleaseInterface, usbProxyUsbIpSetInterface, usbProxyUsbIpClearHaltedEp, usbProxyUsbIpUrbQueue, usbProxyUsbIpUrbCancel, usbProxyUsbIpUrbReap, usbProxyUsbIpWakeup, 0 };