VirtualBox

source: vbox/trunk/src/VBox/Main/linux/USBProxyServiceLinux.cpp@ 32142

Last change on this file since 32142 was 32142, checked in by vboxsync, 14 years ago

Main/linux/USB: moved a vector container-based implementation detail out of the interface

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.9 KB
Line 
1/* $Id: USBProxyServiceLinux.cpp 32142 2010-08-31 13:00:46Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service, Linux Specialization.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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#include "USBProxyService.h"
23#include "Logging.h"
24
25#include <VBox/usb.h>
26#include <VBox/usblib.h>
27#include <VBox/err.h>
28
29#include <iprt/string.h>
30#include <iprt/alloc.h>
31#include <iprt/assert.h>
32#include <iprt/ctype.h>
33#include <iprt/env.h>
34#include <iprt/file.h>
35#include <iprt/err.h>
36#include <iprt/mem.h>
37#include <iprt/param.h>
38#include <iprt/path.h>
39#include <iprt/stream.h>
40#include <iprt/linux/sysfs.h>
41
42#include <stdlib.h>
43#include <string.h>
44#include <stdio.h>
45#include <errno.h>
46#include <unistd.h>
47#include <sys/statfs.h>
48#include <sys/poll.h>
49#ifdef VBOX_WITH_LINUX_COMPILER_H
50# include <linux/compiler.h>
51#endif
52#include <linux/usbdevice_fs.h>
53
54
55/*******************************************************************************
56* Structures and Typedefs *
57*******************************************************************************/
58/** Suffix translation. */
59typedef struct USBSUFF
60{
61 char szSuff[4];
62 unsigned cchSuff;
63 unsigned uMul;
64 unsigned uDiv;
65} USBSUFF, *PUSBSUFF;
66typedef const USBSUFF *PCUSBSUFF;
67
68
69/*******************************************************************************
70* Global Variables *
71*******************************************************************************/
72/**
73 * Suffixes for the endpoint polling interval.
74 */
75static const USBSUFF s_aIntervalSuff[] =
76{
77 { "ms", 2, 1, 0 },
78 { "us", 2, 1, 1000 },
79 { "ns", 2, 1, 1000000 },
80 { "s", 1, 1000, 0 },
81 { "", 0, 0, 0 } /* term */
82};
83
84
85/**
86 * Initialize data members.
87 */
88USBProxyServiceLinux::USBProxyServiceLinux(Host *aHost, const char *aUsbfsRoot /* = "/proc/bus/usb" */)
89 : USBProxyService(aHost), mFile(NIL_RTFILE), mStream(NULL), mWakeupPipeR(NIL_RTFILE),
90 mWakeupPipeW(NIL_RTFILE), mUsbfsRoot(aUsbfsRoot), mUsingUsbfsDevices(true /* see init */), mUdevPolls(0)
91{
92 LogFlowThisFunc(("aHost=%p aUsbfsRoot=%p:{%s}\n", aHost, aUsbfsRoot, aUsbfsRoot));
93}
94
95
96/**
97 * Initializes the object (called right after construction).
98 *
99 * @returns S_OK on success and non-fatal failures, some COM error otherwise.
100 */
101HRESULT USBProxyServiceLinux::init(void)
102{
103 /*
104 * Call the superclass method first.
105 */
106 HRESULT hrc = USBProxyService::init();
107 AssertComRCReturn(hrc, hrc);
108
109 /*
110 * We have two methods available for getting host USB device data - using
111 * USBFS and using sysfs/hal. The default choice depends on build-time
112 * settings and an environment variable; if the default is not available
113 * we fall back to the second.
114 * In the event of both failing, the error from the second method tried
115 * will be presented to the user.
116 */
117#ifdef VBOX_WITH_SYSFS_BY_DEFAULT
118 mUsingUsbfsDevices = false;
119#else
120 mUsingUsbfsDevices = true;
121#endif
122 const char *pszUsbFromEnv = RTEnvGet("VBOX_USB");
123 if (pszUsbFromEnv)
124 {
125 if (!RTStrICmp(pszUsbFromEnv, "USBFS"))
126 {
127 LogRel(("Default USB access method set to \"usbfs\" from environment\n"));
128 mUsingUsbfsDevices = true;
129 }
130 else if (!RTStrICmp(pszUsbFromEnv, "SYSFS"))
131 {
132 LogRel(("Default USB method set to \"sysfs\" from environment\n"));
133 mUsingUsbfsDevices = false;
134 }
135 else
136 LogRel(("Invalid VBOX_USB environment variable setting \"%s\"\n",
137 pszUsbFromEnv));
138 }
139 int rc = mUsingUsbfsDevices ? initUsbfs() : initSysfs();
140 if (RT_FAILURE(rc))
141 {
142 /* For the day when we have VBoxSVC release logging... */
143 LogRel(("Failed to initialise host USB using %s\n",
144 mUsingUsbfsDevices ? "USBFS" : "sysfs/hal"));
145 mUsingUsbfsDevices = !mUsingUsbfsDevices;
146 rc = mUsingUsbfsDevices ? initUsbfs() : initSysfs();
147 }
148 LogRel((RT_SUCCESS(rc) ? "Successfully initialised host USB using %s\n"
149 : "Failed to initialise host USB using %s\n",
150 mUsingUsbfsDevices ? "USBFS" : "sysfs/hal"));
151 mLastError = rc;
152 return S_OK;
153}
154
155
156/**
157 * Initializiation routine for the usbfs based operation.
158 *
159 * @returns iprt status code.
160 */
161int USBProxyServiceLinux::initUsbfs(void)
162{
163 Assert(mUsingUsbfsDevices);
164
165 /*
166 * Open the devices file.
167 */
168 int rc;
169 char *pszDevices;
170 RTStrAPrintf(&pszDevices, "%s/devices", mUsbfsRoot.c_str());
171 if (pszDevices)
172 {
173 rc = RTFileOpen(&mFile, pszDevices, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
174 if (RT_SUCCESS(rc))
175 {
176 /*
177 * Check that we're actually on the usbfs.
178 */
179 struct statfs StFS;
180 if (!fstatfs(mFile, &StFS))
181 {
182 if (StFS.f_type == USBDEVICE_SUPER_MAGIC)
183 {
184 int pipes[2];
185 if (!pipe(pipes))
186 {
187 mWakeupPipeR = pipes[0];
188 mWakeupPipeW = pipes[1];
189 mStream = fdopen(mFile, "r");
190 if (mStream)
191 {
192 /*
193 * Start the poller thread.
194 */
195 rc = start();
196 if (RT_SUCCESS(rc))
197 {
198 RTStrFree(pszDevices);
199 LogFlowThisFunc(("returns successfully - mFile=%d mStream=%p mWakeupPipeR/W=%d/%d\n",
200 mFile, mStream, mWakeupPipeR, mWakeupPipeW));
201 /*
202 * Turn buffering off to work around rewind() problems, see getDevices().
203 */
204 setvbuf(mStream, NULL, _IONBF, 0);
205 return VINF_SUCCESS;
206 }
207
208 fclose(mStream);
209 mStream = NULL;
210 mFile = NIL_RTFILE;
211 }
212 else
213 {
214 rc = RTErrConvertFromErrno(errno);
215 Log(("USBProxyServiceLinux::USBProxyServiceLinux: fdopen failed, errno=%d\n", errno));
216 }
217
218 RTFileClose(mWakeupPipeR);
219 RTFileClose(mWakeupPipeW);
220 mWakeupPipeW = mWakeupPipeR = NIL_RTFILE;
221 }
222 else
223 {
224 rc = RTErrConvertFromErrno(errno);
225 Log(("USBProxyServiceLinux::USBProxyServiceLinux: pipe failed, errno=%d\n", errno));
226 }
227 }
228 else
229 {
230 Log(("USBProxyServiceLinux::USBProxyServiceLinux: StFS.f_type=%d expected=%d\n", StFS.f_type, USBDEVICE_SUPER_MAGIC));
231 rc = VERR_INVALID_PARAMETER;
232 }
233 }
234 else
235 {
236 rc = RTErrConvertFromErrno(errno);
237 Log(("USBProxyServiceLinux::USBProxyServiceLinux: fstatfs failed, errno=%d\n", errno));
238 }
239 RTFileClose(mFile);
240 mFile = NIL_RTFILE;
241 }
242 RTStrFree(pszDevices);
243 }
244 else
245 {
246 rc = VERR_NO_MEMORY;
247 Log(("USBProxyServiceLinux::USBProxyServiceLinux: out of memory!\n"));
248 }
249
250 LogFlowThisFunc(("returns failure!!! (rc=%Rrc)\n", rc));
251 return rc;
252}
253
254
255/**
256 * Initializiation routine for the sysfs based operation.
257 *
258 * @returns iprt status code
259 */
260int USBProxyServiceLinux::initSysfs(void)
261{
262 Assert(!mUsingUsbfsDevices);
263
264#ifdef VBOX_USB_WITH_SYSFS
265 if (!VBoxMainUSBDevInfoInit(&mDeviceList))
266 return VERR_NO_MEMORY;
267 int rc = mWaiter.getStatus();
268 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT || rc == VERR_TRY_AGAIN)
269 rc = start();
270 else if (rc == VERR_NOT_SUPPORTED)
271 /* This can legitimately happen if hal or DBus are not running, but of
272 * course we can't start in this case. */
273 rc = VINF_SUCCESS;
274 return rc;
275
276#else /* !VBOX_USB_WITH_SYSFS */
277 return VERR_NOT_IMPLEMENTED;
278#endif /* !VBOX_USB_WITH_SYSFS */
279}
280
281
282/**
283 * Stop all service threads and free the device chain.
284 */
285USBProxyServiceLinux::~USBProxyServiceLinux()
286{
287 LogFlowThisFunc(("\n"));
288
289 /*
290 * Stop the service.
291 */
292 if (isActive())
293 stop();
294
295 /*
296 * Free resources.
297 */
298 doUsbfsCleanupAsNeeded();
299
300 /* (No extra work for !mUsingUsbfsDevices.) */
301}
302
303
304/**
305 * If any Usbfs-releated resources are currently allocated, then free them
306 * and mark them as freed.
307 */
308void USBProxyServiceLinux::doUsbfsCleanupAsNeeded()
309{
310 /*
311 * Free resources.
312 */
313 if (mStream)
314 {
315 fclose(mStream);
316 mStream = NULL;
317 mFile = NIL_RTFILE;
318 }
319 else if (mFile != NIL_RTFILE)
320 {
321 RTFileClose(mFile);
322 mFile = NIL_RTFILE;
323 }
324
325 if (mWakeupPipeR != NIL_RTFILE)
326 RTFileClose(mWakeupPipeR);
327 if (mWakeupPipeW != NIL_RTFILE)
328 RTFileClose(mWakeupPipeW);
329 mWakeupPipeW = mWakeupPipeR = NIL_RTFILE;
330}
331
332
333int USBProxyServiceLinux::captureDevice(HostUSBDevice *aDevice)
334{
335 Log(("USBProxyServiceLinux::captureDevice: %p {%s}\n", aDevice, aDevice->getName().c_str()));
336 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
337 AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
338
339 /*
340 * Don't think we need to do anything when the device is held... fake it.
341 */
342 Assert(aDevice->getUnistate() == kHostUSBDeviceState_Capturing);
343 interruptWait();
344
345 return VINF_SUCCESS;
346}
347
348
349int USBProxyServiceLinux::releaseDevice(HostUSBDevice *aDevice)
350{
351 Log(("USBProxyServiceLinux::releaseDevice: %p\n", aDevice));
352 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
353 AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
354
355 /*
356 * We're not really holding it atm., just fake it.
357 */
358 Assert(aDevice->getUnistate() == kHostUSBDeviceState_ReleasingToHost);
359 interruptWait();
360
361 return VINF_SUCCESS;
362}
363
364
365bool USBProxyServiceLinux::updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine)
366{
367 if ( aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
368 && aDevice->mUsb->enmState == USBDEVICESTATE_USED_BY_HOST)
369 LogRel(("USBProxy: Device %04x:%04x (%s) has become accessible.\n",
370 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress));
371 return updateDeviceStateFake(aDevice, aUSBDevice, aRunFilters, aIgnoreMachine);
372}
373
374
375/**
376 * A device was added, we need to adjust mUdevPolls.
377 *
378 * See USBProxyService::deviceAdded for details.
379 */
380void USBProxyServiceLinux::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList &llOpenedMachines, PUSBDEVICE aUSBDevice)
381{
382 if (aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST)
383 {
384 LogRel(("USBProxy: Device %04x:%04x (%s) isn't accessible. giving udev a few seconds to fix this...\n",
385 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress));
386 mUdevPolls = 10; /* (10 * 500ms = 5s) */
387 }
388
389 USBProxyService::deviceAdded(aDevice, llOpenedMachines, aUSBDevice);
390}
391
392
393int USBProxyServiceLinux::wait(RTMSINTERVAL aMillies)
394{
395 int rc;
396 if (mUsingUsbfsDevices)
397 rc = waitUsbfs(aMillies);
398 else
399 rc = waitSysfs(aMillies);
400 return rc;
401}
402
403
404/** String written to the wakeup pipe. */
405#define WAKE_UP_STRING "WakeUp!"
406/** Length of the string written. */
407#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
408
409int USBProxyServiceLinux::waitUsbfs(RTMSINTERVAL aMillies)
410{
411 struct pollfd PollFds[2];
412
413 /* Cap the wait interval if we're polling for udevd changing device permissions. */
414 if (aMillies > 500 && mUdevPolls > 0)
415 {
416 mUdevPolls--;
417 aMillies = 500;
418 }
419
420 memset(&PollFds, 0, sizeof(PollFds));
421 PollFds[0].fd = mFile;
422 PollFds[0].events = POLLIN;
423 PollFds[1].fd = mWakeupPipeR;
424 PollFds[1].events = POLLIN | POLLERR | POLLHUP;
425
426 int rc = poll(&PollFds[0], 2, aMillies);
427 if (rc == 0)
428 return VERR_TIMEOUT;
429 if (rc > 0)
430 {
431 /* drain the pipe */
432 if (PollFds[1].revents & POLLIN)
433 {
434 char szBuf[WAKE_UP_STRING_LEN];
435 rc = RTFileRead(mWakeupPipeR, szBuf, sizeof(szBuf), NULL);
436 AssertRC(rc);
437 }
438 return VINF_SUCCESS;
439 }
440 return RTErrConvertFromErrno(errno);
441}
442
443
444int USBProxyServiceLinux::waitSysfs(RTMSINTERVAL aMillies)
445{
446#ifdef VBOX_USB_WITH_SYSFS
447 int rc = mWaiter.Wait(aMillies);
448 if (rc == VERR_TRY_AGAIN)
449 {
450 RTThreadYield();
451 rc = VINF_SUCCESS;
452 }
453 return rc;
454#else /* !VBOX_USB_WITH_SYSFS */
455 return USBProxyService::wait(aMillies);
456#endif /* !VBOX_USB_WITH_SYSFS */
457}
458
459
460int USBProxyServiceLinux::interruptWait(void)
461{
462#ifdef VBOX_USB_WITH_SYSFS
463 LogFlowFunc(("mUsingUsbfsDevices=%d\n", mUsingUsbfsDevices));
464 if (!mUsingUsbfsDevices)
465 {
466 mWaiter.Interrupt();
467 LogFlowFunc(("Returning VINF_SUCCESS\n"));
468 return VINF_SUCCESS;
469 }
470#endif /* VBOX_USB_WITH_SYSFS */
471 int rc = RTFileWrite(mWakeupPipeW, WAKE_UP_STRING, WAKE_UP_STRING_LEN, NULL);
472 if (RT_SUCCESS(rc))
473 RTFileFlush(mWakeupPipeW);
474 LogFlowFunc(("returning %Rrc\n", rc));
475 return rc;
476}
477
478
479/**
480 * "reads" the number suffix. It's more like validating it and
481 * skipping the necessary number of chars.
482 */
483static int usbReadSkipSuffix(char **ppszNext)
484{
485 char *pszNext = *ppszNext;
486 if (!RT_C_IS_SPACE(*pszNext) && *pszNext)
487 {
488 /* skip unit */
489 if (pszNext[0] == 'm' && pszNext[1] == 's')
490 pszNext += 2;
491 else if (pszNext[0] == 'm' && pszNext[1] == 'A')
492 pszNext += 2;
493
494 /* skip parenthesis */
495 if (*pszNext == '(')
496 {
497 pszNext = strchr(pszNext, ')');
498 if (!pszNext++)
499 {
500 AssertMsgFailed(("*ppszNext=%s\n", *ppszNext));
501 return VERR_PARSE_ERROR;
502 }
503 }
504
505 /* blank or end of the line. */
506 if (!RT_C_IS_SPACE(*pszNext) && *pszNext)
507 {
508 AssertMsgFailed(("pszNext=%s\n", pszNext));
509 return VERR_PARSE_ERROR;
510 }
511
512 /* it's ok. */
513 *ppszNext = pszNext;
514 }
515
516 return VINF_SUCCESS;
517}
518
519
520/**
521 * Reads a USB number returning the number and the position of the next character to parse.
522 */
523static int usbReadNum(const char *pszValue, unsigned uBase, uint32_t u32Mask, PCUSBSUFF paSuffs, void *pvNum, char **ppszNext)
524{
525 /*
526 * Initialize return value to zero and strip leading spaces.
527 */
528 switch (u32Mask)
529 {
530 case 0xff: *(uint8_t *)pvNum = 0; break;
531 case 0xffff: *(uint16_t *)pvNum = 0; break;
532 case 0xffffffff: *(uint32_t *)pvNum = 0; break;
533 }
534 pszValue = RTStrStripL(pszValue);
535 if (*pszValue)
536 {
537 /*
538 * Try convert the number.
539 */
540 char *pszNext;
541 uint32_t u32 = 0;
542 RTStrToUInt32Ex(pszValue, &pszNext, uBase, &u32);
543 if (pszNext == pszValue)
544 {
545 AssertMsgFailed(("pszValue=%d\n", pszValue));
546 return VERR_NO_DATA;
547 }
548
549 /*
550 * Check the range.
551 */
552 if (u32 & ~u32Mask)
553 {
554 AssertMsgFailed(("pszValue=%d u32=%#x lMask=%#x\n", pszValue, u32, u32Mask));
555 return VERR_OUT_OF_RANGE;
556 }
557
558 /*
559 * Validate and skip stuff following the number.
560 */
561 if (paSuffs)
562 {
563 if (!RT_C_IS_SPACE(*pszNext) && *pszNext)
564 {
565 for (PCUSBSUFF pSuff = paSuffs; pSuff->szSuff[0]; pSuff++)
566 {
567 if ( !strncmp(pSuff->szSuff, pszNext, pSuff->cchSuff)
568 && (!pszNext[pSuff->cchSuff] || RT_C_IS_SPACE(pszNext[pSuff->cchSuff])))
569 {
570 if (pSuff->uDiv)
571 u32 /= pSuff->uDiv;
572 else
573 u32 *= pSuff->uMul;
574 break;
575 }
576 }
577 }
578 }
579 else
580 {
581 int rc = usbReadSkipSuffix(&pszNext);
582 if (RT_FAILURE(rc))
583 return rc;
584 }
585
586 *ppszNext = pszNext;
587
588 /*
589 * Set the value.
590 */
591 switch (u32Mask)
592 {
593 case 0xff: *(uint8_t *)pvNum = (uint8_t)u32; break;
594 case 0xffff: *(uint16_t *)pvNum = (uint16_t)u32; break;
595 case 0xffffffff: *(uint32_t *)pvNum = (uint32_t)u32; break;
596 }
597 }
598 return VINF_SUCCESS;
599}
600
601
602static int usbRead8(const char *pszValue, unsigned uBase, uint8_t *pu8, char **ppszNext)
603{
604 return usbReadNum(pszValue, uBase, 0xff, NULL, pu8, ppszNext);
605}
606
607
608static int usbRead16(const char *pszValue, unsigned uBase, uint16_t *pu16, char **ppszNext)
609{
610 return usbReadNum(pszValue, uBase, 0xffff, NULL, pu16, ppszNext);
611}
612
613
614#if 0
615static int usbRead16Suff(const char *pszValue, unsigned uBase, PCUSBSUFF paSuffs, uint16_t *pu16, char **ppszNext)
616{
617 return usbReadNum(pszValue, uBase, 0xffff, paSuffs, pu16, ppszNext);
618}
619#endif
620
621
622/**
623 * Reads a USB BCD number returning the number and the position of the next character to parse.
624 * The returned number contains the integer part in the high byte and the decimal part in the low byte.
625 */
626static int usbReadBCD(const char *pszValue, unsigned uBase, uint16_t *pu16, char **ppszNext)
627{
628 /*
629 * Initialize return value to zero and strip leading spaces.
630 */
631 *pu16 = 0;
632 pszValue = RTStrStripL(pszValue);
633 if (*pszValue)
634 {
635 /*
636 * Try convert the number.
637 */
638 /* integer part */
639 char *pszNext;
640 uint32_t u32Int = 0;
641 RTStrToUInt32Ex(pszValue, &pszNext, uBase, &u32Int);
642 if (pszNext == pszValue)
643 {
644 AssertMsgFailed(("pszValue=%s\n", pszValue));
645 return VERR_NO_DATA;
646 }
647 if (u32Int & ~0xff)
648 {
649 AssertMsgFailed(("pszValue=%s u32Int=%#x (int)\n", pszValue, u32Int));
650 return VERR_OUT_OF_RANGE;
651 }
652
653 /* skip dot and read decimal part */
654 if (*pszNext != '.')
655 {
656 AssertMsgFailed(("pszValue=%s pszNext=%s (int)\n", pszValue, pszNext));
657 return VERR_PARSE_ERROR;
658 }
659 char *pszValue2 = RTStrStripL(pszNext + 1);
660 uint32_t u32Dec = 0;
661 RTStrToUInt32Ex(pszValue2, &pszNext, uBase, &u32Dec);
662 if (pszNext == pszValue)
663 {
664 AssertMsgFailed(("pszValue=%s\n", pszValue));
665 return VERR_NO_DATA;
666 }
667 if (u32Dec & ~0xff)
668 {
669 AssertMsgFailed(("pszValue=%s u32Dec=%#x\n", pszValue, u32Dec));
670 return VERR_OUT_OF_RANGE;
671 }
672
673 /*
674 * Validate and skip stuff following the number.
675 */
676 int rc = usbReadSkipSuffix(&pszNext);
677 if (RT_FAILURE(rc))
678 return rc;
679 *ppszNext = pszNext;
680
681 /*
682 * Set the value.
683 */
684 *pu16 = (uint16_t)u32Int << 8 | (uint16_t)u32Dec;
685 }
686 return VINF_SUCCESS;
687}
688
689
690/**
691 * Reads a string, i.e. allocates memory and copies it.
692 *
693 * We assume that a string is pure ASCII, if that's not the case
694 * tell me how to figure out the codeset please.
695 */
696static int usbReadStr(const char *pszValue, const char **ppsz)
697{
698 if (*ppsz)
699 RTStrFree((char *)*ppsz);
700 *ppsz = RTStrDup(pszValue);
701 if (*ppsz)
702 return VINF_SUCCESS;
703 return VERR_NO_MEMORY;
704}
705
706
707/**
708 * Skips the current property.
709 */
710static char *usbReadSkip(char *pszValue)
711{
712 char *psz = strchr(pszValue, '=');
713 if (psz)
714 psz = strchr(psz + 1, '=');
715 if (!psz)
716 return strchr(pszValue, '\0');
717 while (psz > pszValue && !RT_C_IS_SPACE(psz[-1]))
718 psz--;
719 Assert(psz > pszValue);
720 return psz;
721}
722
723
724/**
725 * Determine the USB speed.
726 */
727static int usbReadSpeed(const char *pszValue, USBDEVICESPEED *pSpd, char **ppszNext)
728{
729 pszValue = RTStrStripL(pszValue);
730 /* verified with Linux 2.4.0 ... Linux 2.6.25 */
731 if (!strncmp(pszValue, "1.5", 3))
732 *pSpd = USBDEVICESPEED_LOW;
733 else if (!strncmp(pszValue, "12 ", 3))
734 *pSpd = USBDEVICESPEED_FULL;
735 else if (!strncmp(pszValue, "480", 3))
736 *pSpd = USBDEVICESPEED_HIGH;
737 else
738 *pSpd = USBDEVICESPEED_UNKNOWN;
739 while (pszValue[0] != '\0' && !RT_C_IS_SPACE(pszValue[0]))
740 pszValue++;
741 *ppszNext = (char *)pszValue;
742 return VINF_SUCCESS;
743}
744
745
746/**
747 * Compare a prefix and returns pointer to the char following it if it matches.
748 */
749static char *usbPrefix(char *psz, const char *pszPref, size_t cchPref)
750{
751 if (strncmp(psz, pszPref, cchPref))
752 return NULL;
753 return psz + cchPref;
754}
755
756
757/**
758 * Does some extra checks to improve the detected device state.
759 *
760 * We cannot distinguish between USED_BY_HOST_CAPTURABLE and
761 * USED_BY_GUEST, HELD_BY_PROXY all that well and it shouldn't be
762 * necessary either.
763 *
764 * We will however, distinguish between the device we have permissions
765 * to open and those we don't. This is necessary for two reasons.
766 *
767 * Firstly, because it's futile to even attempt opening a device which we
768 * don't have access to, it only serves to confuse the user. (That said,
769 * it might also be a bit confusing for the user to see that a USB device
770 * is grayed out with no further explanation, and no way of generating an
771 * error hinting at why this is the case.)
772 *
773 * Secondly and more importantly, we're racing against udevd with respect
774 * to permissions and group settings on newly plugged devices. When we
775 * detect a new device that we cannot access we will poll on it for a few
776 * seconds to give udevd time to fix it. The polling is actually triggered
777 * in the 'new device' case in the compare loop.
778 *
779 * The USBDEVICESTATE_USED_BY_HOST state is only used for this no-access
780 * case, while USBDEVICESTATE_UNSUPPORTED is only used in the 'hub' case.
781 * When it's neither of these, we set USBDEVICESTATE_UNUSED or
782 * USBDEVICESTATE_USED_BY_HOST_CAPTURABLE depending on whether there is
783 * a driver associated with any of the interfaces.
784 *
785 * All except the access check and a special idVendor == 0 precaution
786 * is handled at parse time.
787 *
788 * @returns The adjusted state.
789 * @param pDevice The device.
790 */
791static USBDEVICESTATE usbDeterminState(PCUSBDEVICE pDevice)
792{
793 /*
794 * If it's already flagged as unsupported, there is nothing to do.
795 */
796 USBDEVICESTATE enmState = pDevice->enmState;
797 if (enmState == USBDEVICESTATE_UNSUPPORTED)
798 return USBDEVICESTATE_UNSUPPORTED;
799
800 /*
801 * Root hubs and similar doesn't have any vendor id, just
802 * refuse these device.
803 */
804 if (!pDevice->idVendor)
805 return USBDEVICESTATE_UNSUPPORTED;
806
807 /*
808 * Check if we've got access to the device, if we haven't flag
809 * it as used-by-host.
810 */
811#ifndef VBOX_USB_WITH_SYSFS
812 const char *pszAddress = pDevice->pszAddress;
813#else
814 if (pDevice->pszAddress == NULL)
815 /* We can't do much with the device without an address. */
816 return USBDEVICESTATE_UNSUPPORTED;
817 const char *pszAddress = strstr(pDevice->pszAddress, "//device:");
818 pszAddress = pszAddress != NULL
819 ? pszAddress + sizeof("//device:") - 1
820 : pDevice->pszAddress;
821#endif
822 if ( access(pszAddress, R_OK | W_OK) != 0
823 && errno == EACCES)
824 return USBDEVICESTATE_USED_BY_HOST;
825
826#ifdef VBOX_USB_WITH_SYSFS
827 /**
828 * @todo Check that any other essential fields are present and mark as
829 * invalid if not. Particularly to catch the case where the device was
830 * unplugged while we were reading in its properties.
831 */
832#endif
833
834 return enmState;
835}
836
837
838/** Just a worker for USBProxyServiceLinux::getDevices that avoids some code duplication. */
839int USBProxyServiceLinux::addDeviceToChain(PUSBDEVICE pDev, PUSBDEVICE *ppFirst, PUSBDEVICE **pppNext, int rc)
840{
841 /* usbDeterminState requires the address. */
842 PUSBDEVICE pDevNew = (PUSBDEVICE)RTMemDup(pDev, sizeof(*pDev));
843 if (pDevNew)
844 {
845 RTStrAPrintf((char **)&pDevNew->pszAddress, "%s/%03d/%03d", mUsbfsRoot.c_str(), pDevNew->bBus, pDevNew->bDevNum);
846 if (pDevNew->pszAddress)
847 {
848 pDevNew->enmState = usbDeterminState(pDevNew);
849 if (pDevNew->enmState != USBDEVICESTATE_UNSUPPORTED)
850 {
851 if (*pppNext)
852 **pppNext = pDevNew;
853 else
854 *ppFirst = pDevNew;
855 *pppNext = &pDevNew->pNext;
856 }
857 else
858 freeDevice(pDevNew);
859 }
860 else
861 {
862 freeDevice(pDevNew);
863 rc = VERR_NO_MEMORY;
864 }
865 }
866 else
867 {
868 rc = VERR_NO_MEMORY;
869 freeDeviceMembers(pDev);
870 }
871
872 return rc;
873}
874
875
876/**
877 * USBProxyService::getDevices() implementation for usbfs.
878 */
879PUSBDEVICE USBProxyServiceLinux::getDevicesFromUsbfs(void)
880{
881 PUSBDEVICE pFirst = NULL;
882 if (mStream)
883 {
884 PUSBDEVICE *ppNext = NULL;
885 int cHits = 0;
886 char szLine[1024];
887 USBDEVICE Dev;
888 RT_ZERO(Dev);
889 Dev.enmState = USBDEVICESTATE_UNUSED;
890
891 /*
892 * Rewind the stream and make 100% sure we flush the buffer.
893 *
894 * We've had trouble with rewind() messing up on buffered streams when attaching
895 * device clusters such as the Bloomberg keyboard. Therefor the stream is now
896 * without a permanent buffer (see the constructor) and we'll employ a temporary
897 * stack buffer while parsing the file (speed).
898 */
899 rewind(mStream);
900 char szBuf[1024];
901 setvbuf(mStream, szBuf, _IOFBF, sizeof(szBuf));
902
903 int rc = VINF_SUCCESS;
904 while ( RT_SUCCESS(rc)
905 && fgets(szLine, sizeof(szLine), mStream))
906 {
907 char *psz;
908 char *pszValue;
909
910 /* validate and remove the trailing newline. */
911 psz = strchr(szLine, '\0');
912 if (psz[-1] != '\n' && !feof(mStream))
913 {
914 AssertMsgFailed(("Line too long. (cch=%d)\n", strlen(szLine)));
915 continue;
916 }
917
918 /* strip */
919 psz = RTStrStrip(szLine);
920 if (!*psz)
921 continue;
922
923 /*
924 * Interpret the line.
925 * (Ordered by normal occurence.)
926 */
927 char ch = psz[0];
928 if (psz[1] != ':')
929 continue;
930 psz = RTStrStripL(psz + 3);
931#define PREFIX(str) ( (pszValue = usbPrefix(psz, str, sizeof(str) - 1)) != NULL )
932 switch (ch)
933 {
934 /*
935 * T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd
936 * | | | | | | | | |__MaxChildren
937 * | | | | | | | |__Device Speed in Mbps
938 * | | | | | | |__DeviceNumber
939 * | | | | | |__Count of devices at this level
940 * | | | | |__Connector/Port on Parent for this device
941 * | | | |__Parent DeviceNumber
942 * | | |__Level in topology for this bus
943 * | |__Bus number
944 * |__Topology info tag
945 */
946 case 'T':
947 /* add */
948 AssertMsg(cHits >= 3 || cHits == 0, ("cHits=%d\n", cHits));
949 if (cHits >= 3)
950 rc = addDeviceToChain(&Dev, &pFirst, &ppNext, rc);
951 else
952 freeDeviceMembers(&Dev);
953
954 /* Reset device state */
955 memset(&Dev, 0, sizeof (Dev));
956 Dev.enmState = USBDEVICESTATE_UNUSED;
957 cHits = 1;
958
959 /* parse the line. */
960 while (*psz && RT_SUCCESS(rc))
961 {
962 if (PREFIX("Bus="))
963 rc = usbRead8(pszValue, 10, &Dev.bBus, &psz);
964 else if (PREFIX("Port="))
965 rc = usbRead8(pszValue, 10, &Dev.bPort, &psz);
966 else if (PREFIX("Spd="))
967 rc = usbReadSpeed(pszValue, &Dev.enmSpeed, &psz);
968 else if (PREFIX("Dev#="))
969 rc = usbRead8(pszValue, 10, &Dev.bDevNum, &psz);
970 else
971 psz = usbReadSkip(psz);
972 psz = RTStrStripL(psz);
973 }
974 break;
975
976 /*
977 * Bandwidth info:
978 * B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd
979 * | | | |__Number of isochronous requests
980 * | | |__Number of interrupt requests
981 * | |__Total Bandwidth allocated to this bus
982 * |__Bandwidth info tag
983 */
984 case 'B':
985 break;
986
987 /*
988 * D: Ver=x.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd
989 * | | | | | | |__NumberConfigurations
990 * | | | | | |__MaxPacketSize of Default Endpoint
991 * | | | | |__DeviceProtocol
992 * | | | |__DeviceSubClass
993 * | | |__DeviceClass
994 * | |__Device USB version
995 * |__Device info tag #1
996 */
997 case 'D':
998 while (*psz && RT_SUCCESS(rc))
999 {
1000 if (PREFIX("Ver="))
1001 rc = usbReadBCD(pszValue, 16, &Dev.bcdUSB, &psz);
1002 else if (PREFIX("Cls="))
1003 {
1004 rc = usbRead8(pszValue, 16, &Dev.bDeviceClass, &psz);
1005 if (RT_SUCCESS(rc) && Dev.bDeviceClass == 9 /* HUB */)
1006 Dev.enmState = USBDEVICESTATE_UNSUPPORTED;
1007 }
1008 else if (PREFIX("Sub="))
1009 rc = usbRead8(pszValue, 16, &Dev.bDeviceSubClass, &psz);
1010 else if (PREFIX("Prot="))
1011 rc = usbRead8(pszValue, 16, &Dev.bDeviceProtocol, &psz);
1012 //else if (PREFIX("MxPS="))
1013 // rc = usbRead16(pszValue, 10, &Dev.wMaxPacketSize, &psz);
1014 else if (PREFIX("#Cfgs="))
1015 rc = usbRead8(pszValue, 10, &Dev.bNumConfigurations, &psz);
1016 else
1017 psz = usbReadSkip(psz);
1018 psz = RTStrStripL(psz);
1019 }
1020 cHits++;
1021 break;
1022
1023 /*
1024 * P: Vendor=xxxx ProdID=xxxx Rev=xx.xx
1025 * | | | |__Product revision number
1026 * | | |__Product ID code
1027 * | |__Vendor ID code
1028 * |__Device info tag #2
1029 */
1030 case 'P':
1031 while (*psz && RT_SUCCESS(rc))
1032 {
1033 if (PREFIX("Vendor="))
1034 rc = usbRead16(pszValue, 16, &Dev.idVendor, &psz);
1035 else if (PREFIX("ProdID="))
1036 rc = usbRead16(pszValue, 16, &Dev.idProduct, &psz);
1037 else if (PREFIX("Rev="))
1038 rc = usbReadBCD(pszValue, 16, &Dev.bcdDevice, &psz);
1039 else
1040 psz = usbReadSkip(psz);
1041 psz = RTStrStripL(psz);
1042 }
1043 cHits++;
1044 break;
1045
1046 /*
1047 * String.
1048 */
1049 case 'S':
1050 if (PREFIX("Manufacturer="))
1051 rc = usbReadStr(pszValue, &Dev.pszManufacturer);
1052 else if (PREFIX("Product="))
1053 rc = usbReadStr(pszValue, &Dev.pszProduct);
1054 else if (PREFIX("SerialNumber="))
1055 {
1056 rc = usbReadStr(pszValue, &Dev.pszSerialNumber);
1057 if (RT_SUCCESS(rc))
1058 Dev.u64SerialHash = USBLibHashSerial(pszValue);
1059 }
1060 break;
1061
1062 /*
1063 * C:* #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA
1064 * | | | | | |__MaxPower in mA
1065 * | | | | |__Attributes
1066 * | | | |__ConfiguratioNumber
1067 * | | |__NumberOfInterfaces
1068 * | |__ "*" indicates the active configuration (others are " ")
1069 * |__Config info tag
1070 */
1071 case 'C':
1072 break;
1073
1074 /*
1075 * I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss
1076 * | | | | | | | |__Driver name
1077 * | | | | | | | or "(none)"
1078 * | | | | | | |__InterfaceProtocol
1079 * | | | | | |__InterfaceSubClass
1080 * | | | | |__InterfaceClass
1081 * | | | |__NumberOfEndpoints
1082 * | | |__AlternateSettingNumber
1083 * | |__InterfaceNumber
1084 * |__Interface info tag
1085 */
1086 case 'I':
1087 {
1088 /* Check for thing we don't support. */
1089 while (*psz && RT_SUCCESS(rc))
1090 {
1091 if (PREFIX("Driver="))
1092 {
1093 const char *pszDriver = NULL;
1094 rc = usbReadStr(pszValue, &pszDriver);
1095 if ( !pszDriver
1096 || !*pszDriver
1097 || !strcmp(pszDriver, "(none)")
1098 || !strcmp(pszDriver, "(no driver)"))
1099 /* no driver */;
1100 else if (!strcmp(pszDriver, "hub"))
1101 Dev.enmState = USBDEVICESTATE_UNSUPPORTED;
1102 else if (Dev.enmState == USBDEVICESTATE_UNUSED)
1103 Dev.enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
1104 RTStrFree((char *)pszDriver);
1105 break; /* last attrib */
1106 }
1107 else if (PREFIX("Cls="))
1108 {
1109 uint8_t bInterfaceClass;
1110 rc = usbRead8(pszValue, 16, &bInterfaceClass, &psz);
1111 if (RT_SUCCESS(rc) && bInterfaceClass == 9 /* HUB */)
1112 Dev.enmState = USBDEVICESTATE_UNSUPPORTED;
1113 }
1114 else
1115 psz = usbReadSkip(psz);
1116 psz = RTStrStripL(psz);
1117 }
1118 break;
1119 }
1120
1121
1122 /*
1123 * E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms
1124 * | | | | |__Interval (max) between transfers
1125 * | | | |__EndpointMaxPacketSize
1126 * | | |__Attributes(EndpointType)
1127 * | |__EndpointAddress(I=In,O=Out)
1128 * |__Endpoint info tag
1129 */
1130 case 'E':
1131 break;
1132
1133 }
1134#undef PREFIX
1135 } /* parse loop */
1136
1137 /*
1138 * Add the current entry.
1139 */
1140 AssertMsg(cHits >= 3 || cHits == 0, ("cHits=%d\n", cHits));
1141 if (cHits >= 3)
1142 rc = addDeviceToChain(&Dev, &pFirst, &ppNext, rc);
1143
1144 /*
1145 * Success?
1146 */
1147 if (RT_FAILURE(rc))
1148 {
1149 LogFlow(("USBProxyServiceLinux::getDevices: rc=%Rrc\n", rc));
1150 while (pFirst)
1151 {
1152 PUSBDEVICE pFree = pFirst;
1153 pFirst = pFirst->pNext;
1154 freeDevice(pFree);
1155 }
1156 }
1157
1158 /*
1159 * Turn buffering off to detach it from the local buffer and to
1160 * make subsequent rewind() calls work correctly.
1161 */
1162 setvbuf(mStream, NULL, _IONBF, 0);
1163 }
1164 return pFirst;
1165}
1166
1167#ifdef VBOX_USB_WITH_SYSFS
1168
1169/**
1170 * Helper function for extracting the port number on the parent device from
1171 * the sysfs path value.
1172 *
1173 * The sysfs path is a chain of elements separated by forward slashes, and for
1174 * USB devices, the last element in the chain takes the form
1175 * <port>-<port>.[...].<port>[:<config>.<interface>]
1176 * where the first <port> is the port number on the root hub, and the following
1177 * (optional) ones are the port numbers on any other hubs between the device
1178 * and the root hub. The last part (:<config.interface>) is only present for
1179 * interfaces, not for devices. This API should only be called for devices.
1180 * For compatibility with usbfs, which enumerates from zero up, we subtract one
1181 * from the port number.
1182 *
1183 * For root hubs, the last element in the chain takes the form
1184 * usb<hub number>
1185 * and usbfs always returns port number zero.
1186 *
1187 * @returns VBox status. pu8Port is set on success.
1188 * @param pszPath The sysfs path to parse.
1189 * @param pu8Port Where to store the port number.
1190 */
1191static int usbGetPortFromSysfsPath(const char *pszPath, uint8_t *pu8Port)
1192{
1193 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1194 AssertPtrReturn(pu8Port, VERR_INVALID_POINTER);
1195
1196 /*
1197 * This should not be possible until we get PCs with USB as their primary bus.
1198 * Note: We don't assert this, as we don't expect the caller to validate the
1199 * sysfs path.
1200 */
1201 const char *pszLastComp = strrchr(pszPath, '/');
1202 if (!pszLastComp)
1203 {
1204 Log(("usbGetPortFromSysfsPath(%s): failed [1]\n", pszPath));
1205 return VERR_INVALID_PARAMETER;
1206 }
1207 pszLastComp++; /* skip the slash */
1208
1209 /*
1210 * This API should not be called for interfaces, so the last component
1211 * of the path should not contain a colon. We *do* assert this, as it
1212 * might indicate a caller bug.
1213 */
1214 AssertMsgReturn(strchr(pszLastComp, ':') == NULL, ("%s\n", pszPath), VERR_INVALID_PARAMETER);
1215
1216 /*
1217 * Look for the start of the last number.
1218 */
1219 const char *pchDash = strrchr(pszLastComp, '-');
1220 const char *pchDot = strrchr(pszLastComp, '.');
1221 if (!pchDash && !pchDot)
1222 {
1223 /* No -/. so it must be a root hub. Check that it's usb<something>. */
1224 if (strncmp(pszLastComp, "usb", sizeof("usb") - 1) != 0)
1225 {
1226 Log(("usbGetPortFromSysfsPath(%s): failed [2]\n", pszPath));
1227 return VERR_INVALID_PARAMETER;
1228 }
1229 return VERR_NOT_SUPPORTED;
1230 }
1231 else
1232 {
1233 const char *pszLastPort = pchDot != NULL
1234 ? pchDot + 1
1235 : pchDash + 1;
1236 int rc = RTStrToUInt8Full(pszLastPort, 10, pu8Port);
1237 if (rc != VINF_SUCCESS)
1238 {
1239 Log(("usbGetPortFromSysfsPath(%s): failed [3], rc=%Rrc\n", pszPath, rc));
1240 return VERR_INVALID_PARAMETER;
1241 }
1242 if (*pu8Port == 0)
1243 {
1244 Log(("usbGetPortFromSysfsPath(%s): failed [4]\n", pszPath));
1245 return VERR_INVALID_PARAMETER;
1246 }
1247
1248 /* usbfs compatibility, 0-based port number. */
1249 *pu8Port -= 1;
1250 }
1251 return VINF_SUCCESS;
1252}
1253
1254
1255/**
1256 * Dumps a USBDEVICE structure to the log using LogLevel 3.
1257 * @param pDev The structure to log.
1258 * @todo This is really common code.
1259 */
1260DECLINLINE(void) usbLogDevice(PUSBDEVICE pDev)
1261{
1262 NOREF(pDev);
1263
1264 Log3(("USB device:\n"));
1265 Log3(("Product: %s (%x)\n", pDev->pszProduct, pDev->idProduct));
1266 Log3(("Manufacturer: %s (Vendor ID %x)\n", pDev->pszManufacturer, pDev->idVendor));
1267 Log3(("Serial number: %s (%llx)\n", pDev->pszSerialNumber, pDev->u64SerialHash));
1268 Log3(("Device revision: %d\n", pDev->bcdDevice));
1269 Log3(("Device class: %x\n", pDev->bDeviceClass));
1270 Log3(("Device subclass: %x\n", pDev->bDeviceSubClass));
1271 Log3(("Device protocol: %x\n", pDev->bDeviceProtocol));
1272 Log3(("USB version number: %d\n", pDev->bcdUSB));
1273 Log3(("Device speed: %s\n",
1274 pDev->enmSpeed == USBDEVICESPEED_UNKNOWN ? "unknown"
1275 : pDev->enmSpeed == USBDEVICESPEED_LOW ? "1.5 MBit/s"
1276 : pDev->enmSpeed == USBDEVICESPEED_FULL ? "12 MBit/s"
1277 : pDev->enmSpeed == USBDEVICESPEED_HIGH ? "480 MBit/s"
1278 : pDev->enmSpeed == USBDEVICESPEED_VARIABLE ? "variable"
1279 : "invalid"));
1280 Log3(("Number of configurations: %d\n", pDev->bNumConfigurations));
1281 Log3(("Bus number: %d\n", pDev->bBus));
1282 Log3(("Port number: %d\n", pDev->bPort));
1283 Log3(("Device number: %d\n", pDev->bDevNum));
1284 Log3(("Device state: %s\n",
1285 pDev->enmState == USBDEVICESTATE_UNSUPPORTED ? "unsupported"
1286 : pDev->enmState == USBDEVICESTATE_USED_BY_HOST ? "in use by host"
1287 : pDev->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE ? "in use by host, possibly capturable"
1288 : pDev->enmState == USBDEVICESTATE_UNUSED ? "not in use"
1289 : pDev->enmState == USBDEVICESTATE_HELD_BY_PROXY ? "held by proxy"
1290 : pDev->enmState == USBDEVICESTATE_USED_BY_GUEST ? "used by guest"
1291 : "invalid"));
1292 Log3(("OS device address: %s\n", pDev->pszAddress));
1293}
1294
1295/**
1296 * In contrast to usbReadBCD() this function can handle BCD values without
1297 * a decimal separator. This is necessary for parsing bcdDevice.
1298 * @param pszBuf Pointer to the string buffer.
1299 * @param pu15 Pointer to the return value.
1300 * @returns IPRT status code.
1301 */
1302static int convertSysfsStrToBCD(const char *pszBuf, uint16_t *pu16)
1303{
1304 char *pszNext;
1305 int32_t i32;
1306
1307 pszBuf = RTStrStripL(pszBuf);
1308 int rc = RTStrToInt32Ex(pszBuf, &pszNext, 16, &i32);
1309 if ( RT_FAILURE(rc)
1310 || rc == VWRN_NUMBER_TOO_BIG
1311 || i32 < 0)
1312 return VERR_NUMBER_TOO_BIG;
1313 if (*pszNext == '.')
1314 {
1315 if (i32 > 255)
1316 return VERR_NUMBER_TOO_BIG;
1317 int32_t i32Lo;
1318 rc = RTStrToInt32Ex(pszNext+1, &pszNext, 16, &i32Lo);
1319 if ( RT_FAILURE(rc)
1320 || rc == VWRN_NUMBER_TOO_BIG
1321 || i32Lo > 255
1322 || i32Lo < 0)
1323 return VERR_NUMBER_TOO_BIG;
1324 i32 = (i32 << 8) | i32Lo;
1325 }
1326 if ( i32 > 65535
1327 || (*pszNext != '\0' && *pszNext != ' '))
1328 return VERR_NUMBER_TOO_BIG;
1329
1330 *pu16 = (uint16_t)i32;
1331 return VINF_SUCCESS;
1332}
1333
1334#endif /* VBOX_USB_WITH_SYSFS */
1335
1336/**
1337 * USBProxyService::getDevices() implementation for sysfs.
1338 */
1339PUSBDEVICE USBProxyServiceLinux::getDevicesFromSysfs(void)
1340{
1341#ifdef VBOX_USB_WITH_SYSFS
1342 /* Add each of the devices found to the chain. */
1343 PUSBDEVICE pFirst = NULL;
1344 PUSBDEVICE pLast = NULL;
1345 int rc = USBDevInfoUpdateDevices(&mDeviceList);
1346 USBDeviceInfoList_iterator it;
1347 USBDeviceInfoList_iter_init(&it, USBDevInfoBegin(&mDeviceList));
1348 for (; RT_SUCCESS(rc)
1349 && !USBDeviceInfoList_iter_eq(&it, USBDevInfoEnd(&mDeviceList));
1350 USBDeviceInfoList_iter_incr(&it))
1351 {
1352 USBDEVICE *Dev = (USBDEVICE *)RTMemAllocZ(sizeof(USBDEVICE));
1353 if (!Dev)
1354 rc = VERR_NO_MEMORY;
1355 if (RT_SUCCESS(rc))
1356 {
1357 const char *pszSysfsPath = USBDeviceInfoList_iter_target(&it)->mSysfsPath;
1358
1359 /* Fill in the simple fields */
1360 Dev->enmState = USBDEVICESTATE_UNUSED;
1361 Dev->bBus = RTLinuxSysFsReadIntFile(10, "%s/busnum", pszSysfsPath);
1362 Dev->bDeviceClass = RTLinuxSysFsReadIntFile(16, "%s/bDeviceClass", pszSysfsPath);
1363 Dev->bDeviceSubClass = RTLinuxSysFsReadIntFile(16, "%s/bDeviceSubClass", pszSysfsPath);
1364 Dev->bDeviceProtocol = RTLinuxSysFsReadIntFile(16, "%s/bDeviceProtocol", pszSysfsPath);
1365 Dev->bNumConfigurations = RTLinuxSysFsReadIntFile(10, "%s/bNumConfigurations", pszSysfsPath);
1366 Dev->idVendor = RTLinuxSysFsReadIntFile(16, "%s/idVendor", pszSysfsPath);
1367 Dev->idProduct = RTLinuxSysFsReadIntFile(16, "%s/idProduct", pszSysfsPath);
1368 Dev->bDevNum = RTLinuxSysFsReadIntFile(10, "%s/devnum", pszSysfsPath);
1369
1370 /* Now deal with the non-numeric bits. */
1371 char szBuf[1024]; /* Should be larger than anything a sane device
1372 * will need, and insane devices can be unsupported
1373 * until further notice. */
1374 ssize_t cchRead;
1375
1376 /* For simplicity, we just do strcmps on the next one. */
1377 cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/speed",
1378 pszSysfsPath);
1379 if (cchRead <= 0 || (size_t) cchRead == sizeof(szBuf))
1380 Dev->enmState = USBDEVICESTATE_UNSUPPORTED;
1381 else
1382 Dev->enmSpeed = !strcmp(szBuf, "1.5") ? USBDEVICESPEED_LOW
1383 : !strcmp(szBuf, "12") ? USBDEVICESPEED_FULL
1384 : !strcmp(szBuf, "480") ? USBDEVICESPEED_HIGH
1385 : USBDEVICESPEED_UNKNOWN;
1386
1387 cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/version",
1388 pszSysfsPath);
1389 if (cchRead <= 0 || (size_t) cchRead == sizeof(szBuf))
1390 Dev->enmState = USBDEVICESTATE_UNSUPPORTED;
1391 else
1392 {
1393 rc = convertSysfsStrToBCD(szBuf, &Dev->bcdUSB);
1394 if (RT_FAILURE(rc))
1395 {
1396 Dev->enmState = USBDEVICESTATE_UNSUPPORTED;
1397 Dev->bcdUSB = (uint16_t)-1;
1398 }
1399 }
1400
1401 cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/bcdDevice",
1402 pszSysfsPath);
1403 if (cchRead <= 0 || (size_t) cchRead == sizeof(szBuf))
1404 Dev->bcdDevice = (uint16_t)-1;
1405 else
1406 {
1407 rc = convertSysfsStrToBCD(szBuf, &Dev->bcdDevice);
1408 if (RT_FAILURE(rc))
1409 Dev->bcdDevice = (uint16_t)-1;
1410 }
1411
1412 /* Now do things that need string duplication */
1413 cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/product",
1414 pszSysfsPath);
1415 if (cchRead > 0 && (size_t) cchRead < sizeof(szBuf))
1416 {
1417 RTStrPurgeEncoding(szBuf);
1418 Dev->pszProduct = RTStrDup(szBuf);
1419 }
1420
1421 cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/serial",
1422 pszSysfsPath);
1423 if (cchRead > 0 && (size_t) cchRead < sizeof(szBuf))
1424 {
1425 RTStrPurgeEncoding(szBuf);
1426 Dev->pszSerialNumber = RTStrDup(szBuf);
1427 Dev->u64SerialHash = USBLibHashSerial(szBuf);
1428 }
1429
1430 cchRead = RTLinuxSysFsReadStrFile(szBuf, sizeof(szBuf), "%s/manufacturer",
1431 pszSysfsPath);
1432 if (cchRead > 0 && (size_t) cchRead < sizeof(szBuf))
1433 {
1434 RTStrPurgeEncoding(szBuf);
1435 Dev->pszManufacturer = RTStrDup(szBuf);
1436 }
1437
1438 /* Work out the port number */
1439 if (RT_FAILURE(usbGetPortFromSysfsPath(pszSysfsPath, &Dev->bPort)))
1440 Dev->enmState = USBDEVICESTATE_UNSUPPORTED;
1441
1442 /* Check the interfaces to see if we can support the device. */
1443 char *pszIf;
1444 USBDeviceInfo *udi = USBDeviceInfoList_iter_target(&it);
1445 for (pszIf = USBDevInfoFirstInterface(udi->mInterfaces); pszIf;
1446 pszIf = USBDevInfoNextInterface(udi->mInterfaces))
1447 {
1448 ssize_t cb = RTLinuxSysFsGetLinkDest(szBuf, sizeof(szBuf), "%s/driver",
1449 pszIf);
1450 if (cb > 0 && Dev->enmState != USBDEVICESTATE_UNSUPPORTED)
1451 Dev->enmState = (strcmp(szBuf, "hub") == 0)
1452 ? USBDEVICESTATE_UNSUPPORTED
1453 : USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
1454 if (RTLinuxSysFsReadIntFile(16, "%s/bInterfaceClass",
1455 pszIf) == 9 /* hub */)
1456 Dev->enmState = USBDEVICESTATE_UNSUPPORTED;
1457 }
1458
1459 /* We want a copy of the device node and sysfs paths guaranteed not to
1460 * contain double slashes, since we use a double slash as a separator in
1461 * the pszAddress field. */
1462 char szDeviceClean[RTPATH_MAX];
1463 char szSysfsClean[RTPATH_MAX];
1464 char *pszAddress = NULL;
1465 if ( RT_SUCCESS(RTPathReal(USBDeviceInfoList_iter_target(&it)->mDevice, szDeviceClean,
1466 sizeof(szDeviceClean)))
1467 && RT_SUCCESS(RTPathReal(pszSysfsPath, szSysfsClean,
1468 sizeof(szSysfsClean)))
1469 )
1470 RTStrAPrintf(&pszAddress, "sysfs:%s//device:%s", szSysfsClean,
1471 szDeviceClean);
1472 Dev->pszAddress = pszAddress;
1473
1474 /* Work out from the data collected whether we can support this device. */
1475 Dev->enmState = usbDeterminState(Dev);
1476 usbLogDevice(Dev);
1477 }
1478 if ( RT_SUCCESS(rc)
1479 && Dev->enmState != USBDEVICESTATE_UNSUPPORTED
1480 && Dev->pszAddress != NULL
1481 )
1482 {
1483 if (pLast != NULL)
1484 {
1485 pLast->pNext = Dev;
1486 pLast = pLast->pNext;
1487 }
1488 else
1489 pFirst = pLast = Dev;
1490 }
1491 else
1492 freeDevice(Dev);
1493 }
1494 if (RT_FAILURE(rc))
1495 while (pFirst)
1496 {
1497 PUSBDEVICE pNext = pFirst->pNext;
1498 freeDevice(pFirst);
1499 pFirst = pNext;
1500 }
1501
1502 /* Eliminate any duplicates. This was originally a sanity check, but it
1503 * turned out that hal can get confused and return devices twice. */
1504 for (PUSBDEVICE pDev = pFirst; pDev != NULL; pDev = pDev->pNext)
1505 for (PUSBDEVICE pDev2 = pDev; pDev2 != NULL && pDev2->pNext != NULL;
1506 pDev2 = pDev2->pNext)
1507 while ( pDev2->pNext != NULL
1508 && RTStrCmp(pDev->pszAddress, pDev2->pNext->pszAddress) == 0)
1509 {
1510 PUSBDEVICE pDup = pDev2->pNext;
1511 pDev2->pNext = pDup->pNext;
1512 freeDevice(pDup);
1513 }
1514 return pFirst;
1515#else /* !VBOX_USB_WITH_SYSFS */
1516 return NULL;
1517#endif /* !VBOX_USB_WITH_SYSFS */
1518}
1519
1520
1521PUSBDEVICE USBProxyServiceLinux::getDevices(void)
1522{
1523 PUSBDEVICE pDevices;
1524 if (mUsingUsbfsDevices)
1525 pDevices = getDevicesFromUsbfs();
1526 else
1527 pDevices = getDevicesFromSysfs();
1528 return pDevices;
1529}
1530
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