From: Chris Dickens Date: Wed, 3 Jan 2018 21:53:56 +0000 (-0800) Subject: Windows: Improve enumeration process X-Git-Tag: upstream/1.0.22~68 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c74dce858335d87ae07e2eceb7c166fa275bc8fe;p=platform%2Fupstream%2Flibusb.git Windows: Improve enumeration process Prior to this commit, there were some limitations and inefficiencies during the enmeration process. First, the maximum number of device interface GUIDs that could be enumerated was fixed at 64. This limit has been removed and the list of GUIDs is dynamically resized as new ones are encountered. Logic has also been added to detect the presence of duplicate GUIDs in order to speed up the enumeration process. Next, when searching for device interface GUIDs, only the "DeviceInterfaceGUIDs" registry key was being consulted. Now we will also consider "DeviceInterfaceGUID" in order to support devices that have the GUID listed under this key (such as some WCID devices). Finally, there used to be a static list of USB PnP enumerator strings that were used to detect devices during the GENeric enumeration pass. In many cases, this is wasteful as these enumerators are only present with very specific hardware. To improve this, we now keep track of the USB PnP enumerator string encountered as we enumerate the hubs. This allows the enumeration process to only search for devices that could possibly be present on the system given the hardware and drivers that were encountered. Signed-off-by: Chris Dickens --- diff --git a/libusb/os/windows_winusb.c b/libusb/os/windows_winusb.c index 8cb8c05..eb86412 100644 --- a/libusb/os/windows_winusb.c +++ b/libusb/os/windows_winusb.c @@ -216,7 +216,7 @@ static int init_dlls(void) DLL_LOAD_FUNC_PREFIXED(Kernel32, p, IsWow64Process, FALSE); DLL_GET_HANDLE(OLE32); - DLL_LOAD_FUNC_PREFIXED(OLE32, p, CLSIDFromString, TRUE); + DLL_LOAD_FUNC_PREFIXED(OLE32, p, IIDFromString, TRUE); DLL_GET_HANDLE(SetupAPI); DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetClassDevsA, TRUE); @@ -246,7 +246,7 @@ static void exit_dlls(void) * Parameters: * dev_info: a pointer to a dev_info list * dev_info_data: a pointer to an SP_DEVINFO_DATA to be filled (or NULL if not needed) - * usb_class: the generic USB class for which to retrieve interface details + * enumerator: the generic USB class for which to retrieve interface details * index: zero based index of the interface in the device info list * * Note: it is the responsibility of the caller to free the DEVICE_INTERFACE_DETAIL_DATA @@ -254,19 +254,22 @@ static void exit_dlls(void) * incremented index starting at zero) until all interfaces have been returned. */ static bool get_devinfo_data(struct libusb_context *ctx, - HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const char *usb_class, unsigned _index) + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const char *enumerator, unsigned _index) { if (_index == 0) { - *dev_info = pSetupDiGetClassDevsA(NULL, usb_class, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); - if (*dev_info == INVALID_HANDLE_VALUE) + *dev_info = pSetupDiGetClassDevsA(NULL, enumerator, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); + if (*dev_info == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not obtain device info set for PnP enumerator '%s': %s", + enumerator, windows_error_str(0)); return false; + } } dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) { if (GetLastError() != ERROR_NO_MORE_ITEMS) - usbi_err(ctx, "Could not obtain device info data for index %u: %s", - _index, windows_error_str(0)); + usbi_err(ctx, "could not obtain device info data for PnP enumerator '%s' index %u: %s", + enumerator, _index, windows_error_str(0)); pSetupDiDestroyDeviceInfoList(*dev_info); *dev_info = INVALID_HANDLE_VALUE; @@ -1273,21 +1276,13 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered { struct discovered_devs *discdevs; HDEVINFO dev_info = { 0 }; - const char *usb_class[] = {"USB", "NUSB3", "IUSB3", "IARUSB3"}; SP_DEVINFO_DATA dev_info_data = { 0 }; SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; GUID hid_guid; -#define MAX_ENUM_GUIDS 64 - const GUID *guid[MAX_ENUM_GUIDS]; -#define HCD_PASS 0 -#define HUB_PASS 1 -#define GEN_PASS 2 -#define DEV_PASS 3 -#define HID_PASS 4 int r = LIBUSB_SUCCESS; int api, sub_api; - size_t class_index = 0; - unsigned int nb_guids, pass, i, j, ancestor; + unsigned int pass, i, j, ancestor; + char enumerator[16]; char path[MAX_PATH_LENGTH]; char strbuf[MAX_PATH_LENGTH]; struct libusb_device *dev, *parent_dev; @@ -1300,9 +1295,25 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered WCHAR guid_string_w[MAX_GUID_STRING_LENGTH]; GUID *if_guid; LONG s; +#define HCD_PASS 0 +#define HUB_PASS 1 +#define GEN_PASS 2 +#define DEV_PASS 3 +#define HID_PASS 4 +#define EXT_PASS 5 + // Keep a list of guids that will be enumerated +#define GUID_SIZE_STEP 8 + const GUID **guid_list, **new_guid_list; + unsigned int guid_size = GUID_SIZE_STEP; + unsigned int nb_guids; + // Keep a list of PnP enumerator strings that are found + char *usb_enumerator[8]; + unsigned int nb_usb_enumerators = 0; + unsigned int usb_enum_index = 0; // Keep a list of newly allocated devs to unref +#define UNREF_SIZE_STEP 16 libusb_device **unref_list, **new_unref_list; - unsigned int unref_size = 64; + unsigned int unref_size = UNREF_SIZE_STEP; unsigned int unref_cur = 0; // PASS 1 : (re)enumerate HCDs (allows for HCD hotplug) @@ -1314,24 +1325,33 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered // set the device interfaces. // Init the GUID table - guid[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER; - guid[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB; - guid[GEN_PASS] = NULL; - guid[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE; + guid_list = malloc(guid_size * sizeof(void *)); + if (guid_list == NULL) { + usbi_err(ctx, "failed to alloc guid list"); + return LIBUSB_ERROR_NO_MEM; + } + + guid_list[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER; + guid_list[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB; + guid_list[GEN_PASS] = NULL; + guid_list[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE; HidD_GetHidGuid(&hid_guid); - guid[HID_PASS] = &hid_guid; - nb_guids = HID_PASS + 1; + guid_list[HID_PASS] = &hid_guid; + nb_guids = EXT_PASS; - unref_list = calloc(unref_size, sizeof(libusb_device *)); - if (unref_list == NULL) + unref_list = malloc(unref_size * sizeof(void *)); + if (unref_list == NULL) { + usbi_err(ctx, "failed to alloc unref list"); + free((void *)guid_list); return LIBUSB_ERROR_NO_MEM; + } for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) { //#define ENUM_DEBUG #if defined(ENABLE_LOGGING) && defined(ENUM_DEBUG) const char *passname[] = { "HCD", "HUB", "GEN", "DEV", "HID", "EXT" }; - usbi_dbg("#### PROCESSING %ss %s", passname[(pass <= HID_PASS) ? pass : (HID_PASS + 1)], - (pass != GEN_PASS) ? guid_to_string(guid[pass]) : ""); + usbi_dbg("#### PROCESSING %ss %s", passname[MIN(pass, EXT_PASS)], + (pass != GEN_PASS) ? guid_to_string(guid_list[pass]) : ""); #endif for (i = 0; ; i++) { // safe loop: free up any (unprotected) dynamic resource @@ -1353,7 +1373,7 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered if (pass != GEN_PASS) { // Except for GEN, all passes deal with device interfaces - dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid[pass], i); + dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid_list[pass], i); if (dev_interface_details == NULL) break; @@ -1367,12 +1387,12 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered // being listed under the "NUSB3" PnP Symbolic Name rather than "USB". // The Intel USB 3.0 driver behaves similar, but uses "IUSB3" // The Intel Alpine Ridge USB 3.1 driver uses "IARUSB3" - for (; class_index < ARRAYSIZE(usb_class); class_index++) { - if (get_devinfo_data(ctx, &dev_info, &dev_info_data, usb_class[class_index], i)) + for (; usb_enum_index < nb_usb_enumerators; usb_enum_index++) { + if (get_devinfo_data(ctx, &dev_info, &dev_info_data, usb_enumerator[usb_enum_index], i)) break; i = 0; } - if (class_index >= ARRAYSIZE(usb_class)) + if (usb_enum_index == nb_usb_enumerators) break; } @@ -1413,6 +1433,31 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered break; case HUB_PASS: api = USB_API_HUB; + // Fetch the PnP enumerator class for this hub + // This will allow us to enumerate all classes during the GEN pass + if (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ENUMERATOR_NAME, + NULL, (PBYTE)enumerator, sizeof(enumerator), NULL)) { + usbi_err(ctx, "could not read enumerator string for device '%s': %s", dev_id_path, windows_error_str(0)); + LOOP_BREAK(LIBUSB_ERROR_OTHER); + } + for (j = 0; j < nb_usb_enumerators; j++) { + if (strcmp(usb_enumerator[j], enumerator) == 0) + break; + } + if (j == nb_usb_enumerators) { + usbi_dbg("found new PnP enumerator string '%s'", enumerator); + if (nb_usb_enumerators < ARRAYSIZE(usb_enumerator)) { + usb_enumerator[nb_usb_enumerators] = _strdup(enumerator); + if (usb_enumerator[nb_usb_enumerators] != NULL) { + nb_usb_enumerators++; + } else { + usbi_err(ctx, "could not allocate enumerator string '%s'", enumerator); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + } else { + usbi_warn(ctx, "too many enumerator strings, some devices may not be accessible"); + } + } break; case GEN_PASS: // We use the GEN pass to detect driverless devices... @@ -1425,24 +1470,50 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered // ...and to add the additional device interface GUIDs key = pSetupDiOpenDevRegKey(dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); if (key != INVALID_HANDLE_VALUE) { + // Look for both DeviceInterfaceGUIDs *and* DeviceInterfaceGUID, in that order size = sizeof(guid_string_w); s = pRegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, ®_type, (BYTE *)guid_string_w, &size); + if (s == ERROR_FILE_NOT_FOUND) + s = pRegQueryValueExW(key, L"DeviceInterfaceGUID", NULL, ®_type, + (BYTE *)guid_string_w, &size); pRegCloseKey(key); - if (s == ERROR_SUCCESS) { - if (nb_guids >= MAX_ENUM_GUIDS) { - // If this assert is ever reported, grow a GUID table dynamically - usbi_err(ctx, "program assertion failed: too many GUIDs"); - LOOP_BREAK(LIBUSB_ERROR_OVERFLOW); + if ((s == ERROR_SUCCESS) && + (((reg_type == REG_SZ) && (size == (sizeof(guid_string_w) - sizeof(WCHAR)))) || + ((reg_type == REG_MULTI_SZ) && (size == sizeof(guid_string_w))))) { + if (nb_guids == guid_size) { + new_guid_list = realloc((void *)guid_list, (guid_size + GUID_SIZE_STEP) * sizeof(void *)); + if (new_guid_list == NULL) { + usbi_err(ctx, "failed to realloc guid list"); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + guid_list = new_guid_list; + guid_size += GUID_SIZE_STEP; } - if_guid = calloc(1, sizeof(GUID)); + if_guid = malloc(sizeof(*if_guid)); if (if_guid == NULL) { - usbi_err(ctx, "could not calloc for if_guid: not enough memory"); + usbi_err(ctx, "failed to alloc if_guid"); LOOP_BREAK(LIBUSB_ERROR_NO_MEM); } - pCLSIDFromString(guid_string_w, if_guid); - guid[nb_guids++] = if_guid; - usbi_dbg("extra GUID: %s", guid_to_string(if_guid)); + if (pIIDFromString(guid_string_w, if_guid) != 0) { + usbi_warn(ctx, "device '%s' has malformed DeviceInterfaceGUID string, skipping", dev_id_path); + free(if_guid); + } else { + // Check if we've already seen this GUID + for (j = EXT_PASS; j < nb_guids; j++) { + if (memcmp(guid_list[j], if_guid, sizeof(*if_guid)) == 0) + break; + } + if (j == nb_guids) { + usbi_dbg("extra GUID: %s", guid_to_string(if_guid)); + guid_list[nb_guids++] = if_guid; + } else { + // Duplicate, ignore + free(if_guid); + } + } + } else if (s == ERROR_SUCCESS) { + usbi_warn(ctx, "unexpected type/size of DeviceInterfaceGUID for '%s'", dev_id_path); } } break; @@ -1539,17 +1610,16 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered } // Keep track of devices that need unref - unref_list[unref_cur++] = dev; - if (unref_cur >= unref_size) { - unref_size += 64; - new_unref_list = usbi_reallocf(unref_list, unref_size * sizeof(libusb_device *)); + if (unref_cur == unref_size) { + new_unref_list = realloc(unref_list, (unref_size + UNREF_SIZE_STEP) * sizeof(void *)); if (new_unref_list == NULL) { usbi_err(ctx, "could not realloc list for unref - aborting."); LOOP_BREAK(LIBUSB_ERROR_NO_MEM); - } else { - unref_list = new_unref_list; } + unref_list = new_unref_list; + unref_size += UNREF_SIZE_STEP; } + unref_list[unref_cur++] = dev; } // Setup device @@ -1645,8 +1715,13 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered } // Free any additional GUIDs - for (pass = HID_PASS + 1; pass < nb_guids; pass++) - free((void *)guid[pass]); + for (pass = EXT_PASS; pass < nb_guids; pass++) + free((void *)guid_list[pass]); + free((void *)guid_list); + + // Free any PnP enumerator strings + for (i = 0; i < nb_usb_enumerators; i++) + free(usb_enumerator[i]); // Unref newly allocated devs for (i = 0; i < unref_cur; i++) diff --git a/libusb/os/windows_winusb.h b/libusb/os/windows_winusb.h index b411ba2..eed9850 100644 --- a/libusb/os/windows_winusb.h +++ b/libusb/os/windows_winusb.h @@ -293,7 +293,7 @@ struct driver_lookup { /* OLE32 dependency */ DLL_DECLARE_HANDLE(OLE32); -DLL_DECLARE_FUNC_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID)); +DLL_DECLARE_FUNC_PREFIXED(WINAPI, HRESULT, p, IIDFromString, (LPCOLESTR, LPIID)); /* Kernel32 dependencies */ DLL_DECLARE_HANDLE(Kernel32); diff --git a/libusb/version_nano.h b/libusb/version_nano.h index 79fd6f1..b8b0363 100644 --- a/libusb/version_nano.h +++ b/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11243 +#define LIBUSB_NANO 11244