1
/* $Id: USBGetDevices.cpp 35599 2011-01-18 09:52:52Z vboxsync $ */
3
* VirtualBox Linux host USB device enumeration.
7
* Copyright (C) 2006-2010 Oracle Corporation
9
* This file is part of VirtualBox Open Source Edition (OSE), as
10
* available from http://www.virtualbox.org. This file is free software;
11
* you can redistribute it and/or modify it under the terms of the GNU
12
* General Public License (GPL) as published by the Free Software
13
* Foundation, in version 2 as it comes in the "COPYING" file of the
14
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19
/*******************************************************************************
21
*******************************************************************************/
23
#include "USBGetDevices.h"
26
#include <VBox/usblib.h>
28
#include <iprt/linux/sysfs.h>
29
#include <iprt/cdefs.h>
30
#include <iprt/ctype.h>
35
#include <iprt/param.h>
36
#include <iprt/path.h>
37
#include <iprt/string.h>
40
#ifdef VBOX_WITH_LINUX_COMPILER_H
41
# include <linux/compiler.h>
43
#include <linux/usbdevice_fs.h>
45
#include <sys/types.h>
57
/*******************************************************************************
58
* Structures and Typedefs *
59
*******************************************************************************/
60
/** Suffix translation. */
61
typedef struct USBSUFF
68
typedef const USBSUFF *PCUSBSUFF;
70
/** Structure describing a host USB device */
71
typedef struct USBDeviceInfo
73
/** The device node of the device. */
75
/** The system identifier of the device. Specific to the probing
78
/** List of interfaces as sysfs paths */
79
VECTOR_PTR(char *) mvecpszInterfaces;
82
/*******************************************************************************
84
*******************************************************************************/
86
* Suffixes for the endpoint polling interval.
88
static const USBSUFF s_aIntervalSuff[] =
92
{ "ns", 2, 1, 1000000 },
94
{ "", 0, 0, 0 } /* term */
98
* List of well-known USB device tree locations.
100
static const USBDEVTREELOCATION s_aTreeLocations[] =
102
{ "/proc/bus/usb", false },
103
{ "/dev/bus/usb", false },
104
{ "/dev/vboxusb", true },
105
{ "/dev/bus/usb", true },
110
* "reads" the number suffix. It's more like validating it and
111
* skipping the necessary number of chars.
113
static int usbReadSkipSuffix(char **ppszNext)
115
char *pszNext = *ppszNext;
116
if (!RT_C_IS_SPACE(*pszNext) && *pszNext)
119
if (pszNext[0] == 'm' && pszNext[1] == 's')
121
else if (pszNext[0] == 'm' && pszNext[1] == 'A')
124
/* skip parenthesis */
127
pszNext = strchr(pszNext, ')');
130
AssertMsgFailed(("*ppszNext=%s\n", *ppszNext));
131
return VERR_PARSE_ERROR;
135
/* blank or end of the line. */
136
if (!RT_C_IS_SPACE(*pszNext) && *pszNext)
138
AssertMsgFailed(("pszNext=%s\n", pszNext));
139
return VERR_PARSE_ERROR;
151
* Reads a USB number returning the number and the position of the next character to parse.
153
static int usbReadNum(const char *pszValue, unsigned uBase, uint32_t u32Mask, PCUSBSUFF paSuffs, void *pvNum, char **ppszNext)
156
* Initialize return value to zero and strip leading spaces.
160
case 0xff: *(uint8_t *)pvNum = 0; break;
161
case 0xffff: *(uint16_t *)pvNum = 0; break;
162
case 0xffffffff: *(uint32_t *)pvNum = 0; break;
164
pszValue = RTStrStripL(pszValue);
168
* Try convert the number.
172
RTStrToUInt32Ex(pszValue, &pszNext, uBase, &u32);
173
if (pszNext == pszValue)
175
AssertMsgFailed(("pszValue=%d\n", pszValue));
184
AssertMsgFailed(("pszValue=%d u32=%#x lMask=%#x\n", pszValue, u32, u32Mask));
185
return VERR_OUT_OF_RANGE;
189
* Validate and skip stuff following the number.
193
if (!RT_C_IS_SPACE(*pszNext) && *pszNext)
195
for (PCUSBSUFF pSuff = paSuffs; pSuff->szSuff[0]; pSuff++)
197
if ( !strncmp(pSuff->szSuff, pszNext, pSuff->cchSuff)
198
&& (!pszNext[pSuff->cchSuff] || RT_C_IS_SPACE(pszNext[pSuff->cchSuff])))
211
int rc = usbReadSkipSuffix(&pszNext);
223
case 0xff: *(uint8_t *)pvNum = (uint8_t)u32; break;
224
case 0xffff: *(uint16_t *)pvNum = (uint16_t)u32; break;
225
case 0xffffffff: *(uint32_t *)pvNum = (uint32_t)u32; break;
232
static int usbRead8(const char *pszValue, unsigned uBase, uint8_t *pu8, char **ppszNext)
234
return usbReadNum(pszValue, uBase, 0xff, NULL, pu8, ppszNext);
238
static int usbRead16(const char *pszValue, unsigned uBase, uint16_t *pu16, char **ppszNext)
240
return usbReadNum(pszValue, uBase, 0xffff, NULL, pu16, ppszNext);
245
static int usbRead16Suff(const char *pszValue, unsigned uBase, PCUSBSUFF paSuffs, uint16_t *pu16, char **ppszNext)
247
return usbReadNum(pszValue, uBase, 0xffff, paSuffs, pu16, ppszNext);
253
* Reads a USB BCD number returning the number and the position of the next character to parse.
254
* The returned number contains the integer part in the high byte and the decimal part in the low byte.
256
static int usbReadBCD(const char *pszValue, unsigned uBase, uint16_t *pu16, char **ppszNext)
259
* Initialize return value to zero and strip leading spaces.
262
pszValue = RTStrStripL(pszValue);
266
* Try convert the number.
271
RTStrToUInt32Ex(pszValue, &pszNext, uBase, &u32Int);
272
if (pszNext == pszValue)
274
AssertMsgFailed(("pszValue=%s\n", pszValue));
279
AssertMsgFailed(("pszValue=%s u32Int=%#x (int)\n", pszValue, u32Int));
280
return VERR_OUT_OF_RANGE;
283
/* skip dot and read decimal part */
286
AssertMsgFailed(("pszValue=%s pszNext=%s (int)\n", pszValue, pszNext));
287
return VERR_PARSE_ERROR;
289
char *pszValue2 = RTStrStripL(pszNext + 1);
291
RTStrToUInt32Ex(pszValue2, &pszNext, uBase, &u32Dec);
292
if (pszNext == pszValue)
294
AssertMsgFailed(("pszValue=%s\n", pszValue));
299
AssertMsgFailed(("pszValue=%s u32Dec=%#x\n", pszValue, u32Dec));
300
return VERR_OUT_OF_RANGE;
304
* Validate and skip stuff following the number.
306
int rc = usbReadSkipSuffix(&pszNext);
314
*pu16 = (uint16_t)u32Int << 8 | (uint16_t)u32Dec;
321
* Reads a string, i.e. allocates memory and copies it.
323
* We assume that a string is Utf8 and if that's not the case
324
* (pre-2.6.32-kernels used Latin-1, but so few devices return non-ASCII that
325
* this usually goes unnoticed) then we mercilessly force it to be so.
327
static int usbReadStr(const char *pszValue, const char **ppsz)
332
RTStrFree((char *)*ppsz);
333
psz = RTStrDup(pszValue);
336
RTStrPurgeEncoding(psz);
340
return VERR_NO_MEMORY;
345
* Skips the current property.
347
static char *usbReadSkip(char *pszValue)
349
char *psz = strchr(pszValue, '=');
351
psz = strchr(psz + 1, '=');
353
return strchr(pszValue, '\0');
354
while (psz > pszValue && !RT_C_IS_SPACE(psz[-1]))
356
Assert(psz > pszValue);
362
* Determine the USB speed.
364
static int usbReadSpeed(const char *pszValue, USBDEVICESPEED *pSpd, char **ppszNext)
366
pszValue = RTStrStripL(pszValue);
367
/* verified with Linux 2.4.0 ... Linux 2.6.25 */
368
if (!strncmp(pszValue, "1.5", 3))
369
*pSpd = USBDEVICESPEED_LOW;
370
else if (!strncmp(pszValue, "12 ", 3))
371
*pSpd = USBDEVICESPEED_FULL;
372
else if (!strncmp(pszValue, "480", 3))
373
*pSpd = USBDEVICESPEED_HIGH;
375
*pSpd = USBDEVICESPEED_UNKNOWN;
376
while (pszValue[0] != '\0' && !RT_C_IS_SPACE(pszValue[0]))
378
*ppszNext = (char *)pszValue;
384
* Compare a prefix and returns pointer to the char following it if it matches.
386
static char *usbPrefix(char *psz, const char *pszPref, size_t cchPref)
388
if (strncmp(psz, pszPref, cchPref))
390
return psz + cchPref;
395
* Does some extra checks to improve the detected device state.
397
* We cannot distinguish between USED_BY_HOST_CAPTURABLE and
398
* USED_BY_GUEST, HELD_BY_PROXY all that well and it shouldn't be
401
* We will however, distinguish between the device we have permissions
402
* to open and those we don't. This is necessary for two reasons.
404
* Firstly, because it's futile to even attempt opening a device which we
405
* don't have access to, it only serves to confuse the user. (That said,
406
* it might also be a bit confusing for the user to see that a USB device
407
* is grayed out with no further explanation, and no way of generating an
408
* error hinting at why this is the case.)
410
* Secondly and more importantly, we're racing against udevd with respect
411
* to permissions and group settings on newly plugged devices. When we
412
* detect a new device that we cannot access we will poll on it for a few
413
* seconds to give udevd time to fix it. The polling is actually triggered
414
* in the 'new device' case in the compare loop.
416
* The USBDEVICESTATE_USED_BY_HOST state is only used for this no-access
417
* case, while USBDEVICESTATE_UNSUPPORTED is only used in the 'hub' case.
418
* When it's neither of these, we set USBDEVICESTATE_UNUSED or
419
* USBDEVICESTATE_USED_BY_HOST_CAPTURABLE depending on whether there is
420
* a driver associated with any of the interfaces.
422
* All except the access check and a special idVendor == 0 precaution
423
* is handled at parse time.
425
* @returns The adjusted state.
426
* @param pDevice The device.
428
static USBDEVICESTATE usbDeterminState(PCUSBDEVICE pDevice)
431
* If it's already flagged as unsupported, there is nothing to do.
433
USBDEVICESTATE enmState = pDevice->enmState;
434
if (enmState == USBDEVICESTATE_UNSUPPORTED)
435
return USBDEVICESTATE_UNSUPPORTED;
438
* Root hubs and similar doesn't have any vendor id, just
439
* refuse these device.
441
if (!pDevice->idVendor)
442
return USBDEVICESTATE_UNSUPPORTED;
445
* Check if we've got access to the device, if we haven't flag
446
* it as used-by-host.
448
#ifndef VBOX_USB_WITH_SYSFS
449
const char *pszAddress = pDevice->pszAddress;
451
if (pDevice->pszAddress == NULL)
452
/* We can't do much with the device without an address. */
453
return USBDEVICESTATE_UNSUPPORTED;
454
const char *pszAddress = strstr(pDevice->pszAddress, "//device:");
455
pszAddress = pszAddress != NULL
456
? pszAddress + sizeof("//device:") - 1
457
: pDevice->pszAddress;
459
if ( access(pszAddress, R_OK | W_OK) != 0
461
return USBDEVICESTATE_USED_BY_HOST;
463
#ifdef VBOX_USB_WITH_SYSFS
465
* @todo Check that any other essential fields are present and mark as
466
* invalid if not. Particularly to catch the case where the device was
467
* unplugged while we were reading in its properties.
475
/** Just a worker for USBProxyServiceLinux::getDevices that avoids some code duplication. */
476
static int addDeviceToChain(PUSBDEVICE pDev, PUSBDEVICE *ppFirst, PUSBDEVICE **pppNext, const char *pcszUsbfsRoot, bool testfs, int rc)
478
/* usbDeterminState requires the address. */
479
PUSBDEVICE pDevNew = (PUSBDEVICE)RTMemDup(pDev, sizeof(*pDev));
482
RTStrAPrintf((char **)&pDevNew->pszAddress, "%s/%03d/%03d", pcszUsbfsRoot, pDevNew->bBus, pDevNew->bDevNum);
483
if (pDevNew->pszAddress)
485
pDevNew->enmState = usbDeterminState(pDevNew);
486
if (pDevNew->enmState != USBDEVICESTATE_UNSUPPORTED || testfs)
492
*pppNext = &pDevNew->pNext;
506
deviceFreeMembers(pDev);
513
static int openDevicesFile(const char *pcszUsbfsRoot, FILE **ppFile)
517
RTStrAPrintf(&pszPath, "%s/devices", pcszUsbfsRoot);
519
return VERR_NO_MEMORY;
520
pFile = fopen(pszPath, "r");
523
return RTErrConvertFromErrno(errno);
529
* USBProxyService::getDevices() implementation for usbfs. The @a testfs flag
530
* tells the function to return information about unsupported devices as well.
531
* This is used as a sanity test to check that a devices file is really what
534
static PUSBDEVICE getDevicesFromUsbfs(const char *pcszUsbfsRoot, bool testfs)
536
PUSBDEVICE pFirst = NULL;
539
rc = openDevicesFile(pcszUsbfsRoot, &pFile);
542
PUSBDEVICE *ppNext = NULL;
547
Dev.enmState = USBDEVICESTATE_UNUSED;
549
/* Set close on exit and hope no one is racing us. */
550
rc = fcntl(fileno(pFile), F_SETFD, FD_CLOEXEC) >= 0
552
: RTErrConvertFromErrno(errno);
553
while ( RT_SUCCESS(rc)
554
&& fgets(szLine, sizeof(szLine), pFile))
559
/* validate and remove the trailing newline. */
560
psz = strchr(szLine, '\0');
561
if (psz[-1] != '\n' && !feof(pFile))
563
AssertMsgFailed(("Line too long. (cch=%d)\n", strlen(szLine)));
568
psz = RTStrStrip(szLine);
573
* Interpret the line.
574
* (Ordered by normal occurrence.)
579
psz = RTStrStripL(psz + 3);
580
#define PREFIX(str) ( (pszValue = usbPrefix(psz, str, sizeof(str) - 1)) != NULL )
584
* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd
585
* | | | | | | | | |__MaxChildren
586
* | | | | | | | |__Device Speed in Mbps
587
* | | | | | | |__DeviceNumber
588
* | | | | | |__Count of devices at this level
589
* | | | | |__Connector/Port on Parent for this device
590
* | | | |__Parent DeviceNumber
591
* | | |__Level in topology for this bus
593
* |__Topology info tag
597
AssertMsg(cHits >= 3 || cHits == 0, ("cHits=%d\n", cHits));
599
rc = addDeviceToChain(&Dev, &pFirst, &ppNext, pcszUsbfsRoot, testfs, rc);
601
deviceFreeMembers(&Dev);
603
/* Reset device state */
604
memset(&Dev, 0, sizeof (Dev));
605
Dev.enmState = USBDEVICESTATE_UNUSED;
608
/* parse the line. */
609
while (*psz && RT_SUCCESS(rc))
612
rc = usbRead8(pszValue, 10, &Dev.bBus, &psz);
613
else if (PREFIX("Port="))
614
rc = usbRead8(pszValue, 10, &Dev.bPort, &psz);
615
else if (PREFIX("Spd="))
616
rc = usbReadSpeed(pszValue, &Dev.enmSpeed, &psz);
617
else if (PREFIX("Dev#="))
618
rc = usbRead8(pszValue, 10, &Dev.bDevNum, &psz);
620
psz = usbReadSkip(psz);
621
psz = RTStrStripL(psz);
627
* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd
628
* | | | |__Number of isochronous requests
629
* | | |__Number of interrupt requests
630
* | |__Total Bandwidth allocated to this bus
631
* |__Bandwidth info tag
637
* D: Ver=x.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd
638
* | | | | | | |__NumberConfigurations
639
* | | | | | |__MaxPacketSize of Default Endpoint
640
* | | | | |__DeviceProtocol
641
* | | | |__DeviceSubClass
643
* | |__Device USB version
644
* |__Device info tag #1
647
while (*psz && RT_SUCCESS(rc))
650
rc = usbReadBCD(pszValue, 16, &Dev.bcdUSB, &psz);
651
else if (PREFIX("Cls="))
653
rc = usbRead8(pszValue, 16, &Dev.bDeviceClass, &psz);
654
if (RT_SUCCESS(rc) && Dev.bDeviceClass == 9 /* HUB */)
655
Dev.enmState = USBDEVICESTATE_UNSUPPORTED;
657
else if (PREFIX("Sub="))
658
rc = usbRead8(pszValue, 16, &Dev.bDeviceSubClass, &psz);
659
else if (PREFIX("Prot="))
660
rc = usbRead8(pszValue, 16, &Dev.bDeviceProtocol, &psz);
661
//else if (PREFIX("MxPS="))
662
// rc = usbRead16(pszValue, 10, &Dev.wMaxPacketSize, &psz);
663
else if (PREFIX("#Cfgs="))
664
rc = usbRead8(pszValue, 10, &Dev.bNumConfigurations, &psz);
666
psz = usbReadSkip(psz);
667
psz = RTStrStripL(psz);
673
* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx
674
* | | | |__Product revision number
675
* | | |__Product ID code
676
* | |__Vendor ID code
677
* |__Device info tag #2
680
while (*psz && RT_SUCCESS(rc))
682
if (PREFIX("Vendor="))
683
rc = usbRead16(pszValue, 16, &Dev.idVendor, &psz);
684
else if (PREFIX("ProdID="))
685
rc = usbRead16(pszValue, 16, &Dev.idProduct, &psz);
686
else if (PREFIX("Rev="))
687
rc = usbReadBCD(pszValue, 16, &Dev.bcdDevice, &psz);
689
psz = usbReadSkip(psz);
690
psz = RTStrStripL(psz);
699
if (PREFIX("Manufacturer="))
700
rc = usbReadStr(pszValue, &Dev.pszManufacturer);
701
else if (PREFIX("Product="))
702
rc = usbReadStr(pszValue, &Dev.pszProduct);
703
else if (PREFIX("SerialNumber="))
705
rc = usbReadStr(pszValue, &Dev.pszSerialNumber);
707
Dev.u64SerialHash = USBLibHashSerial(pszValue);
712
* C:* #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA
713
* | | | | | |__MaxPower in mA
714
* | | | | |__Attributes
715
* | | | |__ConfiguratioNumber
716
* | | |__NumberOfInterfaces
717
* | |__ "*" indicates the active configuration (others are " ")
724
* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss
725
* | | | | | | | |__Driver name
726
* | | | | | | | or "(none)"
727
* | | | | | | |__InterfaceProtocol
728
* | | | | | |__InterfaceSubClass
729
* | | | | |__InterfaceClass
730
* | | | |__NumberOfEndpoints
731
* | | |__AlternateSettingNumber
732
* | |__InterfaceNumber
733
* |__Interface info tag
737
/* Check for thing we don't support. */
738
while (*psz && RT_SUCCESS(rc))
740
if (PREFIX("Driver="))
742
const char *pszDriver = NULL;
743
rc = usbReadStr(pszValue, &pszDriver);
746
|| !strcmp(pszDriver, "(none)")
747
|| !strcmp(pszDriver, "(no driver)"))
749
else if (!strcmp(pszDriver, "hub"))
750
Dev.enmState = USBDEVICESTATE_UNSUPPORTED;
751
else if (Dev.enmState == USBDEVICESTATE_UNUSED)
752
Dev.enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
753
RTStrFree((char *)pszDriver);
754
break; /* last attrib */
756
else if (PREFIX("Cls="))
758
uint8_t bInterfaceClass;
759
rc = usbRead8(pszValue, 16, &bInterfaceClass, &psz);
760
if (RT_SUCCESS(rc) && bInterfaceClass == 9 /* HUB */)
761
Dev.enmState = USBDEVICESTATE_UNSUPPORTED;
764
psz = usbReadSkip(psz);
765
psz = RTStrStripL(psz);
772
* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms
773
* | | | | |__Interval (max) between transfers
774
* | | | |__EndpointMaxPacketSize
775
* | | |__Attributes(EndpointType)
776
* | |__EndpointAddress(I=In,O=Out)
777
* |__Endpoint info tag
788
* Add the current entry.
790
AssertMsg(cHits >= 3 || cHits == 0, ("cHits=%d\n", cHits));
792
rc = addDeviceToChain(&Dev, &pFirst, &ppNext, pcszUsbfsRoot, testfs, rc);
801
PUSBDEVICE pFree = pFirst;
802
pFirst = pFirst->pNext;
808
LogFlow(("USBProxyServiceLinux::getDevices: rc=%Rrc\n", rc));
812
#ifdef VBOX_USB_WITH_SYSFS
814
static void USBDevInfoCleanup(USBDeviceInfo *pSelf)
816
RTStrFree(pSelf->mDevice);
817
RTStrFree(pSelf->mSysfsPath);
818
pSelf->mDevice = pSelf->mSysfsPath = NULL;
819
VEC_CLEANUP_PTR(&pSelf->mvecpszInterfaces);
822
static int USBDevInfoInit(USBDeviceInfo *pSelf, const char *aDevice,
823
const char *aSystemID)
825
pSelf->mDevice = aDevice ? RTStrDup(aDevice) : NULL;
826
pSelf->mSysfsPath = aSystemID ? RTStrDup(aSystemID) : NULL;
827
VEC_INIT_PTR(&pSelf->mvecpszInterfaces, char *, RTStrFree);
828
if ((aDevice && !pSelf->mDevice) || (aSystemID && ! pSelf->mSysfsPath))
830
USBDevInfoCleanup(pSelf);
836
#define USBDEVICE_MAJOR 189
838
/** Deduce the bus that a USB device is plugged into from the device node
839
* number. See drivers/usb/core/hub.c:usb_new_device as of Linux 2.6.20. */
840
static unsigned usbBusFromDevNum(dev_t devNum)
842
AssertReturn(devNum, 0);
843
AssertReturn(major(devNum) == USBDEVICE_MAJOR, 0);
844
return (minor(devNum) >> 7) + 1;
848
/** Deduce the device number of a USB device on the bus from the device node
849
* number. See drivers/usb/core/hub.c:usb_new_device as of Linux 2.6.20. */
850
static unsigned usbDeviceFromDevNum(dev_t devNum)
852
AssertReturn(devNum, 0);
853
AssertReturn(major(devNum) == USBDEVICE_MAJOR, 0);
854
return (minor(devNum) & 127) + 1;
859
* If a file @a pcszNode from /sys/bus/usb/devices is a device rather than an
860
* interface add an element for the device to @a pvecDevInfo.
862
static int addIfDevice(const char *pcszDevicesRoot,
863
const char *pcszNode,
864
VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo)
866
const char *pcszFile = strrchr(pcszNode, '/');
867
if (strchr(pcszFile, ':'))
869
dev_t devnum = RTLinuxSysFsReadDevNumFile("%s/dev", pcszNode);
870
/* Sanity test of our static helpers */
871
Assert(usbBusFromDevNum(makedev(USBDEVICE_MAJOR, 517)) == 5);
872
Assert(usbDeviceFromDevNum(makedev(USBDEVICE_MAJOR, 517)) == 6);
875
char szDevPath[RTPATH_MAX];
877
cchDevPath = RTLinuxFindDevicePath(devnum, RTFS_TYPE_DEV_CHAR,
878
szDevPath, sizeof(szDevPath),
881
usbBusFromDevNum(devnum),
882
usbDeviceFromDevNum(devnum));
887
if (USBDevInfoInit(&info, szDevPath, pcszNode))
888
if (RT_SUCCESS(VEC_PUSH_BACK_OBJ(pvecDevInfo, USBDeviceInfo,
891
USBDevInfoCleanup(&info);
892
return VERR_NO_MEMORY;
895
/** The logic for testing whether a sysfs address corresponds to an
896
* interface of a device. Both must be referenced by their canonical
897
* sysfs paths. This is not tested, as the test requires file-system
899
static bool muiIsAnInterfaceOf(const char *pcszIface, const char *pcszDev)
901
size_t cchDev = strlen(pcszDev);
903
AssertPtr(pcszIface);
905
Assert(pcszIface[0] == '/');
906
Assert(pcszDev[0] == '/');
907
Assert(pcszDev[cchDev - 1] != '/');
908
/* If this passes, pcszIface is at least cchDev long */
909
if (strncmp(pcszIface, pcszDev, cchDev))
911
/* If this passes, pcszIface is longer than cchDev */
912
if (pcszIface[cchDev] != '/')
914
/* In sysfs an interface is an immediate subdirectory of the device */
915
if (strchr(pcszIface + cchDev + 1, '/'))
917
/* And it always has a colon in its name */
918
if (!strchr(pcszIface + cchDev + 1, ':'))
920
/* And hopefully we have now elimitated everything else */
926
/** Unit test the logic in muiIsAnInterfaceOf in debug builds. */
927
class testIsAnInterfaceOf
930
testIsAnInterfaceOf()
932
Assert(muiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0",
933
"/sys/devices/pci0000:00/0000:00:1a.0/usb3"));
934
Assert(!muiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-1",
935
"/sys/devices/pci0000:00/0000:00:1a.0/usb3"));
936
Assert(!muiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0/driver",
937
"/sys/devices/pci0000:00/0000:00:1a.0/usb3"));
940
static testIsAnInterfaceOf testIsAnInterfaceOfInst;
941
# endif /* __cplusplus */
945
* Tell whether a file in /sys/bus/usb/devices is an interface rather than a
946
* device. To be used with getDeviceInfoFromSysfs().
948
static int addIfInterfaceOf(const char *pcszNode, USBDeviceInfo *pInfo)
950
if (!muiIsAnInterfaceOf(pcszNode, pInfo->mSysfsPath))
952
char *pszDup = (char *)RTStrDup(pcszNode);
954
if (RT_SUCCESS(VEC_PUSH_BACK_PTR(&pInfo->mvecpszInterfaces,
958
return VERR_NO_MEMORY;
961
/** Helper for readFilePaths(). Adds the entries from the open directory
962
* @a pDir to the vector @a pvecpchDevs using either the full path or the
963
* realpath() and skipping hidden files and files on which realpath() fails. */
964
static int readFilePathsFromDir(const char *pcszPath, DIR *pDir,
965
VECTOR_PTR(char *) *pvecpchDevs)
967
struct dirent entry, *pResult;
970
for (err = readdir_r(pDir, &entry, &pResult); pResult;
971
err = readdir_r(pDir, &entry, &pResult))
973
char szPath[RTPATH_MAX + 1], szRealPath[RTPATH_MAX + 1], *pszPath;
974
if (entry.d_name[0] == '.')
976
if (snprintf(szPath, sizeof(szPath), "%s/%s", pcszPath,
978
return RTErrConvertFromErrno(errno);
979
if (!realpath(szPath, szRealPath))
980
return RTErrConvertFromErrno(errno);
981
pszPath = RTStrDup(szRealPath);
983
return VERR_NO_MEMORY;
984
if (RT_FAILURE(rc = VEC_PUSH_BACK_PTR(pvecpchDevs, char *, pszPath)))
987
return RTErrConvertFromErrno(err);
991
* Dump the names of a directory's entries into a vector of char pointers.
993
* @returns zero on success or (positive) posix error value.
994
* @param pcszPath the path to dump.
995
* @param pvecpchDevs an empty vector of char pointers - must be cleaned up
996
* by the caller even on failure.
997
* @param withRealPath whether to canonicalise the filename with realpath
999
static int readFilePaths(const char *pcszPath, VECTOR_PTR(char *) *pvecpchDevs)
1004
AssertPtrReturn(pvecpchDevs, EINVAL);
1005
AssertReturn(VEC_SIZE_PTR(pvecpchDevs) == 0, EINVAL);
1006
AssertPtrReturn(pcszPath, EINVAL);
1008
pDir = opendir(pcszPath);
1010
return RTErrConvertFromErrno(errno);
1011
rc = readFilePathsFromDir(pcszPath, pDir, pvecpchDevs);
1012
if (closedir(pDir) < 0 && RT_SUCCESS(rc))
1013
rc = RTErrConvertFromErrno(errno);
1018
* Logic for USBSysfsEnumerateHostDevices.
1019
* @param pvecDevInfo vector of device information structures to add device
1021
* @param pvecpchDevs empty scratch vector which will be freed by the caller,
1022
* to simplify exit logic
1024
static int doSysfsEnumerateHostDevices(const char *pcszDevicesRoot,
1025
VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo,
1026
VECTOR_PTR(char *) *pvecpchDevs)
1029
USBDeviceInfo *pInfo;
1032
AssertPtrReturn(pvecDevInfo, VERR_INVALID_POINTER);
1033
LogFlowFunc (("pvecDevInfo=%p\n", pvecDevInfo));
1035
rc = readFilePaths("/sys/bus/usb/devices", pvecpchDevs);
1038
VEC_FOR_EACH(pvecpchDevs, char *, ppszEntry)
1039
if (RT_FAILURE(rc = addIfDevice(pcszDevicesRoot, *ppszEntry,
1042
VEC_FOR_EACH(pvecDevInfo, USBDeviceInfo, pInfo)
1043
VEC_FOR_EACH(pvecpchDevs, char *, ppszEntry)
1044
if (RT_FAILURE(rc = addIfInterfaceOf(*ppszEntry, pInfo)))
1046
return VINF_SUCCESS;
1049
static int USBSysfsEnumerateHostDevices(const char *pcszDevicesRoot,
1050
VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo)
1052
VECTOR_PTR(char *) vecpchDevs;
1053
int rc = VERR_NOT_IMPLEMENTED;
1055
AssertReturn(VEC_SIZE_OBJ(pvecDevInfo) == 0, VERR_INVALID_PARAMETER);
1056
LogFlowFunc(("entered\n"));
1057
VEC_INIT_PTR(&vecpchDevs, char *, RTStrFree);
1058
rc = doSysfsEnumerateHostDevices(pcszDevicesRoot, pvecDevInfo,
1060
VEC_CLEANUP_PTR(&vecpchDevs);
1061
LogFlowFunc(("rc=%Rrc\n", rc));
1066
* Helper function for extracting the port number on the parent device from
1067
* the sysfs path value.
1069
* The sysfs path is a chain of elements separated by forward slashes, and for
1070
* USB devices, the last element in the chain takes the form
1071
* <port>-<port>.[...].<port>[:<config>.<interface>]
1072
* where the first <port> is the port number on the root hub, and the following
1073
* (optional) ones are the port numbers on any other hubs between the device
1074
* and the root hub. The last part (:<config.interface>) is only present for
1075
* interfaces, not for devices. This API should only be called for devices.
1076
* For compatibility with usbfs, which enumerates from zero up, we subtract one
1077
* from the port number.
1079
* For root hubs, the last element in the chain takes the form
1081
* and usbfs always returns port number zero.
1083
* @returns VBox status. pu8Port is set on success.
1084
* @param pszPath The sysfs path to parse.
1085
* @param pu8Port Where to store the port number.
1087
static int usbGetPortFromSysfsPath(const char *pszPath, uint8_t *pu8Port)
1089
AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1090
AssertPtrReturn(pu8Port, VERR_INVALID_POINTER);
1093
* This should not be possible until we get PCs with USB as their primary bus.
1094
* Note: We don't assert this, as we don't expect the caller to validate the
1097
const char *pszLastComp = strrchr(pszPath, '/');
1100
Log(("usbGetPortFromSysfsPath(%s): failed [1]\n", pszPath));
1101
return VERR_INVALID_PARAMETER;
1103
pszLastComp++; /* skip the slash */
1106
* This API should not be called for interfaces, so the last component
1107
* of the path should not contain a colon. We *do* assert this, as it
1108
* might indicate a caller bug.
1110
AssertMsgReturn(strchr(pszLastComp, ':') == NULL, ("%s\n", pszPath), VERR_INVALID_PARAMETER);
1113
* Look for the start of the last number.
1115
const char *pchDash = strrchr(pszLastComp, '-');
1116
const char *pchDot = strrchr(pszLastComp, '.');
1117
if (!pchDash && !pchDot)
1119
/* No -/. so it must be a root hub. Check that it's usb<something>. */
1120
if (strncmp(pszLastComp, "usb", sizeof("usb") - 1) != 0)
1122
Log(("usbGetPortFromSysfsPath(%s): failed [2]\n", pszPath));
1123
return VERR_INVALID_PARAMETER;
1125
return VERR_NOT_SUPPORTED;
1129
const char *pszLastPort = pchDot != NULL
1132
int rc = RTStrToUInt8Full(pszLastPort, 10, pu8Port);
1133
if (rc != VINF_SUCCESS)
1135
Log(("usbGetPortFromSysfsPath(%s): failed [3], rc=%Rrc\n", pszPath, rc));
1136
return VERR_INVALID_PARAMETER;
1140
Log(("usbGetPortFromSysfsPath(%s): failed [4]\n", pszPath));
1141
return VERR_INVALID_PARAMETER;
1144
/* usbfs compatibility, 0-based port number. */
1147
return VINF_SUCCESS;
1152
* Dumps a USBDEVICE structure to the log using LogLevel 3.
1153
* @param pDev The structure to log.
1154
* @todo This is really common code.
1156
DECLINLINE(void) usbLogDevice(PUSBDEVICE pDev)
1160
Log3(("USB device:\n"));
1161
Log3(("Product: %s (%x)\n", pDev->pszProduct, pDev->idProduct));
1162
Log3(("Manufacturer: %s (Vendor ID %x)\n", pDev->pszManufacturer, pDev->idVendor));
1163
Log3(("Serial number: %s (%llx)\n", pDev->pszSerialNumber, pDev->u64SerialHash));
1164
Log3(("Device revision: %d\n", pDev->bcdDevice));
1165
Log3(("Device class: %x\n", pDev->bDeviceClass));
1166
Log3(("Device subclass: %x\n", pDev->bDeviceSubClass));
1167
Log3(("Device protocol: %x\n", pDev->bDeviceProtocol));
1168
Log3(("USB version number: %d\n", pDev->bcdUSB));
1169
Log3(("Device speed: %s\n",
1170
pDev->enmSpeed == USBDEVICESPEED_UNKNOWN ? "unknown"
1171
: pDev->enmSpeed == USBDEVICESPEED_LOW ? "1.5 MBit/s"
1172
: pDev->enmSpeed == USBDEVICESPEED_FULL ? "12 MBit/s"
1173
: pDev->enmSpeed == USBDEVICESPEED_HIGH ? "480 MBit/s"
1174
: pDev->enmSpeed == USBDEVICESPEED_VARIABLE ? "variable"
1176
Log3(("Number of configurations: %d\n", pDev->bNumConfigurations));
1177
Log3(("Bus number: %d\n", pDev->bBus));
1178
Log3(("Port number: %d\n", pDev->bPort));
1179
Log3(("Device number: %d\n", pDev->bDevNum));
1180
Log3(("Device state: %s\n",
1181
pDev->enmState == USBDEVICESTATE_UNSUPPORTED ? "unsupported"
1182
: pDev->enmState == USBDEVICESTATE_USED_BY_HOST ? "in use by host"
1183
: pDev->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE ? "in use by host, possibly capturable"
1184
: pDev->enmState == USBDEVICESTATE_UNUSED ? "not in use"
1185
: pDev->enmState == USBDEVICESTATE_HELD_BY_PROXY ? "held by proxy"
1186
: pDev->enmState == USBDEVICESTATE_USED_BY_GUEST ? "used by guest"
1188
Log3(("OS device address: %s\n", pDev->pszAddress));
1192
* In contrast to usbReadBCD() this function can handle BCD values without
1193
* a decimal separator. This is necessary for parsing bcdDevice.
1194
* @param pszBuf Pointer to the string buffer.
1195
* @param pu15 Pointer to the return value.
1196
* @returns IPRT status code.
1198
static int convertSysfsStrToBCD(const char *pszBuf, uint16_t *pu16)
1203
pszBuf = RTStrStripL(pszBuf);
1204
int rc = RTStrToInt32Ex(pszBuf, &pszNext, 16, &i32);
1206
|| rc == VWRN_NUMBER_TOO_BIG
1208
return VERR_NUMBER_TOO_BIG;
1209
if (*pszNext == '.')
1212
return VERR_NUMBER_TOO_BIG;
1214
rc = RTStrToInt32Ex(pszNext+1, &pszNext, 16, &i32Lo);
1216
|| rc == VWRN_NUMBER_TOO_BIG
1219
return VERR_NUMBER_TOO_BIG;
1220
i32 = (i32 << 8) | i32Lo;
1223
|| (*pszNext != '\0' && *pszNext != ' '))
1224
return VERR_NUMBER_TOO_BIG;
1226
*pu16 = (uint16_t)i32;
1227
return VINF_SUCCESS;
1230
#endif /* VBOX_USB_WITH_SYSFS */
1232
static void fillInDeviceFromSysfs(USBDEVICE *Dev, USBDeviceInfo *pInfo)
1235
const char *pszSysfsPath = pInfo->mSysfsPath;
1237
/* Fill in the simple fields */
1238
Dev->enmState = USBDEVICESTATE_UNUSED;
1239
Dev->bBus = RTLinuxSysFsReadIntFile(10, "%s/busnum", pszSysfsPath);
1240
Dev->bDeviceClass = RTLinuxSysFsReadIntFile(16, "%s/bDeviceClass", pszSysfsPath);
1241
Dev->bDeviceSubClass = RTLinuxSysFsReadIntFile(16, "%s/bDeviceSubClass", pszSysfsPath);
1242
Dev->bDeviceProtocol = RTLinuxSysFsReadIntFile(16, "%s/bDeviceProtocol", pszSysfsPath);
1243
Dev->bNumConfigurations = RTLinuxSysFsReadIntFile(10, "%s/bNumConfigurations", pszSysfsPath);
1244
Dev->idVendor = RTLinuxSysFsReadIntFile(16, "%s/idVendor", pszSysfsPath);
1245
Dev->idProduct = RTLinuxSysFsReadIntFile(16, "%s/idProduct", pszSysfsPath);
1246
Dev->bDevNum = RTLinuxSysFsReadIntFile(10, "%s/devnum", pszSysfsPath);
1248
/* Now deal with the non-numeric bits. */
1249
char szBuf[1024]; /* Should be larger than anything a sane device
1250
* will need, and insane devices can be unsupported
1251
* until further notice. */
1254
/* For simplicity, we just do strcmps on the next one. */
1255
cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/speed",
1257
if (cchRead <= 0 || (size_t) cchRead == sizeof(szBuf))
1258
Dev->enmState = USBDEVICESTATE_UNSUPPORTED;
1260
Dev->enmSpeed = !strcmp(szBuf, "1.5") ? USBDEVICESPEED_LOW
1261
: !strcmp(szBuf, "12") ? USBDEVICESPEED_FULL
1262
: !strcmp(szBuf, "480") ? USBDEVICESPEED_HIGH
1263
: USBDEVICESPEED_UNKNOWN;
1265
cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/version",
1267
if (cchRead <= 0 || (size_t) cchRead == sizeof(szBuf))
1268
Dev->enmState = USBDEVICESTATE_UNSUPPORTED;
1271
rc = convertSysfsStrToBCD(szBuf, &Dev->bcdUSB);
1274
Dev->enmState = USBDEVICESTATE_UNSUPPORTED;
1275
Dev->bcdUSB = (uint16_t)-1;
1279
cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/bcdDevice",
1281
if (cchRead <= 0 || (size_t) cchRead == sizeof(szBuf))
1282
Dev->bcdDevice = (uint16_t)-1;
1285
rc = convertSysfsStrToBCD(szBuf, &Dev->bcdDevice);
1287
Dev->bcdDevice = (uint16_t)-1;
1290
/* Now do things that need string duplication */
1291
cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/product",
1293
if (cchRead > 0 && (size_t) cchRead < sizeof(szBuf))
1295
RTStrPurgeEncoding(szBuf);
1296
Dev->pszProduct = RTStrDup(szBuf);
1299
cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/serial",
1301
if (cchRead > 0 && (size_t) cchRead < sizeof(szBuf))
1303
RTStrPurgeEncoding(szBuf);
1304
Dev->pszSerialNumber = RTStrDup(szBuf);
1305
Dev->u64SerialHash = USBLibHashSerial(szBuf);
1308
cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/manufacturer",
1310
if (cchRead > 0 && (size_t) cchRead < sizeof(szBuf))
1312
RTStrPurgeEncoding(szBuf);
1313
Dev->pszManufacturer = RTStrDup(szBuf);
1316
/* Work out the port number */
1317
if (RT_FAILURE(usbGetPortFromSysfsPath(pszSysfsPath, &Dev->bPort)))
1318
Dev->enmState = USBDEVICESTATE_UNSUPPORTED;
1320
/* Check the interfaces to see if we can support the device. */
1322
VEC_FOR_EACH(&pInfo->mvecpszInterfaces, char *, ppszIf)
1324
ssize_t cb = RTLinuxSysFsGetLinkDest(szBuf, sizeof(szBuf), "%s/driver",
1326
if (cb > 0 && Dev->enmState != USBDEVICESTATE_UNSUPPORTED)
1327
Dev->enmState = (strcmp(szBuf, "hub") == 0)
1328
? USBDEVICESTATE_UNSUPPORTED
1329
: USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
1330
if (RTLinuxSysFsReadIntFile(16, "%s/bInterfaceClass",
1331
*ppszIf) == 9 /* hub */)
1332
Dev->enmState = USBDEVICESTATE_UNSUPPORTED;
1335
/* We use a double slash as a separator in the pszAddress field. This is
1336
* alright as the two paths can't contain a slash due to the way we build
1338
char *pszAddress = NULL;
1339
RTStrAPrintf(&pszAddress, "sysfs:%s//device:%s", pszSysfsPath,
1341
Dev->pszAddress = pszAddress;
1343
/* Work out from the data collected whether we can support this device. */
1344
Dev->enmState = usbDeterminState(Dev);
1349
* USBProxyService::getDevices() implementation for sysfs.
1351
static PUSBDEVICE getDevicesFromSysfs(const char *pcszDevicesRoot, bool testfs)
1353
#ifdef VBOX_USB_WITH_SYSFS
1354
/* Add each of the devices found to the chain. */
1355
PUSBDEVICE pFirst = NULL;
1356
PUSBDEVICE pLast = NULL;
1357
VECTOR_OBJ(USBDeviceInfo) vecDevInfo;
1358
USBDeviceInfo *pInfo;
1361
VEC_INIT_OBJ(&vecDevInfo, USBDeviceInfo, USBDevInfoCleanup);
1362
rc = USBSysfsEnumerateHostDevices(pcszDevicesRoot, &vecDevInfo);
1365
VEC_FOR_EACH(&vecDevInfo, USBDeviceInfo, pInfo)
1367
USBDEVICE *Dev = (USBDEVICE *)RTMemAllocZ(sizeof(USBDEVICE));
1369
rc = VERR_NO_MEMORY;
1372
fillInDeviceFromSysfs(Dev, pInfo);
1375
&& ( Dev->enmState != USBDEVICESTATE_UNSUPPORTED
1377
&& Dev->pszAddress != NULL
1383
pLast = pLast->pNext;
1386
pFirst = pLast = Dev;
1394
deviceListFree(&pFirst);
1396
VEC_CLEANUP_OBJ(&vecDevInfo);
1398
#else /* !VBOX_USB_WITH_SYSFS */
1400
#endif /* !VBOX_USB_WITH_SYSFS */
1403
/** Is inotify available and working on this system? This is a requirement
1404
* for using USB with sysfs */
1405
/** @todo test the "inotify in glibc but not in the kernel" case. */
1406
static bool inotifyAvailable(void)
1408
int (*inotify_init)(void);
1410
*(void **)(&inotify_init) = dlsym(RTLD_DEFAULT, "inotify_init");
1413
int fd = inotify_init();
1420
PCUSBDEVTREELOCATION USBProxyLinuxGetDeviceRoot(bool fPreferSysfs)
1422
PCUSBDEVTREELOCATION pcBestUsbfs = NULL;
1423
PCUSBDEVTREELOCATION pcBestSysfs = NULL;
1425
bool fHaveInotify = inotifyAvailable();
1426
for (unsigned i = 0; i < RT_ELEMENTS(s_aTreeLocations); ++i)
1427
if (!s_aTreeLocations[i].fUseSysfs)
1431
PUSBDEVICE pDevices;
1433
pDevices = getDevicesFromUsbfs(s_aTreeLocations[i].szDevicesRoot,
1437
pcBestUsbfs = &s_aTreeLocations[i];
1438
deviceListFree(&pDevices);
1446
&& RTPathExists(s_aTreeLocations[i].szDevicesRoot))
1448
PUSBDEVICE pDevices;
1450
pDevices = getDevicesFromSysfs(s_aTreeLocations[i].szDevicesRoot,
1454
pcBestSysfs = &s_aTreeLocations[i];
1455
deviceListFree(&pDevices);
1459
if (pcBestUsbfs && !fPreferSysfs)
1465
PUSBDEVICE USBProxyLinuxGetDevices(const char *pcszDevicesRoot,
1469
return getDevicesFromUsbfs(pcszDevicesRoot, false);
1471
return getDevicesFromSysfs(pcszDevicesRoot, false);