luxonis / XLink

A cross-platform library for communicating with devices over various physical links.
Apache License 2.0
11 stars 17 forks source link

`getWinUsbMxId()` path construction is errant and doesn't find libusb devices #57

Closed diablodale closed 1 month ago

diablodale commented 1 year ago

getWinUsbMxId() does not correctly transform Win32 location paths into libusb port paths. This causes failure(s) to find USB devices.

getWinUsbMxId() is a fallback function that is used only on Windows when libusb finds a X_LINK_BOOTED device that it can't access due to other app/code having that device already open via libusb; there is no shared access.

getWinUsbMxId() uses the Win32 native SetupDi api to query for usb devices. Then the function attempts to map the Win32 "Location path" into a libusb path of ports. Unfortunately, this mapping is errant and therefore devices are often not found since the path's will not match.

Setup

Repro

  1. Add lots of debug to trace behaviors of Xlink πŸ˜‰
  2. Change tests/color_camera_node_test line 46 to be a forever while(true) loop
  3. Connect 3 OAK devices via USB to the same computer. I recommend at least two of them on separate controllers.
  4. Open two terminals on the computer
  5. Run color_camera_node_test in first terminal and wait until it is in the loop. Observe the debug output. LEAVE IT RUNNING!
  6. Run color_camera_node_test in second terminal and wait until it is in the loop. Observe the debug output

Result

First terminal

Notice the probe of the 3 devices by running the small program on each. Then the chosen device is connected+started.

F: [global] [         0] [ThreadN] getLibusbDeviceMxId:353      attempting small program...
F: [global] [         0] [ThreadN] getUSBDevices:173    getLibusbDeviceMxId() returned: Success
F: [global] [         0] [ThreadN] getUSBDevices:201    inserting...
F: [global] [         0] [ThreadN] getLibusbDeviceMxId:353      attempting small program...
F: [global] [         0] [ThreadN] getUSBDevices:173    getLibusbDeviceMxId() returned: Success
F: [global] [         0] [ThreadN] getUSBDevices:201    inserting...
F: [global] [         0] [ThreadN] getLibusbDeviceMxId:353      attempting small program...
F: [global] [         0] [ThreadN] getUSBDevices:173    getLibusbDeviceMxId() returned: Success
F: [global] [         0] [ThreadN] getUSBDevices:201    inserting...
F: [global] [         0] [ThreadN] getLibusbDeviceMxId:353      attempting small program...
F: [global] [         0] [ThreadN] getUSBDevices:173    getLibusbDeviceMxId(1844301031490A1300) returned: Success
F: [global] [         0] [ThreadN] getUSBDevices:201    inserting found device into result
...

Second terminal

Here the bug is exposed. It again probes all three devices. The 1st and 2nd can run the small program to probe them. However, the 3rd device is running in the other terminal and therefore can not be opened. It is "libusb device is BOOTED and LIBUSB_ERROR_ACCESS" which leads to the windows-only fallback function getWinUsbMxId().

getWinUsbMxId() calls Win32 SetupDi apis, parses for serial numbers in the DeviceInstanceId, and attempts to transfor the Windows path into a libusb port path. In the logs below "seeking..." is the params passed to getWinUsbMxId(). It is seeking vid=03e7 pid=f63b at libusb_path="3.20".

Remember that terminal 1 has one device open. Look below in the "candidate" entries. Each candidate is the vid, pid, and custom mapping path contructed; just before the if() test. Easy to see in the devbug all three OAK devices since they all have the same vid=03e7.

UNBOOTED are... pid=2485 simulatedpath=1.9 pid=2485 simulatedpath=1.1

BOOTED is... pid=f63b simulatedpath=1.20 πŸ‘ŽπŸ˜’

The function was given libusb port path "3.20" yet the simulated mapping generated "1.20". This is errant.

