/** * Close an endpoint. * * @returns VBox status code. */ static int usbProxyFreeBSDEndpointClose(PUSBPROXYDEV pProxyDev, int Endpoint) { PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); PUSBENDPOINTFBSD pEndpointFBSD = &pDevFBSD->aSwEndpoint[Endpoint]; struct usb_fs_close UsbFsClose; int rc = VINF_SUCCESS; LogFlow(("usbProxyFreeBSDEndpointClose: pProxyDev=%p Endpoint=%d\n", (void *)pProxyDev, Endpoint)); /* check for cancelling */ if (pEndpointFBSD->pUrb != NULL) { pEndpointFBSD->fCancelling = true; pDevFBSD->fCancelling = true; } /* check for opened */ if (pEndpointFBSD->fOpen) { pEndpointFBSD->fOpen = false; /* Zero default */ memset(&UsbFsClose, 0, sizeof(UsbFsClose)); /* Set endpoint index */ UsbFsClose.ep_index = Endpoint; /* Close endpoint */ rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_CLOSE, &UsbFsClose, true); } return rc; }
/** * Uninit USB subsystem. */ static int usbProxyFreeBSDFsUnInit(PUSBPROXYDEV pProxyDev) { struct usb_fs_uninit UsbFsUninit; PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); int rc; LogFlow(("usbProxyFreeBSDFsUnInit: ProxyDev=%p\n", (void *)pProxyDev)); /* Sanity check */ AssertPtrReturn(pDevFBSD, VERR_INVALID_PARAMETER); if (pDevFBSD->fInit != true) return VINF_SUCCESS; /* Close any open endpoints. */ for (unsigned n = 0; n != USBFBSD_MAXENDPOINTS; n++) usbProxyFreeBSDEndpointClose(pProxyDev, n); /* Zero default */ memset(&UsbFsUninit, 0, sizeof(UsbFsUninit)); /* Uninit USB subsystem */ rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_UNINIT, &UsbFsUninit, false); if (RT_SUCCESS(rc)) pDevFBSD->fInit = false; return rc; }
/** * Claims all the interfaces and figures out the * current configuration. * * @returns VINF_SUCCESS. * @param pProxyDev The proxy device. */ static DECLCALLBACK(int) usbProxyFreeBSDInit(PUSBPROXYDEV pProxyDev) { PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); int rc; LogFlow(("usbProxyFreeBSDInit: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName)); /* Retrieve current active configuration. */ rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_GET_CONFIG, &pProxyDev->iActiveCfg, true); if (RT_FAILURE(rc) || pProxyDev->iActiveCfg == 255) { pProxyDev->cIgnoreSetConfigs = 0; pProxyDev->iActiveCfg = -1; } else { pProxyDev->cIgnoreSetConfigs = 1; pProxyDev->iActiveCfg++; } Log(("usbProxyFreeBSDInit: iActiveCfg=%d\n", pProxyDev->iActiveCfg)); return rc; }
/** * Copy the device and free resources associated with the backend. */ static DECLCALLBACK(void) usbProxyWinClose(PUSBPROXYDEV pProxyDev) { /* Here we just close the device and free up p->priv * there is no need to do anything like cancel outstanding requests * that will have been done already */ PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); Assert(pPriv); if (!pPriv) return; Log(("usbProxyWinClose: %p\n", pPriv->hDev)); if (pPriv->hDev != INVALID_HANDLE_VALUE) { Assert(pPriv->fClaimed); USBSUP_RELEASEDEV in; DWORD cbReturned = 0; in.bInterfaceNumber = pPriv->bInterfaceNumber; if (!DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_RELEASE_DEVICE, &in, sizeof(in), NULL, 0, &cbReturned, NULL)) { Log(("usbproxy: usbProxyWinClose: DeviceIoControl %#x failed with %#x!!\n", pPriv->hDev, GetLastError())); } if (!CloseHandle(pPriv->hDev)) AssertLogRelMsgFailed(("usbproxy: usbProxyWinClose: CloseHandle %#x failed with %#x!!\n", pPriv->hDev, GetLastError())); pPriv->hDev = INVALID_HANDLE_VALUE; } CloseHandle(pPriv->hEventWakeup); RTCritSectDelete(&pPriv->CritSect); RTMemFree(pPriv->paQueuedUrbs); RTMemFree(pPriv->paHandles); }
static DECLCALLBACK(int) usbProxyWinWakeup(PUSBPROXYDEV pProxyDev) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); SetEvent(pPriv->hEventWakeup); return VINF_SUCCESS; }
/** * Cancels an in-flight URB. * * The URB requires reaping, so we don't change its state. * * @remark There isn't a way to cancel a specific URB on Windows. * on darwin. The interface only supports the aborting of * all URBs pending on an endpoint. Luckily that is usually * exactly what the guest wants to do. */ static DECLCALLBACK(int) usbProxyWinUrbCancel(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); PQUEUED_URB pQUrbWin = (PQUEUED_URB)pUrb->Dev.pvPrivate; USBSUP_CLEAR_ENDPOINT in; DWORD cbReturned; AssertPtrReturn(pQUrbWin, VERR_INVALID_PARAMETER); in.bEndpoint = pUrb->EndPt | (pUrb->enmDir == VUSBDIRECTION_IN ? 0x80 : 0); Log(("Cancel urb %p, endpoint %x\n", pUrb, in.bEndpoint)); cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_ABORT_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL)) return VINF_SUCCESS; DWORD dwErr = GetLastError(); if ( dwErr == ERROR_INVALID_HANDLE_STATE || dwErr == ERROR_BAD_COMMAND) { Log(("usbproxy: device %x unplugged!!\n", pPriv->hDev)); pProxyDev->fDetached = true; return VINF_SUCCESS; /* Fake success and deal with the unplugged device elsewhere. */ } AssertMsgFailed(("lastErr=%ld\n", dwErr)); return RTErrConvertFromWin32(dwErr); }
/** * Wrapper for the ioctl call. * * This wrapper will repeat the call if we get an EINTR or EAGAIN. It can also * handle ENODEV (detached device) errors. * * @returns whatever ioctl returns. * @param pProxyDev The proxy device. * @param iCmd The ioctl command / function. * @param pvArg The ioctl argument / data. * @param fHandleNoDev Whether to handle ENXIO. * @internal */ static int usbProxyFreeBSDDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd, void *pvArg, bool fHandleNoDev) { int rc = VINF_SUCCESS; PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); LogFlow(("usbProxyFreeBSDDoIoCtl: iCmd=%#x\n", iCmd)); do { rc = ioctl(RTFileToNative(pDevFBSD->hFile), iCmd, pvArg); if (rc >= 0) return VINF_SUCCESS; } while (errno == EINTR); if (errno == ENXIO && fHandleNoDev) { Log(("usbProxyFreeBSDDoIoCtl: ENXIO -> unplugged. pProxyDev=%s\n", pProxyDev->pUsbIns->pszName)); errno = ENODEV; } else if (errno != EAGAIN) { LogFlow(("usbProxyFreeBSDDoIoCtl: Returned %d. pProxyDev=%s\n", errno, pProxyDev->pUsbIns->pszName)); } return RTErrConvertFromErrno(errno); }
static DECLCALLBACK(int) usbProxyWinSetConfig(PUSBPROXYDEV pProxyDev, int cfg) { /* Send a SET_CONFIGURATION command to the device. We don't do this * as a normal control message, because the OS might not want to * be left out of the loop on such a thing. * * It would be OK to send a SET_CONFIGURATION control URB at this * point but it has to be synchronous. */ PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); USBSUP_SET_CONFIG in; DWORD cbReturned; Assert(pPriv); Log(("usbproxy: Set config of %p to %d\n", pPriv->hDev, cfg)); in.bConfigurationValue = cfg; /* Here we just need to assert reset signalling on the USB device */ cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_SET_CONFIG, &in, sizeof(in), NULL, 0, &cbReturned, NULL)) return VINF_SUCCESS; return usbProxyWinHandleUnpluggedDevice(pProxyDev, GetLastError()); }
/** * Init USB subsystem. */ static int usbProxyFreeBSDFsInit(PUSBPROXYDEV pProxyDev) { struct usb_fs_init UsbFsInit; PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); int rc; LogFlow(("usbProxyFreeBSDFsInit: pProxyDev=%p\n", (void *)pProxyDev)); /* Sanity check */ AssertPtrReturn(pDevFBSD, VERR_INVALID_PARAMETER); if (pDevFBSD->fInit == true) return VINF_SUCCESS; /* Zero default */ memset(&UsbFsInit, 0, sizeof(UsbFsInit)); UsbFsInit.pEndpoints = pDevFBSD->aHwEndpoint; UsbFsInit.ep_index_max = USBFBSD_MAXENDPOINTS; /* Init USB subsystem */ rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_INIT, &UsbFsInit, false); if (RT_SUCCESS(rc)) pDevFBSD->fInit = true; return rc; }
static DECLCALLBACK(int) usbProxyWinReset(PUSBPROXYDEV pProxyDev, bool fResetOnLinux) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); DWORD cbReturned; int rc; Assert(pPriv); Log(("usbproxy: Reset %x\n", pPriv->hDev)); /* Here we just need to assert reset signalling on the USB device */ cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_RESET, NULL, 0, NULL, 0, &cbReturned, NULL)) { #if 0 /** @todo this needs to be enabled if windows chooses a default config. Test with the TrekStor GO Stick. */ pProxyDev->iActiveCfg = 1; pProxyDev->cIgnoreSetConfigs = 2; #else pProxyDev->iActiveCfg = -1; pProxyDev->cIgnoreSetConfigs = 0; #endif return VINF_SUCCESS; } rc = GetLastError(); if (rc == ERROR_DEVICE_REMOVED) { Log(("usbproxy: device %p unplugged!!\n", pPriv->hDev)); pProxyDev->fDetached = true; } return RTErrConvertFromWin32(rc); }
/** * SET_INTERFACE. * * @returns success indicator. */ static DECLCALLBACK(int) usbProxyFreeBSDSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int iAlt) { PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); struct usb_alt_interface UsbIntAlt; int rc; LogFlow(("usbProxyFreeBSDSetInterface: pProxyDev=%p iIf=%x iAlt=%x\n", pProxyDev, iIf, iAlt)); /* We need to release kernel ressources first. */ rc = usbProxyFreeBSDFsUnInit(pProxyDev); if (RT_FAILURE(rc)) { LogFlow(("usbProxyFreeBSDSetInterface: Freeing kernel resources " "failed failed rc=%d\n", rc)); return rc; } memset(&UsbIntAlt, 0, sizeof(UsbIntAlt)); UsbIntAlt.uai_interface_index = iIf; UsbIntAlt.uai_alt_index = iAlt; rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_ALTINTERFACE, &UsbIntAlt, true); if (RT_FAILURE(rc)) { LogFlow(("usbProxyFreeBSDSetInterface: Setting interface %d %d " "failed rc=%d\n", iIf, iAlt, rc)); return rc; } return usbProxyFreeBSDFsInit(pProxyDev); }
static DECLCALLBACK(int) usbProxyWinReleaseInterface(PUSBPROXYDEV pProxyDev, int ifnum) { /* The opposite of claim_interface. */ PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); Assert(pPriv); return VINF_SUCCESS; }
static DECLCALLBACK(int) usbProxyFreeBSDWakeup(PUSBPROXYDEV pProxyDev) { PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); size_t cbIgnored; LogFlowFunc(("pProxyDev=%p\n", pProxyDev)); return RTPipeWrite(pDevFBSD->hPipeWakeupW, "", 1, &cbIgnored); }
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); }
/** * @copydoc USBPROXYBACK::pfnUrbQueue */ static DECLCALLBACK(int) usbProxySolarisUrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb) { PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); LogFlowFunc((USBPROXY ": usbProxySolarisUrbQueue: pProxyDev=%s pUrb=%p EndPt=%#x enmDir=%d cbData=%d pvData=%p\n", pProxyDev->pUsbIns->pszName, pUrb, 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; 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; }
static DECLCALLBACK(int) usbProxyWinClaimInterface(PUSBPROXYDEV pProxyDev, int ifnum) { /* Called just before we use an interface. Needed on Linux to claim * the interface from the OS, since even when proxying the host OS * might want to allow other programs to use the unused interfaces. * Not relevant for Windows. */ PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); pPriv->bInterfaceNumber = ifnum; Assert(pPriv); return VINF_SUCCESS; }
/** * Converts the given Windows error code to VBox handling unplugged devices. * * @returns VBox status code. * @param pProxDev The USB proxy device instance. * @param dwErr Windows error code. */ static int usbProxyWinHandleUnpluggedDevice(PUSBPROXYDEV pProxyDev, DWORD dwErr) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); if ( dwErr == ERROR_INVALID_HANDLE_STATE || dwErr == ERROR_BAD_COMMAND) { Log(("usbproxy: device %x unplugged!!\n", pPriv->hDev)); pProxyDev->fDetached = true; } else AssertMsgFailed(("lasterr=%d\n", dwErr)); return RTErrConvertFromWin32(dwErr); }
/** * 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 DECLCALLBACK(int) usbProxyFreeBSDSetConfig(PUSBPROXYDEV pProxyDev, int iCfg) { PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); int iCfgIndex; int rc; LogFlow(("usbProxyFreeBSDSetConfig: pProxyDev=%s cfg=%x\n", pProxyDev->pUsbIns->pszName, iCfg)); /* We need to release kernel ressources first. */ rc = usbProxyFreeBSDFsUnInit(pProxyDev); if (RT_FAILURE(rc)) { LogFlow(("usbProxyFreeBSDSetInterface: Freeing kernel resources " "failed failed rc=%d\n", rc)); return rc; } if (iCfg == 0) { /* Unconfigure */ iCfgIndex = 255; } else { /* Get the configuration index matching the value. */ for (iCfgIndex = 0; iCfgIndex < pProxyDev->DevDesc.bNumConfigurations; iCfgIndex++) { if (pProxyDev->paCfgDescs[iCfgIndex].Core.bConfigurationValue == iCfg) break; } if (iCfgIndex == pProxyDev->DevDesc.bNumConfigurations) { LogFlow(("usbProxyFreeBSDSetConfig: configuration " "%d not found\n", iCfg)); return VERR_NOT_FOUND; } } /* Set the config */ rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iCfgIndex, true); if (RT_FAILURE(rc)) return rc; /* Allocate kernel ressources again. */ return usbProxyFreeBSDFsInit(pProxyDev); }
/** * 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; }
/** * 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 to switch configuration. rc=%Rrc\n", rc)); return rc; }
/** * Opens the device file. * * @returns VBox status code. * @param pProxyDev The device instance. * @param pszAddress If we are using usbfs, this is the path to the * device. If we are using sysfs, this is a string of * the form "sysfs:<sysfs path>//device:<device node>". * In the second case, the two paths are guaranteed * not to contain the substring "//". * @param pvBackend Backend specific pointer, unused for the linux backend. */ static DECLCALLBACK(int) usbProxyFreeBSDOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress, void *pvBackend) { PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); int rc; LogFlow(("usbProxyFreeBSDOpen: pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress)); NOREF(pvBackend); /* * Try open the device node. */ RTFILE hFile; rc = RTFileOpen(&hFile, pszAddress, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); if (RT_SUCCESS(rc)) { /* * Initialize the FreeBSD backend data. */ pDevFBSD->hFile = hFile; rc = usbProxyFreeBSDFsInit(pProxyDev); if (RT_SUCCESS(rc)) { /* * Create wakeup pipe. */ rc = RTPipeCreate(&pDevFBSD->hPipeWakeupR, &pDevFBSD->hPipeWakeupW, 0); if (RT_SUCCESS(rc)) { LogFlow(("usbProxyFreeBSDOpen(%p, %s): returns successfully hFile=%RTfile iActiveCfg=%d\n", pProxyDev, pszAddress, pDevFBSD->hFile, pProxyDev->iActiveCfg)); return VINF_SUCCESS; } } RTFileClose(hFile); } else if (rc == VERR_ACCESS_DENIED) rc = VERR_VUSB_USBFS_PERMISSION; Log(("usbProxyFreeBSDOpen(%p, %s) failed, rc=%d!\n", pProxyDev, pszAddress, rc)); return rc; }
/** * Specify an alternate setting for the specified interface of the current configuration. * * @returns success indicator. */ static DECLCALLBACK(int) usbProxySolarisSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int iAlt) { LogFlowFunc((USBPROXY ":usbProxySolarisSetInterface: pProxyDev=%p iIf=%d iAlt=%d\n", pProxyDev, iIf, iAlt)); PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL); AssertPtrReturn(pDevSol, VERR_INVALID_POINTER); VBOXUSBREQ_SET_INTERFACE SetInterfaceReq; SetInterfaceReq.bInterface = iIf; SetInterfaceReq.bAlternate = iAlt; 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 to set interface. rc=%Rrc\n", rc)); return rc; }
/** * Reset a device. * * @returns VBox status code. * @param pDev The device to reset. */ static DECLCALLBACK(int) usbProxyFreeBSDReset(PUSBPROXYDEV pProxyDev, bool fResetOnFreeBSD) { PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); int iParm; int rc = VINF_SUCCESS; LogFlow(("usbProxyFreeBSDReset: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName)); if (!fResetOnFreeBSD) goto done; /* We need to release kernel ressources first. */ rc = usbProxyFreeBSDFsUnInit(pProxyDev); if (RT_FAILURE(rc)) goto done; /* Resetting is only possible as super-user, ignore any failures: */ iParm = 0; rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_DEVICEENUMERATE, &iParm, true); if (RT_FAILURE(rc)) { /* Set the config instead of bus reset */ iParm = 255; rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iParm, true); if (RT_SUCCESS(rc)) { iParm = 0; rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iParm, true); } } usleep(10000); /* nice it! */ /* Allocate kernel ressources again. */ rc = usbProxyFreeBSDFsInit(pProxyDev); if (RT_FAILURE(rc)) goto done; /* Retrieve current active configuration. */ rc = usbProxyFreeBSDInit(pProxyDev); done: pProxyDev->cIgnoreSetConfigs = 2; return rc; }
/** * Clears the halted endpoint 'ep'. */ static DECLCALLBACK(int) usbProxyWinClearHaltedEndPt(PUSBPROXYDEV pProxyDev, unsigned int ep) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); USBSUP_CLEAR_ENDPOINT in; DWORD cbReturned; Assert(pPriv); Log(("usbproxy: Clear endpoint %d of %x\n", ep, pPriv->hDev)); in.bEndpoint = ep; cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_CLEAR_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL)) return VINF_SUCCESS; return usbProxyWinHandleUnpluggedDevice(pProxyDev, GetLastError()); }
/** * Closes the proxy device. */ static DECLCALLBACK(void) usbProxyFreeBSDClose(PUSBPROXYDEV pProxyDev) { PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); LogFlow(("usbProxyFreeBSDClose: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName)); /* sanity check */ AssertPtrReturnVoid(pDevFBSD); usbProxyFreeBSDFsUnInit(pProxyDev); RTPipeClose(pDevFBSD->hPipeWakeupR); RTPipeClose(pDevFBSD->hPipeWakeupW); RTFileClose(pDevFBSD->hFile); pDevFBSD->hFile = NIL_RTFILE; LogFlow(("usbProxyFreeBSDClose: returns\n")); }
/** * 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; }
/** * 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(); }
/** * 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: rc=%Rrc.\n", rc)); return rc; }
static DECLCALLBACK(int) usbProxyWinSetInterface(PUSBPROXYDEV pProxyDev, int ifnum, int setting) { /* Select an alternate setting for an interface, the same applies * here as for set_config, you may convert this in to a control * message if you want but it must be synchronous */ PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); USBSUP_SELECT_INTERFACE in; DWORD cbReturned; Assert(pPriv); Log(("usbproxy: Select interface of %x to %d/%d\n", pPriv->hDev, ifnum, setting)); in.bInterfaceNumber = ifnum; in.bAlternateSetting = setting; /* Here we just need to assert reset signalling on the USB device */ cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_SELECT_INTERFACE, &in, sizeof(in), NULL, 0, &cbReturned, NULL)) return VINF_SUCCESS; return usbProxyWinHandleUnpluggedDevice(pProxyDev, GetLastError()); }
/** * 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) usbProxyFreeBSDUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies) { struct usb_fs_endpoint *pXferEndpoint; PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD); PUSBENDPOINTFBSD pEndpointFBSD; PVUSBURB pUrb; struct usb_fs_complete UsbFsComplete; struct pollfd pfd[2]; int rc; LogFlow(("usbProxyFreeBSDUrbReap: pProxyDev=%p, cMillies=%u\n", pProxyDev, cMillies)); repeat: pUrb = NULL; /* check for cancelled transfers */ if (pDevFBSD->fCancelling) { for (unsigned n = 0; n < USBFBSD_MAXENDPOINTS; n++) { pEndpointFBSD = &pDevFBSD->aSwEndpoint[n]; if (pEndpointFBSD->fCancelling) { pEndpointFBSD->fCancelling = false; pUrb = pEndpointFBSD->pUrb; pEndpointFBSD->pUrb = NULL; if (pUrb != NULL) break; } } if (pUrb != NULL) { pUrb->enmStatus = VUSBSTATUS_INVALID; pUrb->Dev.pvPrivate = NULL; switch (pUrb->enmType) { case VUSBXFERTYPE_MSG: pUrb->cbData = 0; break; case VUSBXFERTYPE_ISOC: pUrb->cbData = 0; for (int n = 0; n < (int)pUrb->cIsocPkts; n++) pUrb->aIsocPkts[n].cb = 0; break; default: pUrb->cbData = 0; break; } return pUrb; } pDevFBSD->fCancelling = false; } /* Zero default */ memset(&UsbFsComplete, 0, sizeof(UsbFsComplete)); /* Check if any endpoints are complete */ rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_COMPLETE, &UsbFsComplete, true); if (RT_SUCCESS(rc)) { pXferEndpoint = &pDevFBSD->aHwEndpoint[UsbFsComplete.ep_index]; pEndpointFBSD = &pDevFBSD->aSwEndpoint[UsbFsComplete.ep_index]; LogFlow(("usbProxyFreeBSDUrbReap: Reaped " "URB %#p\n", pEndpointFBSD->pUrb)); if (pXferEndpoint->status == USB_ERR_CANCELLED) goto repeat; pUrb = pEndpointFBSD->pUrb; pEndpointFBSD->pUrb = NULL; if (pUrb == NULL) goto repeat; switch (pXferEndpoint->status) { case USB_ERR_NORMAL_COMPLETION: pUrb->enmStatus = VUSBSTATUS_OK; break; case USB_ERR_STALLED: pUrb->enmStatus = VUSBSTATUS_STALL; break; default: pUrb->enmStatus = VUSBSTATUS_INVALID; break; } pUrb->Dev.pvPrivate = NULL; switch (pUrb->enmType) { case VUSBXFERTYPE_MSG: pUrb->cbData = pEndpointFBSD->acbData[0] + pEndpointFBSD->acbData[1]; break; case VUSBXFERTYPE_ISOC: { int n; if (pUrb->enmDir == VUSBDIRECTION_OUT) break; pUrb->cbData = 0; for (n = 0; n < (int)pUrb->cIsocPkts; n++) { if (n >= (int)pEndpointFBSD->cMaxFrames) break; pUrb->cbData += pEndpointFBSD->acbData[n]; pUrb->aIsocPkts[n].cb = pEndpointFBSD->acbData[n]; } for (; n < (int)pUrb->cIsocPkts; n++) pUrb->aIsocPkts[n].cb = 0; break; } default: pUrb->cbData = pEndpointFBSD->acbData[0]; break; } LogFlow(("usbProxyFreeBSDUrbReap: Status=%d epindex=%u " "len[0]=%d len[1]=%d\n", (int)pXferEndpoint->status, (unsigned)UsbFsComplete.ep_index, (unsigned)pEndpointFBSD->acbData[0], (unsigned)pEndpointFBSD->acbData[1])); } else if (cMillies != 0 && rc == VERR_RESOURCE_BUSY) { for (;;) { pfd[0].fd = RTFileToNative(pDevFBSD->hFile); pfd[0].events = POLLIN | POLLRDNORM; pfd[0].revents = 0; pfd[1].fd = RTPipeToNative(pDevFBSD->hPipeWakeupR); pfd[1].events = POLLIN | POLLRDNORM; pfd[1].revents = 0; rc = poll(pfd, 2, (cMillies == RT_INDEFINITE_WAIT) ? INFTIM : cMillies); if (rc > 0) { if (pfd[1].revents & POLLIN) { /* Got woken up, drain pipe. */ uint8_t bRead; size_t cbIgnored = 0; RTPipeRead(pDevFBSD->hPipeWakeupR, &bRead, 1, &cbIgnored); /* Make sure we return from this function */ cMillies = 0; } break; } if (rc == 0) return NULL; if (errno != EAGAIN) return NULL; } goto repeat; } return pUrb; }