Hi,
Please find attached files and patches needed to make USB support fully work
in VirtualBox under FreeBSD 8/9+. All patches and code is given under the MIT
license, if that was not already clear to you.
I also want to thank all the people that sponsored me to do this "upgrade" of
the existing FreeBSD USB code in VirtualBox with $500.
Instructions:
1) Replace the USBProxyDevice-freebsd.cpp found in the virtualbox
sources by the one attached to this e-mail.
2) Apply the attached patches
Additional, how to automatically give VirtualBox rights to USB devices:
#
# /usr/local/etc/devd/virtualbox_dev_0x1234.conf
#
notify 50 {
match "system" "USB";
match "subsystem" "DEVICE";
match "type" "ATTACH";
match "vendor" "0x1234";
action "chown virtualbox:virtualbox /dev/$cdev";
};
3) If USB 2.0 support is not present in VirtualBox, try:
sysctl hw.usb.ehci.no_hs=1
And then re-plug the device. Then the all USB HIGH speed USB devices will
attach like FULL speed ones.
--HPS (one of the maintainers of the USB stack in FreeBSD 8+)
--- ./work/VirtualBox-3.2.51.r32802_OSE/src/VBox/Devices/USB/DevOHCI.cpp.orig 2010-10-20 20:57:31.000000000 +0200
+++ ./work/VirtualBox-3.2.51.r32802_OSE/src/VBox/Devices/USB/DevOHCI.cpp 2010-10-20 21:02:11.000000000 +0200
@@ -1110,6 +1110,7 @@
*/
DECLINLINE(void) ohciPhysRead(POHCI pOhci, uint32_t Addr, void *pvBuf, size_t cbBuf)
{
+ if (cbBuf == 0) return;
PDMDevHlpPhysRead(pOhci->CTX_SUFF(pDevIns), Addr, pvBuf, cbBuf);
}
@@ -1118,6 +1119,7 @@
*/
DECLINLINE(void) ohciPhysWrite(POHCI pOhci, uint32_t Addr, const void *pvBuf, size_t cbBuf)
{
+ if (cbBuf == 0) return;
PDMDevHlpPhysWrite(pOhci->CTX_SUFF(pDevIns), Addr, pvBuf, cbBuf);
}
/* $Id: USBProxyDevice-freebsd.cpp 31890 2010-08-24 07:50:47Z vboxsync $ */
/** @file
* USB device proxy - the FreeBSD backend.
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
* Copyright (C) 2010 Hans Petter Selasky
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
#ifdef VBOX
#include <iprt/stdint.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_ioctl.h>
#include <VBox/pdm.h>
#include <VBox/err.h>
#include <VBox/log.h>
#include <VBox/vusb.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/file.h>
#include "../USBProxyDevice.h"
/** Maximum endpoints supported. */
#define USBFBSD_MAXENDPOINTS 127
#define USBFBSD_MAXFRAMES 56
/** This really needs to be defined in vusb.h! */
#ifndef VUSB_DIR_TO_DEV
#define VUSB_DIR_TO_DEV 0x00
#endif
#if 0
#define PASS2(...) __VA_ARGS__
#define PASS(...) PASS2(__VA_ARGS__)
#define UN(...) __VA_ARGS__
static FILE *log_file;
static void
open_log_file(void)
{
if (log_file)
return;
log_file = fopen("my_log.txt", "w+");
}
#undef Log
#define Log(x) do { \
open_log_file(); \
fprintf(log_file, PASS(UN x)); \
} while (0)
#undef LogFlow
#define LogFlow Log
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef struct USBENDPOINTFBSD {
/** Flag whether it is opened. */
bool fOpen;
/** Flag whether it is cancelling. */
bool fCancelling;
/** Buffer pointers. */
void *apvData[USBFBSD_MAXFRAMES];
/** Buffer lengths. */
uint32_t acbData[USBFBSD_MAXFRAMES];
/** Initial buffer length. */
uint32_t cbData0;
/** Pointer to the URB. */
PVUSBURB pUrb;
/** Copy of endpoint number. */
unsigned iEpNum;
/** Maximum transfer length. */
unsigned cMaxIo;
/** Maximum frame count. */
unsigned cMaxFrames;
} USBENDPOINTFBSD, *PUSBENDPOINTFBSD;
/**
* Data for the FreeBSD usb proxy backend.
*/
typedef struct USBPROXYDEVFBSD {
/** The open file. */
RTFILE File;
/** Software endpoint structures */
USBENDPOINTFBSD aSwEndpoint[USBFBSD_MAXENDPOINTS];
/** Flag whether an URB is cancelling. */
bool fCancelling;
/** Flag whether initialised or not */
bool fInit;
/** Kernel endpoint structures */
struct usb_fs_endpoint aHwEndpoint[USBFBSD_MAXENDPOINTS];
} USBPROXYDEVFBSD, *PUSBPROXYDEVFBSD;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int usbProxyFreeBSDEndpointClose(PUSBPROXYDEV pProxyDev, int Endpoint);
/**
* Wrapper for the ioctl call.
*
* This wrapper will repeate 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 = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
LogFlow(("usbProxyFreeBSDDoIoCtl: iCmd=%p\n", (void *)iCmd));
do {
rc = ioctl(pDevFBSD->File, 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);
}
/**
* Init USB subsystem.
*/
static int
usbProxyFreeBSDFsInit(PUSBPROXYDEV pProxyDev)
{
struct usb_fs_init UsbFsInit;
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
int rc;
LogFlow(("usbProxyFreeBSDFsInit: pProxyDev=%p\n", (void *)pProxyDev));
/* Sanity check */
if (pDevFBSD == NULL)
return RTErrConvertFromErrno(EINVAL);
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 (rc == VINF_SUCCESS)
pDevFBSD->fInit = true;
return rc;
}
/**
* Uninit USB subsystem.
*/
static int
usbProxyFreeBSDFsUnInit(PUSBPROXYDEV pProxyDev)
{
struct usb_fs_uninit UsbFsUninit;
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
int rc;
int n;
LogFlow(("usbProxyFreeBSDFsUnInit: ProxyDev=%p\n", (void *)pProxyDev));
/* Sanity check */
if (pDevFBSD == NULL)
return RTErrConvertFromErrno(EINVAL);
if (pDevFBSD->fInit != true)
return VINF_SUCCESS;
/* Close any open endpoints. */
for (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 (rc == VINF_SUCCESS)
pDevFBSD->fInit = false;
return rc;
}
/**
* Setup a USB request packet.
*/
static void
usbProxyFreeBSDSetupReq(struct usb_device_request *pSetupData,
uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue,
uint16_t wIndex, uint16_t wLength)
{
LogFlow(("usbProxyFreeBSDSetupReq: pSetupData=%p bmRequestType=%x "
"bRequest=%x wValue=%x wIndex=%x wLength=%x\n", (void *)pSetupData,
bmRequestType, bRequest, wValue, wIndex, wLength));
pSetupData->bmRequestType = bmRequestType;
pSetupData->bRequest = bRequest;
/* Handle endianess here. Currently no swapping is needed. */
pSetupData->wValue[0] = wValue & 0xff;
pSetupData->wValue[1] = (wValue >> 8) & 0xff;
pSetupData->wIndex[0] = wIndex & 0xff;
pSetupData->wIndex[1] = (wIndex >> 8) & 0xff;
pSetupData->wLength[0] = wLength & 0xff;
pSetupData->wLength[1] = (wLength >> 8) & 0xff;
}
static int
usbProxyFreeBSDEndpointOpen(PUSBPROXYDEV pProxyDev, int Endpoint, int isIsoc, int index)
{
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
PUSBENDPOINTFBSD pEndpointFBSD;
struct usb_fs_endpoint *pXferEndpoint;
struct usb_fs_open UsbFsOpen;
int rc;
LogFlow(("usbProxyFreeBSDEndpointOpen: pProxyDev=%p Endpoint=%d\n",
(void *)pProxyDev, Endpoint));
for (; index != USBFBSD_MAXENDPOINTS; index++) {
pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
if (pEndpointFBSD->fCancelling)
continue;
if (pEndpointFBSD->fOpen && pEndpointFBSD->pUrb == NULL &&
(int)pEndpointFBSD->iEpNum == Endpoint)
return index;
}
if (index == USBFBSD_MAXENDPOINTS) {
for (index = 0; index != USBFBSD_MAXENDPOINTS; index++) {
pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
if (pEndpointFBSD->fCancelling)
continue;
if (!pEndpointFBSD->fOpen)
break;
}
if (index == USBFBSD_MAXENDPOINTS)
return -1;
}
/* set ppBuffer and pLength */
pXferEndpoint = &pDevFBSD->aHwEndpoint[index];
pXferEndpoint->ppBuffer = &pEndpointFBSD->apvData[0];
pXferEndpoint->pLength = &pEndpointFBSD->acbData[0];
LogFlow(("usbProxyFreeBSDEndpointOpen: ep_index=%d "
"ep_num=%d\n", (int)index, (int)Endpoint));
memset(&UsbFsOpen, 0, sizeof(UsbFsOpen));
UsbFsOpen.ep_index = index;
UsbFsOpen.ep_no = Endpoint;
UsbFsOpen.max_bufsize = 256 * 1024;
/* Hardcoded assumption about the URBs we get. */
UsbFsOpen.max_frames = isIsoc ? USBFBSD_MAXFRAMES : 2;
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_OPEN, &UsbFsOpen, true);
if (rc != VINF_SUCCESS) {
if (errno == EBUSY) {
LogFlow(("usbProxyFreeBSDEndpointOpen: EBUSY\n"));
}
return -1;
}
pEndpointFBSD->fOpen = true;
pEndpointFBSD->pUrb = NULL;
pEndpointFBSD->iEpNum = Endpoint;
pEndpointFBSD->cMaxIo = UsbFsOpen.max_bufsize;
pEndpointFBSD->cMaxFrames = UsbFsOpen.max_frames;
return index;
}
static int
usbProxyFreeBSDEndpointClose(PUSBPROXYDEV pProxyDev, int Endpoint)
{
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
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;
}
/**
* 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 int
usbProxyFreeBSDOpen(PUSBPROXYDEV pProxyDev,
const char *pszAddress, void *pvBackend)
{
int rc;
LogFlow(("usbProxyFreeBSDOpen: pProxyDev=%p pszAddress=%s\n",
(void *)pProxyDev, pszAddress));
/*
* Try open the device node.
*/
RTFILE File;
rc = RTFileOpen(&File, pszAddress, RTFILE_O_READWRITE |
RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
if (RT_SUCCESS(rc)) {
/*
* Allocate and initialize the linux backend data.
*/
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)
RTMemAllocZ(sizeof(USBPROXYDEVFBSD));
if (pDevFBSD) {
pDevFBSD->File = File;
pProxyDev->Backend.pv = pDevFBSD;
rc = usbProxyFreeBSDFsInit(pProxyDev);
if (!rc) {
LogFlow(("usbProxyFreeBSDOpen(%p, %s): returns "
"successfully File=%d iActiveCfg=%d\n",
(void *)pProxyDev, pszAddress,
pDevFBSD->File, pProxyDev->iActiveCfg));
return VINF_SUCCESS;
}
RTMemFree(pDevFBSD);
} else {
rc = VERR_NO_MEMORY;
}
RTFileClose(File);
} else if (rc == VERR_ACCESS_DENIED)
rc = VERR_VUSB_USBFS_PERMISSION;
Log(("usbProxyFreeBSDOpen(%p, %s) failed, rc=%d!\n",
(void *)pProxyDev, pszAddress, rc));
pProxyDev->Backend.pv = NULL;
NOREF(pvBackend);
return rc;
}
/**
* Claims all the interfaces and figures out the
* current configuration.
*
* @returns VINF_SUCCESS.
* @param pProxyDev The proxy device.
*/
static int
usbProxyFreeBSDInit(PUSBPROXYDEV pProxyDev)
{
LogFlow(("usbProxyFreeBSDInit: pProxyDev=%s\n",
pProxyDev->pUsbIns->pszName));
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
int rc;
/* Retrieve current active configuration. */
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_GET_CONFIG,
&pProxyDev->iActiveCfg, true);
if (rc != VINF_SUCCESS || pProxyDev->iActiveCfg == 255) {
pProxyDev->cIgnoreSetConfigs = 0;
pProxyDev->iActiveCfg = -1;
} else {
pProxyDev->cIgnoreSetConfigs = 1;
pProxyDev->iActiveCfg++;
}
Log(("usbProxyFreeBSDInit: iActiveCfg=%d\n", pProxyDev->iActiveCfg));
return rc;
}
/**
* Closes the proxy device.
*/
static void
usbProxyFreeBSDClose(PUSBPROXYDEV pProxyDev)
{
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
LogFlow(("usbProxyFreeBSDClose: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
/* sanity check */
if (pDevFBSD == NULL)
return;
usbProxyFreeBSDFsUnInit(pProxyDev);
RTFileClose(pDevFBSD->File);
pDevFBSD->File = NIL_RTFILE;
RTMemFree(pDevFBSD);
pProxyDev->Backend.pv = NULL;
LogFlow(("usbProxyFreeBSDClose: returns\n"));
}
/**
* Reset a device.
*
* @returns VBox status code.
* @param pDev The device to reset.
*/
static int
usbProxyFreeBSDReset(PUSBPROXYDEV pProxyDev, bool fResetOnFreeBSD)
{
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
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 (rc)
goto done;
/* Resetting is only possible as super-user, ignore any failures: */
iParm = 0;
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_DEVICEENUMERATE, &iParm, true);
if (rc != VINF_SUCCESS) {
/* Set the config instead of bus reset */
iParm = 255;
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG,
&iParm, true);
if (rc == VINF_SUCCESS) {
iParm = 0;
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG,
&iParm, true);
}
}
usleep(10000); /* nice it! */
/* Allocate kernel ressources again. */
rc = usbProxyFreeBSDFsInit(pProxyDev);
if (rc != VINF_SUCCESS)
goto done;
/* Retrieve current active configuration. */
rc = usbProxyFreeBSDInit(pProxyDev);
done:
pProxyDev->cIgnoreSetConfigs = 2;
return rc;
}
/**
* 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
usbProxyFreeBSDSetConfig(PUSBPROXYDEV pProxyDev, int iCfg)
{
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
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 (rc != VINF_SUCCESS) {
LogFlow(("usbProxyFreeBSDSetInterface: Freeing kernel resources "
"failed failed rc=%d\n", rc));
return false;
}
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 false;
}
}
/* Set the config */
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iCfgIndex, true);
if (rc != VINF_SUCCESS)
return false;
/* Allocate kernel ressources again. */
rc = usbProxyFreeBSDFsInit(pProxyDev);
if (rc != VINF_SUCCESS)
return false;
return true;
}
/**
* Claims an interface.
* @returns success indicator.
*/
static int
usbProxyFreeBSDClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
{
int rc;
LogFlow(("usbProxyFreeBSDClaimInterface: pProxyDev=%s "
"ifnum=%x\n", pProxyDev->pUsbIns->pszName, iIf));
/*
* Try to detach kernel driver on this interface, ignore any
* failures
*/
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_IFACE_DRIVER_DETACH, &iIf, true);
/* Try to claim interface */
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_CLAIM_INTERFACE, &iIf, true);
if (rc != VINF_SUCCESS)
return false;
return true;
}
/**
* Releases an interface.
* @returns success indicator.
*/
static int
usbProxyFreeBSDReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
{
int rc;
LogFlow(("usbProxyFreeBSDReleaseInterface: pProxyDev=%s "
"ifnum=%x\n", pProxyDev->pUsbIns->pszName, iIf));
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_RELEASE_INTERFACE, &iIf, true);
if (rc != VINF_SUCCESS)
return false;
return true;
}
/**
* SET_INTERFACE.
*
* @returns success indicator.
*/
static int
usbProxyFreeBSDSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int iAlt)
{
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
struct usb_alt_interface UsbIntAlt;
int n;
int rc;
LogFlow(("usbProxyFreeBSDSetInterface: pProxyDev=%p iIf=%x iAlt=%x\n",
(void *)pProxyDev, iIf, iAlt));
/* We need to release kernel ressources first. */
rc = usbProxyFreeBSDFsUnInit(pProxyDev);
if (rc != VINF_SUCCESS) {
LogFlow(("usbProxyFreeBSDSetInterface: Freeing kernel resources "
"failed failed rc=%d\n", rc));
return false;
}
memset(&UsbIntAlt, 0, sizeof(UsbIntAlt));
UsbIntAlt.uai_interface_index = iIf;
UsbIntAlt.uai_alt_index = iAlt;
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_ALTINTERFACE, &UsbIntAlt, true);
if (rc != VINF_SUCCESS) {
LogFlow(("usbProxyFreeBSDSetInterface: Setting interface %d %d "
"failed rc=%d\n", iIf, iAlt, rc));
return false;
}
rc = usbProxyFreeBSDFsInit(pProxyDev);
if (rc != VINF_SUCCESS)
return false;
return true;
}
/**
* Clears the halted endpoint 'ep_num'.
*/
static bool
usbProxyFreeBSDClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int ep_num)
{
struct usb_ctl_request Req;
int rc;
LogFlow(("usbProxyFreeBSDClearHaltedEp: "
"pProxyDev=%s ep_num=%u\n", pProxyDev->pUsbIns->pszName, ep_num));
/*
* Clearing the zero control pipe doesn't make sense.
* Just ignore it.
*/
if ((ep_num & 0xF) == 0)
return true;
memset(&Req, 0, sizeof(Req));
usbProxyFreeBSDSetupReq(&Req.ucr_request, VUSB_DIR_TO_DEV |
VUSB_TO_ENDPOINT, VUSB_REQ_CLEAR_FEATURE, 0, ep_num, 0);
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_DO_REQUEST, &Req, true);
LogFlow(("usbProxyFreeBSDClearHaltedEp: rc=%d\n", rc));
if (rc != VINF_SUCCESS)
return false;
return true;
}
/**
* @copydoc USBPROXYBACK::pfnUrbQueue
*/
static int
usbProxyFreeBSDUrbQueue(PVUSBURB pUrb)
{
PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUrb->pUsbIns, PUSBPROXYDEV);
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
PUSBENDPOINTFBSD pEndpointFBSD;
struct usb_fs_endpoint *pXferEndpoint;
struct usb_fs_start UsbFsStart;
unsigned cFrames;
uint8_t *ptr;
int index;
int ep_num;
int n;
int rc;
LogFlow(("usbProxyFreeBSDUrbQueue: pUrb=%p EndPt=%u Dir=%u\n",
(void *)pUrb, (unsigned)pUrb->EndPt, (unsigned)pUrb->enmDir));
ep_num = pUrb->EndPt;
if ((pUrb->enmType != VUSBXFERTYPE_MSG) && (pUrb->enmDir == VUSBDIRECTION_IN))
ep_num |= 0x80;
index = 0;
retry:
index = usbProxyFreeBSDEndpointOpen(pProxyDev, ep_num,
(pUrb->enmType == VUSBXFERTYPE_ISOC), index);
if (index < 0)
return false;
pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
pXferEndpoint = &pDevFBSD->aHwEndpoint[index];
ptr = pUrb->abData;
switch (pUrb->enmType) {
case VUSBXFERTYPE_MSG:
pEndpointFBSD->apvData[0] = ptr;
pEndpointFBSD->acbData[0] = 8;
/* check wLength */
if (ptr[6] || ptr[7]) {
pEndpointFBSD->apvData[1] = ptr + 8;
pEndpointFBSD->acbData[1] = ptr[6] | (ptr[7] << 8);
cFrames = 2;
} else {
pEndpointFBSD->apvData[1] = NULL;
pEndpointFBSD->acbData[1] = 0;
cFrames = 1;
}
LogFlow(("usbProxyFreeBSDUrbQueue: pUrb->cbData=%u, 0x%02x, "
"0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
pUrb->cbData, ptr[0], ptr[1], ptr[2], ptr[3],
ptr[4], ptr[5], ptr[6], ptr[7]));
pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
pXferEndpoint->flags = USB_FS_FLAG_MULTI_SHORT_OK;
break;
case VUSBXFERTYPE_ISOC:
for (n = 0; n < (int)pUrb->cIsocPkts; n++) {
if (n >= (int)pEndpointFBSD->cMaxFrames)
break;
pEndpointFBSD->apvData[n] = ptr + pUrb->aIsocPkts[n].off;
pEndpointFBSD->acbData[n] = pUrb->aIsocPkts[n].cb;
}
/* Timeout handling will be done during reap. */
pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
pXferEndpoint->flags = USB_FS_FLAG_MULTI_SHORT_OK;
cFrames = n;
break;
default:
pEndpointFBSD->apvData[0] = ptr;
pEndpointFBSD->cbData0 = pUrb->cbData;
/* XXX maybe we have to loop */
if (pUrb->cbData > pEndpointFBSD->cMaxIo)
pEndpointFBSD->acbData[0] = pEndpointFBSD->cMaxIo;
else
pEndpointFBSD->acbData[0] = pUrb->cbData;
/* Timeout handling will be done during reap. */
pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
pXferEndpoint->flags = pUrb->fShortNotOk ? 0 : USB_FS_FLAG_MULTI_SHORT_OK;
cFrames = 1;
break;
}
/* store number of frames */
pXferEndpoint->nFrames = cFrames;
/* zero-default */
memset(&UsbFsStart, 0, sizeof(UsbFsStart));
/* Start the transfer */
UsbFsStart.ep_index = index;
rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_START, &UsbFsStart, true);
LogFlow(("usbProxyFreeBSDUrbQueue: USB_FS_START returned rc=%d "
"len[0]=%u len[1]=%u cbData=%u index=%u ep_num=%u\n", rc,
(unsigned)pEndpointFBSD->acbData[0],
(unsigned)pEndpointFBSD->acbData[1],
(unsigned)pUrb->cbData,
(unsigned)index, (unsigned)ep_num));
if (rc != VINF_SUCCESS) {
if (errno == EBUSY) {
index++;
goto retry;
}
return false;
}
pUrb->Dev.pvPrivate = (void *)(long)(index + 1);
pEndpointFBSD->pUrb = pUrb;
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
usbProxyFreeBSDUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
{
struct usb_fs_endpoint *pXferEndpoint;
PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
PUSBENDPOINTFBSD pEndpointFBSD;
PVUSBURB pUrb;
struct usb_fs_complete UsbFsComplete;
struct pollfd PollFd;
int n;
int rc;
LogFlow(("usbProxyFreeBSDUrbReap: pProxyDev=%p, cMillies=%u\n",
(void *)pProxyDev, cMillies));
repeat:
pUrb = NULL;
/* check for cancelled transfers */
if (pDevFBSD->fCancelling) {
for (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 (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 (rc == VINF_SUCCESS) {
pXferEndpoint = &pDevFBSD->aHwEndpoint[UsbFsComplete.ep_index];
pEndpointFBSD = &pDevFBSD->aSwEndpoint[UsbFsComplete.ep_index];
LogFlow(("usbProxyFreeBSDUrbReap: Reaped "
"URB %p\n", (void *)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:
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 && errno == EBUSY) {
/* Poll for finished transfers */
PollFd.fd = (int)pDevFBSD->File;
PollFd.events = POLLIN | POLLRDNORM;
PollFd.revents = 0;
rc = poll(&PollFd, 1, (cMillies == RT_INDEFINITE_WAIT) ?
INFTIM : cMillies);
if (rc >= 1) {
goto repeat;
} else {
LogFlow(("usbProxyFreeBSDUrbReap: "
"poll returned rc=%d\n", rc));
}
}
return pUrb;
}
/**
* Cancels the URB.
* The URB requires reaping, so we don't change its state.
*/
static void
usbProxyFreeBSDUrbCancel(PVUSBURB pUrb)
{
PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUrb->pUsbIns, PUSBPROXYDEV);
int index;
index = (int)(long)pUrb->Dev.pvPrivate - 1;
if (index < 0 || index >= USBFBSD_MAXENDPOINTS)
return;
LogFlow(("usbProxyFreeBSDUrbCancel: epindex=%u\n", (unsigned)index));
usbProxyFreeBSDEndpointClose(pProxyDev, index);
}
/**
* The FreeBSD USB Proxy Backend.
*/
extern const USBPROXYBACK g_USBProxyDeviceHost =
{
"host",
usbProxyFreeBSDOpen,
usbProxyFreeBSDInit,
usbProxyFreeBSDClose,
usbProxyFreeBSDReset,
usbProxyFreeBSDSetConfig,
usbProxyFreeBSDClaimInterface,
usbProxyFreeBSDReleaseInterface,
usbProxyFreeBSDSetInterface,
usbProxyFreeBSDClearHaltedEp,
usbProxyFreeBSDUrbQueue,
usbProxyFreeBSDUrbCancel,
usbProxyFreeBSDUrbReap,
0
};
/*
* Local Variables:
* mode: c
* c-file-style: "bsd"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: s
* End:
*/
--- ./work/VirtualBox-3.2.51.r32802_OSE/src/VBox/Main/freebsd/USBProxyServiceFreeBSD.cpp.orig 2010-10-22 12:01:17.000000000 +0200
+++ ./work/VirtualBox-3.2.51.r32802_OSE/src/VBox/Main/freebsd/USBProxyServiceFreeBSD.cpp 2010-10-22 12:03:58.000000000 +0200
@@ -222,6 +222,7 @@
int iAddr = 1;
int rc = VINF_SUCCESS;
char *pszDevicePath = NULL;
+ uint32_t PlugTime = 0;
for (;;)
{
@@ -286,7 +287,7 @@
break;
}
- pDevice->enmState = USBDEVICESTATE_UNUSED;;
+ pDevice->enmState = USBDEVICESTATE_UNUSED;
pDevice->bBus = UsbDevInfo.udi_bus;
pDevice->bDeviceClass = UsbDevInfo.udi_class;
pDevice->bDeviceSubClass = UsbDevInfo.udi_subclass;
@@ -324,6 +325,10 @@
pDevice->pszSerialNumber = RTStrDupN(UsbDevInfo.udi_serial, sizeof(UsbDevInfo.udi_serial));
pDevice->u64SerialHash = USBLibHashSerial(pDevice->pszSerialNumber);
}
+ rc = ioctl(FileUsb, USB_GET_PLUGTIME, &PlugTime);
+ if (rc == 0)
+ pDevice->u64SerialHash += PlugTime;
+
pDevice->pszAddress = RTStrDup(pszDevicePath);
pDevice->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
_______________________________________________
vbox-dev mailing list
[email protected]
http://vbox.innotek.de/mailman/listinfo/vbox-dev