USBProxyDevice-freebsd.cpp revision d72aa6b0dab3e9b60aa78bfca99c767c48a406b0
/* $Id$ */
/** @file
* USB device proxy - the FreeBSD backend.
*/
/*
* Copyright (C) 2006-2007 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 32
#define USBFBSD_EPADDR_NUM_MASK 0x0F
#define USBFBSD_EPADDR_DIR_MASK 0x80
#define USBPROXY_FREEBSD_NO_ENTRY_FREE ((unsigned)~0)
/** 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 VUSBURBFBSD
{
/** Pointer to the URB. */
/** Buffer pointers. */
void *apvData[2];
/** Buffer lengths. */
} VUSBURBFBSD, *PVUSBURBFBSD;
typedef struct USBENDPOINTFBSD
{
/** Flag whether it is opened. */
bool fOpen;
/** Index in the endpoint list. */
unsigned iEndpoint;
/** Associated endpoint. */
struct usb_fs_endpoint *pXferEndpoint;
/**
* Data for the FreeBSD usb proxy backend.
*/
typedef struct USBPROXYDEVFBSD
{
/** The open file. */
/** Critical section protecting the two lists. */
/** Pointer to the array of USB endpoints. */
struct usb_fs_endpoint *paXferEndpoints;
/** Pointer to the array of URB structures.
* They entries must be in sync with the above array. */
/** Number of entries in both arrays. */
unsigned cXferEndpoints;
/** Pointer to the Fifo containing the indexes for free Xfer
* endpoints. */
unsigned *paXferFree;
/** Index of the next free entry to write to. */
unsigned iXferFreeNextWrite;
/** Index of the next entry to read from. */
unsigned iXferFreeNextRead;
/** Status of opened endpoints. */
/** The list of landed FreeBSD URBs. Doubly linked.
* Only the split head will appear in this list. */
/** The tail of the landed FreeBSD URBs. */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int usbProxyFreeBSDDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd, void *pvArg, bool fHandleNoDev, uint32_t cTries);
/**
* 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.
* @param cTries The number of retries. Use UINT32_MAX for (kind of) indefinite retries.
* @internal
*/
static int usbProxyFreeBSDDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd, void *pvArg, bool fHandleNoDev, uint32_t cTries)
{
int rc = VINF_SUCCESS;
do
{
do
{
if (rc >= 0)
return rc;
{
break;
}
{
break;
}
} while (cTries-- > 0);
return rc;
}
/**
* Setup a USB request packet.
*/
static void usbProxyFreeBSDSetupReq(struct usb_device_request *pSetupData, uint8_t bmRequestType, uint8_t bRequest,
{
LogFlow(("usbProxyFreeBSDSetupReq: pSetupData=%p bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
/* Handle endianess here. Currently no swapping is needed. */
// pSetupData->wIndex = wIndex;
// pSetupData->wLength = wLength;
}
/**
* The device has been unplugged.
* Cancel all in-flight URBs and put them up for reaping.
*/
{
/*
* Shoot down all flying URBs.
*/
#if 0 /** @todo */
while (pUrbFBSD)
{
/* insert into the taxing list. */
if ( !pCur->pSplitHead
{
if (pUrbTaxing)
pUrbTaxing = pCur;
}
else
}
/* Append the URBs we shot down to the taxing queue. */
if (pUrbTaxing)
{
if (pUrbTaxing->pPrev)
else
}
#endif
}
{
}
{
unsigned iEntry;
{
}
else
return iEntry;
}
{
int EndPtIndex = (Endpoint & USBFBSD_EPADDR_NUM_MASK) + ((Endpoint & USBFBSD_EPADDR_DIR_MASK) ? USBFBSD_MAXENDPOINTS / 2 : 0);
struct usb_fs_endpoint *pXferEndpoint;
if (!pEndpointFBSD->fOpen)
{
struct usb_fs_open UsbFsOpen;
return NULL;
if (rc)
return NULL;
pEndpointFBSD->fOpen = true;
}
else
{
}
return pEndpointFBSD;
}
{
int EndPtIndex = (Endpoint & USBFBSD_EPADDR_NUM_MASK) + ((Endpoint & USBFBSD_EPADDR_DIR_MASK) ? USBFBSD_MAXENDPOINTS / 2 : 0);
if (pEndpointFBSD->fOpen)
{
struct usb_fs_close UsbFsClose;
if (rc)
{
LogFlow(("usbProxyFreeBSDEndpointClose: failed rc=%d errno=%Rrc\n", rc, RTErrConvertFromErrno(errno)));
return RTErrConvertFromErrno(errno);
}
pEndpointFBSD->fOpen = false;
}
return VINF_SUCCESS;
}
/**
* 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.
*/
{
/*
* Try open the device node.
*/
if (RT_SUCCESS(rc))
{
/*
* Allocate and initialize the linux backend data.
*/
if (pDevFBSD)
{
if (RT_SUCCESS(rc))
{
/* Allocate arrays for data transfers. */
pDevFBSD->paXferEndpoints = (struct usb_fs_endpoint *)RTMemAllocZ(cTransfersMax * sizeof(struct usb_fs_endpoint));
{
/* Initialize the kernel side. */
struct usb_fs_init UsbFsInit;
if (!rc)
{
for (unsigned i = 0; i < cTransfersMax; i++)
for (unsigned i= 0; i < USBFBSD_MAXENDPOINTS; i++)
LogFlow(("usbProxyFreeBSDOpen(%p, %s): returns successfully File=%d iActiveCfg=%d\n",
return VINF_SUCCESS;
}
else
}
else
rc = VERR_NO_MEMORY;
if (pDevFBSD->paXferEndpoints)
if (pDevFBSD->paXferFree)
}
}
else
rc = VERR_NO_MEMORY;
}
else if (rc == VERR_ACCESS_DENIED)
return rc;
return VINF_SUCCESS;
}
/**
* Claims all the interfaces and figures out the
* current configuration.
*
* @returns VINF_SUCCESS.
* @param pProxyDev The proxy device.
*/
{
/* Retrieve current active configuration. */
int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_GET_CONFIG, &pProxyDev->iActiveCfg, true, UINT32_MAX);
if (RT_FAILURE(rc))
{
return rc;
}
pProxyDev->iActiveCfg++;
return VINF_SUCCESS;
}
/**
* Closes the proxy device.
*/
{
if (!pDevFBSD)
return;
struct usb_fs_uninit UsbFsUninit;
UsbFsUninit.dummy = 0;
if (pDevFBSD->paXferEndpoints)
if (pDevFBSD->paXferFree)
LogFlow(("usbProxyFreeBSDClose: returns\n"));
}
/**
* Reset a device.
*
* @returns VBox status code.
* @param pDev The device to reset.
*/
{
/* Close any open endpoints. */
for (unsigned i = 0; i < USBFBSD_MAXENDPOINTS; i++)
/* We need to release kernel resources first. */
struct usb_fs_uninit UsbFsUninit;
UsbFsUninit.dummy = 0;
/* Resetting is not possible from a normal user account */
#if 0
int iUnused = 0;
if (rc)
return RTErrConvertFromErrno(errno);
#endif
/* Allocate kernel resources again. */
struct usb_fs_init UsbFsInit;
if (!rc)
{
/* Retrieve current active configuration. */
if (rc)
{
}
else
{
pProxyDev->iActiveCfg++;
}
}
else
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.
*/
{
LogFlow(("usbProxyFreeBSDSetConfig: pProxyDev=%s cfg=%#x\n",
/* Close any open endpoints. */
for (unsigned i = 0; i < USBFBSD_MAXENDPOINTS; i++)
/* We need to release kernel resources first. */
struct usb_fs_uninit UsbFsUninit;
UsbFsUninit.dummy = 0;
int iCfgIndex = 0;
/* Get the configuration index matching the value. */
{
break;
}
{
return false;
}
/* Set the config */
if (RT_FAILURE(rc))
{
LogFlow(("usbProxyFreeBSDSetConfig: setting config index %d failed rc=%d errno=%Rrc\n", iCfgIndex, rc, RTErrConvertFromErrno(errno)));
return false;
}
/* Allocate kernel resources again. */
struct usb_fs_init UsbFsInit;
if (!rc)
return true;
else
return false;
}
/**
* Claims an interface.
* @returns success indicator.
*/
{
LogFlow(("usbProxyFreeBSDClaimInterface: pProxyDev=%s ifnum=%#x\n", pProxyDev->pUsbIns->pszName, iIf));
if (RT_FAILURE(rc))
return false;
return true;
}
/**
* Releases an interface.
* @returns success indicator.
*/
{
LogFlow(("usbProxyFreeBSDReleaseInterface: pProxyDev=%s ifnum=%#x\n", pProxyDev->pUsbIns->pszName, iIf));
if (RT_FAILURE(rc))
return false;
return true;
}
/**
* SET_INTERFACE.
*
* @returns success indicator.
*/
{
/* Close any open endpoints. */
for (unsigned i = 0; i < USBFBSD_MAXENDPOINTS; i++)
/* We need to release kernel resources first. */
struct usb_fs_uninit UsbFsUninit;
UsbFsUninit.dummy = 0;
struct usb_alt_interface UsbIntAlt;
if (rc)
{
LogFlow(("usbProxyFreeBSDSetInterface: Setting interface %d %d failed rc=%d errno=%Rrc\n", iIf, iAlt, rc,RTErrConvertFromErrno(errno)));
return false;
}
/* Allocate kernel resources again. */
struct usb_fs_init UsbFsInit;
if (!rc)
return true;
else
return false;
}
/**
* Clears the halted endpoint 'EndPt'.
*/
{
LogFlow(("usbProxyFreeBSDClearHaltedEp: pProxyDev=%s EndPt=%u\n", pProxyDev->pUsbIns->pszName, EndPt));
/*
* Clearing the zero control pipe doesn't make sense. Just ignore it.
*/
if (EndPt == 0)
return true;
struct usb_ctl_request Req;
usbProxyFreeBSDSetupReq(&Req.ucr_request, VUSB_DIR_TO_DEV | VUSB_TO_ENDPOINT, VUSB_REQ_CLEAR_FEATURE, 0, EndPt, 0);
if (rc)
{
LogFlow(("usbProxyFreeBSDClearHaltedEp: failed rc=%d errno=%Rrc\n", rc, RTErrConvertFromErrno(errno)));
return false;
}
LogFlow(("usbProxyFreeBSDClearHaltedEp: succeeded\n"));
return true;
}
/**
* @copydoc USBPROXYBACK::pfnUrbQueue
*/
{
if (!pEndpointFBSD)
return false;
struct usb_fs_start UsbFsStart;
unsigned cFrames;
{
{
cFrames = 2;
}
else
cFrames = 1;
}
else
{
cFrames = 1;
}
/* Start the transfer */
LogFlow(("usbProxyFreeBSDUrbQueue: USB_FS_START returned rc=%d errno=%Rrc\n", rc, RTErrConvertFromErrno(errno)));
if (rc)
{
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.
*/
{
/* We will poll for finished urbs because the ioctl doesn't take a timeout parameter. */
struct usb_fs_complete UsbFsComplete;
UsbFsComplete.ep_index = 0;
if (!rc)
{
switch (pXferEndpoint->status)
{
break;
case USB_ERR_STALLED:
break;
default:
}
}
else
{
if (rc == 1)
{
// do
{
UsbFsComplete.ep_index = 0;
if (!rc)
{
switch (pXferEndpoint->status)
{
break;
case USB_ERR_STALLED:
break;
default:
}
AssertMsg(((rc == -1) && (errno == EBUSY)), ("Expected return value rc=%d rc=%Rrc\n", rc, RTErrConvertFromErrno(errno)));
}
else
}
// while (!rc);
}
else
LogFlow(("poll returned rc=%d rcRT=%Rrc\n", rc, rc < 0 ? RTErrConvertFromErrno(errno) : VERR_TIMEOUT));
}
return pUrb;
}
/**
* Cancels the URB.
* The URB requires reaping, so we don't change its state.
*/
{
}
/**
* The FreeBSD USB Proxy Backend.
*/
extern const USBPROXYBACK g_USBProxyDeviceHost =
{
"host",
0
};
/*
* Local Variables:
* mode: c
* c-file-style: "bsd"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: s
* End:
*/