From 2da8752d322f497f82a125647a82c4a7c3f92003 Mon Sep 17 00:00:00 2001 From: Dale Phurrough Date: Sat, 13 May 2023 22:49:49 +0200 Subject: [PATCH 1/3] refactor getWinUsbMxId() to fix luxonis/XLink#57 - mvLog(MVLOG_FATAL) temporarily to aid in debug+test --- src/pc/protocols/usb_host.cpp | 148 ++++++++++++++++++++++------------ 1 file changed, 98 insertions(+), 50 deletions(-) diff --git a/src/pc/protocols/usb_host.cpp b/src/pc/protocols/usb_host.cpp index 89a253b..3241cb2 100644 --- a/src/pc/protocols/usb_host.cpp +++ b/src/pc/protocols/usb_host.cpp @@ -1067,83 +1067,131 @@ int usbPlatformWrite(void *fdKey, void *data, int size) #include #pragma comment(lib, "setupapi.lib") #include +#include + +// get MxId given the vidpid and libusb device (Windows only) +// Uses the Win32 SetupDI* apis. Several cautions: +// - Movidius MyriadX usb devices often change their usb path when they load their bootloader/firmware +// - Since USB is dynamic, it is technically possible for a device to change its path at any time std::string getWinUsbMxId(VidPid vidpid, libusb_device* dev) { if (dev == NULL) return {}; + mvLog(MVLOG_FATAL, "seeking %04hx %04hx %s", vidpid.first, vidpid.second, getLibusbDevicePath(dev).c_str()); - constexpr int MAX_NUM_DEVICES = 128; - struct WinUsbDevList2 { - HDEVINFO devInfo; - SP_DEVINFO_DATA infos[MAX_NUM_DEVICES]; - }; - WinUsbDevList2 devList; - WinUsbDevList2* pDevList = &devList; + // init device info vars + HDEVINFO hDevInfoSet; + SP_DEVINFO_DATA devInfoData{}; + devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); - std::string deviceId = ""; + // get USB host controllers; each has exactly one root hub + hDevInfoSet = SetupDiGetClassDevsA(&GUID_DEVINTERFACE_USB_HOST_CONTROLLER, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (hDevInfoSet == INVALID_HANDLE_VALUE) { + return {}; + } - pDevList->devInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if (pDevList->devInfo == INVALID_HANDLE_VALUE) { + // iterate over usb host controllers and populate list with their location path for later matching to device paths + std::vector hostControllerLocationPaths; + for(int i = 0; SetupDiEnumDeviceInfo(hDevInfoSet, i, &devInfoData); i++) { + // get location paths as a REG_MULTI_SZ + std::string locationPaths(1023, 0); + if (!SetupDiGetDeviceRegistryPropertyA(hDevInfoSet, &devInfoData, SPDRP_LOCATION_PATHS, NULL, (PBYTE)locationPaths.c_str(), (DWORD)locationPaths.size(), NULL)) { + continue; + } + + // find PCI path in the multi string and emplace to back of vector + const auto pciPosition = locationPaths.find("PCIROOT"); + if (pciPosition == std::string::npos) { + continue; + } + hostControllerLocationPaths.emplace_back(locationPaths.substr(pciPosition, strnlen_s(locationPaths.c_str() + pciPosition, locationPaths.size() - pciPosition))); + } + + // Free dev info, return if no usb host controllers found + SetupDiDestroyDeviceInfoList(hDevInfoSet); + if (hostControllerLocationPaths.empty()) { return {}; } - // create list - for(int i = 0; i < MAX_NUM_DEVICES; i++) { - pDevList->infos[i].cbSize = sizeof(SP_DEVINFO_DATA); + // get USB devices + hDevInfoSet = SetupDiGetClassDevsA(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (hDevInfoSet == INVALID_HANDLE_VALUE) { + return {}; } - for(int i = 0; SetupDiEnumDeviceInfo(pDevList->devInfo, i, pDevList->infos + i) && i < MAX_NUM_DEVICES; i++) { - char instance_id[128] = {}; - if(!SetupDiGetDeviceInstanceIdA(pDevList->devInfo, pDevList->infos + i, (PSTR) instance_id, sizeof(instance_id), NULL)) { + // iterate over usb devices and populate with device info + std::string goalPath{getLibusbDevicePath(dev)}; + std::string deviceId; + for(int i = 0; SetupDiEnumDeviceInfo(hDevInfoSet, i, &devInfoData); i++) { + // get device instance id + char instanceId[128] {}; + if(!SetupDiGetDeviceInstanceIdA(hDevInfoSet, &devInfoData, (PSTR)instanceId, sizeof(instanceId), NULL)) { continue; } - char serial_id[128] = {}; - int vid = 0, pid = 0; - if(sscanf(instance_id, "USB\\VID_%hx&PID_%hx\\%s", &vid, &pid, serial_id) != 3) { + + // get device vid, pid, and serial id + char serialId[128] {}; + uint16_t vid = 0, pid = 0; + if(sscanf(instanceId, "USB\\VID_%hx&PID_%hx\\%s", &vid, &pid, serialId) != 3) { continue; } - char location_paths[1024] = {}; - if (!SetupDiGetDeviceRegistryProperty(pDevList->devInfo, pDevList->infos + i, SPDRP_LOCATION_PATHS, NULL, (PBYTE)&location_paths, sizeof(location_paths), NULL)) { + // check if this is the device we are looking for + if(vidpid.first != vid || vidpid.second != pid) { continue; } - char* full_path = location_paths; - while (*full_path) { - - std::string holder(full_path); - holder.resize(holder.size() + 32); - - size_t pos = 0; - std::string out = ""; - if ((pos = std::string(holder.c_str()).find("#USBROOT")) != std::string::npos) { - int off = 0; - int port = 0; - if (sscanf(holder.c_str() + pos, "#USBROOT(%4d)%n", &port, &off) == 1) { - pos += off; - out += std::to_string(port + 1); // USBROOT+1 to match - while (sscanf(holder.c_str() + pos, "#USB(%4d)%n", &port, &off) == 1) { - pos += off; - out += "." + std::to_string(port); - } - } - } + // get location paths as a REG_MULTI_SZ + std::string locationPaths(1023, 0); + if (!SetupDiGetDeviceRegistryPropertyA(hDevInfoSet, &devInfoData, SPDRP_LOCATION_PATHS, NULL, (PBYTE)locationPaths.c_str(), (DWORD)locationPaths.size(), NULL)) { + continue; + } - if(vidpid.first == vid && vidpid.second == pid && out == getLibusbDevicePath(dev)) { - // compare - deviceId = serial_id; - break; - } + // find PCI path in the multi string and isolate that path + const auto pciPosition = locationPaths.find("PCIROOT"); + if (pciPosition == std::string::npos) { + continue; + } + const auto usbPath = locationPaths.substr(pciPosition, strnlen_s(locationPaths.c_str() + pciPosition, locationPaths.size() - pciPosition)); + + // find matching host controller + const auto hostController = std::find_if(hostControllerLocationPaths.begin(), hostControllerLocationPaths.end(), [&usbPath](const std::string& candidateController) noexcept { + // check if the usb path starts with the candidate controller path + return usbPath.find(candidateController) == 0; + }); + if (hostController == hostControllerLocationPaths.end()) { + mvLog(MVLOG_FATAL, "Found device with matching vid/pid but no matching USBROOT hub"); + continue; + } - // Move to next string - full_path = full_path + strlen(full_path) + 1; + // initialize pseudo libusb path using the host controller index +1 as the "libusb bus number" + std::string pseudoLibUsbPath = std::to_string(std::distance(hostControllerLocationPaths.begin(), hostController) + 1); + + // find USBROOT in the location path as the starting search position + // there is only one root hub per host controller, so it is always on port 0 + auto searchPosition = locationPaths.find("#USBROOT(0)"); + if (searchPosition == std::string::npos) { + mvLog(MVLOG_FATAL, "Malformed USBROOT hub path"); + continue; + } + constexpr auto usbRootLength = sizeof("#USBROOT(0)") - 1; + searchPosition += usbRootLength; + + // parse and transform the Windows USB path to the pseudo libusb path + int charsRead = 0; + int port = 0; + while (sscanf(usbPath.c_str() + searchPosition, "#USB(%4d)%n", &port, &charsRead) == 1) { + searchPosition += charsRead; + pseudoLibUsbPath += '.' + std::to_string(port); } - if(!deviceId.empty()) { + mvLog(MVLOG_FATAL, "Candidate %04hx %04hx %s %s", vid, pid, pseudoLibUsbPath.c_str(), serialId); + if(pseudoLibUsbPath == goalPath) { + deviceId = serialId; break; } } // Free dev info - SetupDiDestroyDeviceInfoList(pDevList->devInfo); + SetupDiDestroyDeviceInfoList(hDevInfoSet); // Return deviceId if found return deviceId; From 4ac4914f188d72cb12aaab331c0c15b67706b20a Mon Sep 17 00:00:00 2001 From: Dale Phurrough Date: Sun, 14 May 2023 05:05:42 +0200 Subject: [PATCH 2/3] minor optimizations --- src/pc/protocols/usb_host.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/pc/protocols/usb_host.cpp b/src/pc/protocols/usb_host.cpp index 3241cb2..b859bd8 100644 --- a/src/pc/protocols/usb_host.cpp +++ b/src/pc/protocols/usb_host.cpp @@ -1165,20 +1165,15 @@ std::string getWinUsbMxId(VidPid vidpid, libusb_device* dev) { // initialize pseudo libusb path using the host controller index +1 as the "libusb bus number" std::string pseudoLibUsbPath = std::to_string(std::distance(hostControllerLocationPaths.begin(), hostController) + 1); - // find USBROOT in the location path as the starting search position - // there is only one root hub per host controller, so it is always on port 0 - auto searchPosition = locationPaths.find("#USBROOT(0)"); - if (searchPosition == std::string::npos) { - mvLog(MVLOG_FATAL, "Malformed USBROOT hub path"); - continue; - } - constexpr auto usbRootLength = sizeof("#USBROOT(0)") - 1; - searchPosition += usbRootLength; + // there is only one root hub per host controller, it is always on port 0, + // therefore start the search past this known root hub in the usb path + static constexpr auto usbRootLength = sizeof("#USBROOT(0)") - 1; + auto searchPosition{usbPath.c_str() + hostController->size() + usbRootLength}; // parse and transform the Windows USB path to the pseudo libusb path int charsRead = 0; int port = 0; - while (sscanf(usbPath.c_str() + searchPosition, "#USB(%4d)%n", &port, &charsRead) == 1) { + while (sscanf(searchPosition, "#USB(%4d)%n", &port, &charsRead) == 1) { searchPosition += charsRead; pseudoLibUsbPath += '.' + std::to_string(port); } From bdab3a0dd952fd09d08e8b9ea2ed7c6c5b20fcd5 Mon Sep 17 00:00:00 2001 From: Dale Phurrough Date: Mon, 15 May 2023 19:20:29 +0200 Subject: [PATCH 3/3] remove log fatal debug output in prep of merge --- src/pc/protocols/usb_host.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pc/protocols/usb_host.cpp b/src/pc/protocols/usb_host.cpp index b859bd8..32bc245 100644 --- a/src/pc/protocols/usb_host.cpp +++ b/src/pc/protocols/usb_host.cpp @@ -1075,7 +1075,6 @@ int usbPlatformWrite(void *fdKey, void *data, int size) // - Since USB is dynamic, it is technically possible for a device to change its path at any time std::string getWinUsbMxId(VidPid vidpid, libusb_device* dev) { if (dev == NULL) return {}; - mvLog(MVLOG_FATAL, "seeking %04hx %04hx %s", vidpid.first, vidpid.second, getLibusbDevicePath(dev).c_str()); // init device info vars HDEVINFO hDevInfoSet; @@ -1158,7 +1157,7 @@ std::string getWinUsbMxId(VidPid vidpid, libusb_device* dev) { return usbPath.find(candidateController) == 0; }); if (hostController == hostControllerLocationPaths.end()) { - mvLog(MVLOG_FATAL, "Found device with matching vid/pid but no matching USBROOT hub"); + mvLog(MVLOG_WARN, "Found device with matching vid/pid but no matching USBROOT hub"); continue; } @@ -1178,7 +1177,6 @@ std::string getWinUsbMxId(VidPid vidpid, libusb_device* dev) { pseudoLibUsbPath += '.' + std::to_string(port); } - mvLog(MVLOG_FATAL, "Candidate %04hx %04hx %s %s", vid, pid, pseudoLibUsbPath.c_str(), serialId); if(pseudoLibUsbPath == goalPath) { deviceId = serialId; break;