summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/USB/usbip
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
commit16f504a9dca3fe3b70568f67b7d41241ae485288 (patch)
treec60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Devices/USB/usbip
parentInitial commit. (diff)
downloadvirtualbox-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.kup0
-rw-r--r--src/VBox/Devices/USB/usbip/USBProxyDevice-usbip.cpp1818
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
+};