diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Devices/USB/usbip | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/USB/usbip')
-rw-r--r-- | src/VBox/Devices/USB/usbip/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/Devices/USB/usbip/USBProxyDevice-usbip.cpp | 1818 |
2 files changed, 1818 insertions, 0 deletions
diff --git a/src/VBox/Devices/USB/usbip/Makefile.kup b/src/VBox/Devices/USB/usbip/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Devices/USB/usbip/Makefile.kup diff --git a/src/VBox/Devices/USB/usbip/USBProxyDevice-usbip.cpp b/src/VBox/Devices/USB/usbip/USBProxyDevice-usbip.cpp new file mode 100644 index 00000000..b91fcffd --- /dev/null +++ b/src/VBox/Devices/USB/usbip/USBProxyDevice-usbip.cpp @@ -0,0 +1,1818 @@ +/* $Id: USBProxyDevice-usbip.cpp $ */ +/** @file + * USB device proxy - USB/IP backend. + */ + +/* + * Copyright (C) 2014-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_DRV_USBPROXY + +#include <VBox/log.h> +#include <VBox/err.h> +#include <VBox/vmm/pdm.h> + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/alloc.h> +#include <iprt/string.h> +#include <iprt/socket.h> +#include <iprt/poll.h> +#include <iprt/tcp.h> +#include <iprt/pipe.h> +#include <iprt/list.h> +#include <iprt/semaphore.h> + +#include "../USBProxyDevice.h" + + +/********************************************************************************************************************************* +* Constants And Macros, Structures and Typedefs * +*********************************************************************************************************************************/ + +/** The USB version number used for the protocol. */ +#define USBIP_VERSION UINT16_C(0x0111) +/** Request indicator in the command code. */ +#define USBIP_INDICATOR_REQ RT_BIT(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(3) +/** URB unlink (cancel) command identifier. */ +#define USBIP_CMD_UNLINK UINT32_C(2) +/** 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(1) +/** URB direction - output. */ +#define USBIP_DIR_OUT UINT32_C(0) + +/** @name USB/IP error codes. + * @{ */ +/** Success indicator. */ +#define USBIP_STATUS_SUCCESS INT32_C(0) +/** Pipe stalled. */ +#define USBIP_STATUS_PIPE_STALLED INT32_C(-32) +/** URB was unlinked by a call to usb_unlink_urb(). */ +#define USBIP_STATUS_URB_UNLINKED INT32_C(-104) +/** Short read. */ +#define USBIP_STATUS_SHORT_READ INT32_C(-121) +/** @} */ + +/** + * 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; + /** Configuration value. */ + uint8_t bConfigurationValue; + /** 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() +AssertCompileSize(UsbIpExportedDevice, 312); + +/** + * 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. */ + int32_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. */ + int32_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() +AssertCompileSize(UsbIpReqSubmit, 48); + +/** + * USB/IP Submit reply. + */ +#pragma pack(1) +typedef struct UsbIpRetSubmit +{ + /** The reply header. */ + UsbIpReqRetHdr Hdr; + /** Status code. */ + int32_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() +AssertCompileSize(UsbIpRetSubmit, 48); + +/** + * Unlink URB request. + */ +#pragma pack(1) +typedef struct UsbIpReqUnlink +{ + /** The request header. */ + UsbIpReqRetHdr Hdr; + /** The sequence number to unlink. */ + uint32_t u32SeqNum; + /** Padding - unused. */ + uint8_t abPadding[24]; +} UsbIpReqUnlink; +/** Pointer to a URB unlink request. */ +typedef UsbIpReqUnlink *PUsbIpReqUnlink; +#pragma pack() +AssertCompileSize(UsbIpReqUnlink, 48); + +/** + * Unlink URB reply. + */ +#pragma pack(1) +typedef struct UsbIpRetUnlink +{ + /** The reply header. */ + UsbIpReqRetHdr Hdr; + /** Status of the request. */ + int32_t u32Status; + /** Padding - unused. */ + uint8_t abPadding[24]; +} UsbIpRetUnlink; +/** Pointer to a URB unlink request. */ +typedef UsbIpRetUnlink *PUsbIpRetUnlink; +#pragma pack() +AssertCompileSize(UsbIpRetUnlink, 48); + +/** + * Union of possible replies from the server during normal operation. + */ +#pragma pack(1) +typedef union UsbIpRet +{ + /** The header. */ + UsbIpReqRetHdr Hdr; + /** Submit reply. */ + UsbIpRetSubmit RetSubmit; + /** Unlink reply. */ + UsbIpRetUnlink RetUnlink; + /** Byte view. */ + uint8_t abReply[1]; +} UsbIpRet; +/** Pointer to a reply union. */ +typedef UsbIpRet *PUsbIpRet; +#pragma pack() + +/** + * Isochronous packet descriptor. +*/ +#pragma pack(1) +typedef struct UsbIpIsocPktDesc +{ + /** Offset */ + uint32_t u32Offset; + /** Length of the packet including padding. */ + uint32_t u32Length; + /** Size of the transmitted data. */ + uint32_t u32ActualLength; + /** Completion status for this packet. */ + int32_t i32Status; +} UsbIpIsocPktDesc; +/** Pointer to a isochronous packet descriptor. */ +typedef UsbIpIsocPktDesc *PUsbIpIsocPktDesc; +#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; + /** Sequence number of the unlink command if the URB was cancelled. */ + uint32_t u32SeqNumUrbUnlink; + /** Flag whether the URB was cancelled. */ + bool fCancelled; + /** USB xfer type. */ + VUSBXFERTYPE enmType; + /** USB xfer direction. */ + VUSBDIRECTION enmDir; + /** Completion status. */ + VUSBSTATUS enmStatus; + /** Pointer to the VUSB URB. */ + PVUSBURB pVUsbUrb; +} USBPROXYURBUSBIP; +/** Pointer to a USB/IP URB. */ +typedef USBPROXYURBUSBIP *PUSBPROXYURBUSBIP; + +/** + * USB/IP data receive states. + */ +typedef enum USBPROXYUSBIPRECVSTATE +{ + /** Invalid receive state. */ + USBPROXYUSBIPRECVSTATE_INVALID = 0, + /** Currently receiving the common header structure. */ + USBPROXYUSBIPRECVSTATE_HDR_COMMON, + /** Currently receieving the rest of the header structure. */ + USBPROXYUSBIPRECVSTATE_HDR_RESIDUAL, + /** Currently receiving data into the URB buffer. */ + USBPROXYUSBIPRECVSTATE_URB_BUFFER, + /** Currently receiving the isochronous packet descriptors. */ + USBPROXYUSBIPRECVSTATE_ISOC_PKT_DESCS, + /** Usual 32bit hack. */ + USBPROXYUSBIPRECVSTATE_32BIT_HACK = 0x7fffffff +} USBPROXYUSBIPRECVSTATE; +/** Pointer to an receive state. */ +typedef USBPROXYUSBIPRECVSTATE *PUSBPROXYUSBIPRECVSTATE; + +/** + * 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; + /** 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; + /** List of URBs to submit. */ + RTLISTANCHOR ListUrbsToQueue; + /** 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; + /** Temporary buffer for the next reply header */ + UsbIpRet BufRet; + /** Temporary buffer to hold all isochronous packet descriptors. */ + UsbIpIsocPktDesc aIsocPktDesc[8]; + /** Pointer to the current buffer to write received data to. */ + uint8_t *pbRecv; + /** Number of bytes received so far. */ + size_t cbRecv; + /** Number of bytes left to receive. until we advance the state machine and process the data */ + size_t cbLeft; + /** The current receiving state. */ + USBPROXYUSBIPRECVSTATE enmRecvState; + /** The URB we currently receive a response for. */ + PUSBPROXYURBUSBIP pUrbUsbIp; +} USBPROXYDEVUSBIP, *PUSBPROXYDEVUSBIP; + +/** Pollset id of the socket. */ +#define USBIP_POLL_ID_SOCKET 0 +/** Pollset id of the pipe. */ +#define USBIP_POLL_ID_PIPE 1 + +/** USB/IP address prefix for identifcation. */ +#define USBIP_URI_PREFIX "usbip://" +/** USB/IP address prefix length. */ +#define USBIP_URI_PREFIX_LEN (sizeof(USBIP_URI_PREFIX) - 1) + +/** Waking reason for the USB I/P reaper: New URBs to queue. */ +#define USBIP_REAPER_WAKEUP_REASON_QUEUE 'Q' +/** Waking reason for the USB I/P reaper: External wakeup. */ +#define USBIP_REAPER_WAKEUP_REASON_EXTERNAL 'E' + +/** + * 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(&pRetSubmit->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); +} + +/** + * Converts a isochronous packet descriptor from host to network endianness. + * + * @returns nothing. + * @param pIsocPktDesc The packet descriptor to convert. + */ +DECLINLINE(void) usbProxyUsbIpIsocPktDescH2N(PUsbIpIsocPktDesc pIsocPktDesc) +{ + pIsocPktDesc->u32Offset = RT_H2N_U32(pIsocPktDesc->u32Offset); + pIsocPktDesc->u32Length = RT_H2N_U32(pIsocPktDesc->u32Length); + pIsocPktDesc->u32ActualLength = RT_H2N_U32(pIsocPktDesc->u32ActualLength); + pIsocPktDesc->i32Status = RT_H2N_U32(pIsocPktDesc->i32Status); +} + +/** + * Converts a isochronous packet descriptor from network to host endianness. + * + * @returns nothing. + * @param pIsocPktDesc The packet descriptor to convert. + */ +DECLINLINE(void) usbProxyUsbIpIsocPktDescN2H(PUsbIpIsocPktDesc pIsocPktDesc) +{ + pIsocPktDesc->u32Offset = RT_N2H_U32(pIsocPktDesc->u32Offset); + pIsocPktDesc->u32Length = RT_N2H_U32(pIsocPktDesc->u32Length); + pIsocPktDesc->u32ActualLength = RT_N2H_U32(pIsocPktDesc->u32ActualLength); + pIsocPktDesc->i32Status = RT_N2H_U32(pIsocPktDesc->i32Status); +} + +/** + * Converts a unlink request from host to network endianness. + * + * @returns nothing. + * @param pReqUnlink The unlink request to convert. + */ +DECLINLINE(void) usbProxyUsbIpReqUnlinkH2N(PUsbIpReqUnlink pReqUnlink) +{ + usbProxyUsbIpReqRetHdrH2N(&pReqUnlink->Hdr); + pReqUnlink->u32SeqNum = RT_H2N_U32(pReqUnlink->u32SeqNum); +} + +/** + * Converts a unlink reply from network to host endianness. + * + * @returns nothing. + * @param pRetUnlink The unlink reply to convert. + */ +DECLINLINE(void) usbProxyUsbIpRetUnlinkN2H(PUsbIpRetUnlink pRetUnlink) +{ + usbProxyUsbIpReqRetHdrN2H(&pRetUnlink->Hdr); + pRetUnlink->u32Status = RT_N2H_U32(pRetUnlink->u32Status); +} + +/** + * 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 a VUSB status code. + * + * @returns VUSB status code. + * @param i32Status The USB/IP status code from the reply. + */ +DECLINLINE(VUSBSTATUS) usbProxyUsbIpVUsbStatusConvertFromStatus(int32_t i32Status) +{ + if (RT_LIKELY( i32Status == USBIP_STATUS_SUCCESS + || i32Status == USBIP_STATUS_SHORT_READ)) + return VUSBSTATUS_OK; + + switch (i32Status) + { + case USBIP_STATUS_PIPE_STALLED: + return VUSBSTATUS_STALL; + default: + return VUSBSTATUS_DNR; + } + /* not reached */ +} + +/** + * 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) +{ + uint32_t u32SeqNum = ASMAtomicIncU32(&pProxyDevUsbIp->u32SeqNumNext); + if (RT_UNLIKELY(!u32SeqNum)) + u32SeqNum = ASMAtomicIncU32(&pProxyDevUsbIp->u32SeqNumNext); + + return u32SeqNum; +} + +/** + * Links a given URB into the given list. + * + * @returns nothing. + * @param pProxyDevUsbIp The USB/IP proxy device data. + * @param pList The list to link the URB into. + * @param pUrbUsbIp The URB to link. + */ +DECLINLINE(void) usbProxyUsbIpLinkUrb(PUSBPROXYDEVUSBIP pProxyDevUsbIp, PRTLISTANCHOR pList, PUSBPROXYURBUSBIP pUrbUsbIp) +{ + int rc = RTSemFastMutexRequest(pProxyDevUsbIp->hMtxLists); + AssertRC(rc); + RTListAppend(pList, &pUrbUsbIp->NodeList); + RTSemFastMutexRelease(pProxyDevUsbIp->hMtxLists); +} + +/** + * Unlinks a given URB from the current assigned list. + * + * @returns nothing. + * @param pProxyDevUsbIp The USB/IP proxy device data. + * @param pUrbUsbIp The URB to unlink. + */ +DECLINLINE(void) usbProxyUsbIpUnlinkUrb(PUSBPROXYDEVUSBIP pProxyDevUsbIp, PUSBPROXYURBUSBIP pUrbUsbIp) +{ + int rc = RTSemFastMutexRequest(pProxyDevUsbIp->hMtxLists); + AssertRC(rc); + RTListNodeRemove(&pUrbUsbIp->NodeList); + RTSemFastMutexRelease(pProxyDevUsbIp->hMtxLists); +} + +/** + * Allocates a USB/IP proxy specific URB state. + * + * @returns Pointer to the USB/IP specific URB data or NULL on failure. + * @param pProxyDevUsbIp The USB/IP proxy device data. + */ +static PUSBPROXYURBUSBIP usbProxyUsbIpUrbAlloc(PUSBPROXYDEVUSBIP pProxyDevUsbIp) +{ + NOREF(pProxyDevUsbIp); + return (PUSBPROXYURBUSBIP)RTMemAllocZ(sizeof(USBPROXYURBUSBIP)); +} + +/** + * Frees the given USB/IP URB state. + * + * @returns nothing. + * @param pProxyDevUsbIp The USB/IP proxy device data. + * @param pUrbUsbIp The USB/IP speciic URB data. + */ +static void usbProxyUsbIpUrbFree(PUSBPROXYDEVUSBIP pProxyDevUsbIp, PUSBPROXYURBUSBIP pUrbUsbIp) +{ + NOREF(pProxyDevUsbIp); + RTMemFree(pUrbUsbIp); +} + +/** + * 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; + + if (!RTStrNCmp(pszAddress, USBIP_URI_PREFIX, USBIP_URI_PREFIX_LEN)) + { + pszAddress += USBIP_URI_PREFIX_LEN; + + const char *pszPortStart = RTStrStr(pszAddress, ":"); + if (pszPortStart) + { + pszPortStart++; + + const char *pszBusIdStart = RTStrStr(pszPortStart, ":"); + if (pszBusIdStart) + { + size_t cbHost = pszPortStart - pszAddress - 1; + size_t cbBusId = strlen(pszBusIdStart); + + pszBusIdStart++; + + rc = RTStrToUInt32Ex(pszPortStart, NULL, 10 /* uBase */, &pProxyDevUsbIp->uPort); + if ( rc == VINF_SUCCESS + || rc == VWRN_TRAILING_CHARS) + { + rc = RTStrAllocEx(&pProxyDevUsbIp->pszHost, cbHost + 1); + if (RT_SUCCESS(rc)) + rc = RTStrAllocEx(&pProxyDevUsbIp->pszBusId, cbBusId + 1); + if (RT_SUCCESS(rc)) + { + rc = RTStrCopyEx(pProxyDevUsbIp->pszHost, cbHost + 1, pszAddress, cbHost); + AssertRC(rc); + + rc = RTStrCopyEx(pProxyDevUsbIp->pszBusId, cbBusId + 1, pszBusIdStart, cbBusId); + AssertRC(rc); + + return VINF_SUCCESS; + } + } + else + rc = VERR_INVALID_PARAMETER; + } + else + rc = VERR_INVALID_PARAMETER; + } + 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 code. + * @param pProxyDevUsbIp The USB/IP proxy device data. + */ +static int usbProxyUsbIpConnect(PUSBPROXYDEVUSBIP pProxyDevUsbIp) +{ + int rc = VINF_SUCCESS; + rc = RTTcpClientConnect(pProxyDevUsbIp->pszHost, pProxyDevUsbIp->uPort, &pProxyDevUsbIp->hSocket); + if (RT_SUCCESS(rc)) + { + /* Disable send coalescing. */ + rc = RTTcpSetSendCoalescing(pProxyDevUsbIp->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(USBIP_STATUS_SUCCESS); + rc = RTStrCopy(&ReqImport.aszBusId[0], sizeof(ReqImport.aszBusId), pProxyDevUsbIp->pszBusId); + if (rc == VINF_SUCCESS) + { + rc = RTTcpWrite(pProxyDevUsbIp->hSocket, &ReqImport, sizeof(ReqImport)); + if (RT_SUCCESS(rc)) + { + /* Read the reply. */ + UsbIpRetImport RetImport; + rc = RTTcpRead(pProxyDevUsbIp->hSocket, &RetImport, sizeof(RetImport), NULL); + if (RT_SUCCESS(rc)) + { + RetImport.u16Version = RT_N2H_U16(RetImport.u16Version); + RetImport.u16Cmd = RT_N2H_U16(RetImport.u16Cmd); + RetImport.u32Status = RT_N2H_U32(RetImport.u32Status); + if ( RetImport.u16Version == USBIP_VERSION + && RetImport.u16Cmd == USBIP_REQ_RET_IMPORT + && RetImport.u32Status == USBIP_STATUS_SUCCESS) + { + /* Read the device data. */ + UsbIpExportedDevice Device; + rc = RTTcpRead(pProxyDevUsbIp->hSocket, &Device, sizeof(Device), NULL); + if (RT_SUCCESS(rc)) + { + usbProxyUsbIpExportedDeviceN2H(&Device); + pProxyDevUsbIp->u32DevId = (Device.u32BusNum << 16) | Device.u32DevNum; + + rc = RTPollSetAddSocket(pProxyDevUsbIp->hPollSet, pProxyDevUsbIp->hSocket, + RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, USBIP_POLL_ID_SOCKET); + } + } + 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))); + rc = VERR_INVALID_PARAMETER; + } + + if (RT_FAILURE(rc)) + RTTcpClientCloseEx(pProxyDevUsbIp->hSocket, false /*fGracefulShutdown*/); + } + if (RT_FAILURE(rc)) + LogRel(("UsbIp: Connecting to the host %s failed with %Rrc\n", pProxyDevUsbIp->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 = RTPollSetRemove(pProxyDevUsbIp->hPollSet, USBIP_POLL_ID_SOCKET); + Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND); + + rc = RTTcpClientCloseEx(pProxyDevUsbIp->hSocket, false /*fGracefulShutdown*/); + if (RT_SUCCESS(rc)) + pProxyDevUsbIp->hSocket = NIL_RTSOCKET; + return rc; +} + +/** + * Returns the URB matching the given sequence number from the in flight list. + * + * @returns pointer to the URB matching the given sequence number or NULL + * @param pProxyDevUsbIp The USB/IP proxy device data. + * @param u32SeqNum The sequence number to search for. + */ +static PUSBPROXYURBUSBIP usbProxyUsbIpGetInFlightUrbFromSeqNum(PUSBPROXYDEVUSBIP pProxyDevUsbIp, uint32_t u32SeqNum) +{ + bool fFound = false; + + int rc = RTSemFastMutexRequest(pProxyDevUsbIp->hMtxLists); + AssertRC(rc); + PUSBPROXYURBUSBIP pIt; + RTListForEach(&pProxyDevUsbIp->ListUrbsInFlight, pIt, USBPROXYURBUSBIP, NodeList) + { + if (pIt->u32SeqNumUrb == u32SeqNum) + { + fFound = true; + break; + } + } + RTSemFastMutexRelease(pProxyDevUsbIp->hMtxLists); + + return fFound ? pIt : NULL; +} + +/** + * Returns the URB matching the given sequence number from the cancel list. + * + * @returns pointer to the URB matching the given sequence number or NULL + * @param pProxyDevUsbIp The USB/IP proxy device data. + * @param u32SeqNum The sequence number to search for. + */ +static PUSBPROXYURBUSBIP usbProxyUsbIpGetCancelledUrbFromSeqNum(PUSBPROXYDEVUSBIP pProxyDevUsbIp, uint32_t u32SeqNum) +{ + bool fFound = false; + + int rc = RTSemFastMutexRequest(pProxyDevUsbIp->hMtxLists); + AssertRC(rc); + PUSBPROXYURBUSBIP pIt; + RTListForEach(&pProxyDevUsbIp->ListUrbsInFlight, pIt, USBPROXYURBUSBIP, NodeList) + { + if ( pIt->u32SeqNumUrbUnlink == u32SeqNum + && pIt->fCancelled == true) + { + fFound = true; + break; + } + } + RTSemFastMutexRelease(pProxyDevUsbIp->hMtxLists); + + return fFound ? pIt : NULL; +} + +/** + * Resets the receive state for a new reply. + * + * @returns nothing. + * @param pProxyDevUsbIp The USB/IP proxy device data. + */ +static void usbProxyUsbIpResetRecvState(PUSBPROXYDEVUSBIP pProxyDevUsbIp) +{ + pProxyDevUsbIp->enmRecvState = USBPROXYUSBIPRECVSTATE_HDR_COMMON; + pProxyDevUsbIp->pbRecv = (uint8_t *)&pProxyDevUsbIp->BufRet; + pProxyDevUsbIp->cbRecv = 0; + pProxyDevUsbIp->cbLeft = sizeof(UsbIpReqRetHdr); +} + +static void usbProxyUsbIpRecvStateAdvance(PUSBPROXYDEVUSBIP pProxyDevUsbIp, USBPROXYUSBIPRECVSTATE enmState, + uint8_t *pbData, size_t cbData) +{ + pProxyDevUsbIp->enmRecvState = enmState; + pProxyDevUsbIp->cbRecv = 0; + pProxyDevUsbIp->cbLeft = cbData; + pProxyDevUsbIp->pbRecv = pbData; +} + +/** + * Handles reception of a USB/IP PDU. + * + * @returns VBox status code. + * @param pProxyDevUsbIp The USB/IP proxy device data. + * @param ppUrbUsbIp Where to store the pointer to the USB/IP URB which completed. + * Will be NULL if the received PDU is not complete and we have + * have to wait for more data or on failure. + */ +static int usbProxyUsbIpRecvPdu(PUSBPROXYDEVUSBIP pProxyDevUsbIp, PUSBPROXYURBUSBIP *ppUrbUsbIp) +{ + int rc = VINF_SUCCESS; + size_t cbRead = 0; + PUSBPROXYURBUSBIP pUrbUsbIp = NULL; + + Assert(pProxyDevUsbIp->cbLeft); + + /* Read any available data first. */ + rc = RTTcpReadNB(pProxyDevUsbIp->hSocket, pProxyDevUsbIp->pbRecv, pProxyDevUsbIp->cbLeft, &cbRead); + if (RT_SUCCESS(rc)) + { + pProxyDevUsbIp->cbRecv += cbRead; + pProxyDevUsbIp->cbLeft -= cbRead; + pProxyDevUsbIp->pbRecv += cbRead; + + /* Process the received data if there is nothing to receive left for the current state. */ + if (!pProxyDevUsbIp->cbLeft) + { + switch (pProxyDevUsbIp->enmRecvState) + { + case USBPROXYUSBIPRECVSTATE_HDR_COMMON: + { + Assert(pProxyDevUsbIp->cbRecv == sizeof(UsbIpReqRetHdr)); + + /* + * Determine the residual amount of data to receive until + * the complete reply header was received. + */ + switch (RT_N2H_U32(pProxyDevUsbIp->BufRet.Hdr.u32ReqRet)) + { + case USBIP_RET_SUBMIT: + pProxyDevUsbIp->cbLeft = sizeof(UsbIpRetSubmit) - sizeof(UsbIpReqRetHdr); + pProxyDevUsbIp->enmRecvState = USBPROXYUSBIPRECVSTATE_HDR_RESIDUAL; + break; + case USBIP_RET_UNLINK: + pProxyDevUsbIp->cbLeft = sizeof(UsbIpRetUnlink) - sizeof(UsbIpReqRetHdr); + pProxyDevUsbIp->enmRecvState = USBPROXYUSBIPRECVSTATE_HDR_RESIDUAL; + break; + default: + AssertLogRelMsgFailed(("Invalid reply header received: %d\n", + pProxyDevUsbIp->BufRet.Hdr.u32ReqRet)); + usbProxyUsbIpResetRecvState(pProxyDevUsbIp); + } + + break; + } + case USBPROXYUSBIPRECVSTATE_HDR_RESIDUAL: + { + switch (RT_N2H_U32(pProxyDevUsbIp->BufRet.Hdr.u32ReqRet)) + { + case USBIP_RET_SUBMIT: + /* Get the URB from the in flight list. */ + pProxyDevUsbIp->pUrbUsbIp = usbProxyUsbIpGetInFlightUrbFromSeqNum(pProxyDevUsbIp, RT_N2H_U32(pProxyDevUsbIp->BufRet.Hdr.u32SeqNum)); + if (pProxyDevUsbIp->pUrbUsbIp) + { + usbProxyUsbIpRetSubmitN2H(&pProxyDevUsbIp->BufRet.RetSubmit); + + /* We still have to receive the transfer buffer, even in case of an error. */ + pProxyDevUsbIp->pUrbUsbIp->enmStatus = usbProxyUsbIpVUsbStatusConvertFromStatus(pProxyDevUsbIp->BufRet.RetSubmit.u32Status); + if (pProxyDevUsbIp->pUrbUsbIp->enmDir == VUSBDIRECTION_IN) + { + uint8_t *pbData = NULL; + size_t cbRet = 0; + + AssertPtr(pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb); + if (pProxyDevUsbIp->pUrbUsbIp->enmType == VUSBXFERTYPE_MSG) + { + /* Preserve the setup request. */ + pbData = &pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb->abData[sizeof(VUSBSETUP)]; + cbRet = pProxyDevUsbIp->BufRet.RetSubmit.u32ActualLength + sizeof(VUSBSETUP); + } + else + { + pbData = &pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb->abData[0]; + cbRet = pProxyDevUsbIp->BufRet.RetSubmit.u32ActualLength; + } + + if (pProxyDevUsbIp->BufRet.RetSubmit.u32ActualLength) + { + if (RT_LIKELY(pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb->cbData >= cbRet)) + { + pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb->cbData = (uint32_t)cbRet; + usbProxyUsbIpRecvStateAdvance(pProxyDevUsbIp, USBPROXYUSBIPRECVSTATE_URB_BUFFER, + pbData, pProxyDevUsbIp->BufRet.RetSubmit.u32ActualLength); + } + else + { + /* + * Bogus length returned from the USB/IP remote server. + * Error out because there is no way to find the end of the current + * URB and the beginning of the next one. The error will cause closing the + * connection to the rogue remote and all URBs get completed with an error. + */ + LogRelMax(10, ("USB/IP: Received reply with sequence number %u contains invalid length %zu (max %zu)\n", + pProxyDevUsbIp->BufRet.Hdr.u32SeqNum, cbRet, + pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb->cbData)); + rc = VERR_NET_PROTOCOL_ERROR; + } + } + else + { + pUrbUsbIp = pProxyDevUsbIp->pUrbUsbIp; + usbProxyUsbIpResetRecvState(pProxyDevUsbIp); + } + } + else + { + Assert(pProxyDevUsbIp->pUrbUsbIp->enmDir == VUSBDIRECTION_OUT); + pUrbUsbIp = pProxyDevUsbIp->pUrbUsbIp; + usbProxyUsbIpResetRecvState(pProxyDevUsbIp); + } + } + else + { + LogRel(("USB/IP: Received reply with sequence number %u doesn't match any local URB\n", + RT_N2H_U32(pProxyDevUsbIp->BufRet.Hdr.u32SeqNum))); + usbProxyUsbIpResetRecvState(pProxyDevUsbIp); + rc = VERR_NET_PROTOCOL_ERROR; + } + break; + case USBIP_RET_UNLINK: + pProxyDevUsbIp->pUrbUsbIp = usbProxyUsbIpGetCancelledUrbFromSeqNum(pProxyDevUsbIp, RT_N2H_U32(pProxyDevUsbIp->BufRet.Hdr.u32SeqNum)); + if (pProxyDevUsbIp->pUrbUsbIp) + { + usbProxyUsbIpRetUnlinkN2H(&pProxyDevUsbIp->BufRet.RetUnlink); + pUrbUsbIp = pProxyDevUsbIp->pUrbUsbIp; + pUrbUsbIp->pVUsbUrb->enmStatus = usbProxyUsbIpVUsbStatusConvertFromStatus(pProxyDevUsbIp->BufRet.RetUnlink.u32Status); + } + /* else: Probably received the data for the URB and is complete already. */ + + usbProxyUsbIpResetRecvState(pProxyDevUsbIp); + break; + } + + break; + } + case USBPROXYUSBIPRECVSTATE_URB_BUFFER: + if (pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb->enmType == VUSBXFERTYPE_ISOC) + usbProxyUsbIpRecvStateAdvance(pProxyDevUsbIp, USBPROXYUSBIPRECVSTATE_ISOC_PKT_DESCS, + (uint8_t *)&pProxyDevUsbIp->aIsocPktDesc[0], + pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb->cIsocPkts * sizeof(UsbIpIsocPktDesc)); + else + { + pUrbUsbIp = pProxyDevUsbIp->pUrbUsbIp; + usbProxyUsbIpResetRecvState(pProxyDevUsbIp); + } + break; + case USBPROXYUSBIPRECVSTATE_ISOC_PKT_DESCS: + /* Process all received isochronous packet descriptors. */ + for (unsigned i = 0; i < pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb->cIsocPkts; i++) + { + PVUSBURBISOCPTK pIsocPkt = &pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb->aIsocPkts[i]; + PUsbIpIsocPktDesc pIsocPktUsbIp = &pProxyDevUsbIp->aIsocPktDesc[i]; + + usbProxyUsbIpIsocPktDescN2H(pIsocPktUsbIp); + pIsocPkt->enmStatus = usbProxyUsbIpVUsbStatusConvertFromStatus(pIsocPktUsbIp->i32Status); + + if (RT_LIKELY( pIsocPktUsbIp->u32Offset < pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb->cbData + && pProxyDevUsbIp->pUrbUsbIp->pVUsbUrb->cbData - pIsocPktUsbIp->u32Offset >= pIsocPktUsbIp->u32ActualLength)) + { + pIsocPkt->off = pIsocPktUsbIp->u32Offset; + pIsocPkt->cb = pIsocPktUsbIp->u32ActualLength; + } + else + { + /* + * The offset and length value in the isoc packet descriptor are bogus and would cause a buffer overflow later on, leave an + * error message and disconnect from the rogue remote end. + */ + LogRelMax(10, ("USB/IP: Received reply with sequence number %u contains invalid isoc packet descriptor %u (offset=%u length=%u)\n", + pProxyDevUsbIp->BufRet.Hdr.u32SeqNum, i, + pIsocPktUsbIp->u32Offset, pIsocPktUsbIp->u32ActualLength)); + rc = VERR_NET_PROTOCOL_ERROR; + break; + } + } + + pUrbUsbIp = pProxyDevUsbIp->pUrbUsbIp; + usbProxyUsbIpResetRecvState(pProxyDevUsbIp); + break; + default: + AssertLogRelMsgFailed(("USB/IP: Invalid receive state %d\n", pProxyDevUsbIp->enmRecvState)); + } + } + } + + if (RT_SUCCESS(rc)) + *ppUrbUsbIp = pUrbUsbIp; + else + { + /* Complete all URBs with DNR error and mark device as unplugged, the current one is still in the in flight list. */ + pProxyDevUsbIp->pUrbUsbIp = NULL; + usbProxyUsbIpResetRecvState(pProxyDevUsbIp); + usbProxyUsbIpDisconnect(pProxyDevUsbIp); + + rc = RTSemFastMutexRequest(pProxyDevUsbIp->hMtxLists); + AssertRC(rc); + PUSBPROXYURBUSBIP pIt; + PUSBPROXYURBUSBIP pItNext; + RTListForEachSafe(&pProxyDevUsbIp->ListUrbsInFlight, pIt, pItNext, USBPROXYURBUSBIP, NodeList) + { + if (pIt->pVUsbUrb) /* can be NULL for requests created by usbProxyUsbIpCtrlUrbExchangeSync(). */ + pIt->pVUsbUrb->enmStatus = VUSBSTATUS_CRC; + RTListNodeRemove(&pIt->NodeList); + RTListAppend(&pProxyDevUsbIp->ListUrbsLanded, &pIt->NodeList); + } + RTSemFastMutexRelease(pProxyDevUsbIp->hMtxLists); + } + + return rc; +} + +/** + * Worker for queueing an URB on the main I/O thread. + * + * @returns VBox status code. + * @param pProxyDevUsbIp The USB/IP proxy device data. + * @param pUrbUsbIp The USB/IP URB to queue. + */ +static int usbProxyUsbIpUrbQueueWorker(PUSBPROXYDEVUSBIP pProxyDevUsbIp, PUSBPROXYURBUSBIP pUrbUsbIp) +{ + PVUSBURB pUrb = pUrbUsbIp->pVUsbUrb; + + pUrbUsbIp->u32SeqNumUrb = usbProxyUsbIpSeqNumGet(pProxyDevUsbIp); + pUrbUsbIp->enmType = pUrb->enmType; + pUrbUsbIp->enmStatus = pUrb->enmStatus; + pUrbUsbIp->enmDir = pUrb->enmDir; + + UsbIpReqSubmit ReqSubmit; + + RT_ZERO(ReqSubmit); + ReqSubmit.Hdr.u32ReqRet = USBIP_CMD_SUBMIT; + ReqSubmit.Hdr.u32SeqNum = pUrbUsbIp->u32SeqNumUrb; + ReqSubmit.Hdr.u32DevId = pProxyDevUsbIp->u32DevId; + ReqSubmit.Hdr.u32Endpoint = pUrb->EndPt; + ReqSubmit.Hdr.u32Direction = pUrb->enmDir == VUSBDIRECTION_IN ? USBIP_DIR_IN : USBIP_DIR_OUT; + ReqSubmit.u32XferFlags = 0; + if (pUrb->enmDir == VUSBDIRECTION_IN && pUrb->fShortNotOk) + ReqSubmit.u32XferFlags |= USBIP_XFER_FLAGS_SHORT_NOT_OK; + + ReqSubmit.u32TransferBufferLength = pUrb->cbData; + ReqSubmit.u32StartFrame = 0; + ReqSubmit.u32NumIsocPkts = 0; + ReqSubmit.u32Interval = 0; + + RTSGSEG aSegReq[3]; /* Maximum number of segments used for a Isochronous transfer. */ + UsbIpIsocPktDesc aIsocPktsDesc[8]; + unsigned cSegsUsed = 1; + aSegReq[0].pvSeg = &ReqSubmit; + aSegReq[0].cbSeg = sizeof(ReqSubmit); + + switch (pUrb->enmType) + { + case VUSBXFERTYPE_MSG: + memcpy(&ReqSubmit.Setup, &pUrb->abData, sizeof(ReqSubmit.Setup)); + ReqSubmit.u32TransferBufferLength -= sizeof(VUSBSETUP); + if (pUrb->enmDir == VUSBDIRECTION_OUT) + { + aSegReq[cSegsUsed].cbSeg = pUrb->cbData - sizeof(VUSBSETUP); + aSegReq[cSegsUsed].pvSeg = pUrb->abData + sizeof(VUSBSETUP); + if (aSegReq[cSegsUsed].cbSeg) + cSegsUsed++; + } + LogFlowFunc(("Message (Control) URB\n")); + break; + case VUSBXFERTYPE_ISOC: + LogFlowFunc(("Isochronous URB\n")); + ReqSubmit.u32XferFlags |= USBIP_XFER_FLAGS_ISO_ASAP; + ReqSubmit.u32NumIsocPkts = pUrb->cIsocPkts; + if (pUrb->enmDir == VUSBDIRECTION_OUT) + { + aSegReq[cSegsUsed].cbSeg = pUrb->cbData; + aSegReq[cSegsUsed].pvSeg = pUrb->abData; + cSegsUsed++; + } + + for (unsigned i = 0; i < pUrb->cIsocPkts; i++) + { + aIsocPktsDesc[i].u32Offset = pUrb->aIsocPkts[i].off; + aIsocPktsDesc[i].u32Length = pUrb->aIsocPkts[i].cb; + aIsocPktsDesc[i].u32ActualLength = 0; /** @todo */ + aIsocPktsDesc[i].i32Status = pUrb->aIsocPkts[i].enmStatus; + usbProxyUsbIpIsocPktDescH2N(&aIsocPktsDesc[i]); + } + + if (pUrb->cIsocPkts) + { + aSegReq[cSegsUsed].cbSeg = pUrb->cIsocPkts * sizeof(UsbIpIsocPktDesc); + aSegReq[cSegsUsed].pvSeg = &aIsocPktsDesc[0]; + cSegsUsed++; + } + + break; + case VUSBXFERTYPE_BULK: + case VUSBXFERTYPE_INTR: + LogFlowFunc(("Bulk URB\n")); + if (pUrb->enmDir == VUSBDIRECTION_OUT) + { + aSegReq[cSegsUsed].cbSeg = pUrb->cbData; + aSegReq[cSegsUsed].pvSeg = pUrb->abData; + cSegsUsed++; + } + break; + default: + return VERR_INVALID_PARAMETER; /** @todo better status code. */ + } + + usbProxyUsbIpReqSubmitH2N(&ReqSubmit); + + Assert(cSegsUsed <= RT_ELEMENTS(aSegReq)); + + /* Send the command. */ + RTSGBUF SgBufReq; + RTSgBufInit(&SgBufReq, &aSegReq[0], cSegsUsed); + + int rc = RTTcpSgWrite(pProxyDevUsbIp->hSocket, &SgBufReq); + if (RT_SUCCESS(rc)) + { + /* Link the URB into the list of in flight URBs. */ + usbProxyUsbIpLinkUrb(pProxyDevUsbIp, &pProxyDevUsbIp->ListUrbsInFlight, pUrbUsbIp); + } + + return rc; +} + +/** + * Queues all pending URBs from the list. + * + * @returns VBox status code. + * @param pProxyDevUsbIp The USB/IP proxy device data. + */ +static int usbProxyUsbIpUrbsQueuePending(PUSBPROXYDEVUSBIP pProxyDevUsbIp) +{ + RTLISTANCHOR ListUrbsPending; + + int rc = RTSemFastMutexRequest(pProxyDevUsbIp->hMtxLists); + AssertRC(rc); + RTListMove(&ListUrbsPending, &pProxyDevUsbIp->ListUrbsToQueue); + RTSemFastMutexRelease(pProxyDevUsbIp->hMtxLists); + + PUSBPROXYURBUSBIP pIter; + PUSBPROXYURBUSBIP pIterNext; + RTListForEachSafe(&ListUrbsPending, pIter, pIterNext, USBPROXYURBUSBIP, NodeList) + { + RTListNodeRemove(&pIter->NodeList); + rc = usbProxyUsbIpUrbQueueWorker(pProxyDevUsbIp, pIter); + if (RT_FAILURE(rc)) + { + /* Complete URB with an error and place into landed list. */ + pIter->pVUsbUrb->enmStatus = VUSBSTATUS_DNR; + usbProxyUsbIpLinkUrb(pProxyDevUsbIp, &pProxyDevUsbIp->ListUrbsLanded, pIter); + } + } + + return VINF_SUCCESS; +} + +/** + * Kick the reaper thread. + * + * @returns VBox status code. + * @param pProxyDevUsbIp The USB/IP proxy device data. + * @param bReason The wakeup reason. + */ +static char usbProxyReaperKick(PUSBPROXYDEVUSBIP pProxyDevUsbIp, char bReason) +{ + int rc = VINF_SUCCESS; + size_t cbWritten = 0; + + rc = RTPipeWrite(pProxyDevUsbIp->hPipeW, &bReason, 1, &cbWritten); + Assert(RT_SUCCESS(rc) || cbWritten == 0); + + return rc; +} + +/** + * Drain the wakeup pipe. + * + * @returns Wakeup reason. + * @param pProxyDevUsbIp The USB/IP proxy device data. + */ +static char usbProxyUsbIpWakeupPipeDrain(PUSBPROXYDEVUSBIP pProxyDevUsbIp) +{ + char bRead = 0; + size_t cbRead = 0; + int rc = RTPipeRead(pProxyDevUsbIp->hPipeR, &bRead, 1, &cbRead); + Assert(RT_SUCCESS(rc) && cbRead == 1); NOREF(rc); + + return bRead; +} + +/** + * Executes the poll/receive loop either until a URB is received (with an optional matching sequence number) or + * the given timeout has elapsed. + * + * @returns Pointer to the received USB/IP URB or NULL on timeout or error. + * @param pProxyDevUsbIp The USB/IP proxy device data. + * @param u32SeqNumRet The sequence number of a specific reply to return the URB for, 0 if + * any received URB is accepted. + * @param fPollWakePipe Flag whether to poll the wakeup pipe. + * @param cMillies Maximum number of milliseconds to wait for an URB to arrive. + */ +static PUSBPROXYURBUSBIP usbProxyUsbIpPollWorker(PUSBPROXYDEVUSBIP pProxyDevUsbIp, uint32_t u32SeqNumRet, + bool fPollWakePipe, RTMSINTERVAL cMillies) +{ + int rc = VINF_SUCCESS; + PUSBPROXYURBUSBIP pUrbUsbIp = NULL; + + if (!fPollWakePipe) + { + rc = RTPollSetEventsChange(pProxyDevUsbIp->hPollSet, USBIP_POLL_ID_PIPE, RTPOLL_EVT_ERROR); + AssertRC(rc); + } + + while (!pUrbUsbIp && RT_SUCCESS(rc) && cMillies) + { + uint32_t uIdReady = 0; + uint32_t fEventsRecv = 0; + RTMSINTERVAL msStart = RTTimeMilliTS(); + RTMSINTERVAL msNow; + + rc = RTPoll(pProxyDevUsbIp->hPollSet, cMillies, &fEventsRecv, &uIdReady); + Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT); + if (RT_SUCCESS(rc)) + { + msNow = RTTimeMilliTS(); + cMillies = msNow - msStart >= cMillies ? 0 : cMillies - (msNow - msStart); + + if (uIdReady == USBIP_POLL_ID_SOCKET) + { + rc = usbProxyUsbIpRecvPdu(pProxyDevUsbIp, &pUrbUsbIp); + if ( RT_SUCCESS(rc) + && pUrbUsbIp) + { + /* Link the URB into the landed list if a specifc reply is requested and the URB doesn't match. */ + if ( u32SeqNumRet != 0 + && pUrbUsbIp->u32SeqNumUrb != u32SeqNumRet) + { + usbProxyUsbIpUnlinkUrb(pProxyDevUsbIp, pUrbUsbIp); + usbProxyUsbIpLinkUrb(pProxyDevUsbIp, &pProxyDevUsbIp->ListUrbsLanded, pUrbUsbIp); + pUrbUsbIp = NULL; + } + } + } + else + { + AssertLogRelMsg(uIdReady == USBIP_POLL_ID_PIPE, ("Invalid pollset ID given\n")); + + char bReason = usbProxyUsbIpWakeupPipeDrain(pProxyDevUsbIp); + if (bReason == USBIP_REAPER_WAKEUP_REASON_QUEUE) + usbProxyUsbIpUrbsQueuePending(pProxyDevUsbIp); + else + { + Assert(bReason == USBIP_REAPER_WAKEUP_REASON_EXTERNAL); + break; + } + } + } + } + + if (!fPollWakePipe) + { + rc = RTPollSetEventsChange(pProxyDevUsbIp->hPollSet, USBIP_POLL_ID_PIPE, RTPOLL_EVT_READ); + AssertRC(rc); + } + + return pUrbUsbIp; +} + +/** + * 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; + + UsbIpReqSubmit ReqSubmit; + USBPROXYURBUSBIP UsbIpUrb; + + RT_ZERO(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); + + UsbIpUrb.u32SeqNumUrb = u32SeqNum; + UsbIpUrb.u32SeqNumUrbUnlink = 0; + UsbIpUrb.fCancelled = false; + UsbIpUrb.enmType = VUSBXFERTYPE_MSG; + UsbIpUrb.enmDir = VUSBDIRECTION_OUT; + UsbIpUrb.pVUsbUrb = NULL; + + /* Send the command. */ + rc = RTTcpWrite(pProxyDevUsbIp->hSocket, &ReqSubmit, sizeof(ReqSubmit)); + if (RT_SUCCESS(rc)) + { + usbProxyUsbIpLinkUrb(pProxyDevUsbIp, &pProxyDevUsbIp->ListUrbsInFlight, &UsbIpUrb); + PUSBPROXYURBUSBIP pUrbUsbIp = usbProxyUsbIpPollWorker(pProxyDevUsbIp, u32SeqNum, false /*fPollWakePipe*/, + 30 * RT_MS_1SEC); + Assert( !pUrbUsbIp + || pUrbUsbIp == &UsbIpUrb); /* The returned URB should point to the URB we submitted. */ + usbProxyUsbIpUnlinkUrb(pProxyDevUsbIp, &UsbIpUrb); + + if (!pUrbUsbIp) + rc = VERR_TIMEOUT; + } + + return rc; +} + + +/* + * The USB proxy device functions. + */ + +/** + * @interface_method_impl{USBPROXYBACK,pfnOpen} + */ +static DECLCALLBACK(int) usbProxyUsbIpOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress) +{ + LogFlowFunc(("pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress)); + + PUSBPROXYDEVUSBIP pDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); + int rc = VINF_SUCCESS; + + RTListInit(&pDevUsbIp->ListUrbsInFlight); + RTListInit(&pDevUsbIp->ListUrbsLanded); + RTListInit(&pDevUsbIp->ListUrbsToQueue); + pDevUsbIp->hSocket = NIL_RTSOCKET; + pDevUsbIp->hPollSet = NIL_RTPOLLSET; + pDevUsbIp->hPipeW = NIL_RTPIPE; + pDevUsbIp->hPipeR = NIL_RTPIPE; + pDevUsbIp->u32SeqNumNext = 0; + pDevUsbIp->pszHost = NULL; + pDevUsbIp->pszBusId = NULL; + usbProxyUsbIpResetRecvState(pDevUsbIp); + + rc = RTSemFastMutexCreate(&pDevUsbIp->hMtxLists); + if (RT_SUCCESS(rc)) + { + /* 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(pDevUsbIp); + } + + 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; +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnClose} + */ +static DECLCALLBACK(void) usbProxyUsbIpClose(PUSBPROXYDEV pProxyDev) +{ + int rc = VINF_SUCCESS; + LogFlowFunc(("pProxyDev = %p\n", pProxyDev)); + + PUSBPROXYDEVUSBIP pDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); + if (pDevUsbIp->hSocket != NIL_RTSOCKET) + usbProxyUsbIpDisconnect(pDevUsbIp); + + /* Destroy the pipe and pollset if necessary. */ + if (pDevUsbIp->hPollSet != NIL_RTPOLLSET) + { + 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->pszHost) + RTStrFree(pDevUsbIp->pszHost); + if (pDevUsbIp->pszBusId) + RTStrFree(pDevUsbIp->pszBusId); + + /* Clear the URB lists. */ + rc = RTSemFastMutexRequest(pDevUsbIp->hMtxLists); + AssertRC(rc); + PUSBPROXYURBUSBIP pIter; + PUSBPROXYURBUSBIP pIterNext; + 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); +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnReset} + */ +static DECLCALLBACK(int) usbProxyUsbIpReset(PUSBPROXYDEV pProxyDev, bool fResetOnLinux) +{ + LogFlowFunc(("pProxyDev = %p\n", pProxyDev)); + + int rc = VINF_SUCCESS; + PUSBPROXYDEVUSBIP pProxyDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); + VUSBSETUP Setup; + + if (fResetOnLinux) + { + Setup.bmRequestType = RT_BIT(5) | 0x03; /* Port request. */ + Setup.bRequest = 0x03; /* SET_FEATURE */ + Setup.wValue = 4; /* Port feature: Reset */ + Setup.wIndex = 0; /* Port number, irrelevant */ + Setup.wLength = 0; + rc = usbProxyUsbIpCtrlUrbExchangeSync(pProxyDevUsbIp, &Setup); + if (RT_SUCCESS(rc)) + { + pProxyDev->iActiveCfg = -1; + pProxyDev->cIgnoreSetConfigs = 2; + } + } + + return rc; +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnSetConfig} + */ +static DECLCALLBACK(int) usbProxyUsbIpSetConfig(PUSBPROXYDEV pProxyDev, int iCfg) +{ + LogFlowFunc(("pProxyDev=%s cfg=%#x\n", pProxyDev->pUsbIns->pszName, iCfg)); + + PUSBPROXYDEVUSBIP pProxyDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); + VUSBSETUP Setup; + + Setup.bmRequestType = 0; + Setup.bRequest = 0x09; + Setup.wValue = iCfg; + Setup.wIndex = 0; + Setup.wLength = 0; + return usbProxyUsbIpCtrlUrbExchangeSync(pProxyDevUsbIp, &Setup); +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnClaimInterface} + */ +static DECLCALLBACK(int) usbProxyUsbIpClaimInterface(PUSBPROXYDEV pProxyDev, int iIf) +{ + RT_NOREF(pProxyDev, iIf); + LogFlowFunc(("pProxyDev=%s iIf=%#x\n", pProxyDev->pUsbIns->pszName, iIf)); + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnReleaseInterface} + */ +static DECLCALLBACK(int) usbProxyUsbIpReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf) +{ + RT_NOREF(pProxyDev, iIf); + LogFlowFunc(("pProxyDev=%s iIf=%#x\n", pProxyDev->pUsbIns->pszName, iIf)); + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnSetInterface} + */ +static DECLCALLBACK(int) usbProxyUsbIpSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int setting) +{ + LogFlowFunc(("pProxyDev=%p iIf=%#x setting=%#x\n", pProxyDev, iIf, setting)); + + PUSBPROXYDEVUSBIP pProxyDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); + VUSBSETUP Setup; + + Setup.bmRequestType = 0x1; + Setup.bRequest = 0x0b; /* SET_INTERFACE */ + Setup.wValue = setting; + Setup.wIndex = iIf; + Setup.wLength = 0; + return usbProxyUsbIpCtrlUrbExchangeSync(pProxyDevUsbIp, &Setup); +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnClearHaltedEndpoint} + */ +static DECLCALLBACK(int) usbProxyUsbIpClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int iEp) +{ + LogFlowFunc(("pProxyDev=%s ep=%u\n", pProxyDev->pUsbIns->pszName, iEp)); + + PUSBPROXYDEVUSBIP pProxyDevUsbIp = 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(pProxyDevUsbIp, &Setup); +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnUrbQueue} + */ +static DECLCALLBACK(int) usbProxyUsbIpUrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb) +{ + LogFlowFunc(("pUrb=%p\n", pUrb)); + + PUSBPROXYDEVUSBIP pProxyDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); + + /* Allocate a USB/IP Urb. */ + PUSBPROXYURBUSBIP pUrbUsbIp = usbProxyUsbIpUrbAlloc(pProxyDevUsbIp); + if (!pUrbUsbIp) + return VERR_NO_MEMORY; + + pUrbUsbIp->fCancelled = false; + pUrbUsbIp->pVUsbUrb = pUrb; + pUrb->Dev.pvPrivate = pUrbUsbIp; + + int rc = RTSemFastMutexRequest(pProxyDevUsbIp->hMtxLists); + AssertRC(rc); + RTListAppend(&pProxyDevUsbIp->ListUrbsToQueue, &pUrbUsbIp->NodeList); + RTSemFastMutexRelease(pProxyDevUsbIp->hMtxLists); + + return usbProxyReaperKick(pProxyDevUsbIp, USBIP_REAPER_WAKEUP_REASON_QUEUE); +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnUrbReap} + */ +static DECLCALLBACK(PVUSBURB) usbProxyUsbIpUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies) +{ + LogFlowFunc(("pProxyDev=%s\n", pProxyDev->pUsbIns->pszName)); + + PUSBPROXYDEVUSBIP pProxyDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); + PUSBPROXYURBUSBIP pUrbUsbIp = NULL; + PVUSBURB pUrb = NULL; + int rc = VINF_SUCCESS; + + /* Queue new URBs first. */ + rc = usbProxyUsbIpUrbsQueuePending(pProxyDevUsbIp); + AssertRC(rc); + + /* Any URBs pending delivery? */ + if (!RTListIsEmpty(&pProxyDevUsbIp->ListUrbsLanded)) + pUrbUsbIp = RTListGetFirst(&pProxyDevUsbIp->ListUrbsLanded, USBPROXYURBUSBIP, NodeList); + else + pUrbUsbIp = usbProxyUsbIpPollWorker(pProxyDevUsbIp, 0, true /*fPollWakePipe*/, cMillies); + + if (pUrbUsbIp) + { + pUrb = pUrbUsbIp->pVUsbUrb; + pUrb->enmStatus = pUrbUsbIp->enmStatus; + + /* unlink from the pending delivery list */ + usbProxyUsbIpUnlinkUrb(pProxyDevUsbIp, pUrbUsbIp); + usbProxyUsbIpUrbFree(pProxyDevUsbIp, pUrbUsbIp); + } + + return pUrb; +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnUrbCancel} + */ +static DECLCALLBACK(int) usbProxyUsbIpUrbCancel(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb) +{ + LogFlowFunc(("pUrb=%p\n", pUrb)); + + PUSBPROXYDEVUSBIP pProxyDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); + PUSBPROXYURBUSBIP pUrbUsbIp = (PUSBPROXYURBUSBIP)pUrb->Dev.pvPrivate; + UsbIpReqUnlink ReqUnlink; + + RT_ZERO(ReqUnlink); + + uint32_t u32SeqNum = usbProxyUsbIpSeqNumGet(pProxyDevUsbIp); + ReqUnlink.Hdr.u32ReqRet = USBIP_CMD_UNLINK; + ReqUnlink.Hdr.u32SeqNum = u32SeqNum; + ReqUnlink.Hdr.u32DevId = pProxyDevUsbIp->u32DevId; + ReqUnlink.Hdr.u32Direction = USBIP_DIR_OUT; + ReqUnlink.Hdr.u32Endpoint = pUrb->EndPt; + ReqUnlink.u32SeqNum = pUrbUsbIp->u32SeqNumUrb; + + usbProxyUsbIpReqUnlinkH2N(&ReqUnlink); + int rc = RTTcpWrite(pProxyDevUsbIp->hSocket, &ReqUnlink, sizeof(ReqUnlink)); + if (RT_SUCCESS(rc)) + { + pUrbUsbIp->u32SeqNumUrbUnlink = u32SeqNum; + pUrbUsbIp->fCancelled = true; + } + + return rc; +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnWakeup} + */ +static DECLCALLBACK(int) usbProxyUsbIpWakeup(PUSBPROXYDEV pProxyDev) +{ + LogFlowFunc(("pProxyDev=%s\n", pProxyDev->pUsbIns->pszName)); + + PUSBPROXYDEVUSBIP pProxyDevUsbIp = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVUSBIP); + return usbProxyReaperKick(pProxyDevUsbIp, USBIP_REAPER_WAKEUP_REASON_EXTERNAL); +} + + +/** + * 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 +}; |