diff options
Diffstat (limited to 'src/VBox/Devices/USB/solaris/USBProxyDevice-solaris.cpp')
-rw-r--r-- | src/VBox/Devices/USB/solaris/USBProxyDevice-solaris.cpp | 918 |
1 files changed, 918 insertions, 0 deletions
diff --git a/src/VBox/Devices/USB/solaris/USBProxyDevice-solaris.cpp b/src/VBox/Devices/USB/solaris/USBProxyDevice-solaris.cpp new file mode 100644 index 00000000..54e130d2 --- /dev/null +++ b/src/VBox/Devices/USB/solaris/USBProxyDevice-solaris.cpp @@ -0,0 +1,918 @@ +/* $Id: USBProxyDevice-solaris.cpp $ */ +/** @file + * USB device proxy - the Solaris backend. + */ + +/* + * Copyright (C) 2009-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 <sys/poll.h> +#include <errno.h> +#include <strings.h> +#include <limits.h> + +#include <VBox/log.h> +#include <VBox/err.h> +#include <VBox/vmm/pdm.h> + +#include <iprt/string.h> +#include <iprt/critsect.h> +#include <iprt/time.h> +#include <iprt/file.h> +#include <iprt/mem.h> +#include <iprt/pipe.h> +#include "../USBProxyDevice.h" +#include <VBox/usblib.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Log Prefix. */ +#define USBPROXY "USBProxy" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Wrapper around the solaris urb request structure. + * This is required to track in-flight and landed URBs. + */ +typedef struct USBPROXYURBSOL +{ + /** Pointer to the Solaris device. */ + struct USBPROXYDEVSOL *pDevSol; + /** Pointer to the VUSB URB (set to NULL if canceled). */ + PVUSBURB pVUsbUrb; + /** Pointer to the next solaris URB. */ + struct USBPROXYURBSOL *pNext; + /** Pointer to the previous solaris URB. */ + struct USBPROXYURBSOL *pPrev; +} USBPROXYURBSOL, *PUSBPROXYURBSOL; + +/** + * Data for the solaris usb proxy backend. + */ +typedef struct USBPROXYDEVSOL +{ + /** Path of the USB device in the devices tree (persistent). */ + char *pszDevicePath; + /** The connection to the client driver. */ + RTFILE hFile; + /** Pointer to the proxy device instance. */ + PUSBPROXYDEV pProxyDev; + /** Critical section protecting the two lists. */ + RTCRITSECT CritSect; + /** The list of free solaris URBs. Singly linked. */ + PUSBPROXYURBSOL pFreeHead; + /** The list of active solaris 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. */ + PUSBPROXYURBSOL pInFlightHead; + /** The list of landed solaris URBs. Doubly linked. + * Only the split head will appear in this list. */ + PUSBPROXYURBSOL pTaxingHead; + /** The tail of the landed solaris URBs. */ + PUSBPROXYURBSOL pTaxingTail; + /** Pipe handle for waking up - writing end. */ + RTPIPE hPipeWakeupW; + /** Pipe handle for waking up - reading end. */ + RTPIPE hPipeWakeupR; +} USBPROXYDEVSOL, *PUSBPROXYDEVSOL; + +static PVUSBURB usbProxySolarisUrbComplete(PUSBPROXYDEVSOL pDevSol); + + +/** + * Allocates a Solaris URB request structure. + * + * @returns Pointer to an active URB request. + * @returns NULL on failure. + * + * @param pDevSol The solaris USB device. + */ +static PUSBPROXYURBSOL usbProxySolarisUrbAlloc(PUSBPROXYDEVSOL pDevSol) +{ + PUSBPROXYURBSOL pUrbSol; + + RTCritSectEnter(&pDevSol->CritSect); + + /* + * Try remove a Solaris URB from the free list, if none there allocate a new one. + */ + pUrbSol = pDevSol->pFreeHead; + if (pUrbSol) + pDevSol->pFreeHead = pUrbSol->pNext; + else + { + RTCritSectLeave(&pDevSol->CritSect); + pUrbSol = (PUSBPROXYURBSOL)RTMemAlloc(sizeof(*pUrbSol)); + if (!pUrbSol) + return NULL; + RTCritSectEnter(&pDevSol->CritSect); + } + pUrbSol->pVUsbUrb = NULL; + pUrbSol->pDevSol = pDevSol; + + /* + * Link it into the active list + */ + pUrbSol->pPrev = NULL; + pUrbSol->pNext = pDevSol->pInFlightHead; + if (pUrbSol->pNext) + pUrbSol->pNext->pPrev = pUrbSol; + pDevSol->pInFlightHead = pUrbSol; + + RTCritSectLeave(&pDevSol->CritSect); + return pUrbSol; +} + + +/** + * Frees a Solaris URB request structure. + * + * @param pDevSol The Solaris USB device. + * @param pUrbSol The Solaris URB to free. + */ +static void usbProxySolarisUrbFree(PUSBPROXYDEVSOL pDevSol, PUSBPROXYURBSOL pUrbSol) +{ + RTCritSectEnter(&pDevSol->CritSect); + + /* + * Remove from the active or taxing list. + */ + if (pUrbSol->pNext) + pUrbSol->pNext->pPrev = pUrbSol->pPrev; + else if (pDevSol->pTaxingTail == pUrbSol) + pDevSol->pTaxingTail = pUrbSol->pPrev; + + if (pUrbSol->pPrev) + pUrbSol->pPrev->pNext = pUrbSol->pNext; + else if (pDevSol->pTaxingHead == pUrbSol) + pDevSol->pTaxingHead = pUrbSol->pNext; + else if (pDevSol->pInFlightHead == pUrbSol) + pDevSol->pInFlightHead = pUrbSol->pNext; + else + AssertFailed(); + + /* + * Link it into the free list. + */ + pUrbSol->pPrev = NULL; + pUrbSol->pNext = pDevSol->pFreeHead; + pDevSol->pFreeHead = pUrbSol; + + pUrbSol->pVUsbUrb = NULL; + pUrbSol->pDevSol = NULL; + + RTCritSectLeave(&pDevSol->CritSect); +} + + +/* + * Close the connection to the USB client driver. + * + * This is required because our userland enumeration relies on drivers/device trees + * to recognize active devices, and hence if this device is unplugged we should no + * longer keep the client driver loaded. + */ +static void usbProxySolarisCloseFile(PUSBPROXYDEVSOL pDevSol) +{ + RTFileClose(pDevSol->hFile); + pDevSol->hFile = NIL_RTFILE; +} + + +/** + * The client driver IOCtl Wrapper function. + * + * @returns VBox status code. + * @param pDevSol The Solaris device instance. + * @param Function The Function. + * @param pvData Opaque pointer to the data. + * @param cbData Size of the data pointed to by pvData. + */ +static int usbProxySolarisIOCtl(PUSBPROXYDEVSOL pDevSol, unsigned Function, void *pvData, size_t cbData) +{ + if (RT_UNLIKELY(pDevSol->hFile == NIL_RTFILE)) + { + LogFlow((USBPROXY ":usbProxySolarisIOCtl: Connection to driver gone!\n")); + return VERR_VUSB_DEVICE_NOT_ATTACHED; + } + + VBOXUSBREQ Req; + Req.u32Magic = VBOXUSB_MAGIC; + Req.rc = -1; + Req.cbData = cbData; + Req.pvDataR3 = pvData; + + int Ret = -1; + int rc = RTFileIoCtl(pDevSol->hFile, Function, &Req, sizeof(Req), &Ret); + if (RT_SUCCESS(rc)) + { + if (RT_FAILURE(Req.rc)) + { + if (Req.rc == VERR_VUSB_DEVICE_NOT_ATTACHED) + { + pDevSol->pProxyDev->fDetached = true; + usbProxySolarisCloseFile(pDevSol); + LogRel((USBPROXY ": Command %#x failed, USB Device '%s' disconnected!\n", Function, + pDevSol->pProxyDev->pUsbIns->pszName)); + } + else + LogRel((USBPROXY ": Command %#x failed. Req.rc=%Rrc\n", Function, Req.rc)); + } + + return Req.rc; + } + + LogRel((USBPROXY ": Function %#x failed. rc=%Rrc\n", Function, rc)); + return rc; +} + + +/** + * Get the active configuration from the device. The first time this is called + * our client driver would returned the cached configuration since the device is first plugged in. + * Subsequent get configuration requests are passed on to the device. + * + * @returns VBox status code. + * @param pDevSol The Solaris device instance. + * + */ +static inline int usbProxySolarisGetActiveConfig(PUSBPROXYDEVSOL pDevSol) +{ + VBOXUSBREQ_GET_CONFIG GetConfigReq; + bzero(&GetConfigReq, sizeof(GetConfigReq)); + int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_GET_CONFIG, &GetConfigReq, sizeof(GetConfigReq)); + if (RT_SUCCESS(rc)) + { + pDevSol->pProxyDev->iActiveCfg = GetConfigReq.bConfigValue; + pDevSol->pProxyDev->cIgnoreSetConfigs = 0; + } + else + { + if (rc != VERR_VUSB_DEVICE_NOT_ATTACHED) + LogRel((USBPROXY ": Failed to get configuration. rc=%Rrc\n", rc)); + + pDevSol->pProxyDev->iActiveCfg = -1; + pDevSol->pProxyDev->cIgnoreSetConfigs = 0; + } + return rc; +} + + +/** + * Opens the USB device. + * + * @returns VBox status code. + * @param pProxyDev The device instance. + * @param pszAddress The unique device identifier. + * The format of this string is "VendorId:ProducIt:Release:StaticPath". + */ +static DECLCALLBACK(int) usbProxySolarisOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress) +{ + PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); + + LogFlowFunc((USBPROXY ":usbProxySolarisOpen: pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress)); + + /* + * Initialize our USB R3 lib. + */ + int rc = USBLibInit(); + if (RT_SUCCESS(rc)) + { + /* + * Allocate and initialize the solaris backend data. + */ + AssertCompile(PATH_MAX >= MAXPATHLEN); + char szDeviceIdent[PATH_MAX+48]; + rc = RTStrPrintf(szDeviceIdent, sizeof(szDeviceIdent), "%s", pszAddress); + if (RT_SUCCESS(rc)) + { + rc = RTCritSectInit(&pDevSol->CritSect); + if (RT_SUCCESS(rc)) + { + /* + * Create wakeup pipe. + */ + rc = RTPipeCreate(&pDevSol->hPipeWakeupR, &pDevSol->hPipeWakeupW, 0); + if (RT_SUCCESS(rc)) + { + int Instance; + char *pszDevicePath = NULL; + rc = USBLibGetClientInfo(szDeviceIdent, &pszDevicePath, &Instance); + if (RT_SUCCESS(rc)) + { + pDevSol->pszDevicePath = pszDevicePath; + + /* + * Open the client driver. + */ + RTFILE hFile; + rc = RTFileOpen(&hFile, pDevSol->pszDevicePath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); + if (RT_SUCCESS(rc)) + { + pDevSol->hFile = hFile; + pDevSol->pProxyDev = pProxyDev; + + /* + * Verify client driver version. + */ + VBOXUSBREQ_GET_VERSION GetVersionReq; + bzero(&GetVersionReq, sizeof(GetVersionReq)); + rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_GET_VERSION, &GetVersionReq, sizeof(GetVersionReq)); + if (RT_SUCCESS(rc)) + { + if ( GetVersionReq.u32Major == VBOXUSB_VERSION_MAJOR + && GetVersionReq.u32Minor >= VBOXUSB_VERSION_MINOR) + { + /* + * Try & get the current cached config from Solaris. + */ + usbProxySolarisGetActiveConfig(pDevSol); + return VINF_SUCCESS; + } + else + { + LogRel((USBPROXY ": Version mismatch, Driver v%d.%d expecting ~v%d.%d\n", GetVersionReq.u32Major, + GetVersionReq.u32Minor, VBOXUSB_VERSION_MAJOR, VBOXUSB_VERSION_MINOR)); + rc = VERR_VERSION_MISMATCH; + } + } + else + LogRel((USBPROXY ": Failed to query driver version. rc=%Rrc\n", rc)); + + RTFileClose(pDevSol->hFile); + pDevSol->hFile = NIL_RTFILE; + pDevSol->pProxyDev = NULL; + } + else + LogRel((USBPROXY ": Failed to open device. rc=%Rrc pszDevicePath=%s\n", rc, pDevSol->pszDevicePath)); + + RTStrFree(pDevSol->pszDevicePath); + pDevSol->pszDevicePath = NULL; + } + else + { + LogRel((USBPROXY ": Failed to get client info. rc=%Rrc szDeviceIdent=%s\n", rc, szDeviceIdent)); + if (rc == VERR_NOT_FOUND) + rc = VERR_OPEN_FAILED; + } + RTPipeClose(pDevSol->hPipeWakeupR); + RTPipeClose(pDevSol->hPipeWakeupW); + } + + RTCritSectDelete(&pDevSol->CritSect); + } + else + LogRel((USBPROXY ": RTCritSectInit failed. rc=%Rrc pszAddress=%s\n", rc, pszAddress)); + } + else + LogRel((USBPROXY ": RTStrAPrintf failed. rc=%Rrc pszAddress=%s\n", rc, pszAddress)); + } + else + LogRel((USBPROXY ": USBLibInit failed. rc=%Rrc\n", rc)); + + USBLibTerm(); + return rc; +} + + +/** + * Close the USB device. + * + * @param pProxyDev The device instance. + */ +static DECLCALLBACK(void) usbProxySolarisClose(PUSBPROXYDEV pProxyDev) +{ + LogFlow((USBPROXY ":usbProxySolarisClose: pProxyDev=%p\n", pProxyDev)); + + PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); + + /* Close the device (do not re-enumerate). */ + VBOXUSBREQ_CLOSE_DEVICE CloseReq; + CloseReq.ResetLevel = VBOXUSB_RESET_LEVEL_CLOSE; + usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_CLOSE_DEVICE, &CloseReq, sizeof(CloseReq)); + + pProxyDev->fDetached = true; + usbProxySolarisCloseFile(pDevSol); + + /* + * Now we can close it and free all the resources. + */ + RTCritSectDelete(&pDevSol->CritSect); + + PUSBPROXYURBSOL pUrbSol = NULL; + while ((pUrbSol = pDevSol->pInFlightHead) != NULL) + { + pDevSol->pInFlightHead = pUrbSol->pNext; + RTMemFree(pUrbSol); + } + + while ((pUrbSol = pDevSol->pFreeHead) != NULL) + { + pDevSol->pFreeHead = pUrbSol->pNext; + RTMemFree(pUrbSol); + } + + RTPipeClose(pDevSol->hPipeWakeupR); + RTPipeClose(pDevSol->hPipeWakeupW); + + RTStrFree(pDevSol->pszDevicePath); + pDevSol->pszDevicePath = NULL; + + USBLibTerm(); +} + + +/** + * Reset the device. + * + * @returns VBox status code. + * @param pProxyDev The device to reset. + * @param fRootHubReset Is this a root hub reset or device specific reset request. + */ +static DECLCALLBACK(int) usbProxySolarisReset(PUSBPROXYDEV pProxyDev, bool fRootHubReset) +{ + LogFlowFunc((USBPROXY ": usbProxySolarisReset: pProxyDev=%s fRootHubReset=%d\n", pProxyDev->pUsbIns->pszName, fRootHubReset)); + + /** Pass all resets to the device. The Trekstor USB (1.1) stick requires this to work. */ + PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); + + /* Soft reset the device. */ + VBOXUSBREQ_CLOSE_DEVICE CloseReq; + CloseReq.ResetLevel = VBOXUSB_RESET_LEVEL_SOFT; + int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_CLOSE_DEVICE, &CloseReq, sizeof(CloseReq)); + if (RT_SUCCESS(rc)) + { + /* Get the active config. Solaris USBA sets a default config. */ + usbProxySolarisGetActiveConfig(pDevSol); + } + else if (rc != VERR_VUSB_DEVICE_NOT_ATTACHED) + LogRel((USBPROXY ": usbProxySolarisReset: Failed! rc=%d\n", rc)); + + return rc; +} + + +/** + * Set the active 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 value to set. + */ +static DECLCALLBACK(int) usbProxySolarisSetConfig(PUSBPROXYDEV pProxyDev, int iCfg) +{ + LogFlowFunc((USBPROXY ": usbProxySolarisSetConfig: pProxyDev=%p iCfg=%#x\n", pProxyDev, iCfg)); + + PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); + AssertPtrReturn(pDevSol, VERR_INVALID_POINTER); + + VBOXUSBREQ_SET_CONFIG SetConfigReq; + SetConfigReq.bConfigValue = iCfg; + int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_SET_CONFIG, &SetConfigReq, sizeof(SetConfigReq)); + if ( RT_FAILURE(rc) + && rc != VERR_VUSB_DEVICE_NOT_ATTACHED) + LogRel((USBPROXY ": usbProxySolarisSetConfig: Failed! rc=%Rrc\n", rc)); + + return rc; +} + + +/** + * Claims an interface. + * + * This is a stub on Solaris since we release/claim all interfaces at + * as and when required with endpoint opens. + * + * @returns success indicator (always true). + */ +static DECLCALLBACK(int) usbProxySolarisClaimInterface(PUSBPROXYDEV pProxyDev, int iIf) +{ + return VINF_SUCCESS; +} + + +/** + * Releases an interface. + * + * This is a stub on Solaris since we release/claim all interfaces at + * as and when required with endpoint opens. + * + * @returns success indicator. + */ +static DECLCALLBACK(int) usbProxySolarisReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf) +{ + return VINF_SUCCESS; +} + + +/** + * Specify an alternate setting for the specified interface of the current configuration. + * + * @returns success indicator. + */ +static DECLCALLBACK(int) usbProxySolarisSetInterface(PUSBPROXYDEV pProxyDev, int bIf, int bAlt) +{ + LogFlowFunc((USBPROXY ": usbProxySolarisSetInterface: pProxyDev=%p bIf=%#x iAlt=%#x\n", pProxyDev, bIf, bAlt)); + + PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); + AssertPtrReturn(pDevSol, VERR_INVALID_POINTER); + + VBOXUSBREQ_SET_INTERFACE SetInterfaceReq; + SetInterfaceReq.bInterface = bIf; + SetInterfaceReq.bAlternate = bAlt; + int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_SET_INTERFACE, &SetInterfaceReq, sizeof(SetInterfaceReq)); + if ( RT_FAILURE(rc) + && rc != VERR_VUSB_DEVICE_NOT_ATTACHED) + LogRel((USBPROXY ": usbProxySolarisSetInterface: Failed! rc=%Rrc\n", rc)); + + return rc; +} + + +/** + * Clears the halted endpoint 'EndPt'. + */ +static DECLCALLBACK(int) usbProxySolarisClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int EndPt) +{ + LogFlowFunc((USBPROXY ": usbProxySolarisClearHaltedEp: pProxyDev=%p EndPt=%#x\n", pProxyDev, EndPt)); + + PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); + AssertPtrReturn(pDevSol, VERR_INVALID_POINTER); + + VBOXUSBREQ_CLEAR_EP ClearEpReq; + ClearEpReq.bEndpoint = EndPt; + int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_CLEAR_EP, &ClearEpReq, sizeof(ClearEpReq)); + if ( RT_FAILURE(rc) + && rc != VERR_VUSB_DEVICE_NOT_ATTACHED) + LogRel((USBPROXY ": usbProxySolarisClearHaltedEp: Failed! rc=%Rrc\n", rc)); + + return rc; +} + + +/** + * @interface_method_impl{USBPROXYBACK,pfnUrbQueue} + */ +static DECLCALLBACK(int) usbProxySolarisUrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb) +{ + PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); + + LogFlowFunc((USBPROXY ": usbProxySolarisUrbQueue: pProxyDev=%s pUrb=%p pszDesc=%s EndPt=%#x enmDir=%d cbData=%d pvData=%p\n", + pProxyDev->pUsbIns->pszName, pUrb, pUrb->pszDesc, pUrb->EndPt, pUrb->enmDir, pUrb->cbData, pUrb->abData)); + + PUSBPROXYURBSOL pUrbSol = usbProxySolarisUrbAlloc(pDevSol); + if (RT_UNLIKELY(!pUrbSol)) + { + LogRel((USBPROXY ": usbProxySolarisUrbQueue: Failed to allocate URB\n")); + return VERR_NO_MEMORY; + } + + pUrbSol->pVUsbUrb = pUrb; + pUrbSol->pDevSol = pDevSol; + + uint8_t EndPt = pUrb->EndPt; + if (EndPt) + EndPt |= pUrb->enmDir == VUSBDIRECTION_IN ? VUSB_DIR_TO_HOST : VUSB_DIR_TO_DEVICE; + + VBOXUSBREQ_URB UrbReq; + UrbReq.pvUrbR3 = pUrbSol; + UrbReq.bEndpoint = EndPt; + UrbReq.enmType = pUrb->enmType; + UrbReq.enmDir = pUrb->enmDir; + UrbReq.enmStatus = pUrb->enmStatus; + UrbReq.fShortOk = !pUrb->fShortNotOk; + UrbReq.cbData = pUrb->cbData; + UrbReq.pvData = &pUrb->abData[0]; + + Log6((USBPROXY ": Sending: EndPt=%#x Dir=%d cbData=%u\n", pUrb->EndPt, pUrb->enmDir, pUrb->cbData)); + + if (pUrb->enmType == VUSBXFERTYPE_ISOC) + { + UrbReq.cIsocPkts = pUrb->cIsocPkts; + for (unsigned i = 0; i < pUrb->cIsocPkts; i++) + { + UrbReq.aIsocPkts[i].cbPkt = pUrb->aIsocPkts[i].cb; + UrbReq.aIsocPkts[i].cbActPkt = 0; + UrbReq.aIsocPkts[i].enmStatus = VUSBSTATUS_INVALID; + } + } + + int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_SEND_URB, &UrbReq, sizeof(UrbReq)); + if (RT_SUCCESS(rc)) + { + if (pUrb->enmType == VUSBXFERTYPE_ISOC) + LogFlow((USBPROXY ":usbProxySolarisUrbQueue: Success cbData=%d\n", pUrb->cbData)); + pUrb->Dev.pvPrivate = pUrbSol; + return VINF_SUCCESS; + } + + if (rc != VERR_VUSB_DEVICE_NOT_ATTACHED) + { + LogRel((USBPROXY ": usbProxySolarisUrbQueue: Failed! pProxyDev=%s pUrb=%p EndPt=%#x bEndpoint=%#x enmType=%d " + "enmDir=%d cbData=%u rc=%Rrc\n", pProxyDev->pUsbIns->pszName, pUrb, pUrb->EndPt, + UrbReq.bEndpoint, pUrb->enmType, pUrb->enmDir, pUrb->cbData, rc)); + } + + return rc; +} + + +/** + * Cancels a URB. + * + * The URB requires reaping, so we don't change its state. + * @remark There isn't any way to cancel a specific asynchronous request + * on Solaris. So we just abort pending URBs on the pipe. + */ +static DECLCALLBACK(int) usbProxySolarisUrbCancel(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb) +{ + PUSBPROXYURBSOL pUrbSol = (PUSBPROXYURBSOL)pUrb->Dev.pvPrivate; + PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); + AssertPtrReturn(pDevSol, VERR_INVALID_POINTER); + + LogFlowFunc((USBPROXY ": usbProxySolarisUrbCancel: pUrb=%p pUrbSol=%p pDevSol=%p\n", pUrb, pUrbSol, pUrbSol->pDevSol)); + + /* Aborting the control pipe isn't supported, pretend success. */ + if (!pUrb->EndPt) + return VINF_SUCCESS; + + VBOXUSBREQ_ABORT_PIPE AbortPipeReq; + AbortPipeReq.bEndpoint = pUrb->EndPt | (pUrb->enmDir == VUSBDIRECTION_IN ? VUSB_DIR_TO_HOST : VUSB_DIR_TO_DEVICE); + int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_ABORT_PIPE, &AbortPipeReq, sizeof(AbortPipeReq)); + if ( RT_FAILURE(rc) + && rc != VERR_VUSB_DEVICE_NOT_ATTACHED) + LogRel((USBPROXY ": usbProxySolarisUrbCancel: Failed to abort pipe. rc=%Rrc\n", rc)); + + LogFlow((USBPROXY ": usbProxySolarisUrbCancel: returns rc=%Rrc\n", rc)); + return rc; +} + + +/** + * 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 DECLCALLBACK(PVUSBURB) usbProxySolarisUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies) +{ + LogFlowFunc((USBPROXY ":usbProxySolarisUrbReap pProxyDev=%p cMillies=%u\n", pProxyDev, cMillies)); + + PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); + + /* + * Don't block if nothing is in the air. + */ + if (!pDevSol->pInFlightHead) + return NULL; + + /* + * Deque URBs inflight or those landed. + */ + if (cMillies > 0) + { + for (;;) + { + int cMilliesWait = cMillies == RT_INDEFINITE_WAIT ? -1 : (int)cMillies; + + struct pollfd aFd[2]; + size_t const cFds = RT_ELEMENTS(aFd); + + aFd[0].fd = RTFileToNative(pDevSol->hFile); + aFd[0].events = POLLIN; + aFd[0].revents = 0; + + aFd[1].fd = RTPipeToNative(pDevSol->hPipeWakeupR); + aFd[1].events = POLLIN; + aFd[1].revents = 0; + + int rc = poll(&aFd[0], cFds, cMilliesWait); + if (rc > 0) + { + if (aFd[0].revents & POLLHUP) + { + LogRel((USBPROXY ": USB Device '%s' disconnected!\n", pDevSol->pProxyDev->pUsbIns->pszName)); + pProxyDev->fDetached = true; + usbProxySolarisCloseFile(pDevSol); + } + + if (aFd[1].revents & POLLIN) + { + /* Got woken up, drain pipe. */ + uint8_t bRead; + size_t cbIgnored = 0; + RTPipeRead(pDevSol->hPipeWakeupR, &bRead, 1, &cbIgnored); + + /* + * It is possible that we got woken up and have an URB pending + * for completion. Do it on the way out. Otherwise return + * immediately to the caller. + */ + if (!(aFd[0].revents & POLLIN)) + return NULL; + } + break; + } + else if (rc == 0) + return NULL; + else if (errno != EAGAIN) + { + LogFlow((USBPROXY ":usbProxySolarisUrbReap Poll rc=%d errno=%d\n", rc, errno)); + return NULL; + } + } + } + + usbProxySolarisUrbComplete(pDevSol); + + /* + * Any URBs pending delivery? + */ + PVUSBURB pUrb = NULL; + while ( pDevSol->pTaxingHead + && !pUrb) + { + RTCritSectEnter(&pDevSol->CritSect); + + PUSBPROXYURBSOL pUrbSol = pDevSol->pTaxingHead; + if (pUrbSol) + { + pUrb = pUrbSol->pVUsbUrb; + if (pUrb) + { + /* + * Remove it from the taxing list and move it to the free list. + */ + pUrb->Dev.pvPrivate = NULL; + usbProxySolarisUrbFree(pDevSol, pUrbSol); + } + } + RTCritSectLeave(&pDevSol->CritSect); + } + + return pUrb; +} + + +/** + * Reads a completed/error'd URB from the client driver (no waiting). + * + * @param pDevSol The Solaris device instance. + */ +static PVUSBURB usbProxySolarisUrbComplete(PUSBPROXYDEVSOL pDevSol) +{ + LogFlowFunc((USBPROXY ": usbProxySolarisUrbComplete: pDevSol=%p\n", pDevSol)); + + VBOXUSBREQ_URB UrbReq; + bzero(&UrbReq, sizeof(UrbReq)); + + int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_REAP_URB, &UrbReq, sizeof(UrbReq)); + if (RT_SUCCESS(rc)) + { + if (UrbReq.pvUrbR3) + { + PUSBPROXYURBSOL pUrbSol = (PUSBPROXYURBSOL)UrbReq.pvUrbR3; + PVUSBURB pUrb = pUrbSol->pVUsbUrb; + if (RT_LIKELY(pUrb)) + { + Assert(pUrb->u32Magic == VUSBURB_MAGIC); + + /* + * Update the URB. + */ + if ( pUrb->enmType == VUSBXFERTYPE_ISOC + && pUrb->enmDir == VUSBDIRECTION_IN) + { + size_t cbData = 0; + for (unsigned i = 0; i < UrbReq.cIsocPkts; i++) + { + pUrb->aIsocPkts[i].cb = UrbReq.aIsocPkts[i].cbActPkt; + cbData += UrbReq.aIsocPkts[i].cbActPkt; + pUrb->aIsocPkts[i].enmStatus = UrbReq.aIsocPkts[i].enmStatus; + } + + LogFlow((USBPROXY ":usbProxySolarisUrbComplete: Isoc cbData=%d cbActPktSum=%d\n", pUrb->cbData, cbData)); + pUrb->cbData = cbData; + pUrb->enmStatus = UrbReq.enmStatus; + } + else + { + pUrb->cbData = UrbReq.cbData; + pUrb->enmStatus = UrbReq.enmStatus; + } + + RTCritSectEnter(&pDevSol->CritSect); + + /* + * Remove from the active list. + */ + if (pUrbSol->pNext) + pUrbSol->pNext->pPrev = pUrbSol->pPrev; + if (pUrbSol->pPrev) + pUrbSol->pPrev->pNext = pUrbSol->pNext; + else + { + Assert(pDevSol->pInFlightHead == pUrbSol); + pDevSol->pInFlightHead = pUrbSol->pNext; + } + + /* + * Add to the tail of the taxing list. + */ + pUrbSol->pNext = NULL; + pUrbSol->pPrev = pDevSol->pTaxingTail; + if (pDevSol->pTaxingTail) + pDevSol->pTaxingTail->pNext = pUrbSol; + else + pDevSol->pTaxingHead = pUrbSol; + pDevSol->pTaxingTail = pUrbSol; + + RTCritSectLeave(&pDevSol->CritSect); + + Log6((USBPROXY ": Reaping: EndPt=%#x Dir=%d cbData=%u\n", pUrb->EndPt, pUrb->enmDir, pUrb->cbData)); + if (pUrb->cbData < 1024) + Log6(("%.*Rhxd\n", pUrb->cbData, pUrb->abData)); + return pUrb; + } + } + } + else + { + if (rc != VERR_VUSB_DEVICE_NOT_ATTACHED) + LogRel((USBPROXY ": Reaping URB failed. rc=%Rrc\n", rc)); + } + + return NULL; +} + + +static DECLCALLBACK(int) usbProxySolarisWakeup(PUSBPROXYDEV pProxyDev) +{ + PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); + size_t cbIgnored; + + LogFlowFunc(("pProxyDev=%p\n", pProxyDev)); + + return RTPipeWrite(pDevSol->hPipeWakeupW, "", 1, &cbIgnored); +} + + +/** + * The Solaris USB Proxy Backend. + */ +extern const USBPROXYBACK g_USBProxyDeviceHost = +{ + /* pszName */ + "host", + /* cbBackend */ + sizeof(USBPROXYDEVSOL), + usbProxySolarisOpen, + NULL, + usbProxySolarisClose, + usbProxySolarisReset, + usbProxySolarisSetConfig, + usbProxySolarisClaimInterface, + usbProxySolarisReleaseInterface, + usbProxySolarisSetInterface, + usbProxySolarisClearHaltedEp, + usbProxySolarisUrbQueue, + usbProxySolarisUrbCancel, + usbProxySolarisUrbReap, + usbProxySolarisWakeup, + 0 +}; + |