USBProxyDevice-freebsd.cpp revision 0aefc1b31a411a6cdfa560b5d95d96b47f6fd9e4
/* $Id$ */
/** @file
* USB device proxy - the FreeBSD backend.
*/
/*
* Includes contributions from Hans Petter Selasky
*
* Copyright (C) 2006-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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
#endif
#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 "../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
/*******************************************************************************
* 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. */
/** Initial buffer length. */
/** Pointer to the URB. */
/** Copy of endpoint number. */
unsigned iEpNum;
/** Maximum transfer length. */
unsigned cMaxIo;
/** Maximum frame count. */
unsigned cMaxFrames;
/**
* Data for the FreeBSD usb proxy backend.
*/
typedef struct USBPROXYDEVFBSD
{
/** The open file. */
/** Software endpoint structures */
/** Flag whether an URB is cancelling. */
bool fCancelling;
/** Flag whether initialised or not */
bool fInit;
/** Kernel endpoint structures */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* 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
*/
void *pvArg, bool fHandleNoDev)
{
int rc = VINF_SUCCESS;
do
{
if (rc >= 0)
return VINF_SUCCESS;
{
Log(("usbProxyFreeBSDDoIoCtl: ENXIO -> unplugged. pProxyDev=%s\n",
}
{
LogFlow(("usbProxyFreeBSDDoIoCtl: Returned %d. pProxyDev=%s\n",
}
return RTErrConvertFromErrno(errno);
}
/**
* Init USB subsystem.
*/
{
struct usb_fs_init UsbFsInit;
int rc;
/* Sanity check */
return VINF_SUCCESS;
/* Zero default */
/* Init USB subsystem */
if (RT_SUCCESS(rc))
return rc;
}
/**
* Uninit USB subsystem.
*/
{
struct usb_fs_uninit UsbFsUninit;
int rc;
/* Sanity check */
return VINF_SUCCESS;
/* Close any open endpoints. */
for (unsigned n = 0; n != USBFBSD_MAXENDPOINTS; n++)
/* Zero default */
/* Uninit USB subsystem */
if (RT_SUCCESS(rc))
return rc;
}
/**
* Setup a USB request packet.
*/
{
LogFlow(("usbProxyFreeBSDSetupReq: pSetupData=%p bmRequestType=%x "
"bRequest=%x wValue=%x wIndex=%x wLength=%x\n", (void *)pSetupData,
/* Handle endianess here. Currently no swapping is needed. */
}
{
struct usb_fs_endpoint *pXferEndpoint;
struct usb_fs_open UsbFsOpen;
int rc;
LogFlow(("usbProxyFreeBSDEndpointOpen: pProxyDev=%p Endpoint=%d\n",
{
if (pEndpointFBSD->fCancelling)
continue;
if ( pEndpointFBSD->fOpen
&& !pEndpointFBSD->pUrb
return index;
}
if (index == USBFBSD_MAXENDPOINTS)
{
{
if (pEndpointFBSD->fCancelling)
continue;
if (!pEndpointFBSD->fOpen)
break;
}
if (index == USBFBSD_MAXENDPOINTS)
return -1;
}
/* set ppBuffer and pLength */
LogFlow(("usbProxyFreeBSDEndpointOpen: ep_index=%d ep_num=%d\n",
/* Hardcoded assumption about the URBs we get. */
if (RT_FAILURE(rc))
{
if (rc == VERR_RESOURCE_BUSY)
LogFlow(("usbProxyFreeBSDEndpointOpen: EBUSY\n"));
return -1;
}
pEndpointFBSD->fOpen = true;
return index;
}
/**
* Close an endpoint.
*
* @returns VBox status code.
*/
{
struct usb_fs_close UsbFsClose;
int rc = VINF_SUCCESS;
LogFlow(("usbProxyFreeBSDEndpointClose: pProxyDev=%p Endpoint=%d\n",
/* check for cancelling */
{
pEndpointFBSD->fCancelling = true;
pDevFBSD->fCancelling = true;
}
/* check for opened */
if (pEndpointFBSD->fOpen)
{
pEndpointFBSD->fOpen = false;
/* Zero default */
/* Set endpoint index */
/* Close endpoint */
}
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.
*/
void *pvBackend)
{
int rc;
/*
* Try open the device node.
*/
if (RT_SUCCESS(rc))
{
/*
* Initialize the FreeBSD backend data.
*/
if (RT_SUCCESS(rc))
{
LogFlow(("usbProxyFreeBSDOpen(%p, %s): returns successfully hFile=%RTfile iActiveCfg=%d\n",
return VINF_SUCCESS;
}
}
else if (rc == VERR_ACCESS_DENIED)
Log(("usbProxyFreeBSDOpen(%p, %s) failed, rc=%d!\n",
return rc;
}
/**
* Claims all the interfaces and figures out the
* current configuration.
*
* @returns VINF_SUCCESS.
* @param pProxyDev The proxy device.
*/
{
int rc;
LogFlow(("usbProxyFreeBSDInit: pProxyDev=%s\n",
/* Retrieve current active configuration. */
&pProxyDev->iActiveCfg, true);
{
pProxyDev->cIgnoreSetConfigs = 0;
}
else
{
pProxyDev->iActiveCfg++;
}
return rc;
}
/**
* Closes the proxy device.
*/
{
/* sanity check */
LogFlow(("usbProxyFreeBSDClose: returns\n"));
}
/**
* Reset a device.
*
* @returns VBox status code.
* @param pDev The device to reset.
*/
{
int iParm;
int rc = VINF_SUCCESS;
LogFlow(("usbProxyFreeBSDReset: pProxyDev=%s\n",
if (!fResetOnFreeBSD)
goto done;
/* We need to release kernel ressources first. */
if (RT_FAILURE(rc))
goto done;
/* Resetting is only possible as super-user, ignore any failures: */
iParm = 0;
if (RT_FAILURE(rc))
{
/* Set the config instead of bus reset */
iParm = 255;
if (RT_SUCCESS(rc))
{
iParm = 0;
}
}
/* Allocate kernel ressources again. */
if (RT_FAILURE(rc))
goto done;
/* Retrieve current active configuration. */
done:
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.
*/
{
int iCfgIndex;
int rc;
LogFlow(("usbProxyFreeBSDSetConfig: pProxyDev=%s cfg=%x\n",
/* We need to release kernel ressources first. */
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. */
{
break;
}
{
LogFlow(("usbProxyFreeBSDSetConfig: configuration "
"%d not found\n", iCfg));
return VERR_NOT_FOUND;
}
}
/* Set the config */
if (RT_FAILURE(rc))
return rc;
/* Allocate kernel ressources again. */
return usbProxyFreeBSDFsInit(pProxyDev);
}
/**
* Claims an interface.
* @returns success indicator.
*/
{
int rc;
LogFlow(("usbProxyFreeBSDClaimInterface: pProxyDev=%s "
/*
* Try to detach kernel driver on this interface, ignore any
* failures
*/
/* Try to claim interface */
}
/**
* Releases an interface.
* @returns success indicator.
*/
{
int rc;
LogFlow(("usbProxyFreeBSDReleaseInterface: pProxyDev=%s "
}
/**
* SET_INTERFACE.
*
* @returns success indicator.
*/
{
struct usb_alt_interface UsbIntAlt;
int rc;
LogFlow(("usbProxyFreeBSDSetInterface: pProxyDev=%p iIf=%x iAlt=%x\n",
/* We need to release kernel ressources first. */
if (RT_FAILURE(rc))
{
LogFlow(("usbProxyFreeBSDSetInterface: Freeing kernel resources "
"failed failed rc=%d\n", rc));
return rc;
}
if (RT_FAILURE(rc))
{
LogFlow(("usbProxyFreeBSDSetInterface: Setting interface %d %d "
return rc;
}
return usbProxyFreeBSDFsInit(pProxyDev);
}
/**
* Clears the halted endpoint 'ep_num'.
*/
{
LogFlow(("usbProxyFreeBSDClearHaltedEp: pProxyDev=%s ep_num=%u\n",
/*
* Clearing the zero control pipe doesn't make sense.
* Just ignore it.
*/
if ((ep_num & 0xF) == 0)
return VINF_SUCCESS;
struct usb_ctl_request Req;
VUSB_REQ_CLEAR_FEATURE, 0, ep_num, 0);
return rc;
}
/**
* @copydoc USBPROXYBACK::pfnUrbQueue
*/
{
struct usb_fs_endpoint *pXferEndpoint;
struct usb_fs_start UsbFsStart;
unsigned cFrames;
int index;
int ep_num;
int rc;
LogFlow(("usbProxyFreeBSDUrbQueue: pUrb=%p EndPt=%u Dir=%u\n",
ep_num |= 0x80;
index = 0;
index);
if (index < 0)
return VERR_INVALID_PARAMETER;
{
case VUSBXFERTYPE_MSG:
{
/* check wLength */
{
cFrames = 2;
}
else
{
cFrames = 1;
}
LogFlow(("usbProxyFreeBSDUrbQueue: pUrb->cbData=%u, 0x%02x, "
"0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
break;
}
case VUSBXFERTYPE_ISOC:
{
unsigned i;
{
if (i >= pEndpointFBSD->cMaxFrames)
break;
}
/* Timeout handling will be done during reap. */
cFrames = i;
break;
}
default:
{
/* XXX maybe we have to loop */
else
/* Timeout handling will be done during reap. */
cFrames = 1;
break;
}
}
/* store number of frames */
/* zero-default */
/* Start the transfer */
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],
if (RT_FAILURE(rc))
{
if (rc == VERR_RESOURCE_BUSY)
{
index++;
goto retry;
}
return false;
}
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.
*/
{
struct usb_fs_endpoint *pXferEndpoint;
struct usb_fs_complete UsbFsComplete;
int rc;
LogFlow(("usbProxyFreeBSDUrbReap: pProxyDev=%p, cMillies=%u\n",
/* check for cancelled transfers */
if (pDevFBSD->fCancelling)
{
for (unsigned n = 0; n < USBFBSD_MAXENDPOINTS; n++)
{
if (pEndpointFBSD->fCancelling)
{
pEndpointFBSD->fCancelling = false;
break;
}
}
{
{
case VUSBXFERTYPE_MSG:
break;
case VUSBXFERTYPE_ISOC:
break;
default:
break;
}
return pUrb;
}
pDevFBSD->fCancelling = false;
}
/* Zero default */
/* Check if any endpoints are complete */
if (RT_SUCCESS(rc))
{
LogFlow(("usbProxyFreeBSDUrbReap: Reaped "
goto repeat;
goto repeat;
switch (pXferEndpoint->status)
{
break;
case USB_ERR_STALLED:
break;
default:
break;
}
{
case VUSBXFERTYPE_MSG:
break;
case VUSBXFERTYPE_ISOC:
{
int n;
break;
{
if (n >= (int)pEndpointFBSD->cMaxFrames)
break;
}
break;
}
default:
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],
}
{
/* Poll for finished transfers */
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.
*/
{
int index;
return VINF_SUCCESS; /* invalid index, pretend success. */
}
/**
* The FreeBSD USB Proxy Backend.
*/
extern const USBPROXYBACK g_USBProxyDeviceHost =
{
/* pszName */
"host",
/* cbBackend */
sizeof(PUSBPROXYDEVFBSD),
0
};
/*
* Local Variables:
* mode: c
* c-file-style: "bsd"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: s
* End:
*/