/* $Id$ */
/** @file
* USB device proxy - the Linux backend.
*/
/*
* Copyright (C) 2006-2010 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.
*/
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/semaphore.h>
#include "../USBProxyDevice.h"
#define INCL_BASE
#define INCL_ERRORS
#include <os2.h>
#include <usbcalls.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Structure for keeping track of the URBs for a device.
*/
typedef struct USBPROXYURBOS2
{
/** Pointer to the virtual URB. */
/** Pointer to the next OS/2 URB. */
/** Pointer to the previous OS/2 URB. */
/**
* Data for the OS/2 usb proxy backend.
*/
typedef struct USBPROXYDEVOS2
{
/** The async thread for this device.
* Currently only one thread is used, but this might have to change... */
/** Thread termination indicator. */
bool volatile fTerminate;
/** The USB handle. */
/** Critical section protecting the lists. */
/** For blocking reap calls. */
/** List of URBs to process. Doubly linked. */
/** The tail pointer. */
/** The list of free linux URBs. Singly linked. */
/** The list of active linux 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 linux URBs. Doubly linked.
* Only the split head will appear in this list. */
/** The tail of the landed linux URBs. */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifdef DYNAMIC_USBCALLS
static int usbProxyOs2GlobalInit(void);
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
#ifdef DYNAMIC_USBCALLS
static APIRET (APIENTRY *g_pfnUsbCtrlMessage)(USBHANDLE, UCHAR, UCHAR, USHORT, USHORT, USHORT, void *, ULONG);
#else
#endif
#ifdef DYNAMIC_USBCALLS
/**
* Loads usbcalls.dll and resolves the symbols we need.
*
* The usbcalls.dll will not be unloaded.
*
* @returns VBox status code.
*/
static int usbProxyOs2GlobalInit(void)
{
if (RT_SUCCESS(rc))
{
&& (rc = DosQueryProcAddr(g_hmod, 0, (PCSZ)"UsbCtrlMessage", (PPFN)&g_pfnUsbCtrlMessage)) == NO_ERROR
)
{
return VINF_SUCCESS;
}
g_pfnUsbOpen = NULL;
}
g_hmod = NULLHANDLE;
return rc;
}
#endif
/**
* Allocates a OS/2 URB request structure.
* @returns Pointer to an active URB request.
* @returns NULL on failure.
* @param pProxyDev The proxy device instance.
*/
{
/*
* Try remove a linux URB from the free list, if none there allocate a new one.
*/
if (pUrbOs2)
else
{
if (!pUrbOs2)
return NULL;
}
/*
* Link it into the active list
*/
return pUrbOs2;
}
/**
* Frees a linux URB request structure.
*
* @param pProxyDev The proxy device instance.
* @param pUrbOs2 The linux URB to free.
*/
{
/*
* Remove from the active list.
*/
/*
* Link it into the free list.
*/
}
/**
* Thread for executing the URBs asynchronously.
*
* @returns VINF_SUCCESS.
* @param Thread Thread handle (IPRT).
* @param pvProxyDev Pointer to the proxy device we're servicing.
*/
{
/*
* The main loop.
*
* We're always in the critsect, except when waiting or submitting a URB.
*/
while (!pDevOs2->fTerminate)
{
/*
* Anything to do?
*/
if (pUrbOs2)
{
else
/*
* Move it to the in-flight list and submit it.
*/
if (pDevOs2->pInFlightHead)
//else
// pDevOs2->pInFlightTail = pUrbOs2;
/*
* Process the URB.
*/
{
{
if (pvLow)
if (rc)
{
cbLow = 0;
}
}
if (pvLow)
}
{
case VUSBXFERTYPE_MSG:
{
pSetup + 1,
5*60000 /* min */);
break;
}
case VUSBXFERTYPE_BULK:
{
/* there is a thing with altnative interface thing here... */
{
rc = g_pfnUsbBulkRead2(pDevOs2->hDevice, pUrb->EndPt | 0x80, 0, !pUrb->fShortNotOk, &cbData, pbData, 500);//5*6000);
}
else
{
rc = g_pfnUsbBulkWrite2(pDevOs2->hDevice, pUrb->EndPt, 0, !pUrb->fShortNotOk, cbData, pbData, 500);//5*6000);
}
break;
}
case VUSBXFERTYPE_INTR:
case VUSBXFERTYPE_ISOC:
default:
break;
}
/* unbuffer */
/* Convert rc to USB status code. */
if (!rc)
{
}
else
Log2(("%s: usbProxyOs2AsyncThread: orc=%d enmStatus=%d cbData=%d \n", pUrb->pszDesc, orc, pUrb->enmStatus, pUrb->cbData)); NOREF(orc);
/*
* Retire it to the completed list
*/
if (pDevOs2->pTaxingTail)
else
Log2(("%s: usbProxyOs2AsyncThread: orc=%d enmStatus=%d cbData=%d!\n", pUrb->pszDesc, orc, pUrb->enmStatus, pUrb->cbData)); NOREF(orc);
}
else
{
/*
* Wait for something to do.
*/
}
}
if (pvLow)
return VINF_SUCCESS;
}
/**
*
* @returns VBox status code.
* @param pProxyDev The device instance.
* @param pszAddress The path to the device.
* @param pvBackend Backend specific pointer, unused for the linux backend.
*/
{
int rc;
/*
* Lazy init.
*/
#ifdef DYNAMIC_USBCALLS
if (!g_pfnUsbOpen)
{
rc = usbProxyOs2GlobalInit();
if (RT_FAILURE(rc))
return rc;
}
#else
static bool g_fInitialized = false;
if (!g_fInitialized)
{
rc = InitUsbCalls();
return RTErrConvertFromOS2(rc);
g_fInitialized = true;
}
#endif
/*
* Parse out the open parameters from the address string.
*/
do
{
switch (chValue)
{
default:
}
if (*psz == ';')
psz++;
} while (*psz);
/*
* Try open (acquire) it.
*/
if (!rc)
{
/*
* Allocate and initialize the OS/2 backend data.
*/
if (pDevOs2)
{
pDevOs2->fTerminate = false;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/** @todo
* Determine the active configuration.
*/
//pProxyDev->cIgnoreSetConfigs = 1;
//pProxyDev->iActiveCfg = 1;
pProxyDev->cIgnoreSetConfigs = 0;
/*
* Create the async worker thread and we're done.
*/
rc = RTThreadCreate(&pDevOs2->Thread, usbProxyOs2AsyncThread, pProxyDev, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "usbproxy");
if (RT_SUCCESS(rc))
{
LogFlow(("usbProxyOs2Open(%p, %s): returns successfully - iActiveCfg=%d\n",
return VINF_SUCCESS;
}
/* failure */
}
}
}
else
rc = VERR_NO_MEMORY;
}
else
Log(("usbProxyOs2Open(%p, %s) failed, rc=%Rrc! urc=%d\n", pProxyDev, pszAddress, rc, urc)); NOREF(urc);
return rc;
}
/**
* Closes the proxy device.
*/
{
if (!pDevOs2)
return;
/*
* Tell the thread to terminate.
*/
/*
* Now we can free all the resources and close the device.
*/
{
}
LogFlow(("usbProxyOs2Close: returns\n"));
}
/**
* Reset a device.
*
* @returns VBox status code.
* @param pDev The device to reset.
*/
{
return VINF_SUCCESS;
}
/**
* 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(("usbProxyOs2SetConfig: pProxyDev=%s cfg=%#x\n",
/*
* This is sync - bad.
*/
0x00, /* bmRequestType - ?? */
0x09, /* bRequest - ?? */
iCfg, /* wValue - configuration */
0, /* wIndex*/
0, /* wLength */
NULL, /* pvData */
50 /* Timeout (ms) */);
if (rc)
LogFlow(("usbProxyOs2SetConfig: pProxyDev=%s cfg=%#X -> rc=%d\n", pProxyDev->pUsbIns->pszName, iCfg, rc));
return rc == 0;
}
/**
* Claims an interface.
* @returns success indicator.
*/
{
return true;
}
/**
* Releases an interface.
* @returns success indicator.
*/
{
LogFlow(("usbProxyOs2ReleaseInterface: pProxyDev=%s ifnum=%#x\n", pProxyDev->pUsbIns->pszName, iIf));
return true;
}
/**
* SET_INTERFACE.
*
* @returns success indicator.
*/
{
return true;
}
/**
* Clears the halted endpoint 'EndPt'.
*/
{
/*
* This is sync - bad.
*/
0x02, /* bmRequestType - ?? */
0x01, /* bRequest - ?? */
0, /* wValue - endpoint halt */
EndPt, /* wIndex - endpoint # */
0, /* wLength */
NULL, /* pvData */
50 /* Timeout (ms) */);
if (rc)
LogFlow(("usbProxyOs2ClearHaltedEp: pProxyDev=%s EndPt=%u -> rc=%d\n", pProxyDev->pUsbIns->pszName, EndPt, rc));
return rc == 0;
}
/**
* @copydoc USBPROXYBACK::pfnUrbQueue
*/
{
LogFlow(("usbProxyOs2UrbQueue: pProxyDev=%s pUrb=%p EndPt=%d cbData=%d\n",
/*
* Quickly validate the input.
*/
{
case VUSBDIRECTION_IN:
case VUSBDIRECTION_OUT:
break;
default:
return false;
}
{
case VUSBXFERTYPE_MSG:
break;
case VUSBXFERTYPE_BULK:
break;
///@todo case VUSBXFERTYPE_INTR:
// break;
// case VUSBXFERTYPE_ISOC:
// break;
default:
return false;
}
/*
* Allocate an OS/2 urb tracking structure, initialize it,
* add it to the todo list, and wake up the async thread.
*/
if (!pUrbOs2)
return false;
else
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.
*/
{
for (;;)
{
/*
* Any URBs pending delivery?
*/
if (pUrbOs2)
{
break;
}
/*
* Block for something to completed, if requested and sensible.
*/
if (!cMillies)
break;
if ( !pDevOs2->pInFlightHead
break;
cMillies = 0;
}
return pUrb;
}
/**
* Cancels the URB.
* The URB requires reaping, so we don't change its state.
*/
{
#if 0
if (pUrbOs2->pSplitHead)
{
/* split */
{
if (pCur->fSplitElementReaped)
continue;
continue;
break;
Log(("usb-linux: Discard URB %p failed, errno=%d. pProxyDev=%s!!! (split)\n",
}
}
else
{
/* unsplit */
Log(("usb-linux: Discard URB %p failed, errno=%d. pProxyDev=%s!!!\n",
}
#endif
}
/**
* The Linux USB Proxy Backend.
*/
{
"host",
NULL,
0
};