USBProxyDevice.cpp revision 43747b1f0bc8302a238fb35e55857a5e9aa1933d
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere/* $Id$ */
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere/** @file
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * USBProxy - USB device proxy.
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere */
8468be3efdd76e64e871fe3799397721f988ff42noodl
0679f53d7ea85c2cbb4970366f592782d9e26f5flgentis/*
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * Copyright (C) 2006-2007 Oracle Corporation
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere *
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * This file is part of VirtualBox Open Source Edition (OSE), as
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * available from http://www.virtualbox.org. This file is free software;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * you can redistribute it and/or modify it under the terms of the GNU
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * General Public License (GPL) as published by the Free Software
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * Foundation, in version 2 as it comes in the "COPYING" file of the
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere */
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere/*******************************************************************************
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere* Header Files *
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere*******************************************************************************/
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere#include <VBox/usb.h>
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere#include <VBox/usbfilter.h>
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere#include <VBox/vmm/pdm.h>
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere#include <VBox/err.h>
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere#include <iprt/alloc.h>
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere#include <iprt/string.h>
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere#include <VBox/log.h>
0679f53d7ea85c2cbb4970366f592782d9e26f5flgentis#include <iprt/assert.h>
0679f53d7ea85c2cbb4970366f592782d9e26f5flgentis#include "USBProxyDevice.h"
d97a741c05718ce163becb5a588116d350926863lgentis#include "VUSBInternal.h"
d97a741c05718ce163becb5a588116d350926863lgentis#include "Builtins.h"
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere/*******************************************************************************
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere* Global Variables *
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere*******************************************************************************/
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere/** A dummy name used early during the construction phase to avoid log crashes. */
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclerestatic char g_szDummyName[] = "proxy xxxx:yyyy";
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere/* Synchronously obtain a standard USB descriptor for a device, used in order
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * to grab configuration descriptors when we first add the device
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere */
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclerestatic void *GetStdDescSync(PUSBPROXYDEV pProxyDev, uint8_t iDescType, uint8_t iIdx, uint16_t LangId, uint16_t cbHint)
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere{
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere LogFlow(("GetStdDescSync: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere for (;;)
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere {
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere /*
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * Setup a MSG URB, queue and reap it.
f614d5d9e20c335ff9553d37a89b4eaeed7897dajfclere */
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere VUSBURB Urb;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere AssertCompile(RT_SIZEOFMEMB(VUSBURB, abData) >= _4K);
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Urb.u32Magic = VUSBURB_MAGIC;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Urb.enmState = VUSBURBSTATE_IN_FLIGHT;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Urb.pszDesc = (char*)"URB sync";
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere memset(&Urb.VUsb, 0, sizeof(Urb.VUsb));
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere memset(&Urb.Hci, 0, sizeof(Urb.Hci));
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Urb.Dev.pvPrivate = NULL;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Urb.Dev.pNext = NULL;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Urb.pUsbIns = pProxyDev->pUsbIns;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Urb.DstAddress = 0;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Urb.EndPt = 0;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Urb.enmType = VUSBXFERTYPE_MSG;
58cca7772bc8086a8d3d4ae8f55e7d9378f3d731lgentis Urb.enmDir = VUSBDIRECTION_IN;
58cca7772bc8086a8d3d4ae8f55e7d9378f3d731lgentis Urb.fShortNotOk = false;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Urb.enmStatus = VUSBSTATUS_INVALID;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere cbHint = RT_MIN(cbHint, sizeof(Urb.abData) - sizeof(VUSBSETUP));
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Urb.cbData = cbHint + sizeof(VUSBSETUP);
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere PVUSBSETUP pSetup = (PVUSBSETUP)Urb.abData;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere pSetup->bmRequestType = VUSB_DIR_TO_HOST | VUSB_REQ_STANDARD | VUSB_TO_DEVICE;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere pSetup->bRequest = VUSB_REQ_GET_DESCRIPTOR;
8468be3efdd76e64e871fe3799397721f988ff42noodl pSetup->wValue = (iDescType << 8) | iIdx;
8468be3efdd76e64e871fe3799397721f988ff42noodl pSetup->wIndex = LangId;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere pSetup->wLength = cbHint;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere if (!pProxyDev->pOps->pfnUrbQueue(&Urb))
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere break;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere /* Don't wait forever, it's just a simple request that should
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere return immediately. Since we're executing in the EMT thread
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere it's important not to get stuck here. (Some of the builtin
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere iMac devices may not refuse respond for instance.) */
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere PVUSBURB pUrbReaped = pProxyDev->pOps->pfnUrbReap(pProxyDev, 10000 /* ms */);
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere if (!pUrbReaped)
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere {
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere pProxyDev->pOps->pfnUrbCancel(&Urb);
8468be3efdd76e64e871fe3799397721f988ff42noodl pUrbReaped = pProxyDev->pOps->pfnUrbReap(pProxyDev, RT_INDEFINITE_WAIT);
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere }
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere if (pUrbReaped != &Urb)
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere {
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Log(("GetStdDescSync: pfnUrbReap failed, pUrbReaped=%p\n", pUrbReaped));
58cca7772bc8086a8d3d4ae8f55e7d9378f3d731lgentis break;
58cca7772bc8086a8d3d4ae8f55e7d9378f3d731lgentis }
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere if (Urb.enmStatus != VUSBSTATUS_OK)
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere {
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Log(("GetStdDescSync: Urb.enmStatus=%d\n", Urb.enmStatus));
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere break;
58cca7772bc8086a8d3d4ae8f55e7d9378f3d731lgentis }
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere /*
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * Check the length, config descriptors have total_length field
58cca7772bc8086a8d3d4ae8f55e7d9378f3d731lgentis */
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere uint8_t *pbDesc = (uint8_t *)(pSetup + 1);
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere uint32_t cbDesc;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere if (iDescType == VUSB_DT_CONFIG)
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere {
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere if (Urb.cbData < sizeof(VUSBSETUP) + 4)
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere {
17d2fcb33284ba6a887ec86305dcaddcd5459dd4lgentis Log(("GetStdDescSync: Urb.cbData=%#x (min 4)\n", Urb.cbData));
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere break;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere }
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere cbDesc = RT_LE2H_U16(((uint16_t *)pbDesc)[1]);
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere }
491f8a844db495830f377211855efe643f503bc1lgentis else
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere {
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere if (Urb.cbData < sizeof(VUSBSETUP) + 1)
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere {
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Log(("GetStdDescSync: Urb.cbData=%#x (min 1)\n", Urb.cbData));
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere break;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere }
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere cbDesc = ((uint8_t *)pbDesc)[0];
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere }
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
58cca7772bc8086a8d3d4ae8f55e7d9378f3d731lgentis Log(("GetStdDescSync: got Urb.cbData=%u, cbDesc=%u cbHint=%u\n", Urb.cbData, cbDesc, cbHint));
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere if ( Urb.cbData == cbHint + sizeof(VUSBSETUP)
58cca7772bc8086a8d3d4ae8f55e7d9378f3d731lgentis && cbDesc > Urb.cbData - sizeof(VUSBSETUP))
58cca7772bc8086a8d3d4ae8f55e7d9378f3d731lgentis {
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere cbHint = cbDesc;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere if (cbHint > sizeof(Urb.abData))
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere {
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere AssertMsgFailed(("cbHint=%u\n", cbHint));
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere break;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere }
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere continue;
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere }
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere Assert(cbDesc <= Urb.cbData - sizeof(VUSBSETUP));
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere#ifdef LOG_ENABLED
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere vusbUrbTrace(&Urb, "GetStdDescSync", true);
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere#endif
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere /*
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere * Fine, we got everything return a heap duplicate of the descriptor.
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere */
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere return RTMemDup(pbDesc, cbDesc);
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere }
c988e0b6814e1d0f8eaf9139ca13e2e2a7fcc96ejfclere return NULL;
}
/**
* Frees a descriptor returned by GetStdDescSync().
*/
static void free_desc(void *pvDesc)
{
RTMemFree(pvDesc);
}
/**
* Get and a device descriptor and byteswap it appropriately.
*/
static bool usbProxyGetDeviceDesc(PUSBPROXYDEV pProxyDev, PVUSBDESCDEVICE pOut)
{
/*
* Get the descriptor from the device.
*/
PVUSBDESCDEVICE pIn = (PVUSBDESCDEVICE)GetStdDescSync(pProxyDev, VUSB_DT_DEVICE, 0, 0, VUSB_DT_DEVICE_MIN_LEN);
if (!pIn)
{
Log(("usbProxyGetDeviceDesc: pProxyDev=%s: GetStdDescSync failed\n", pProxyDev->pUsbIns->pszName));
return false;
}
if (pIn->bLength < VUSB_DT_DEVICE_MIN_LEN)
{
Log(("usb-proxy: pProxyDev=%s: Corrupted device descriptor. bLength=%d\n", pProxyDev->pUsbIns->pszName, pIn->bLength));
return false;
}
/*
* Convert it.
*/
pOut->bLength = VUSB_DT_DEVICE_MIN_LEN;
pOut->bDescriptorType = VUSB_DT_DEVICE;
pOut->bcdUSB = RT_LE2H_U16(pIn->bcdUSB);
pOut->bDeviceClass = pIn->bDeviceClass;
pOut->bDeviceSubClass = pIn->bDeviceSubClass;
pOut->bDeviceProtocol = pIn->bDeviceProtocol;
pOut->bMaxPacketSize0 = pIn->bMaxPacketSize0;
pOut->idVendor = RT_LE2H_U16(pIn->idVendor);
pOut->idProduct = RT_LE2H_U16(pIn->idProduct);
pOut->bcdDevice = RT_LE2H_U16(pIn->bcdDevice);
pOut->iManufacturer = pIn->iManufacturer;
pOut->iProduct = pIn->iProduct;
pOut->iSerialNumber = pIn->iSerialNumber;
pOut->bNumConfigurations = pIn->bNumConfigurations;
free_desc(pIn);
return true;
}
/**
* Count the numbers and types of each kind of descriptor that we need to
* copy out of the config descriptor
*/
struct desc_counts
{
size_t num_ed, num_id, num_if;
/** bitmap (128 bits) */
uint32_t idmap[4];
};
static int count_descriptors(struct desc_counts *cnt, uint8_t *buf, size_t len)
{
PVUSBDESCCONFIG cfg;
uint8_t *tmp, *end;
uint32_t i, x;
memset(cnt, 0, sizeof(*cnt));
end = buf + len;
cfg = (PVUSBDESCCONFIG)buf;
if ( cfg->bLength < VUSB_DT_CONFIG_MIN_LEN )
return 0;
if ( cfg->bLength > len )
return 0;
for (tmp = buf + cfg->bLength; ((tmp + 1) < end) && *tmp; tmp += *tmp)
{
uint8_t type;
uint32_t ifnum;
PVUSBDESCINTERFACE id;
PVUSBDESCENDPOINT ed;
type = *(tmp + 1);
switch ( type ) {
case VUSB_DT_INTERFACE:
id = (PVUSBDESCINTERFACE)tmp;
if ( id->bLength < VUSB_DT_INTERFACE_MIN_LEN )
return 0;
cnt->num_id++;
ifnum = id->bInterfaceNumber;
cnt->idmap[ifnum >> 6] |= (1 << (ifnum & 0x1f));
break;
case VUSB_DT_ENDPOINT:
ed = (PVUSBDESCENDPOINT)tmp;
if ( ed->bLength < VUSB_DT_ENDPOINT_MIN_LEN )
return 0;
cnt->num_ed++;
break;
default:
break;
}
}
/* count interfaces */
for(i=0; i < RT_ELEMENTS(cnt->idmap); i++)
for(x=1; x; x<<=1)
if ( cnt->idmap[i] & x )
cnt->num_if++;
return 1;
}
/* Setup a vusb_interface structure given some preallocated structures
* to use, (we counted them already)
*/
static int copy_interface(PVUSBINTERFACE pIf, uint8_t ifnum,
PVUSBDESCINTERFACEEX *id, PVUSBDESCENDPOINTEX *ed,
uint8_t *buf, size_t len)
{
PVUSBDESCINTERFACEEX cur_if = NULL;
uint32_t altmap[4] = {0,};
uint8_t *tmp, *end = buf + len;
uint8_t alt;
int state;
size_t num_ep = 0;
buf += *(uint8_t *)buf;
pIf->cSettings = 0;
pIf->paSettings = NULL;
for (tmp = buf, state = 0; ((tmp + 1) < end) && *tmp; tmp += *tmp)
{
uint8_t type;
PVUSBDESCINTERFACE ifd;
PVUSBDESCENDPOINT epd;
PVUSBDESCENDPOINTEX cur_ep;
type = tmp[1];
switch ( type ) {
case VUSB_DT_INTERFACE:
state = 0;
ifd = (PVUSBDESCINTERFACE)tmp;
/* Ignoring this interface */
if ( ifd->bInterfaceNumber != ifnum )
break;
/* Check we didn't see this alternate setting already
* because that will break stuff
*/
alt = ifd->bAlternateSetting;
if ( altmap[alt >> 6] & (1 << (alt & 0x1f)) )
return 0;
altmap[alt >> 6] |= (1 << (alt & 0x1f));
cur_if = *id;
(*id)++;
if ( pIf->cSettings == 0 )
pIf->paSettings = cur_if;
memcpy(cur_if, ifd, sizeof(cur_if->Core));
/** @todo copy any additional descriptor bytes into pvMore */
pIf->cSettings++;
state = 1;
num_ep = 0;
break;
case VUSB_DT_ENDPOINT:
if ( state == 0 )
break;
epd = (PVUSBDESCENDPOINT)tmp;
cur_ep = *ed;
(*ed)++;
if ( num_ep == 0 )
cur_if->paEndpoints = cur_ep;
if ( num_ep > cur_if->Core.bNumEndpoints )
return 0;
memcpy(cur_ep, epd, sizeof(cur_ep->Core));
/** @todo copy any additional descriptor bytes into pvMore */
cur_ep->Core.wMaxPacketSize = RT_LE2H_U16(cur_ep->Core.wMaxPacketSize);
num_ep++;
break;
default:
/** @todo Here be dragons! Additional descriptors needs copying into pvClass
* (RTMemDup be your friend). @bugref{2693} */
break;
}
}
return 1;
}
/**
* Copy all of a devices config descriptors, this is needed so that the USB
* core layer knows all about how to map the different functions on to the
* virtual USB bus.
*/
static bool copy_config(PUSBPROXYDEV pProxyDev, uint8_t idx, PVUSBDESCCONFIGEX out)
{
PVUSBDESCCONFIG cfg;
PVUSBINTERFACE pIf;
PVUSBDESCINTERFACEEX ifd;
PVUSBDESCENDPOINTEX epd;
struct desc_counts cnt;
void *descs;
size_t tot_len;
size_t cbIface;
uint32_t i, x;
descs = GetStdDescSync(pProxyDev, VUSB_DT_CONFIG, idx, 0, VUSB_DT_CONFIG_MIN_LEN);
if ( descs == NULL ) {
Log(("copy_config: GetStdDescSync failed\n"));
return false;
}
cfg = (PVUSBDESCCONFIG)descs;
tot_len = RT_LE2H_U16(cfg->wTotalLength);
if ( !count_descriptors(&cnt, (uint8_t *)descs, tot_len) ) {
Log(("copy_config: count_descriptors failed\n"));
goto err;
}
if ( cfg->bNumInterfaces != cnt.num_if )
Log(("usb-proxy: config%u: bNumInterfaces %u != %u\n",
idx, cfg->bNumInterfaces, cnt.num_if));
Log(("usb-proxy: config%u: %u bytes id=%u ed=%u if=%u\n",
idx, tot_len, cnt.num_id, cnt.num_ed, cnt.num_if));
cbIface = cnt.num_if * sizeof(VUSBINTERFACE)
+ cnt.num_id * sizeof(VUSBDESCINTERFACEEX)
+ cnt.num_ed * sizeof(VUSBDESCENDPOINTEX);
out->paIfs = (PCVUSBINTERFACE)RTMemAllocZ(cbIface);
if ( out->paIfs == NULL ) {
free_desc(descs);
return false;
}
pIf = (PVUSBINTERFACE)out->paIfs;
ifd = (PVUSBDESCINTERFACEEX)&pIf[cnt.num_if];
epd = (PVUSBDESCENDPOINTEX)&ifd[cnt.num_id];
out->Core.bLength = cfg->bLength;
out->Core.bDescriptorType = cfg->bDescriptorType;
out->Core.wTotalLength = 0; /* Auto Calculated */
out->Core.bNumInterfaces = (uint8_t)cnt.num_if;
out->Core.bConfigurationValue = cfg->bConfigurationValue;
out->Core.iConfiguration = cfg->iConfiguration;
out->Core.bmAttributes = cfg->bmAttributes;
out->Core.MaxPower = cfg->MaxPower;
for(i=0; i < 4; i++)
for(x=0; x < 32; x++)
if ( cnt.idmap[i] & (1 << x) )
if ( !copy_interface(pIf++, (i << 6) | x, &ifd, &epd, (uint8_t *)descs, tot_len) ) {
Log(("copy_interface(%d,,) failed\n", pIf - 1));
goto err;
}
free_desc(descs);
return true;
err:
Log(("usb-proxy: config%u: Corrupted configuration descriptor\n", idx));
free_desc(descs);
return false;
}
/**
* Edit out masked interface descriptors.
*
* @param pProxyDev The proxy device
*/
static void usbProxyDevEditOutMaskedIfs(PUSBPROXYDEV pProxyDev)
{
unsigned cRemoved = 0;
PVUSBDESCCONFIGEX paCfgs = pProxyDev->paCfgDescs;
for (unsigned iCfg = 0; iCfg < pProxyDev->DevDesc.bNumConfigurations; iCfg++)
{
PVUSBINTERFACE paIfs = (PVUSBINTERFACE)paCfgs[iCfg].paIfs;
for (unsigned iIf = 0; iIf < paCfgs[iCfg].Core.bNumInterfaces; iIf++)
for (uint32_t iAlt = 0; iAlt < paIfs[iIf].cSettings; iAlt++)
if ( paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber < 32
&& ((1 << paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber) & pProxyDev->fMaskedIfs))
{
Log(("usb-proxy: removing interface #%d (iIf=%d iAlt=%d) on config #%d (iCfg=%d)\n",
paIfs[iIf].paSettings[iAlt].Core.bInterfaceNumber, iIf, iAlt, paCfgs[iCfg].Core.bConfigurationValue, iCfg));
cRemoved++;
paCfgs[iCfg].Core.bNumInterfaces--;
unsigned cToCopy = paCfgs[iCfg].Core.bNumInterfaces - iIf;
if (cToCopy)
memmove(&paIfs[iIf], &paIfs[iIf + 1], sizeof(paIfs[0]) * cToCopy);
memset(&paIfs[iIf + cToCopy], '\0', sizeof(paIfs[0]));
break;
}
}
Log(("usb-proxy: edited out %d interface(s).\n", cRemoved));
}
/**
* @copydoc PDMUSBREG::pfnUsbReset
*
* USB Device Proxy: Call OS specific code to reset the device.
*/
static DECLCALLBACK(int) usbProxyDevReset(PPDMUSBINS pUsbIns, bool fResetOnLinux)
{
PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
if (pProxyDev->fMaskedIfs)
{
Log(("usbProxyDevReset: pProxyDev=%s - ignoring reset request fMaskedIfs=%#x\n", pUsbIns->pszName, pProxyDev->fMaskedIfs));
return VINF_SUCCESS;
}
LogFlow(("usbProxyDevReset: pProxyDev=%s\n", pUsbIns->pszName));
return pProxyDev->pOps->pfnReset(pProxyDev, fResetOnLinux);
}
/**
* @copydoc PDMUSBREG::pfnUsbGetDescriptorCache
*/
static DECLCALLBACK(PCPDMUSBDESCCACHE) usbProxyDevGetDescriptorCache(PPDMUSBINS pUsbIns)
{
PUSBPROXYDEV pThis = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
return &pThis->DescCache;
}
/**
* @copydoc PDMUSBREG::pfnUsbSetConfiguration
*
* USB Device Proxy: Release claimed interfaces, tell the OS+device about the config change, claim the new interfaces.
*/
static DECLCALLBACK(int) usbProxyDevSetConfiguration(PPDMUSBINS pUsbIns, uint8_t bConfigurationValue,
const void *pvOldCfgDesc, const void *pvOldIfState, const void *pvNewCfgDesc)
{
PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
LogFlow(("usbProxyDevSetConfiguration: pProxyDev=%s iActiveCfg=%d bConfigurationValue=%d\n",
pUsbIns->pszName, pProxyDev->iActiveCfg, bConfigurationValue));
/*
* Release the current config.
*/
if (pvOldCfgDesc)
{
PCVUSBDESCCONFIGEX pOldCfgDesc = (PCVUSBDESCCONFIGEX)pvOldCfgDesc;
PCVUSBINTERFACESTATE pOldIfState = (PCVUSBINTERFACESTATE)pvOldIfState;
for (unsigned i = 0; i < pOldCfgDesc->Core.bNumInterfaces; i++)
if (pOldIfState[i].pCurIfDesc)
pProxyDev->pOps->pfnReleaseInterface(pProxyDev, pOldIfState[i].pCurIfDesc->Core.bInterfaceNumber);
}
/*
* Do the actual SET_CONFIGURE.
* The mess here is because most backends will already have selected a
* configuration and there are a bunch of devices which will freak out
* if we do SET_CONFIGURE twice with the same value. (PalmOne, TrekStor USB-StickGO, ..)
*
* After open and reset the backend should use the members iActiveCfg and cIgnoreSetConfigs
* to indicate the new configuration state and what to do on the next SET_CONFIGURATION call.
*/
if ( pProxyDev->iActiveCfg != bConfigurationValue
|| ( bConfigurationValue == 0
&& pProxyDev->iActiveCfg != -1 /* this test doesn't make sense, we know it's 0 */
&& pProxyDev->cIgnoreSetConfigs >= 2)
|| !pProxyDev->cIgnoreSetConfigs)
{
pProxyDev->cIgnoreSetConfigs = 0;
if (!pProxyDev->pOps->pfnSetConfig(pProxyDev, bConfigurationValue))
{
pProxyDev->iActiveCfg = -1;
return VERR_GENERAL_FAILURE;
}
pProxyDev->iActiveCfg = bConfigurationValue;
}
else if (pProxyDev->cIgnoreSetConfigs > 0)
pProxyDev->cIgnoreSetConfigs--;
/*
* Claim the interfaces.
*/
PCVUSBDESCCONFIGEX pNewCfgDesc = (PCVUSBDESCCONFIGEX)pvNewCfgDesc;
Assert(pNewCfgDesc->Core.bConfigurationValue == bConfigurationValue);
for (unsigned iIf = 0; iIf < pNewCfgDesc->Core.bNumInterfaces; iIf++)
{
PCVUSBINTERFACE pIf = &pNewCfgDesc->paIfs[iIf];
for (uint32_t iAlt = 0; iAlt < pIf->cSettings; iAlt++)
{
if (pIf->paSettings[iAlt].Core.bAlternateSetting != 0)
continue;
pProxyDev->pOps->pfnClaimInterface(pProxyDev, pIf->paSettings[iAlt].Core.bInterfaceNumber);
/* ignore failures - the backend deals with that and does the necessary logging. */
break;
}
}
return VINF_SUCCESS;
}
/**
* @copydoc PDMUSBREG::pfnUsbSetInterface
*
* USB Device Proxy: Call OS specific code to select alternate interface settings.
*/
static DECLCALLBACK(int) usbProxyDevSetInterface(PPDMUSBINS pUsbIns, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
{
PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
LogFlow(("usbProxyDevSetInterface: pProxyDev=%s bInterfaceNumber=%d bAlternateSetting=%d\n",
pUsbIns->pszName, bInterfaceNumber, bAlternateSetting));
/** @todo this is fishy, pfnSetInterface returns true/false from what I can see... */
if (pProxyDev->pOps->pfnSetInterface(pProxyDev, bInterfaceNumber, bAlternateSetting) < 0)
return VERR_GENERAL_FAILURE;
return VINF_SUCCESS;
}
/**
* @copydoc PDMUSBREG::pfnUsbClearHaltedEndpoint
*
* USB Device Proxy: Call OS specific code to clear the endpoint.
*/
static DECLCALLBACK(int) usbProxyDevClearHaltedEndpoint(PPDMUSBINS pUsbIns, unsigned uEndpoint)
{
PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
LogFlow(("usbProxyDevClearHaltedEndpoint: pProxyDev=%s uEndpoint=%u\n",
pUsbIns->pszName, uEndpoint));
if (!pProxyDev->pOps->pfnClearHaltedEndpoint(pProxyDev, uEndpoint))
return VERR_GENERAL_FAILURE;
return VINF_SUCCESS;
}
/**
* @copydoc PDMUSBREG::pfnUrbQueue
*
* USB Device Proxy: Call OS specific code.
*/
static DECLCALLBACK(int) usbProxyDevUrbQueue(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
{
PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
if (!pProxyDev->pOps->pfnUrbQueue(pUrb))
return pProxyDev->fDetached
? VERR_VUSB_DEVICE_NOT_ATTACHED
: VERR_VUSB_FAILED_TO_QUEUE_URB;
return VINF_SUCCESS;
}
/**
* @copydoc PDMUSBREG::pfnUrbCancel
*
* USB Device Proxy: Call OS specific code.
*/
static DECLCALLBACK(int) usbProxyDevUrbCancel(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
{
PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
pProxyDev->pOps->pfnUrbCancel(pUrb);
return VINF_SUCCESS;
}
/**
* @copydoc PDMUSBREG::pfnUrbReap
*
* USB Device Proxy: Call OS specific code.
*/
static DECLCALLBACK(PVUSBURB) usbProxyDevUrbReap(PPDMUSBINS pUsbIns, RTMSINTERVAL cMillies)
{
PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
PVUSBURB pUrb = pProxyDev->pOps->pfnUrbReap(pProxyDev, cMillies);
if ( pUrb
&& pUrb->enmState == VUSBURBSTATE_CANCELLED
&& pUrb->enmStatus == VUSBSTATUS_OK)
pUrb->enmStatus = VUSBSTATUS_DNR;
return pUrb;
}
/** @copydoc PDMUSBREG::pfnDestruct */
static DECLCALLBACK(void) usbProxyDestruct(PPDMUSBINS pUsbIns)
{
PUSBPROXYDEV pThis = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
Log(("usbProxyDestruct: destroying pProxyDev=%s\n", pUsbIns->pszName));
/* close it. */
if (pThis->fOpened)
{
pThis->pOps->pfnClose(pThis);
pThis->fOpened = false;
}
/* free the config descriptors. */
if (pThis->paCfgDescs)
{
for (unsigned i = 0; i < pThis->DevDesc.bNumConfigurations; i++)
RTMemFree((void *)pThis->paCfgDescs[i].paIfs);
/** @todo bugref{2693} cleanup */
RTMemFree(pThis->paCfgDescs);
pThis->paCfgDescs = NULL;
}
/* free dev */
if (&g_szDummyName[0] != pUsbIns->pszName)
RTStrFree(pUsbIns->pszName);
pUsbIns->pszName = NULL;
}
/**
* Helper function used by usbProxyConstruct when
* reading a filter from CFG.
*
* @returns VBox status code.
* @param pFilter The filter.
* @param enmFieldIdx The filter field indext.
* @param pNode The CFGM node.
* @param pszExact The exact value name.
* @param pszExpr The expression value name.
*/
static int usbProxyQueryNum(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, PCFGMNODE pNode, const char *pszExact, const char *pszExpr)
{
char szTmp[256];
/* try exact first */
uint16_t u16;
int rc = CFGMR3QueryU16(pNode, pszExact, &u16);
if (RT_SUCCESS(rc))
{
rc = USBFilterSetNumExact(pFilter, enmFieldIdx, u16, true);
AssertRCReturn(rc, rc);
/* make sure only the exact attribute is present. */
rc = CFGMR3QueryString(pNode, pszExpr, szTmp, sizeof(szTmp));
if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
{
szTmp[0] = '\0';
CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
LogRel(("usbProxyConstruct: %s: Both %s and %s are present!\n", szTmp, pszExact, pszExpr));
return VERR_INVALID_PARAMETER;
}
return VINF_SUCCESS;
}
if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
{
szTmp[0] = '\0';
CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
LogRel(("usbProxyConstruct: %s: %s query failed, rc=%Rrc\n", szTmp, pszExact, rc));
return rc;
}
/* expression? */
rc = CFGMR3QueryString(pNode, pszExpr, szTmp, sizeof(szTmp));
if (RT_SUCCESS(rc))
{
rc = USBFilterSetNumExpression(pFilter, enmFieldIdx, szTmp, true);
AssertRCReturn(rc, rc);
return VINF_SUCCESS;
}
if (RT_UNLIKELY(rc != VERR_CFGM_VALUE_NOT_FOUND))
{
szTmp[0] = '\0';
CFGMR3GetName(pNode, szTmp, sizeof(szTmp));
LogRel(("usbProxyConstruct: %s: %s query failed, rc=%Rrc\n", szTmp, pszExpr, rc));
return rc;
}
return VINF_SUCCESS;
}
/** @copydoc PDMUSBREG::pfnConstruct */
static DECLCALLBACK(int) usbProxyConstruct(PPDMUSBINS pUsbIns, int iInstance, PCFGMNODE pCfg, PCFGMNODE pCfgGlobal)
{
PUSBPROXYDEV pThis = PDMINS_2_DATA(pUsbIns, PUSBPROXYDEV);
LogFlow(("usbProxyConstruct: pUsbIns=%p iInstance=%d\n", pUsbIns, iInstance));
/*
* Initialize the instance data.
*/
pThis->pUsbIns = pUsbIns;
pThis->pUsbIns->pszName = g_szDummyName;
pThis->iActiveCfg = -1;
pThis->fMaskedIfs = 0;
pThis->fOpened = false;
pThis->fInited = false;
/*
* Read the basic configuration.
*/
char szAddress[1024];
int rc = CFGMR3QueryString(pCfg, "Address", szAddress, sizeof(szAddress));
AssertRCReturn(rc, rc);
bool fRemote;
rc = CFGMR3QueryBool(pCfg, "Remote", &fRemote);
AssertRCReturn(rc, rc);
void *pvBackend;
rc = CFGMR3QueryPtr(pCfg, "pvBackend", &pvBackend);
AssertRCReturn(rc, rc);
/*
* Select backend and open the device.
*/
if (!fRemote)
pThis->pOps = &g_USBProxyDeviceHost;
else
pThis->pOps = &g_USBProxyDeviceVRDP;
rc = pThis->pOps->pfnOpen(pThis, szAddress, pvBackend);
if (RT_FAILURE(rc))
return rc;
pThis->fOpened = true;
/*
* Get the device descriptor and format the device name (for logging).
*/
if (!usbProxyGetDeviceDesc(pThis, &pThis->DevDesc))
{
Log(("usbProxyConstruct: usbProxyGetDeviceDesc failed\n"));
return VERR_READ_ERROR;
}
RTStrAPrintf(&pUsbIns->pszName, "%p[proxy %04x:%04x]", pThis, pThis->DevDesc.idVendor, pThis->DevDesc.idProduct); /** @todo append the user comment */
AssertReturn(pUsbIns->pszName, VERR_NO_MEMORY);
/*
* Get config descriptors.
*/
size_t cbConfigs = pThis->DevDesc.bNumConfigurations * sizeof(pThis->paCfgDescs[0]);
pThis->paCfgDescs = (PVUSBDESCCONFIGEX)RTMemAllocZ(cbConfigs);
AssertReturn(pThis->paCfgDescs, VERR_NO_MEMORY);
unsigned i;
for (i = 0; i < pThis->DevDesc.bNumConfigurations; i++)
if (!copy_config(pThis, i, (PVUSBDESCCONFIGEX)&pThis->paCfgDescs[i]))
break;
if (i < pThis->DevDesc.bNumConfigurations)
{
Log(("usbProxyConstruct: copy_config failed, i=%d\n", i));
return VERR_READ_ERROR;
}
/*
* Pickup best matching global configuration for this device.
* The global configuration is organized like this:
*
* GlobalConfig/Whatever/
* |- idVendor = 300
* |- idProduct = 300
* - Config/
*
* The first level contains filter attributes which we stuff into a USBFILTER
* structure and match against the device info that's available. The highest
* ranked match is will be used. If nothing is found, the values will be
* queried from the GlobalConfig node (simplifies code and might actually
* be useful).
*/
PCFGMNODE pCfgGlobalDev = pCfgGlobal;
PCFGMNODE pCur = CFGMR3GetFirstChild(pCfgGlobal);
if (pCur)
{
/*
* Create a device filter from the device configuration
* descriptor ++. No strings currently.
*/
USBFILTER Device;
USBFilterInit(&Device, USBFILTERTYPE_CAPTURE);
rc = USBFilterSetNumExact(&Device, USBFILTERIDX_VENDOR_ID, pThis->DevDesc.idVendor, true); AssertRC(rc);
rc = USBFilterSetNumExact(&Device, USBFILTERIDX_PRODUCT_ID, pThis->DevDesc.idProduct, true); AssertRC(rc);
rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_REV, pThis->DevDesc.bcdDevice, true); AssertRC(rc);
rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_CLASS, pThis->DevDesc.bDeviceClass, true); AssertRC(rc);
rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_SUB_CLASS, pThis->DevDesc.bDeviceSubClass, true); AssertRC(rc);
rc = USBFilterSetNumExact(&Device, USBFILTERIDX_DEVICE_PROTOCOL, pThis->DevDesc.bDeviceProtocol, true); AssertRC(rc);
/** @todo manufacturer, product and serial strings */
int iBestMatchRate = -1;
PCFGMNODE pBestMatch = NULL;
for (pCur = CFGMR3GetFirstChild(pCfgGlobal); pCur; pCur = CFGMR3GetNextChild(pCur))
{
/*
* Construct a filter from the attributes in the node.
*/
USBFILTER Filter;
USBFilterInit(&Filter, USBFILTERTYPE_CAPTURE);
/* numeric */
if ( RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_VENDOR_ID, pCur, "idVendor", "idVendorExpr"))
|| RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_PRODUCT_ID, pCur, "idProduct", "idProcutExpr"))
|| RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_REV, pCur, "bcdDevice", "bcdDeviceExpr"))
|| RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_CLASS, pCur, "bDeviceClass", "bDeviceClassExpr"))
|| RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS, pCur, "bDeviceSubClass", "bDeviceSubClassExpr"))
|| RT_FAILURE(usbProxyQueryNum(&Filter, USBFILTERIDX_DEVICE_PROTOCOL, pCur, "bDeviceProtocol", "bDeviceProtocolExpr")))
continue; /* skip it */
/* strings */
/** @todo manufacturer, product and serial strings */
/* ignore unknown config values, but not without bitching. */
if (!CFGMR3AreValuesValid(pCur,
"idVendor\0idVendorExpr\0"
"idProduct\0idProductExpr\0"
"bcdDevice\0bcdDeviceExpr\0"
"bDeviceClass\0bDeviceClassExpr\0"
"bDeviceSubClass\0bDeviceSubClassExpr\0"
"bDeviceProtocol\0bDeviceProtocolExpr\0"))
LogRel(("usbProxyConstruct: Unknown value(s) in config filter (ignored)!\n"));
/*
* Try match it and on match see if it has is a higher rate hit
* than the previous match. Quit if its a 100% match.
*/
int iRate = USBFilterMatchRated(&Filter, &Device);
if (iRate > iBestMatchRate)
{
pBestMatch = pCur;
iBestMatchRate = iRate;
if (iRate >= 100)
break;
}
}
if (pBestMatch)
pCfgGlobalDev = CFGMR3GetChild(pBestMatch, "Config");
if (pCfgGlobalDev)
pCfgGlobalDev = pCfgGlobal;
}
/*
* Query the rest of the configuration using the global as fallback.
*/
rc = CFGMR3QueryU32(pCfg, "MaskedIfs", &pThis->fMaskedIfs);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
rc = CFGMR3QueryU32(pCfgGlobalDev, "MaskedIfs", &pThis->fMaskedIfs);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
pThis->fMaskedIfs = 0;
else
AssertRCReturn(rc, rc);
bool fForce11Device;
rc = CFGMR3QueryBool(pCfg, "Force11Device", &fForce11Device);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
rc = CFGMR3QueryBool(pCfgGlobalDev, "Force11Device", &fForce11Device);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
fForce11Device = false;
else
AssertRCReturn(rc, rc);
bool fForce11PacketSize;
rc = CFGMR3QueryBool(pCfg, "Force11PacketSize", &fForce11PacketSize);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
rc = CFGMR3QueryBool(pCfgGlobalDev, "Force11PacketSize", &fForce11PacketSize);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
fForce11PacketSize = false;
else
AssertRCReturn(rc, rc);
/*
* If we're masking interfaces, edit the descriptors.
*/
bool fEdited = pThis->fMaskedIfs != 0;
if (pThis->fMaskedIfs)
usbProxyDevEditOutMaskedIfs(pThis);
/*
* Do 2.0 -> 1.1 device edits if requested to do so.
*/
if ( fForce11PacketSize
&& pThis->DevDesc.bcdUSB >= 0x0200)
{
PVUSBDESCCONFIGEX paCfgs = pThis->paCfgDescs;
for (unsigned iCfg = 0; iCfg < pThis->DevDesc.bNumConfigurations; iCfg++)
{
PVUSBINTERFACE paIfs = (PVUSBINTERFACE)paCfgs[iCfg].paIfs;
for (unsigned iIf = 0; iIf < paCfgs[iCfg].Core.bNumInterfaces; iIf++)
for (uint32_t iAlt = 0; iAlt < paIfs[iIf].cSettings; iAlt++)
{
/*
* USB 1.1 defines the max for control, interrupt and bulk to be 64 bytes.
* While isochronous has a max of 1023 bytes.
*/
PVUSBDESCENDPOINTEX paEps = (PVUSBDESCENDPOINTEX)paIfs[iIf].paSettings[iAlt].paEndpoints;
for (unsigned iEp = 0; iEp < paIfs[iIf].paSettings[iAlt].Core.bNumEndpoints; iEp++)
{
const uint16_t cbMax = (paEps[iEp].Core.bmAttributes & 3) == 1 /* isoc */
? 1023
: 64;
if (paEps[iEp].Core.wMaxPacketSize > cbMax)
{
Log(("usb-proxy: pProxyDev=%s correcting wMaxPacketSize from %#x to %#x (mainly for vista)\n",
pUsbIns->pszName, paEps[iEp].Core.wMaxPacketSize, cbMax));
paEps[iEp].Core.wMaxPacketSize = cbMax;
fEdited = true;
}
}
}
}
}
if ( fForce11Device
&& pThis->DevDesc.bcdUSB == 0x0200)
{
/*
* Discourages windows from helping you find a 2.0 port.
*/
Log(("usb-proxy: %s correcting USB version 2.0 to 1.1 (to avoid Windows warning)\n", pUsbIns->pszName));
pThis->DevDesc.bcdUSB = 0x110;
fEdited = true;
}
/*
* Init the PDM/VUSB descriptor cache.
*/
pThis->DescCache.pDevice = &pThis->DevDesc;
pThis->DescCache.paConfigs = pThis->paCfgDescs;
pThis->DescCache.paLanguages = NULL;
pThis->DescCache.cLanguages = 0;
pThis->DescCache.fUseCachedDescriptors = fEdited;
pThis->DescCache.fUseCachedStringsDescriptors = false;
/*
* Call the backend if it wishes to do some more initializing
* after we've read the config and descriptors.
*/
if (pThis->pOps->pfnInit)
{
rc = pThis->pOps->pfnInit(pThis);
if (RT_FAILURE(rc))
return rc;
}
pThis->fInited = true;
/*
* We're good!
*/
Log(("usb-proxy: created pProxyDev=%s address '%s' fMaskedIfs=%#x (rc=%Rrc)\n",
pUsbIns->pszName, szAddress, pThis->fMaskedIfs, rc));
return VINF_SUCCESS;
}
/**
* The USB proxy device registration record.
*/
const PDMUSBREG g_UsbDevProxy =
{
/* u32Version */
PDM_USBREG_VERSION,
/* szName */
"USBProxy",
/* pszDescription */
"USB Proxy Device.",
/* fFlags */
0,
/* cMaxInstances */
~0,
/* cbInstance */
sizeof(USBPROXYDEV),
/* pfnConstruct */
usbProxyConstruct,
/* pfnDestruct */
usbProxyDestruct,
/* pfnVMInitComplete */
NULL,
/* pfnVMPowerOn */
NULL,
/* pfnVMReset */
NULL,
/* pfnVMSuspend */
NULL,
/* pfnVMResume */
NULL,
/* pfnVMPowerOff */
NULL,
/* pfnHotPlugged */
NULL,
/* pfnHotUnplugged */
NULL,
/* pfnDriverAttach */
NULL,
/* pfnDriverDetach */
NULL,
/* pfnQueryInterface */
NULL,
/* pfnUsbReset */
usbProxyDevReset,
/* pfnUsbGetDescriptorCache */
usbProxyDevGetDescriptorCache,
/* pfnUsbSetConfiguration */
usbProxyDevSetConfiguration,
/* pfnUsbSetInterface */
usbProxyDevSetInterface,
/* pfnUsbClearHaltedEndpoint */
usbProxyDevClearHaltedEndpoint,
/* pfnUrbNew */
NULL,
/* pfnUrbQueue */
usbProxyDevUrbQueue,
/* pfnUrbCancel */
usbProxyDevUrbCancel,
/* pfnUrbReap */
usbProxyDevUrbReap,
/* u32TheEnd */
PDM_USBREG_VERSION
};