USBProxyDevice-solaris.cpp revision 0aefc1b31a411a6cdfa560b5d95d96b47f6fd9e4
/* $Id$ */
/** @file
* USB device proxy - the Solaris backend.
*/
/*
* Copyright (C) 2009-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 DEBUG_ramshankar
# define LOG_INSTANCE RTLogRelDefaultInstance()
#endif
#include <errno.h>
#include <strings.h>
#include <limits.h>
#include <iprt/critsect.h>
#include "../USBProxyDevice.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). */
/** Pointer to the next solaris URB. */
struct USBPROXYURBSOL *pNext;
/** Pointer to the previous solaris URB. */
struct USBPROXYURBSOL *pPrev;
/**
* 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. */
/** Pointer to the proxy device instance. */
/** Critical section protecting the two lists. */
/** The list of free solaris URBs. Singly linked. */
/** 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. */
/** The list of landed solaris URBs. Doubly linked.
* Only the split head will appear in this list. */
/** The tail of the landed solaris URBs. */
/** Pipe handle for waking up - writing end. */
/** Pipe handle for waking up - reading end. */
/**
* Allocates a Solaris URB request structure.
*
* @returns Pointer to an active URB request.
* @returns NULL on failure.
*
* @param pDevSol The solaris USB device.
*/
{
/*
* Try remove a Solaris URB from the free list, if none there allocate a new one.
*/
if (pUrbSol)
else
{
if (!pUrbSol)
return NULL;
}
/*
* Link it into the active list
*/
return pUrbSol;
}
/**
* Frees a Solaris URB request structure.
*
* @param pDevSol The Solaris USB device.
* @param pUrbSol The Solaris URB to free.
*/
{
/*
* Remove from the active or taxing list.
*/
else
AssertFailed();
/*
* Link it into the free list.
*/
}
/*
* Close the connection to the USB client driver.
*
* to recognize active devices, and hence if this device is unplugged we should no
* longer keep the client driver loaded.
*/
{
}
/**
* 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)
{
{
return VERR_VUSB_DEVICE_NOT_ATTACHED;
}
int Ret = -1;
if (RT_SUCCESS(rc))
{
{
{
LogRel((USBPROXY ":Command %#x failed, USB Device '%s' disconnected!\n", Function, pDevSol->pProxyDev->pUsbIns->pszName));
}
else
}
}
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.
*
*/
{
int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_GET_CONFIG, &GetConfigReq, sizeof(GetConfigReq));
if (RT_SUCCESS(rc))
{
}
else
{
if (rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
}
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".
* @param pvBackend Backend specific pointer, unused for the solaris backend.
*/
static DECLCALLBACK(int) usbProxySolarisOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress, void *pvBackend)
{
LogFlowFunc((USBPROXY ":usbProxySolarisOpen pProxyDev=%p pszAddress=%s pvBackend=%p\n", pProxyDev, pszAddress, pvBackend));
/*
* Initialize our USB R3 lib.
*/
int rc = USBLibInit();
if (RT_SUCCESS(rc))
{
/*
* Allocate and initialize the solaris backend data.
*/
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/*
* Create wakeup pipe.
*/
if (RT_SUCCESS(rc))
{
int Instance;
char *pszDevicePath = NULL;
if (RT_SUCCESS(rc))
{
/*
* Open the client driver.
*/
rc = RTFileOpen(&hFile, pDevSol->pszDevicePath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
if (RT_SUCCESS(rc))
{
/*
* Verify client driver version.
*/
rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_GET_VERSION, &GetVersionReq, sizeof(GetVersionReq));
if (RT_SUCCESS(rc))
{
{
/*
* Try & get the current cached config from Solaris.
*/
return VINF_SUCCESS;
}
else
{
}
}
else
}
else
LogRel((USBPROXY ":failed to open device. rc=%Rrc pszDevicePath=%s\n", rc, pDevSol->pszDevicePath));
}
else
{
LogRel((USBPROXY ":failed to get client info. rc=%Rrc pszDevicePath=%s\n", rc, pDevSol->pszDevicePath));
if (rc == VERR_NOT_FOUND)
}
}
}
else
}
else
}
else
USBLibTerm();
return rc;
}
/**
* Close the USB device.
*
* @param pProxyDev The device instance.
*/
{
/* Close the device (do not re-enumerate). */
/*
* Now we can close it and free all the resources.
*/
{
}
{
}
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.
*/
{
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. */
/* Soft reset the device. */
if (RT_SUCCESS(rc))
{
/* Get the active config. Solaris USBA sets a default config. */
}
else if (rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
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.
*/
{
int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_SET_CONFIG, &SetConfigReq, sizeof(SetConfigReq));
if ( RT_FAILURE(rc)
&& rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
return rc;
}
/**
* Claims an interface.
*
* as and when required with endpoint opens.
*
* @returns success indicator (always true).
*/
{
return VINF_SUCCESS;
}
/**
* Releases an interface.
*
* as and when required with endpoint opens.
*
* @returns success indicator.
*/
{
return VINF_SUCCESS;
}
/**
* Specify an alternate setting for the specified interface of the current configuration.
*
* @returns success indicator.
*/
{
LogFlowFunc((USBPROXY ":usbProxySolarisSetInterface: pProxyDev=%p iIf=%d iAlt=%d\n", pProxyDev, iIf, iAlt));
int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_SET_INTERFACE, &SetInterfaceReq, sizeof(SetInterfaceReq));
if ( RT_FAILURE(rc)
&& rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
return rc;
}
/**
* Clears the halted endpoint 'EndPt'.
*/
{
if ( RT_FAILURE(rc)
&& rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
return rc;
}
/**
* @copydoc USBPROXYBACK::pfnUrbQueue
*/
{
LogFlowFunc((USBPROXY ": usbProxySolarisUrbQueue: pProxyDev=%s pUrb=%p EndPt=%#x enmDir=%d cbData=%d pvData=%p\n",
if (RT_UNLIKELY(!pUrbSol))
{
return VERR_NO_MEMORY;
}
if (EndPt)
{
{
}
}
if (RT_SUCCESS(rc))
{
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.
*/
{
LogFlowFunc((USBPROXY ":usbProxySolarisUrbCancel pUrb=%p pUrbSol=%p pDevSol=%p\n", pUrb, pUrbSol, pUrbSol->pDevSol));
/* Aborting the control pipe isn't supported, pretend success. */
return VINF_SUCCESS;
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)
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.
*/
{
//LogFlowFunc((USBPROXY ":usbProxySolarisUrbReap pProxyDev=%p cMillies=%u\n", pProxyDev, cMillies));
/*
* Don't block if nothing is in the air.
*/
if (!pDevSol->pInFlightHead)
return NULL;
/*
* Deque URBs inflight or those landed.
*/
if (cMillies > 0)
{
for (;;)
{
if (rc > 0)
{
{
LogRel((USBPROXY ":Reaping failed, USB Device '%s' disconnected!\n", pDevSol->pProxyDev->pUsbIns->pszName));
}
{
/* Got woken up, drain pipe. */
/*
* 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.
*/
return NULL;
}
break;
}
if (rc == 0)
{
//LogFlow((USBPROXY ":usbProxySolarisUrbReap: Timed out\n"));
return NULL;
}
{
return NULL;
}
}
}
/*
* Any URBs pending delivery?
*/
while (pDevSol->pTaxingHead)
{
if (pUrbSol)
{
if (pUrb)
{
}
}
}
return pUrb;
}
/**
*
* @param pDevSol The Solaris device instance.
*/
{
if (RT_SUCCESS(rc))
{
{
{
/*
* Update the URB.
*/
{
{
}
LogFlow((USBPROXY ":usbProxySolarisUrbComplete ISOC cbData=%d cbActPktSum=%d\n", pUrb->cbData, cbData));
}
else
{
}
/*
* Remove from the active list.
*/
else
{
}
/*
* Link it into the taxing list.
*/
if (pDevSol->pTaxingTail)
else
pUrb->cbData, pUrb->EndPt, pUrb->enmDir, pUrb->enmStatus == VUSBSTATUS_OK ? "OK" : "** Failed **", pUrb->enmStatus));
// if (pUrb->cbData < 2049)
// LogFlow((USBPROXY "%.*Rhxd\n", pUrb->cbData, pUrb->abData));
return pUrb;
}
}
}
else
{
if (rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
}
return NULL;
}
{
}
/**
* The Solaris USB Proxy Backend.
*/
extern const USBPROXYBACK g_USBProxyDeviceHost =
{
/* pszName */
"host",
/* cbBackend */
sizeof(USBPROXYDEVSOL),
NULL,
0
};