diff options
Diffstat (limited to 'libusb/os/windows_winusb.c')
-rw-r--r-- | libusb/os/windows_winusb.c | 540 |
1 files changed, 366 insertions, 174 deletions
diff --git a/libusb/os/windows_winusb.c b/libusb/os/windows_winusb.c index ffc1612..926b9e8 100644 --- a/libusb/os/windows_winusb.c +++ b/libusb/os/windows_winusb.c @@ -263,6 +263,9 @@ static int get_interface_details(struct libusb_context *ctx, HDEVINFO dev_info, char guid_string[MAX_GUID_STRING_LENGTH]; DWORD size; +#ifndef ENABLE_LOGGING + UNUSED(*guid_string); +#endif dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA); dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); for (;;) { @@ -485,6 +488,20 @@ static int get_interface_by_endpoint(struct libusb_config_descriptor *conf_desc, return LIBUSB_ERROR_NOT_FOUND; } +static const struct libusb_interface_descriptor *get_interface_descriptor_by_number(struct libusb_device_handle *dev_handle, struct libusb_config_descriptor *conf_desc, int iface, uint8_t altsetting) +{ + int i; + + for (i = 0; i < conf_desc->bNumInterfaces; i++) { + if (altsetting < conf_desc->interface[i].num_altsetting && conf_desc->interface[i].altsetting[altsetting].bInterfaceNumber == iface) { + return &conf_desc->interface[i].altsetting[altsetting]; + } + } + + usbi_err(HANDLE_CTX(dev_handle), "interface %d with altsetting %d not found for device", iface, (int)altsetting); + return NULL; +} + /* * Open a device and associate the HANDLE with the context's I/O completion port */ @@ -523,11 +540,12 @@ static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, uin return r; } - if (iface >= conf_desc->bNumInterfaces) { - usbi_err(HANDLE_CTX(dev_handle), "interface %d out of range for device", iface); - return LIBUSB_ERROR_NOT_FOUND; + if_desc = get_interface_descriptor_by_number(dev_handle, conf_desc, iface, altsetting); + if (if_desc == NULL) { + r = LIBUSB_ERROR_NOT_FOUND; + goto end; } - if_desc = &conf_desc->interface[iface].altsetting[altsetting]; + safe_free(priv->usb_interface[iface].endpoint); if (if_desc->bNumEndpoints == 0) { @@ -535,8 +553,8 @@ static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, uin } else { priv->usb_interface[iface].endpoint = malloc(if_desc->bNumEndpoints); if (priv->usb_interface[iface].endpoint == NULL) { - libusb_free_config_descriptor(conf_desc); - return LIBUSB_ERROR_NO_MEM; + r = LIBUSB_ERROR_NO_MEM; + goto end; } priv->usb_interface[iface].nb_endpoints = if_desc->bNumEndpoints; for (i = 0; i < if_desc->bNumEndpoints; i++) { @@ -544,7 +562,6 @@ static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, uin usbi_dbg(HANDLE_CTX(dev_handle), "(re)assigned endpoint %02X to interface %u", priv->usb_interface[iface].endpoint[i], iface); } } - libusb_free_config_descriptor(conf_desc); // Extra init may be required to configure endpoints if (priv->apib->configure_endpoints) @@ -553,6 +570,8 @@ static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, uin if (r == LIBUSB_SUCCESS) priv->usb_interface[iface].current_altsetting = altsetting; +end: + libusb_free_config_descriptor(conf_desc); return r; } @@ -1053,7 +1072,6 @@ static int init_device(struct libusb_device *dev, struct libusb_device *parent_d DWORD size; uint8_t bus_number, depth; int r; - int ginfotimeout; priv = usbi_get_device_priv(dev); @@ -1114,61 +1132,45 @@ static int init_device(struct libusb_device *dev, struct libusb_device *parent_d conn_info.ConnectionIndex = (ULONG)port_number; // coverity[tainted_data_argument] - ginfotimeout = 20; - do { - if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, sizeof(conn_info), - &conn_info, sizeof(conn_info), &size, NULL)) { - usbi_warn(ctx, "could not get node connection information for device '%s': %s", - priv->dev_id, windows_error_str(0)); - CloseHandle(hub_handle); - return LIBUSB_ERROR_NO_DEVICE; - } - if (conn_info.ConnectionStatus == NoDeviceConnected) { - usbi_err(ctx, "device '%s' is no longer connected!", priv->dev_id); - CloseHandle(hub_handle); - return LIBUSB_ERROR_NO_DEVICE; - } - - if ((conn_info.DeviceDescriptor.bLength != LIBUSB_DT_DEVICE_SIZE) - || (conn_info.DeviceDescriptor.bDescriptorType != LIBUSB_DT_DEVICE)) { - SleepEx(50, TRUE); - continue; - } + if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, sizeof(conn_info), + &conn_info, sizeof(conn_info), &size, NULL)) { + usbi_warn(ctx, "could not get node connection information for device '%s': %s", + priv->dev_id, windows_error_str(0)); + CloseHandle(hub_handle); + return LIBUSB_ERROR_NO_DEVICE; + } - static_assert(sizeof(dev->device_descriptor) == sizeof(conn_info.DeviceDescriptor), - "mismatch between libusb and OS device descriptor sizes"); - memcpy(&dev->device_descriptor, &conn_info.DeviceDescriptor, LIBUSB_DT_DEVICE_SIZE); - usbi_localize_device_descriptor(&dev->device_descriptor); - - priv->active_config = conn_info.CurrentConfigurationValue; - if (priv->active_config == 0) { - usbi_dbg(ctx, "0x%x:0x%x found %u configurations (not configured)", - dev->device_descriptor.idVendor, - dev->device_descriptor.idProduct, - dev->device_descriptor.bNumConfigurations); - SleepEx(50, TRUE); - } - } while (priv->active_config == 0 && --ginfotimeout >= 0); + if (conn_info.ConnectionStatus == NoDeviceConnected) { + usbi_err(ctx, "device '%s' is no longer connected!", priv->dev_id); + CloseHandle(hub_handle); + return LIBUSB_ERROR_NO_DEVICE; + } if ((conn_info.DeviceDescriptor.bLength != LIBUSB_DT_DEVICE_SIZE) - || (conn_info.DeviceDescriptor.bDescriptorType != LIBUSB_DT_DEVICE)) { + || (conn_info.DeviceDescriptor.bDescriptorType != LIBUSB_DT_DEVICE)) { usbi_err(ctx, "device '%s' has invalid descriptor!", priv->dev_id); CloseHandle(hub_handle); return LIBUSB_ERROR_OTHER; } - if (priv->active_config == 0) { - usbi_info(ctx, "0x%x:0x%x found %u configurations but device isn't configured, " - "forcing current configuration to 1", - dev->device_descriptor.idVendor, - dev->device_descriptor.idProduct, - dev->device_descriptor.bNumConfigurations); - priv->active_config = 1; - } else { - usbi_dbg(ctx, "found %u configurations (current config: %u)", dev->device_descriptor.bNumConfigurations, priv->active_config); + static_assert(sizeof(dev->device_descriptor) == sizeof(conn_info.DeviceDescriptor), + "mismatch between libusb and OS device descriptor sizes"); + memcpy(&dev->device_descriptor, &conn_info.DeviceDescriptor, LIBUSB_DT_DEVICE_SIZE); + usbi_localize_device_descriptor(&dev->device_descriptor); + + if (conn_info.CurrentConfigurationValue == 0) { + usbi_dbg(ctx, "found %u configurations for device '%s' but device is not configured (i.e. current config: 0), ignoring it", + dev->device_descriptor.bNumConfigurations, + priv->dev_id); + CloseHandle(hub_handle); + return LIBUSB_ERROR_OTHER; } + priv->active_config = conn_info.CurrentConfigurationValue; + usbi_dbg(ctx, "found %u configurations (current config: %u) for device '%s'", + dev->device_descriptor.bNumConfigurations, priv->active_config, priv->dev_id); + // Cache as many config descriptors as we can cache_config_descriptors(dev, hub_handle); @@ -1265,37 +1267,24 @@ static bool get_dev_port_number(HDEVINFO dev_info, SP_DEVINFO_DATA *dev_info_dat } static int enumerate_hcd_root_hub(struct libusb_context *ctx, const char *dev_id, - uint8_t bus_number, DEVINST devinst) + DEVINST devinst) { - struct libusb_device *dev; - struct winusb_device_priv *priv; - unsigned long session_id; DEVINST child_devinst; + struct libusb_device* dev; if (CM_Get_Child(&child_devinst, devinst, 0) != CR_SUCCESS) { usbi_warn(ctx, "could not get child devinst for '%s'", dev_id); return LIBUSB_SUCCESS; } - session_id = (unsigned long)child_devinst; - dev = usbi_get_device_by_session_id(ctx, session_id); + dev = usbi_get_device_by_session_id(ctx, (unsigned long)child_devinst); if (dev == NULL) { - usbi_err(ctx, "program assertion failed - HCD '%s' child not found", dev_id); + usbi_warn(ctx, "HCD '%s' child not found", dev_id); return LIBUSB_SUCCESS; } - if (dev->bus_number == 0) { - // Only do this once - usbi_dbg(ctx, "assigning HCD '%s' bus number %u", dev_id, bus_number); - dev->bus_number = bus_number; - - if (sscanf(dev_id, "PCI\\VEN_%04hx&DEV_%04hx%*s", &dev->device_descriptor.idVendor, &dev->device_descriptor.idProduct) != 2) - usbi_warn(ctx, "could not infer VID/PID of HCD root hub from '%s'", dev_id); - - priv = usbi_get_device_priv(dev); - priv->root_hub = true; - } - + if (sscanf(dev_id, "PCI\\VEN_%04hx&DEV_%04hx%*s", &dev->device_descriptor.idVendor, &dev->device_descriptor.idProduct) != 2) + usbi_warn(ctx, "could not infer VID/PID of HCD from '%s'", dev_id); libusb_unref_device(dev); return LIBUSB_SUCCESS; } @@ -1358,14 +1347,25 @@ static int set_composite_interface(struct libusb_context *ctx, struct libusb_dev struct winusb_device_priv *priv = usbi_get_device_priv(dev); int interface_number; const char *mi_str; + int iadi, iadintfi; + char* endptr; + struct libusb_interface_association_descriptor_array *iad_array; + const struct libusb_interface_association_descriptor *iad; // Because MI_## are not necessarily in sequential order (some composite // devices will have only MI_00 & MI_03 for instance), we retrieve the actual // interface number from the path's MI value mi_str = strstr(device_id, "MI_"); - if ((mi_str != NULL) && isdigit((unsigned char)mi_str[3]) && isdigit((unsigned char)mi_str[4])) { - interface_number = ((mi_str[3] - '0') * 10) + (mi_str[4] - '0'); - } else { + + endptr = NULL; + // This initialization, while redundant, is needed to make MSVC happy + interface_number = -1; + + if (mi_str != NULL) { + interface_number = strtoul(&mi_str[3], &endptr, 16); + } + + if (mi_str == NULL || endptr - &mi_str[3] != 2) { usbi_warn(ctx, "failure to read interface number for %s, using default value", device_id); interface_number = 0; } @@ -1396,6 +1396,27 @@ static int set_composite_interface(struct libusb_context *ctx, struct libusb_dev return LIBUSB_ERROR_NO_MEM; } + // For WinUSBX, set up associations for interfaces grouped by an IAD + if ((api == USB_API_WINUSBX) && !libusb_get_active_interface_association_descriptors(dev, &iad_array)) { + for (iadi = 0; iadi < iad_array->length; iadi++) { + iad = &iad_array->iad[iadi]; + if (iad->bFirstInterface == interface_number) { + priv->usb_interface[interface_number].num_associated_interfaces = iad->bInterfaceCount; + priv->usb_interface[interface_number].first_associated_interface = iad->bFirstInterface; + for (iadintfi = 1; iadintfi < iad->bInterfaceCount; iadintfi++) { + usbi_dbg(ctx, "interface[%d] is associated with interface[%d]", + interface_number + iadintfi, interface_number); + priv->usb_interface[interface_number + iadintfi].apib = &usb_api_backend[api]; + priv->usb_interface[interface_number + iadintfi].sub_api = sub_api; + priv->usb_interface[interface_number + iadintfi].num_associated_interfaces = iad->bInterfaceCount; + priv->usb_interface[interface_number + iadintfi].first_associated_interface = iad->bFirstInterface; + } + break; + } + } + libusb_free_interface_association_descriptors(iad_array); + } + return LIBUSB_SUCCESS; } @@ -1427,6 +1448,140 @@ static int set_hid_interface(struct libusb_context *ctx, struct libusb_device *d return LIBUSB_SUCCESS; } +// get the n-th device interface GUID indexed by guid_number +static int get_guid(struct libusb_context *ctx, char *dev_id, HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, + int guid_number, GUID **if_guid) +{ + DWORD size, reg_type; + HKEY key; + char *guid_string, *new_guid_string; + char *guid, *guid_term; + LONG s; + int pass, guids_left; + int err = LIBUSB_SUCCESS; +#if !defined(ENABLE_LOGGING) + UNUSED(dev_id); +#endif + + key = pSetupDiOpenDevRegKey(*dev_info, dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (key == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "Cannot get the additional GUIDs for '%s'", dev_id); + return LIBUSB_ERROR_ACCESS; + } + // Reserve buffer large enough to hold one GUID with two terminating characters + size = MAX_GUID_STRING_LENGTH + 1; + // Allocate memory for storing the guid_string with two extra terminating characters + // This is necessary for parsing the REG_MULTI_SZ type below + guid_string = malloc(size + 2); + if (guid_string == NULL) { + usbi_err(ctx, "failed to alloc guid_string"); + return LIBUSB_ERROR_NO_MEM; + } + + // The 1st pass tries to get the guid. If it fails due to ERROR_MORE_DATA + // then reallocate enough memory for the 2nd pass + for (pass = 0; pass < 2; pass++) { + // Look for both DeviceInterfaceGUIDs *and* DeviceInterfaceGUID, in that order + // If multiple GUIDs, find the n-th that is indexed by guid_number + s = pRegQueryValueExA(key, "DeviceInterfaceGUIDs", NULL, ®_type, + (LPBYTE)guid_string, &size); + if (s == ERROR_FILE_NOT_FOUND) + s = pRegQueryValueExA(key, "DeviceInterfaceGUID", NULL, ®_type, + (LPBYTE)guid_string, &size); + if (s == ERROR_SUCCESS) { + // The GUID was read successfully + break; + } else if (s == ERROR_FILE_NOT_FOUND) { + usbi_info(ctx, "no DeviceInterfaceGUID registered for '%s'", dev_id); + err = LIBUSB_ERROR_ACCESS; + goto exit; + } else if (s == ERROR_MORE_DATA) { + if (pass == 1) { + // Previous pass should have allocated enough memory, but reading failed + usbi_warn(ctx, "unexpected error from pRegQueryValueExA for '%s'", dev_id); + err = LIBUSB_ERROR_OTHER; + goto exit; + } + new_guid_string = realloc((void *)guid_string, size + 2); + if (new_guid_string == NULL) { + usbi_err(ctx, "failed to realloc guid string"); + err = LIBUSB_ERROR_NO_MEM; + goto exit; + } + guid_string = new_guid_string; + } else { + usbi_warn(ctx, "unexpected error from pRegQueryValueExA for '%s'", dev_id); + err = LIBUSB_ERROR_ACCESS; + goto exit; + } + } + + // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexa#remarks + // - "string may not have been stored with the proper terminating null characters" + // - The following GUIDs should be consider as valid: + // "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\0", "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}", + // "{xxx.....xx}\0\0\0", "{xxx.....xx}\0{xxx.....xx}\0{xxx.....xx}\0", + // "{xxx.....xx}\0{xxx.....xx}\0{xxx.....xx}", "{xxx.....xx}{xxx.....xx}{xxx.....xx}", + // "{xxx.....xx}\0{xxx.....xx}\0{xxx.....xx}\0\0\0\0" + if ((reg_type == REG_SZ ) || (reg_type == REG_MULTI_SZ)) { + /* Get the n-th GUID indexed by guid_number since the DeviceInterfaceGUIDs may + contain more GUIDs */ + guid = guid_string; + // Add two terminating chars for not overrunning the allocated memory while iterating + guid[size] = '\0'; + guid[size + 1] = '\0'; + // Iterate the GUIDs in the guid string + guids_left = guid_number; + while (guids_left) { + guid = strchr(guid, '}'); + if (guid == NULL) { + usbi_warn(ctx, "no GUID with index %d registered for '%s'", guid_number, dev_id); + err = LIBUSB_ERROR_ACCESS; + goto exit; + } + guid++; + // Skip the terminating char if available + if (*guid == '\0') { + guid++; + } + guids_left--; + } + // Add terminating char to the string + guid_term = strchr(guid, '}'); + if (guid_term == NULL) { + usbi_warn(ctx, "no GUID with index %d registered for '%s'", guid_number, dev_id); + err = LIBUSB_ERROR_ACCESS; + goto exit; + } + // Terminate the current guid string to handle the variant without separators + guid_term++; + *guid_term = '\0'; + } else { + usbi_warn(ctx, "unexpected type of DeviceInterfaceGUID for '%s'", dev_id); + err = LIBUSB_ERROR_ACCESS; + goto exit; + } + + *if_guid = malloc(sizeof(GUID)); + if (*if_guid == NULL) { + usbi_err(ctx, "failed to alloc if_guid"); + err = LIBUSB_ERROR_NO_MEM; + goto exit; + } + if (!string_to_guid(guid, *if_guid)) { + usbi_warn(ctx, "device '%s' has malformed DeviceInterfaceGUID string '%s', skipping", dev_id, guid); + free(*if_guid); + *if_guid = NULL; + err = LIBUSB_ERROR_NO_MEM; + goto exit; + } + +exit: + pRegCloseKey(key); + free(guid_string); + return err; +} + /* * get_device_list: libusb backend device enumeration function */ @@ -1439,18 +1594,19 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ GUID hid_guid; int r = LIBUSB_SUCCESS; int api, sub_api; - unsigned int pass, i, j; + unsigned int pass, pass_type, i, j; char enumerator[16]; char dev_id[MAX_PATH_LENGTH]; struct libusb_device *dev, *parent_dev; struct winusb_device_priv *priv, *parent_priv; char *dev_interface_path = NULL; unsigned long session_id; - DWORD size, port_nr, reg_type, install_state; - HKEY key; + DWORD size, port_nr, install_state; + uint8_t bus_number = 0; +#if defined(ENABLE_LOGGING) char guid_string[MAX_GUID_STRING_LENGTH]; +#endif GUID *if_guid; - LONG s; #define HUB_PASS 0 #define DEV_PASS 1 #define HCD_PASS 2 @@ -1471,14 +1627,16 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ libusb_device **unref_list, **new_unref_list; unsigned int unref_size = UNREF_SIZE_STEP; unsigned int unref_cur = 0; + DWORD hub_port_nr; - // PASS 1 : (re)enumerate HCDs (allows for HCD hotplug) - // PASS 2 : (re)enumerate HUBS - // PASS 3 : (re)enumerate generic USB devices (including driverless) - // and list additional USB device interface GUIDs to explore - // PASS 4 : (re)enumerate master USB devices that have a device interface - // PASS 5+: (re)enumerate device interfaced GUIDs (including HID) and - // set the device interfaces. + // PASS 0 : enumerate HUBs + // PASS 1 : (re)enumerate master devices that have a DEVice interface + // PASS 2 : (re)enumerate HCDs (allow for HCD hotplug) + // PASS 3 : (re)enumerate GENeric devices (including driverless) + // and list additional device interface GUIDs to explore + // PASS 4 : (re)enumerate device interface GUIDs (including HID) + // and set the device interfaces + // PASS 5+: (re)enumerate additional EXTra GUID devices // Init the GUID table guid_list = malloc(guid_size * sizeof(void *)); @@ -1515,10 +1673,10 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ } for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) { -//#define ENUM_DEBUG -#if defined(ENABLE_LOGGING) && defined(ENUM_DEBUG) + pass_type = MIN(pass, EXT_PASS); +#if defined(ENABLE_LOGGING) const char * const passname[] = {"HUB", "DEV", "HCD", "GEN", "HID", "EXT"}; - usbi_dbg(ctx, "#### PROCESSING %ss %s", passname[MIN(pass, EXT_PASS)], guid_to_string(guid_list[pass], guid_string)); + usbi_dbg(ctx, "ENUM pass %s %s", passname[pass_type], guid_to_string(guid_list[pass], guid_string)); #endif if ((pass == HID_PASS) && (guid_list[HID_PASS] == NULL)) continue; @@ -1536,11 +1694,6 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ if (r != LIBUSB_SUCCESS) break; - if ((pass == HCD_PASS) && (i == UINT8_MAX)) { - usbi_warn(ctx, "program assertion failed - found more than %u buses, skipping the rest", UINT8_MAX); - break; - } - if (pass != GEN_PASS) { // Except for GEN, all passes deal with device interfaces r = get_interface_details(ctx, *dev_info, &dev_info_data, guid_list[pass], &_index, &dev_interface_path); @@ -1569,14 +1722,12 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ continue; } -#ifdef ENUM_DEBUG - usbi_dbg(ctx, "PRO: %s", dev_id); -#endif + usbi_dbg(ctx, "ENUM processing %s", dev_id); // Set API to use or get additional data from generic pass api = USB_API_UNSUPPORTED; sub_api = SUB_API_NOTSET; - switch (pass) { + switch (pass_type) { case HCD_PASS: break; case HUB_PASS: @@ -1615,68 +1766,44 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ usbi_info(ctx, "libusb will not be able to access it"); } // ...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) - break; - // Look for both DeviceInterfaceGUIDs *and* DeviceInterfaceGUID, in that order - // If multiple GUIDs just process the first and ignore the others - size = sizeof(guid_string); - s = pRegQueryValueExA(key, "DeviceInterfaceGUIDs", NULL, ®_type, - (LPBYTE)guid_string, &size); - if (s == ERROR_FILE_NOT_FOUND) - s = pRegQueryValueExA(key, "DeviceInterfaceGUID", NULL, ®_type, - (LPBYTE)guid_string, &size); - pRegCloseKey(key); - if (s == ERROR_FILE_NOT_FOUND) { - break; /* no DeviceInterfaceGUID registered */ - } else if (s != ERROR_SUCCESS && s != ERROR_MORE_DATA) { - usbi_warn(ctx, "unexpected error from pRegQueryValueExA for '%s'", dev_id); - break; - } - // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexa#remarks - // - "string may not have been stored with the proper terminating null characters" - // - "Note that REG_MULTI_SZ strings could have two terminating null characters" - if ((reg_type == REG_SZ && size >= sizeof(guid_string) - sizeof(char)) - || (reg_type == REG_MULTI_SZ && size >= sizeof(guid_string) - 2 * sizeof(char))) { - 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 = malloc(sizeof(*if_guid)); - if (if_guid == NULL) { - usbi_err(ctx, "failed to alloc if_guid"); - LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + r = get_guid(ctx, dev_id, dev_info, &dev_info_data, 0, &if_guid); + if (r == LIBUSB_SUCCESS) { + // 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 (!string_to_guid(guid_string, if_guid)) { - usbi_warn(ctx, "device '%s' has malformed DeviceInterfaceGUID string '%s', skipping", dev_id, guid_string); - 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(ctx, "extra GUID: %s", guid_string); - guid_list[nb_guids++] = if_guid; - } else { - // Duplicate, ignore - free(if_guid); + if (j == nb_guids) { + usbi_dbg(ctx, "extra GUID: %s", guid_to_string(if_guid, guid_string)); + // Extend the guid_list capacity if needed + 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"); + free(if_guid); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + guid_list = new_guid_list; + guid_size += GUID_SIZE_STEP; } + guid_list[nb_guids++] = if_guid; + } else { + // Duplicate, ignore + free(if_guid); } + } else if (r == LIBUSB_ERROR_ACCESS) { + r = LIBUSB_SUCCESS; + } else if (r == LIBUSB_ERROR_NO_MEM) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); } else { - usbi_warn(ctx, "unexpected type/size of DeviceInterfaceGUID for '%s'", dev_id); + usbi_warn(ctx, "unexpected error during getting DeviceInterfaceGUID for '%s'", dev_id); } break; case HID_PASS: api = USB_API_HID; break; - default: + case DEV_PASS: + case EXT_PASS: // Get the API type (after checking that the driver installation is OK) if ((!pSetupDiGetDeviceRegistryPropertyA(*dev_info, &dev_info_data, SPDRP_INSTALL_STATE, NULL, (PBYTE)&install_state, sizeof(install_state), &size)) || (size != sizeof(install_state))) { @@ -1689,6 +1816,8 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ } get_api_type(dev_info, &dev_info_data, &api, &sub_api); break; + default: + assert(false); // unreachable since all pass types covered explicitly } // Find parent device (for the passes that need it) @@ -1768,7 +1897,7 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ } // Setup device - switch (pass) { + switch (pass_type) { case HUB_PASS: case DEV_PASS: // If the device has already been setup, don't do it again @@ -1781,7 +1910,22 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ priv->sub_api = sub_api; switch (api) { case USB_API_COMPOSITE: + break; case USB_API_HUB: + parent_dev = get_ancestor(ctx, dev_info_data.DevInst, NULL); + if (parent_dev == NULL) { + if (!get_dev_port_number(*dev_info, &dev_info_data, &hub_port_nr) || hub_port_nr == 0) { + if (bus_number == UINT8_MAX) { + usbi_warn(ctx, "program assertion failed - found more than %u buses, skipping the rest", UINT8_MAX); + break; + } + priv->root_hub = true; + dev->bus_number = ++bus_number; + usbi_dbg(ctx, "assigning Root Hub '%s' bus number %u", dev_id, bus_number); + } + } else { + libusb_unref_device(parent_dev); + } break; case USB_API_HID: priv->hid = calloc(1, sizeof(struct hid_device_priv)); @@ -1801,7 +1945,7 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ } break; case HCD_PASS: - r = enumerate_hcd_root_hub(ctx, dev_id, (uint8_t)(i + 1), dev_info_data.DevInst); + r = enumerate_hcd_root_hub(ctx, dev_id, dev_info_data.DevInst); break; case GEN_PASS: port_nr = 0; @@ -1822,7 +1966,8 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ r = LIBUSB_SUCCESS; } break; - default: // HID_PASS and later + case HID_PASS: + case EXT_PASS: if (parent_priv->apib->id == USB_API_HID || parent_priv->apib->id == USB_API_COMPOSITE) { if (parent_priv->apib->id == USB_API_HID) { usbi_dbg(ctx, "setting HID interface for [%lX]:", parent_dev->session_data); @@ -1846,6 +1991,8 @@ static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_ } libusb_unref_device(parent_dev); break; + default: + assert(false); // unreachable since all pass types covered explicitly } } } @@ -2152,7 +2299,10 @@ const struct windows_backend winusb_backend = { * USB API backends */ -static const char * const composite_driver_names[] = {"USBCCGP"}; +static const char * const composite_driver_names[] = { + "USBCCGP", // (Windows built-in) USB Composite Device + "dg_ssudbus" // SAMSUNG Mobile USB Composite Device +}; static const char * const winusbx_driver_names[] = {"libusbK", "libusb0", "WinUSB"}; static const char * const hid_driver_names[] = {"HIDUSB", "MOUHID", "KBDHID"}; const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { @@ -2301,6 +2451,7 @@ static bool winusbx_init(struct libusb_context *ctx) WinUSB_Set(hWinUSB, ResetPipe, true); WinUSB_Set(hWinUSB, SetCurrentAlternateSetting, true); WinUSB_Set(hWinUSB, SetPipePolicy, true); + WinUSB_Set(hWinUSB, GetPipePolicy, true); WinUSB_Set(hWinUSB, WritePipe, true); // Check for isochronous transfers support (available starting with Windows 8.1) @@ -2463,7 +2614,7 @@ static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle) struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle); struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); HANDLE handle; - int i; + int i, ai; if (sub_api == SUB_API_NOTSET) sub_api = priv->sub_api; @@ -2472,17 +2623,41 @@ static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle) return; if (priv->apib->id == USB_API_COMPOSITE) { - // If this is a composite device, just free and close all WinUSB-like - // interfaces directly (each is independent and not associated with another) + // If this is a composite device, just free and close any WinUSB-like + // interfaces that are not part of an associated group + // (each is independent and not associated with another). + // For associated interface groupings, free interfaces that + // are NOT the first within that group (i.e. not bFirstInterface), + // then free & close bFirstInterface last. for (i = 0; i < USB_MAXINTERFACES; i++) { if (priv->usb_interface[i].apib->id == USB_API_WINUSBX) { - handle = handle_priv->interface_handle[i].api_handle; - if (HANDLE_VALID(handle)) - WinUSBX[sub_api].Free(handle); + if (priv->usb_interface[i].num_associated_interfaces == 0) { + handle = handle_priv->interface_handle[i].api_handle; + if (HANDLE_VALID(handle)) + WinUSBX[sub_api].Free(handle); + + handle = handle_priv->interface_handle[i].dev_handle; + if (HANDLE_VALID(handle)) + CloseHandle(handle); + } else { + if (i==priv->usb_interface[i].first_associated_interface) { + //first free all handles for all *other* associated interfaces + for (ai = 1; ai < priv->usb_interface[i].num_associated_interfaces; ai++) { + handle = handle_priv->interface_handle[i + ai].api_handle; + if (HANDLE_VALID(handle)) + WinUSBX[sub_api].Free(handle); + } + + //free & close bFirstInterface + handle = handle_priv->interface_handle[i].api_handle; + if (HANDLE_VALID(handle)) + WinUSBX[sub_api].Free(handle); - handle = handle_priv->interface_handle[i].dev_handle; - if (HANDLE_VALID(handle)) - CloseHandle(handle); + handle = handle_priv->interface_handle[i].dev_handle; + if (HANDLE_VALID(handle)) + CloseHandle(handle); + } + } } } } else { @@ -2563,6 +2738,7 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle); struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev); bool is_using_usbccgp = (priv->apib->id == USB_API_COMPOSITE); + bool is_associated_interface = (priv->usb_interface[iface].num_associated_interfaces != 0); HDEVINFO dev_info; char *dev_interface_path = NULL; char *dev_interface_path_guid_start; @@ -2571,12 +2747,18 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev HANDLE file_handle, winusb_handle; DWORD err, _index; int r; + uint8_t initialized_iface; CHECK_WINUSBX_AVAILABLE(sub_api); // If the device is composite, but using the default Windows composite parent driver (usbccgp) // or if it's the first WinUSB-like interface, we get a handle through Initialize(). - if ((is_using_usbccgp) || (iface == 0)) { + // If it's an associated interface, and is the first one (iface==bFirstInterface), we also + // want to get the handle through Initialize(). If it's an associated interface, and NOT + // the first one, we want to direct control to the 'else' where the handle will be obtained + // via GetAssociatedInterface(). + if (((is_using_usbccgp) || (iface == 0)) && + (!is_associated_interface || (iface==priv->usb_interface[iface].first_associated_interface))) { // composite device (independent interfaces) or interface 0 file_handle = handle_priv->interface_handle[iface].dev_handle; if (!HANDLE_VALID(file_handle)) @@ -2637,21 +2819,32 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev } handle_priv->interface_handle[iface].api_handle = winusb_handle; } else { + if (is_associated_interface) { + initialized_iface = priv->usb_interface[iface].first_associated_interface; + if (iface <= initialized_iface) { + usbi_err(ctx, "invalid associated index. iface=%u, initialized iface=%u", iface, initialized_iface); + return LIBUSB_ERROR_NOT_FOUND; + } + } else { + initialized_iface = 0; + } + // For all other interfaces, use GetAssociatedInterface() - winusb_handle = handle_priv->interface_handle[0].api_handle; + winusb_handle = handle_priv->interface_handle[initialized_iface].api_handle; // It is a requirement for multiple interface devices on Windows that, to you // must first claim the first interface before you claim the others if (!HANDLE_VALID(winusb_handle)) { - file_handle = handle_priv->interface_handle[0].dev_handle; + file_handle = handle_priv->interface_handle[initialized_iface].dev_handle; if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) { - handle_priv->interface_handle[0].api_handle = winusb_handle; - usbi_warn(ctx, "auto-claimed interface 0 (required to claim %u with WinUSB)", iface); + handle_priv->interface_handle[initialized_iface].api_handle = winusb_handle; + usbi_warn(ctx, "auto-claimed interface %u (required to claim %u with WinUSB)", initialized_iface, iface); } else { - usbi_warn(ctx, "failed to auto-claim interface 0 (required to claim %u with WinUSB): %s", iface, windows_error_str(0)); + usbi_warn(ctx, "failed to auto-claim interface %u (required to claim %u with WinUSB): %s", + initialized_iface, iface, windows_error_str(0)); return LIBUSB_ERROR_ACCESS; } } - if (!WinUSBX[sub_api].GetAssociatedInterface(winusb_handle, (UCHAR)(iface - 1), + if (!WinUSBX[sub_api].GetAssociatedInterface(winusb_handle, (UCHAR)(iface - 1 - initialized_iface), &handle_priv->interface_handle[iface].api_handle)) { handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE; switch (GetLastError()) { @@ -2666,7 +2859,7 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev return LIBUSB_ERROR_ACCESS; } } - handle_priv->interface_handle[iface].dev_handle = handle_priv->interface_handle[0].dev_handle; + handle_priv->interface_handle[iface].dev_handle = handle_priv->interface_handle[initialized_iface].dev_handle; } usbi_dbg(ctx, "claimed interface %u", iface); handle_priv->active_interface = iface; @@ -2849,7 +3042,7 @@ static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_ha static void WINAPI winusbx_native_iso_transfer_continue_stream_callback(struct libusb_transfer *transfer) { // If this callback is invoked, this means that we attempted to set ContinueStream - // to TRUE when calling Read/WriteIsochPipeAsap in winusbx_do_iso_transfer. + // to TRUE when calling Read/WriteIsochPipeAsap in winusbx_submit_iso_transfer(). // The role of this callback is to fallback to ContinueStream = FALSE if the transfer // did not succeed. @@ -3252,8 +3445,7 @@ static enum libusb_transfer_status winusbx_copy_transfer_data(int sub_api, struc // Copy the requested value back for consistency with other platforms. transfer->iso_packet_desc[i].actual_length = transfer->iso_packet_desc[i].length; } - // TODO translate USDB_STATUS codes http://msdn.microsoft.com/en-us/library/ff539136(VS.85).aspx to libusb_transfer_status - //transfer->iso_packet_desc[i].status = transfer_priv->iso_context->IsoPackets[i].status; + transfer->iso_packet_desc[i].status = usbd_status_to_libusb_transfer_status(iso_context->IsoPackets[i].status); } } else if (sub_api == SUB_API_WINUSB) { if (IS_XFERIN(transfer)) { |