summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/USB/os2/USBProxyDevice-os2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/USB/os2/USBProxyDevice-os2.cpp')
-rw-r--r--src/VBox/Devices/USB/os2/USBProxyDevice-os2.cpp879
1 files changed, 879 insertions, 0 deletions
diff --git a/src/VBox/Devices/USB/os2/USBProxyDevice-os2.cpp b/src/VBox/Devices/USB/os2/USBProxyDevice-os2.cpp
new file mode 100644
index 00000000..3b1997e2
--- /dev/null
+++ b/src/VBox/Devices/USB/os2/USBProxyDevice-os2.cpp
@@ -0,0 +1,879 @@
+/* $Id: USBProxyDevice-os2.cpp $ */
+/** @file
+ * USB device proxy - the Linux backend.
+ */
+
+/*
+ * Copyright (C) 2006-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
+ */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
+#include <VBox/vmm/pdm.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/stream.h>
+#include <iprt/alloc.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <iprt/asm.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/file.h>
+#include "../USBProxyDevice.h"
+
+#define INCL_BASE
+#define INCL_ERRORS
+#include <os2.h>
+#include <usbcalls.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Structure for keeping track of the URBs for a device.
+ */
+typedef struct USBPROXYURBOS2
+{
+ /** Pointer to the virtual URB. */
+ PVUSBURB pUrb;
+ /** Pointer to the next OS/2 URB. */
+ struct USBPROXYURBOS2 *pNext;
+ /** Pointer to the previous OS/2 URB. */
+ struct USBPROXYURBOS2 *pPrev;
+} USBPROXYURBOS2, *PUSBPROXYURBOS2;
+
+/**
+ * Data for the OS/2 usb proxy backend.
+ */
+typedef struct USBPROXYDEVOS2
+{
+ /** The async thread for this device.
+ * Currently only one thread is used, but this might have to change... */
+ RTTHREAD Thread;
+ /** Thread termination indicator. */
+ bool volatile fTerminate;
+ /** The USB handle. */
+ USBHANDLE hDevice;
+ /** Critical section protecting the lists. */
+ RTCRITSECT CritSect;
+ /** For blocking reap calls. */
+ RTSEMEVENT EventSyncWait;
+ /** List of URBs to process. Doubly linked. */
+ PUSBPROXYURBOS2 pTodoHead;
+ /** The tail pointer. */
+ PUSBPROXYURBOS2 pTodoTail;
+ /** The list of free linux URBs. Singly linked. */
+ PUSBPROXYURBOS2 pFreeHead;
+ /** The list of active linux URBs. Doubly linked.
+ * We must maintain this so we can properly reap URBs of a detached device.
+ * Only the split head will appear in this list. */
+ PUSBPROXYURBOS2 pInFlightHead;
+ /** The list of landed linux URBs. Doubly linked.
+ * Only the split head will appear in this list. */
+ PUSBPROXYURBOS2 pTaxingHead;
+ /** The tail of the landed linux URBs. */
+ PUSBPROXYURBOS2 pTaxingTail;
+} USBPROXYDEVOS2, *PUSBPROXYDEVOS2;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifdef DYNAMIC_USBCALLS
+static int usbProxyOs2GlobalInit(void);
+#endif
+static PUSBPROXYURBOS2 usbProxyOs2UrbAlloc(PUSBPROXYDEV pProxyDev);
+static void usbProxyOs2UrbFree(PUSBPROXYDEV pProxyDev, PUSBPROXYURBOS2 pUrbOs2);
+static DECLCALLBACK(int) usbProxyOs2AsyncThread(RTTHREAD Thread, void *pvProxyDev);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef DYNAMIC_USBCALLS
+static HMODULE g_hmod;
+static APIRET (APIENTRY *g_pfnUsbOpen)(PUSBHANDLE, USHORT, USHORT, USHORT, USHORT);
+static APIRET (APIENTRY *g_pfnUsbClose)(USBHANDLE);
+static APIRET (APIENTRY *g_pfnUsbCtrlMessage)(USBHANDLE, UCHAR, UCHAR, USHORT, USHORT, USHORT, void *, ULONG);
+static APIRET (APIENTRY *g_pfnUsbBulkRead2)(USBHANDLE, UCHAR, UCHAR, BOOL, PULONG, void *, ULONG);
+static APIRET (APIENTRY *g_pfnUsbBulkWrite2)(USBHANDLE, UCHAR, UCHAR, BOOL, ULONG, void *, ULONG);
+#else
+# define g_pfnUsbOpen UsbOpen
+# define g_pfnUsbClose UsbClose
+# define g_pfnUsbCtrlMessage UsbCtrlMessage
+# define g_pfnUsbBulkRead2 UsbBulkRead2
+# define g_pfnUsbBulkWrite2 UsbBulkWrite2
+#endif
+
+
+
+#ifdef DYNAMIC_USBCALLS
+/**
+ * Loads usbcalls.dll and resolves the symbols we need.
+ *
+ * The usbcalls.dll will not be unloaded.
+ *
+ * @returns VBox status code.
+ */
+static int usbProxyOs2GlobalInit(void)
+{
+ int rc = DosLoadModule(NULL, 0, (PCSZ)"usbcalls", &g_hmod);
+ rc = RTErrConvertFromOS2(rc);
+ if (RT_SUCCESS(rc))
+ {
+ if ( (rc = DosQueryProcAddr(g_hmod, 0, (PCSZ)"UsbOpen", (PPFN)&g_pfnUsbOpen)) == NO_ERROR
+ && (rc = DosQueryProcAddr(g_hmod, 0, (PCSZ)"UsbClose", (PPFN)&g_pfnUsbClose)) == NO_ERROR
+ && (rc = DosQueryProcAddr(g_hmod, 0, (PCSZ)"UsbCtrlMessage", (PPFN)&g_pfnUsbCtrlMessage)) == NO_ERROR
+ && (rc = DosQueryProcAddr(g_hmod, 0, (PCSZ)"UsbBulkRead", (PPFN)&g_pfnUsbBulkRead)) == NO_ERROR
+ && (rc = DosQueryProcAddr(g_hmod, 0, (PCSZ)"UsbBulkWrite", (PPFN)&g_pfnUsbBulkWrite)) == NO_ERROR
+ )
+ {
+
+ return VINF_SUCCESS;
+ }
+
+ g_pfnUsbOpen = NULL;
+ g_pfnUsbClose = NULL;
+ g_pfnUsbCtrlMessage = NULL;
+ g_pfnUsbBulkRead = NULL;
+ g_pfnUsbBulkWrite = NULL;
+ DosFreeModule(g_hmod);
+ }
+
+ g_hmod = NULLHANDLE;
+ return rc;
+}
+#endif
+
+
+
+/**
+ * Allocates a OS/2 URB request structure.
+ * @returns Pointer to an active URB request.
+ * @returns NULL on failure.
+ * @param pProxyDev The proxy device instance.
+ */
+static PUSBPROXYURBOS2 usbProxyOs2UrbAlloc(PUSBPROXYDEV pProxyDev)
+{
+ PUSBPROXYDEVOS2 pDevOs2 = (PUSBPROXYDEVOS2)pProxyDev->Backend.pv;
+ PUSBPROXYURBOS2 pUrbOs2;
+
+ RTCritSectEnter(&pDevOs2->CritSect);
+
+ /*
+ * Try remove a linux URB from the free list, if none there allocate a new one.
+ */
+ pUrbOs2 = pDevOs2->pFreeHead;
+ if (pUrbOs2)
+ pDevOs2->pFreeHead = pUrbOs2->pNext;
+ else
+ {
+ RTCritSectLeave(&pDevOs2->CritSect);
+ pUrbOs2 = (PUSBPROXYURBOS2)RTMemAlloc(sizeof(*pUrbOs2));
+ if (!pUrbOs2)
+ return NULL;
+ RTCritSectEnter(&pDevOs2->CritSect);
+ }
+
+ /*
+ * Link it into the active list
+ */
+ pUrbOs2->pPrev = NULL;
+ pUrbOs2->pNext = pDevOs2->pInFlightHead;
+ if (pUrbOs2->pNext)
+ pUrbOs2->pNext->pPrev = pUrbOs2;
+ pDevOs2->pInFlightHead = pUrbOs2;
+
+ RTCritSectLeave(&pDevOs2->CritSect);
+ return pUrbOs2;
+}
+
+
+/**
+ * Frees a linux URB request structure.
+ *
+ * @param pProxyDev The proxy device instance.
+ * @param pUrbOs2 The linux URB to free.
+ */
+static void usbProxyOs2UrbFree(PUSBPROXYDEV pProxyDev, PUSBPROXYURBOS2 pUrbOs2)
+{
+ PUSBPROXYDEVOS2 pDevOs2 = (PUSBPROXYDEVOS2)pProxyDev->Backend.pv;
+
+ RTCritSectEnter(&pDevOs2->CritSect);
+
+ /*
+ * Remove from the active list.
+ */
+ if (pUrbOs2->pNext)
+ pUrbOs2->pNext->pPrev = pUrbOs2->pPrev;
+ else if (pDevOs2->pTaxingTail == pUrbOs2)
+ pDevOs2->pTaxingTail = pUrbOs2->pPrev;
+ else if (pDevOs2->pTodoTail == pUrbOs2)
+ pDevOs2->pTodoTail = pUrbOs2->pPrev;
+
+ if (pUrbOs2->pPrev)
+ pUrbOs2->pPrev->pNext = pUrbOs2->pNext;
+ else if (pDevOs2->pTaxingHead == pUrbOs2)
+ pDevOs2->pTaxingHead = pUrbOs2->pNext;
+ else if (pDevOs2->pInFlightHead == pUrbOs2)
+ pDevOs2->pInFlightHead = pUrbOs2->pNext;
+ else if (pDevOs2->pTodoHead == pUrbOs2)
+ pDevOs2->pTodoHead = pUrbOs2->pNext;
+
+ /*
+ * Link it into the free list.
+ */
+ pUrbOs2->pPrev = NULL;
+ pUrbOs2->pNext = pDevOs2->pFreeHead;
+ pDevOs2->pFreeHead = pUrbOs2;
+
+ RTCritSectLeave(&pDevOs2->CritSect);
+}
+
+
+/**
+ * Thread for executing the URBs asynchronously.
+ *
+ * @returns VINF_SUCCESS.
+ * @param Thread Thread handle (IPRT).
+ * @param pvProxyDev Pointer to the proxy device we're servicing.
+ */
+static DECLCALLBACK(int) usbProxyOs2AsyncThread(RTTHREAD Thread, void *pvProxyDev)
+{
+ PUSBPROXYDEV pProxyDev = (PUSBPROXYDEV)pvProxyDev;
+ PUSBPROXYDEVOS2 pDevOs2 = (PUSBPROXYDEVOS2)pProxyDev->Backend.pv;
+ size_t cbLow = 0;
+ void *pvLow = NULL;
+
+
+ /*
+ * The main loop.
+ *
+ * We're always in the critsect, except when waiting or submitting a URB.
+ */
+ int rc = RTCritSectEnter(&pDevOs2->CritSect); AssertRC(rc);
+
+ while (!pDevOs2->fTerminate)
+ {
+ /*
+ * Anything to do?
+ */
+ PUSBPROXYURBOS2 pUrbOs2 = pDevOs2->pTodoHead;
+ if (pUrbOs2)
+ {
+ pDevOs2->pTodoHead = pUrbOs2->pNext;
+ if (pUrbOs2->pNext)
+ pUrbOs2->pNext->pPrev = NULL;
+ else
+ pDevOs2->pTodoTail = NULL;
+
+ /*
+ * Move it to the in-flight list and submit it.
+ */
+ pUrbOs2->pPrev = NULL;
+ pUrbOs2->pNext = pDevOs2->pInFlightHead;
+ if (pDevOs2->pInFlightHead)
+ pDevOs2->pInFlightHead->pPrev = pUrbOs2;
+ //else
+ // pDevOs2->pInFlightTail = pUrbOs2;
+ Log3(("%s: usbProxyOs2AsyncThread: pPickup\n", pUrbOs2->pUrb->pszDesc));
+
+ RTCritSectLeave(&pDevOs2->CritSect);
+
+ /*
+ * Process the URB.
+ */
+ PVUSBURB pUrb = pUrbOs2->pUrb;
+ uint8_t *pbData = &pUrb->abData[0];
+ ULONG cbData = pUrb->cbData;
+ if ( (uintptr_t)pbData >= 0x20000000
+ || ((uintptr_t)pbData & 0xfff))
+ {
+ if (cbData > cbLow)
+ {
+ if (pvLow)
+ DosFreeMem(pvLow);
+ cbLow = (cbData + 0xffff) & ~0xffff;
+ rc = DosAllocMem(&pvLow, cbLow, PAG_WRITE | PAG_READ | OBJ_TILE | PAG_COMMIT);
+ if (rc)
+ {
+ cbLow = 0;
+ pvLow = NULL;
+ }
+ }
+ if (pvLow)
+ pbData = (uint8_t *)memcpy(pvLow, pbData, cbData);
+ }
+
+ switch (pUrb->enmType)
+ {
+ case VUSBXFERTYPE_MSG:
+ {
+ PVUSBSETUP pSetup = (PVUSBSETUP)&pbData[0];
+ Log2(("%s: usbProxyOs2AsyncThread: CtlrMsg\n", pUrb->pszDesc));
+ rc = g_pfnUsbCtrlMessage(pDevOs2->hDevice, /** @todo this API must take a endpoint number! */
+ pSetup->bmRequestType,
+ pSetup->bRequest,
+ pSetup->wValue,
+ pSetup->wIndex,
+ pSetup->wLength,
+ pSetup + 1,
+ 5*60000 /* min */);
+ break;
+ }
+
+ case VUSBXFERTYPE_BULK:
+ {
+ /* there is a thing with altnative interface thing here... */
+
+ if (pUrb->enmDir == VUSBDIRECTION_IN)
+ {
+ Log2(("%s: usbProxyOs2AsyncThread: BulkRead %d\n", pUrb->pszDesc, cbData));
+ rc = g_pfnUsbBulkRead2(pDevOs2->hDevice, pUrb->EndPt | 0x80, 0, !pUrb->fShortNotOk, &cbData, pbData, 500);//5*6000);
+ }
+ else
+ {
+ Log2(("%s: usbProxyOs2AsyncThread: BulkWrite %d\n", pUrb->pszDesc, cbData));
+ rc = g_pfnUsbBulkWrite2(pDevOs2->hDevice, pUrb->EndPt, 0, !pUrb->fShortNotOk, cbData, pbData, 500);//5*6000);
+ }
+ break;
+ }
+
+ case VUSBXFERTYPE_INTR:
+ case VUSBXFERTYPE_ISOC:
+ default:
+ Log2(("%s: usbProxyOs2AsyncThread: Unsupported\n", pUrb->pszDesc));
+ rc = USB_IORB_FAILED;
+ break;
+ }
+
+ /* unbuffer */
+ if (pbData == pvLow)
+ memcpy(pUrb->abData, pbData, pUrb->cbData);
+
+ /* Convert rc to USB status code. */
+ int orc = rc;
+ if (!rc)
+ pUrb->enmStatus = VUSBSTATUS_OK;
+ else if (rc == USB_ERROR_LESSTRANSFERED && !pUrb->fShortNotOk)
+ {
+ Assert(pUrb->cbData >= cbData);
+ pUrb->cbData = cbData;
+ pUrb->enmStatus = VUSBSTATUS_DATA_UNDERRUN;
+ }
+ else
+ pUrb->enmStatus = VUSBSTATUS_STALL;
+ Log2(("%s: usbProxyOs2AsyncThread: orc=%d enmStatus=%d cbData=%d \n", pUrb->pszDesc, orc, pUrb->enmStatus, pUrb->cbData)); NOREF(orc);
+
+ /*
+ * Retire it to the completed list
+ */
+ RTCritSectEnter(&pDevOs2->CritSect);
+
+ pUrbOs2->pNext = NULL;
+ pUrbOs2->pPrev = pDevOs2->pTaxingTail;
+ if (pDevOs2->pTaxingTail)
+ pDevOs2->pTaxingTail->pNext = pUrbOs2;
+ else
+ pDevOs2->pTaxingHead = pUrbOs2;
+ pDevOs2->pTaxingTail = pUrbOs2;
+
+ RTSemEventSignal(pDevOs2->EventSyncWait);
+ Log2(("%s: usbProxyOs2AsyncThread: orc=%d enmStatus=%d cbData=%d!\n", pUrb->pszDesc, orc, pUrb->enmStatus, pUrb->cbData)); NOREF(orc);
+ }
+ else
+ {
+ RTThreadUserReset(Thread);
+ RTCritSectLeave(&pDevOs2->CritSect);
+
+ /*
+ * Wait for something to do.
+ */
+ RTThreadUserWait(Thread, 30*1000 /* 30 sec */);
+
+ RTCritSectEnter(&pDevOs2->CritSect);
+ }
+ }
+
+ RTCritSectLeave(&pDevOs2->CritSect);
+ if (pvLow)
+ DosFreeMem(pvLow);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Opens the /proc/bus/usb/bus/addr file.
+ *
+ * @returns VBox status code.
+ * @param pProxyDev The device instance.
+ * @param pszAddress The path to the device.
+ */
+static int usbProxyOs2Open(PUSBPROXYDEV pProxyDev, const char *pszAddress)
+{
+ LogFlow(("usbProxyOs2Open: pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress));
+ int rc;
+
+ /*
+ * Lazy init.
+ */
+#ifdef DYNAMIC_USBCALLS
+ if (!g_pfnUsbOpen)
+ {
+ rc = usbProxyOs2GlobalInit();
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+#else
+static bool g_fInitialized = false;
+ if (!g_fInitialized)
+ {
+ rc = InitUsbCalls();
+ if (rc != NO_ERROR)
+ return RTErrConvertFromOS2(rc);
+ g_fInitialized = true;
+ }
+#endif
+
+ /*
+ * Parse out the open parameters from the address string.
+ */
+ uint16_t idProduct = 0;
+ uint16_t idVendor = 0;
+ uint16_t bcdDevice = 0;
+ uint32_t iEnum = 0;
+ const char *psz = pszAddress;
+ do
+ {
+ const char chValue = *psz;
+ AssertReleaseReturn(psz[1] == '=', VERR_INTERNAL_ERROR);
+ uint64_t u64Value;
+ int rc = RTStrToUInt64Ex(psz + 2, (char **)&psz, 0, &u64Value);
+ AssertReleaseRCReturn(rc, rc);
+ AssertReleaseReturn(!*psz || *psz == ';', rc);
+ switch (chValue)
+ {
+ case 'p': idProduct = (uint16_t)u64Value; break;
+ case 'v': idVendor = (uint16_t)u64Value; break;
+ case 'r': bcdDevice = (uint16_t)u64Value; break;
+ case 'e': iEnum = (uint16_t)u64Value; break;
+ default:
+ AssertReleaseMsgFailedReturn(("chValue=%#x\n", chValue), VERR_INTERNAL_ERROR);
+ }
+ if (*psz == ';')
+ psz++;
+ } while (*psz);
+
+
+ /*
+ * Try open (acquire) it.
+ */
+ USBHANDLE hDevice = 0;
+ int urc = rc = g_pfnUsbOpen(&hDevice, idVendor, idProduct, bcdDevice, iEnum);
+ if (!rc)
+ {
+ /*
+ * Allocate and initialize the OS/2 backend data.
+ */
+ PUSBPROXYDEVOS2 pDevOs2 = (PUSBPROXYDEVOS2)RTMemAllocZ(sizeof(*pDevOs2));
+ if (pDevOs2)
+ {
+ pDevOs2->hDevice = hDevice;
+ pDevOs2->fTerminate = false;
+ rc = RTCritSectInit(&pDevOs2->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pDevOs2->EventSyncWait);
+ if (RT_SUCCESS(rc))
+ {
+ pProxyDev->Backend.pv = pDevOs2;
+
+ /** @todo
+ * Determine the active configuration.
+ */
+ //pProxyDev->cIgnoreSetConfigs = 1;
+ //pProxyDev->iActiveCfg = 1;
+ pProxyDev->cIgnoreSetConfigs = 0;
+ pProxyDev->iActiveCfg = -1;
+
+ /*
+ * Create the async worker thread and we're done.
+ */
+ rc = RTThreadCreate(&pDevOs2->Thread, usbProxyOs2AsyncThread, pProxyDev, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "usbproxy");
+ if (RT_SUCCESS(rc))
+ {
+ LogFlow(("usbProxyOs2Open(%p, %s): returns successfully - iActiveCfg=%d\n",
+ pProxyDev, pszAddress, pProxyDev->iActiveCfg));
+ return VINF_SUCCESS;
+ }
+
+ /* failure */
+ RTSemEventDestroy(pDevOs2->EventSyncWait);
+ }
+ RTCritSectDelete(&pDevOs2->CritSect);
+ }
+ RTMemFree(pDevOs2);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ g_pfnUsbClose(hDevice);
+ }
+ else
+ rc = VERR_VUSB_USBFS_PERMISSION; /** @todo fix me */
+
+ Log(("usbProxyOs2Open(%p, %s) failed, rc=%Rrc! urc=%d\n", pProxyDev, pszAddress, rc, urc)); NOREF(urc);
+ pProxyDev->Backend.pv = NULL;
+
+ NOREF(pvBackend);
+ return rc;
+}
+
+
+/**
+ * Closes the proxy device.
+ */
+static void usbProxyOs2Close(PUSBPROXYDEV pProxyDev)
+{
+ LogFlow(("usbProxyOs2Close: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
+ PUSBPROXYDEVOS2 pDevOs2 = (PUSBPROXYDEVOS2)pProxyDev->Backend.pv;
+ Assert(pDevOs2);
+ if (!pDevOs2)
+ return;
+
+ /*
+ * Tell the thread to terminate.
+ */
+ ASMAtomicXchgBool(&pDevOs2->fTerminate, true);
+ int rc = RTThreadUserSignal(pDevOs2->Thread); AssertRC(rc);
+ rc = RTThreadWait(pDevOs2->Thread, 60*1000 /* 1 min */, NULL); AssertRC(rc);
+
+ /*
+ * Now we can free all the resources and close the device.
+ */
+ RTCritSectDelete(&pDevOs2->CritSect);
+ RTSemEventDestroy(pDevOs2->EventSyncWait);
+
+ Assert(!pDevOs2->pInFlightHead);
+ Assert(!pDevOs2->pTodoHead);
+ Assert(!pDevOs2->pTodoTail);
+ Assert(!pDevOs2->pTaxingHead);
+ Assert(!pDevOs2->pTaxingTail);
+
+ PUSBPROXYURBOS2 pUrbOs2;
+ while ((pUrbOs2 = pDevOs2->pFreeHead) != NULL)
+ {
+ pDevOs2->pFreeHead = pUrbOs2->pNext;
+ RTMemFree(pUrbOs2);
+ }
+
+ g_pfnUsbClose(pDevOs2->hDevice);
+ pDevOs2->hDevice = 0;
+
+ RTMemFree(pDevOs2);
+ pProxyDev->Backend.pv = NULL;
+ LogFlow(("usbProxyOs2Close: returns\n"));
+}
+
+
+/** @interface_method_impl{USBPROXYBACK,pfnReset} */
+static int usbProxyOs2Reset(PUSBPROXYDEV pProxyDev, bool fResetOnLinux)
+{
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * SET_CONFIGURATION.
+ *
+ * The caller makes sure that it's not called first time after open or reset
+ * with the active interface.
+ *
+ * @returns success indicator.
+ * @param pProxyDev The device instance data.
+ * @param iCfg The configuration to set.
+ */
+static int usbProxyOs2SetConfig(PUSBPROXYDEV pProxyDev, int iCfg)
+{
+ PUSBPROXYDEVOS2 pDevOs2 = (PUSBPROXYDEVOS2)pProxyDev->Backend.pv;
+ LogFlow(("usbProxyOs2SetConfig: pProxyDev=%s cfg=%#x\n",
+ pProxyDev->pUsbIns->pszName, iCfg));
+
+ /*
+ * This is sync - bad.
+ */
+ int rc = g_pfnUsbCtrlMessage(pDevOs2->hDevice,
+ 0x00, /* bmRequestType - ?? */
+ 0x09, /* bRequest - ?? */
+ iCfg, /* wValue - configuration */
+ 0, /* wIndex*/
+ 0, /* wLength */
+ NULL, /* pvData */
+ 50 /* Timeout (ms) */);
+ if (rc)
+ LogFlow(("usbProxyOs2SetConfig: pProxyDev=%s cfg=%#X -> rc=%d\n", pProxyDev->pUsbIns->pszName, iCfg, rc));
+ return rc == 0;
+}
+
+
+/**
+ * Claims an interface.
+ * @returns success indicator.
+ */
+static int usbProxyOs2ClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
+{
+ LogFlow(("usbProxyOs2ClaimInterface: pProxyDev=%s ifnum=%#x\n", pProxyDev->pUsbIns->pszName, iIf));
+ return true;
+}
+
+
+/**
+ * Releases an interface.
+ * @returns success indicator.
+ */
+static int usbProxyOs2ReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
+{
+ LogFlow(("usbProxyOs2ReleaseInterface: pProxyDev=%s ifnum=%#x\n", pProxyDev->pUsbIns->pszName, iIf));
+ return true;
+}
+
+
+/**
+ * SET_INTERFACE.
+ *
+ * @returns success indicator.
+ */
+static int usbProxyOs2SetInterface(PUSBPROXYDEV pProxyDev, int iIf, int iAlt)
+{
+ LogFlow(("usbProxyOs2SetInterface: pProxyDev=%p iIf=%#x iAlt=%#x\n", pProxyDev, iIf, iAlt));
+ return true;
+}
+
+
+/**
+ * Clears the halted endpoint 'EndPt'.
+ */
+static bool usbProxyOs2ClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int EndPt)
+{
+ PUSBPROXYDEVOS2 pDevOs2 = (PUSBPROXYDEVOS2)pProxyDev->Backend.pv;
+ LogFlow(("usbProxyOs2ClearHaltedEp: pProxyDev=%s EndPt=%x\n", pProxyDev->pUsbIns->pszName, EndPt));
+
+ /*
+ * This is sync - bad.
+ */
+ int rc = g_pfnUsbCtrlMessage(pDevOs2->hDevice,
+ 0x02, /* bmRequestType - ?? */
+ 0x01, /* bRequest - ?? */
+ 0, /* wValue - endpoint halt */
+ EndPt, /* wIndex - endpoint # */
+ 0, /* wLength */
+ NULL, /* pvData */
+ 50 /* Timeout (ms) */);
+ if (rc)
+ LogFlow(("usbProxyOs2ClearHaltedEp: pProxyDev=%s EndPt=%u -> rc=%d\n", pProxyDev->pUsbIns->pszName, EndPt, rc));
+ return rc == 0;
+}
+
+
+/**
+ * @interface_method_impl{USBPROXYBACK,pfnUrbQueue}
+ */
+static int usbProxyOs2UrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
+{
+ PUSBPROXYDEVOS2 pDevOs2 = (PUSBPROXYDEVOS2)pProxyDev->Backend.pv;
+ LogFlow(("usbProxyOs2UrbQueue: pProxyDev=%s pUrb=%p EndPt=%d cbData=%d\n",
+ pProxyDev->pUsbIns->pszName, pUrb, pUrb->EndPt, pUrb->cbData));
+
+ /*
+ * Quickly validate the input.
+ */
+ switch (pUrb->enmDir)
+ {
+ case VUSBDIRECTION_IN:
+ case VUSBDIRECTION_OUT:
+ break;
+ default:
+ AssertMsgFailed(("usbProxyOs2UrbQueue: Invalid direction %d\n", pUrb->enmDir));
+ return false;
+ }
+
+ switch (pUrb->enmType)
+ {
+ case VUSBXFERTYPE_MSG:
+ break;
+ case VUSBXFERTYPE_BULK:
+ break;
+/// @todo case VUSBXFERTYPE_INTR:
+// break;
+// case VUSBXFERTYPE_ISOC:
+// break;
+ default:
+ return false;
+ }
+
+ /*
+ * Allocate an OS/2 urb tracking structure, initialize it,
+ * add it to the todo list, and wake up the async thread.
+ */
+ PUSBPROXYURBOS2 pUrbOs2 = usbProxyOs2UrbAlloc(pProxyDev);
+ if (!pUrbOs2)
+ return false;
+
+ pUrbOs2->pUrb = pUrb;
+
+ RTCritSectEnter(&pDevOs2->CritSect);
+
+ pUrbOs2->pNext = NULL;
+ pUrbOs2->pPrev = pDevOs2->pTodoTail;
+ if (pDevOs2->pTodoTail)
+ pDevOs2->pTodoTail->pNext = pUrbOs2;
+ else
+ pDevOs2->pTodoHead = pUrbOs2;
+ pDevOs2->pTodoTail = pUrbOs2;
+
+ RTCritSectLeave(&pDevOs2->CritSect);
+
+ RTThreadUserSignal(pDevOs2->Thread);
+ return true;
+}
+
+
+/**
+ * Reap URBs in-flight on a device.
+ *
+ * @returns Pointer to a completed URB.
+ * @returns NULL if no URB was completed.
+ * @param pProxyDev The device.
+ * @param cMillies Number of milliseconds to wait. Use 0 to not wait at all.
+ */
+static PVUSBURB usbProxyOs2UrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
+{
+ PVUSBURB pUrb = NULL;
+ PUSBPROXYDEVOS2 pDevOs2 = (PUSBPROXYDEVOS2)pProxyDev->Backend.pv;
+
+ RTCritSectEnter(&pDevOs2->CritSect);
+ for (;;)
+ {
+ /*
+ * Any URBs pending delivery?
+ */
+ PUSBPROXYURBOS2 pUrbOs2 = pDevOs2->pTaxingHead;
+ if (pUrbOs2)
+ {
+ pUrb = pUrbOs2->pUrb;
+ usbProxyOs2UrbFree(pProxyDev, pUrbOs2);
+ break;
+ }
+
+ /*
+ * Block for something to completed, if requested and sensible.
+ */
+ if (!cMillies)
+ break;
+ if ( !pDevOs2->pInFlightHead
+ && !pDevOs2->pTodoHead)
+ break;
+
+ RTCritSectLeave(&pDevOs2->CritSect);
+
+ int rc = RTSemEventWait(pDevOs2->EventSyncWait, cMillies);
+ Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT); NOREF(rc);
+ cMillies = 0;
+
+ RTCritSectEnter(&pDevOs2->CritSect);
+ }
+ RTCritSectLeave(&pDevOs2->CritSect);
+
+ LogFlow(("usbProxyOs2UrbReap: dev=%s returns %p\n", pProxyDev->pUsbIns->pszName, pUrb));
+ return pUrb;
+}
+
+
+/**
+ * Cancels the URB.
+ * The URB requires reaping, so we don't change its state.
+ */
+static void usbProxyOs2UrbCancel(PVUSBURB pUrb)
+{
+#if 0
+ PUSBPROXYDEV pProxyDev = (PUSBPROXYDEV)pUrb->pDev;
+ PUSBPROXYURBOS2 pUrbOs2 = (PUSBPROXYURBOS2)pUrb->Dev.pvProxyUrb;
+ if (pUrbOs2->pSplitHead)
+ {
+ /* split */
+ Assert(pUrbOs2 == pUrbOs2->pSplitHead);
+ for (PUSBPROXYURBOS2 pCur = pUrbOs2; pCur; pCur = pCur->pSplitNext)
+ {
+ if (pCur->fSplitElementReaped)
+ continue;
+ if ( !usbProxyOs2DoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pCur->KUrb, true, UINT32_MAX)
+ || errno == ENOENT)
+ continue;
+ if (errno == ENODEV)
+ break;
+ Log(("usb-linux: Discard URB %p failed, errno=%d. pProxyDev=%s!!! (split)\n",
+ pUrb, errno, pProxyDev->pUsbIns->pszName));
+ }
+ }
+ else
+ {
+ /* unsplit */
+ if ( usbProxyOs2DoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pUrbOs2->KUrb, true, UINT32_MAX)
+ && errno != ENODEV /* deal with elsewhere. */
+ && errno != ENOENT)
+ Log(("usb-linux: Discard URB %p failed, errno=%d. pProxyDev=%s!!!\n",
+ pUrb, errno, pProxyDev->pUsbIns->pszName));
+ }
+#endif
+}
+
+
+/**
+ * The Linux USB Proxy Backend.
+ */
+extern const USBPROXYBACK g_USBProxyDeviceHost =
+{
+ "host",
+ usbProxyOs2Open,
+ NULL,
+ usbProxyOs2Close,
+ usbProxyOs2Reset,
+ usbProxyOs2SetConfig,
+ usbProxyOs2ClaimInterface,
+ usbProxyOs2ReleaseInterface,
+ usbProxyOs2SetInterface,
+ usbProxyOs2ClearHaltedEp,
+ usbProxyOs2UrbQueue,
+ usbProxyOs2UrbCancel,
+ usbProxyOs2UrbReap,
+ 0
+};
+