F: [global] [         0] [ThreadN] getLibusbDeviceMxId:353      attempting small program...
F: [global] [         0] [ThreadN] getUSBDevices:173    getLibusbDeviceMxId() returned: Success
F: [global] [         0] [ThreadN] getUSBDevices:201    inserting found device into result
F: [global] [         0] [ThreadN] getLibusbDeviceMxId:353      attempting small program...
F: [global] [         0] [ThreadN] getUSBDevices:173    getLibusbDeviceMxId() returned: Success
F: [global] [         0] [ThreadN] getUSBDevices:201    inserting found device into result
F: [global] [         0] [ThreadN] getLibusbDeviceMxId:330      libusb_open: Access denied (insufficient permissions)
F: [global] [         0] [ThreadN] getLibusbDeviceMxId:335      libusb device is BOOTED and LIBUSB_ERROR_ACCESS
F: [global] [         0] [ThreadN] getWinUsbMxId:1077   seeking   03e7  f63b  3.20
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  2485  1.9
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  2485  
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 0bda  0316  1.18
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 0bda  0316  
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  f63b  1.20
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  f63b  
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 048d  ce00  1.6
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 048d  ce00  
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 04f2  b68b  1.13
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 04f2  b68b  
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 8087  0026  1.14
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 8087  0026  
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  2485  1.1
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  2485  
F: [global] [         0] [ThreadN] getLibusbDeviceMxId:330      libusb_open: Access denied (insufficient permissions)
F: [global] [         0] [ThreadN] getLibusbDeviceMxId:335      libusb device is BOOTED and LIBUSB_ERROR_ACCESS
F: [global] [         0] [ThreadN] getWinUsbMxId:1077   seeking   03e7  f63b  3.20
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  2485  1.9
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  2485  
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 0bda  0316  1.18
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 0bda  0316  
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  f63b  1.20
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  f63b  
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 048d  ce00  1.6
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 048d  ce00  
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 04f2  b68b  1.13
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 04f2  b68b  
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 8087  0026  1.14
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 8087  0026
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  2485  1.1
F: [global] [         0] [ThreadN] getWinUsbMxId:1136   candidate 03e7  2485
F: [global] [         0] [ThreadN] getUSBDevices:173    getLibusbDeviceMxId() returned: Access denied (insufficient permissions)
F: [global] [         0] [ThreadN] getUSBDevices:201    inserting found device into result
[2023-05-12 22:28:47.296] [depthai] [warning] Insufficient permissions to communicate with X_LINK_BOOTED device having name "3.20". Make sure udev rules are set
...

These are the Windows Device Manager location paths for the two running Luxonis Devices during the above logs.

# this is the device running in terminal 1
USB\VID_03E7&PID_F63B\1844301031490A1300
# its location paths
PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(20)
ACPI(_SB_)#ACPI(PCI0)#ACPI(XHC_)#ACPI(RHUB)#ACPI(SS04)

# this is the device running in terminal 2
USB\VID_03E7&PID_F63B\14442C10C1A1C6D200
# its location paths
PCIROOT(0)#PCI(1B00)#PCI(0000)#PCI(0200)#PCI(0000)#USBROOT(0)#USB(3)
ACPI(_SB_)#ACPI(PCI0)#ACPI(RP17)#ACPI(PXSX)#ACPI(TBDU)#ACPI(XHC_)#ACPI(RHUB)#ACPI(SS01)

Expected

device on libusb path "3.20" is found using the Win32 apis and its MXID returned

diablodale commented 1 year ago

My guess, the function path code doesn't handle a computer with multiple USBROOT aka USB controllers. This is my laptop. It has three USB-3 type-A ports. And it has one USB-3 type-C port. I have two OAKs on the type-As. And one OAK on the type-C.

image

Notice above there are three USB controllers. That aligns with the the 3.1 native libusb port path. libusb saw the three controllers.

If I have only one OAK 14442C10C1A1C6D200 plugged into the type-C it has the long path

PCIROOT(0)#PCI(1B00)#PCI(0000)#PCI(0200)#PCI(0000)#USBROOT(0)#USB(3)
ACPI(_SB_)#ACPI(PCI0)#ACPI(RP17)#ACPI(PXSX)#ACPI(TBDU)#ACPI(XHC_)#ACPI(RHUB)#ACPI(SS01)

and looks like this image

diablodale commented 1 year ago

Adding documentation to align with forthcoming PR and testing

From some research, I learned that USB host controllers each have exactly one root usb hub. It is a 1:1 relationship.

libusb itself uses a single comprehensive snapshot of everything USB to then derive its "libusb" versions of everything with https://github.com/libusb/libusb/blob/07441f54244991af55df158b61fd69ca95b39662/libusb/os/windows_winusb.c#LL1478C12-L1478C34. Between calls to libusb_get_device_list it is possible for any or all devices and paths to change their relative location.

My 3 usb OAKs plugged into usb ports but not opened/running "Movidius MyriadX" on 3 different ports shows...

Port_#0012.Hub_#0002
PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(12)

Port_#0001.Hub_#0002
PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(1)

Port_#0009.Hub_#0002
PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(9)

when running their paths change (not confirmed same order of devices)

Port_#0003.Hub_#0003
PCIROOT(0)#PCI(1B00)#PCI(0000)#PCI(0200)#PCI(0000)#USBROOT(0)#USB(3)

Port_#0021.Hub_#0002
PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(21)

Port_#0020.Hub_#0002
PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(20)
diablodale commented 1 month ago

@themarpe as a friendly caution...I have not pushed my final stable xlink code to my public repo. if you source any code from my public xlink repo, you might be getting incomplete work.

themarpe commented 1 month ago

Thanks for the headsup - seems to just be autoclose from this already merged PR https://github.com/luxonis/XLink/pull/60 since push to main/master

Can reopen, but AFAIK the crux of the above was in fact addressed (in the described)