diff options
| author | Michael Meskes <meskes@debian.org> | 2009-02-09 17:42:08 +0100 |
|---|---|---|
| committer | Michael Meskes <meskes@debian.org> | 2009-02-09 17:42:08 +0100 |
| commit | 5b633c860b9ccb98910812f91c2474fda316b50b (patch) | |
| tree | db6656e828509048e53ad8aea69d2edb9f64f442 /src/VBox/Frontends/VBoxBFE/USBProxyServiceLinux.cpp | |
| parent | ce414e6eec1583def0dc7be0926f1a07364cb5e3 (diff) | |
| download | virtualbox-5b633c860b9ccb98910812f91c2474fda316b50b.tar.gz | |
Imported 2.1.2-dfsgupstream/2.1.2-dfsg
Diffstat (limited to 'src/VBox/Frontends/VBoxBFE/USBProxyServiceLinux.cpp')
| -rw-r--r-- | src/VBox/Frontends/VBoxBFE/USBProxyServiceLinux.cpp | 1055 |
1 files changed, 1055 insertions, 0 deletions
diff --git a/src/VBox/Frontends/VBoxBFE/USBProxyServiceLinux.cpp b/src/VBox/Frontends/VBoxBFE/USBProxyServiceLinux.cpp new file mode 100644 index 000000000..562d2852e --- /dev/null +++ b/src/VBox/Frontends/VBoxBFE/USBProxyServiceLinux.cpp @@ -0,0 +1,1055 @@ +/* $Id: USBProxyServiceLinux.cpp 13837 2008-11-05 02:54:02Z vboxsync $ */ +/** @file + * VBox frontends: Basic Frontend (BFE): + * Implementation of USBProxyServiceLinux class + * + * WARNING: This file needs to be resynced and is currently disabled. + */ + +/* + * Copyright (C) 2006-2007 Sun Microsystems, Inc. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 USA or visit http://www.sun.com if you need + * additional information or have any questions. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "USBProxyService.h" +#include "Logging.h" + +#include <VBox/usb.h> +#include <VBox/err.h> + +#include <iprt/string.h> +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/file.h> +#include <iprt/err.h> + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <sys/statfs.h> +#include <sys/poll.h> +#include <unistd.h> +#ifndef VBOX_WITHOUT_LINUX_COMPILER_H +# include <linux/compiler.h> +#endif +#include <linux/usbdevice_fs.h> + + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** Suffix translation. */ +typedef struct USBSUFF +{ + char szSuff[4]; + unsigned cchSuff; + unsigned uMul; + unsigned uDiv; +} USBSUFF, *PUSBSUFF; +typedef const USBSUFF *PCUSBSUFF; + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * Suffixes for the endpoint polling interval. + */ +static const USBSUFF s_aIntervalSuff[] = +{ + { "ms", 2, 1, 0 }, + { "us", 2, 1, 1000 }, + { "ns", 2, 1, 1000000 }, + { "s", 1, 1000, 0 }, + { "", 0, 0, 0 } /* term */ +}; + + +/** + * Initialize data members. + */ +USBProxyServiceLinux::USBProxyServiceLinux (HostUSB *aHost, const char *aUsbfsRoot /* = "/proc/bus/usb" */) + : USBProxyService (aHost), mFile (NIL_RTFILE), mStream (NULL), mWakeupPipeR (NIL_RTFILE), + mWakeupPipeW (NIL_RTFILE), mUsbfsRoot (aUsbfsRoot) +{ + LogFlowMember (("USBProxyServiceLinux::USBProxyServiceLinux: aHost=%p aUsbfsRoot=%p:{%s}\n", aHost, aUsbfsRoot, aUsbfsRoot)); + + /* + * Open the devices file. + */ + int rc = VERR_NO_MEMORY; + char *pszDevices; + RTStrAPrintf (&pszDevices, "%s/devices", aUsbfsRoot); + if (pszDevices) + { + rc = RTFileOpen (&mFile, pszDevices, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); + if (RT_SUCCESS (rc)) + { + /* + * Check that we're actually on the usbfs. + */ + struct statfs StFS; + if (!fstatfs (mFile, &StFS)) + { + if (StFS.f_type == USBDEVICE_SUPER_MAGIC) + { + int pipes[2]; + if (!pipe (pipes)) + { + mWakeupPipeR = pipes[0]; + mWakeupPipeW = pipes[1]; + mStream = fdopen (mFile, "r"); + if (mStream) + { + /* + * Start the poller thread. + */ + rc = start(); + if (RT_SUCCESS (rc)) + { + RTStrFree (pszDevices); + LogFlowMember (("USBProxyServiceLinux::USBProxyServiceLinux: returns successfully - mFile=%d mStream=%p mWakeupPipeR/W=%d/%d\n", + mFile, mStream, mWakeupPipeR, mWakeupPipeW)); + return; + } + + fclose (mStream); + mStream = NULL; + mFile = NIL_RTFILE; + } + + RTFileClose (mWakeupPipeR); + RTFileClose (mWakeupPipeW); + mWakeupPipeW = mWakeupPipeR = NIL_RTFILE; + } + } + else + { + Log (("USBProxyServiceLinux::USBProxyServiceLinux: StFS.f_type=%d expected=%d\n", StFS.f_type, USBDEVICE_SUPER_MAGIC)); + rc = VERR_INVALID_PARAMETER; + } + } + else + { + rc = RTErrConvertFromErrno (errno); + Log (("USBProxyServiceLinux::USBProxyServiceLinux: fstatfs failed, errno=%d\n", errno)); + } + RTFileClose (mFile); + mFile = NIL_RTFILE; + } + else + { +#ifndef DEBUG_fm3 + /* I'm currently using Linux with disabled USB support */ + AssertRC (rc); +#endif + Log (("USBProxyServiceLinux::USBProxyServiceLinux: RTFileOpen(,%s,,) -> %Rrc\n", pszDevices, rc)); + } + RTStrFree (pszDevices); + } + else + Log (("USBProxyServiceLinux::USBProxyServiceLinux: out of memory!\n")); + + mLastError = rc; + LogFlowMember (("USBProxyServiceLinux::USBProxyServiceLinux: returns failure!!! (rc=%Rrc)\n", rc)); +} + + +/** + * Stop all service threads and free the device chain. + */ +USBProxyServiceLinux::~USBProxyServiceLinux() +{ + LogFlowMember (("USBProxyServiceLinux::~USBProxyServiceLinux:\n")); + + /* + * Stop the service. + */ + if (isActive()) + stop(); + + /* + * Free resources. + */ + if (mStream) + { + fclose (mStream); + mStream = NULL; + mFile = NIL_RTFILE; + } + else if (mFile != NIL_RTFILE) + { + RTFileClose (mFile); + mFile = NIL_RTFILE; + } + + RTFileClose (mWakeupPipeR); + RTFileClose (mWakeupPipeW); + mWakeupPipeW = mWakeupPipeR = NIL_RTFILE; +} + + +int USBProxyServiceLinux::captureDevice (HostUSBDevice *aDevice) +{ + /* + * Don't think we need to do anything when the device is held... + */ + return VINF_SUCCESS; +} + + +int USBProxyServiceLinux::holdDevice (HostUSBDevice *pDevice) +{ + /* + * This isn't really implemented, we can usually wrestle + * any user when we need it... Anyway, I don't have anywhere to store + * any info per device atm. + */ + return VINF_SUCCESS; +} + + +int USBProxyServiceLinux::releaseDevice (HostUSBDevice *aDevice) +{ + /* + * We're not really holding it atm. + */ + return VINF_SUCCESS; +} + + +int USBProxyServiceLinux::resetDevice (HostUSBDevice *aDevice) +{ + /* + * We don't dare reset anything, but the USB Proxy Device + * will reset upon detach, so this should be ok. + */ + return VINF_SUCCESS; +} + + +int USBProxyServiceLinux::wait (unsigned aMillies) +{ + struct pollfd PollFds[2]; + + memset(&PollFds, 0, sizeof(PollFds)); + PollFds[0].fd = mFile; + PollFds[0].events = POLLIN; + PollFds[1].fd = mWakeupPipeR; + PollFds[1].events = POLLIN | POLLERR | POLLHUP; + + int rc = poll (&PollFds[0], 2, aMillies); + if (rc == 0) + return VERR_TIMEOUT; + if (rc > 0) + return VINF_SUCCESS; + return RTErrConvertFromErrno (errno); +} + + +int USBProxyServiceLinux::interruptWait (void) +{ + int rc = RTFileWrite (mWakeupPipeW, "Wakeup!", sizeof("Wakeup!") - 1, NULL); + if (RT_SUCCESS (rc)) + fsync (mWakeupPipeW); + return rc; +} + + +/** + * "reads" the number suffix. It's more like validating it and + * skipping the necessary number of chars. + */ +static int usbReadSkipSuffix (char **ppszNext) +{ + char *pszNext = *ppszNext; + if (!isspace (*pszNext) && *pszNext) + { + /* skip unit */ + if (pszNext[0] == 'm' && pszNext[1] == 's') + pszNext += 2; + else if (pszNext[0] == 'm' && pszNext[1] == 'A') + pszNext += 2; + + /* skip parenthesis */ + if (*pszNext == '(') + { + pszNext = strchr (pszNext, ')'); + if (!pszNext++) + { + AssertMsgFailed (("*ppszNext=%s\n", *ppszNext)); + return VERR_PARSE_ERROR; + } + } + + /* blank or end of the line. */ + if (!isspace (*pszNext) && *pszNext) + { + AssertMsgFailed (("pszNext=%s\n", pszNext)); + return VERR_PARSE_ERROR; + } + + /* it's ok. */ + *ppszNext = pszNext; + } + + return VINF_SUCCESS; +} + + +/** + * Reads a USB number returning the number and the position of the next character to parse. + */ +static int usbReadNum (const char *pszValue, unsigned uBase, uint32_t u32Mask, PCUSBSUFF paSuffs, void *pvNum, char **ppszNext) +{ + /* + * Initialize return value to zero and strip leading spaces. + */ + switch (u32Mask) + { + case 0xff: *(uint8_t *)pvNum = 0; break; + case 0xffff: *(uint16_t *)pvNum = 0; break; + case 0xffffffff: *(uint32_t *)pvNum = 0; break; + } + pszValue = RTStrStripL (pszValue); + if (*pszValue) + { + /* + * Try convert the number. + */ + char *pszNext; + uint32_t u32 = 0; + RTStrToUInt32Ex (pszValue, &pszNext, uBase, &u32); + if (pszNext == pszValue) + { + AssertMsgFailed (("pszValue=%d\n", pszValue)); + return VERR_NO_DATA; + } + + /* + * Check the range. + */ + if (u32 & ~u32Mask) + { + AssertMsgFailed (("pszValue=%d u32=%#x lMask=%#x\n", pszValue, u32, u32Mask)); + return VERR_OUT_OF_RANGE; + } + + /* + * Validate and skip stuff following the number. + */ + if (paSuffs) + { + if (!isspace (*pszNext) && *pszNext) + { + for (PCUSBSUFF pSuff = paSuffs; pSuff->szSuff[0]; pSuff++) + { + if ( !strncmp (pSuff->szSuff, pszNext, pSuff->cchSuff) + && (!pszNext[pSuff->cchSuff] || isspace (pszNext[pSuff->cchSuff]))) + { + if (pSuff->uDiv) + u32 /= pSuff->uDiv; + else + u32 *= pSuff->uMul; + break; + } + } + } + } + else + { + int rc = usbReadSkipSuffix (&pszNext); + if (RT_FAILURE (rc)) + return rc; + } + + *ppszNext = pszNext; + + /* + * Set the value. + */ + switch (u32Mask) + { + case 0xff: *(uint8_t *)pvNum = (uint8_t)u32; break; + case 0xffff: *(uint16_t *)pvNum = (uint16_t)u32; break; + case 0xffffffff: *(uint32_t *)pvNum = (uint32_t)u32; break; + } + } + return VINF_SUCCESS; +} + +static int usbRead8 (const char *pszValue, unsigned uBase, uint8_t *pu8, char **ppszNext) +{ + return usbReadNum (pszValue, uBase, 0xff, NULL, pu8, ppszNext); +} + +static int usbRead16 (const char *pszValue, unsigned uBase, uint16_t *pu16, char **ppszNext) +{ + return usbReadNum (pszValue, uBase, 0xffff, NULL, pu16, ppszNext); +} + +static int usbRead16Suff (const char *pszValue, unsigned uBase, PCUSBSUFF paSuffs, uint16_t *pu16, char **ppszNext) +{ + return usbReadNum (pszValue, uBase, 0xffff, paSuffs, pu16, ppszNext); +} + +/** + * Reads a USB BCD number returning the number and the position of the next character to parse. + * The returned number contains the integer part in the high byte and the decimal part in the low byte. + */ +static int usbReadBCD (const char *pszValue, unsigned uBase, uint16_t *pu16, char **ppszNext) +{ + /* + * Initialize return value to zero and strip leading spaces. + */ + *pu16 = 0; + pszValue = RTStrStripL (pszValue); + if (*pszValue) + { + /* + * Try convert the number. + */ + /* integer part */ + char *pszNext; + uint32_t u32Int = 0; + RTStrToUInt32Ex (pszValue, &pszNext, uBase, &u32Int); + if (pszNext == pszValue) + { + AssertMsgFailed (("pszValue=%s\n", pszValue)); + return VERR_NO_DATA; + } + if (u32Int & ~0xff) + { + AssertMsgFailed (("pszValue=%s u32Int=%#x (int)\n", pszValue, u32Int)); + return VERR_OUT_OF_RANGE; + } + + /* skip dot and read decimal part */ + if (*pszNext != '.') + { + AssertMsgFailed (("pszValue=%s pszNext=%s (int)\n", pszValue, pszNext)); + return VERR_PARSE_ERROR; + } + char *pszValue2 = RTStrStripL (pszNext + 1); + uint32_t u32Dec = 0; + RTStrToUInt32Ex (pszValue2, &pszNext, uBase, &u32Dec); + if (pszNext == pszValue) + { + AssertMsgFailed (("pszValue=%s\n", pszValue)); + return VERR_NO_DATA; + } + if (u32Dec & ~0xff) + { + AssertMsgFailed (("pszValue=%s u32Dec=%#x\n", pszValue, u32Dec)); + return VERR_OUT_OF_RANGE; + } + + /* + * Validate and skip stuff following the number. + */ + int rc = usbReadSkipSuffix (&pszNext); + if (RT_FAILURE (rc)) + return rc; + *ppszNext = pszNext; + + /* + * Set the value. + */ + *pu16 = (uint16_t)u32Int << 8 | (uint16_t)u32Dec; + } + return VINF_SUCCESS; +} + + +/** + * Reads a string, i.e. allocates memory and copies it. + * + * We assume that a string is pure ASCII, if that's not the case + * tell me how to figure out the codeset please. + */ +static int usbReadStr (const char *pszValue, const char **ppsz) +{ + if (*ppsz) + RTStrFree ((char *)*ppsz); + *ppsz = RTStrDup (pszValue); + if (*ppsz) + return VINF_SUCCESS; + return VERR_NO_MEMORY; +} + + +/** + * Skips the current property. + */ +static char * usbReadSkip (const char *pszValue) +{ + char *psz = strchr (pszValue, '='); + if (psz) + psz = strchr (psz + 1, '='); + if (!psz) + return strchr (pszValue, '\0'); + while (psz > pszValue && !isspace (psz[-1])) + psz--; + Assert (psz > pszValue); + return psz; +} + + +/** + * Compare a prefix and returns pointer to the char following it if it matches. + */ +static char *usbPrefix (char *psz, const char *pszPref, size_t cchPref) +{ + if (strncmp (psz, pszPref, cchPref)) + return NULL; + return psz + cchPref; +} + + +/** + * Checks which state the device is in. + */ +static USBDEVICESTATE usbDeterminState (PCUSBDEVICE pDevice) +{ + if (!pDevice->idVendor) + return USBDEVICESTATE_UNSUPPORTED; + + /* + * We cannot distinguish between USED_BY_HOST_CAPTURABLE and + * USED_BY_GUEST, HELD_BY_PROXY all that well and it shouldn't be + * necessary either. + */ + USBDEVICESTATE enmState = USBDEVICESTATE_UNUSED; + for (int iCfg = pDevice->bNumConfigurations - 1; iCfg >= 0; iCfg--) + for (int iIf = pDevice->paConfigurations[iCfg].bConfigurationValue - 1; iIf >= 0; iIf--) + { + const char *pszDriver = pDevice->paConfigurations[iCfg].paInterfaces[iIf].pszDriver; + if (pszDriver) + { + if (!strcmp (pszDriver, "hub")) + { + enmState = USBDEVICESTATE_USED_BY_HOST; + break; + } + enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; + } + } + + return enmState; +} + + +PUSBDEVICE USBProxyServiceLinux::getDevices (void) +{ + PUSBDEVICE pFirst = NULL; + if (mStream) + { + PUSBDEVICE *ppNext = NULL; + USBDEVICE Dev = {0}; + int cHits = 0; + int iCfg = 0; + PUSBCONFIG pCfg = NULL; + PUSBINTERFACE pIf = NULL; + int iEp = 0; + PUSBENDPOINT pEp = NULL; + char szLine[1024]; + + rewind (mStream); + int rc = VINF_SUCCESS; + while ( RT_SUCCESS (rc) + && fgets (szLine, sizeof (szLine), mStream)) + { + char *psz; + char *pszValue; + + /* validate and remove the trailing newline. */ + psz = strchr (szLine, '\0'); + if (psz[-1] != '\n' && !feof (mStream)) + { + AssertMsgFailed (("Line too long. (cch=%d)\n", strlen (szLine))); + continue; + } + + /* strip */ + psz = RTStrStrip (szLine); + if (!*psz) + continue; + + /* + * Interpret the line. + * (Ordered by normal occurence.) + */ + char ch = psz[0]; + if (psz[1] != ':') + continue; + psz = RTStrStripL (psz + 3); + #define PREFIX(str) ( (pszValue = usbPrefix (psz, str, sizeof (str) - 1)) != NULL ) + switch (ch) + { + /* + * T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd + * | | | | | | | | |__MaxChildren + * | | | | | | | |__Device Speed in Mbps + * | | | | | | |__DeviceNumber + * | | | | | |__Count of devices at this level + * | | | | |__Connector/Port on Parent for this device + * | | | |__Parent DeviceNumber + * | | |__Level in topology for this bus + * | |__Bus number + * |__Topology info tag + */ + case 'T': + /* add */ + AssertMsg (cHits >= 3 || cHits == 0, ("cHits=%d\n", cHits)); + if (cHits >= 3) + { + Dev.enmState = usbDeterminState (&Dev); + PUSBDEVICE pDev = (PUSBDEVICE) RTMemAlloc (sizeof(*pDev)); + if (pDev) + { + *pDev = Dev; + if (Dev.enmState != USBDEVICESTATE_UNSUPPORTED) + { + RTStrAPrintf((char **)&pDev->pszAddress, "%s/%03d/%03d", mUsbfsRoot.c_str(), pDev->bBus, pDev->bDevNum); + if (pDev->pszAddress) + { + if (ppNext) + *ppNext = pDev; + else + pFirst = pDev; + ppNext = &pDev->pNext; + } + else + { + freeDevice (pDev); + rc = VERR_NO_MEMORY; + } + } + else + freeDevice (pDev); + memset (&Dev, 0, sizeof (Dev)); + } + else + rc = VERR_NO_MEMORY; + } + + /* Reset device state */ + cHits = 1; + iCfg = 0; + pCfg = NULL; + pIf = NULL; + iEp = 0; + pEp = NULL; + + + /* parse the line. */ + while (*psz && RT_SUCCESS (rc)) + { + if (PREFIX ("Bus=")) + rc = usbRead8 (pszValue, 10, &Dev.bBus, &psz); + else if (PREFIX ("Lev=")) + rc = usbRead8 (pszValue, 10, &Dev.bLevel, &psz); + else if (PREFIX ("Dev#=")) + rc = usbRead8 (pszValue, 10, &Dev.bDevNum, &psz); + else if (PREFIX ("Prnt=")) + rc = usbRead8 (pszValue, 10, &Dev.bDevNumParent, &psz); + else if (PREFIX ("Port=")) + rc = usbRead8 (pszValue, 10, &Dev.bPort, &psz); + else if (PREFIX ("Cnt=")) + rc = usbRead8 (pszValue, 10, &Dev.bNumDevices, &psz); + //else if (PREFIX ("Spd=")) + // rc = usbReadSpeed (pszValue, &Dev.cbSpeed, &psz); + else if (PREFIX ("MxCh=")) + rc = usbRead8 (pszValue, 10, &Dev.bMaxChildren, &psz); + else + psz = usbReadSkip (psz); + psz = RTStrStripL (psz); + } + break; + + /* + * Bandwidth info: + * B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd + * | | | |__Number of isochronous requests + * | | |__Number of interrupt requests + * | |__Total Bandwidth allocated to this bus + * |__Bandwidth info tag + */ + case 'B': + break; + + /* + * D: Ver=x.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd + * | | | | | | |__NumberConfigurations + * | | | | | |__MaxPacketSize of Default Endpoint + * | | | | |__DeviceProtocol + * | | | |__DeviceSubClass + * | | |__DeviceClass + * | |__Device USB version + * |__Device info tag #1 + */ + case 'D': + while (*psz && RT_SUCCESS (rc)) + { + if (PREFIX ("Ver=")) + rc = usbReadBCD (pszValue, 16, &Dev.bcdUSB, &psz); + else if (PREFIX ("Cls=")) + rc = usbRead8 (pszValue, 16, &Dev.bDeviceClass, &psz); + else if (PREFIX ("Sub=")) + rc = usbRead8 (pszValue, 16, &Dev.bDeviceSubClass, &psz); + else if (PREFIX ("Prot=")) + rc = usbRead8 (pszValue, 16, &Dev.bDeviceProtocol, &psz); + //else if (PREFIX ("MxPS=")) + // rc = usbRead16 (pszValue, 10, &Dev.wMaxPacketSize, &psz); + else if (PREFIX ("#Cfgs=")) + rc = usbRead8 (pszValue, 10, &Dev.bNumConfigurations, &psz); + else + psz = usbReadSkip (psz); + psz = RTStrStripL (psz); + } + cHits++; + break; + + /* + * P: Vendor=xxxx ProdID=xxxx Rev=xx.xx + * | | | |__Product revision number + * | | |__Product ID code + * | |__Vendor ID code + * |__Device info tag #2 + */ + case 'P': + while (*psz && RT_SUCCESS (rc)) + { + if (PREFIX ("Vendor=")) + rc = usbRead16 (pszValue, 16, &Dev.idVendor, &psz); + else if (PREFIX ("ProdID=")) + rc = usbRead16 (pszValue, 16, &Dev.idProduct, &psz); + else if (PREFIX ("Rev=")) + rc = usbReadBCD (pszValue, 16, &Dev.bcdDevice, &psz); + else + psz = usbReadSkip (psz); + psz = RTStrStripL (psz); + } + cHits++; + break; + + /* + * String. + */ + case 'S': + if (PREFIX ("Manufacturer=")) + rc = usbReadStr (pszValue, &Dev.pszManufacturer); + else if (PREFIX ("Product=")) + rc = usbReadStr (pszValue, &Dev.pszProduct); + else if (PREFIX ("SerialNumber=")) + { + rc = usbReadStr (pszValue, &Dev.pszSerialNumber); + if (RT_SUCCESS (rc)) + Dev.u64SerialHash = calcSerialHash (pszValue); + } + break; + + /* + * C:* #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA + * | | | | | |__MaxPower in mA + * | | | | |__Attributes + * | | | |__ConfiguratioNumber + * | | |__NumberOfInterfaces + * | |__ "*" indicates the active configuration (others are " ") + * |__Config info tag + */ + case 'C': + { + USBCONFIG Cfg = {0}; + Cfg.fActive = psz[-2] == '*'; + while (*psz && RT_SUCCESS (rc)) + { + if (PREFIX ("#Ifs=")) + rc = usbRead8 (pszValue, 10, &Cfg.bNumInterfaces, &psz); + else if (PREFIX ("Cfg#=")) + rc = usbRead8 (pszValue, 10, &Cfg.bConfigurationValue, &psz); + else if (PREFIX ("Atr=")) + rc = usbRead8 (pszValue, 16, &Cfg.bmAttributes, &psz); + else if (PREFIX ("MxPwr=")) + rc = usbRead16 (pszValue, 10, &Cfg.u16MaxPower, &psz); + else + psz = usbReadSkip (psz); + psz = RTStrStripL (psz); + } + if (RT_SUCCESS (rc)) + { + if (iCfg < Dev.bNumConfigurations) + { + /* Add the config. */ + if (!Dev.paConfigurations) + { + Dev.paConfigurations = pCfg = (PUSBCONFIG) RTMemAllocZ (sizeof (Cfg) * Dev.bNumConfigurations); + if (pCfg) + { + *pCfg = Cfg; + iCfg = 1; + } + else + rc = VERR_NO_MEMORY; + } + else + { + *++pCfg = Cfg; + iCfg++; + } + } + else + { + AssertMsgFailed (("iCfg=%d bNumConfigurations=%d\n", iCfg, Dev.bNumConfigurations)); + rc = VERR_INTERNAL_ERROR; + } + } + + /* new config, so, start anew with interfaces and endpoints. */ + pIf = NULL; + iEp = 0; + pEp = NULL; + break; + } + + /* + * I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss + * | | | | | | | |__Driver name + * | | | | | | | or "(none)" + * | | | | | | |__InterfaceProtocol + * | | | | | |__InterfaceSubClass + * | | | | |__InterfaceClass + * | | | |__NumberOfEndpoints + * | | |__AlternateSettingNumber + * | |__InterfaceNumber + * |__Interface info tag + */ + case 'I': + { + USBINTERFACE If = {0}; + bool fIfAdopted = false; + while (*psz && RT_SUCCESS (rc)) + { + if (PREFIX ("If#=")) + rc = usbRead8 (pszValue, 10, &If.bInterfaceNumber, &psz); + else if (PREFIX ("Alt=")) + rc = usbRead8 (pszValue, 10, &If.bAlternateSetting, &psz); + else if (PREFIX ("#EPs=")) + rc = usbRead8 (pszValue, 10, &If.bNumEndpoints, &psz); + else if (PREFIX ("Cls=")) + rc = usbRead8 (pszValue, 16, &If.bInterfaceClass, &psz); + else if (PREFIX ("Sub=")) + rc = usbRead8 (pszValue, 16, &If.bInterfaceSubClass, &psz); + else if (PREFIX ("Prot=")) + rc = usbRead8 (pszValue, 16, &If.bInterfaceProtocol, &psz); + else if (PREFIX ("Driver=")) + { + rc = usbReadStr (pszValue, &If.pszDriver); + if ( If.pszDriver + && ( !strcmp (If.pszDriver, "(none)") + || !strcmp (If.pszDriver, "(no driver)") + || !*If.pszDriver)) + { + RTStrFree ((char *)If.pszDriver); + If.pszDriver = NULL; + } + break; + } + else + psz = usbReadSkip (psz); + psz = RTStrStripL (psz); + } + if (RT_SUCCESS (rc)) + { + if (pCfg && If.bInterfaceNumber < pCfg->bNumInterfaces) + { + /* Add the config. */ + if (!pCfg->paInterfaces) + { + pCfg->paInterfaces = pIf = (PUSBINTERFACE) RTMemAllocZ (sizeof (If) * pCfg->bNumInterfaces); + if (pIf) + { + Assert (!If.bInterfaceNumber); Assert (!If.bAlternateSetting); + *pIf = If; + fIfAdopted = true; + } + else + rc = VERR_NO_MEMORY; + } + else + { + /* + * Alternate settings makes life *difficult*! + * ASSUMES: ORDER ASC bInterfaceNumber, bAlternateSetting + */ + pIf = &pCfg->paInterfaces[If.bInterfaceNumber]; + if (!If.bAlternateSetting) + { + freeInterfaceMembers (pIf, 1); + *pIf = If; + fIfAdopted = true; + } + else + { + PUSBINTERFACE paAlts = (PUSBINTERFACE) RTMemRealloc (pIf->paAlts, (pIf->cAlts + 1) * sizeof(*pIf)); + if (paAlts) + { + pIf->paAlts = paAlts; + // don't do pIf = &paAlts[pIf->cAlts++]; as it will increment after the assignment + unsigned cAlts = pIf->cAlts++; + pIf = &paAlts[cAlts]; + *pIf = If; + fIfAdopted = true; + } + else + rc = VERR_NO_MEMORY; + } + } + } + else + { + AssertMsgFailed (("iCfg=%d bInterfaceNumber=%d bNumInterfaces=%d\n", iCfg, If.bInterfaceNumber, pCfg->bNumInterfaces)); + rc = VERR_INTERNAL_ERROR; + } + } + + if (!fIfAdopted) + freeInterfaceMembers (&If, 1); + + /* start anew with endpoints. */ + iEp = 0; + pEp = NULL; + break; + } + + + /* + * E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms + * | | | | |__Interval (max) between transfers + * | | | |__EndpointMaxPacketSize + * | | |__Attributes(EndpointType) + * | |__EndpointAddress(I=In,O=Out) + * |__Endpoint info tag + */ + case 'E': + { + USBENDPOINT Ep = {0}; + while (*psz && RT_SUCCESS (rc)) + { + if (PREFIX ("Ad=")) + rc = usbRead8 (pszValue, 16, &Ep.bEndpointAddress, &psz); + else if (PREFIX ("Atr=")) + rc = usbRead8 (pszValue, 16, &Ep.bmAttributes, &psz); + else if (PREFIX ("MxPS=")) + rc = usbRead16 (pszValue, 10, &Ep.wMaxPacketSize, &psz); + else if (PREFIX ("Ivl=")) + rc = usbRead16Suff (pszValue, 10, &s_aIntervalSuff[0], &Ep.u16Interval, &psz); + else + psz = usbReadSkip (psz); + psz = RTStrStripL (psz); + } + if (RT_SUCCESS (rc)) + { + if (pIf && iEp < pIf->bNumEndpoints) + { + /* Add the config. */ + if (!pIf->paEndpoints) + { + pIf->paEndpoints = pEp = (PUSBENDPOINT) RTMemAllocZ (sizeof (Ep) * pIf->bNumEndpoints); + if (pEp) + { + *pEp = Ep; + iEp = 1; + } + else + rc = VERR_NO_MEMORY; + } + else + { + *++pEp = Ep; + iEp++; + } + } + else + { + AssertMsgFailed (("iCfg=%d bInterfaceNumber=%d iEp=%d bNumInterfaces=%d\n", iCfg, pIf->bInterfaceNumber, iEp, pIf->bNumEndpoints)); + rc = VERR_INTERNAL_ERROR; + } + } + break; + } + + } + #undef PREFIX + } /* parse loop */ + + /* + * Add the current entry. + */ + AssertMsg (cHits >= 3 || cHits == 0, ("cHits=%d\n", cHits)); + if (cHits >= 3) + { + Dev.enmState = usbDeterminState (&Dev); + PUSBDEVICE pDev = (PUSBDEVICE) RTMemAlloc (sizeof(*pDev)); + if (pDev) + { + *pDev = Dev; + if (Dev.enmState != USBDEVICESTATE_UNSUPPORTED) + { + RTStrAPrintf((char **)&pDev->pszAddress, "%s/%03d/%03d", mUsbfsRoot.c_str(), pDev->bBus, pDev->bDevNum); + if (pDev->pszAddress) + { + if (ppNext) + *ppNext = pDev; + else + pFirst = pDev; + ppNext = &pDev->pNext; + } + else + { + rc = VERR_NO_MEMORY; + freeDevice (pDev); + } + } + else + freeDevice (pDev); + } + else + rc = VERR_NO_MEMORY; + } + + /* + * Success? + */ + if (RT_FAILURE (rc)) + { + LogFlow (("USBProxyServiceLinux::getDevices: rc=%Rrc\n", rc)); + while (pFirst) + { + PUSBDEVICE pFree = pFirst; + pFirst = pFirst->pNext; + freeDevice (pFree); + } + } + } + return pFirst; +} + |
