* Copyright (c) 2009-2010 Pete Batard <pbatard@gmail.com>
* 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
// 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;
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);
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
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);
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:
* 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) {
}
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));
_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;
}
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<htab_size; i++) {
+ if (htab_table[i].used) {
+ safe_free(htab_table[i].str);
+ }
+ }
+ usbi_mutex_destroy(&htab_write_mutex);
+ safe_free(htab_table);
+}
+
+/* This is the search function. It uses double hashing with open addressing.
+ We use an trick to speed up the lookup. The table is created with one
+ more element available. This enables us to use the index zero special.
+ This index will never be used because we store the first hash index in
+ the field used where zero means not used. Every other value means used.
+ The used field can be used as a first fast comparison for equality of
+ the stored and the parameter value. This helps to prevent unnecessary
+ expensive calls of strcmp. */
+static unsigned long htab_hash(char* str)
+{
+ unsigned long hval, hval2;
+ unsigned long idx;
+ unsigned long r = 5381;
+ int c;
+ char* sz = str;
+
+ // Compute main hash value (algorithm suggested by Nokia)
+ while ((c = *sz++))
+ r = ((r << 5) + r) + c;
+ if (r == 0)
+ ++r;
+
+ // compute table hash: simply take the modulus
+ hval = r % htab_size;
+ if (hval == 0)
+ ++hval;
+
+ // Try the first index
+ idx = hval;
+
+ if (htab_table[idx].used) {
+ if ( (htab_table[idx].used == hval)
+ && (safe_strcmp(str, htab_table[idx].str) == 0) ) {
+ // existing hash
+ return idx;
+ }
+ usbi_dbg("hash collision ('%s' vs '%s')", str, htab_table[idx].str);
+
+ // Second hash function, as suggested in [Knuth]
+ hval2 = 1 + hval % (htab_size - 2);
+
+ do {
+ // Because size is prime this guarantees to step through all available indexes
+ if (idx <= hval2) {
+ idx = htab_size + idx - hval2;
+ } else {
+ idx -= hval2;
+ }
+
+ // If we visited all entries leave the loop unsuccessfully
+ if (idx == hval) {
+ break;
+ }
+
+ // If entry is found use it.
+ if ( (htab_table[idx].used == hval)
+ && (safe_strcmp(str, htab_table[idx].str) == 0) ) {
+ return idx;
+ }
+ }
+ while (htab_table[idx].used);
+ }
+
+ // Not found => 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; i<level; i++) {
+ if (CM_Get_Parent(&parent_devinst, devinst, 0) != CR_SUCCESS) {
+ return 0;
+ }
+ devinst = parent_devinst;
+ }
+ if (CM_Get_Device_IDA(devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) {
+ return 0;
+ }
+ // TODO (post hotplug): try without sanitizing
+ sanitized_path = sanitize_path(path);
+ if (sanitized_path == NULL) {
+ return 0;
+ }
+ session_id = htab_hash(sanitized_path);
+ safe_free(sanitized_path);
+ return session_id;
+}
+
/*
* Populate the endpoints addresses of the device_priv interface helper structs
*/
{
for (current_interface=0; current_interface<USB_MAXINTERFACES; current_interface++) {
// Must claim an interface of the same API type
- if ( (priv->usb_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) {
*/
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);
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);
}
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)) {
CloseHandle(timer_mutex);
timer_mutex = NULL;
}
+ htab_destroy();
}
if (r != LIBUSB_SUCCESS)
}
/*
- * 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);
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;
// 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;
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);
}
}
/*
- * 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<USB_API_MAX; api++) {
+ for (k=0; k<3; k++) {
+ if (is_api_driver(lookup[k].list, api)) {
+ usbi_dbg("matched %s name against %s", lookup[k].designation, usb_api_backend[api].designation);
+ break;
+ }
}
+ if (k >= 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<<api;
+
+ return LIBUSB_SUCCESS;
}
/*
- * Composite device interfaces are not enumerated using GUID_DEVINTERFACE_USB_DEVICE,
- * but instead require a different lookup mechanism
+ * get_device_list: libusb backend device enumeration function
*/
-static int set_composite_device(struct libusb_context *ctx, DEVINST devinst, struct windows_device_priv *priv)
+static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs)
{
- DEVINST child_devinst;
- unsigned i, j, max_guids, nb_paths, interface_number;
- uint8_t api;
- bool found;
- DWORD type, size;
- CONFIGRET r;
+ struct discovered_devs *discdevs = *_discdevs;
HDEVINFO dev_info;
SP_DEVINFO_DATA dev_info_data;
SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL;
+#define MAX_ENUM_GUIDS 64
+ const GUID* guid[MAX_ENUM_GUIDS];
+#define HCD_PASS 0
+#define HUB_PASS 1
+#define GEN_PASS 2
+#define DEV_PASS 3
+ int r = LIBUSB_SUCCESS;
+ unsigned int nb_guids, pass, i, j, ancestor;
+ char path[MAX_PATH_LENGTH];
+ char strbuf[MAX_PATH_LENGTH];
+ struct libusb_device *dev, *parent_dev;
+ struct windows_device_priv *priv, *parent_priv;
+ char* dev_interface_path = NULL;
+ char* dev_id_path = NULL;
+ unsigned long session_id;
+ DWORD size, reg_type, port_nr, install_state;
HKEY key;
WCHAR guid_string_w[MAX_GUID_STRING_LENGTH];
- GUID guid, class_guid;
- GUID guid_table[MAX_USB_DEVICES];
- char* sanitized_path[MAX_USB_DEVICES];
- uint8_t api_type[MAX_USB_DEVICES];
- char* sanitized_short = NULL;
- char path[MAX_PATH_LENGTH];
- char driver[MAX_KEY_LENGTH];
-
- dev_info = pSetupDiGetClassDevsA(NULL, "USB", NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES);
- if (dev_info == INVALID_HANDLE_VALUE) {
- return LIBUSB_ERROR_NOT_FOUND;
+ GUID* if_guid;
+ LONG s;
+ uint8_t api;
+ // Keep a list of newly allocated devs to unref
+ libusb_device** unref_list;
+ unsigned int unref_size = 64;
+ unsigned int unref_cur = 0;
+
+ // 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 and set the device interfaces.
+
+ // Init the GUID table
+ guid[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER;
+ guid[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB;
+ guid[GEN_PASS] = NULL;
+ guid[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE;
+ nb_guids = DEV_PASS+1;
+
+ unref_list = malloc(unref_size*sizeof(libusb_device*));
+ if (unref_list == NULL) {
+ return LIBUSB_ERROR_NO_MEM;
}
- max_guids = 0;
-
- // First, retrieve all the device interface GUIDs
- for (i = 0; ; i++)
- {
- dev_info_data.cbSize = sizeof(dev_info_data);
- if (!pSetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {
+ for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) {
+//#define ENUM_DEBUG
+#ifdef ENUM_DEBUG
+ switch(pass) {
+ case HCD_PASS:
+ usbi_dbg("PROCESSING HCDs %s", guid_to_string(guid[pass]));
+ break;
+ case HUB_PASS:
+ usbi_dbg("PROCESSING HUBs %s", guid_to_string(guid[pass]));
+ break;
+ case DEV_PASS:
+ usbi_dbg("PROCESSING DEVs %s", guid_to_string(guid[pass]));
+ break;
+ case GEN_PASS:
+ usbi_dbg("PROCESSING GENs");
+ break;
+ default:
+ usbi_dbg("PROCESSING EXTs %s", guid_to_string(guid[pass]));
break;
}
+#endif
+ for (i = 0; ; i++) {
+ // safe loop: free up any (unprotected) dynamic resource
+ // NB: this is always executed before breaking the loop
+ safe_free(dev_interface_details);
+ safe_free(dev_interface_path);
+ safe_free(dev_id_path);
+ session_id = 0;
+ priv = parent_priv = NULL;
+ dev = parent_dev = NULL;
- key = pSetupDiOpenDevRegKey(dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
- if (key == INVALID_HANDLE_VALUE) {
- usbi_dbg("could not open registry key");
- continue;
- }
-
- size = sizeof(guid_string_w);
- r = RegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, &type,
- (BYTE*)guid_string_w, &size);
- RegCloseKey(key);
- if (r != ERROR_SUCCESS) {
- continue;
- }
- pCLSIDFromString(guid_string_w, &guid);
-
- // identical device interface GUIDs are not supposed to happen, but are a real possibility
- // => check and ignore duplicates
- found = false;
- for (j=0; j<max_guids; j++) {
- if (memcmp(&guid_table[j], &guid, sizeof(GUID)) == 0) {
- found = true;
+ // Safe loop: end of loop conditions
+ if (r != LIBUSB_SUCCESS) {
break;
}
- }
- if (!found) {
- guid_table[max_guids++] = guid;
- if (max_guids > 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; j<max_guids; j++)
- {
- guid = guid_table[j];
-
- for (i = 0; ; i++)
- {
- safe_free(dev_interface_details);
- dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid, i);
- if (dev_interface_details == NULL)
- break;
+ if (pass != GEN_PASS) {
+ // Except for GEN, all passes deal with device interfaces
+ dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid[pass], i);
+ if (dev_interface_details == NULL) {
+ break;
+ } else {
+ dev_interface_path = sanitize_path(dev_interface_details->DevicePath);
+ 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; api<USB_API_MAX; api++) {
- if ( (is_api_driver(driver, api))
- || (guid_eq(&class_guid, usb_api_backend[api].class_guid)) ) {
- api_type[nb_paths] = api;
- sanitized_path[nb_paths++] = sanitize_path(dev_interface_details->DevicePath);
- 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<USB_MAXINTERFACES; i++)
- {
- if (i == 0) {
- r = CM_Get_Child(&child_devinst, devinst, 0);
- } else {
- r = CM_Get_Sibling(&child_devinst, child_devinst, 0);
- }
- if (r == CR_NO_SUCH_DEVNODE) { // end of the siblings
- break;
- } else if (r != CR_SUCCESS) {
- usbi_dbg("unable to find interface sibling #%d, error = %X", i, r);
- break;
- }
-
- r = CM_Get_Device_IDA(child_devinst, path, MAX_PATH_LENGTH, 0);
- if (r != CR_SUCCESS) {
- usbi_err(ctx, "could not retrieve simple path for interface %d: CR error %d",
- i, r);
- continue;
- }
- sanitized_short = sanitize_path(path);
- if (sanitized_short == NULL) {
- usbi_err(ctx, "could not sanitize path for interface %d", i);
- continue;
- }
- // Because MI_## are not necessarily in sequential order, we
- // retrieve the actual interface number from the path's MI value
- interface_number = i;
- for (j=0; sanitized_short[j] != 0; ) {
- if ( (sanitized_short[j++] == 'M') && (sanitized_short[j++] == 'I')
- && (sanitized_short[j++] == '_') ) {
- interface_number = (sanitized_short[j++] - '0')*10;
- interface_number += sanitized_short[j] - '0';
+ // Set API to use or get additional data from generic pass
+ api = USB_API_UNSUPPORTED;
+ switch (pass) {
+ case HCD_PASS:
+ break;
+ case GEN_PASS:
+ // We use the GEN pass to detect driverless devices...
+ size = sizeof(strbuf);
+ if (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_DRIVER,
+ ®_type, (BYTE*)strbuf, size, &size)) {
+ usbi_info(ctx, "The following device has no driver: '%s'", dev_id_path);
+ 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) {
+ size = sizeof(guid_string_w);
+ s = pRegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, ®_type,
+ (BYTE*)guid_string_w, &size);
+ pRegCloseKey(key);
+ if (s == ERROR_SUCCESS) {
+ if (nb_guids >= MAX_ENUM_GUIDS) {
+ // If this assert is ever reported, grow a GUID table dynamically
+ usbi_err(ctx, "program assertion failed: too many GUIDs");
+ LOOP_BREAK(LIBUSB_ERROR_OVERFLOW);
+ }
+ if_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; j<nb_paths; j++) {
- if ( (safe_strncmp(sanitized_path[j], sanitized_short, safe_strlen(sanitized_short)) == 0) ) {
- priv->usb_interface[interface_number].path = sanitized_path[j];
- priv->usb_interface[interface_number].apib = &usb_api_backend[api_type[j]];
- priv->composite_api_flags |= 1<<api_type[j];
- sanitized_path[j] = NULL;
- }
- }
- safe_free(sanitized_short);
-
- if (priv->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; j<nb_paths; j++) {
- safe_free(sanitized_path[j]);
- }
-
- if (found == 0) {
- usbi_warn(ctx, "composite device: no interfaces were found");
- return LIBUSB_ERROR_NOT_FOUND;
- }
-
- return LIBUSB_SUCCESS;
-}
-
-/*
- * This function retrieves and sets the paths of all non-hub devices
- * NB: No I/O with device is required during this call
- */
-static int set_device_paths(struct libusb_context *ctx, struct discovered_devs *discdevs)
-{
- // 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"}
- };
- struct windows_device_priv *priv;
- struct windows_device_priv *parent_priv;
- char path[MAX_PATH_LENGTH];
- char *sanitized_path = NULL;
- HDEVINFO dev_info;
- SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL;
- SP_DEVINFO_DATA dev_info_data;
- DEVINST parent_devinst;
- GUID guid;
- DWORD size, reg_type, install_state, port_nr;
- int r = LIBUSB_SUCCESS;
- unsigned i, j, k, l;
- uint8_t api;
- bool found;
-
- // TODO (after first official release): MI_## automated driver installation
- guid = GUID_DEVINTERFACE_USB_DEVICE;
- for (i = 0; ; i++)
- {
- // safe loop: free up any (unprotected) dynamic resource
- safe_free(dev_interface_details);
- safe_free(sanitized_path);
-
- dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid, i);
- // safe loop: end of loop condition
- if ( (dev_interface_details == NULL)
- || (r != LIBUSB_SUCCESS) )
- break;
-
- // Check 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_interface_details->DevicePath, 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; j<discdevs->len; 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; api<USB_API_MAX; api++) {
- for (k=0; k<3; k++) {
- if (is_api_driver(lookup[k].list, api)) {
- usbi_dbg("matched %s name against %s", lookup[k].designation, usb_api_backend[api].designation);
- break;
- }
+ // Setup device
+ switch (pass) {
+ case HCD_PASS:
+ dev->bus_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; j<USB_MAXINTERFACES; j++) {
+ priv->usb_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; k<USB_MAXINTERFACES; k++) {
- priv->usb_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; i<unref_cur; i++) {
+ safe_unref_device(unref_list[i]);
+ }
+ safe_free(unref_list);
return r;
}
*/
static void windows_exit(void)
{
- struct windows_hcd_priv* hcd_tmp;
int i;
HANDLE semaphore;
char sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID)
// Only works if exits and inits are balanced exactly
if (--concurrent_usage < 0) { // Last exit
- while (hcd_root != NULL)
- {
- hcd_tmp = hcd_root; // Keep a copy for free
- hcd_root = hcd_root->next;
- windows_hcd_priv_release(hcd_tmp);
- safe_free(hcd_tmp);
- }
-
for (i=0; i<USB_API_MAX; i++) {
usb_api_backend[i].exit();
}
CloseHandle(timer_mutex);
timer_mutex = NULL;
}
+ htab_destroy();
}
ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
static void windows_destroy_device(struct libusb_device *dev)
{
- struct windows_device_priv *priv = __device_priv(dev);
- windows_device_priv_release(priv, dev->num_configurations);
+ windows_device_priv_release(dev);
}
static void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
}
// 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] = {
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,