VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/linux/USBGetDevices.cpp@ 63378

Last change on this file since 63378 was 63378, checked in by vboxsync, 8 years ago

Main: warnings (gcc)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 59.6 KB
Line 
1/* $Id: USBGetDevices.cpp 63378 2016-08-12 18:29:33Z vboxsync $ */
2/** @file
3 * VirtualBox Linux host USB device enumeration.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
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.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define VBOX_USB_WITH_USBFS
23#include "USBGetDevices.h"
24
25#include <VBox/err.h>
26#include <VBox/usb.h>
27#include <VBox/usblib.h>
28
29#include <iprt/linux/sysfs.h>
30#include <iprt/cdefs.h>
31#include <iprt/ctype.h>
32#include <iprt/dir.h>
33#include <iprt/env.h>
34#include <iprt/file.h>
35#include <iprt/fs.h>
36#include <iprt/log.h>
37#include <iprt/mem.h>
38#include <iprt/param.h>
39#include <iprt/path.h>
40#include <iprt/string.h>
41#include "vector.h"
42
43#ifdef VBOX_WITH_LINUX_COMPILER_H
44# include <linux/compiler.h>
45#endif
46#include <linux/usbdevice_fs.h>
47
48#include <sys/types.h>
49#include <sys/stat.h>
50#include <sys/vfs.h>
51
52#include <dirent.h>
53#include <dlfcn.h>
54#include <errno.h>
55#include <fcntl.h>
56#include <stdio.h>
57#include <string.h>
58#include <unistd.h>
59
60
61/*********************************************************************************************************************************
62* Structures and Typedefs *
63*********************************************************************************************************************************/
64/** Structure describing a host USB device */
65typedef struct USBDeviceInfo
66{
67 /** The device node of the device. */
68 char *mDevice;
69 /** The system identifier of the device. Specific to the probing
70 * method. */
71 char *mSysfsPath;
72 /** List of interfaces as sysfs paths */
73 VECTOR_PTR(char *) mvecpszInterfaces;
74} USBDeviceInfo;
75
76
77/**
78 * Does some extra checks to improve the detected device state.
79 *
80 * We cannot distinguish between USED_BY_HOST_CAPTURABLE and
81 * USED_BY_GUEST, HELD_BY_PROXY all that well and it shouldn't be
82 * necessary either.
83 *
84 * We will however, distinguish between the device we have permissions
85 * to open and those we don't. This is necessary for two reasons.
86 *
87 * Firstly, because it's futile to even attempt opening a device which we
88 * don't have access to, it only serves to confuse the user. (That said,
89 * it might also be a bit confusing for the user to see that a USB device
90 * is grayed out with no further explanation, and no way of generating an
91 * error hinting at why this is the case.)
92 *
93 * Secondly and more importantly, we're racing against udevd with respect
94 * to permissions and group settings on newly plugged devices. When we
95 * detect a new device that we cannot access we will poll on it for a few
96 * seconds to give udevd time to fix it. The polling is actually triggered
97 * in the 'new device' case in the compare loop.
98 *
99 * The USBDEVICESTATE_USED_BY_HOST state is only used for this no-access
100 * case, while USBDEVICESTATE_UNSUPPORTED is only used in the 'hub' case.
101 * When it's neither of these, we set USBDEVICESTATE_UNUSED or
102 * USBDEVICESTATE_USED_BY_HOST_CAPTURABLE depending on whether there is
103 * a driver associated with any of the interfaces.
104 *
105 * All except the access check and a special idVendor == 0 precaution
106 * is handled at parse time.
107 *
108 * @returns The adjusted state.
109 * @param pDevice The device.
110 */
111static USBDEVICESTATE usbDeterminState(PCUSBDEVICE pDevice)
112{
113 /*
114 * If it's already flagged as unsupported, there is nothing to do.
115 */
116 USBDEVICESTATE enmState = pDevice->enmState;
117 if (enmState == USBDEVICESTATE_UNSUPPORTED)
118 return USBDEVICESTATE_UNSUPPORTED;
119
120 /*
121 * Root hubs and similar doesn't have any vendor id, just
122 * refuse these device.
123 */
124 if (!pDevice->idVendor)
125 return USBDEVICESTATE_UNSUPPORTED;
126
127 /*
128 * Check if we've got access to the device, if we haven't flag
129 * it as used-by-host.
130 */
131#ifndef VBOX_USB_WITH_SYSFS
132 const char *pszAddress = pDevice->pszAddress;
133#else
134 if (pDevice->pszAddress == NULL)
135 /* We can't do much with the device without an address. */
136 return USBDEVICESTATE_UNSUPPORTED;
137 const char *pszAddress = strstr(pDevice->pszAddress, "//device:");
138 pszAddress = pszAddress != NULL
139 ? pszAddress + sizeof("//device:") - 1
140 : pDevice->pszAddress;
141#endif
142 if ( access(pszAddress, R_OK | W_OK) != 0
143 && errno == EACCES)
144 return USBDEVICESTATE_USED_BY_HOST;
145
146#ifdef VBOX_USB_WITH_SYSFS
147 /**
148 * @todo Check that any other essential fields are present and mark as
149 * invalid if not. Particularly to catch the case where the device was
150 * unplugged while we were reading in its properties.
151 */
152#endif
153
154 return enmState;
155}
156
157
158/**
159 * Dumps a USBDEVICE structure to the log using LogLevel 3.
160 * @param pDev The structure to log.
161 * @todo This is really common code.
162 */
163static void usbLogDevice(PUSBDEVICE pDev)
164{
165 NOREF(pDev);
166 if (LogIs3Enabled())
167 {
168 Log3(("USB device:\n"));
169 Log3(("Product: %s (%x)\n", pDev->pszProduct, pDev->idProduct));
170 Log3(("Manufacturer: %s (Vendor ID %x)\n", pDev->pszManufacturer, pDev->idVendor));
171 Log3(("Serial number: %s (%llx)\n", pDev->pszSerialNumber, pDev->u64SerialHash));
172 Log3(("Device revision: %d\n", pDev->bcdDevice));
173 Log3(("Device class: %x\n", pDev->bDeviceClass));
174 Log3(("Device subclass: %x\n", pDev->bDeviceSubClass));
175 Log3(("Device protocol: %x\n", pDev->bDeviceProtocol));
176 Log3(("USB version number: %d\n", pDev->bcdUSB));
177 Log3(("Device speed: %s\n",
178 pDev->enmSpeed == USBDEVICESPEED_UNKNOWN ? "unknown"
179 : pDev->enmSpeed == USBDEVICESPEED_LOW ? "1.5 MBit/s"
180 : pDev->enmSpeed == USBDEVICESPEED_FULL ? "12 MBit/s"
181 : pDev->enmSpeed == USBDEVICESPEED_HIGH ? "480 MBit/s"
182 : pDev->enmSpeed == USBDEVICESPEED_SUPER ? "5.0 GBit/s"
183 : pDev->enmSpeed == USBDEVICESPEED_VARIABLE ? "variable"
184 : "invalid"));
185 Log3(("Number of configurations: %d\n", pDev->bNumConfigurations));
186 Log3(("Bus number: %d\n", pDev->bBus));
187 Log3(("Port number: %d\n", pDev->bPort));
188 Log3(("Device number: %d\n", pDev->bDevNum));
189 Log3(("Device state: %s\n",
190 pDev->enmState == USBDEVICESTATE_UNSUPPORTED ? "unsupported"
191 : pDev->enmState == USBDEVICESTATE_USED_BY_HOST ? "in use by host"
192 : pDev->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE ? "in use by host, possibly capturable"
193 : pDev->enmState == USBDEVICESTATE_UNUSED ? "not in use"
194 : pDev->enmState == USBDEVICESTATE_HELD_BY_PROXY ? "held by proxy"
195 : pDev->enmState == USBDEVICESTATE_USED_BY_GUEST ? "used by guest"
196 : "invalid"));
197 Log3(("OS device address: %s\n", pDev->pszAddress));
198 }
199}
200
201
202#ifdef VBOX_USB_WITH_USBFS
203
204/**
205 * "reads" the number suffix.
206 *
207 * It's more like validating it and skipping the necessary number of chars.
208 */
209static int usbfsReadSkipSuffix(char **ppszNext)
210{
211 char *pszNext = *ppszNext;
212 if (!RT_C_IS_SPACE(*pszNext) && *pszNext)
213 {
214 /* skip unit */
215 if (pszNext[0] == 'm' && pszNext[1] == 's')
216 pszNext += 2;
217 else if (pszNext[0] == 'm' && pszNext[1] == 'A')
218 pszNext += 2;
219
220 /* skip parenthesis */
221 if (*pszNext == '(')
222 {
223 pszNext = strchr(pszNext, ')');
224 if (!pszNext++)
225 {
226 AssertMsgFailed(("*ppszNext=%s\n", *ppszNext));
227 return VERR_PARSE_ERROR;
228 }
229 }
230
231 /* blank or end of the line. */
232 if (!RT_C_IS_SPACE(*pszNext) && *pszNext)
233 {
234 AssertMsgFailed(("pszNext=%s\n", pszNext));
235 return VERR_PARSE_ERROR;
236 }
237
238 /* it's ok. */
239 *ppszNext = pszNext;
240 }
241
242 return VINF_SUCCESS;
243}
244
245
246/**
247 * Reads a USB number returning the number and the position of the next character to parse.
248 */
249static int usbfsReadNum(const char *pszValue, unsigned uBase, uint32_t u32Mask, void *pvNum, char **ppszNext)
250{
251 /*
252 * Initialize return value to zero and strip leading spaces.
253 */
254 switch (u32Mask)
255 {
256 case 0xff: *(uint8_t *)pvNum = 0; break;
257 case 0xffff: *(uint16_t *)pvNum = 0; break;
258 case 0xffffffff: *(uint32_t *)pvNum = 0; break;
259 }
260 pszValue = RTStrStripL(pszValue);
261 if (*pszValue)
262 {
263 /*
264 * Try convert the number.
265 */
266 char *pszNext;
267 uint32_t u32 = 0;
268 RTStrToUInt32Ex(pszValue, &pszNext, uBase, &u32);
269 if (pszNext == pszValue)
270 {
271 AssertMsgFailed(("pszValue=%d\n", pszValue));
272 return VERR_NO_DATA;
273 }
274
275 /*
276 * Check the range.
277 */
278 if (u32 & ~u32Mask)
279 {
280 AssertMsgFailed(("pszValue=%d u32=%#x lMask=%#x\n", pszValue, u32, u32Mask));
281 return VERR_OUT_OF_RANGE;
282 }
283
284 int rc = usbfsReadSkipSuffix(&pszNext);
285 if (RT_FAILURE(rc))
286 return rc;
287
288 *ppszNext = pszNext;
289
290 /*
291 * Set the value.
292 */
293 switch (u32Mask)
294 {
295 case 0xff: *(uint8_t *)pvNum = (uint8_t)u32; break;
296 case 0xffff: *(uint16_t *)pvNum = (uint16_t)u32; break;
297 case 0xffffffff: *(uint32_t *)pvNum = (uint32_t)u32; break;
298 }
299 }
300 return VINF_SUCCESS;
301}
302
303
304static int usbfsRead8(const char *pszValue, unsigned uBase, uint8_t *pu8, char **ppszNext)
305{
306 return usbfsReadNum(pszValue, uBase, 0xff, pu8, ppszNext);
307}
308
309
310static int usbfsRead16(const char *pszValue, unsigned uBase, uint16_t *pu16, char **ppszNext)
311{
312 return usbfsReadNum(pszValue, uBase, 0xffff, pu16, ppszNext);
313}
314
315
316/**
317 * Reads a USB BCD number returning the number and the position of the next character to parse.
318 * The returned number contains the integer part in the high byte and the decimal part in the low byte.
319 */
320static int usbfsReadBCD(const char *pszValue, unsigned uBase, uint16_t *pu16, char **ppszNext)
321{
322 /*
323 * Initialize return value to zero and strip leading spaces.
324 */
325 *pu16 = 0;
326 pszValue = RTStrStripL(pszValue);
327 if (*pszValue)
328 {
329 /*
330 * Try convert the number.
331 */
332 /* integer part */
333 char *pszNext;
334 uint32_t u32Int = 0;
335 RTStrToUInt32Ex(pszValue, &pszNext, uBase, &u32Int);
336 if (pszNext == pszValue)
337 {
338 AssertMsgFailed(("pszValue=%s\n", pszValue));
339 return VERR_NO_DATA;
340 }
341 if (u32Int & ~0xff)
342 {
343 AssertMsgFailed(("pszValue=%s u32Int=%#x (int)\n", pszValue, u32Int));
344 return VERR_OUT_OF_RANGE;
345 }
346
347 /* skip dot and read decimal part */
348 if (*pszNext != '.')
349 {
350 AssertMsgFailed(("pszValue=%s pszNext=%s (int)\n", pszValue, pszNext));
351 return VERR_PARSE_ERROR;
352 }
353 char *pszValue2 = RTStrStripL(pszNext + 1);
354 uint32_t u32Dec = 0;
355 RTStrToUInt32Ex(pszValue2, &pszNext, uBase, &u32Dec);
356 if (pszNext == pszValue)
357 {
358 AssertMsgFailed(("pszValue=%s\n", pszValue));
359 return VERR_NO_DATA;
360 }
361 if (u32Dec & ~0xff)
362 {
363 AssertMsgFailed(("pszValue=%s u32Dec=%#x\n", pszValue, u32Dec));
364 return VERR_OUT_OF_RANGE;
365 }
366
367 /*
368 * Validate and skip stuff following the number.
369 */
370 int rc = usbfsReadSkipSuffix(&pszNext);
371 if (RT_FAILURE(rc))
372 return rc;
373 *ppszNext = pszNext;
374
375 /*
376 * Set the value.
377 */
378 *pu16 = (uint16_t)((u32Int << 8) | (uint16_t)u32Dec);
379 }
380 return VINF_SUCCESS;
381}
382
383
384/**
385 * Reads a string, i.e. allocates memory and copies it.
386 *
387 * We assume that a string is Utf8 and if that's not the case
388 * (pre-2.6.32-kernels used Latin-1, but so few devices return non-ASCII that
389 * this usually goes unnoticed) then we mercilessly force it to be so.
390 */
391static int usbfsReadStr(const char *pszValue, const char **ppsz)
392{
393 char *psz;
394
395 if (*ppsz)
396 RTStrFree((char *)*ppsz);
397 psz = RTStrDup(pszValue);
398 if (psz)
399 {
400 USBLibPurgeEncoding(psz);
401 *ppsz = psz;
402 return VINF_SUCCESS;
403 }
404 return VERR_NO_MEMORY;
405}
406
407
408/**
409 * Skips the current property.
410 */
411static char *usbfsReadSkip(char *pszValue)
412{
413 char *psz = strchr(pszValue, '=');
414 if (psz)
415 psz = strchr(psz + 1, '=');
416 if (!psz)
417 return strchr(pszValue, '\0');
418 while (psz > pszValue && !RT_C_IS_SPACE(psz[-1]))
419 psz--;
420 Assert(psz > pszValue);
421 return psz;
422}
423
424
425/**
426 * Determine the USB speed.
427 */
428static int usbfsReadSpeed(const char *pszValue, USBDEVICESPEED *pSpd, char **ppszNext)
429{
430 pszValue = RTStrStripL(pszValue);
431 /* verified with Linux 2.4.0 ... Linux 2.6.25 */
432 if (!strncmp(pszValue, RT_STR_TUPLE("1.5")))
433 *pSpd = USBDEVICESPEED_LOW;
434 else if (!strncmp(pszValue, RT_STR_TUPLE("12 ")))
435 *pSpd = USBDEVICESPEED_FULL;
436 else if (!strncmp(pszValue, RT_STR_TUPLE("480")))
437 *pSpd = USBDEVICESPEED_HIGH;
438 else if (!strncmp(pszValue, RT_STR_TUPLE("5000")))
439 *pSpd = USBDEVICESPEED_SUPER;
440 else
441 *pSpd = USBDEVICESPEED_UNKNOWN;
442 while (pszValue[0] != '\0' && !RT_C_IS_SPACE(pszValue[0]))
443 pszValue++;
444 *ppszNext = (char *)pszValue;
445 return VINF_SUCCESS;
446}
447
448
449/**
450 * Compare a prefix and returns pointer to the char following it if it matches.
451 */
452static char *usbfsPrefix(char *psz, const char *pszPref, size_t cchPref)
453{
454 if (strncmp(psz, pszPref, cchPref))
455 return NULL;
456 return psz + cchPref;
457}
458
459
460/** Just a worker for USBProxyServiceLinux::getDevices that avoids some code duplication. */
461static int usbfsAddDeviceToChain(PUSBDEVICE pDev, PUSBDEVICE *ppFirst, PUSBDEVICE **pppNext, const char *pszUsbfsRoot,
462 bool fUnsupportedDevicesToo, int rc)
463{
464 /* usbDeterminState requires the address. */
465 PUSBDEVICE pDevNew = (PUSBDEVICE)RTMemDup(pDev, sizeof(*pDev));
466 if (pDevNew)
467 {
468 RTStrAPrintf((char **)&pDevNew->pszAddress, "%s/%03d/%03d", pszUsbfsRoot, pDevNew->bBus, pDevNew->bDevNum);
469 if (pDevNew->pszAddress)
470 {
471 pDevNew->enmState = usbDeterminState(pDevNew);
472 if (pDevNew->enmState != USBDEVICESTATE_UNSUPPORTED || fUnsupportedDevicesToo)
473 {
474 if (*pppNext)
475 **pppNext = pDevNew;
476 else
477 *ppFirst = pDevNew;
478 *pppNext = &pDevNew->pNext;
479 }
480 else
481 deviceFree(pDevNew);
482 }
483 else
484 {
485 deviceFree(pDevNew);
486 rc = VERR_NO_MEMORY;
487 }
488 }
489 else
490 {
491 rc = VERR_NO_MEMORY;
492 deviceFreeMembers(pDev);
493 }
494
495 return rc;
496}
497
498
499static int usbfsOpenDevicesFile(const char *pszUsbfsRoot, FILE **ppFile)
500{
501 char *pszPath;
502 FILE *pFile;
503 RTStrAPrintf(&pszPath, "%s/devices", pszUsbfsRoot);
504 if (!pszPath)
505 return VERR_NO_MEMORY;
506 pFile = fopen(pszPath, "r");
507 RTStrFree(pszPath);
508 if (!pFile)
509 return RTErrConvertFromErrno(errno);
510 *ppFile = pFile;
511 return VINF_SUCCESS;
512}
513
514
515/**
516 * USBProxyService::getDevices() implementation for usbfs.
517 *
518 * The @a fUnsupportedDevicesToo flag tells the function to return information
519 * about unsupported devices as well. This is used as a sanity test to check
520 * that a devices file is really what we expect.
521 */
522static PUSBDEVICE usbfsGetDevices(const char *pszUsbfsRoot, bool fUnsupportedDevicesToo)
523{
524 PUSBDEVICE pFirst = NULL;
525 FILE *pFile = NULL;
526 int rc;
527 rc = usbfsOpenDevicesFile(pszUsbfsRoot, &pFile);
528 if (RT_SUCCESS(rc))
529 {
530 PUSBDEVICE *ppNext = NULL;
531 int cHits = 0;
532 char szLine[1024];
533 USBDEVICE Dev;
534 RT_ZERO(Dev);
535 Dev.enmState = USBDEVICESTATE_UNUSED;
536
537 /* Set close on exit and hope no one is racing us. */
538 rc = fcntl(fileno(pFile), F_SETFD, FD_CLOEXEC) >= 0
539 ? VINF_SUCCESS
540 : RTErrConvertFromErrno(errno);
541 while ( RT_SUCCESS(rc)
542 && fgets(szLine, sizeof(szLine), pFile))
543 {
544 char *psz;
545 char *pszValue;
546
547 /* validate and remove the trailing newline. */
548 psz = strchr(szLine, '\0');
549 if (psz[-1] != '\n' && !feof(pFile))
550 {
551 AssertMsgFailed(("Line too long. (cch=%d)\n", strlen(szLine)));
552 continue;
553 }
554
555 /* strip */
556 psz = RTStrStrip(szLine);
557 if (!*psz)
558 continue;
559
560 /*
561 * Interpret the line.
562 * (Ordered by normal occurrence.)
563 */
564 char ch = psz[0];
565 if (psz[1] != ':')
566 continue;
567 psz = RTStrStripL(psz + 3);
568#define PREFIX(str) ( (pszValue = usbfsPrefix(psz, str, sizeof(str) - 1)) != NULL )
569 switch (ch)
570 {
571 /*
572 * T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd
573 * | | | | | | | | |__MaxChildren
574 * | | | | | | | |__Device Speed in Mbps
575 * | | | | | | |__DeviceNumber
576 * | | | | | |__Count of devices at this level
577 * | | | | |__Connector/Port on Parent for this device
578 * | | | |__Parent DeviceNumber
579 * | | |__Level in topology for this bus
580 * | |__Bus number
581 * |__Topology info tag
582 */
583 case 'T':
584 /* add */
585 AssertMsg(cHits >= 3 || cHits == 0, ("cHits=%d\n", cHits));
586 if (cHits >= 3)
587 rc = usbfsAddDeviceToChain(&Dev, &pFirst, &ppNext, pszUsbfsRoot, fUnsupportedDevicesToo, rc);
588 else
589 deviceFreeMembers(&Dev);
590
591 /* Reset device state */
592 RT_ZERO(Dev);
593 Dev.enmState = USBDEVICESTATE_UNUSED;
594 cHits = 1;
595
596 /* parse the line. */
597 while (*psz && RT_SUCCESS(rc))
598 {
599 if (PREFIX("Bus="))
600 rc = usbfsRead8(pszValue, 10, &Dev.bBus, &psz);
601 else if (PREFIX("Port="))
602 rc = usbfsRead8(pszValue, 10, &Dev.bPort, &psz);
603 else if (PREFIX("Spd="))
604 rc = usbfsReadSpeed(pszValue, &Dev.enmSpeed, &psz);
605 else if (PREFIX("Dev#="))
606 rc = usbfsRead8(pszValue, 10, &Dev.bDevNum, &psz);
607 else
608 psz = usbfsReadSkip(psz);
609 psz = RTStrStripL(psz);
610 }
611 break;
612
613 /*
614 * Bandwidth info:
615 * B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd
616 * | | | |__Number of isochronous requests
617 * | | |__Number of interrupt requests
618 * | |__Total Bandwidth allocated to this bus
619 * |__Bandwidth info tag
620 */
621 case 'B':
622 break;
623
624 /*
625 * D: Ver=x.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd
626 * | | | | | | |__NumberConfigurations
627 * | | | | | |__MaxPacketSize of Default Endpoint
628 * | | | | |__DeviceProtocol
629 * | | | |__DeviceSubClass
630 * | | |__DeviceClass
631 * | |__Device USB version
632 * |__Device info tag #1
633 */
634 case 'D':
635 while (*psz && RT_SUCCESS(rc))
636 {
637 if (PREFIX("Ver="))
638 rc = usbfsReadBCD(pszValue, 16, &Dev.bcdUSB, &psz);
639 else if (PREFIX("Cls="))
640 {
641 rc = usbfsRead8(pszValue, 16, &Dev.bDeviceClass, &psz);
642 if (RT_SUCCESS(rc) && Dev.bDeviceClass == 9 /* HUB */)
643 Dev.enmState = USBDEVICESTATE_UNSUPPORTED;
644 }
645 else if (PREFIX("Sub="))
646 rc = usbfsRead8(pszValue, 16, &Dev.bDeviceSubClass, &psz);
647 else if (PREFIX("Prot="))
648 rc = usbfsRead8(pszValue, 16, &Dev.bDeviceProtocol, &psz);
649 //else if (PREFIX("MxPS="))
650 // rc = usbRead16(pszValue, 10, &Dev.wMaxPacketSize, &psz);
651 else if (PREFIX("#Cfgs="))
652 rc = usbfsRead8(pszValue, 10, &Dev.bNumConfigurations, &psz);
653 else
654 psz = usbfsReadSkip(psz);
655 psz = RTStrStripL(psz);
656 }
657 cHits++;
658 break;
659
660 /*
661 * P: Vendor=xxxx ProdID=xxxx Rev=xx.xx
662 * | | | |__Product revision number
663 * | | |__Product ID code
664 * | |__Vendor ID code
665 * |__Device info tag #2
666 */
667 case 'P':
668 while (*psz && RT_SUCCESS(rc))
669 {
670 if (PREFIX("Vendor="))
671 rc = usbfsRead16(pszValue, 16, &Dev.idVendor, &psz);
672 else if (PREFIX("ProdID="))
673 rc = usbfsRead16(pszValue, 16, &Dev.idProduct, &psz);
674 else if (PREFIX("Rev="))
675 rc = usbfsReadBCD(pszValue, 16, &Dev.bcdDevice, &psz);
676 else
677 psz = usbfsReadSkip(psz);
678 psz = RTStrStripL(psz);
679 }
680 cHits++;
681 break;
682
683 /*
684 * String.
685 */
686 case 'S':
687 if (PREFIX("Manufacturer="))
688 rc = usbfsReadStr(pszValue, &Dev.pszManufacturer);
689 else if (PREFIX("Product="))
690 rc = usbfsReadStr(pszValue, &Dev.pszProduct);
691 else if (PREFIX("SerialNumber="))
692 {
693 rc = usbfsReadStr(pszValue, &Dev.pszSerialNumber);
694 if (RT_SUCCESS(rc))
695 Dev.u64SerialHash = USBLibHashSerial(pszValue);
696 }
697 break;
698
699 /*
700 * C:* #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA
701 * | | | | | |__MaxPower in mA
702 * | | | | |__Attributes
703 * | | | |__ConfiguratioNumber
704 * | | |__NumberOfInterfaces
705 * | |__ "*" indicates the active configuration (others are " ")
706 * |__Config info tag
707 */
708 case 'C':
709 break;
710
711 /*
712 * I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss
713 * | | | | | | | |__Driver name
714 * | | | | | | | or "(none)"
715 * | | | | | | |__InterfaceProtocol
716 * | | | | | |__InterfaceSubClass
717 * | | | | |__InterfaceClass
718 * | | | |__NumberOfEndpoints
719 * | | |__AlternateSettingNumber
720 * | |__InterfaceNumber
721 * |__Interface info tag
722 */
723 case 'I':
724 {
725 /* Check for thing we don't support. */
726 while (*psz && RT_SUCCESS(rc))
727 {
728 if (PREFIX("Driver="))
729 {
730 const char *pszDriver = NULL;
731 rc = usbfsReadStr(pszValue, &pszDriver);
732 if ( !pszDriver
733 || !*pszDriver
734 || !strcmp(pszDriver, "(none)")
735 || !strcmp(pszDriver, "(no driver)"))
736 /* no driver */;
737 else if (!strcmp(pszDriver, "hub"))
738 Dev.enmState = USBDEVICESTATE_UNSUPPORTED;
739 else if (Dev.enmState == USBDEVICESTATE_UNUSED)
740 Dev.enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
741 RTStrFree((char *)pszDriver);
742 break; /* last attrib */
743 }
744 else if (PREFIX("Cls="))
745 {
746 uint8_t bInterfaceClass;
747 rc = usbfsRead8(pszValue, 16, &bInterfaceClass, &psz);
748 if (RT_SUCCESS(rc) && bInterfaceClass == 9 /* HUB */)
749 Dev.enmState = USBDEVICESTATE_UNSUPPORTED;
750 }
751 else
752 psz = usbfsReadSkip(psz);
753 psz = RTStrStripL(psz);
754 }
755 break;
756 }
757
758
759 /*
760 * E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms
761 * | | | | |__Interval (max) between transfers
762 * | | | |__EndpointMaxPacketSize
763 * | | |__Attributes(EndpointType)
764 * | |__EndpointAddress(I=In,O=Out)
765 * |__Endpoint info tag
766 */
767 case 'E':
768 break;
769
770 }
771#undef PREFIX
772 } /* parse loop */
773 fclose(pFile);
774
775 /*
776 * Add the current entry.
777 */
778 AssertMsg(cHits >= 3 || cHits == 0, ("cHits=%d\n", cHits));
779 if (cHits >= 3)
780 rc = usbfsAddDeviceToChain(&Dev, &pFirst, &ppNext, pszUsbfsRoot, fUnsupportedDevicesToo, rc);
781
782 /*
783 * Success?
784 */
785 if (RT_FAILURE(rc))
786 {
787 while (pFirst)
788 {
789 PUSBDEVICE pFree = pFirst;
790 pFirst = pFirst->pNext;
791 deviceFree(pFree);
792 }
793 }
794 }
795 if (RT_FAILURE(rc))
796 LogFlow(("USBProxyServiceLinux::getDevices: rc=%Rrc\n", rc));
797 return pFirst;
798}
799
800#endif /* VBOX_USB_WITH_USBFS */
801#ifdef VBOX_USB_WITH_SYSFS
802
803static void usbsysfsCleanupDevInfo(USBDeviceInfo *pSelf)
804{
805 RTStrFree(pSelf->mDevice);
806 RTStrFree(pSelf->mSysfsPath);
807 pSelf->mDevice = pSelf->mSysfsPath = NULL;
808 VEC_CLEANUP_PTR(&pSelf->mvecpszInterfaces);
809}
810
811
812static int usbsysfsInitDevInfo(USBDeviceInfo *pSelf, const char *aDevice, const char *aSystemID)
813{
814 pSelf->mDevice = aDevice ? RTStrDup(aDevice) : NULL;
815 pSelf->mSysfsPath = aSystemID ? RTStrDup(aSystemID) : NULL;
816 VEC_INIT_PTR(&pSelf->mvecpszInterfaces, char *, RTStrFree);
817 if ((aDevice && !pSelf->mDevice) || (aSystemID && ! pSelf->mSysfsPath))
818 {
819 usbsysfsCleanupDevInfo(pSelf);
820 return 0;
821 }
822 return 1;
823}
824
825# define USBDEVICE_MAJOR 189
826
827/**
828 * Calculate the bus (a.k.a root hub) number of a USB device from it's sysfs
829 * path.
830 *
831 * sysfs nodes representing root hubs have file names of the form
832 * usb<n>, where n is the bus number; other devices start with that number.
833 * See [http://www.linux-usb.org/FAQ.html#i6] and
834 * [http://www.kernel.org/doc/Documentation/usb/proc_usb_info.txt] for
835 * equivalent information about usbfs.
836 *
837 * @returns a bus number greater than 0 on success or 0 on failure.
838 */
839static unsigned usbsysfsGetBusFromPath(const char *pszPath)
840{
841 const char *pszFile = strrchr(pszPath, '/');
842 if (!pszFile)
843 return 0;
844 unsigned bus = RTStrToUInt32(pszFile + 1);
845 if ( !bus
846 && pszFile[1] == 'u' && pszFile[2] == 's' && pszFile[3] == 'b')
847 bus = RTStrToUInt32(pszFile + 4);
848 return bus;
849}
850
851
852/**
853 * Calculate the device number of a USB device.
854 *
855 * See drivers/usb/core/hub.c:usb_new_device as of Linux 2.6.20.
856 */
857static dev_t usbsysfsMakeDevNum(unsigned bus, unsigned device)
858{
859 AssertReturn(bus > 0, 0);
860 AssertReturn(((device - 1) & ~127) == 0, 0);
861 AssertReturn(device > 0, 0);
862 return makedev(USBDEVICE_MAJOR, ((bus - 1) << 7) + device - 1);
863}
864
865
866/**
867 * If a file @a pszNode from /sys/bus/usb/devices is a device rather than an
868 * interface add an element for the device to @a pvecDevInfo.
869 */
870static int usbsysfsAddIfDevice(const char *pszDevicesRoot, const char *pszNode, VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo)
871{
872 const char *pszFile = strrchr(pszNode, '/');
873 if (!pszFile)
874 return VERR_INVALID_PARAMETER;
875 if (strchr(pszFile, ':'))
876 return VINF_SUCCESS;
877
878 unsigned bus = usbsysfsGetBusFromPath(pszNode);
879 if (!bus)
880 return VINF_SUCCESS;
881
882 int64_t device;
883 int rc = RTLinuxSysFsReadIntFile(10, &device, "%s/devnum", pszNode);
884 if (RT_FAILURE(rc))
885 return VINF_SUCCESS;
886
887 dev_t devnum = usbsysfsMakeDevNum(bus, (int)device);
888 if (!devnum)
889 return VINF_SUCCESS;
890
891 char szDevPath[RTPATH_MAX];
892 rc = RTLinuxCheckDevicePath(devnum, RTFS_TYPE_DEV_CHAR,
893 szDevPath, sizeof(szDevPath),
894 "%s/%.3d/%.3d",
895 pszDevicesRoot, bus, device);
896 if (RT_FAILURE(rc))
897 return VINF_SUCCESS;
898
899 USBDeviceInfo info;
900 if (usbsysfsInitDevInfo(&info, szDevPath, pszNode))
901 {
902 rc = VEC_PUSH_BACK_OBJ(pvecDevInfo, USBDeviceInfo, &info);
903 if (RT_SUCCESS(rc))
904 return VINF_SUCCESS;
905 }
906 usbsysfsCleanupDevInfo(&info);
907 return VERR_NO_MEMORY;
908}
909
910
911/**
912 * The logic for testing whether a sysfs address corresponds to an interface of
913 * a device.
914 *
915 * Both must be referenced by their canonical sysfs paths. This is not tested,
916 * as the test requires file-system interaction.
917 */
918static bool usbsysfsMuiIsAnInterfaceOf(const char *pszIface, const char *pszDev)
919{
920 size_t cchDev = strlen(pszDev);
921
922 AssertPtr(pszIface);
923 AssertPtr(pszDev);
924 Assert(pszIface[0] == '/');
925 Assert(pszDev[0] == '/');
926 Assert(pszDev[cchDev - 1] != '/');
927
928 /* If this passes, pszIface is at least cchDev long */
929 if (strncmp(pszIface, pszDev, cchDev))
930 return false;
931
932 /* If this passes, pszIface is longer than cchDev */
933 if (pszIface[cchDev] != '/')
934 return false;
935
936 /* In sysfs an interface is an immediate subdirectory of the device */
937 if (strchr(pszIface + cchDev + 1, '/'))
938 return false;
939
940 /* And it always has a colon in its name */
941 if (!strchr(pszIface + cchDev + 1, ':'))
942 return false;
943
944 /* And hopefully we have now elimitated everything else */
945 return true;
946}
947
948
949# ifdef DEBUG
950# ifdef __cplusplus
951/** Unit test the logic in muiIsAnInterfaceOf in debug builds. */
952class testIsAnInterfaceOf
953{
954public:
955 testIsAnInterfaceOf()
956 {
957 Assert(usbsysfsMuiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0",
958 "/sys/devices/pci0000:00/0000:00:1a.0/usb3"));
959 Assert(!usbsysfsMuiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-1",
960 "/sys/devices/pci0000:00/0000:00:1a.0/usb3"));
961 Assert(!usbsysfsMuiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0/driver",
962 "/sys/devices/pci0000:00/0000:00:1a.0/usb3"));
963 }
964};
965static testIsAnInterfaceOf testIsAnInterfaceOfInst;
966# endif /* __cplusplus */
967# endif /* DEBUG */
968
969
970/**
971 * Tell whether a file in /sys/bus/usb/devices is an interface rather than a
972 * device.
973 */
974static int usbsysfsAddIfInterfaceOf(const char *pszNode, USBDeviceInfo *pInfo)
975{
976 if (!usbsysfsMuiIsAnInterfaceOf(pszNode, pInfo->mSysfsPath))
977 return VINF_SUCCESS;
978
979 char *pszDup = (char *)RTStrDup(pszNode);
980 if (pszDup)
981 {
982 int rc = VEC_PUSH_BACK_PTR(&pInfo->mvecpszInterfaces, char *, pszDup);
983 if (RT_SUCCESS(rc))
984 return VINF_SUCCESS;
985 RTStrFree(pszDup);
986 }
987 return VERR_NO_MEMORY;
988}
989
990
991/**
992 * Helper for usbsysfsReadFilePaths().
993 *
994 * Adds the entries from the open directory @a pDir to the vector @a pvecpchDevs
995 * using either the full path or the realpath() and skipping hidden files and
996 * files on which realpath() fails.
997 */
998static int usbsysfsReadFilePathsFromDir(const char *pszPath, DIR *pDir, VECTOR_PTR(char *) *pvecpchDevs)
999{
1000 struct dirent entry, *pResult;
1001 int err, rc;
1002
1003 for (err = readdir_r(pDir, &entry, &pResult); pResult;
1004 err = readdir_r(pDir, &entry, &pResult))
1005 {
1006 char szPath[RTPATH_MAX + 1];
1007 char szRealPath[RTPATH_MAX + 1];
1008 if (entry.d_name[0] == '.')
1009 continue;
1010 if (snprintf(szPath, sizeof(szPath), "%s/%s", pszPath, entry.d_name) < 0)
1011 return RTErrConvertFromErrno(errno); /** @todo r=bird: snprintf isn't document to set errno. Also, wouldn't it be better to continue on errors? Finally, you don't need to copy pszPath each time... */
1012 if (!realpath(szPath, szRealPath))
1013 return RTErrConvertFromErrno(errno);
1014 char *pszPathCopy = RTStrDup(szRealPath);
1015 if (!pszPathCopy)
1016 return VERR_NO_MEMORY;
1017 if (RT_FAILURE(rc = VEC_PUSH_BACK_PTR(pvecpchDevs, char *, pszPathCopy)))
1018 return rc;
1019 }
1020 return RTErrConvertFromErrno(err);
1021}
1022
1023
1024/**
1025 * Dump the names of a directory's entries into a vector of char pointers.
1026 *
1027 * @returns zero on success or (positive) posix error value.
1028 * @param pszPath the path to dump.
1029 * @param pvecpchDevs an empty vector of char pointers - must be cleaned up
1030 * by the caller even on failure.
1031 * @param withRealPath whether to canonicalise the filename with realpath
1032 */
1033static int usbsysfsReadFilePaths(const char *pszPath, VECTOR_PTR(char *) *pvecpchDevs)
1034{
1035 AssertPtrReturn(pvecpchDevs, EINVAL);
1036 AssertReturn(VEC_SIZE_PTR(pvecpchDevs) == 0, EINVAL);
1037 AssertPtrReturn(pszPath, EINVAL);
1038
1039 DIR *pDir = opendir(pszPath);
1040 if (!pDir)
1041 return RTErrConvertFromErrno(errno);
1042 int rc = usbsysfsReadFilePathsFromDir(pszPath, pDir, pvecpchDevs);
1043 if (closedir(pDir) < 0 && RT_SUCCESS(rc))
1044 rc = RTErrConvertFromErrno(errno);
1045 return rc;
1046}
1047
1048
1049/**
1050 * Logic for USBSysfsEnumerateHostDevices.
1051 *
1052 * @param pvecDevInfo vector of device information structures to add device
1053 * information to
1054 * @param pvecpchDevs empty scratch vector which will be freed by the caller,
1055 * to simplify exit logic
1056 */
1057static int usbsysfsEnumerateHostDevicesWorker(const char *pszDevicesRoot,
1058 VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo,
1059 VECTOR_PTR(char *) *pvecpchDevs)
1060{
1061
1062 AssertPtrReturn(pvecDevInfo, VERR_INVALID_POINTER);
1063 LogFlowFunc (("pvecDevInfo=%p\n", pvecDevInfo));
1064
1065 int rc = usbsysfsReadFilePaths("/sys/bus/usb/devices", pvecpchDevs);
1066 if (RT_FAILURE(rc))
1067 return rc;
1068
1069 char **ppszEntry;
1070 VEC_FOR_EACH(pvecpchDevs, char *, ppszEntry)
1071 {
1072 rc = usbsysfsAddIfDevice(pszDevicesRoot, *ppszEntry, pvecDevInfo);
1073 if (RT_FAILURE(rc))
1074 return rc;
1075 }
1076
1077 USBDeviceInfo *pInfo;
1078 VEC_FOR_EACH(pvecDevInfo, USBDeviceInfo, pInfo)
1079 VEC_FOR_EACH(pvecpchDevs, char *, ppszEntry)
1080 {
1081 rc = usbsysfsAddIfInterfaceOf(*ppszEntry, pInfo);
1082 if (RT_FAILURE(rc))
1083 return rc;
1084 }
1085 return VINF_SUCCESS;
1086}
1087
1088
1089static int usbsysfsEnumerateHostDevices(const char *pszDevicesRoot, VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo)
1090{
1091 VECTOR_PTR(char *) vecpchDevs;
1092
1093 AssertReturn(VEC_SIZE_OBJ(pvecDevInfo) == 0, VERR_INVALID_PARAMETER);
1094 LogFlowFunc(("entered\n"));
1095 VEC_INIT_PTR(&vecpchDevs, char *, RTStrFree);
1096 int rc = usbsysfsEnumerateHostDevicesWorker(pszDevicesRoot, pvecDevInfo, &vecpchDevs);
1097 VEC_CLEANUP_PTR(&vecpchDevs);
1098 LogFlowFunc(("rc=%Rrc\n", rc));
1099 return rc;
1100}
1101
1102
1103/**
1104 * Helper function for extracting the port number on the parent device from
1105 * the sysfs path value.
1106 *
1107 * The sysfs path is a chain of elements separated by forward slashes, and for
1108 * USB devices, the last element in the chain takes the form
1109 * <port>-<port>.[...].<port>[:<config>.<interface>]
1110 * where the first <port> is the port number on the root hub, and the following
1111 * (optional) ones are the port numbers on any other hubs between the device
1112 * and the root hub. The last part (:<config.interface>) is only present for
1113 * interfaces, not for devices. This API should only be called for devices.
1114 * For compatibility with usbfs, which enumerates from zero up, we subtract one
1115 * from the port number.
1116 *
1117 * For root hubs, the last element in the chain takes the form
1118 * usb<hub number>
1119 * and usbfs always returns port number zero.
1120 *
1121 * @returns VBox status code. pu8Port is set on success.
1122 * @param pszPath The sysfs path to parse.
1123 * @param pu8Port Where to store the port number.
1124 */
1125static int usbsysfsGetPortFromStr(const char *pszPath, uint8_t *pu8Port)
1126{
1127 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1128 AssertPtrReturn(pu8Port, VERR_INVALID_POINTER);
1129
1130 /*
1131 * This should not be possible until we get PCs with USB as their primary bus.
1132 * Note: We don't assert this, as we don't expect the caller to validate the
1133 * sysfs path.
1134 */
1135 const char *pszLastComp = strrchr(pszPath, '/');
1136 if (!pszLastComp)
1137 {
1138 Log(("usbGetPortFromSysfsPath(%s): failed [1]\n", pszPath));
1139 return VERR_INVALID_PARAMETER;
1140 }
1141 pszLastComp++; /* skip the slash */
1142
1143 /*
1144 * This API should not be called for interfaces, so the last component
1145 * of the path should not contain a colon. We *do* assert this, as it
1146 * might indicate a caller bug.
1147 */
1148 AssertMsgReturn(strchr(pszLastComp, ':') == NULL, ("%s\n", pszPath), VERR_INVALID_PARAMETER);
1149
1150 /*
1151 * Look for the start of the last number.
1152 */
1153 const char *pchDash = strrchr(pszLastComp, '-');
1154 const char *pchDot = strrchr(pszLastComp, '.');
1155 if (!pchDash && !pchDot)
1156 {
1157 /* No -/. so it must be a root hub. Check that it's usb<something>. */
1158 if (strncmp(pszLastComp, RT_STR_TUPLE("usb")) != 0)
1159 {
1160 Log(("usbGetPortFromSysfsPath(%s): failed [2]\n", pszPath));
1161 return VERR_INVALID_PARAMETER;
1162 }
1163 return VERR_NOT_SUPPORTED;
1164 }
1165
1166 const char *pszLastPort = pchDot != NULL
1167 ? pchDot + 1
1168 : pchDash + 1;
1169 int rc = RTStrToUInt8Full(pszLastPort, 10, pu8Port);
1170 if (rc != VINF_SUCCESS)
1171 {
1172 Log(("usbGetPortFromSysfsPath(%s): failed [3], rc=%Rrc\n", pszPath, rc));
1173 return VERR_INVALID_PARAMETER;
1174 }
1175 if (*pu8Port == 0)
1176 {
1177 Log(("usbGetPortFromSysfsPath(%s): failed [4]\n", pszPath));
1178 return VERR_INVALID_PARAMETER;
1179 }
1180
1181 /* usbfs compatibility, 0-based port number. */
1182 *pu8Port = (uint8_t)(*pu8Port - 1);
1183 return VINF_SUCCESS;
1184}
1185
1186
1187/**
1188 * Converts a sysfs BCD value into a uint16_t.
1189 *
1190 * In contrast to usbReadBCD() this function can handle BCD values without
1191 * a decimal separator. This is necessary for parsing bcdDevice.
1192 *
1193 * @param pszBuf Pointer to the string buffer.
1194 * @param pu15 Pointer to the return value.
1195 * @returns IPRT status code.
1196 */
1197static int usbsysfsConvertStrToBCD(const char *pszBuf, uint16_t *pu16)
1198{
1199 char *pszNext;
1200 int32_t i32;
1201
1202 pszBuf = RTStrStripL(pszBuf);
1203 int rc = RTStrToInt32Ex(pszBuf, &pszNext, 16, &i32);
1204 if ( RT_FAILURE(rc)
1205 || rc == VWRN_NUMBER_TOO_BIG
1206 || i32 < 0)
1207 return VERR_NUMBER_TOO_BIG;
1208 if (*pszNext == '.')
1209 {
1210 if (i32 > 255)
1211 return VERR_NUMBER_TOO_BIG;
1212 int32_t i32Lo;
1213 rc = RTStrToInt32Ex(pszNext+1, &pszNext, 16, &i32Lo);
1214 if ( RT_FAILURE(rc)
1215 || rc == VWRN_NUMBER_TOO_BIG
1216 || i32Lo > 255
1217 || i32Lo < 0)
1218 return VERR_NUMBER_TOO_BIG;
1219 i32 = (i32 << 8) | i32Lo;
1220 }
1221 if ( i32 > 65535
1222 || (*pszNext != '\0' && *pszNext != ' '))
1223 return VERR_NUMBER_TOO_BIG;
1224
1225 *pu16 = (uint16_t)i32;
1226 return VINF_SUCCESS;
1227}
1228
1229
1230/**
1231 * Returns the byte value for the given device property or sets the given default if an
1232 * error occurs while obtaining it.
1233 *
1234 * @returns uint8_t value of the given property.
1235 * @param uBase The base of the number in the sysfs property.
1236 * @param bDef The default to set on error.
1237 * @param pszFormat The format string for the property.
1238 * @param ... Arguments for the format string.
1239 */
1240static uint8_t usbsysfsReadDevicePropertyU8Def(unsigned uBase, uint8_t bDef, const char *pszFormat, ...)
1241{
1242 int64_t i64Tmp = 0;
1243
1244 va_list va;
1245 va_start(va, pszFormat);
1246 int rc = RTLinuxSysFsReadIntFileV(uBase, &i64Tmp, pszFormat, va);
1247 va_end(va);
1248 if (RT_SUCCESS(rc))
1249 return (uint8_t)i64Tmp;
1250 else
1251 return bDef;
1252}
1253
1254
1255/**
1256 * Returns the uint16_t value for the given device property or sets the given default if an
1257 * error occurs while obtaining it.
1258 *
1259 * @returns uint16_t value of the given property.
1260 * @param uBase The base of the number in the sysfs property.
1261 * @param u16Def The default to set on error.
1262 * @param pszFormat The format string for the property.
1263 * @param ... Arguments for the format string.
1264 */
1265static uint16_t usbsysfsReadDevicePropertyU16Def(unsigned uBase, uint16_t u16Def, const char *pszFormat, ...)
1266{
1267 int64_t i64Tmp = 0;
1268
1269 va_list va;
1270 va_start(va, pszFormat);
1271 int rc = RTLinuxSysFsReadIntFileV(uBase, &i64Tmp, pszFormat, va);
1272 va_end(va);
1273 if (RT_SUCCESS(rc))
1274 return (uint16_t)i64Tmp;
1275 else
1276 return u16Def;
1277}
1278
1279
1280static void usbsysfsFillInDevice(USBDEVICE *pDev, USBDeviceInfo *pInfo)
1281{
1282 int rc;
1283 const char *pszSysfsPath = pInfo->mSysfsPath;
1284
1285 /* Fill in the simple fields */
1286 pDev->enmState = USBDEVICESTATE_UNUSED;
1287 pDev->bBus = (uint8_t)usbsysfsGetBusFromPath(pszSysfsPath);
1288 pDev->bDeviceClass = usbsysfsReadDevicePropertyU8Def(16, 0, "%s/bDeviceClass", pszSysfsPath);
1289 pDev->bDeviceSubClass = usbsysfsReadDevicePropertyU8Def(16, 0, "%s/bDeviceSubClass", pszSysfsPath);
1290 pDev->bDeviceProtocol = usbsysfsReadDevicePropertyU8Def(16, 0, "%s/bDeviceProtocol", pszSysfsPath);
1291 pDev->bNumConfigurations = usbsysfsReadDevicePropertyU8Def(10, 0, "%s/bNumConfigurations", pszSysfsPath);
1292 pDev->idVendor = usbsysfsReadDevicePropertyU16Def(16, 0, "%s/idVendor", pszSysfsPath);
1293 pDev->idProduct = usbsysfsReadDevicePropertyU16Def(16, 0, "%s/idProduct", pszSysfsPath);
1294 pDev->bDevNum = usbsysfsReadDevicePropertyU8Def(10, 0, "%s/devnum", pszSysfsPath);
1295
1296 /* Now deal with the non-numeric bits. */
1297 char szBuf[1024]; /* Should be larger than anything a sane device
1298 * will need, and insane devices can be unsupported
1299 * until further notice. */
1300 size_t cchRead;
1301
1302 /* For simplicity, we just do strcmps on the next one. */
1303 rc = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), &cchRead, "%s/speed", pszSysfsPath);
1304 if (RT_FAILURE(rc) || cchRead == sizeof(szBuf))
1305 pDev->enmState = USBDEVICESTATE_UNSUPPORTED;
1306 else
1307 pDev->enmSpeed = !strcmp(szBuf, "1.5") ? USBDEVICESPEED_LOW
1308 : !strcmp(szBuf, "12") ? USBDEVICESPEED_FULL
1309 : !strcmp(szBuf, "480") ? USBDEVICESPEED_HIGH
1310 : !strcmp(szBuf, "5000") ? USBDEVICESPEED_SUPER
1311 : USBDEVICESPEED_UNKNOWN;
1312
1313 rc = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), &cchRead, "%s/version", pszSysfsPath);
1314 if (RT_FAILURE(rc) || cchRead == sizeof(szBuf))
1315 pDev->enmState = USBDEVICESTATE_UNSUPPORTED;
1316 else
1317 {
1318 rc = usbsysfsConvertStrToBCD(szBuf, &pDev->bcdUSB);
1319 if (RT_FAILURE(rc))
1320 {
1321 pDev->enmState = USBDEVICESTATE_UNSUPPORTED;
1322 pDev->bcdUSB = UINT16_MAX;
1323 }
1324 }
1325
1326 rc = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), &cchRead, "%s/bcdDevice", pszSysfsPath);
1327 if (RT_FAILURE(rc) || cchRead == sizeof(szBuf))
1328 pDev->bcdDevice = UINT16_MAX;
1329 else
1330 {
1331 rc = usbsysfsConvertStrToBCD(szBuf, &pDev->bcdDevice);
1332 if (RT_FAILURE(rc))
1333 pDev->bcdDevice = UINT16_MAX;
1334 }
1335
1336 /* Now do things that need string duplication */
1337 rc = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), &cchRead, "%s/product", pszSysfsPath);
1338 if (RT_SUCCESS(rc) && cchRead < sizeof(szBuf))
1339 {
1340 USBLibPurgeEncoding(szBuf);
1341 pDev->pszProduct = RTStrDup(szBuf);
1342 }
1343
1344 rc = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), &cchRead, "%s/serial", pszSysfsPath);
1345 if (RT_SUCCESS(rc) && cchRead < sizeof(szBuf))
1346 {
1347 USBLibPurgeEncoding(szBuf);
1348 pDev->pszSerialNumber = RTStrDup(szBuf);
1349 pDev->u64SerialHash = USBLibHashSerial(szBuf);
1350 }
1351
1352 rc = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), &cchRead, "%s/manufacturer", pszSysfsPath);
1353 if (RT_SUCCESS(rc) && cchRead < sizeof(szBuf))
1354 {
1355 USBLibPurgeEncoding(szBuf);
1356 pDev->pszManufacturer = RTStrDup(szBuf);
1357 }
1358
1359 /* Work out the port number */
1360 if (RT_FAILURE(usbsysfsGetPortFromStr(pszSysfsPath, &pDev->bPort)))
1361 pDev->enmState = USBDEVICESTATE_UNSUPPORTED;
1362
1363 /* Check the interfaces to see if we can support the device. */
1364 char **ppszIf;
1365 VEC_FOR_EACH(&pInfo->mvecpszInterfaces, char *, ppszIf)
1366 {
1367 rc = RTLinuxSysFsGetLinkDest(szBuf, sizeof(szBuf), NULL, "%s/driver", *ppszIf);
1368 if (RT_SUCCESS(rc) && pDev->enmState != USBDEVICESTATE_UNSUPPORTED)
1369 pDev->enmState = (strcmp(szBuf, "hub") == 0)
1370 ? USBDEVICESTATE_UNSUPPORTED
1371 : USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
1372 if (usbsysfsReadDevicePropertyU8Def(16, 9 /* bDev */, "%s/bInterfaceClass", *ppszIf) == 9 /* hub */)
1373 pDev->enmState = USBDEVICESTATE_UNSUPPORTED;
1374 }
1375
1376 /* We use a double slash as a separator in the pszAddress field. This is
1377 * alright as the two paths can't contain a slash due to the way we build
1378 * them. */
1379 char *pszAddress = NULL;
1380 RTStrAPrintf(&pszAddress, "sysfs:%s//device:%s", pszSysfsPath, pInfo->mDevice);
1381 pDev->pszAddress = pszAddress;
1382 pDev->pszBackend = RTStrDup("host");
1383
1384 /* Work out from the data collected whether we can support this device. */
1385 pDev->enmState = usbDeterminState(pDev);
1386 usbLogDevice(pDev);
1387}
1388
1389
1390/**
1391 * USBProxyService::getDevices() implementation for sysfs.
1392 */
1393static PUSBDEVICE usbsysfsGetDevices(const char *pszDevicesRoot, bool fUnsupportedDevicesToo)
1394{
1395 /* Add each of the devices found to the chain. */
1396 PUSBDEVICE pFirst = NULL;
1397 PUSBDEVICE pLast = NULL;
1398 VECTOR_OBJ(USBDeviceInfo) vecDevInfo;
1399 USBDeviceInfo *pInfo;
1400 int rc;
1401
1402 VEC_INIT_OBJ(&vecDevInfo, USBDeviceInfo, usbsysfsCleanupDevInfo);
1403 rc = usbsysfsEnumerateHostDevices(pszDevicesRoot, &vecDevInfo);
1404 if (RT_FAILURE(rc))
1405 return NULL;
1406 VEC_FOR_EACH(&vecDevInfo, USBDeviceInfo, pInfo)
1407 {
1408 USBDEVICE *pDev = (USBDEVICE *)RTMemAllocZ(sizeof(USBDEVICE));
1409 if (!pDev)
1410 rc = VERR_NO_MEMORY;
1411 if (RT_SUCCESS(rc))
1412 usbsysfsFillInDevice(pDev, pInfo);
1413 if ( RT_SUCCESS(rc)
1414 && ( pDev->enmState != USBDEVICESTATE_UNSUPPORTED
1415 || fUnsupportedDevicesToo)
1416 && pDev->pszAddress != NULL
1417 )
1418 {
1419 if (pLast != NULL)
1420 {
1421 pLast->pNext = pDev;
1422 pLast = pLast->pNext;
1423 }
1424 else
1425 pFirst = pLast = pDev;
1426 }
1427 else
1428 deviceFree(pDev);
1429 if (RT_FAILURE(rc))
1430 break;
1431 }
1432 if (RT_FAILURE(rc))
1433 deviceListFree(&pFirst);
1434
1435 VEC_CLEANUP_OBJ(&vecDevInfo);
1436 return pFirst;
1437}
1438
1439#endif /* VBOX_USB_WITH_SYSFS */
1440#ifdef UNIT_TEST
1441
1442/* Set up mock functions for USBProxyLinuxCheckDeviceRoot - here dlsym and close
1443 * for the inotify presence check. */
1444static int testInotifyInitGood(void) { return 0; }
1445static int testInotifyInitBad(void) { return -1; }
1446static bool s_fHaveInotifyLibC = true;
1447static bool s_fHaveInotifyKernel = true;
1448
1449static void *testDLSym(void *handle, const char *symbol)
1450{
1451 RT_NOREF(handle, symbol);
1452 Assert(handle == RTLD_DEFAULT);
1453 Assert(!RTStrCmp(symbol, "inotify_init"));
1454 if (!s_fHaveInotifyLibC)
1455 return NULL;
1456 if (s_fHaveInotifyKernel)
1457 return (void *)(uintptr_t)testInotifyInitGood;
1458 return (void *)(uintptr_t)testInotifyInitBad;
1459}
1460
1461void TestUSBSetInotifyAvailable(bool fHaveInotifyLibC, bool fHaveInotifyKernel)
1462{
1463 s_fHaveInotifyLibC = fHaveInotifyLibC;
1464 s_fHaveInotifyKernel = fHaveInotifyKernel;
1465}
1466# define dlsym testDLSym
1467# define close(a) do {} while (0)
1468
1469#endif /* UNIT_TEST */
1470
1471/**
1472 * Is inotify available and working on this system?
1473 *
1474 * This is a requirement for using USB with sysfs
1475 */
1476static bool usbsysfsInotifyAvailable(void)
1477{
1478 int (*inotify_init)(void);
1479
1480 *(void **)(&inotify_init) = dlsym(RTLD_DEFAULT, "inotify_init");
1481 if (!inotify_init)
1482 return false;
1483 int fd = inotify_init();
1484 if (fd == -1)
1485 return false;
1486 close(fd);
1487 return true;
1488}
1489
1490#ifdef UNIT_TEST
1491
1492# undef dlsym
1493# undef close
1494
1495/** Unit test list of usbfs addresses of connected devices. */
1496static const char **g_papszUsbfsDeviceAddresses = NULL;
1497
1498static PUSBDEVICE testGetUsbfsDevices(const char *pszUsbfsRoot, bool fUnsupportedDevicesToo)
1499{
1500 RT_NOREF(pszUsbfsRoot, fUnsupportedDevicesToo);
1501 const char **psz;
1502 PUSBDEVICE pList = NULL, pTail = NULL;
1503 for (psz = g_papszUsbfsDeviceAddresses; psz && *psz; ++psz)
1504 {
1505 PUSBDEVICE pNext = (PUSBDEVICE)RTMemAllocZ(sizeof(USBDEVICE));
1506 if (pNext)
1507 pNext->pszAddress = RTStrDup(*psz);
1508 if (!pNext || !pNext->pszAddress)
1509 {
1510 if (pNext)
1511 RTMemFree(pNext);
1512 deviceListFree(&pList);
1513 return NULL;
1514 }
1515 if (pTail)
1516 pTail->pNext = pNext;
1517 else
1518 pList = pNext;
1519 pTail = pNext;
1520 }
1521 return pList;
1522}
1523# define usbfsGetDevices testGetUsbfsDevices
1524
1525/**
1526 * Specify the list of devices that will appear to be available through
1527 * usbfs during unit testing (of USBProxyLinuxGetDevices)
1528 * @param pacszDeviceAddresses NULL terminated array of usbfs device addresses
1529 */
1530void TestUSBSetAvailableUsbfsDevices(const char **papszDeviceAddresses)
1531{
1532 g_papszUsbfsDeviceAddresses = papszDeviceAddresses;
1533}
1534
1535/** Unit test list of files reported as accessible by access(3). We only do
1536 * accessible or not accessible. */
1537static const char **g_papszAccessibleFiles = NULL;
1538
1539static int testAccess(const char *pszPath, int mode)
1540{
1541 RT_NOREF(mode);
1542 const char **psz;
1543 for (psz = g_papszAccessibleFiles; psz && *psz; ++psz)
1544 if (!RTStrCmp(pszPath, *psz))
1545 return 0;
1546 return -1;
1547}
1548# define access testAccess
1549
1550
1551/**
1552 * Specify the list of files that access will report as accessible (at present
1553 * we only do accessible or not accessible) during unit testing (of
1554 * USBProxyLinuxGetDevices)
1555 * @param papszAccessibleFiles NULL terminated array of file paths to be
1556 * reported accessible
1557 */
1558void TestUSBSetAccessibleFiles(const char **papszAccessibleFiles)
1559{
1560 g_papszAccessibleFiles = papszAccessibleFiles;
1561}
1562
1563
1564/** The path we pretend the usbfs root is located at, or NULL. */
1565const char *s_pszTestUsbfsRoot;
1566/** Should usbfs be accessible to the current user? */
1567bool s_fTestUsbfsAccessible;
1568/** The path we pretend the device node tree root is located at, or NULL. */
1569const char *s_pszTestDevicesRoot;
1570/** Should the device node tree be accessible to the current user? */
1571bool s_fTestDevicesAccessible;
1572/** The result of the usbfs/inotify-specific init */
1573int s_rcTestMethodInitResult;
1574/** The value of the VBOX_USB environment variable. */
1575const char *s_pszTestEnvUsb;
1576/** The value of the VBOX_USB_ROOT environment variable. */
1577const char *s_pszTestEnvUsbRoot;
1578
1579
1580/** Select which access methods will be available to the @a init method
1581 * during unit testing, and (hack!) what return code it will see from
1582 * the access method-specific initialisation. */
1583void TestUSBSetupInit(const char *pszUsbfsRoot, bool fUsbfsAccessible,
1584 const char *pszDevicesRoot, bool fDevicesAccessible,
1585 int rcMethodInitResult)
1586{
1587 s_pszTestUsbfsRoot = pszUsbfsRoot;
1588 s_fTestUsbfsAccessible = fUsbfsAccessible;
1589 s_pszTestDevicesRoot = pszDevicesRoot;
1590 s_fTestDevicesAccessible = fDevicesAccessible;
1591 s_rcTestMethodInitResult = rcMethodInitResult;
1592}
1593
1594
1595/** Specify the environment that the @a init method will see during unit
1596 * testing. */
1597void TestUSBSetEnv(const char *pszEnvUsb, const char *pszEnvUsbRoot)
1598{
1599 s_pszTestEnvUsb = pszEnvUsb;
1600 s_pszTestEnvUsbRoot = pszEnvUsbRoot;
1601}
1602
1603/* For testing we redefine anything that accesses the outside world to
1604 * return test values. */
1605# define RTEnvGet(a) \
1606 ( !RTStrCmp(a, "VBOX_USB") ? s_pszTestEnvUsb \
1607 : !RTStrCmp(a, "VBOX_USB_ROOT") ? s_pszTestEnvUsbRoot \
1608 : NULL)
1609# define USBProxyLinuxCheckDeviceRoot(pszPath, fUseNodes) \
1610 ( ((fUseNodes) && s_fTestDevicesAccessible \
1611 && !RTStrCmp(pszPath, s_pszTestDevicesRoot)) \
1612 || (!(fUseNodes) && s_fTestUsbfsAccessible \
1613 && !RTStrCmp(pszPath, s_pszTestUsbfsRoot)))
1614# define RTDirExists(pszDir) \
1615 ( (pszDir) \
1616 && ( !RTStrCmp(pszDir, s_pszTestDevicesRoot) \
1617 || !RTStrCmp(pszDir, s_pszTestUsbfsRoot)))
1618# define RTFileExists(pszFile) \
1619 ( (pszFile) \
1620 && s_pszTestUsbfsRoot \
1621 && !RTStrNCmp(pszFile, s_pszTestUsbfsRoot, strlen(s_pszTestUsbfsRoot)) \
1622 && !RTStrCmp(pszFile + strlen(s_pszTestUsbfsRoot), "/devices"))
1623
1624#endif /* UNIT_TEST */
1625
1626/**
1627 * Use USBFS-like or sysfs/device node-like access method?
1628 *
1629 * Selects the access method that will be used to access USB devices based on
1630 * what is available on the host and what if anything the user has specified
1631 * in the environment.
1632 *
1633 * @returns iprt status value
1634 * @param pfUsingUsbfsDevices on success this will be set to true if
1635 * the prefered access method is USBFS-like and to
1636 * false if it is sysfs/device node-like
1637 * @param ppszDevicesRoot on success the root of the tree of USBFS-like
1638 * device nodes will be stored here
1639 */
1640int USBProxyLinuxChooseMethod(bool *pfUsingUsbfsDevices, const char **ppszDevicesRoot)
1641{
1642 /*
1643 * We have two methods available for getting host USB device data - using
1644 * USBFS and using sysfs. The default choice is sysfs; if that is not
1645 * available we fall back to USBFS.
1646 * In the event of both failing, an appropriate error will be returned.
1647 * The user may also specify a method and root using the VBOX_USB and
1648 * VBOX_USB_ROOT environment variables. In this case we don't check
1649 * the root they provide for validity.
1650 */
1651 bool fUsbfsChosen = false;
1652 bool fSysfsChosen = false;
1653 const char *pszUsbFromEnv = RTEnvGet("VBOX_USB");
1654 const char *pszUsbRoot = NULL;
1655 if (pszUsbFromEnv)
1656 {
1657 bool fValidVBoxUSB = true;
1658
1659 pszUsbRoot = RTEnvGet("VBOX_USB_ROOT");
1660 if (!RTStrICmp(pszUsbFromEnv, "USBFS"))
1661 {
1662 LogRel(("Default USB access method set to \"usbfs\" from environment\n"));
1663 fUsbfsChosen = true;
1664 }
1665 else if (!RTStrICmp(pszUsbFromEnv, "SYSFS"))
1666 {
1667 LogRel(("Default USB method set to \"sysfs\" from environment\n"));
1668 fSysfsChosen = true;
1669 }
1670 else
1671 {
1672 LogRel(("Invalid VBOX_USB environment variable setting \"%s\"\n", pszUsbFromEnv));
1673 fValidVBoxUSB = false;
1674 pszUsbFromEnv = NULL;
1675 }
1676 if (!fValidVBoxUSB && pszUsbRoot)
1677 pszUsbRoot = NULL;
1678 }
1679 if (!pszUsbRoot)
1680 {
1681 if ( !fUsbfsChosen
1682 && USBProxyLinuxCheckDeviceRoot("/dev/vboxusb", true))
1683 {
1684 fSysfsChosen = true;
1685 pszUsbRoot = "/dev/vboxusb";
1686 }
1687 else if ( !fSysfsChosen
1688 && USBProxyLinuxCheckDeviceRoot("/proc/bus/usb", false))
1689 {
1690 fUsbfsChosen = true;
1691 pszUsbRoot = "/proc/bus/usb";
1692 }
1693 }
1694 else if (!USBProxyLinuxCheckDeviceRoot(pszUsbRoot, fSysfsChosen))
1695 pszUsbRoot = NULL;
1696 if (pszUsbRoot)
1697 {
1698 *pfUsingUsbfsDevices = fUsbfsChosen;
1699 *ppszDevicesRoot = pszUsbRoot;
1700 return VINF_SUCCESS;
1701 }
1702 /* else */
1703 return pszUsbFromEnv ? VERR_NOT_FOUND
1704 : RTDirExists("/dev/vboxusb") ? VERR_VUSB_USB_DEVICE_PERMISSION
1705 : RTFileExists("/proc/bus/usb/devices") ? VERR_VUSB_USBFS_PERMISSION
1706 : VERR_NOT_FOUND;
1707}
1708
1709#ifdef UNIT_TEST
1710# undef RTEnvGet
1711# undef USBProxyLinuxCheckDeviceRoot
1712# undef RTDirExists
1713# undef RTFileExists
1714#endif
1715
1716/**
1717 * Check whether a USB device tree root is usable.
1718 *
1719 * @param pszRoot the path to the root of the device tree
1720 * @param fIsDeviceNodes whether this is a device node (or usbfs) tree
1721 * @note returns a pointer into a static array so it will stay valid
1722 */
1723bool USBProxyLinuxCheckDeviceRoot(const char *pszRoot, bool fIsDeviceNodes)
1724{
1725 bool fOK = false;
1726 if (!fIsDeviceNodes) /* usbfs */
1727 {
1728#ifdef VBOX_USB_WITH_USBFS
1729 if (!access(pszRoot, R_OK | X_OK))
1730 {
1731 fOK = true;
1732 PUSBDEVICE pDevices = usbfsGetDevices(pszRoot, true);
1733 if (pDevices)
1734 {
1735 PUSBDEVICE pDevice;
1736 for (pDevice = pDevices; pDevice && fOK; pDevice = pDevice->pNext)
1737 if (access(pDevice->pszAddress, R_OK | W_OK))
1738 fOK = false;
1739 deviceListFree(&pDevices);
1740 }
1741 }
1742#endif
1743 }
1744#ifdef VBOX_USB_WITH_SYSFS
1745 /* device nodes */
1746 else if (usbsysfsInotifyAvailable() && !access(pszRoot, R_OK | X_OK))
1747 fOK = true;
1748#endif
1749 return fOK;
1750}
1751
1752#ifdef UNIT_TEST
1753# undef usbfsGetDevices
1754# undef access
1755#endif
1756
1757/**
1758 * Get the list of USB devices supported by the system.
1759 *
1760 * Result should be freed using #deviceFree or something equivalent.
1761 *
1762 * @param pszDevicesRoot the path to the root of the device tree
1763 * @param fUseSysfs whether to use sysfs (or usbfs) for enumeration
1764 */
1765PUSBDEVICE USBProxyLinuxGetDevices(const char *pszDevicesRoot, bool fUseSysfs)
1766{
1767 if (!fUseSysfs)
1768 {
1769#ifdef VBOX_USB_WITH_USBFS
1770 return usbfsGetDevices(pszDevicesRoot, false);
1771#else
1772 return NULL;
1773#endif
1774 }
1775
1776#ifdef VBOX_USB_WITH_SYSFS
1777 return usbsysfsGetDevices(pszDevicesRoot, false);
1778#else
1779 return NULL;
1780#endif
1781}
1782
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette