From bdcbace8814250a1995b329728931c0f8e5eb90c Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Thu, 14 Jan 2010 01:16:33 +0000 Subject: svn r25: - WinUSB composite device support (with DeviceClasses registry lookup) - better interface handling - other minor improvements --- examples/xusb.c | 13 +- libusb/os/windows_usb.c | 983 ++++++++++++++++++++++++++++-------------------- libusb/os/windows_usb.h | 47 ++- 3 files changed, 626 insertions(+), 417 deletions(-) diff --git a/examples/xusb.c b/examples/xusb.c index 7e61c51..8614e2f 100644 --- a/examples/xusb.c +++ b/examples/xusb.c @@ -81,6 +81,7 @@ int test_device(uint16_t vid, uint16_t pid) { libusb_device_handle *handle; int r; + int iface = 1; printf("Opening device...\n"); handle = libusb_open_device_with_vid_pid(NULL, vid, pid); @@ -90,23 +91,23 @@ int test_device(uint16_t vid, uint16_t pid) return -1; } - printf("Claiming interface...\n"); - r = libusb_claim_interface(handle, 0); + printf("Claiming interface %d...\n", iface); + r = libusb_claim_interface(handle, iface); if (r != LIBUSB_SUCCESS) { // Maybe we need to detach the driver perr("failed. Trying to detach driver...\n"); - CALL_CHECK(libusb_detach_kernel_driver(handle, 0)); + CALL_CHECK(libusb_detach_kernel_driver(handle, iface)); printf("Claiming interface again...\n"); - CALL_CHECK(libusb_claim_interface(handle, 0)); + CALL_CHECK(libusb_claim_interface(handle, iface)); } char string[128]; printf("Retieving string descriptor...\n"); - CALL_CHECK(libusb_get_string_descriptor_ascii(handle, 2, string, 128)); + CALL_CHECK(libusb_get_string_descriptor_ascii(handle, 3, string, 128)); printf("Got string: \"%s\"\n", string); printf("Releasing interface...\n"); - CALL_CHECK(libusb_release_interface(handle, 0)); + CALL_CHECK(libusb_release_interface(handle, iface)); printf("Closing device...\n"); libusb_close(handle); diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c index 101ade3..51454c6 100644 --- a/libusb/os/windows_usb.c +++ b/libusb/os/windows_usb.c @@ -70,14 +70,6 @@ const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} }; #endif -// HACK: WinUSB composite device test -#ifndef GUID_DEVINTERFACE_LIBUSB_DEVICE - // {78a1c341-4539-11d3-b88d-00c04fad5171} -// const GUID GUID_DEVINTERFACE_LIBUSB_DEVICE = { 0x78a1c341, 0x4539, 0x11d3, {0xb8, 0x8d, 0x00, 0xc0, 0x4f, 0xad, 0x51, 0x71} }; - // {b35924d6-3e16-4a9e-9782-5524a4b79bac (XBOX) - const GUID GUID_DEVINTERFACE_LIBUSB_DEVICE = { 0xB35924d6, 0x3E16, 0x4A9E, {0x97, 0x82, 0x55, 0x24, 0xA4, 0xB7, 0x9b, 0xAC} }; -#endif - // The 3 macros below are used in conjunction with safe loops. #define LOOP_CHECK(fcall) { r=fcall; if (r != LIBUSB_SUCCESS) continue; } #define LOOP_CONTINUE(...) { usbi_warn(ctx, __VA_ARGS__); continue; } @@ -131,14 +123,17 @@ char* wchar_to_utf8(LPCWSTR wstr) /* * Converts a windows error to human readable string + * uses retval as errorcode, or, if 0, use GetLastError() */ -static char *windows_error_str(void) +static char *windows_error_str(uint32_t retval) { static char err_string[ERR_BUFFER_SIZE]; + DWORD size; - unsigned int errcode, format_errcode; + uint32_t errcode, format_errcode; + + errcode = retval?retval:GetLastError(); - errcode = GetLastError(); safe_sprintf(err_string, ERR_BUFFER_SIZE, "[%08X] ", errcode); size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errcode, @@ -162,34 +157,36 @@ static char err_string[ERR_BUFFER_SIZE]; */ static char* sanitize_path(const char* path) { + const char root_prefix[] = "\\\\.\\"; int j; - size_t size; - char* ret_path; - bool has_root_prefix = false; + size_t size, root_size; + char* ret_path = NULL; + int add_root = 0; if (path == NULL) return NULL; size = strlen(path)+1; - if ((size > 3) && (path[0] == '\\') && (path[1] == '\\') && (path[3] == '\\')) { - has_root_prefix = true; - } else { - size += sizeof(ROOT_PREFIX); + root_size = sizeof(root_prefix)-1; + + // Microsoft indiscriminatly uses '\\?\', '\\.\', '##?#" or "##.#" for root prefixes. + if (!((size > 3) && (((path[0] == '\\') && (path[1] == '\\') && (path[3] == '\\')) || + ((path[0] == '#') && (path[1] == '#') && (path[3] == '#'))))) { + add_root = root_size; + size += add_root; } if ((ret_path = (char*)calloc(size, 1)) == NULL) return NULL; - if (!has_root_prefix) { - safe_strncpy(ret_path, size, ROOT_PREFIX, sizeof(ROOT_PREFIX)); - } - safe_strncat(ret_path, size, path, strlen(path)); + safe_strncpy(&ret_path[add_root], size-add_root, path, strlen(path)); - // Microsoft indiscriminatly uses '\\?\' or '\\.\' for root prefixes. Ensure '\\.\' is used - ret_path[2] = '.'; + // Ensure consistancy with root prefix + for (j=0; j 0) && (ret_content != NULL)) + ret_content[0] = '\0'; + + if (key_index >= nb_keys) { + usbi_err(ctx, "key index out of range for registry path %s: %d vs %d keys", + key_path, key_index, nb_keys); + RegCloseKey(parent_key); + return -1; + } + + // If what we're interested in is the key name, read the key name + // directly into the return string buffer. Otherwise, use internal + if (only_read_keys) { + size = ret_size; + _key_name = ret_content; + } else { + size = MAX_KEY_LENGTH; + _key_name = internal_key_name; + } + r = RegEnumKeyEx(parent_key, key_index, _key_name, &size, + NULL, NULL, NULL, NULL); + + switch (r) { + case ERROR_SUCCESS: + break; + case ERROR_MORE_DATA: + usbi_err(ctx, "name of key %d exceeds buffer by %d bytes", key_index, + size+1-(only_read_keys?ret_size:MAX_KEY_LENGTH)); + RegCloseKey(parent_key); + return -1; + default: + usbi_err(ctx, "could not read key %d: %s", key_index, windows_error_str(r)); + RegCloseKey(parent_key); + return -1; + } + + // Don't push it further if we only want key names + if (only_read_keys) { + RegCloseKey(parent_key); + return nb_keys; + } + + // Read values + for (i=0; ihEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!io->hEvent) { + free(io); + return NULL; + } + return io; +} + +// From libusb-win32 +void free_overlapped(OVERLAPPED* io) +{ + if(io->hEvent) + CloseHandle(io->hEvent); + free(io); +} + /* * init: libusb backend init function * @@ -295,7 +440,7 @@ static int windows_init(struct libusb_context *ctx) // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { LOOP_CONTINUE("could not access interface data for bus %u, skipping: %s", - bus, windows_error_str()); + bus, windows_error_str(0)); } } else { @@ -312,7 +457,7 @@ static int windows_init(struct libusb_context *ctx) if (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_interface_data, dev_interface_details, size, &size, NULL)) { LOOP_CONTINUE("could not access interface data for bus %u, skipping: %s", - bus, windows_error_str()); + bus, windows_error_str(0)); } // Allocate and init a new priv structure to hold our data @@ -407,7 +552,7 @@ static int force_hcd_device_descriptor(struct libusb_device *dev, HANDLE handle) if (!DeviceIoControl(handle, IOCTL_USB_GET_HUB_CAPABILITIES_EX, &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()); + priv->path, windows_error_str(0)); return LIBUSB_ERROR_IO; } priv->dev_descriptor.idProduct = hub_caps.HubIs2xCapable?2:1; @@ -464,7 +609,7 @@ 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()); + usbi_err(ctx, "could not access configuration descriptor (dummy): %s", windows_error_str(0)); LOOP_BREAK(LIBUSB_ERROR_IO); } @@ -490,7 +635,7 @@ 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()); + usbi_err(ctx, "could not access configuration descriptor (actual): %s", windows_error_str(0)); LOOP_BREAK(LIBUSB_ERROR_IO); } @@ -585,7 +730,7 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs conn_info.ConnectionIndex = i; if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION, &conn_info, size, &conn_info, size, &size, NULL)) { - LOOP_CONTINUE("could not get node connection information: %s", windows_error_str()); + LOOP_CONTINUE("could not get node connection information: %s", windows_error_str(0)); } if (conn_info.ConnectionStatus == NoDeviceConnected) { @@ -612,7 +757,7 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs size = size_initial; if (!DeviceIoControl(hub_handle, getname_ioctl, &s_hubname, size, &s_hubname, size, &size, NULL)) { - LOOP_CONTINUE("could not get hub path (dummy): %s", windows_error_str()); + LOOP_CONTINUE("could not get hub path (dummy): %s", windows_error_str(0)); } size = is_hcd?s_hubname.u.root.ActualLength:s_hubname.u.node.ActualLength; @@ -626,7 +771,7 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs } if (!DeviceIoControl(hub_handle, getname_ioctl, &s_hubname, size, &s_hubname, size, &size, NULL)) { - LOOP_CONTINUE("could not get hub path (actual): %s", windows_error_str()); + LOOP_CONTINUE("could not get hub path (actual): %s", windows_error_str(0)); } // Add prefix @@ -647,7 +792,7 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs handle = CreateFileA(path_str, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_POSIX_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL); if(handle == INVALID_HANDLE_VALUE) { - LOOP_CONTINUE("could not open hub %s: %s", path_str, windows_error_str()); + LOOP_CONTINUE("could not open hub %s: %s", path_str, windows_error_str(0)); } } @@ -713,7 +858,7 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_INFORMATION, &hub_node, size, &hub_node, size, &size, NULL)) { LOOP_CONTINUE("could not retreive information for hub %s: %s", - priv->path, windows_error_str()); + priv->path, windows_error_str(0)); } if (hub_node.NodeType != UsbHub) { @@ -731,6 +876,128 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs return r; } +static char* get_composite_interface_path(struct libusb_context *ctx, char* path) +{ + char cmp_path[MAX_PATH]; + LONG i, j, r; + bool found = false; + char* ret_path = NULL; + + char guid_name[40]; // 40 chars are enough for a GUID string + char instance_path[MAX_PATH]; + char device_classes_path[] = "SYSTEM\\ControlSet001\\Control\\DeviceClasses\\"; + char device_guid_path[MAX_PATH_LENGTH]; // could do without, but clearer code + char full_shebang[MAX_KEY_LENGTH]; + + int nb_device_classes = read_registry_key(ctx, device_classes_path, NULL, -1, NULL, 0); + if (nb_device_classes <= 0) { + usbi_err(ctx, "could not find any device classes"); + return NULL; + } + + // browse GUIDs: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\DeviceClasses\* + // eg. of returned value: "{b35924d6-3e16-4a9e-9782-5524a4b79bac}" + for (i=0; i MAX_KEY_LENGTH) { + usbi_warn(ctx, "key path exceeds buffer - skipping key: %s\\%s", device_guid_path, instance_path); + continue; + } + safe_strcpy(full_shebang, MAX_KEY_LENGTH, device_guid_path); + safe_strcat(full_shebang, MAX_KEY_LENGTH, "\\"); + safe_strcat(full_shebang, MAX_KEY_LENGTH, instance_path); + + // Read the DeviceInstance attribute for this key + if (read_registry_key(ctx, full_shebang, "DeviceInstance", 0, cmp_path, MAX_PATH) <= 0) { + continue; + } + + upperize(cmp_path); // Fix for MS inconsistancy + + if (safe_strncmp(cmp_path, path, MAX_PATH) == 0) { + if (found) { + usbi_dbg("I stand corrected: device GUIDs do actually serve a purpose."); + } else { + ret_path = sanitize_path(instance_path); + } + found = true; + } + } + } + + return ret_path; +} + +static int set_composite_device(struct libusb_context *ctx, DEVINST devinst, struct windows_device_priv *priv) +{ + DEVINST child_devinst; + int interface_number, found; + char path[MAX_PATH_LENGTH]; + CONFIGRET r; + + // TODO: can we check that these kids actually use WinUSB.sys? + // Also, we assume MS does list siblings in interface order + // TODO: lookup the rightmost MI_## and use that for interface_nr + found = 0; + for (interface_number = 0; interface_numberinterface_path[interface_number] = get_composite_interface_path(ctx, path); + if (priv->interface_path[interface_number] == NULL) { + usbi_warn(ctx, "could not retreive full path for interface %d", + interface_number); + continue; + } + usbi_dbg("interface_path[%d]: %s", interface_number, priv->interface_path[interface_number]); + found++; + } + + if (found == 0) { + usbi_dbg("composite device: no interfaces 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 @@ -742,197 +1009,165 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs * char path[MAX_PATH_LENGTH]; char reg_key[MAX_KEY_LENGTH]; char *sanitized_path = NULL; - HDEVINFO dev_info; //, dev_info2; + HDEVINFO dev_info; SP_DEVICE_INTERFACE_DATA dev_interface_data; SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL; SP_DEVINFO_DATA dev_info_data; - DEVINST parent_devinst; //, child_devinst + DEVINST parent_devinst; GUID guid; DWORD size, reg_type; int r = LIBUSB_SUCCESS; unsigned i, j, port_nr, hub_nr; bool found; - // List all connected devices that are not a hub + /* + * List *most* connected devices that are not a hub + * + * Note that the BIG problem with Microsoft's SetupDi functions is they DO NOT list WinUSB + * MI_## devices, i.e. devices associated to a specific interface of a composite USB device. + * Moreover, until someone proves otherwise (especially on Windows 7 x64 - good luck with that!), + * the only way to retrieve a interface device path that can actually be used with CreateFile + * is to lookup all the GUIDs in HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\DeviceClasses\ + * + * TODO: MI_## automated driver installation: + */ guid = GUID_DEVINTERFACE_USB_DEVICE; -// guid = GUID_DEVINTERFACE_LIBUSB_DEVICE; // Why doesn't this work?!?! dev_info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE); - // "USB\\VID_045E&PID_0289\\7&7EF95EB&0&1" - // "USB\\VID_15BA&PID_0004&MI_00\\7&259ca3ed&5&0000" - // dev_info = SetupDiGetClassDevsA(NULL, "USB\\VID_15BA&PID_0004&MI_00\\7&259ca3ed&5&0000", NULL, DIGCF_ALLCLASSES|DIGCF_DEVICEINTERFACE); + if (dev_info == INVALID_HANDLE_VALUE) { + SetupDiDestroyDeviceInfoList(dev_info); + return LIBUSB_SUCCESS; + } - if (dev_info != INVALID_HANDLE_VALUE) + dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + for (i = 0; ; i++) { - dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + // safe loop: free up any (unprotected) dynamic resource + safe_free(dev_interface_details); + safe_free(sanitized_path); + + // safe loop: end of loop condition + guid = GUID_DEVINTERFACE_USB_DEVICE; + if ( (SetupDiEnumDeviceInterfaces(dev_info, NULL, &guid, i, &dev_interface_data) != TRUE) + ||(r != LIBUSB_SUCCESS) ) + break; - for (i = 0; ; i++) - { - - // safe loop: free up any (unprotected) dynamic resource - safe_free(dev_interface_details); - safe_free(sanitized_path); + // Read interface data (dummy + actual) to access the device path + if (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_interface_data, NULL, 0, &size, NULL)) { + // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + LOOP_CONTINUE("could not access interface data (dummy) for device #%u, skipping: %s", + i, windows_error_str(0)); + } + } + else { + LOOP_CONTINUE("program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong."); + } - // safe loop: end of loop condition - guid = GUID_DEVINTERFACE_USB_DEVICE; - if ( (SetupDiEnumDeviceInterfaces(dev_info, NULL, &guid, i, &dev_interface_data) != TRUE) - ||(r != LIBUSB_SUCCESS) ) - break; + if ((dev_interface_details = malloc(size)) == NULL) { + usbi_err(ctx, "could not allocate interface data for device #%u. aborting.", i); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } -// usbi_dbg("*********i = %d", i); + dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + if (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_interface_data, + dev_interface_details, size, &size, NULL)) { + LOOP_CONTINUE("could not access interface data (actual) for device #%u, skipping: %s", + i, windows_error_str(0)); + } - // Read interface data (dummy + actual) to access the device path - if (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_interface_data, NULL, 0, &size, NULL)) { - // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - LOOP_CONTINUE("could not access interface data (dummy) for device #%u, skipping: %s", - i, windows_error_str()); - } - } - else { - LOOP_CONTINUE("program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong."); - } + // Retrieve location information (port#) through the Location Information registry data + dev_info_data.cbSize = sizeof(dev_info_data); + if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) { + LOOP_CONTINUE("could not retrieve info data for device #%u, skipping: %s", + i, windows_error_str(0)); + } - if ((dev_interface_details = malloc(size)) == NULL) { - usbi_err(ctx, "could not allocate interface data for device #%u. aborting.", i); - LOOP_BREAK(LIBUSB_ERROR_NO_MEM); - } + if(!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_LOCATION_INFORMATION, + ®_type, (BYTE*)reg_key, MAX_KEY_LENGTH, &size)) { + LOOP_CONTINUE("could not retrieve location information for device #%u, skipping: %s", + i, windows_error_str(0)); + } - dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - if (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_interface_data, - dev_interface_details, size, &size, NULL)) { - LOOP_CONTINUE("could not access interface data (actual) for device #%u, skipping: %s", - i, windows_error_str()); - } + if (size != sizeof("Port_#1234.Hub_#1234")) { + LOOP_CONTINUE("unexpected registry key size for device #%u, skipping", i); + } + if (sscanf(reg_key, "Port_#%04d.Hub_#%04d", &port_nr, &hub_nr) != 2) { + LOOP_CONTINUE("failure to read port and hub number for device #%u, skipping", i); + } -// usbi_dbg("GOT %s", dev_interface_details->DevicePath); + // Retrieve parent's path using PnP Configuration Manager (CM) + if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) { + LOOP_CONTINUE("could not retrieve parent info data for device #%u, skipping: %s", + i, windows_error_str(0)); + } + + if (CM_Get_Device_ID(parent_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) { + LOOP_CONTINUE("could not retrieve parent's path for device #%u, skipping: %s", + i, windows_error_str(0)); + } - // Retrieve location information (port#) through the Location Information registry data - dev_info_data.cbSize = sizeof(dev_info_data); - if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) { - LOOP_CONTINUE("could not retrieve info data for device #%u, skipping: %s", - i, windows_error_str()); - } + // Fix parent's path inconsistancies before attempting to compare + sanitized_path = sanitize_path(path); + if (sanitized_path == NULL) { + LOOP_CONTINUE("could not sanitize parent's path for device #%u, skipping.", i); + } - if(!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_LOCATION_INFORMATION, - ®_type, (BYTE*)reg_key, MAX_KEY_LENGTH, &size)) { - LOOP_CONTINUE("could not retrieve location information for device #%u, skipping: %s", - i, windows_error_str()); - } + // 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 (size != sizeof("Port_#1234.Hub_#1234")) { - LOOP_CONTINUE("unexpected registry key size for device #%u, skipping", i); - } - if (sscanf(reg_key, "Port_#%04d.Hub_#%04d", &port_nr, &hub_nr) != 2) { - LOOP_CONTINUE("failure to read port and hub number for device #%u, skipping", i); + // ignore HCDs + if (priv->parent_dev == NULL) { + continue; } + parent_priv = __device_priv(priv->parent_dev); - // Retrieve parent's path using PnP Configuration Manager (CM) - if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) { - LOOP_CONTINUE("could not retrieve parent info data for device #%u, skipping: %s", - i, windows_error_str()); - } - - if (CM_Get_Device_ID(parent_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) { - LOOP_CONTINUE("could not retrieve parent's path for device #%u, skipping: %s", - i, windows_error_str()); - } + // NB: we compare strings of different lengths below => strncmp + if ( (safe_strncmp(parent_priv->path, sanitized_path, sizeof(sanitized_path)) == 0) + && (port_nr == priv->connection_index) ) { - // Fix parent's path inconsistancies before attempting to compare - sanitized_path = sanitize_path(path); - if (sanitized_path == NULL) { - LOOP_CONTINUE("could not sanitize parent's path for device #%u, skipping.", i); - } + priv->path = sanitize_path(dev_interface_details->DevicePath); - // 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]); - // ignore HCDs - if (priv->parent_dev == NULL) { - continue; - } - parent_priv = __device_priv(priv->parent_dev); - if ( (safe_strncmp(parent_priv->path, sanitized_path, strlen(sanitized_path)) == 0) && - (port_nr == priv->connection_index) ) { - - safe_free(sanitized_path); - - // before anything, check the service name to know what kind of device we have. - // The service name is really the driver name without ".sys" ("WinUSB", "HidUsb", ...) - // It tells us if we can use WinUSB or if we need to handle a composite device - if(!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_SERVICE, - ®_type, (BYTE*)reg_key, MAX_KEY_LENGTH, &size)) { - LOOP_CONTINUE("could not retrieve driver information for device #%u, skipping: %s", - i, windows_error_str()); - } - usbi_dbg("driver: %s", reg_key); - - // Composite devices use the USB Common Class Generic Parent driver - if (safe_strncmp(reg_key, "usbccgp", MAX_KEY_LENGTH) == 0) { - usbi_dbg("composite device %s", dev_interface_details->DevicePath); -/* - // We can't use the ccgp driver for WinUSB. Instead, try the first child - if (CM_Get_Child(&child_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) { - LOOP_CONTINUE("could not retrieve child info data for device #%u, skipping: %s", - i, windows_error_str()); - } - - if (CM_Get_Device_ID(child_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) { - LOOP_CONTINUE("could not retrieve children's path for device #%u, skipping: %s", - i, windows_error_str()); - } - - // Of course, no CM function returns a path that we can actually use with CreateFile - // (that would have been too easy), and of course, there's absolutely NO WAY to list - // the composite WinUSB devices using SetupDi, because Microsoft decided they should - // be 100% invisible to the SetupDi functions... - - - // Try a call requiring admin privs... - - dev_info2 = SetupDiCreateDeviceInfoList(NULL, NULL); - if (dev_info2 == INVALID_HANDLE_VALUE) { - usbi_dbg("dammit 1"); - } - - if (!SetupDiCreateDeviceInfo(dev_info2, "USB\\VID_15BA&PID_0004&MI_00\\7&259ca3ed&5&0000", NULL, NULL, NULL, 0, NULL)) { - usbi_dbg("dammit 2: %s", windows_error_str()); - } - -// SetupDiEnumDeviceInterfaces -*/ -// safe_strncat(path, MAX_PATH_LENGTH, "#{A5DCBF10-6530-11D2-901F-00C04FB951ED}", sizeof("#{A5DCBF10-6530-11D2-901F-00C04FB951ED}")); - // sanitized_path = sanitize_path(path); - sanitized_path = sanitize_path(dev_interface_details->DevicePath); - if (sanitized_path == NULL) { - LOOP_CONTINUE("could not sanitize child's path for device #%u, skipping.", i); - } - - // TODO: actual check for WinUSB driver on ALL children - safe_strncpy(reg_key, MAX_KEY_LENGTH, "WinUSB", sizeof("WinUSB")); - usbi_dbg("TODO: FETCH ACTUAL DRIVER (forcing driver to: %s)", reg_key); - } else { - sanitized_path = sanitize_path(dev_interface_details->DevicePath); - } - - priv->driver = safe_strdup(reg_key); - priv->path = sanitized_path; - sanitized_path = NULL; // safe loop - usbi_dbg("path (%d:%d): %s", discdevs->devices[j]->bus_number, - discdevs->devices[j]->device_address, priv->path); - found = true; + usbi_dbg("path (%d:%d): %s", discdevs->devices[j]->bus_number, + discdevs->devices[j]->device_address, priv->path); + // Check the service name to know what kind of device we have. + // The service name is really the driver name without ".sys" ("WinUSB", "HidUsb", ...) + // It tells us if we can use WinUSB, if we have a composite device, and the API to use + if(!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_SERVICE, + ®_type, (BYTE*)reg_key, MAX_KEY_LENGTH, &size)) { + usbi_err(ctx, "could not retrieve driver information for device #%u, skipping: %s", + i, windows_error_str(0)); break; } - } - if (!found) { - LOOP_CONTINUE("could not match %s with a libusb device.", dev_interface_details->DevicePath); + + priv->driver = safe_strdup(reg_key); + usbi_dbg("driver: %s", priv->driver); + found = true; + + if (safe_strcmp(priv->driver, "WinUSB") == 0) { + priv->api = API_WINUSB; + // For non composite, the first interface is the same as the device + priv->interface_path[0] = safe_strdup(priv->path); // needs strdup + // Composite (multi-interface) devices are identified by their use of + // the USB Common Class Generic Parent driver + } else if (safe_strcmp(reg_key, "usbccgp") == 0) { + // This call sets the paths for all available interfaces + set_composite_device(ctx, dev_info_data.DevInst, priv); + // TODO: for now, just assume child devices are WinUSB + priv->api = API_WINUSB; + } + break; } } - SetupDiDestroyDeviceInfoList(dev_info); + if (!found) { + LOOP_CONTINUE("could not match %s with a libusb device.", dev_interface_details->DevicePath); + } } + SetupDiDestroyDeviceInfoList(dev_info); return LIBUSB_SUCCESS; } @@ -963,7 +1198,7 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered handle = CreateFileA(hcd->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_POSIX_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL); if (handle == INVALID_HANDLE_VALUE) { - LOOP_CONTINUE("could not open bus %u, skipping: %s", bus, windows_error_str()); + LOOP_CONTINUE("could not open bus %u, skipping: %s", bus, windows_error_str(0)); } LOOP_CHECK(usb_enumerate_hub(ctx, _discdevs, handle, bus, NULL, 1)); @@ -1047,66 +1282,30 @@ static int windows_get_active_config_descriptor(struct libusb_device *dev, unsig return windows_get_config_descriptor(dev, priv->active_config-1, buffer, len, host_endian); } -// From libusb-win32 -static OVERLAPPED* create_overlapped(void) -{ - OVERLAPPED* io = malloc(sizeof(OVERLAPPED)); - - if(io == NULL) - return NULL; - - memset(io, 0, sizeof(OVERLAPPED)); - io->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if(!io->hEvent) { - free(io); - return NULL; - } - return io; -} - -// From libusb-win32 -void free_overlapped(OVERLAPPED* io) -{ - if(io->hEvent) - CloseHandle(io->hEvent); - free(io); -} - static int windows_open(struct libusb_device_handle *dev_handle) { int r = LIBUSB_SUCCESS; struct windows_device_priv *priv = __device_priv(dev_handle->dev); -// struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; - // Select the API to use - if (safe_strcmp(priv->driver, "WinUSB") == 0) { - r = winusb_open(dev_handle); - } else { - r = LIBUSB_ERROR_NOT_SUPPORTED; - } + API_CALL(priv->api, open, dev_handle); // TODO: update pipe info here? - // TODO: HANDLE is a void* which we cast to int => MASSIVE POTENTIAL ISSUE!!! - // => provide a lookup table for fd <-> HANDLE ? -// usbi_dbg("WARNING: casting (HANDLE) %p to (int) 0x%x", handle_priv->file_handle, (int)handle_priv->file_handle); -// usbi_add_pollfd(HANDLE_CTX(dev_handle), (int)handle_priv->file_handle, POLLIN); - return r; } static void windows_close(struct libusb_device_handle *dev_handle) { struct windows_device_priv *priv = __device_priv(dev_handle->dev); -// struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; - if (safe_strcmp(priv->driver, "WinUSB") == 0) { + switch(priv->api) { + case API_WINUSB: winusb_close(dev_handle); + break; + default: + break; } // TODO: free pipe? - // TODO: HANDLE vs int -// usbi_remove_pollfd (HANDLE_CTX(dev_handle), (int)handle_priv->file_handle); - } /* @@ -1134,15 +1333,12 @@ static int windows_set_configuration(struct libusb_device_handle *dev_handle, in static int windows_claim_interface(struct libusb_device_handle *dev_handle, int iface) { int r = LIBUSB_SUCCESS; - struct windows_device_priv *priv = __device_priv(dev_handle->dev); - // Select the API to use - if (safe_strcmp(priv->driver, "WinUSB") == 0) { - r = winusb_claim_interface(dev_handle, iface); - } else { - r = LIBUSB_ERROR_NOT_SUPPORTED; - } + if (iface >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + + API_CALL(priv->api, claim_interface, dev_handle, iface); return r; } @@ -1152,12 +1348,7 @@ static int windows_release_interface(struct libusb_device_handle *dev_handle, in int r = LIBUSB_SUCCESS; struct windows_device_priv *priv = __device_priv(dev_handle->dev); - // Select the API to use - if (safe_strcmp(priv->driver, "WinUSB") == 0) { - r = winusb_release_interface(dev_handle, iface); - } else { - r = LIBUSB_ERROR_NOT_SUPPORTED; - } + API_CALL(priv->api, release_interface, dev_handle, iface); return r; } @@ -1182,7 +1373,6 @@ static int windows_kernel_driver_active(struct libusb_device_handle *dev_handle, return LIBUSB_ERROR_NOT_SUPPORTED; } -/* attaching/detaching kernel drivers is not currently supported (maybe in the future?) */ static int windows_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface) { return LIBUSB_ERROR_NOT_SUPPORTED; } @@ -1212,24 +1402,28 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev); OVERLAPPED *io; int r; - // TODO: is it safe to create io in open? - if((io = create_overlapped()) == NULL) + // Our custom poll doesn't provide as fine grained control over an OVERLAPPED + // (which we use as fd) as POSIX's poll provides over an fd + // => deal with OVERLAPPED in transfer related functions + if ((io = create_overlapped()) == NULL) return LIBUSB_ERROR_NO_MEM; - r = winusb_submit_control_transfer(itransfer, io); + API_CALL(priv->api, submit_control_transfer, itransfer, io); + if (r != LIBUSB_SUCCESS) { free_overlapped(io); return r; } - // TODO: are transfer reused? transfer_priv->io = io; // TODO: we use the OVERLAPPED _pointer_ as a file descriptor with a // *VERY DANGEROUS* cast to _int_) + // => use a global lookup table for fd <-> HANDLE usbi_dbg("WARNING: casting (OVERLAPPED*) %p to (int) 0x%x", io, (int)io); usbi_add_pollfd(ctx, (int)io, POLLIN); @@ -1276,7 +1470,7 @@ static int windows_cancel_transfer(struct usbi_transfer *itransfer) case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: return windows_abort_transfers(itransfer); default: - usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); return LIBUSB_ERROR_INVALID_PARAM; } } @@ -1285,25 +1479,32 @@ static void windows_clear_transfer_priv(struct usbi_transfer *itransfer) { } -static void windows_control_callback (struct usbi_transfer *itransfer, int io_result, uint32_t io_size) +static void windows_control_callback (struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) { int status; usbi_dbg("handling control completion with status %d", io_result); - // TODO switch(io_result) { - default: + case NO_ERROR: status = LIBUSB_TRANSFER_COMPLETED; itransfer->transferred += io_size; break; + case ERROR_GEN_FAILURE: // is the error we get for unsupported on Windows + usbi_dbg("unsupported control request"); + status = LIBUSB_TRANSFER_STALL; + break; + default: + usbi_err(ITRANSFER_CTX(itransfer), "control error: %s", windows_error_str(0)); + status = LIBUSB_TRANSFER_ERROR; + break; } usbi_handle_transfer_completion(itransfer, status); } -static void windows_handle_callback (struct usbi_transfer *itransfer, int io_result, uint32_t io_size) +static void windows_handle_callback (struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) { struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); @@ -1316,11 +1517,11 @@ static void windows_handle_callback (struct usbi_transfer *itransfer, int io_res // windows_bulk_callback (itransfer, io_result, io_size); break; case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: - // TODO: ain't gonna happen with WinUSB + // TODO: ain't gonna happen with WinUSB only // windows_isoc_callback (itransfer, io_result); break; default: - usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); + usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type); } } @@ -1330,14 +1531,11 @@ static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, int i = 0; bool found = false; struct usbi_transfer *transfer; - DWORD io_size; - + DWORD io_size, io_result; -// pthread_mutex_lock(&ctx->open_devs_lock); + pthread_mutex_lock(&ctx->open_devs_lock); for (i = 0; i < nfds && num_ready > 0; i++) { struct pollfd *pollfd = &fds[i]; -// struct libusb_device_handle *handle; -// struct windows_device_handle_priv *hpriv = NULL; usbi_dbg("checking fd %x with revents = %x", fds[i], pollfd->revents); @@ -1347,6 +1545,8 @@ static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, num_ready--; + // Using transfer_priv to store our polling data + // is the most logical choice for now list_for_each_entry(transfer, &ctx->flying_transfers, list) { transfer_priv = usbi_transfer_get_os_priv(transfer); if ((int)transfer_priv->io == pollfd->fd) { @@ -1356,30 +1556,20 @@ static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, } if (found) { - if (!GetOverlappedResult(transfer_priv->handle, transfer_priv->io, &io_size, false)) { - usbi_err(ctx, "GetOverlappedResult failed: %s", windows_error_str()); + if (GetOverlappedResult(transfer_priv->handle, transfer_priv->io, &io_size, false)) { + io_result = NO_ERROR; } else { - // Remove the polled "fd" - usbi_remove_pollfd(ctx, (int)transfer_priv->io); - windows_handle_callback(transfer, 0, io_size); - } - } - -/* - list_for_each_entry(handle, &ctx->open_devs, list) { - hpriv = (struct windows_device_handle_priv *)handle->os_priv; - // TODO: does this actually work? - usbi_dbg("pollfd->fd = %x", pollfd->fd); - // TODO: HANDLE vs int - if ((int)hpriv->io == pollfd->fd) { - usbi_dbg("io == fd"); - break; - } + io_result = GetLastError(); + } + usbi_remove_pollfd(ctx, (int)transfer_priv->io); + windows_handle_callback(transfer, io_result, io_size); + } else { + usbi_err(ctx, "could not find a matching transfer for fd %x", fds[i]); + return LIBUSB_ERROR_NOT_FOUND; } -*/ } -// pthread_mutex_unlock(&ctx->open_devs_lock); + pthread_mutex_unlock(&ctx->open_devs_lock); return LIBUSB_SUCCESS; } @@ -1504,95 +1694,51 @@ static int winusb_open(struct libusb_device_handle *dev_handle) struct windows_device_priv *priv = __device_priv(dev_handle->dev); struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; - HANDLE handle; + HANDLE file_handle; + int i; CHECK_WINUSB_AVAILABLE; -/* - usbi_dbg("testing detached device: disconnect NOW!"); - Sleep(2000); -*/ - // TODO: better check for detached devices - handle = CreateFileA(priv->path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); - if (handle == INVALID_HANDLE_VALUE) { - usbi_err(ctx, "could not open device %s: %s", priv->path, windows_error_str()); - // TODO? Create a windows errcode -> libusb errcode function - switch(GetLastError()) { - case ERROR_FILE_NOT_FOUND: // The device was disconnected - return LIBUSB_ERROR_NO_DEVICE; - case ERROR_ACCESS_DENIED: - return LIBUSB_ERROR_ACCESS; - default: - return LIBUSB_ERROR_IO; - } - } - - handle_priv->file_handle = handle; -} - -/* - WINUSB_INTERFACE_HANDLE interface_handle; - USB_INTERFACE_DESCRIPTOR interface_desc; - WINUSB_PIPE_INFORMATION pipe_info; - uint8_t speed, i; - ULONG length; - - length = sizeof(speed); - if (!WinUsb_QueryDeviceInformation(winusb_handle, DEVICE_SPEED, &length, &speed)) { - usbi_err(ctx, "could not get device speed: %s", windows_error_str()); - return LIBUSB_ERROR_IO; - } - - // TODO: - // Because the Fx2 device supports only one interface that has no alternative settings, - // the AlternateSettingNumber parameter is set to zero and the function is called only - // once. If the device supports multiple interfaces, call WinUsb_GetAssociatedInterface - // to obtain interface handles for associated interfaces. - - if (!WinUsb_QueryInterfaceSettings(winusb_handle, 0, &interface_desc)) { - usbi_err(ctx, "could not get interface settings: %s", windows_error_str()); - return LIBUSB_ERROR_IO; - } - - for(i=0; iinterface_path[i] != NULL) { + file_handle = CreateFileA(priv->interface_path[i], GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + if (file_handle == INVALID_HANDLE_VALUE) { + usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0)); + // TODO? Create a windows errcode -> libusb errcode function + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ACCESS_DENIED: + return LIBUSB_ERROR_ACCESS; + default: + return LIBUSB_ERROR_IO; + } + } + handle_priv->interface_handle[i].file = file_handle; } } return LIBUSB_SUCCESS; } -*/ + static void winusb_close(struct libusb_device_handle *dev_handle) { -// struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; - + HANDLE file_handle; + int i; + if (!api_winusb_available) return; - if ((handle_priv->file_handle != 0) && (handle_priv->file_handle != INVALID_HANDLE_VALUE)) { - CloseHandle(handle_priv->file_handle); + for (i = 0; i < USB_MAXINTERFACES; i++) { + file_handle = handle_priv->interface_handle[i].file; + if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) { + CloseHandle(file_handle); + } } } @@ -1605,79 +1751,93 @@ static void winusb_close(struct libusb_device_handle *dev_handle) * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it * was opened * - another LIBUSB_ERROR code on other failure + * + * NOTE: For non composite devices on Windows, you must first claim interface + * 0 before you can claim the other interfaces */ static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int iface) { struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; + struct windows_device_priv *priv = __device_priv(dev_handle->dev); + bool is_composite = (safe_strcmp(priv->driver, "usbccgp") == 0); + HANDLE file_handle, winusb_handle; CHECK_WINUSB_AVAILABLE; -/* - usbi_dbg("testing detached device: disconnect NOW!"); - Sleep(2000); -*/ - - // TODO - if (iface != 0) { - usbi_dbg("mutliple interfaces not supported yet"); - return LIBUSB_ERROR_NOT_SUPPORTED; - } + winusb_handle = handle_priv->interface_handle[iface].winusb; + + // interfaces for composite devices are always independent, therefore + // "alt" interfaces are only found on non-composite + if ((!is_composite) && (iface != 0)) { + // TODO: for now, we'll make this as a requirement on Windows that + // to claim an "alt" interface >= 1 on a non-composite device, you + // must first have claimed interface 0 + if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) { + return LIBUSB_ERROR_ACCESS; + } - if ((handle_priv->interface_handle[iface] != 0) && (handle_priv->interface_handle[iface] != INVALID_HANDLE_VALUE)) { - return LIBUSB_ERROR_BUSY; + if (!WinUsb_GetAssociatedInterface(winusb_handle, iface-1, + &handle_priv->interface_handle[iface].winusb)) { + handle_priv->interface_handle[iface].winusb = INVALID_HANDLE_VALUE; + switch(GetLastError()) { + case ERROR_NO_MORE_ITEMS: // invalid iface + return LIBUSB_ERROR_NOT_FOUND; + case ERROR_BAD_COMMAND: // The device was disconnected + return LIBUSB_ERROR_NO_DEVICE; + case ERROR_ALREADY_EXISTS: // already claimed + return LIBUSB_ERROR_BUSY; + default: + usbi_err(ctx, "could not claim interface %d: %s", iface, windows_error_str(0)); + return LIBUSB_ERROR_ACCESS; + } + } + usbi_dbg("claimed interface %d", iface); + return LIBUSB_SUCCESS; } - if ((handle_priv->file_handle == 0) || (handle_priv->file_handle == INVALID_HANDLE_VALUE)) { + // composite device (independent interfaces) or interface 0 + file_handle = handle_priv->interface_handle[iface].file; + if ((file_handle == 0) || (file_handle == INVALID_HANDLE_VALUE)) { return LIBUSB_ERROR_NOT_FOUND; } - // The handle returned by WinUsb_Initialize is the first interface handle - if (!WinUsb_Initialize(handle_priv->file_handle, &handle_priv->interface_handle[0])) { - usbi_err(ctx, "could not claim interface 0: %s", windows_error_str()); + if (!WinUsb_Initialize(file_handle, &winusb_handle)) { + usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(0)); + handle_priv->interface_handle[iface].winusb = INVALID_HANDLE_VALUE; switch(GetLastError()) { case ERROR_BAD_COMMAND: // The device was disconnected return LIBUSB_ERROR_NO_DEVICE; default: + usbi_err(ctx, "could not claim interface %d: %s", iface, windows_error_str(0)); return LIBUSB_ERROR_ACCESS; } - - // TODO: check error for LIBUSB_ERROR_BUSY - handle_priv->interface_handle[0] = INVALID_HANDLE_VALUE; - return LIBUSB_ERROR_IO; } - - // TODO: for other interface handles, use WinUsb_GetAssociatedInterface - + handle_priv->interface_handle[iface].winusb = winusb_handle; + usbi_dbg("claimed interface %d", iface); return LIBUSB_SUCCESS; } +/* + * TODO: + * This function should also generate a SET_INTERFACE control request, + * resetting the alternate setting of that interface to 0. It's OK for + * this function to block as a result. + */ static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface) { -// struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)dev_handle->os_priv; + HANDLE winusb_handle; CHECK_WINUSB_AVAILABLE; - // TODO - if (iface != 0) { - usbi_dbg("mutliple interfaces not supported yet"); - return LIBUSB_ERROR_NOT_SUPPORTED; - } - - if ((handle_priv->file_handle == 0) || (handle_priv->file_handle == INVALID_HANDLE_VALUE)) { + winusb_handle = handle_priv->interface_handle[iface].winusb; + if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) { return LIBUSB_ERROR_NOT_FOUND; } - // libusb_open zeroes the priv struct, which is different from INVALID_HANDLE_VALUE (-1) - // However: http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.kernel/2005-05/msg00448.html - // "On any NT-derived platform 0 (zero) can never be a valid handle value", therefore we should be OK - if ((handle_priv->interface_handle[iface] != 0) && (handle_priv->interface_handle[iface] != INVALID_HANDLE_VALUE)) { - WinUsb_Free(handle_priv->interface_handle[iface]); - } else { - return LIBUSB_ERROR_NOT_FOUND; - } + WinUsb_Free(winusb_handle); return LIBUSB_SUCCESS; } @@ -1690,18 +1850,33 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer, OVERL struct windows_device_handle_priv *handle_priv = (struct windows_device_handle_priv *)transfer->dev_handle->os_priv; WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *) transfer->buffer; ULONG size, junk; + HANDLE winusb_handle; + bool found; + int i; - //TODO: multiple interfaces - int iface = 0; // Force interface 0 + CHECK_WINUSB_AVAILABLE; size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE; if (size > MAX_CTRL_BUFFER_LENGTH) return LIBUSB_ERROR_INVALID_PARAM; - if (!WinUsb_ControlTransfer(handle_priv->interface_handle[iface], *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, &junk, io)) { + // If it's control, the first active interface will do + for (i=0,found=false; (iinterface_handle[i].winusb; + if ((winusb_handle != 0) && (winusb_handle != INVALID_HANDLE_VALUE)) { + found = true; + } + } + if (!found) { + usbi_err(ctx, "no active interface"); + return LIBUSB_ERROR_ACCESS; + } + usbi_dbg("will use interface %d", i-1); + + if (!WinUsb_ControlTransfer(winusb_handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, &junk, io)) { if(GetLastError() != ERROR_IO_PENDING) { - usbi_err(ctx, "WinUsb_ControlTransfer failed: %s", windows_error_str()); + usbi_err(ctx, "WinUsb_ControlTransfer failed: %s", windows_error_str(0)); return LIBUSB_ERROR_IO; } } else { @@ -1709,8 +1884,8 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer, OVERL return LIBUSB_ERROR_IO; } - // TODO: don't store this is transfer_priv - transfer_priv->handle = handle_priv->interface_handle[iface]; + // Again, use priv_transfer to store data needed for async polling + transfer_priv->handle = winusb_handle; usbi_dbg("overlapped WinUsb_ControlTransfer initiated"); diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h index 524fa4f..e659896 100644 --- a/libusb/os/windows_usb.h +++ b/libusb/os/windows_usb.h @@ -44,25 +44,47 @@ #define safe_free(p) do {if (p != NULL) {free(p); p = NULL;}} while(0) #define safe_closehandle(h) do {if (h != INVALID_HANDLE_VALUE) {CloseHandle(h); h = INVALID_HANDLE_VALUE;}} while(0) #define safe_strncpy(dst, dst_max, src, count) strncpy(dst, src, min(count, dst_max - 1)) +#define safe_strcpy(dst, dst_max, src) safe_strncpy(dst, dst_max, src, strlen(src)+1) #define safe_strncat(dst, dst_max, src, count) strncat(dst, src, min(count, dst_max - strlen(dst) - 1)) +#define safe_strcat(dst, dst_max, src) safe_strncat(dst, dst_max, src, strlen(src)+1) #define safe_strcmp(str1, str2) strcmp(((str1==NULL)?"":str1), ((str2==NULL)?"":str2)) #define safe_strncmp(str1, str2, count) strncmp(((str1==NULL)?"":str1), ((str2==NULL)?"":str2), count) #define safe_strdup _strdup #define safe_sprintf _snprintf #define safe_unref_device(dev) do {if (dev != NULL) {libusb_unref_device(dev); dev = NULL;}} while(0) +void inline upperize(char* str) { + size_t i; + if (str == NULL) return; + for (i=0; iparent_dev = NULL; p->connection_index = 0; p->path = NULL; p->driver = NULL; + p->api = API_NONE; p->active_config = 0; p->config_descriptor = NULL; memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR)); + for (i=0; iinterface_path[i] = NULL; } static inline void windows_device_priv_release(struct windows_device_priv* p, int num_configurations) { @@ -115,24 +143,27 @@ static inline void windows_device_priv_release(struct windows_device_priv* p, in safe_free(p->config_descriptor[i]); } safe_free(p->config_descriptor); + for (i=0; iinterface_path[i]); } static inline struct windows_device_priv *__device_priv(struct libusb_device *dev) { return (struct windows_device_priv *)dev->os_priv; } -typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; +struct winusb_handles { + HANDLE file; + HANDLE winusb; +}; struct windows_device_handle_priv { - bool is_open; - HANDLE file_handle; - WINUSB_INTERFACE_HANDLE interface_handle[USB_MAXINTERFACES]; + struct winusb_handles interface_handle[USB_MAXINTERFACES]; }; +// used for async polling functions struct windows_transfer_priv { OVERLAPPED* io; HANDLE handle; - uint32_t io_size; }; @@ -295,6 +326,8 @@ typedef struct { } WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; #pragma pack() +typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; + DLL_DECLARE(WINAPI, BOOL, WinUsb_Initialize, (HANDLE, PWINUSB_INTERFACE_HANDLE)); DLL_DECLARE(WINAPI, BOOL, WinUsb_Free, (WINUSB_INTERFACE_HANDLE)); -- cgit v1.2.1