VirtualBox

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

Last change on this file since 60149 was 60149, checked in by vboxsync, 9 years ago

USBGetDevices.cpp: fixes

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

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