- Timestamp:
- Sep 8, 2010 3:43:32 PM (14 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 1 added
- 7 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/Makefile.kmk
r32312 r32324 361 361 VBoxSVC_SOURCES.darwin += darwin/USBProxyServiceDarwin.cpp 362 362 VBoxSVC_SOURCES.linux += linux/USBProxyServiceLinux.cpp 363 VBoxSVC_SOURCES.linux += linux/USBGetDevices.cpp 363 364 VBoxSVC_SOURCES.os2 += os2/USBProxyServiceOs2.cpp 364 365 VBoxSVC_SOURCES.solaris += solaris/USBProxyServiceSolaris.cpp -
trunk/src/VBox/Main/include/HostHardwareLinux.h
r32299 r32324 110 110 typedef VBoxMainDriveInfo::DriveInfo DriveInfo; 111 111 112 113 /** Structure describing a host USB device */114 typedef struct USBDeviceInfo115 {116 /** The device node of the device. */117 char *mDevice;118 /** The system identifier of the device. Specific to the probing119 * method. */120 char *mSysfsPath;121 /** List of interfaces as sysfs paths */122 VECTOR_PTR(char *) mvecpszInterfaces;123 } USBDeviceInfo;124 125 /** Destructor. */126 void USBDevInfoCleanup(USBDeviceInfo *pSelf);127 128 /** Constructor - the strings will be duplicated. */129 int USBDevInfoInit(USBDeviceInfo *pSelf, const char *aDevice,130 const char *aSystemID);131 132 /**133 * Enumerate USB devices attached to the host using sysfs and return them as a134 * vector135 * @returns iprt status code136 * @param pvecDevInfo vector to add the devices onto the end of. Should be137 * initialised and empty.138 */139 int USBSysfsEnumerateHostDevices(VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo);140 141 142 112 /** Implementation of the hotplug waiter class below */ 143 113 class VBoxMainHotplugWaiterImpl -
trunk/src/VBox/Main/include/vector.h
r32262 r32324 27 27 # define MAIN_VECTOR_H 28 28 29 /*** Helper macros and deinitions ***/ 29 /******************************************************************************* 30 * Header Files * 31 *******************************************************************************/ 32 33 #include <stdlib.h> 34 35 /******************************************************************************* 36 * Helper macros and definitions * 37 *******************************************************************************/ 30 38 31 39 /** The unit by which the vector capacity is increased */ … … 208 216 } 209 217 210 /*** Public interface macros ***/ 218 /******************************************************************************* 219 * Public interface macros * 220 *******************************************************************************/ 211 221 212 222 /** -
trunk/src/VBox/Main/linux/HostHardwareLinux.cpp
r32299 r32324 1401 1401 { } 1402 1402 } 1403 1404 void USBDevInfoCleanup(USBDeviceInfo *pSelf)1405 {1406 RTStrFree(pSelf->mDevice);1407 RTStrFree(pSelf->mSysfsPath);1408 pSelf->mDevice = pSelf->mSysfsPath = NULL;1409 VEC_CLEANUP_PTR(&pSelf->mvecpszInterfaces);1410 }1411 1412 int USBDevInfoInit(USBDeviceInfo *pSelf, const char *aDevice,1413 const char *aSystemID)1414 {1415 pSelf->mDevice = aDevice ? RTStrDup(aDevice) : NULL;1416 pSelf->mSysfsPath = aSystemID ? RTStrDup(aSystemID) : NULL;1417 VEC_INIT_PTR(&pSelf->mvecpszInterfaces, char *, RTStrFree);1418 if ((aDevice && !pSelf->mDevice) || (aSystemID && ! pSelf->mSysfsPath))1419 {1420 USBDevInfoCleanup(pSelf);1421 return 0;1422 }1423 return 1;1424 }1425 1426 #ifdef VBOX_USB_WITH_SYSFS1427 # ifdef VBOX_USB_WITH_INOTIFY1428 1429 #define USBDEVICE_MAJOR 1891430 1431 /** Deduce the bus that a USB device is plugged into from the device node1432 * number. See drivers/usb/core/hub.c:usb_new_device as of Linux 2.6.20. */1433 static unsigned usbBusFromDevNum(dev_t devNum)1434 {1435 AssertReturn(devNum, 0);1436 AssertReturn(major(devNum) == USBDEVICE_MAJOR, 0);1437 return (minor(devNum) >> 7) + 1;1438 }1439 1440 1441 /** Deduce the device number of a USB device on the bus from the device node1442 * number. See drivers/usb/core/hub.c:usb_new_device as of Linux 2.6.20. */1443 static unsigned usbDeviceFromDevNum(dev_t devNum)1444 {1445 AssertReturn(devNum, 0);1446 AssertReturn(major(devNum) == USBDEVICE_MAJOR, 0);1447 return (minor(devNum) & 127) + 1;1448 }1449 1450 1451 /**1452 * If a file @a pcszNode from /sys/bus/usb/devices is a device rather than an1453 * interface add an element for the device to @a pvecDevInfo.1454 */1455 static int addIfDevice(const char *pcszNode,1456 VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo)1457 {1458 const char *pcszFile = strrchr(pcszNode, '/');1459 if (strchr(pcszFile, ':'))1460 return VINF_SUCCESS;1461 dev_t devnum = RTLinuxSysFsReadDevNumFile("%s/dev", pcszNode);1462 /* Sanity test of our static helpers */1463 Assert(usbBusFromDevNum(makedev(USBDEVICE_MAJOR, 517)) == 5);1464 Assert(usbDeviceFromDevNum(makedev(USBDEVICE_MAJOR, 517)) == 6);1465 if (!devnum)1466 return VINF_SUCCESS;1467 char szDevPath[RTPATH_MAX];1468 ssize_t cchDevPath;1469 cchDevPath = RTLinuxFindDevicePath(devnum, RTFS_TYPE_DEV_CHAR,1470 szDevPath, sizeof(szDevPath),1471 "/dev/bus/usb/%.3d/%.3d",1472 usbBusFromDevNum(devnum),1473 usbDeviceFromDevNum(devnum));1474 if (cchDevPath < 0)1475 return VINF_SUCCESS;1476 1477 USBDeviceInfo info;1478 if (USBDevInfoInit(&info, szDevPath, pcszNode))1479 if (RT_SUCCESS(VEC_PUSH_BACK_OBJ(pvecDevInfo, USBDeviceInfo,1480 &info)))1481 return VINF_SUCCESS;1482 USBDevInfoCleanup(&info);1483 return VERR_NO_MEMORY;1484 }1485 1486 /** The logic for testing whether a sysfs address corresponds to an1487 * interface of a device. Both must be referenced by their canonical1488 * sysfs paths. This is not tested, as the test requires file-system1489 * interaction. */1490 static bool muiIsAnInterfaceOf(const char *pcszIface, const char *pcszDev)1491 {1492 size_t cchDev = strlen(pcszDev);1493 1494 AssertPtr(pcszIface);1495 AssertPtr(pcszDev);1496 Assert(pcszIface[0] == '/');1497 Assert(pcszDev[0] == '/');1498 Assert(pcszDev[cchDev - 1] != '/');1499 /* If this passes, pcszIface is at least cchDev long */1500 if (strncmp(pcszIface, pcszDev, cchDev))1501 return false;1502 /* If this passes, pcszIface is longer than cchDev */1503 if (pcszIface[cchDev] != '/')1504 return false;1505 /* In sysfs an interface is an immediate subdirectory of the device */1506 if (strchr(pcszIface + cchDev + 1, '/'))1507 return false;1508 /* And it always has a colon in its name */1509 if (!strchr(pcszIface + cchDev + 1, ':'))1510 return false;1511 /* And hopefully we have now elimitated everything else */1512 return true;1513 }1514 1515 #ifdef DEBUG1516 /** Unit test the logic in muiIsAnInterfaceOf in debug builds. */1517 class testIsAnInterfaceOf1518 {1519 public:1520 testIsAnInterfaceOf()1521 {1522 Assert(muiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0",1523 "/sys/devices/pci0000:00/0000:00:1a.0/usb3"));1524 Assert(!muiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-1",1525 "/sys/devices/pci0000:00/0000:00:1a.0/usb3"));1526 Assert(!muiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0/driver",1527 "/sys/devices/pci0000:00/0000:00:1a.0/usb3"));1528 }1529 };1530 static testIsAnInterfaceOf testIsAnInterfaceOfInst;1531 #endif1532 1533 /**1534 * Tell whether a file in /sys/bus/usb/devices is an interface rather than a1535 * device. To be used with getDeviceInfoFromSysfs().1536 */1537 static int addIfInterfaceOf(const char *pcszNode, USBDeviceInfo *pInfo)1538 {1539 if (!muiIsAnInterfaceOf(pcszNode, pInfo->mSysfsPath))1540 return VINF_SUCCESS;1541 char *pszDup = (char *)RTStrDup(pcszNode);1542 if (pszDup)1543 if (RT_SUCCESS(VEC_PUSH_BACK_PTR(&pInfo->mvecpszInterfaces,1544 char *, pszDup)))1545 return VINF_SUCCESS;1546 RTStrFree(pszDup);1547 return VERR_NO_MEMORY;1548 }1549 1550 /**1551 * Logic for USBSysfsEnumerateHostDevices.1552 * @param pvecDevInfo vector of device information structures to add device1553 * information to1554 * @param pvecpchDevs empty scratch vector which will be freed by the caller1555 */1556 static int doSysfsEnumerateHostDevices(VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo,1557 VECTOR_PTR(char *) *pvecpchDevs)1558 {1559 char **ppszEntry;1560 USBDeviceInfo *pInfo;1561 int rc;1562 1563 AssertPtrReturn(pvecDevInfo, VERR_INVALID_POINTER);1564 LogFlowFunc (("pvecDevInfo=%p\n", pvecDevInfo));1565 1566 rc = readFilePaths("/sys/bus/usb/devices", pvecpchDevs, true);1567 if (RT_FAILURE(rc))1568 return rc;1569 VEC_FOR_EACH(pvecpchDevs, char *, ppszEntry)1570 if (RT_FAILURE(rc = addIfDevice(*ppszEntry, pvecDevInfo)))1571 return rc;1572 VEC_FOR_EACH(pvecDevInfo, USBDeviceInfo, pInfo)1573 VEC_FOR_EACH(pvecpchDevs, char *, ppszEntry)1574 if (RT_FAILURE(rc = addIfInterfaceOf(*ppszEntry, pInfo)))1575 return rc;1576 return VINF_SUCCESS;1577 }1578 # endif /* VBOX_USB_WITH_INOTIFY */1579 #endif /* VBOX_USB_WITH_SYSFS */1580 1581 int USBSysfsEnumerateHostDevices(VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo)1582 {1583 VECTOR_PTR(char *) vecpchDevs;1584 int rc = VERR_NOT_IMPLEMENTED;1585 1586 AssertReturn(VEC_SIZE_OBJ(pvecDevInfo) == 0, VERR_INVALID_PARAMETER);1587 LogFlowFunc(("entered\n"));1588 VEC_INIT_PTR(&vecpchDevs, char *, RTStrFree);1589 #ifdef VBOX_USB_WITH_SYSFS1590 # ifdef VBOX_USB_WITH_INOTIFY1591 rc = doSysfsEnumerateHostDevices(pvecDevInfo, &vecpchDevs);1592 # endif1593 #endif /* !VBOX_USB_WITH_SYSFS */1594 VEC_CLEANUP_PTR(&vecpchDevs);1595 LogFlowFunc(("rc=%Rrc\n", rc));1596 return rc;1597 } -
trunk/src/VBox/Main/linux/USBGetDevices.cpp
r32301 r32324 1 1 /* $Id$ */ 2 2 /** @file 3 * VirtualBox USB Proxy Service, Linux Specialization.3 * VirtualBox Linux host USB device enumeration. 4 4 */ 5 5 … … 20 20 * Header Files * 21 21 *******************************************************************************/ 22 #include "USBProxyService.h" 23 #include " Logging.h"22 23 #include "USBGetDevices.h" 24 24 25 25 #include <VBox/usb.h> 26 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> 27 28 #include <iprt/linux/sysfs.h> 29 #include <iprt/cdefs.h> 32 30 #include <iprt/ctype.h> 33 #include <iprt/env.h>34 #include <iprt/file.h>35 31 #include <iprt/err.h> 32 #include <iprt/fs.h> 33 #include <iprt/log.h> 36 34 #include <iprt/mem.h> 37 35 #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> 36 #include <iprt/string.h> 37 #include "vector.h" 38 39 #include <linux/usbdevice_fs.h> 40 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <sys/vfs.h> 44 45 #include <dirent.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <stdio.h> 43 49 #include <string.h> 44 #include <stdio.h>45 #include <errno.h>46 50 #include <unistd.h> 47 #include <sys/statfs.h>48 #include <sys/poll.h>49 #ifdef VBOX_WITH_LINUX_COMPILER_H50 # include <linux/compiler.h>51 #endif52 #include <linux/usbdevice_fs.h>53 54 51 55 52 /******************************************************************************* … … 66 63 typedef const USBSUFF *PCUSBSUFF; 67 64 65 /** Structure describing a host USB device */ 66 typedef struct USBDeviceInfo 67 { 68 /** The device node of the device. */ 69 char *mDevice; 70 /** The system identifier of the device. Specific to the probing 71 * method. */ 72 char *mSysfsPath; 73 /** List of interfaces as sysfs paths */ 74 VECTOR_PTR(char *) mvecpszInterfaces; 75 } USBDeviceInfo; 68 76 69 77 /******************************************************************************* … … 83 91 84 92 85 /** 86 * Initialize data members. 87 */ 88 USBProxyServiceLinux::USBProxyServiceLinux(Host *aHost, const char *aUsbfsRoot /* = "/proc/bus/usb" */) 89 : USBProxyService(aHost), mFile(NIL_RTFILE), 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 */ 101 HRESULT 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 static int USBProxyLinuxCheckForUsbfs(const char *pcszDevices) 157 { 158 RTFILE File; 159 int rc; 160 161 rc = RTFileOpen(&File, pcszDevices, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); 162 if (RT_SUCCESS(rc)) 93 int USBProxyLinuxCheckForUsbfs(const char *pcszDevices) 94 { 95 int fd; 96 97 fd = open(pcszDevices, O_RDONLY, 00600); 98 if (fd) 163 99 { 164 100 /* … … 166 102 */ 167 103 struct statfs StFS; 168 if (!fstatfs( File, &StFS))104 if (!fstatfs(fd, &StFS)) 169 105 { 170 106 if (StFS.f_type == USBDEVICE_SUPER_MAGIC) … … 176 112 return RTErrConvertFromErrno(errno); 177 113 } 178 return rc;179 }180 181 182 /**183 * Initializiation routine for the usbfs based operation.184 *185 * @returns iprt status code.186 */187 int USBProxyServiceLinux::initUsbfs(void)188 {189 Assert(mUsingUsbfsDevices);190 191 /*192 * Open the devices file.193 */194 int rc;195 char *pszDevices;196 RTStrAPrintf(&pszDevices, "%s/devices", mUsbfsRoot.c_str());197 if (pszDevices)198 {199 rc = USBProxyLinuxCheckForUsbfs(pszDevices);200 if (RT_SUCCESS(rc))201 {202 rc = RTFileOpen(&mFile, pszDevices, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);203 if (RT_SUCCESS(rc))204 {205 int pipes[2];206 if (!pipe(pipes))207 {208 mWakeupPipeR = pipes[0];209 mWakeupPipeW = pipes[1];210 /*211 * Start the poller thread.212 */213 rc = start();214 if (RT_SUCCESS(rc))215 {216 RTStrFree(pszDevices);217 LogFlowThisFunc(("returns successfully - mWakeupPipeR/W=%d/%d\n",218 mWakeupPipeR, mWakeupPipeW));219 return VINF_SUCCESS;220 }221 222 RTFileClose(mWakeupPipeR);223 RTFileClose(mWakeupPipeW);224 mWakeupPipeW = mWakeupPipeR = NIL_RTFILE;225 }226 else227 {228 rc = RTErrConvertFromErrno(errno);229 Log(("USBProxyServiceLinux::USBProxyServiceLinux: pipe failed, errno=%d\n", errno));230 }231 RTFileClose(mFile);232 }233 234 }235 RTStrFree(pszDevices);236 }237 114 else 238 { 239 rc = VERR_NO_MEMORY; 240 Log(("USBProxyServiceLinux::USBProxyServiceLinux: out of memory!\n")); 241 } 242 243 LogFlowThisFunc(("returns failure!!! (rc=%Rrc)\n", rc)); 244 return rc; 245 } 246 247 248 /** 249 * Initializiation routine for the sysfs based operation. 250 * 251 * @returns iprt status code 252 */ 253 int USBProxyServiceLinux::initSysfs(void) 254 { 255 Assert(!mUsingUsbfsDevices); 256 257 #ifdef VBOX_USB_WITH_SYSFS 258 int rc = mWaiter.getStatus(); 259 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT || rc == VERR_TRY_AGAIN) 260 rc = start(); 261 else if (rc == VERR_NOT_SUPPORTED) 262 /* This can legitimately happen if hal or DBus are not running, but of 263 * course we can't start in this case. */ 264 rc = VINF_SUCCESS; 265 return rc; 266 267 #else /* !VBOX_USB_WITH_SYSFS */ 268 return VERR_NOT_IMPLEMENTED; 269 #endif /* !VBOX_USB_WITH_SYSFS */ 270 } 271 272 273 /** 274 * Stop all service threads and free the device chain. 275 */ 276 USBProxyServiceLinux::~USBProxyServiceLinux() 277 { 278 LogFlowThisFunc(("\n")); 279 280 /* 281 * Stop the service. 282 */ 283 if (isActive()) 284 stop(); 285 286 /* 287 * Free resources. 288 */ 289 doUsbfsCleanupAsNeeded(); 290 291 /* (No extra work for !mUsingUsbfsDevices.) */ 292 } 293 294 295 /** 296 * If any Usbfs-releated resources are currently allocated, then free them 297 * and mark them as freed. 298 */ 299 void USBProxyServiceLinux::doUsbfsCleanupAsNeeded() 300 { 301 /* 302 * Free resources. 303 */ 304 if (mFile != NIL_RTFILE) 305 { 306 RTFileClose(mFile); 307 mFile = NIL_RTFILE; 308 } 309 310 if (mWakeupPipeR != NIL_RTFILE) 311 RTFileClose(mWakeupPipeR); 312 if (mWakeupPipeW != NIL_RTFILE) 313 RTFileClose(mWakeupPipeW); 314 mWakeupPipeW = mWakeupPipeR = NIL_RTFILE; 315 } 316 317 318 int USBProxyServiceLinux::captureDevice(HostUSBDevice *aDevice) 319 { 320 Log(("USBProxyServiceLinux::captureDevice: %p {%s}\n", aDevice, aDevice->getName().c_str())); 321 AssertReturn(aDevice, VERR_GENERAL_FAILURE); 322 AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE); 323 324 /* 325 * Don't think we need to do anything when the device is held... fake it. 326 */ 327 Assert(aDevice->getUnistate() == kHostUSBDeviceState_Capturing); 328 interruptWait(); 329 330 return VINF_SUCCESS; 331 } 332 333 334 int USBProxyServiceLinux::releaseDevice(HostUSBDevice *aDevice) 335 { 336 Log(("USBProxyServiceLinux::releaseDevice: %p\n", aDevice)); 337 AssertReturn(aDevice, VERR_GENERAL_FAILURE); 338 AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE); 339 340 /* 341 * We're not really holding it atm., just fake it. 342 */ 343 Assert(aDevice->getUnistate() == kHostUSBDeviceState_ReleasingToHost); 344 interruptWait(); 345 346 return VINF_SUCCESS; 347 } 348 349 350 bool USBProxyServiceLinux::updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine) 351 { 352 if ( aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE 353 && aDevice->mUsb->enmState == USBDEVICESTATE_USED_BY_HOST) 354 LogRel(("USBProxy: Device %04x:%04x (%s) has become accessible.\n", 355 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress)); 356 return updateDeviceStateFake(aDevice, aUSBDevice, aRunFilters, aIgnoreMachine); 357 } 358 359 360 /** 361 * A device was added, we need to adjust mUdevPolls. 362 * 363 * See USBProxyService::deviceAdded for details. 364 */ 365 void USBProxyServiceLinux::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList &llOpenedMachines, PUSBDEVICE aUSBDevice) 366 { 367 if (aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST) 368 { 369 LogRel(("USBProxy: Device %04x:%04x (%s) isn't accessible. giving udev a few seconds to fix this...\n", 370 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress)); 371 mUdevPolls = 10; /* (10 * 500ms = 5s) */ 372 } 373 374 USBProxyService::deviceAdded(aDevice, llOpenedMachines, aUSBDevice); 375 } 376 377 378 int USBProxyServiceLinux::wait(RTMSINTERVAL aMillies) 379 { 380 int rc; 381 if (mUsingUsbfsDevices) 382 rc = waitUsbfs(aMillies); 383 else 384 rc = waitSysfs(aMillies); 385 return rc; 386 } 387 388 389 /** String written to the wakeup pipe. */ 390 #define WAKE_UP_STRING "WakeUp!" 391 /** Length of the string written. */ 392 #define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 ) 393 394 int USBProxyServiceLinux::waitUsbfs(RTMSINTERVAL aMillies) 395 { 396 struct pollfd PollFds[2]; 397 398 /* Cap the wait interval if we're polling for udevd changing device permissions. */ 399 if (aMillies > 500 && mUdevPolls > 0) 400 { 401 mUdevPolls--; 402 aMillies = 500; 403 } 404 405 memset(&PollFds, 0, sizeof(PollFds)); 406 PollFds[0].fd = mFile; 407 PollFds[0].events = POLLIN; 408 PollFds[1].fd = mWakeupPipeR; 409 PollFds[1].events = POLLIN | POLLERR | POLLHUP; 410 411 int rc = poll(&PollFds[0], 2, aMillies); 412 if (rc == 0) 413 return VERR_TIMEOUT; 414 if (rc > 0) 415 { 416 /* drain the pipe */ 417 if (PollFds[1].revents & POLLIN) 418 { 419 char szBuf[WAKE_UP_STRING_LEN]; 420 rc = RTFileRead(mWakeupPipeR, szBuf, sizeof(szBuf), NULL); 421 AssertRC(rc); 422 } 423 return VINF_SUCCESS; 424 } 425 return RTErrConvertFromErrno(errno); 426 } 427 428 429 int USBProxyServiceLinux::waitSysfs(RTMSINTERVAL aMillies) 430 { 431 #ifdef VBOX_USB_WITH_SYSFS 432 int rc = mWaiter.Wait(aMillies); 433 if (rc == VERR_TRY_AGAIN) 434 { 435 RTThreadYield(); 436 rc = VINF_SUCCESS; 437 } 438 return rc; 439 #else /* !VBOX_USB_WITH_SYSFS */ 440 return USBProxyService::wait(aMillies); 441 #endif /* !VBOX_USB_WITH_SYSFS */ 442 } 443 444 445 int USBProxyServiceLinux::interruptWait(void) 446 { 447 #ifdef VBOX_USB_WITH_SYSFS 448 LogFlowFunc(("mUsingUsbfsDevices=%d\n", mUsingUsbfsDevices)); 449 if (!mUsingUsbfsDevices) 450 { 451 mWaiter.Interrupt(); 452 LogFlowFunc(("Returning VINF_SUCCESS\n")); 453 return VINF_SUCCESS; 454 } 455 #endif /* VBOX_USB_WITH_SYSFS */ 456 int rc = RTFileWrite(mWakeupPipeW, WAKE_UP_STRING, WAKE_UP_STRING_LEN, NULL); 457 if (RT_SUCCESS(rc)) 458 RTFileFlush(mWakeupPipeW); 459 LogFlowFunc(("returning %Rrc\n", rc)); 460 return rc; 461 } 462 463 /** 464 * Free all the members of a USB device created by the Linux enumeration code. 465 * @note this duplicates a USBProxyService method which we needed access too 466 * without pulling in the rest of the proxy service code. 467 * 468 * @param pDevice Pointer to the device. 469 */ 470 static void deviceFreeMembers(PUSBDEVICE pDevice) 471 { 472 RTStrFree((char *)pDevice->pszManufacturer); 473 pDevice->pszManufacturer = NULL; 474 RTStrFree((char *)pDevice->pszProduct); 475 pDevice->pszProduct = NULL; 476 RTStrFree((char *)pDevice->pszSerialNumber); 477 pDevice->pszSerialNumber = NULL; 478 479 RTStrFree((char *)pDevice->pszAddress); 480 pDevice->pszAddress = NULL; 481 } 482 483 /** 484 * Free one USB device created by the Linux enumeration code. 485 * @note this duplicates a USBProxyService method which we needed access too 486 * without pulling in the rest of the proxy service code. 487 * 488 * @param pDevice Pointer to the device. 489 */ 490 static void deviceFree(PUSBDEVICE pDevice) 491 { 492 deviceFreeMembers(pDevice); 493 RTMemFree(pDevice); 494 } 115 return RTErrConvertFromErrno(errno); 116 return 0; 117 } 118 495 119 496 120 /** … … 1185 809 #ifdef VBOX_USB_WITH_SYSFS 1186 810 811 static void USBDevInfoCleanup(USBDeviceInfo *pSelf) 812 { 813 RTStrFree(pSelf->mDevice); 814 RTStrFree(pSelf->mSysfsPath); 815 pSelf->mDevice = pSelf->mSysfsPath = NULL; 816 VEC_CLEANUP_PTR(&pSelf->mvecpszInterfaces); 817 } 818 819 static int USBDevInfoInit(USBDeviceInfo *pSelf, const char *aDevice, 820 const char *aSystemID) 821 { 822 pSelf->mDevice = aDevice ? RTStrDup(aDevice) : NULL; 823 pSelf->mSysfsPath = aSystemID ? RTStrDup(aSystemID) : NULL; 824 VEC_INIT_PTR(&pSelf->mvecpszInterfaces, char *, RTStrFree); 825 if ((aDevice && !pSelf->mDevice) || (aSystemID && ! pSelf->mSysfsPath)) 826 { 827 USBDevInfoCleanup(pSelf); 828 return 0; 829 } 830 return 1; 831 } 832 833 #define USBDEVICE_MAJOR 189 834 835 /** Deduce the bus that a USB device is plugged into from the device node 836 * number. See drivers/usb/core/hub.c:usb_new_device as of Linux 2.6.20. */ 837 static unsigned usbBusFromDevNum(dev_t devNum) 838 { 839 AssertReturn(devNum, 0); 840 AssertReturn(major(devNum) == USBDEVICE_MAJOR, 0); 841 return (minor(devNum) >> 7) + 1; 842 } 843 844 845 /** Deduce the device number of a USB device on the bus from the device node 846 * number. See drivers/usb/core/hub.c:usb_new_device as of Linux 2.6.20. */ 847 static unsigned usbDeviceFromDevNum(dev_t devNum) 848 { 849 AssertReturn(devNum, 0); 850 AssertReturn(major(devNum) == USBDEVICE_MAJOR, 0); 851 return (minor(devNum) & 127) + 1; 852 } 853 854 855 /** 856 * If a file @a pcszNode from /sys/bus/usb/devices is a device rather than an 857 * interface add an element for the device to @a pvecDevInfo. 858 */ 859 static int addIfDevice(const char *pcszNode, 860 VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo) 861 { 862 const char *pcszFile = strrchr(pcszNode, '/'); 863 if (strchr(pcszFile, ':')) 864 return VINF_SUCCESS; 865 dev_t devnum = RTLinuxSysFsReadDevNumFile("%s/dev", pcszNode); 866 /* Sanity test of our static helpers */ 867 Assert(usbBusFromDevNum(makedev(USBDEVICE_MAJOR, 517)) == 5); 868 Assert(usbDeviceFromDevNum(makedev(USBDEVICE_MAJOR, 517)) == 6); 869 if (!devnum) 870 return VINF_SUCCESS; 871 char szDevPath[RTPATH_MAX]; 872 ssize_t cchDevPath; 873 cchDevPath = RTLinuxFindDevicePath(devnum, RTFS_TYPE_DEV_CHAR, 874 szDevPath, sizeof(szDevPath), 875 "/dev/bus/usb/%.3d/%.3d", 876 usbBusFromDevNum(devnum), 877 usbDeviceFromDevNum(devnum)); 878 if (cchDevPath < 0) 879 return VINF_SUCCESS; 880 881 USBDeviceInfo info; 882 if (USBDevInfoInit(&info, szDevPath, pcszNode)) 883 if (RT_SUCCESS(VEC_PUSH_BACK_OBJ(pvecDevInfo, USBDeviceInfo, 884 &info))) 885 return VINF_SUCCESS; 886 USBDevInfoCleanup(&info); 887 return VERR_NO_MEMORY; 888 } 889 890 /** The logic for testing whether a sysfs address corresponds to an 891 * interface of a device. Both must be referenced by their canonical 892 * sysfs paths. This is not tested, as the test requires file-system 893 * interaction. */ 894 static bool muiIsAnInterfaceOf(const char *pcszIface, const char *pcszDev) 895 { 896 size_t cchDev = strlen(pcszDev); 897 898 AssertPtr(pcszIface); 899 AssertPtr(pcszDev); 900 Assert(pcszIface[0] == '/'); 901 Assert(pcszDev[0] == '/'); 902 Assert(pcszDev[cchDev - 1] != '/'); 903 /* If this passes, pcszIface is at least cchDev long */ 904 if (strncmp(pcszIface, pcszDev, cchDev)) 905 return false; 906 /* If this passes, pcszIface is longer than cchDev */ 907 if (pcszIface[cchDev] != '/') 908 return false; 909 /* In sysfs an interface is an immediate subdirectory of the device */ 910 if (strchr(pcszIface + cchDev + 1, '/')) 911 return false; 912 /* And it always has a colon in its name */ 913 if (!strchr(pcszIface + cchDev + 1, ':')) 914 return false; 915 /* And hopefully we have now elimitated everything else */ 916 return true; 917 } 918 919 #ifdef DEBUG 920 # ifdef __cplusplus 921 /** Unit test the logic in muiIsAnInterfaceOf in debug builds. */ 922 class testIsAnInterfaceOf 923 { 924 public: 925 testIsAnInterfaceOf() 926 { 927 Assert(muiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0", 928 "/sys/devices/pci0000:00/0000:00:1a.0/usb3")); 929 Assert(!muiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-1", 930 "/sys/devices/pci0000:00/0000:00:1a.0/usb3")); 931 Assert(!muiIsAnInterfaceOf("/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0/driver", 932 "/sys/devices/pci0000:00/0000:00:1a.0/usb3")); 933 } 934 }; 935 static testIsAnInterfaceOf testIsAnInterfaceOfInst; 936 # endif /* __cplusplus */ 937 #endif /* DEBUG */ 938 939 /** 940 * Tell whether a file in /sys/bus/usb/devices is an interface rather than a 941 * device. To be used with getDeviceInfoFromSysfs(). 942 */ 943 static int addIfInterfaceOf(const char *pcszNode, USBDeviceInfo *pInfo) 944 { 945 if (!muiIsAnInterfaceOf(pcszNode, pInfo->mSysfsPath)) 946 return VINF_SUCCESS; 947 char *pszDup = (char *)RTStrDup(pcszNode); 948 if (pszDup) 949 if (RT_SUCCESS(VEC_PUSH_BACK_PTR(&pInfo->mvecpszInterfaces, 950 char *, pszDup))) 951 return VINF_SUCCESS; 952 RTStrFree(pszDup); 953 return VERR_NO_MEMORY; 954 } 955 956 /** Helper for readFilePaths(). Adds the entries from the open directory 957 * @a pDir to the vector @a pvecpchDevs using either the full path or the 958 * realpath() and skipping hidden files and files on which realpath() fails. */ 959 static int readFilePathsFromDir(const char *pcszPath, DIR *pDir, 960 VECTOR_PTR(char *) *pvecpchDevs) 961 { 962 struct dirent entry, *pResult; 963 int err, rc; 964 965 for (err = readdir_r(pDir, &entry, &pResult); pResult; 966 err = readdir_r(pDir, &entry, &pResult)) 967 { 968 char szPath[RTPATH_MAX + 1], szRealPath[RTPATH_MAX + 1], *pszPath; 969 if (entry.d_name[0] == '.') 970 continue; 971 if (snprintf(szPath, sizeof(szPath), "%s/%s", pcszPath, 972 entry.d_name) < 0) 973 return RTErrConvertFromErrno(errno); 974 pszPath = RTStrDup(realpath(szPath, szRealPath)); 975 if (!pszPath) 976 return VERR_NO_MEMORY; 977 if (RT_FAILURE(rc = VEC_PUSH_BACK_PTR(pvecpchDevs, char *, pszPath))) 978 return rc; 979 } 980 return RTErrConvertFromErrno(err); 981 } 982 983 /** 984 * Dump the names of a directory's entries into a vector of char pointers. 985 * 986 * @returns zero on success or (positive) posix error value. 987 * @param pcszPath the path to dump. 988 * @param pvecpchDevs an empty vector of char pointers - must be cleaned up 989 * by the caller even on failure. 990 * @param withRealPath whether to canonicalise the filename with realpath 991 */ 992 static int readFilePaths(const char *pcszPath, VECTOR_PTR(char *) *pvecpchDevs) 993 { 994 DIR *pDir; 995 int rc; 996 997 AssertPtrReturn(pvecpchDevs, EINVAL); 998 AssertReturn(VEC_SIZE_PTR(pvecpchDevs) == 0, EINVAL); 999 AssertPtrReturn(pcszPath, EINVAL); 1000 1001 pDir = opendir(pcszPath); 1002 if (!pDir) 1003 return RTErrConvertFromErrno(errno); 1004 rc = readFilePathsFromDir(pcszPath, pDir, pvecpchDevs); 1005 if (closedir(pDir) < 0 && RT_SUCCESS(rc)) 1006 rc = RTErrConvertFromErrno(errno); 1007 return rc; 1008 } 1009 1010 /** 1011 * Logic for USBSysfsEnumerateHostDevices. 1012 * @param pvecDevInfo vector of device information structures to add device 1013 * information to 1014 * @param pvecpchDevs empty scratch vector which will be freed by the caller 1015 */ 1016 static int doSysfsEnumerateHostDevices(VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo, 1017 VECTOR_PTR(char *) *pvecpchDevs) 1018 { 1019 char **ppszEntry; 1020 USBDeviceInfo *pInfo; 1021 int rc; 1022 1023 AssertPtrReturn(pvecDevInfo, VERR_INVALID_POINTER); 1024 LogFlowFunc (("pvecDevInfo=%p\n", pvecDevInfo)); 1025 1026 rc = readFilePaths("/sys/bus/usb/devices", pvecpchDevs); 1027 if (RT_FAILURE(rc)) 1028 return rc; 1029 VEC_FOR_EACH(pvecpchDevs, char *, ppszEntry) 1030 if (RT_FAILURE(rc = addIfDevice(*ppszEntry, pvecDevInfo))) 1031 return rc; 1032 VEC_FOR_EACH(pvecDevInfo, USBDeviceInfo, pInfo) 1033 VEC_FOR_EACH(pvecpchDevs, char *, ppszEntry) 1034 if (RT_FAILURE(rc = addIfInterfaceOf(*ppszEntry, pInfo))) 1035 return rc; 1036 return VINF_SUCCESS; 1037 } 1038 1039 static int USBSysfsEnumerateHostDevices(VECTOR_OBJ(USBDeviceInfo) *pvecDevInfo) 1040 { 1041 VECTOR_PTR(char *) vecpchDevs; 1042 int rc = VERR_NOT_IMPLEMENTED; 1043 1044 AssertReturn(VEC_SIZE_OBJ(pvecDevInfo) == 0, VERR_INVALID_PARAMETER); 1045 LogFlowFunc(("entered\n")); 1046 VEC_INIT_PTR(&vecpchDevs, char *, RTStrFree); 1047 rc = doSysfsEnumerateHostDevices(pvecDevInfo, &vecpchDevs); 1048 VEC_CLEANUP_PTR(&vecpchDevs); 1049 LogFlowFunc(("rc=%Rrc\n", rc)); 1050 return rc; 1051 } 1052 1187 1053 /** 1188 1054 * Helper function for extracting the port number on the parent device from … … 1455 1321 } 1456 1322 1457 /* We want a copy of the device node and sysfs paths guaranteed not to 1458 * contain double slashes, since we use a double slash as a separator in 1459 * the pszAddress field. */ 1460 char szDeviceClean[RTPATH_MAX]; 1461 char szSysfsClean[RTPATH_MAX]; 1323 /* We use a double slash as a separator in the pszAddress field. This is 1324 * alright as the two paths can't contain a slash due to the way we build 1325 * them. */ 1462 1326 char *pszAddress = NULL; 1463 if ( RT_SUCCESS(RTPathReal(pInfo->mDevice, szDeviceClean, 1464 sizeof(szDeviceClean))) 1465 && RT_SUCCESS(RTPathReal(pszSysfsPath, szSysfsClean, 1466 sizeof(szSysfsClean))) 1467 ) 1468 RTStrAPrintf(&pszAddress, "sysfs:%s//device:%s", szSysfsClean, 1469 szDeviceClean); 1327 RTStrAPrintf(&pszAddress, "sysfs:%s//device:%s", pszSysfsPath, 1328 pInfo->mDevice); 1470 1329 Dev->pszAddress = pszAddress; 1471 1330 … … 1534 1393 } 1535 1394 1536 staticPUSBDEVICE USBProxyLinuxGetDevices(const char *pcszUsbfsRoot)1395 PUSBDEVICE USBProxyLinuxGetDevices(const char *pcszUsbfsRoot) 1537 1396 { 1538 1397 if (pcszUsbfsRoot) … … 1541 1400 return getDevicesFromSysfs(); 1542 1401 } 1543 1544 PUSBDEVICE USBProxyServiceLinux::getDevices(void)1545 {1546 if (mUsingUsbfsDevices)1547 return USBProxyLinuxGetDevices(mUsbfsRoot.c_str());1548 else1549 return USBProxyLinuxGetDevices(NULL);1550 }1551 -
trunk/src/VBox/Main/linux/USBProxyServiceLinux.cpp
r32301 r32324 21 21 *******************************************************************************/ 22 22 #include "USBProxyService.h" 23 #include "USBGetDevices.h" 23 24 #include "Logging.h" 24 25 … … 151 152 mLastError = rc; 152 153 return S_OK; 153 }154 155 156 static int USBProxyLinuxCheckForUsbfs(const char *pcszDevices)157 {158 RTFILE File;159 int rc;160 161 rc = RTFileOpen(&File, pcszDevices, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);162 if (RT_SUCCESS(rc))163 {164 /*165 * Check that we're actually on the usbfs.166 */167 struct statfs StFS;168 if (!fstatfs(File, &StFS))169 {170 if (StFS.f_type == USBDEVICE_SUPER_MAGIC)171 return VINF_SUCCESS;172 else173 return VERR_NOT_FOUND;174 }175 else176 return RTErrConvertFromErrno(errno);177 }178 return rc;179 154 } 180 155 … … 461 436 } 462 437 463 /**464 * Free all the members of a USB device created by the Linux enumeration code.465 * @note this duplicates a USBProxyService method which we needed access too466 * without pulling in the rest of the proxy service code.467 *468 * @param pDevice Pointer to the device.469 */470 static void deviceFreeMembers(PUSBDEVICE pDevice)471 {472 RTStrFree((char *)pDevice->pszManufacturer);473 pDevice->pszManufacturer = NULL;474 RTStrFree((char *)pDevice->pszProduct);475 pDevice->pszProduct = NULL;476 RTStrFree((char *)pDevice->pszSerialNumber);477 pDevice->pszSerialNumber = NULL;478 479 RTStrFree((char *)pDevice->pszAddress);480 pDevice->pszAddress = NULL;481 }482 483 /**484 * Free one USB device created by the Linux enumeration code.485 * @note this duplicates a USBProxyService method which we needed access too486 * without pulling in the rest of the proxy service code.487 *488 * @param pDevice Pointer to the device.489 */490 static void deviceFree(PUSBDEVICE pDevice)491 {492 deviceFreeMembers(pDevice);493 RTMemFree(pDevice);494 }495 496 /**497 * "reads" the number suffix. It's more like validating it and498 * skipping the necessary number of chars.499 */500 static int usbReadSkipSuffix(char **ppszNext)501 {502 char *pszNext = *ppszNext;503 if (!RT_C_IS_SPACE(*pszNext) && *pszNext)504 {505 /* skip unit */506 if (pszNext[0] == 'm' && pszNext[1] == 's')507 pszNext += 2;508 else if (pszNext[0] == 'm' && pszNext[1] == 'A')509 pszNext += 2;510 511 /* skip parenthesis */512 if (*pszNext == '(')513 {514 pszNext = strchr(pszNext, ')');515 if (!pszNext++)516 {517 AssertMsgFailed(("*ppszNext=%s\n", *ppszNext));518 return VERR_PARSE_ERROR;519 }520 }521 522 /* blank or end of the line. */523 if (!RT_C_IS_SPACE(*pszNext) && *pszNext)524 {525 AssertMsgFailed(("pszNext=%s\n", pszNext));526 return VERR_PARSE_ERROR;527 }528 529 /* it's ok. */530 *ppszNext = pszNext;531 }532 533 return VINF_SUCCESS;534 }535 536 537 /**538 * Reads a USB number returning the number and the position of the next character to parse.539 */540 static int usbReadNum(const char *pszValue, unsigned uBase, uint32_t u32Mask, PCUSBSUFF paSuffs, void *pvNum, char **ppszNext)541 {542 /*543 * Initialize return value to zero and strip leading spaces.544 */545 switch (u32Mask)546 {547 case 0xff: *(uint8_t *)pvNum = 0; break;548 case 0xffff: *(uint16_t *)pvNum = 0; break;549 case 0xffffffff: *(uint32_t *)pvNum = 0; break;550 }551 pszValue = RTStrStripL(pszValue);552 if (*pszValue)553 {554 /*555 * Try convert the number.556 */557 char *pszNext;558 uint32_t u32 = 0;559 RTStrToUInt32Ex(pszValue, &pszNext, uBase, &u32);560 if (pszNext == pszValue)561 {562 AssertMsgFailed(("pszValue=%d\n", pszValue));563 return VERR_NO_DATA;564 }565 566 /*567 * Check the range.568 */569 if (u32 & ~u32Mask)570 {571 AssertMsgFailed(("pszValue=%d u32=%#x lMask=%#x\n", pszValue, u32, u32Mask));572 return VERR_OUT_OF_RANGE;573 }574 575 /*576 * Validate and skip stuff following the number.577 */578 if (paSuffs)579 {580 if (!RT_C_IS_SPACE(*pszNext) && *pszNext)581 {582 for (PCUSBSUFF pSuff = paSuffs; pSuff->szSuff[0]; pSuff++)583 {584 if ( !strncmp(pSuff->szSuff, pszNext, pSuff->cchSuff)585 && (!pszNext[pSuff->cchSuff] || RT_C_IS_SPACE(pszNext[pSuff->cchSuff])))586 {587 if (pSuff->uDiv)588 u32 /= pSuff->uDiv;589 else590 u32 *= pSuff->uMul;591 break;592 }593 }594 }595 }596 else597 {598 int rc = usbReadSkipSuffix(&pszNext);599 if (RT_FAILURE(rc))600 return rc;601 }602 603 *ppszNext = pszNext;604 605 /*606 * Set the value.607 */608 switch (u32Mask)609 {610 case 0xff: *(uint8_t *)pvNum = (uint8_t)u32; break;611 case 0xffff: *(uint16_t *)pvNum = (uint16_t)u32; break;612 case 0xffffffff: *(uint32_t *)pvNum = (uint32_t)u32; break;613 }614 }615 return VINF_SUCCESS;616 }617 618 619 static int usbRead8(const char *pszValue, unsigned uBase, uint8_t *pu8, char **ppszNext)620 {621 return usbReadNum(pszValue, uBase, 0xff, NULL, pu8, ppszNext);622 }623 624 625 static int usbRead16(const char *pszValue, unsigned uBase, uint16_t *pu16, char **ppszNext)626 {627 return usbReadNum(pszValue, uBase, 0xffff, NULL, pu16, ppszNext);628 }629 630 631 #if 0632 static int usbRead16Suff(const char *pszValue, unsigned uBase, PCUSBSUFF paSuffs, uint16_t *pu16, char **ppszNext)633 {634 return usbReadNum(pszValue, uBase, 0xffff, paSuffs, pu16, ppszNext);635 }636 #endif637 638 639 /**640 * Reads a USB BCD number returning the number and the position of the next character to parse.641 * The returned number contains the integer part in the high byte and the decimal part in the low byte.642 */643 static int usbReadBCD(const char *pszValue, unsigned uBase, uint16_t *pu16, char **ppszNext)644 {645 /*646 * Initialize return value to zero and strip leading spaces.647 */648 *pu16 = 0;649 pszValue = RTStrStripL(pszValue);650 if (*pszValue)651 {652 /*653 * Try convert the number.654 */655 /* integer part */656 char *pszNext;657 uint32_t u32Int = 0;658 RTStrToUInt32Ex(pszValue, &pszNext, uBase, &u32Int);659 if (pszNext == pszValue)660 {661 AssertMsgFailed(("pszValue=%s\n", pszValue));662 return VERR_NO_DATA;663 }664 if (u32Int & ~0xff)665 {666 AssertMsgFailed(("pszValue=%s u32Int=%#x (int)\n", pszValue, u32Int));667 return VERR_OUT_OF_RANGE;668 }669 670 /* skip dot and read decimal part */671 if (*pszNext != '.')672 {673 AssertMsgFailed(("pszValue=%s pszNext=%s (int)\n", pszValue, pszNext));674 return VERR_PARSE_ERROR;675 }676 char *pszValue2 = RTStrStripL(pszNext + 1);677 uint32_t u32Dec = 0;678 RTStrToUInt32Ex(pszValue2, &pszNext, uBase, &u32Dec);679 if (pszNext == pszValue)680 {681 AssertMsgFailed(("pszValue=%s\n", pszValue));682 return VERR_NO_DATA;683 }684 if (u32Dec & ~0xff)685 {686 AssertMsgFailed(("pszValue=%s u32Dec=%#x\n", pszValue, u32Dec));687 return VERR_OUT_OF_RANGE;688 }689 690 /*691 * Validate and skip stuff following the number.692 */693 int rc = usbReadSkipSuffix(&pszNext);694 if (RT_FAILURE(rc))695 return rc;696 *ppszNext = pszNext;697 698 /*699 * Set the value.700 */701 *pu16 = (uint16_t)u32Int << 8 | (uint16_t)u32Dec;702 }703 return VINF_SUCCESS;704 }705 706 707 /**708 * Reads a string, i.e. allocates memory and copies it.709 *710 * We assume that a string is pure ASCII, if that's not the case711 * tell me how to figure out the codeset please.712 */713 static int usbReadStr(const char *pszValue, const char **ppsz)714 {715 if (*ppsz)716 RTStrFree((char *)*ppsz);717 *ppsz = RTStrDup(pszValue);718 if (*ppsz)719 return VINF_SUCCESS;720 return VERR_NO_MEMORY;721 }722 723 724 /**725 * Skips the current property.726 */727 static char *usbReadSkip(char *pszValue)728 {729 char *psz = strchr(pszValue, '=');730 if (psz)731 psz = strchr(psz + 1, '=');732 if (!psz)733 return strchr(pszValue, '\0');734 while (psz > pszValue && !RT_C_IS_SPACE(psz[-1]))735 psz--;736 Assert(psz > pszValue);737 return psz;738 }739 740 741 /**742 * Determine the USB speed.743 */744 static int usbReadSpeed(const char *pszValue, USBDEVICESPEED *pSpd, char **ppszNext)745 {746 pszValue = RTStrStripL(pszValue);747 /* verified with Linux 2.4.0 ... Linux 2.6.25 */748 if (!strncmp(pszValue, "1.5", 3))749 *pSpd = USBDEVICESPEED_LOW;750 else if (!strncmp(pszValue, "12 ", 3))751 *pSpd = USBDEVICESPEED_FULL;752 else if (!strncmp(pszValue, "480", 3))753 *pSpd = USBDEVICESPEED_HIGH;754 else755 *pSpd = USBDEVICESPEED_UNKNOWN;756 while (pszValue[0] != '\0' && !RT_C_IS_SPACE(pszValue[0]))757 pszValue++;758 *ppszNext = (char *)pszValue;759 return VINF_SUCCESS;760 }761 762 763 /**764 * Compare a prefix and returns pointer to the char following it if it matches.765 */766 static char *usbPrefix(char *psz, const char *pszPref, size_t cchPref)767 {768 if (strncmp(psz, pszPref, cchPref))769 return NULL;770 return psz + cchPref;771 }772 773 774 /**775 * Does some extra checks to improve the detected device state.776 *777 * We cannot distinguish between USED_BY_HOST_CAPTURABLE and778 * USED_BY_GUEST, HELD_BY_PROXY all that well and it shouldn't be779 * necessary either.780 *781 * We will however, distinguish between the device we have permissions782 * to open and those we don't. This is necessary for two reasons.783 *784 * Firstly, because it's futile to even attempt opening a device which we785 * don't have access to, it only serves to confuse the user. (That said,786 * it might also be a bit confusing for the user to see that a USB device787 * is grayed out with no further explanation, and no way of generating an788 * error hinting at why this is the case.)789 *790 * Secondly and more importantly, we're racing against udevd with respect791 * to permissions and group settings on newly plugged devices. When we792 * detect a new device that we cannot access we will poll on it for a few793 * seconds to give udevd time to fix it. The polling is actually triggered794 * in the 'new device' case in the compare loop.795 *796 * The USBDEVICESTATE_USED_BY_HOST state is only used for this no-access797 * case, while USBDEVICESTATE_UNSUPPORTED is only used in the 'hub' case.798 * When it's neither of these, we set USBDEVICESTATE_UNUSED or799 * USBDEVICESTATE_USED_BY_HOST_CAPTURABLE depending on whether there is800 * a driver associated with any of the interfaces.801 *802 * All except the access check and a special idVendor == 0 precaution803 * is handled at parse time.804 *805 * @returns The adjusted state.806 * @param pDevice The device.807 */808 static USBDEVICESTATE usbDeterminState(PCUSBDEVICE pDevice)809 {810 /*811 * If it's already flagged as unsupported, there is nothing to do.812 */813 USBDEVICESTATE enmState = pDevice->enmState;814 if (enmState == USBDEVICESTATE_UNSUPPORTED)815 return USBDEVICESTATE_UNSUPPORTED;816 817 /*818 * Root hubs and similar doesn't have any vendor id, just819 * refuse these device.820 */821 if (!pDevice->idVendor)822 return USBDEVICESTATE_UNSUPPORTED;823 824 /*825 * Check if we've got access to the device, if we haven't flag826 * it as used-by-host.827 */828 #ifndef VBOX_USB_WITH_SYSFS829 const char *pszAddress = pDevice->pszAddress;830 #else831 if (pDevice->pszAddress == NULL)832 /* We can't do much with the device without an address. */833 return USBDEVICESTATE_UNSUPPORTED;834 const char *pszAddress = strstr(pDevice->pszAddress, "//device:");835 pszAddress = pszAddress != NULL836 ? pszAddress + sizeof("//device:") - 1837 : pDevice->pszAddress;838 #endif839 if ( access(pszAddress, R_OK | W_OK) != 0840 && errno == EACCES)841 return USBDEVICESTATE_USED_BY_HOST;842 843 #ifdef VBOX_USB_WITH_SYSFS844 /**845 * @todo Check that any other essential fields are present and mark as846 * invalid if not. Particularly to catch the case where the device was847 * unplugged while we were reading in its properties.848 */849 #endif850 851 return enmState;852 }853 854 855 /** Just a worker for USBProxyServiceLinux::getDevices that avoids some code duplication. */856 static int addDeviceToChain(PUSBDEVICE pDev, PUSBDEVICE *ppFirst, PUSBDEVICE **pppNext, const char *pcszUsbfsRoot, int rc)857 {858 /* usbDeterminState requires the address. */859 PUSBDEVICE pDevNew = (PUSBDEVICE)RTMemDup(pDev, sizeof(*pDev));860 if (pDevNew)861 {862 RTStrAPrintf((char **)&pDevNew->pszAddress, "%s/%03d/%03d", pcszUsbfsRoot, pDevNew->bBus, pDevNew->bDevNum);863 if (pDevNew->pszAddress)864 {865 pDevNew->enmState = usbDeterminState(pDevNew);866 if (pDevNew->enmState != USBDEVICESTATE_UNSUPPORTED)867 {868 if (*pppNext)869 **pppNext = pDevNew;870 else871 *ppFirst = pDevNew;872 *pppNext = &pDevNew->pNext;873 }874 else875 deviceFree(pDevNew);876 }877 else878 {879 deviceFree(pDevNew);880 rc = VERR_NO_MEMORY;881 }882 }883 else884 {885 rc = VERR_NO_MEMORY;886 deviceFreeMembers(pDev);887 }888 889 return rc;890 }891 892 893 static int openDevicesFile(const char *pcszUsbfsRoot, FILE **ppFile)894 {895 char *pszPath;896 FILE *pFile;897 RTStrAPrintf(&pszPath, "%s/devices", pcszUsbfsRoot);898 if (!pszPath)899 return VERR_NO_MEMORY;900 pFile = fopen(pszPath, "r");901 RTStrFree(pszPath);902 if (!pFile)903 return RTErrConvertFromErrno(errno);904 *ppFile = pFile;905 return VINF_SUCCESS;906 }907 908 /**909 * USBProxyService::getDevices() implementation for usbfs.910 */911 static PUSBDEVICE getDevicesFromUsbfs(const char *pcszUsbfsRoot)912 {913 PUSBDEVICE pFirst = NULL;914 FILE *pFile;915 int rc;916 rc = openDevicesFile(pcszUsbfsRoot, &pFile);917 if (RT_SUCCESS(rc))918 {919 PUSBDEVICE *ppNext = NULL;920 int cHits = 0;921 char szLine[1024];922 USBDEVICE Dev;923 RT_ZERO(Dev);924 Dev.enmState = USBDEVICESTATE_UNUSED;925 926 rc = VINF_SUCCESS;927 while ( RT_SUCCESS(rc)928 && fgets(szLine, sizeof(szLine), pFile))929 {930 char *psz;931 char *pszValue;932 933 /* validate and remove the trailing newline. */934 psz = strchr(szLine, '\0');935 if (psz[-1] != '\n' && !feof(pFile))936 {937 AssertMsgFailed(("Line too long. (cch=%d)\n", strlen(szLine)));938 continue;939 }940 941 /* strip */942 psz = RTStrStrip(szLine);943 if (!*psz)944 continue;945 946 /*947 * Interpret the line.948 * (Ordered by normal occurence.)949 */950 char ch = psz[0];951 if (psz[1] != ':')952 continue;953 psz = RTStrStripL(psz + 3);954 #define PREFIX(str) ( (pszValue = usbPrefix(psz, str, sizeof(str) - 1)) != NULL )955 switch (ch)956 {957 /*958 * T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd959 * | | | | | | | | |__MaxChildren960 * | | | | | | | |__Device Speed in Mbps961 * | | | | | | |__DeviceNumber962 * | | | | | |__Count of devices at this level963 * | | | | |__Connector/Port on Parent for this device964 * | | | |__Parent DeviceNumber965 * | | |__Level in topology for this bus966 * | |__Bus number967 * |__Topology info tag968 */969 case 'T':970 /* add */971 AssertMsg(cHits >= 3 || cHits == 0, ("cHits=%d\n", cHits));972 if (cHits >= 3)973 rc = addDeviceToChain(&Dev, &pFirst, &ppNext, pcszUsbfsRoot, rc);974 else975 deviceFreeMembers(&Dev);976 977 /* Reset device state */978 memset(&Dev, 0, sizeof (Dev));979 Dev.enmState = USBDEVICESTATE_UNUSED;980 cHits = 1;981 982 /* parse the line. */983 while (*psz && RT_SUCCESS(rc))984 {985 if (PREFIX("Bus="))986 rc = usbRead8(pszValue, 10, &Dev.bBus, &psz);987 else if (PREFIX("Port="))988 rc = usbRead8(pszValue, 10, &Dev.bPort, &psz);989 else if (PREFIX("Spd="))990 rc = usbReadSpeed(pszValue, &Dev.enmSpeed, &psz);991 else if (PREFIX("Dev#="))992 rc = usbRead8(pszValue, 10, &Dev.bDevNum, &psz);993 else994 psz = usbReadSkip(psz);995 psz = RTStrStripL(psz);996 }997 break;998 999 /*1000 * Bandwidth info:1001 * B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd1002 * | | | |__Number of isochronous requests1003 * | | |__Number of interrupt requests1004 * | |__Total Bandwidth allocated to this bus1005 * |__Bandwidth info tag1006 */1007 case 'B':1008 break;1009 1010 /*1011 * D: Ver=x.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd1012 * | | | | | | |__NumberConfigurations1013 * | | | | | |__MaxPacketSize of Default Endpoint1014 * | | | | |__DeviceProtocol1015 * | | | |__DeviceSubClass1016 * | | |__DeviceClass1017 * | |__Device USB version1018 * |__Device info tag #11019 */1020 case 'D':1021 while (*psz && RT_SUCCESS(rc))1022 {1023 if (PREFIX("Ver="))1024 rc = usbReadBCD(pszValue, 16, &Dev.bcdUSB, &psz);1025 else if (PREFIX("Cls="))1026 {1027 rc = usbRead8(pszValue, 16, &Dev.bDeviceClass, &psz);1028 if (RT_SUCCESS(rc) && Dev.bDeviceClass == 9 /* HUB */)1029 Dev.enmState = USBDEVICESTATE_UNSUPPORTED;1030 }1031 else if (PREFIX("Sub="))1032 rc = usbRead8(pszValue, 16, &Dev.bDeviceSubClass, &psz);1033 else if (PREFIX("Prot="))1034 rc = usbRead8(pszValue, 16, &Dev.bDeviceProtocol, &psz);1035 //else if (PREFIX("MxPS="))1036 // rc = usbRead16(pszValue, 10, &Dev.wMaxPacketSize, &psz);1037 else if (PREFIX("#Cfgs="))1038 rc = usbRead8(pszValue, 10, &Dev.bNumConfigurations, &psz);1039 else1040 psz = usbReadSkip(psz);1041 psz = RTStrStripL(psz);1042 }1043 cHits++;1044 break;1045 1046 /*1047 * P: Vendor=xxxx ProdID=xxxx Rev=xx.xx1048 * | | | |__Product revision number1049 * | | |__Product ID code1050 * | |__Vendor ID code1051 * |__Device info tag #21052 */1053 case 'P':1054 while (*psz && RT_SUCCESS(rc))1055 {1056 if (PREFIX("Vendor="))1057 rc = usbRead16(pszValue, 16, &Dev.idVendor, &psz);1058 else if (PREFIX("ProdID="))1059 rc = usbRead16(pszValue, 16, &Dev.idProduct, &psz);1060 else if (PREFIX("Rev="))1061 rc = usbReadBCD(pszValue, 16, &Dev.bcdDevice, &psz);1062 else1063 psz = usbReadSkip(psz);1064 psz = RTStrStripL(psz);1065 }1066 cHits++;1067 break;1068 1069 /*1070 * String.1071 */1072 case 'S':1073 if (PREFIX("Manufacturer="))1074 rc = usbReadStr(pszValue, &Dev.pszManufacturer);1075 else if (PREFIX("Product="))1076 rc = usbReadStr(pszValue, &Dev.pszProduct);1077 else if (PREFIX("SerialNumber="))1078 {1079 rc = usbReadStr(pszValue, &Dev.pszSerialNumber);1080 if (RT_SUCCESS(rc))1081 Dev.u64SerialHash = USBLibHashSerial(pszValue);1082 }1083 break;1084 1085 /*1086 * C:* #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA1087 * | | | | | |__MaxPower in mA1088 * | | | | |__Attributes1089 * | | | |__ConfiguratioNumber1090 * | | |__NumberOfInterfaces1091 * | |__ "*" indicates the active configuration (others are " ")1092 * |__Config info tag1093 */1094 case 'C':1095 break;1096 1097 /*1098 * I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss1099 * | | | | | | | |__Driver name1100 * | | | | | | | or "(none)"1101 * | | | | | | |__InterfaceProtocol1102 * | | | | | |__InterfaceSubClass1103 * | | | | |__InterfaceClass1104 * | | | |__NumberOfEndpoints1105 * | | |__AlternateSettingNumber1106 * | |__InterfaceNumber1107 * |__Interface info tag1108 */1109 case 'I':1110 {1111 /* Check for thing we don't support. */1112 while (*psz && RT_SUCCESS(rc))1113 {1114 if (PREFIX("Driver="))1115 {1116 const char *pszDriver = NULL;1117 rc = usbReadStr(pszValue, &pszDriver);1118 if ( !pszDriver1119 || !*pszDriver1120 || !strcmp(pszDriver, "(none)")1121 || !strcmp(pszDriver, "(no driver)"))1122 /* no driver */;1123 else if (!strcmp(pszDriver, "hub"))1124 Dev.enmState = USBDEVICESTATE_UNSUPPORTED;1125 else if (Dev.enmState == USBDEVICESTATE_UNUSED)1126 Dev.enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;1127 RTStrFree((char *)pszDriver);1128 break; /* last attrib */1129 }1130 else if (PREFIX("Cls="))1131 {1132 uint8_t bInterfaceClass;1133 rc = usbRead8(pszValue, 16, &bInterfaceClass, &psz);1134 if (RT_SUCCESS(rc) && bInterfaceClass == 9 /* HUB */)1135 Dev.enmState = USBDEVICESTATE_UNSUPPORTED;1136 }1137 else1138 psz = usbReadSkip(psz);1139 psz = RTStrStripL(psz);1140 }1141 break;1142 }1143 1144 1145 /*1146 * E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms1147 * | | | | |__Interval (max) between transfers1148 * | | | |__EndpointMaxPacketSize1149 * | | |__Attributes(EndpointType)1150 * | |__EndpointAddress(I=In,O=Out)1151 * |__Endpoint info tag1152 */1153 case 'E':1154 break;1155 1156 }1157 #undef PREFIX1158 } /* parse loop */1159 1160 /*1161 * Add the current entry.1162 */1163 AssertMsg(cHits >= 3 || cHits == 0, ("cHits=%d\n", cHits));1164 if (cHits >= 3)1165 rc = addDeviceToChain(&Dev, &pFirst, &ppNext, pcszUsbfsRoot, rc);1166 1167 /*1168 * Success?1169 */1170 if (RT_FAILURE(rc))1171 {1172 while (pFirst)1173 {1174 PUSBDEVICE pFree = pFirst;1175 pFirst = pFirst->pNext;1176 deviceFree(pFree);1177 }1178 }1179 }1180 if (RT_FAILURE(rc))1181 LogFlow(("USBProxyServiceLinux::getDevices: rc=%Rrc\n", rc));1182 return pFirst;1183 }1184 1185 #ifdef VBOX_USB_WITH_SYSFS1186 1187 /**1188 * Helper function for extracting the port number on the parent device from1189 * the sysfs path value.1190 *1191 * The sysfs path is a chain of elements separated by forward slashes, and for1192 * USB devices, the last element in the chain takes the form1193 * <port>-<port>.[...].<port>[:<config>.<interface>]1194 * where the first <port> is the port number on the root hub, and the following1195 * (optional) ones are the port numbers on any other hubs between the device1196 * and the root hub. The last part (:<config.interface>) is only present for1197 * interfaces, not for devices. This API should only be called for devices.1198 * For compatibility with usbfs, which enumerates from zero up, we subtract one1199 * from the port number.1200 *1201 * For root hubs, the last element in the chain takes the form1202 * usb<hub number>1203 * and usbfs always returns port number zero.1204 *1205 * @returns VBox status. pu8Port is set on success.1206 * @param pszPath The sysfs path to parse.1207 * @param pu8Port Where to store the port number.1208 */1209 static int usbGetPortFromSysfsPath(const char *pszPath, uint8_t *pu8Port)1210 {1211 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);1212 AssertPtrReturn(pu8Port, VERR_INVALID_POINTER);1213 1214 /*1215 * This should not be possible until we get PCs with USB as their primary bus.1216 * Note: We don't assert this, as we don't expect the caller to validate the1217 * sysfs path.1218 */1219 const char *pszLastComp = strrchr(pszPath, '/');1220 if (!pszLastComp)1221 {1222 Log(("usbGetPortFromSysfsPath(%s): failed [1]\n", pszPath));1223 return VERR_INVALID_PARAMETER;1224 }1225 pszLastComp++; /* skip the slash */1226 1227 /*1228 * This API should not be called for interfaces, so the last component1229 * of the path should not contain a colon. We *do* assert this, as it1230 * might indicate a caller bug.1231 */1232 AssertMsgReturn(strchr(pszLastComp, ':') == NULL, ("%s\n", pszPath), VERR_INVALID_PARAMETER);1233 1234 /*1235 * Look for the start of the last number.1236 */1237 const char *pchDash = strrchr(pszLastComp, '-');1238 const char *pchDot = strrchr(pszLastComp, '.');1239 if (!pchDash && !pchDot)1240 {1241 /* No -/. so it must be a root hub. Check that it's usb<something>. */1242 if (strncmp(pszLastComp, "usb", sizeof("usb") - 1) != 0)1243 {1244 Log(("usbGetPortFromSysfsPath(%s): failed [2]\n", pszPath));1245 return VERR_INVALID_PARAMETER;1246 }1247 return VERR_NOT_SUPPORTED;1248 }1249 else1250 {1251 const char *pszLastPort = pchDot != NULL1252 ? pchDot + 11253 : pchDash + 1;1254 int rc = RTStrToUInt8Full(pszLastPort, 10, pu8Port);1255 if (rc != VINF_SUCCESS)1256 {1257 Log(("usbGetPortFromSysfsPath(%s): failed [3], rc=%Rrc\n", pszPath, rc));1258 return VERR_INVALID_PARAMETER;1259 }1260 if (*pu8Port == 0)1261 {1262 Log(("usbGetPortFromSysfsPath(%s): failed [4]\n", pszPath));1263 return VERR_INVALID_PARAMETER;1264 }1265 1266 /* usbfs compatibility, 0-based port number. */1267 *pu8Port -= 1;1268 }1269 return VINF_SUCCESS;1270 }1271 1272 1273 /**1274 * Dumps a USBDEVICE structure to the log using LogLevel 3.1275 * @param pDev The structure to log.1276 * @todo This is really common code.1277 */1278 DECLINLINE(void) usbLogDevice(PUSBDEVICE pDev)1279 {1280 NOREF(pDev);1281 1282 Log3(("USB device:\n"));1283 Log3(("Product: %s (%x)\n", pDev->pszProduct, pDev->idProduct));1284 Log3(("Manufacturer: %s (Vendor ID %x)\n", pDev->pszManufacturer, pDev->idVendor));1285 Log3(("Serial number: %s (%llx)\n", pDev->pszSerialNumber, pDev->u64SerialHash));1286 Log3(("Device revision: %d\n", pDev->bcdDevice));1287 Log3(("Device class: %x\n", pDev->bDeviceClass));1288 Log3(("Device subclass: %x\n", pDev->bDeviceSubClass));1289 Log3(("Device protocol: %x\n", pDev->bDeviceProtocol));1290 Log3(("USB version number: %d\n", pDev->bcdUSB));1291 Log3(("Device speed: %s\n",1292 pDev->enmSpeed == USBDEVICESPEED_UNKNOWN ? "unknown"1293 : pDev->enmSpeed == USBDEVICESPEED_LOW ? "1.5 MBit/s"1294 : pDev->enmSpeed == USBDEVICESPEED_FULL ? "12 MBit/s"1295 : pDev->enmSpeed == USBDEVICESPEED_HIGH ? "480 MBit/s"1296 : pDev->enmSpeed == USBDEVICESPEED_VARIABLE ? "variable"1297 : "invalid"));1298 Log3(("Number of configurations: %d\n", pDev->bNumConfigurations));1299 Log3(("Bus number: %d\n", pDev->bBus));1300 Log3(("Port number: %d\n", pDev->bPort));1301 Log3(("Device number: %d\n", pDev->bDevNum));1302 Log3(("Device state: %s\n",1303 pDev->enmState == USBDEVICESTATE_UNSUPPORTED ? "unsupported"1304 : pDev->enmState == USBDEVICESTATE_USED_BY_HOST ? "in use by host"1305 : pDev->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE ? "in use by host, possibly capturable"1306 : pDev->enmState == USBDEVICESTATE_UNUSED ? "not in use"1307 : pDev->enmState == USBDEVICESTATE_HELD_BY_PROXY ? "held by proxy"1308 : pDev->enmState == USBDEVICESTATE_USED_BY_GUEST ? "used by guest"1309 : "invalid"));1310 Log3(("OS device address: %s\n", pDev->pszAddress));1311 }1312 1313 /**1314 * In contrast to usbReadBCD() this function can handle BCD values without1315 * a decimal separator. This is necessary for parsing bcdDevice.1316 * @param pszBuf Pointer to the string buffer.1317 * @param pu15 Pointer to the return value.1318 * @returns IPRT status code.1319 */1320 static int convertSysfsStrToBCD(const char *pszBuf, uint16_t *pu16)1321 {1322 char *pszNext;1323 int32_t i32;1324 1325 pszBuf = RTStrStripL(pszBuf);1326 int rc = RTStrToInt32Ex(pszBuf, &pszNext, 16, &i32);1327 if ( RT_FAILURE(rc)1328 || rc == VWRN_NUMBER_TOO_BIG1329 || i32 < 0)1330 return VERR_NUMBER_TOO_BIG;1331 if (*pszNext == '.')1332 {1333 if (i32 > 255)1334 return VERR_NUMBER_TOO_BIG;1335 int32_t i32Lo;1336 rc = RTStrToInt32Ex(pszNext+1, &pszNext, 16, &i32Lo);1337 if ( RT_FAILURE(rc)1338 || rc == VWRN_NUMBER_TOO_BIG1339 || i32Lo > 2551340 || i32Lo < 0)1341 return VERR_NUMBER_TOO_BIG;1342 i32 = (i32 << 8) | i32Lo;1343 }1344 if ( i32 > 655351345 || (*pszNext != '\0' && *pszNext != ' '))1346 return VERR_NUMBER_TOO_BIG;1347 1348 *pu16 = (uint16_t)i32;1349 return VINF_SUCCESS;1350 }1351 1352 #endif /* VBOX_USB_WITH_SYSFS */1353 1354 static void fillInDeviceFromSysfs(USBDEVICE *Dev, USBDeviceInfo *pInfo)1355 {1356 int rc;1357 const char *pszSysfsPath = pInfo->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 device1372 * will need, and insane devices can be unsupported1373 * 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 else1382 Dev->enmSpeed = !strcmp(szBuf, "1.5") ? USBDEVICESPEED_LOW1383 : !strcmp(szBuf, "12") ? USBDEVICESPEED_FULL1384 : !strcmp(szBuf, "480") ? USBDEVICESPEED_HIGH1385 : 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 else1392 {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 else1406 {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 **ppszIf;1444 VEC_FOR_EACH(&pInfo->mvecpszInterfaces, char *, ppszIf)1445 {1446 ssize_t cb = RTLinuxSysFsGetLinkDest(szBuf, sizeof(szBuf), "%s/driver",1447 *ppszIf);1448 if (cb > 0 && Dev->enmState != USBDEVICESTATE_UNSUPPORTED)1449 Dev->enmState = (strcmp(szBuf, "hub") == 0)1450 ? USBDEVICESTATE_UNSUPPORTED1451 : USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;1452 if (RTLinuxSysFsReadIntFile(16, "%s/bInterfaceClass",1453 *ppszIf) == 9 /* hub */)1454 Dev->enmState = USBDEVICESTATE_UNSUPPORTED;1455 }1456 1457 /* We want a copy of the device node and sysfs paths guaranteed not to1458 * contain double slashes, since we use a double slash as a separator in1459 * the pszAddress field. */1460 char szDeviceClean[RTPATH_MAX];1461 char szSysfsClean[RTPATH_MAX];1462 char *pszAddress = NULL;1463 if ( RT_SUCCESS(RTPathReal(pInfo->mDevice, szDeviceClean,1464 sizeof(szDeviceClean)))1465 && RT_SUCCESS(RTPathReal(pszSysfsPath, szSysfsClean,1466 sizeof(szSysfsClean)))1467 )1468 RTStrAPrintf(&pszAddress, "sysfs:%s//device:%s", szSysfsClean,1469 szDeviceClean);1470 Dev->pszAddress = pszAddress;1471 1472 /* Work out from the data collected whether we can support this device. */1473 Dev->enmState = usbDeterminState(Dev);1474 usbLogDevice(Dev);1475 }1476 1477 /**1478 * USBProxyService::getDevices() implementation for sysfs.1479 */1480 static PUSBDEVICE getDevicesFromSysfs(void)1481 {1482 #ifdef VBOX_USB_WITH_SYSFS1483 /* Add each of the devices found to the chain. */1484 PUSBDEVICE pFirst = NULL;1485 PUSBDEVICE pLast = NULL;1486 VECTOR_OBJ(USBDeviceInfo) vecDevInfo;1487 USBDeviceInfo *pInfo;1488 int rc;1489 1490 VEC_INIT_OBJ(&vecDevInfo, USBDeviceInfo, USBDevInfoCleanup);1491 rc = USBSysfsEnumerateHostDevices(&vecDevInfo);1492 if (RT_FAILURE(rc))1493 return NULL;1494 VEC_FOR_EACH(&vecDevInfo, USBDeviceInfo, pInfo)1495 {1496 USBDEVICE *Dev = (USBDEVICE *)RTMemAllocZ(sizeof(USBDEVICE));1497 if (!Dev)1498 rc = VERR_NO_MEMORY;1499 if (RT_SUCCESS(rc))1500 {1501 fillInDeviceFromSysfs(Dev, pInfo);1502 }1503 if ( RT_SUCCESS(rc)1504 && Dev->enmState != USBDEVICESTATE_UNSUPPORTED1505 && Dev->pszAddress != NULL1506 )1507 {1508 if (pLast != NULL)1509 {1510 pLast->pNext = Dev;1511 pLast = pLast->pNext;1512 }1513 else1514 pFirst = pLast = Dev;1515 }1516 else1517 deviceFree(Dev);1518 if (RT_FAILURE(rc))1519 break;1520 }1521 if (RT_FAILURE(rc))1522 while (pFirst)1523 {1524 PUSBDEVICE pNext = pFirst->pNext;1525 deviceFree(pFirst);1526 pFirst = pNext;1527 }1528 1529 VEC_CLEANUP_OBJ(&vecDevInfo);1530 return pFirst;1531 #else /* !VBOX_USB_WITH_SYSFS */1532 return NULL;1533 #endif /* !VBOX_USB_WITH_SYSFS */1534 }1535 1536 static PUSBDEVICE USBProxyLinuxGetDevices(const char *pcszUsbfsRoot)1537 {1538 if (pcszUsbfsRoot)1539 return getDevicesFromUsbfs(pcszUsbfsRoot);1540 else1541 return getDevicesFromSysfs();1542 }1543 438 1544 439 PUSBDEVICE USBProxyServiceLinux::getDevices(void) -
trunk/src/VBox/Main/testcase/Makefile.kmk
r30931 r32324 151 151 tstHostHardwareLinux_SOURCES = \ 152 152 tstHostHardwareLinux.cpp \ 153 ../linux/HostHardwareLinux.cpp 153 ../linux/HostHardwareLinux.cpp \ 154 ../linux/USBGetDevices.cpp 154 155 tstHostHardwareLinux_INCS = . ../include 155 156 tstHostHardwareLinux_DEFS = \ -
trunk/src/VBox/Main/testcase/tstHostHardwareLinux.cpp
r32299 r32324 19 19 20 20 #include <HostHardwareLinux.h> 21 #include <USBGetDevices.h> 21 22 22 23 #include <VBox/err.h> … … 52 53 } 53 54 return rc; 55 } 56 57 void printDevices(PUSBDEVICE pDevices, const char *pcszAccess) 58 { 59 PUSBDEVICE pDevice = pDevices; 60 61 RTPrintf("Enumerating usb devices using %s\n", pcszAccess); 62 while (pDevice) 63 { 64 RTPrintf(" Manufacturer: %s, product: %s, serial number string: %s\n", 65 pDevice->pszManufacturer, pDevice->pszProduct, 66 pDevice->pszSerialNumber); 67 RTPrintf(" Device address: %s\n", pDevice->pszAddress); 68 pDevice = pDevice->pNext; 69 } 70 } 71 72 void freeDevices(PUSBDEVICE pDevices) 73 { 74 PUSBDEVICE pDevice = pDevices, pDeviceNext; 75 76 while (pDevice) 77 { 78 pDeviceNext = pDevice->pNext; 79 deviceFree(pDevice); 80 pDevice = pDeviceNext; 81 } 54 82 } 55 83 … … 91 119 } 92 120 #ifdef VBOX_USB_WITH_SYSFS 93 VECTOR_OBJ(USBDeviceInfo) vecDevInfo;94 VEC_INIT_OBJ(&vecDevInfo, USBDeviceInfo, USBDevInfoCleanup);95 rc = USBSysfsEnumerateHostDevices(&vecDevInfo);96 if ( RT_FAILURE(rc))121 PUSBDEVICE pDevice = USBProxyLinuxGetDevices(NULL); 122 printDevices(pDevice, "sysfs"); 123 freeDevices(pDevice); 124 if (USBProxyLinuxCheckForUsbfs("/proc/bus/usb/devices")) 97 125 { 98 RTPrintf ("Failed to update the host USB device information, error %Rrc\n",99 rc);100 return 1;126 pDevice = USBProxyLinuxGetDevices("/proc/bus/usb"); 127 printDevices(pDevice, "/proc/bus/usb"); 128 freeDevices(pDevice); 101 129 } 102 RTPrintf ("Listing USB devices detected:\n"); 103 USBDeviceInfo *pInfo; 104 VEC_FOR_EACH(&vecDevInfo, USBDeviceInfo, pInfo) 130 if (USBProxyLinuxCheckForUsbfs("/dev/bus/usb/devices")) 105 131 { 106 char szProduct[1024]; 107 if (RTLinuxSysFsReadStrFile(szProduct, sizeof(szProduct), 108 "%s/product", pInfo->mSysfsPath) == -1) 109 { 110 if (errno != ENOENT) 111 { 112 RTPrintf ("Failed to get the product name for device %s: error %s\n", 113 pInfo->mDevice, strerror(errno)); 114 return 1; 115 } 116 else 117 szProduct[0] = '\0'; 118 } 119 RTPrintf (" device: %s (%s), sysfs path: %s\n", szProduct, pInfo->mDevice, 120 pInfo->mSysfsPath); 121 RTPrintf (" interfaces:\n"); 122 char **ppszIf; 123 VEC_FOR_EACH(&pInfo->mvecpszInterfaces, char *, ppszIf) 124 { 125 char szDriver[RTPATH_MAX]; 126 strcpy(szDriver, "none"); 127 ssize_t size = RTLinuxSysFsGetLinkDest(szDriver, sizeof(szDriver), 128 "%s/driver", *ppszIf); 129 if (size == -1 && errno != ENOENT) 130 { 131 RTPrintf ("Failed to get the driver for interface %s of device %s: error %s\n", 132 *ppszIf, pInfo->mDevice, strerror(errno)); 133 return 1; 134 } 135 if (RTLinuxSysFsExists("%s/driver", *ppszIf) != (size != -1)) 136 { 137 RTPrintf ("RTLinuxSysFsExists did not return the expected value for the driver link of interface %s of device %s.\n", 138 *ppszIf, pInfo->mDevice); 139 return 1; 140 } 141 uint64_t u64InterfaceClass; 142 u64InterfaceClass = RTLinuxSysFsReadIntFile(16, "%s/bInterfaceClass", 143 *ppszIf); 144 RTPrintf (" sysfs path: %s, driver: %s, interface class: 0x%x\n", 145 *ppszIf, szDriver, u64InterfaceClass); 146 } 132 pDevice = USBProxyLinuxGetDevices("/dev/bus/usb"); 133 printDevices(pDevice, "/dev/bus/usb"); 134 freeDevices(pDevice); 147 135 } 148 VEC_CLEANUP_OBJ(&vecDevInfo);149 136 VBoxMainHotplugWaiter waiter; 150 137 RTPrintf ("Waiting for a hotplug event for five seconds...\n");
Note:
See TracChangeset
for help on using the changeset viewer.