summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPete Batard <pbatard@gmail.com>2010-01-14 01:16:33 +0000
committerPete Batard <pbatard@gmail.com>2010-01-14 01:16:33 +0000
commitbdcbace8814250a1995b329728931c0f8e5eb90c (patch)
tree0b759a298514fd08d902c55053f58940a80145dd
parenteacdaf7c213adbe0ceee37d3b62019d607241a52 (diff)
downloadlibusb-bdcbace8814250a1995b329728931c0f8e5eb90c.tar.gz
svn r25:
- WinUSB composite device support (with DeviceClasses registry lookup) - better interface handling - other minor improvements
-rw-r--r--examples/xusb.c13
-rw-r--r--libusb/os/windows_usb.c983
-rw-r--r--libusb/os/windows_usb.h47
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<root_size; j++)
+ ret_path[j] = root_prefix[j];
// Same goes for '\' and '#' after the root prefix. Ensure '#' is used
- for(j=sizeof(ROOT_PREFIX)-1; j<size; j++) {
+ for(j=root_size; j<size; j++) {
ret_path[j] = toupper(ret_path[j]); // Fix case too
if (ret_path[j] == '\\')
ret_path[j] = '#';
@@ -198,6 +195,154 @@ static char* sanitize_path(const char* path)
return ret_path;
}
+/*
+ * reads an REG_SZ key (name or value)
+ * if val_name is NULL, the function returns the key name indexed by key_index into ret_content
+ * if val_name is not NULL, the functions returns the RG_SZ value of attribute val_name into ret_content
+ * returns total number of keys/values found for this path, or -1 on error
+ */
+static int read_registry_key(struct libusb_context *ctx, char* key_path, char* val_name, int key_index, char* ret_content, int ret_size)
+{
+ HKEY parent_key;
+ LONG i, r;
+ DWORD size, val_name_size, type;
+ DWORD nb_keys = 0;
+ DWORD nb_values = 0;
+ char internal_key_name[MAX_KEY_LENGTH];
+ char internal_val_name[MAX_KEY_LENGTH];
+ char *_key_name;
+ bool only_read_keys = (val_name == NULL);
+
+ r = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key_path, 0, KEY_READ, &parent_key);
+ if (r != ERROR_SUCCESS) {
+ usbi_err(ctx, "could not access registry path %s: %s", key_path, windows_error_str(r));
+ return -1;
+ }
+
+ // Get the number of keys and values
+ r = RegQueryInfoKey(parent_key, NULL, NULL, NULL, &nb_keys, NULL, NULL,
+ &nb_values, NULL, NULL, NULL, NULL);
+
+ if (r != ERROR_SUCCESS) {
+ usbi_err(ctx, "could not read keys for registry path %s: %s",
+ key_path, windows_error_str(r));
+ RegCloseKey(parent_key);
+ return -1;
+ }
+
+ // If what we're interested in is empty, just say so
+ if ( ((only_read_keys) && (nb_keys == 0)) ||
+ ((!only_read_keys) && (nb_values == 0)) ) {
+ RegCloseKey(parent_key);
+ return 0;
+ }
+
+ // A negative key_index means just return the number of keys
+ if (key_index < 0) {
+ RegCloseKey(parent_key);
+ return nb_keys;
+ }
+
+ if ((ret_size > 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; i<nb_values; i++)
+ {
+ size = ret_size;
+ val_name_size = MAX_KEY_LENGTH;
+ internal_val_name[0] = '\0';
+ r = RegEnumValue(parent_key, i, internal_val_name, &val_name_size,
+ NULL, &type, ret_content, &size);
+
+ switch(r) {
+ case ERROR_SUCCESS:
+ if ((type == REG_SZ) && (safe_strcmp(internal_val_name, val_name) == 0)) {
+ // There's only ever one value with the name of val_name, so return
+ RegCloseKey(parent_key);
+ return nb_values;
+ }
+ break;
+ case ERROR_MORE_DATA:
+ usbi_err(ctx, "value of %s exceeds buffer by %d bytes", val_name, size-ret_size+1);
+ RegCloseKey(parent_key);
+ return -1;
+ default:
+ usbi_err(ctx, "could not read value of %s: %s", val_name, windows_error_str(r));
+ RegCloseKey(parent_key);
+ return -1;
+ }
+ }
+
+ // If we get here, value was not matched
+ RegCloseKey(parent_key);
+ return 0;
+}
+
+// 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);
+}
+
/*
* 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<nb_device_classes; i++) {
+ r = read_registry_key(ctx, device_classes_path, NULL, i, guid_name, 40);
+
+ if (r <= 0)
+ {
+ usbi_err(ctx, "could not read device class #%d", i);
+ continue;
+ }
+
+ safe_strcpy(device_guid_path, MAX_PATH_LENGTH, device_classes_path);
+ safe_strcat(device_guid_path, MAX_PATH_LENGTH, guid_name);
+ int nb_device_paths = read_registry_key(ctx, device_guid_path, NULL, -1, NULL, 0);
+ if (nb_device_paths <= 0) {
+ continue;
+ }
+
+ // browse device instance paths.
+ // eg: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\DeviceClasses\{b35924d6-3e16-4a9e-9782-5524a4b79bac}\*
+ // eg of returned value: "##?#USB#VID_045E&PID_0289#7&7ef95eb&0&1#{b35924d6-3e16-4a9e-9782-5524a4b79bac}"
+ for (j=0; j<nb_device_paths; j++) {
+ if (read_registry_key(ctx, device_guid_path, NULL, j, instance_path, MAX_PATH) <= 0) {
+ continue;
+ }
+ if (strlen(device_guid_path) + strlen(instance_path) + 2 > 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_number<USB_MAXINTERFACES; interface_number++)
+ {
+ if (interface_number == 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("interface_nr = %d, error = %X", interface_number, r);
+ break;
+ }
+
+ r = CM_Get_Device_ID(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",
+ interface_number, r);
+ }
+
+ priv->interface_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,
+ &reg_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,
- &reg_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; j<discdevs->len; 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; j<discdevs->len; 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,
- &reg_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,
+ &reg_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; i<interface_desc.bNumEndpoints; i++)
- {
- if (!WinUsb_QueryPipe(winusb_handle, 0, i, &pipe_info)) {
- usbi_err(ctx, "could not query pipe: %s", windows_error_str());
- return LIBUSB_ERROR_IO;
- }
+ // TODO: better check for detached devices
- if (pipe_info.PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_IN(pipe_info.PipeId)) {
- usbi_dbg("pipe %d: bulk In pipe", i);
-// bulk_in_pipe = pipe_info.PipeId;
- }
- else if(pipe_info.PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_OUT(pipe_info.PipeId)) {
- usbi_dbg("pipe %d: bulk Out pipe", i);
-// bulk_out_pipe = pipe_info.PipeId;
- }
- else if(pipe_info.PipeType == UsbdPipeTypeInterrupt) {
- usbi_dbg("pipe %d: interrupt pipe", i);
-// interrupt_pipe = pipe_info.PipeId;
- }
- else {
- usbi_dbg("%d: ceci n'est pas un(e) pipe", i);
- break;
+ // Each interface requires a sperate handle for WinUSB
+ for (i = 0; i < USB_MAXINTERFACES; i++) {
+ if (priv->interface_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; (i<USB_MAXINTERFACES)&&(!found); i++) {
+ winusb_handle = handle_priv->interface_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)?"<NULL>":str1), ((str2==NULL)?"<NULL>":str2))
#define safe_strncmp(str1, str2, count) strncmp(((str1==NULL)?"<NULL>":str1), ((str2==NULL)?"<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; i<strlen(str); i++)
+ str[i] = toupper(str[i]);
+}
// #define MAX_ISO_BUFFER_LENGTH 32768
// #define MAX_BULK_BUFFER_LENGTH 16384
#define MAX_CTRL_BUFFER_LENGTH 4096
-#define ROOT_PREFIX "\\\\.\\"
#define MAX_PATH_LENGTH 128
-#define MAX_KEY_LENGTH 64
+#define MAX_KEY_LENGTH 256
#define ERR_BUFFER_SIZE 256
#define wchar_to_utf8_ms(wstr, str, strlen) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, strlen, NULL, NULL)
#define ERRNO GetLastError()
+// API (driver access) types
+enum api_type {
+ API_NONE,
+ API_WINUSB,
+};
+
+#define API_CALL(api, fname, ...) \
+ switch(api) { \
+ case API_WINUSB: \
+ r = winusb_##fname(__VA_ARGS__); \
+ break; \
+ default: \
+ r = LIBUSB_ERROR_NOT_SUPPORTED; \
+ break; \
+ }
/*
* private structures definition
@@ -90,20 +112,26 @@ struct windows_device_priv {
struct libusb_device *parent_dev; // access to parent is required for usermode ops
ULONG connection_index; // also required for some usermode ops
char *path; // path used by Windows to reference the USB node
+ char *interface_path[USB_MAXINTERFACES]; // each interface has a path as well
char *driver; // driver name (eg WinUSB, USBSTOR, HidUsb, etc)
+ enum api_type api;
uint8_t active_config;
USB_DEVICE_DESCRIPTOR dev_descriptor;
unsigned char **config_descriptor; // list of pointers to the cached config descriptors
};
static inline void windows_device_priv_init(struct windows_device_priv* p) {
+ int i;
p->parent_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; i<USB_MAXINTERFACES; i++)
+ p->interface_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; i<USB_MAXINTERFACES; i++)
+ safe_free(p->interface_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));