From: Peter Stuge Date: Tue, 10 May 2011 08:05:29 +0000 (+0200) Subject: Windows: enumeration overhaul X-Git-Tag: upstream/1.0.21~832 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3ccd9bddec316ee4b867959fd7f616eb4d410997;p=platform%2Fupstream%2Flibusb.git Windows: enumeration overhaul * uses multiple passes in a single call * uses a hash table, in anticipation for hotplug * adds a (dummy) HUB interface for harmonization * adds calloc on device struct creation in core (to detect and avoid unnecessary double initialization) --- diff --git a/libusb/core.c b/libusb/core.c index 6605c96..c40d9d5 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -508,7 +508,7 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, unsigned long session_id) { size_t priv_size = usbi_backend->device_priv_size; - struct libusb_device *dev = malloc(sizeof(*dev) + priv_size); + struct libusb_device *dev = calloc(1, sizeof(*dev) + priv_size); int r; if (!dev) diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c index 6750c55..6983287 100644 --- a/libusb/os/windows_usb.c +++ b/libusb/os/windows_usb.c @@ -3,6 +3,7 @@ * Copyright (c) 2009-2010 Pete Batard * With contributions from Michael Plante, Orin Eman et al. * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * Hash table functions adapted from glibc, by Ulrich Drepper et al. * Major code testing contribution by Xiaofan Chen * * This library is free software; you can redistribute it and/or @@ -93,7 +94,6 @@ static int composite_copy_transfer_data(struct usbi_transfer *itransfer, uint32_ // Global variables -struct windows_hcd_priv* hcd_root = NULL; uint64_t hires_frequency, hires_ticks_to_ps; const uint64_t epoch_time = UINT64_C(116444736000000000); // 1970.01.01 00:00:000 in MS Filetime enum windows_version windows_version = WINDOWS_UNSUPPORTED; @@ -114,32 +114,6 @@ HANDLE timer_response = NULL; bool api_winusb_available = false; #define CHECK_WINUSB_AVAILABLE do { if (!api_winusb_available) return LIBUSB_ERROR_ACCESS; } while (0) - -/* - * Converts a WCHAR string to UTF8 (allocate returned string) - * Returns NULL on error - */ -static char* wchar_to_utf8(LPCWSTR wstr) -{ - int size; - char* str; - - // Find out the size we need to allocate for our converted string - size = wchar_to_utf8_ms(wstr, NULL, 0); - if (size <= 1) // An empty string would be size 1 - return NULL; - - if ((str = malloc(size)) == NULL) - return NULL; - - if (wchar_to_utf8_ms(wstr, str, size) != size) { - safe_free(str); - return NULL; - } - - return str; -} - static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) { if ((guid1 != NULL) && (guid2 != NULL)) { return (memcmp(guid1, guid2, sizeof(GUID)) == 0); @@ -147,15 +121,16 @@ static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) { return false; } -#if 0 -static char* guid_to_string(const GUID guid) +#if defined(ENABLE_DEBUG_LOGGING) +static char* guid_to_string(const GUID* guid) { static char guid_string[MAX_GUID_STRING_LENGTH]; + if (guid == NULL) return NULL; sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", - (unsigned int)guid.Data1, guid.Data2, guid.Data3, - guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], - guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); + (unsigned int)guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); return guid_string; } #endif @@ -247,7 +222,6 @@ static int init_dlls(void) DLL_LOAD(Cfgmgr32.dll, CM_Get_Child, TRUE); DLL_LOAD(Cfgmgr32.dll, CM_Get_Sibling, TRUE); DLL_LOAD(Cfgmgr32.dll, CM_Get_Device_IDA, TRUE); - // Prefixed to avoid conflict with header files DLL_LOAD_PREFIXED(OLE32.dll, p, CLSIDFromString, TRUE); DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiGetClassDevsA, TRUE); @@ -257,11 +231,48 @@ static int init_dlls(void) DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiDestroyDeviceInfoList, TRUE); DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiOpenDevRegKey, TRUE); DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiGetDeviceRegistryPropertyA, TRUE); - DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiGetDeviceRegistryPropertyW, TRUE); + DLL_LOAD_PREFIXED(AdvAPI32.dll, p, RegQueryValueExW, TRUE); + DLL_LOAD_PREFIXED(AdvAPI32.dll, p, RegCloseKey, TRUE); return LIBUSB_SUCCESS; } /* + * enumerate interfaces for the whole USB class + * + * 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) + * guid: the GUID 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 + * structure returned and call this function repeatedly using the same guid (with an + * 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, unsigned _index) +{ + if (_index <= 0) { + *dev_info = pSetupDiGetClassDevsA(NULL, "USB", NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); + if (*dev_info == INVALID_HANDLE_VALUE) { + 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)); + } + pSetupDiDestroyDeviceInfoList(*dev_info); + *dev_info = INVALID_HANDLE_VALUE; + return false; + } + return true; +} + +/* * enumerate interfaces for a specific GUID * * Parameters: @@ -275,17 +286,14 @@ static int init_dlls(void) * incremented index starting at zero) until all interfaces have been returned. */ SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details(struct libusb_context *ctx, - HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, GUID guid, unsigned _index) + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID* guid, unsigned _index) { SP_DEVICE_INTERFACE_DATA dev_interface_data; SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; DWORD size; if (_index <= 0) { - *dev_info = pSetupDiGetClassDevsA(&guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); - } - if (*dev_info == INVALID_HANDLE_VALUE) { - return NULL; + *dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); } if (dev_info_data != NULL) { @@ -302,7 +310,7 @@ SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details(struct libusb_context * } dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, &guid, _index, &dev_interface_data)) { + if (!pSetupDiEnumDeviceInterfaces(*dev_info, NULL, guid, _index, &dev_interface_data)) { if (GetLastError() != ERROR_NO_MORE_ITEMS) { usbi_err(ctx, "Could not obtain interface data for index %u: %s", _index, windows_error_str(0)); @@ -320,8 +328,7 @@ SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details(struct libusb_context * _index, windows_error_str(0)); goto err_exit; } - } - else { + } else { usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong."); goto err_exit; } @@ -346,6 +353,205 @@ err_exit: return NULL; } +/* Hash table functions - modified From glibc 2.3.2: + [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 + [Knuth] The Art of Computer Programming, part 3 (6.4) */ +typedef struct htab_entry { + unsigned long used; + char* str; +} htab_entry; +static htab_entry* htab_table = NULL; +static usbi_mutex_t htab_write_mutex = NULL; +static unsigned long htab_size, htab_filled; + +/* For the used double hash method the table size has to be a prime. To + correct the user given table size we need a prime test. This trivial + algorithm is adequate because the code is called only during init and + the number is likely to be small */ +static int isprime(unsigned long number) +{ + // no even number will be passed + unsigned int divider = 3; + + while((divider * divider < number) && (number % divider != 0)) + divider += 2; + + return (number % divider != 0); +} + +/* Before using the hash table we must allocate memory for it. + We allocate one element more as the found prime number says. + This is done for more effective indexing as explained in the + comment for the hash function. */ +static int htab_create(struct libusb_context *ctx, unsigned long nel) +{ + if (htab_table != NULL) { + usbi_err(ctx, "hash table already allocated"); + } + + // Create a mutex + usbi_mutex_init(&htab_write_mutex, NULL); + + // Change nel to the first prime number not smaller as nel. + nel |= 1; + while(!isprime(nel)) + nel += 2; + + htab_size = nel; + usbi_dbg("using %d entries hash table", nel); + htab_filled = 0; + + // allocate memory and zero out. + htab_table = (htab_entry*)calloc(htab_size + 1, sizeof(htab_entry)); + if (htab_table == NULL) { + usbi_err(ctx, "could not allocate space for hash table"); + return 0; + } + + return 1; +} + +/* After using the hash table it has to be destroyed. */ +static void htab_destroy(void) +{ + size_t i; + if (htab_table == NULL) { + return; + } + + for (i=0; i New entry + + // If the table is full return an error + if (htab_filled >= htab_size) { + usbi_err(NULL, "hash table is full (%d entries)", htab_size); + return 0; + } + + // Concurrent threads might be storing the same entry at the same time + // (eg. "simultaneous" enums from different threads) => use a mutex + usbi_mutex_lock(&htab_write_mutex); + // Just free any previously allocated string (which should be the same as + // new one). The possibility of concurrent threads storing a collision + // string (same hash, different string) at the same time is extremely low + safe_free(htab_table[idx].str); + htab_table[idx].used = hval; + htab_table[idx].str = malloc(safe_strlen(str)+1); + if (htab_table[idx].str == NULL) { + usbi_err(NULL, "could not duplicate string for hash table"); + usbi_mutex_unlock(&htab_write_mutex); + return 0; + } + memcpy(htab_table[idx].str, str, safe_strlen(str)+1); + ++htab_filled; + usbi_mutex_unlock(&htab_write_mutex); + + return idx; +} + +/* + * Returns the session ID of a device's nth level ancestor + * If there's no device at the nth level, return 0 + */ +static unsigned long get_ancestor_session_id(DWORD devinst, unsigned level) +{ + DWORD parent_devinst; + unsigned long session_id = 0; + char* sanitized_path = NULL; + char path[MAX_PATH_LENGTH]; + unsigned i; + + if (level < 1) return 0; + for (i = 0; iusb_interface[current_interface].apib == &usb_api_backend[api_type]) + if ( (priv->usb_interface[current_interface].apib->id == api_type) && (libusb_claim_interface(transfer->dev_handle, current_interface) == LIBUSB_SUCCESS) ) { usbi_dbg("auto-claimed interface %d for control request", current_interface); if (handle_priv->autoclaim_count[current_interface] != 0) { @@ -492,14 +698,9 @@ static void auto_release(struct usbi_transfer *itransfer) */ static int windows_init(struct libusb_context *ctx) { - HDEVINFO dev_info; - SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL; - GUID guid; - libusb_bus_t bus; int i, r = LIBUSB_ERROR_OTHER; OSVERSIONINFO os_version; HANDLE semaphore; - struct windows_hcd_priv** _hcd_cur; char sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID) sprintf(sem_name, "libusb_init%08X", (unsigned int)GetCurrentProcessId()&0xFFFFFFFF); @@ -515,13 +716,11 @@ static int windows_init(struct libusb_context *ctx) usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0)); CloseHandle(semaphore); return LIBUSB_ERROR_NO_MEM; - } + } // NB: concurrent usage supposes that init calls are equally balanced with // exit calls. If init is called more than exit, we will not exit properly if ( ++concurrent_usage == 0 ) { // First init? - _hcd_cur = &hcd_root; - // Detect OS version memset(&os_version, 0, sizeof(OSVERSIONINFO)); os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); @@ -588,47 +787,14 @@ static int windows_init(struct libusb_context *ctx) } SetThreadAffinityMask(timer_thread, 0); - guid = GUID_DEVINTERFACE_USB_HOST_CONTROLLER; - - r = LIBUSB_SUCCESS; - for (bus = 0; ; bus++) - { - // safe loop: free up any (unprotected) dynamic resource - // NB: this is always executed before breaking the loop - safe_free(dev_interface_details); - safe_free(*_hcd_cur); - - dev_interface_details = get_interface_details(ctx, &dev_info, NULL, guid, bus); - // safe loop: end of loop condition - if ((dev_interface_details == NULL) || (r != LIBUSB_SUCCESS)) - break; - - // Will need to change storage and size of libusb_bus_t if this ever occurs - if (bus == LIBUSB_BUS_MAX) { - usbi_warn(ctx, "program assertion failed - found more than %d buses, skipping the rest.", LIBUSB_BUS_MAX); - continue; - } - - // Allocate and init a new priv structure to hold our data - if ((*_hcd_cur = malloc(sizeof(struct windows_hcd_priv))) == NULL) { - usbi_err(ctx, "could not allocate private structure for bus %u. aborting.", bus); - LOOP_BREAK(LIBUSB_ERROR_NO_MEM); - } - windows_hcd_priv_init(*_hcd_cur); - (*_hcd_cur)->path = sanitize_path(dev_interface_details->DevicePath); - - _hcd_cur = &((*_hcd_cur)->next); - } - // TODO (2nd official release): thread for hotplug (see darwin source) + // Create a hash table to store session ids. Second parameter is better if prime + htab_create(ctx, HTAB_SIZE); } - - if (hcd_root == NULL) - r = LIBUSB_ERROR_NO_DEVICE; - else - r = LIBUSB_SUCCESS; + // At this stage, either we went through full init successfully, or didn't need to + r = LIBUSB_SUCCESS; init_exit: // Holds semaphore here. - if(!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed? + if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed? if (timer_thread) { SetEvent(timer_request[1]); // actually the signal to quit the thread. if (WAIT_OBJECT_0 != WaitForSingleObject(timer_thread, INFINITE)) { @@ -653,6 +819,7 @@ init_exit: // Holds semaphore here. CloseHandle(timer_mutex); timer_mutex = NULL; } + htab_destroy(); } if (r != LIBUSB_SUCCESS) @@ -664,88 +831,43 @@ init_exit: // Holds semaphore here. } /* - * Initialize device structure, including active config - */ -static int initialize_device(struct libusb_device *dev, libusb_bus_t busnum, - libusb_devaddr_t devaddr, char *path, int connection_index, uint8_t active_config, - struct libusb_device *parent_dev) -{ - struct windows_device_priv *priv = __device_priv(dev); - - windows_device_priv_init(priv); - - dev->bus_number = busnum; - dev->device_address = devaddr; - priv->path = path; - priv->connection_index = connection_index; - priv->parent_dev = parent_dev; - - priv->active_config = active_config; - - if (priv->active_config != 0) { - usbi_dbg("active config: %d", priv->active_config); - } else { - // USB devices that don't have a config value are usually missing a driver - // TODO (after first official release): use this for automated driver installation - // NB: SetupDiGetDeviceRegistryProperty w/ SPDRP_INSTALL_STATE would tell us - // if the driver is properly installed, but driverless devices don't seem to - // be enumerable by SetupDi... - usbi_dbg("* This device has no driver => libusb will not be able to access it *"); - } - - return LIBUSB_SUCCESS; -} - -/* * HCD (root) hubs need to have their device descriptor manually populated * - * Note that we follow the Linux convention and use the "Linux Foundation root hub" - * vendor ID as well as the product ID to indicate the hub speed + * Note that, like Microsoft does in the device manager, we populate the + * Vendor and Device ID for HCD hubs with the ones from the PCI HCD device. */ -static int force_hcd_device_descriptor(struct libusb_device *dev, HANDLE handle) +static int force_hcd_device_descriptor(struct libusb_device *dev) { - DWORD size; - USB_HUB_CAPABILITIES hub_caps; - USB_HUB_CAPABILITIES_EX hub_caps_ex; - struct windows_device_priv *priv = __device_priv(dev); + struct windows_device_priv *parent_priv, *priv = __device_priv(dev); struct libusb_context *ctx = DEVICE_CTX(dev); + int vid, pid; + dev->num_configurations = 1; priv->dev_descriptor.bLength = sizeof(USB_DEVICE_DESCRIPTOR); priv->dev_descriptor.bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE; - dev->num_configurations = priv->dev_descriptor.bNumConfigurations = 1; - - // The following is used to set the VIS:PID of root HUBs similarly to what - // Linux does: 1d6b:0001 is for 1x root hubs, 1d6b:0002 for 2x - priv->dev_descriptor.idVendor = 0x1d6b; // Linux Foundation root hub - if (windows_version >= WINDOWS_VISTA_AND_LATER) { - size = sizeof(USB_HUB_CAPABILITIES_EX); - if (DeviceIoControl(handle, IOCTL_USB_GET_HUB_CAPABILITIES_EX, &hub_caps_ex, - size, &hub_caps_ex, size, &size, NULL)) { - // Sanity check. HCD hub should always be root - if (!hub_caps_ex.CapabilityFlags.HubIsRoot) { - usbi_warn(ctx, "program assertion failed - HCD hub is not reported as root hub."); - } - priv->dev_descriptor.idProduct = hub_caps_ex.CapabilityFlags.HubIsHighSpeedCapable?2:1; - } + priv->dev_descriptor.bNumConfigurations = 1; + priv->active_config = 1; + + if (priv->parent_dev == NULL) { + usbi_err(ctx, "program assertion failed - HCD hub has no parent"); + return LIBUSB_ERROR_NO_DEVICE; + } + parent_priv = __device_priv(priv->parent_dev); + if (sscanf(parent_priv->path, "\\\\.\\PCI#VEN_%04x&DEV_%04x%*s", &vid, &pid) == 2) { + priv->dev_descriptor.idVendor = (uint16_t)vid; + priv->dev_descriptor.idProduct = (uint16_t)pid; } else { - size = sizeof(USB_HUB_CAPABILITIES); - if (!DeviceIoControl(handle, IOCTL_USB_GET_HUB_CAPABILITIES, &hub_caps, - size, &hub_caps, size, &size, NULL)) { - usbi_warn(ctx, "could not read hub capabilities (std) for hub %s: %s", - priv->path, windows_error_str(0)); - priv->dev_descriptor.idProduct = 1; // Indicate 1x speed - } else { - priv->dev_descriptor.idProduct = hub_caps.HubIs2xCapable?2:1; - } + usbi_warn(ctx, "could not infer VID/PID of HCD hub from '%s'", parent_priv->path); + priv->dev_descriptor.idVendor = 0x1d6b; // Linux Foundation root hub + priv->dev_descriptor.idProduct = 1; } - return LIBUSB_SUCCESS; } /* * fetch and cache all the config descriptors through I/O */ -static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle) +static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle, char* device_id) { DWORD size, ret_size; struct libusb_context *ctx = DEVICE_CTX(dev); @@ -778,7 +900,7 @@ static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle size = sizeof(USB_CONFIGURATION_DESCRIPTOR_SHORT); memset(&cd_buf_short, 0, size); - cd_buf_short.req.ConnectionIndex = priv->connection_index; + cd_buf_short.req.ConnectionIndex = (ULONG)priv->port; cd_buf_short.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; cd_buf_short.req.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR; cd_buf_short.req.SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i; @@ -788,24 +910,24 @@ static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle // Dummy call to get the required data size if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &cd_buf_short, size, &cd_buf_short, size, &ret_size, NULL)) { - usbi_err(ctx, "could not access configuration descriptor (dummy): %s", windows_error_str(0)); + usbi_err(ctx, "could not access configuration descriptor (dummy) for '%s': %s", device_id, windows_error_str(0)); LOOP_BREAK(LIBUSB_ERROR_IO); } if ((ret_size != size) || (cd_buf_short.data.wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))) { - usbi_err(ctx, "unexpected configuration descriptor size (dummy)."); + usbi_err(ctx, "unexpected configuration descriptor size (dummy) for '%s'.", device_id); LOOP_BREAK(LIBUSB_ERROR_IO); } size = sizeof(USB_DESCRIPTOR_REQUEST) + cd_buf_short.data.wTotalLength; if ((cd_buf_actual = (PUSB_DESCRIPTOR_REQUEST)malloc(size)) == NULL) { - usbi_err(ctx, "could not allocate configuration descriptor buffer. aborting."); + usbi_err(ctx, "could not allocate configuration descriptor buffer for '%s'.", device_id); LOOP_BREAK(LIBUSB_ERROR_NO_MEM); } memset(cd_buf_actual, 0, size); // Actual call - cd_buf_actual->ConnectionIndex = priv->connection_index; + cd_buf_actual->ConnectionIndex = (ULONG)priv->port; cd_buf_actual->SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN; cd_buf_actual->SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR; cd_buf_actual->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i; @@ -814,19 +936,19 @@ static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, cd_buf_actual, size, cd_buf_actual, size, &ret_size, NULL)) { - usbi_err(ctx, "could not access configuration descriptor (actual): %s", windows_error_str(0)); + usbi_err(ctx, "could not access configuration descriptor (actual) for '%s': %s", device_id, windows_error_str(0)); LOOP_BREAK(LIBUSB_ERROR_IO); } cd_data = (PUSB_CONFIGURATION_DESCRIPTOR)((UCHAR*)cd_buf_actual+sizeof(USB_DESCRIPTOR_REQUEST)); if ((size != ret_size) || (cd_data->wTotalLength != cd_buf_short.data.wTotalLength)) { - usbi_err(ctx, "unexpected configuration descriptor size (actual)."); + usbi_err(ctx, "unexpected configuration descriptor size (actual) for '%s'.", device_id); LOOP_BREAK(LIBUSB_ERROR_IO); } if (cd_data->bDescriptorType != USB_CONFIGURATION_DESCRIPTOR_TYPE) { - usbi_err(ctx, "not a configuration descriptor"); + usbi_err(ctx, "not a configuration descriptor for '%s'", device_id); LOOP_BREAK(LIBUSB_ERROR_IO); } @@ -844,628 +966,535 @@ static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle } /* - * Recursively enumerates and finds all hubs & devices + * Populate a libusb device structure */ -static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs **_discdevs, - HANDLE hub_handle, libusb_bus_t busnum, struct libusb_device *parent_dev, uint8_t nb_ports) +static int init_device(struct libusb_device* dev, struct libusb_device* parent_dev, + uint8_t port_number, char* device_id, DWORD devinst) { - struct discovered_devs *discdevs = *_discdevs; - struct libusb_device *dev = NULL; - DWORD size, size_initial, size_fixed, getname_ioctl; - HANDLE handle = INVALID_HANDLE_VALUE; - USB_HUB_NAME_FIXED s_hubname; + HANDLE handle; + DWORD size; USB_NODE_CONNECTION_INFORMATION conn_info; - USB_NODE_INFORMATION hub_node; - bool is_hcd, need_unref = false; - int i, r; - LPCWSTR wstr; - char *tmp_str = NULL, *path_str = NULL; - unsigned long session_id; - libusb_devaddr_t devaddr = 0; struct windows_device_priv *priv, *parent_priv; + struct libusb_context *ctx = DEVICE_CTX(dev); + struct libusb_device* tmp_dev; + unsigned i; - // obviously, root (HCD) hubs have no parent - is_hcd = (parent_dev == NULL); - if (is_hcd) - { - if (nb_ports != 1) { - usbi_warn(ctx, "program assertion failed - invalid number of ports for HCD."); - return LIBUSB_ERROR_INVALID_PARAM; + if ((dev == NULL) || (parent_dev == NULL)) { + return LIBUSB_ERROR_NOT_FOUND; + } + priv = __device_priv(dev); + parent_priv = __device_priv(parent_dev); + if (parent_priv->apib->id != USB_API_HUB) { + usbi_warn(ctx, "parent for device '%s' is not a hub", device_id); + return LIBUSB_ERROR_NOT_FOUND; + } + + // It is possible for the parent hub not to have been initialized yet + // If that's the case, lookup the ancestors to set the bus number + if (parent_dev->bus_number == 0) { + for (i=2; ; i++) { + tmp_dev = usbi_get_device_by_session_id(ctx, get_ancestor_session_id(devinst, i)); + if (tmp_dev == NULL) break; + if (tmp_dev->bus_number != 0) { + usbi_dbg("got bus number from ancestor #%d", i); + parent_dev->bus_number = tmp_dev->bus_number; + break; + } } - parent_priv = NULL; - size_initial = sizeof(USB_ROOT_HUB_NAME); - size_fixed = sizeof(USB_ROOT_HUB_NAME_FIXED); - getname_ioctl = IOCTL_USB_GET_ROOT_HUB_NAME; } - else - { - parent_priv = __device_priv(parent_dev); - size_initial = sizeof(USB_NODE_CONNECTION_NAME); - size_fixed = sizeof(USB_NODE_CONNECTION_NAME_FIXED); - getname_ioctl = IOCTL_USB_GET_NODE_CONNECTION_NAME; + if (parent_dev->bus_number == 0) { + usbi_err(ctx, "program assertion failed: unable to find ancestor bus number for '%s'", device_id); + return LIBUSB_ERROR_NOT_FOUND; } + dev->bus_number = parent_dev->bus_number; + priv->port = port_number; + priv->depth = parent_priv->depth + 1; + priv->parent_dev = parent_dev; - // Loop through all the ports on this hub - for (i = 1, r = LIBUSB_SUCCESS; ; i++) - { - // safe loop: release all dynamic resources - if (need_unref) { - safe_unref_device(dev); - need_unref = false; + // If the device address is already set, we can stop here + if (dev->device_address != 0) { + return LIBUSB_SUCCESS; + } + memset(&conn_info, 0, sizeof(conn_info)); + if (priv->depth != 0) { // Not a HCD hub + handle = CreateFileA(parent_priv->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + if (handle == INVALID_HANDLE_VALUE) { + usbi_warn(ctx, "could not open hub %s: %s", parent_priv->path, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + size = sizeof(USB_NODE_CONNECTION_INFORMATION); + conn_info.ConnectionIndex = (ULONG)port_number; + if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION, &conn_info, size, + &conn_info, size, &size, NULL)) { + usbi_warn(ctx, "could not get node connection information for device '%s': %s", + device_id, windows_error_str(0)); + safe_closehandle(handle); + return LIBUSB_ERROR_NO_DEVICE; + } + if (conn_info.ConnectionStatus == NoDeviceConnected) { + usbi_err(ctx, "device '%s' is no longer connected!", device_id); + safe_closehandle(handle); + return LIBUSB_ERROR_NO_DEVICE; + } + dev->device_address = (uint8_t)conn_info.DeviceAddress; + memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR)); + dev->num_configurations = priv->dev_descriptor.bNumConfigurations; + priv->active_config = conn_info.CurrentConfigurationValue; + usbi_dbg("found %d configurations (active conf: %d)", dev->num_configurations, priv->active_config); + // If we can't read the config descriptors, just set the number of confs to zero + if (cache_config_descriptors(dev, handle, device_id) != LIBUSB_SUCCESS) { + dev->num_configurations = 0; + priv->dev_descriptor.bNumConfigurations = 0; } - safe_free(tmp_str); - safe_free(path_str); safe_closehandle(handle); - // safe loop: end of loop condition - if ((i > nb_ports) || (r != LIBUSB_SUCCESS)) - break; - - memset(&conn_info, 0, sizeof(conn_info)); - // For non HCDs, check if the node on this port is a hub or a regular device - if (!is_hcd) { - size = sizeof(USB_NODE_CONNECTION_INFORMATION); - conn_info.ConnectionIndex = i; - if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION, &conn_info, size, - &conn_info, size, &size, NULL)) { - usbi_warn(ctx, "could not get node connection information: %s", windows_error_str(0)); - continue; - } - - if (conn_info.ConnectionStatus == NoDeviceConnected) { - continue; - } - - if (conn_info.DeviceAddress == LIBUSB_DEVADDR_MAX) { - usbi_warn(ctx, "program assertion failed - device address is %d " - "(conflicts with root hub), ignoring device", LIBUSB_DEVADDR_MAX); - continue; - } - - s_hubname.u.node.ConnectionIndex = i; // Only used for non HCDs (s_hubname is an union) + if (conn_info.DeviceAddress > UINT8_MAX) { + usbi_err(ctx, "program assertion failed: device address overflow"); } - else - { - // HCDs have only 1 node, and it's always a hub - conn_info.DeviceAddress = LIBUSB_DEVADDR_MAX; // using 0 can conflict with driverless devices - conn_info.DeviceIsHub = true; - conn_info.CurrentConfigurationValue = 1; - } - - // If this node is a hub (HCD or not), open it - if (conn_info.DeviceIsHub) { - size = size_initial; - if (!DeviceIoControl(hub_handle, getname_ioctl, &s_hubname, size, - &s_hubname, size, &size, NULL)) { - usbi_warn(ctx, "could not get hub path (dummy): %s", windows_error_str(0)); - continue; - } + dev->device_address = (uint8_t)conn_info.DeviceAddress; + } else { + dev->device_address = UINT8_MAX; // Hubs from HCD have a devaddr of 255 + force_hcd_device_descriptor(dev); + } - size = is_hcd?s_hubname.u.root.ActualLength:s_hubname.u.node.ActualLength; - if (size > size_fixed) { - usbi_warn(ctx, "program assertion failed - hub path is too long"); - continue; - } + usbi_dbg("(bus: %d, addr: %d, depth: %d, port: %d): '%s'", + dev->bus_number, dev->device_address, priv->depth, priv->port, device_id); - if (!is_hcd) { - // previous call trashes some of the data - s_hubname.u.node.ConnectionIndex = i; - } - if (!DeviceIoControl(hub_handle, getname_ioctl, &s_hubname, size, - &s_hubname, size, &size, NULL)) { - usbi_warn(ctx, "could not get hub path (actual): %s", windows_error_str(0)); - continue; - } + return LIBUSB_SUCCESS; +} - // Add prefix - wstr = is_hcd?s_hubname.u.root.RootHubName:s_hubname.u.node.NodeName; - tmp_str = wchar_to_utf8(wstr); - if (tmp_str == NULL) { - usbi_err(ctx, "could not convert hub path string."); - LOOP_BREAK(LIBUSB_ERROR_NO_MEM); - } +// Returns the api type, or 0 if not found/unsupported +static uint8_t get_api_type(struct libusb_context *ctx, + HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data) +{ + // Precedence for filter drivers vs driver is in the order of this array + struct driver_lookup lookup[3] = { + {"\0\0", SPDRP_SERVICE, "driver"}, + {"\0\0", SPDRP_UPPERFILTERS, "upper filter driver"}, + {"\0\0", SPDRP_LOWERFILTERS, "lower filter driver"} + }; + DWORD size, reg_type; + unsigned k, l; + uint8_t api; - path_str = sanitize_path(tmp_str); - if (path_str == NULL) { - usbi_err(ctx, "could not sanitize hub path string."); - LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + // Check the service & filter names to know the API we should use + for (k=0; k<3; k++) { + if (pSetupDiGetDeviceRegistryPropertyA(*dev_info, dev_info_data, lookup[k].reg_prop, + ®_type, (BYTE*)lookup[k].list, MAX_KEY_LENGTH, &size)) { + // Turn the REG_SZ SPDRP_SERVICE into REG_MULTI_SZ + if (lookup[k].reg_prop == SPDRP_SERVICE) { + // our buffers are MAX_KEY_LENGTH+1 so we can overflow if needed + lookup[k].list[safe_strlen(lookup[k].list)+1] = 0; } - - // Open Hub - handle = CreateFileA(path_str, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL); - if(handle == INVALID_HANDLE_VALUE) { - usbi_warn(ctx, "could not open hub %s: %s", path_str, windows_error_str(0)); - continue; + // MULTI_SZ is a pain to work with. Turn it into something much more manageable + // NB: none of the driver names we check against contain LIST_SEPARATOR, + // (currently ';'), so even if an unsuported one does, it's not an issue + for (l=0; (lookup[k].list[l] != 0) || (lookup[k].list[l+1] != 0); l++) { + if (lookup[k].list[l] == 0) { + lookup[k].list[l] = LIST_SEPARATOR; + } } - } - - // Generate a session ID - // Will need to change the session_id computation if this assertion fails - if (conn_info.DeviceAddress > LIBUSB_DEVADDR_MAX) { - usbi_warn(ctx, "program assertion failed - device address is greater than %d, ignoring device", - LIBUSB_DEVADDR_MAX); - continue; + upperize(lookup[k].list); + usbi_dbg("%s(s): %s", lookup[k].designation, lookup[k].list); } else { - devaddr = (uint8_t)conn_info.DeviceAddress; - } - // Same trick as linux for session_id, with same caveat - session_id = busnum << (sizeof(libusb_devaddr_t)*8) | devaddr; - usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr, session_id); - - // Allocate device if needed - dev = usbi_get_device_by_session_id(ctx, session_id); - if (dev) { - usbi_dbg("using existing device for session %ld", session_id); - priv = __device_priv(dev); - // Because we are rebuilding the list, there's no guarantee - // the parent device pointer is still the same. - // Other device data should still be reusable - priv->parent_dev = parent_dev; - } else { - usbi_dbg("allocating new device for session %ld", session_id); - if ((dev = usbi_alloc_device(ctx, session_id)) == NULL) { - LOOP_BREAK(LIBUSB_ERROR_NO_MEM); - } - need_unref = true; - - LOOP_CHECK(initialize_device(dev, busnum, devaddr, path_str, i, - conn_info.CurrentConfigurationValue, parent_dev)); - priv = __device_priv(dev); - - path_str = NULL; // protect our path from being freed - - // Setup the cached descriptors. Note that only non HCDs can fetch descriptors - if (!is_hcd) { - // The device descriptor has been read with conn_info - memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR)); - dev->num_configurations = priv->dev_descriptor.bNumConfigurations; - // If we can't read the config descriptors, just set the number of confs to zero - if (cache_config_descriptors(dev, hub_handle) != LIBUSB_SUCCESS) { - dev->num_configurations = 0; - priv->dev_descriptor.bNumConfigurations = 0; - } - } else { - LOOP_CHECK(force_hcd_device_descriptor(dev, handle)); + if (GetLastError() != ERROR_INVALID_DATA) { + usbi_dbg("could not access %s: %s", lookup[k].designation, windows_error_str(0)); } - LOOP_CHECK(usbi_sanitize_device(dev)); + lookup[k].list[0] = 0; } + } - discdevs = discovered_devs_append(*_discdevs, dev); - if (!discdevs) { - LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + for (api=1; api= 3) continue; + return api; + } + return 0; +} - *_discdevs = discdevs; +static int set_composite_interface(struct libusb_context* ctx, struct libusb_device* dev, + char* dev_interface_path, char* device_id, uint8_t api) +{ + unsigned i; + struct windows_device_priv *priv = __device_priv(dev); + int interface_number; - // Finally, if device is a hub, recurse - if (conn_info.DeviceIsHub) { - // Find number of ports for this hub - size = sizeof(USB_NODE_INFORMATION); - if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_INFORMATION, &hub_node, size, - &hub_node, size, &size, NULL)) { - usbi_warn(ctx, "could not retreive information for hub %s: %s", - priv->path, windows_error_str(0)); - continue; - } + if (priv->apib->id != USB_API_COMPOSITE) { + usbi_err(ctx, "program assertion failed: '%s' is not composite", device_id); + return LIBUSB_ERROR_NO_DEVICE; + } - if (hub_node.NodeType != UsbHub) { - usbi_warn(ctx, "unexpected hub type (%d) for hub %s", hub_node.NodeType, priv->path); - continue; - } + // 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 + interface_number = 0; + for (i=0; device_id[i] != 0; ) { + if ( (device_id[i++] == 'M') && (device_id[i++] == 'I') + && (device_id[i++] == '_') ) { + interface_number = (device_id[i++] - '0')*10; + interface_number += device_id[i] - '0'; + break; + } + } - usbi_dbg("%d ports Hub: %s", hub_node.u.HubInformation.HubDescriptor.bNumberOfPorts, priv->path); + if (device_id[i] == 0) { + usbi_warn(ctx, "failure to read interface number for %s. Using default value %d", + device_id, interface_number); + } - usb_enumerate_hub(ctx, _discdevs, handle, busnum, dev, - hub_node.u.HubInformation.HubDescriptor.bNumberOfPorts); - } + if (priv->usb_interface[interface_number].path != NULL) { + usbi_warn(ctx, "interface[%d] already set - ignoring: %s", interface_number, device_id); + return LIBUSB_ERROR_ACCESS; } - return r; + usbi_dbg("interface[%d] = %s", interface_number, dev_interface_path); + priv->usb_interface[interface_number].path = dev_interface_path; + priv->usb_interface[interface_number].apib = &usb_api_backend[api]; + priv->composite_api_flags |= 1< check and ignore duplicates - found = false; - for (j=0; j MAX_USB_DEVICES) { - usbi_warn(ctx, "more than %d devices - ignoring the rest", MAX_USB_DEVICES); + if ((pass == HCD_PASS) && (i == UINT8_MAX)) { + usbi_warn(ctx, "program assertion failed - found more than %d buses, skipping the rest.", UINT8_MAX); break; } - } - } - pSetupDiDestroyDeviceInfoList(dev_info); - - // Now let's find the device interface paths for all these devices - nb_paths = 0; - for (j=0; jDevicePath); + if (dev_interface_path == NULL) { + usbi_warn(ctx, "could not sanitize device interface path for '%s'", dev_interface_details->DevicePath); + continue; + } + } + } else { + if (!get_devinfo_data(ctx, &dev_info, &dev_info_data, i)) { + break; + } + } - // In case we can't read the driver string through SPDRP_SERVICE, - // we need the ClassGUID for comparison. - if(!pSetupDiGetDeviceRegistryPropertyW(dev_info, &dev_info_data, SPDRP_CLASSGUID, - NULL, (BYTE*)guid_string_w, sizeof(guid_string_w), &size)) { - usbi_warn(ctx, "could not read class GUID for device %s, skipping: %s", - dev_interface_details->DevicePath, windows_error_str(0)); + // Read the Device ID path. This is what we'll use as UID + // Note that if the device is plugged in a different port or hub, the Device ID changes + if (CM_Get_Device_IDA(dev_info_data.DevInst, path, sizeof(path), 0) != CR_SUCCESS) { + usbi_warn(ctx, "could not read the device id path for devinst %X, skipping", + dev_info_data.DevInst); continue; } - pCLSIDFromString(guid_string_w, &class_guid); - - // Attempt to read the driver string - if(!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_SERVICE, - NULL, (BYTE*)driver, MAX_KEY_LENGTH, &size)) { - driver[0] = 0; + dev_id_path = sanitize_path(path); + if (dev_id_path == NULL) { + usbi_warn(ctx, "could not sanitize device id path for devinst %X, skipping", + dev_info_data.DevInst); + continue; } +#ifdef ENUM_DEBUG + usbi_dbg("PRO: %s", dev_id_path); +#endif - for (api=USB_API_WINUSB; apiDevicePath); - if (nb_paths > MAX_USB_DEVICES) { - usbi_warn(ctx, "more than %d devices - ignoring the rest", MAX_USB_DEVICES); - break; - } + // The SPDRP_ADDRESS for USB devices is the device port number on the hub + port_nr = 0; + if ((pass >= HUB_PASS) && (pass <= GEN_PASS)) { + if ( (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ADDRESS, + ®_type, (BYTE*)&port_nr, 4, &size)) + || (size != 4) ) { + usbi_warn(ctx, "could not retrieve port number for device '%s', skipping: %s", + dev_id_path, windows_error_str(0)); + continue; } } - } - } - - // Finally, match the interface paths with the interfaces. We do that - // by looking at the children of the composite device - // NB: if the interfaces are not found in their expected position, - // claim_interface will issue a warning - found = false; - memset(&child_devinst, 0, sizeof(DEVINST)); // prevents /W4 warning - for (i = 0; i= 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_guid = calloc(1, sizeof(GUID)); + pCLSIDFromString(guid_string_w, if_guid); + guid[nb_guids++] = if_guid; + usbi_dbg("extra GUID: %s", guid_to_string(if_guid)); + } + } + break; + default: + // Get the API type (after checking that the driver installation is OK) + if ( (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_INSTALL_STATE, + ®_type, (BYTE*)&install_state, 4, &size)) + || (size != 4) ){ + usbi_warn(ctx, "could not detect installation state of driver for '%s': %s", + dev_id_path, windows_error_str(0)); + } else if (install_state != 0) { + usbi_warn(ctx, "driver for device '%s' is reporting an issue (code: %d) - skipping", + dev_id_path, install_state); + continue; + } + api = get_api_type(ctx, &dev_info, &dev_info_data); break; } - } - if (sanitized_short[j] == 0) { - usbi_warn(ctx, "failure to read interface number for %s. Using default value %d", - sanitized_short, interface_number); - } - - for (j=0; jusb_interface[interface_number].path = sanitized_path[j]; - priv->usb_interface[interface_number].apib = &usb_api_backend[api_type[j]]; - priv->composite_api_flags |= 1<usb_interface[interface_number].path == NULL) { - usbi_warn(ctx, "interface_path[%d]: unhandled API - interface will be disabled", - interface_number); - continue; - } - usbi_dbg("interface_path[%d]: %s", interface_number, priv->usb_interface[interface_number].path); - found = true; - } - - for (j=0; jDevicePath, windows_error_str(0)); - } else if (install_state != 0) { - usbi_warn(ctx, "driver for device %s is reporting an issue (code: %d) - skipping", - dev_interface_details->DevicePath, install_state); - continue; - } - - // The SPDRP_ADDRESS for USB devices should be the device port number on the hub - if ( (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ADDRESS, - ®_type, (BYTE*)&port_nr, 4, &size)) - || (size != 4) ){ - usbi_warn(ctx, "could not retrieve port number for device %s, skipping: %s", - dev_interface_details->DevicePath, windows_error_str(0)); - continue; - } - - // Retrieve parent's path using PnP Configuration Manager (CM) - if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) { - usbi_warn(ctx, "could not retrieve parent info data for device %s, skipping: %s", - dev_interface_details->DevicePath, windows_error_str(0)); - continue; - } - if (CM_Get_Device_IDA(parent_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) { - usbi_warn(ctx, "could not retrieve parent's path for device %s, skipping: %s", - dev_interface_details->DevicePath, windows_error_str(0)); - continue; - } - - // Fix parent's path inconsistencies before attempting to compare - sanitized_path = sanitize_path(path); - if (sanitized_path == NULL) { - usbi_warn(ctx, "could not sanitize parent's path for device %s, skipping.", - dev_interface_details->DevicePath); - continue; - } - - // With the parent path and port number, we should be able to locate our device - // by comparing these values to the ones we got when enumerating hubs - found = false; - for (j=0; jlen; j++) { - priv = __device_priv(discdevs->devices[j]); - - if (priv->parent_dev == NULL) { - continue; // ignore HCDs + // Find parent device (for the passes that need it) + switch (pass) { + case HCD_PASS: + case DEV_PASS: + case HUB_PASS: + break; + default: + // Go through the ancestors until we see a face we recognize + parent_dev = NULL; + for (ancestor = 1; parent_dev == NULL; ancestor++) { + session_id = get_ancestor_session_id(dev_info_data.DevInst, ancestor); + if (session_id == 0) { + break; + } + parent_dev = usbi_get_device_by_session_id(ctx, session_id); + } + if (parent_dev == NULL) { + usbi_dbg("unlisted ancestor for '%s' (newly connected, etc.) - ignoring", dev_id_path); + continue; + } + parent_priv = __device_priv(parent_dev); + // virtual USB devices are also listed during GEN - don't process these yet + if ( (pass == GEN_PASS) && (parent_priv->apib->id != USB_API_HUB) ) { + continue; + } + break; } - parent_priv = __device_priv(priv->parent_dev); - - // NB: we compare strings of different lengths below => strncmp - if ( (safe_strncmp(parent_priv->path, sanitized_path, safe_strlen(sanitized_path)) == 0) - && (port_nr == priv->connection_index) ) { - - priv->path = sanitize_path(dev_interface_details->DevicePath); - - usbi_dbg("path (%d:%d): %s", discdevs->devices[j]->bus_number, - discdevs->devices[j]->device_address, priv->path); - - // Check the service & filter names to know the API we should use - for (k=0; k<3; k++) { - if (pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, lookup[k].reg_prop, - ®_type, (BYTE*)lookup[k].list, MAX_KEY_LENGTH, &size)) { - // Turn the REG_SZ SPDRP_SERVICE into REG_MULTI_SZ - if (lookup[k].reg_prop == SPDRP_SERVICE) { - // our buffers are MAX_KEY_LENGTH+1 so we can overflow if needed - lookup[k].list[safe_strlen(lookup[k].list)+1] = 0; - } - // MULTI_SZ is a pain to work with. Turn it into something much more manageable - // NB: none of the driver names we check against contain LIST_SEPARATOR, - // (currently ';'), so even if an unsuported one does, it's not an issue - for (l=0; (lookup[k].list[l] != 0) || (lookup[k].list[l+1] != 0); l++) { - if (lookup[k].list[l] == 0) { - lookup[k].list[l] = LIST_SEPARATOR; - } - } - upperize(lookup[k].list); - usbi_dbg("%s(s): %s", lookup[k].designation, lookup[k].list); - found = true; - } else { - if (GetLastError() != ERROR_INVALID_DATA) { - usbi_dbg("could not access %s: %s", lookup[k].designation, windows_error_str(0)); + // Create new or match existing device, using the (hashed) device_id as session id + if (pass <= DEV_PASS) { // For subsequent passes, we'll lookup the parent + // These are the passes that create "new" devices + session_id = htab_hash(dev_id_path); + dev = usbi_get_device_by_session_id(ctx, session_id); + if (dev == NULL) { + if (pass == DEV_PASS) { + // This can occur if the OS only reports a newly plugged device after we started enum + usbi_warn(ctx, "'%s' was only detected in late pass (newly connected device?)" + " - ignoring", dev_id_path); + continue; + } + usbi_dbg("allocating new device for session [%X]", session_id); + if ((dev = usbi_alloc_device(ctx, session_id)) == NULL) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + windows_device_priv_init(dev); + // Keep track of devices that need unref + unref_list[unref_cur++] = dev; + if (unref_cur > unref_size) { + unref_size += 64; + unref_list = realloc(unref_list, unref_size*sizeof(libusb_device*)); + if (unref_list == NULL) { + usbi_err(ctx, "could not realloc list for unref - aborting."); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); } - lookup[k].list[0] = 0; } + } else { + usbi_dbg("found existing device for session [%X] (%d.%d)", + session_id, dev->bus_number, dev->device_address); } + priv = __device_priv(dev); + } - for (api=0; apibus_number = (uint8_t)(i + 1); // bus 0 is reserved for disconnected + dev->device_address = 0; + dev->num_configurations = 0; + priv->apib = &usb_api_backend[USB_API_HUB]; + priv->depth = UINT8_MAX; // Overflow to 0 for HCD Hubs + priv->path = dev_interface_path; dev_interface_path = NULL; + break; + case DEV_PASS: + // If the device has already been setup, don't do it again + if (priv->path != NULL) + break; + // Take care of API initialization + priv->path = dev_interface_path; dev_interface_path = NULL; + priv->apib = &usb_api_backend[api]; + switch(api) { + case USB_API_COMPOSITE: + break; + default: + // For other devices, the first interface is the same as the device + priv->usb_interface[0].path = malloc(safe_strlen(priv->path)+1); + if (priv->usb_interface[0].path != NULL) { + safe_strcpy(priv->usb_interface[0].path, safe_strlen(priv->path)+1, priv->path); + } + // The following is needed if we want API calls to work for both simple + // and composite devices. + for(j=0; jusb_interface[j].apib = &usb_api_backend[api]; } - if (k >= 3) continue; - priv->apib = &usb_api_backend[api]; - switch(api) { - case USB_API_COMPOSITE: - set_composite_device(ctx, dev_info_data.DevInst, priv); + break; + } + break; + case HUB_PASS: + priv->apib = &usb_api_backend[api]; + priv->path = dev_interface_path; dev_interface_path = NULL; + break; + case GEN_PASS: + r = init_device(dev, parent_dev, (uint8_t)port_nr, dev_id_path, dev_info_data.DevInst); + if (r == LIBUSB_SUCCESS) { + // Append device to the list of discovered devices + discdevs = discovered_devs_append(*_discdevs, dev); + if (!discdevs) { + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + *_discdevs = discdevs; + } else if (r == LIBUSB_ERROR_NO_DEVICE) { + // This can occur if the device was disconnected but Windows hasn't + // refreshed its enumeration yet - in that case, we ignore the device + r = LIBUSB_SUCCESS; + } + break; + default: // later passes + if (parent_priv->apib->id == USB_API_COMPOSITE) { + usbi_dbg("setting composite interface for [%lX]:", parent_dev->session_data); + switch (set_composite_interface(ctx, parent_dev, dev_interface_path, dev_id_path, api)) { + case LIBUSB_SUCCESS: + dev_interface_path = NULL; + break; + case LIBUSB_ERROR_ACCESS: + // interface has already been set => make sure dev_interface_path is freed then break; default: - // For other devices, the first interface is the same as the device - priv->usb_interface[0].path = malloc(safe_strlen(priv->path)+1); - if (priv->usb_interface[0].path != NULL) { - safe_strcpy(priv->usb_interface[0].path, safe_strlen(priv->path)+1, priv->path); - } - // The following is needed if we want to API calls to work for both simple - // and composite devices, as - for(k=0; kusb_interface[k].apib = &usb_api_backend[api]; - } + LOOP_BREAK(r); break; } } break; } } - if (!found) { - usbi_warn(ctx, "could not match %s with a libusb device.", dev_interface_details->DevicePath); - continue; - } } - return LIBUSB_SUCCESS; -} - -/* - * get_device_list: libusb backend device enumeration function - */ -static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) -{ - struct windows_hcd_priv* hcd; - HANDLE handle = INVALID_HANDLE_VALUE; - int r = LIBUSB_SUCCESS; - libusb_bus_t bus; - - // Use the index of the HCD in the chained list as bus # - for (hcd = hcd_root, bus = 0; ; hcd = hcd->next, bus++) - { - safe_closehandle(handle); - - if ( (hcd == NULL) || (r != LIBUSB_SUCCESS) ) - break; - - if (bus == LIBUSB_BUS_MAX) { - usbi_warn(ctx, "program assertion failed - got more than %d buses, skipping the rest.", LIBUSB_BUS_MAX); - continue; - } - - handle = CreateFileA(hcd->path, GENERIC_WRITE, FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - if (handle == INVALID_HANDLE_VALUE) { - usbi_warn(ctx, "could not open bus %u, skipping: %s", bus, windows_error_str(0)); - continue; - } - - LOOP_CHECK(usb_enumerate_hub(ctx, _discdevs, handle, bus, NULL, 1)); + // Free any additional GUIDs + for (pass = DEV_PASS+1; pass < nb_guids; pass++) { + safe_free(guid[pass]); } - // Set the interface path for non-hubs - r = set_device_paths(ctx, *_discdevs); + // Unref newly allocated devs + for (i=0; inext; - windows_hcd_priv_release(hcd_tmp); - safe_free(hcd_tmp); - } - for (i=0; inum_configurations); + windows_device_priv_release(dev); } static void windows_clear_transfer_priv(struct usbi_transfer *itransfer) @@ -2175,6 +2195,7 @@ static int unsupported_copy_transfer_data(struct usbi_transfer *itransfer, uint3 } // These names must be uppercase +const char* hub_driver_names[] = {"USBHUB"}; const char* composite_driver_names[] = {"USBCCGP"}; const char* winusb_driver_names[] = {"WINUSB"}; const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { @@ -2200,6 +2221,27 @@ const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { unsupported_abort_transfers, unsupported_copy_transfer_data, }, { + USB_API_HUB, + "HUB API", + &CLASS_GUID_UNSUPPORTED, + hub_driver_names, + sizeof(hub_driver_names)/sizeof(hub_driver_names[0]), + unsupported_init, + unsupported_exit, + unsupported_open, + unsupported_close, + unsupported_claim_interface, + unsupported_set_interface_altsetting, + unsupported_release_interface, + unsupported_clear_halt, + unsupported_reset_device, + unsupported_submit_bulk_transfer, + unsupported_submit_iso_transfer, + unsupported_submit_control_transfer, + unsupported_abort_control, + unsupported_abort_transfers, + unsupported_copy_transfer_data, + }, { USB_API_COMPOSITE, "Composite API", &CLASS_GUID_COMPOSITE, diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h index 5b0eef8..5ad0304 100644 --- a/libusb/os/windows_usb.h +++ b/libusb/os/windows_usb.h @@ -41,15 +41,6 @@ #define false FALSE #endif -#if !defined(libusb_bus_t) -#define libusb_bus_t uint8_t -#define LIBUSB_BUS_MAX UINT8_MAX -#endif -#if !defined(libusb_devaddr_t) -#define libusb_devaddr_t uint8_t -#define LIBUSB_DEVADDR_MAX UINT8_MAX -#endif - // Missing from MSVC6 setupapi.h #if !defined(SPDRP_ADDRESS) #define SPDRP_ADDRESS 28 @@ -96,26 +87,33 @@ inline void upperize(char* str) { #define TIMER_REQUEST_RETRY_MS 100 #define ERR_BUFFER_SIZE 256 #define LIST_SEPARATOR ';' +#define HTAB_SIZE 1021 -// http://msdn.microsoft.com/en-us/library/bb663109.aspx -// http://msdn.microsoft.com/en-us/library/bb663093.aspx +// http://msdn.microsoft.com/en-us/library/ff545978.aspx +// http://msdn.microsoft.com/en-us/library/ff545972.aspx +// http://msdn.microsoft.com/en-us/library/ff545982.aspx #if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER) const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} }; #endif #if !defined(GUID_DEVINTERFACE_USB_DEVICE) const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} }; #endif +#if !defined(GUID_DEVINTERFACE_USB_HUB) +const GUID GUID_DEVINTERFACE_USB_HUB = { 0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8} }; +#endif +const GUID GUID_NULL = { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; /* * Multiple USB API backend support */ #define USB_API_UNSUPPORTED 0 -#define USB_API_COMPOSITE 1 -#define USB_API_WINUSB 2 -#define USB_API_MAX 3 +#define USB_API_HUB 1 +#define USB_API_COMPOSITE 2 +#define USB_API_WINUSB 3 +#define USB_API_MAX 4 -const GUID CLASS_GUID_UNSUPPORTED = { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x57, 0xDA} }; +#define CLASS_GUID_UNSUPPORTED GUID_NULL const GUID CLASS_GUID_LIBUSB_WINUSB = { 0x78A1C341, 0x4539, 0x11D3, {0xB8, 0x8D, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71} }; const GUID CLASS_GUID_COMPOSITE = { 0x36FC9E60, 0xC465, 0x11cF, {0x80, 0x56, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} }; @@ -153,44 +151,35 @@ extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX]; * private structures definition * with inline pseudo constructors/destructors */ - -// HCDs -struct windows_hcd_priv { - char *path; - struct windows_hcd_priv *next; -}; - -static inline void windows_hcd_priv_init(struct windows_hcd_priv* p) { - p->path = NULL; - p->next = NULL; -} - -static inline void windows_hcd_priv_release(struct windows_hcd_priv* p) { - safe_free(p->path); -} - typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR; struct windows_device_priv { - struct libusb_device *parent_dev; // access to parent is required for usermode ops - ULONG connection_index; // also required for some usermode ops - char *path; // path used by Windows to reference the USB node + uint8_t depth; // distance to HCD + uint8_t port; // port number on the hub + struct libusb_device *parent_dev; // access to parent is required for usermode ops + char *path; // device interface path struct windows_usb_api_backend const *apib; struct { - char *path; // each interface needs a Windows device interface path, + char *path; // each interface needs a device interface path, struct windows_usb_api_backend const *apib; // an API backend (multiple drivers support), - int8_t nb_endpoints; // and a set of endpoint addresses (USB_MAXENDPOINTS) + int8_t nb_endpoints; // and a set of endpoint addresses (USB_MAXENDPOINTS) uint8_t *endpoint; } usb_interface[USB_MAXINTERFACES]; - uint8_t composite_api_flags; // composite devices require additional data + uint8_t composite_api_flags; // composite devices require additional data uint8_t active_config; USB_DEVICE_DESCRIPTOR dev_descriptor; - unsigned char **config_descriptor; // list of pointers to the cached config descriptors + unsigned char **config_descriptor; // list of pointers to the cached config descriptors }; -static inline void windows_device_priv_init(struct windows_device_priv* p) { +static inline struct windows_device_priv *__device_priv(struct libusb_device *dev) { + return (struct windows_device_priv *)dev->os_priv; +} + +static inline void windows_device_priv_init(libusb_device* dev) { + struct windows_device_priv* p = __device_priv(dev); int i; + p->depth = 0; + p->port = 0; p->parent_dev = NULL; - p->connection_index = 0; p->path = NULL; p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; p->composite_api_flags = 0; @@ -205,11 +194,12 @@ static inline void windows_device_priv_init(struct windows_device_priv* p) { } } -static inline void windows_device_priv_release(struct windows_device_priv* p, int num_configurations) { +static inline void windows_device_priv_release(libusb_device* dev) { + struct windows_device_priv* p = __device_priv(dev); int i; safe_free(p->path); - if ((num_configurations > 0) && (p->config_descriptor != NULL)) { - for (i=0; i < num_configurations; i++) + if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) { + for (i=0; i < dev->num_configurations; i++) safe_free(p->config_descriptor[i]); } safe_free(p->config_descriptor); @@ -219,10 +209,6 @@ static inline void windows_device_priv_release(struct windows_device_priv* p, in } } -static inline struct windows_device_priv *__device_priv(struct libusb_device *dev) { - return (struct windows_device_priv *)dev->os_priv; -} - struct interface_handle_t { HANDLE dev_handle; // WinUSB needs an extra handle for the file HANDLE api_handle; // used by the API to communicate with the device @@ -297,9 +283,8 @@ DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO)); DLL_DECLARE_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM)); DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyA, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)); -DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyW, (HDEVINFO, - PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)); - +DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD)); +DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY)); /* * Windows DDK API definitions. Most of it copied from MinGW's includes @@ -330,7 +315,6 @@ typedef RETURN_TYPE CONFIGRET; #define USB_REQUEST_SET_INTERFACE LIBUSB_REQUEST_SET_INTERFACE #define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME -#define HCD_GET_ROOT_HUB_NAME 258 #define USB_GET_NODE_INFORMATION 258 #define USB_GET_NODE_CONNECTION_INFORMATION 259 #define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260 @@ -505,7 +489,6 @@ typedef struct _USB_HUB_NAME_FIXED { } u; } USB_HUB_NAME_FIXED; - typedef struct _USB_HUB_INFORMATION { USB_HUB_DESCRIPTOR HubDescriptor; BOOLEAN HubIsBusPowered;