Windows: Restore HID support
diff --git a/examples/xusb.c b/examples/xusb.c
index 08de6ba..3f00f34 100644
--- a/examples/xusb.c
+++ b/examples/xusb.c
@@ -80,9 +80,14 @@
 
 // HID Class-Specific Requests values. See section 7.2 of the HID specifications
 #define HID_GET_REPORT                0x01
+#define HID_GET_IDLE                  0x02
+#define HID_GET_PROTOCOL              0x03
 #define HID_SET_REPORT                0x09
+#define HID_SET_IDLE                  0x0A
+#define HID_SET_PROTOCOL              0x0B
 #define HID_REPORT_TYPE_INPUT         0x01
 #define HID_REPORT_TYPE_OUTPUT        0x02
+#define HID_REPORT_TYPE_FEATURE       0x03
 
 // Mass Storage Requests values. See section 3 of the Bulk-Only Mass Storage Class specifications
 #define BOMS_RESET                    0xFF
@@ -132,6 +137,7 @@
 	USE_PS3,
 	USE_XBOX,
 	USE_SCSI,
+	USE_HID,
 } test_mode;
 uint16_t VID, PID;
 
@@ -525,6 +531,157 @@
 	return 0;
 }
 
+// HID
+int get_hid_record_size(uint8_t *hid_report_descriptor, int size, int type)
+{
+	uint8_t i, j = 0;
+	uint8_t offset;
+	int record_size[3] = {0, 0, 0};
+	int nb_bits = 0, nb_items = 0;
+	bool found_record_marker;
+
+	found_record_marker = false;
+	for (i = hid_report_descriptor[0]+1; i < size; i += offset) {
+		offset = (hid_report_descriptor[i]&0x03) + 1;
+		if (offset == 4)
+			offset = 5;
+		switch (hid_report_descriptor[i] & 0xFC) {
+		case 0x74:	// bitsize
+			nb_bits = hid_report_descriptor[i+1];
+			break;
+		case 0x94:	// count
+			nb_items = 0;
+			for (j=1; j<offset; j++) {
+				nb_items = ((uint32_t)hid_report_descriptor[i+j]) << (8*(j-1));
+			}
+			break;
+		case 0x80:	// input
+			found_record_marker = true;
+			j = 0;
+			break;
+		case 0x90:	// output
+			found_record_marker = true;
+			j = 1;
+			break;
+		case 0xb0:	// feature
+			found_record_marker = true;
+			j = 2;
+			break;
+		case 0xC0:	// end of collection
+			nb_items = 0;
+			nb_bits = 0;
+			break;
+		default:
+			continue;
+		}
+		if (found_record_marker) {
+			found_record_marker = false;
+			record_size[j] += nb_items*nb_bits;
+		}
+	}
+	if ((type < HID_REPORT_TYPE_INPUT) || (type > HID_REPORT_TYPE_FEATURE)) {
+		return 0;
+	} else {
+		return (record_size[type - HID_REPORT_TYPE_INPUT]+7)/8;
+	}
+}
+
+int test_hid(libusb_device_handle *handle, uint8_t endpoint_in)
+{
+	int r, size, descriptor_size;
+	uint8_t hid_report_descriptor[256];
+	uint8_t *report_buffer;
+	FILE *fd;
+
+	printf("\nReading HID Report Descriptors:\n");
+	descriptor_size = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_STANDARD|LIBUSB_RECIPIENT_INTERFACE,
+		LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_REPORT<<8, 0, hid_report_descriptor, sizeof(hid_report_descriptor), 1000);
+	if (descriptor_size < 0) {
+		printf("   Failed\n");
+		return -1;
+	}
+	display_buffer_hex(hid_report_descriptor, descriptor_size);
+	if ((binary_dump) && ((fd = fopen(binary_name, "w")) != NULL)) {
+		if (fwrite(hid_report_descriptor, 1, descriptor_size, fd) != descriptor_size) {
+			printf("   Error writing descriptor to file\n");
+		}
+		fclose(fd);
+	}
+
+	size = get_hid_record_size(hid_report_descriptor, descriptor_size, HID_REPORT_TYPE_FEATURE);
+	if (size <= 0) {
+		printf("\nSkipping Feature Report readout (None detected)\n");
+	} else {
+		report_buffer = (uint8_t*) calloc(size, 1);
+		if (report_buffer == NULL) {
+			return -1;
+		}
+
+		printf("\nReading Feature Report (length %d)...\n", size);
+		r = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE,
+			HID_GET_REPORT, (HID_REPORT_TYPE_FEATURE<<8)|0, 0, report_buffer, (uint16_t)size, 5000);
+		if (r >= 0) {
+			display_buffer_hex(report_buffer, size);
+		} else {
+			switch(r) {
+			case LIBUSB_ERROR_NOT_FOUND:
+				printf("   No Feature Report available for this device\n");
+				break;
+			case LIBUSB_ERROR_PIPE:
+				printf("   Detected stall - resetting pipe...\n");
+				libusb_clear_halt(handle, 0);
+				break;
+			default:
+				printf("   Error: %s\n", libusb_error_name(r));
+				break;
+			}
+		}
+		free(report_buffer);
+	}
+
+	size = get_hid_record_size(hid_report_descriptor, descriptor_size, HID_REPORT_TYPE_INPUT);
+	if (size <= 0) {
+		printf("\nSkipping Input Report readout (None detected)\n");
+	} else {
+		report_buffer = (uint8_t*) calloc(size, 1);
+		if (report_buffer == NULL) {
+			return -1;
+		}
+
+		printf("\nReading Input Report (length %d)...\n", size);
+		r = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE,
+			HID_GET_REPORT, (HID_REPORT_TYPE_INPUT<<8)|0x00, 0, report_buffer, (uint16_t)size, 5000);
+		if (r >= 0) {
+			display_buffer_hex(report_buffer, size);
+		} else {
+			switch(r) {
+			case LIBUSB_ERROR_TIMEOUT:
+				printf("   Timeout! Please make sure you act on the device within the 5 seconds allocated...\n");
+				break;
+			case LIBUSB_ERROR_PIPE:
+				printf("   Detected stall - resetting pipe...\n");
+				libusb_clear_halt(handle, 0);
+				break;
+			default:
+				printf("   Error: %s\n", libusb_error_name(r));
+				break;
+			}
+		}
+
+		// Attempt a bulk read from endpoint 0 (this should just return a raw input report)
+		printf("\nTesting interrupt read using endpoint %02X...\n", endpoint_in);
+		r = libusb_interrupt_transfer(handle, endpoint_in, report_buffer, size, &size, 5000);
+		if (r >= 0) {
+			display_buffer_hex(report_buffer, size);
+		} else {
+			printf("   %s\n", libusb_error_name(r));
+		}
+
+		free(report_buffer);
+	}
+	return 0;
+}
+
 // Read the MS WinUSB Feature Descriptors, that are used on Windows 8 for automated driver installation
 static void read_ms_winsub_feature_descriptors(libusb_device_handle *handle, uint8_t bRequest, int iface_number)
 {
@@ -654,8 +811,8 @@
 			for (k=0; k<conf_desc->usb_interface[i].altsetting[j].bNumEndpoints; k++) {
 				endpoint = &conf_desc->usb_interface[i].altsetting[j].endpoint[k];
 				printf("       endpoint[%d].address: %02X\n", k, endpoint->bEndpointAddress);
-				// Use the first bulk IN/OUT endpoints found as default for testing
-				if ((endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) {
+				// Use the first interrupt or bulk IN/OUT endpoints as default for testing
+				if ((endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) & (LIBUSB_TRANSFER_TYPE_BULK | LIBUSB_TRANSFER_TYPE_INTERRUPT)) {
 					if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
 						if (!endpoint_in)
 							endpoint_in = endpoint->bEndpointAddress;
@@ -718,6 +875,9 @@
 		msleep(2000);
 		CALL_CHECK(set_xbox_actuators(handle, 0, 0));
 		break;
+	case USE_HID:
+		test_hid(handle, endpoint_in);
+		break;
 	case USE_SCSI:
 		CALL_CHECK(test_mass_storage(handle, endpoint_in, endpoint_out));
 	case USE_GENERIC:
@@ -803,6 +963,12 @@
 					PID = 0x0268;
 					test_mode = USE_PS3;
 					break;
+				case 's':
+					// Microsoft Sidewinder Precision Pro Joystick - 1 HID interface
+					VID = 0x045E;
+					PID = 0x0008;
+					test_mode = USE_HID;
+					break;
 				case 'x':
 					// Microsoft XBox Controller Type S - 1 interface
 					VID = 0x045E;
@@ -841,6 +1007,7 @@
 		printf("   -k: test generic Mass Storage USB device (using WinUSB)\n");
 		printf("   -j: test FTDI based JTAG device (using WinUSB)\n");
 		printf("   -p: test Sony PS3 SixAxis controller (using WinUSB)\n");
+		printf("   -s: test Microsoft Sidewinder Precision Pro (using HID)\n");
 		printf("   -x: test Microsoft XBox Controller Type S (using WinUSB)\n");
 		return 0;
 	}
diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c
index c4212c0..1d9a80b 100644
--- a/libusb/os/windows_usb.c
+++ b/libusb/os/windows_usb.c
@@ -3,6 +3,7 @@
  * Copyright © 2009-2012 Pete Batard <pete@akeo.ie>
  * With contributions from Michael Plante, Orin Eman et al.
  * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software
  * Hash table functions adapted from glibc, by Ulrich Drepper et al.
  * Major code testing contribution by Xiaofan Chen
  *
@@ -68,6 +69,20 @@
 static int winusb_abort_control(struct usbi_transfer *itransfer);
 static int winusb_reset_device(struct libusb_device_handle *dev_handle);
 static int winusb_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size);
+// HID API prototypes
+static int hid_init(struct libusb_context *ctx);
+static int hid_exit(void);
+static int hid_open(struct libusb_device_handle *dev_handle);
+static void hid_close(struct libusb_device_handle *dev_handle);
+static int hid_claim_interface(struct libusb_device_handle *dev_handle, int iface);
+static int hid_release_interface(struct libusb_device_handle *dev_handle, int iface);
+static int hid_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting);
+static int hid_submit_control_transfer(struct usbi_transfer *itransfer);
+static int hid_submit_bulk_transfer(struct usbi_transfer *itransfer);
+static int hid_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint);
+static int hid_abort_transfers(struct usbi_transfer *itransfer);
+static int hid_reset_device(struct libusb_device_handle *dev_handle);
+static int hid_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size);
 // Composite API prototypes
 static int composite_init(struct libusb_context *ctx);
 static int composite_exit(void);
@@ -104,6 +119,8 @@
 // API globals
 bool api_winusb_available = false;
 #define CHECK_WINUSB_AVAILABLE do { if (!api_winusb_available) return LIBUSB_ERROR_ACCESS; } while (0)
+bool api_hid_available = false;
+#define CHECK_HID_AVAILABLE do { if (!api_hid_available) return LIBUSB_ERROR_ACCESS; } while (0)
 
 static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) {
 	if ((guid1 != NULL) && (guid2 != NULL)) {
@@ -628,6 +645,14 @@
 	int current_interface = *interface_number;
 	int r = LIBUSB_SUCCESS;
 
+	switch(api_type) {
+	case USB_API_WINUSB:
+	case USB_API_HID:
+		break;
+	default:
+		return LIBUSB_ERROR_INVALID_PARAM;
+	}
+
 	usbi_mutex_lock(&autoclaim_lock);
 	if (current_interface < 0)	// No serviceable interface was found
 	{
@@ -1152,19 +1177,44 @@
 			device_id, interface_number);
 	}
 
+	// HID devices can have multiple collections (COL##) for each MI_## interface
 	if (priv->usb_interface[interface_number].path != NULL) {
-		usbi_warn(ctx, "interface[%d] already set - ignoring: %s", interface_number, device_id);
+		if (api != USB_API_HID) {
+			usbi_warn(ctx, "program assertion failed %s is not an USB HID collection", device_id);
+			return LIBUSB_ERROR_OTHER;
+		}
+		usbi_dbg("interface[%d] already set - ignoring HID collection: %s",
+			interface_number, device_id);
 		return LIBUSB_ERROR_ACCESS;
 	}
 
 	usbi_dbg("interface[%d] = %s", interface_number, dev_interface_path);
 	priv->usb_interface[interface_number].path = dev_interface_path;
 	priv->usb_interface[interface_number].apib = &usb_api_backend[api];
+	if ((api == USB_API_HID) && (priv->hid == NULL)) {
+		priv->hid = calloc(1, sizeof(struct hid_device_priv));
+	}
 	priv->composite_api_flags |= 1<<api;
 
 	return LIBUSB_SUCCESS;
 }
 
+static int set_hid_interface(struct libusb_context* ctx, struct libusb_device* dev,
+							char* dev_interface_path)
+{
+	struct windows_device_priv *priv = _device_priv(dev);
+
+	if (priv->hid == NULL) {
+		usbi_err(ctx, "program assertion failed: parent is not HID");
+		return LIBUSB_ERROR_NO_DEVICE;
+	}
+	priv->usb_interface[priv->hid->nb_interfaces].path = dev_interface_path;
+	priv->usb_interface[priv->hid->nb_interfaces].apib = &usb_api_backend[USB_API_HID];
+	usbi_dbg("interface[%d] = %s", priv->hid->nb_interfaces, dev_interface_path);
+	priv->hid->nb_interfaces++;
+	return LIBUSB_SUCCESS;
+}
+
 /*
  * get_device_list: libusbx backend device enumeration function
  */
@@ -1175,12 +1225,14 @@
 	char* usb_class[2] = {"USB", "NUSB3"};
 	SP_DEVINFO_DATA dev_info_data;
 	SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL;
+	GUID hid_guid;
 #define MAX_ENUM_GUIDS 64
 	const GUID* guid[MAX_ENUM_GUIDS];
 #define HCD_PASS 0
 #define HUB_PASS 1
 #define GEN_PASS 2
 #define DEV_PASS 3
+#define HID_PASS 4
 	int r = LIBUSB_SUCCESS;
 	int class_index = 0;
 	unsigned int nb_guids, pass, i, j, ancestor;
@@ -1208,14 +1260,17 @@
 	// PASS 3 : (re)enumerate generic USB devices (including driverless)
 	//           and list additional USB device interface GUIDs to explore
 	// PASS 4 : (re)enumerate master USB devices that have a device interface
-	// PASS 5+: (re)enumerate device interfaced GUIDs and set the device interfaces.
+	// PASS 5+: (re)enumerate device interfaced GUIDs (including HID) and
+	//           set the device interfaces.
 
 	// Init the GUID table
 	guid[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER;
 	guid[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB;
 	guid[GEN_PASS] = NULL;
 	guid[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE;
-	nb_guids = DEV_PASS+1;
+	HidD_GetHidGuid(&hid_guid);
+	guid[HID_PASS] = &hid_guid;
+	nb_guids = HID_PASS+1;
 
 	unref_list = (libusb_device**) calloc(unref_size, sizeof(libusb_device*));
 	if (unref_list == NULL) {
@@ -1238,6 +1293,9 @@
 		case GEN_PASS:
 			usbi_dbg("PROCESSING GENs");
 			break;
+		case HID_PASS:
+			usbi_dbg("PROCESSING HIDs %s", guid_to_string(guid[pass]));
+			break;
 		default:
 			usbi_dbg("PROCESSING EXTs %s", guid_to_string(guid[pass]));
 			break;
@@ -1345,6 +1403,9 @@
 					}
 				}
 				break;
+			case HID_PASS:
+				api = USB_API_HID;
+				break;
 			default:
 				// Get the API type (after checking that the driver installation is OK)
 				if ( (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_INSTALL_STATE,
@@ -1378,7 +1439,7 @@
 					parent_dev = usbi_get_device_by_session_id(ctx, session_id);
 				}
 				if (parent_dev == NULL) {
-					usbi_dbg("unlisted ancestor for '%s' (newly connected, etc.) - ignoring", dev_id_path);
+					usbi_dbg("unlisted ancestor for '%s' (non USB HID, newly connected, etc.) - ignoring", dev_id_path);
 					continue;
 				}
 				parent_priv = _device_priv(parent_dev);
@@ -1445,6 +1506,13 @@
 				case USB_API_COMPOSITE:
 				case USB_API_HUB:
 					break;
+				case USB_API_HID:
+					priv->hid = calloc(1, sizeof(struct hid_device_priv));
+					if (priv->hid == NULL) {
+						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+					}
+					priv->hid->nb_interfaces = 0;
+					break;
 				default:
 					// For other devices, the first interface is the same as the device
 					priv->usb_interface[0].path = (char*) calloc(safe_strlen(priv->path)+1, 1);
@@ -1476,8 +1544,13 @@
 					r = LIBUSB_SUCCESS;
 				}
 				break;
-			default:	// later passes
-				if (parent_priv->apib->id == USB_API_COMPOSITE) {
+			default:	// HID_PASS and later
+				if (parent_priv->apib->id == USB_API_HID) {
+					usbi_dbg("setting HID interface for [%lX]:", parent_dev->session_data);
+					r = set_hid_interface(ctx, parent_dev, dev_interface_path);
+					if (r != LIBUSB_SUCCESS) LOOP_BREAK(r);
+					dev_interface_path = NULL;
+				} else if (parent_priv->apib->id == USB_API_COMPOSITE) {
 					usbi_dbg("setting composite interface for [%lX]:", parent_dev->session_data);
 					switch (set_composite_interface(ctx, parent_dev, dev_interface_path, dev_id_path, api)) {
 					case LIBUSB_SUCCESS:
@@ -1497,7 +1570,7 @@
 	}
 
 	// Free any additional GUIDs
-	for (pass = DEV_PASS+1; pass < nb_guids; pass++) {
+	for (pass = HID_PASS+1; pass < nb_guids; pass++) {
 		safe_free(guid[pass]);
 	}
 
@@ -1753,6 +1826,7 @@
 	struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
 
 	usbi_free_fd(transfer_priv->pollable_fd.fd);
+	safe_free(transfer_priv->hid_buffer);
 	// When auto claim is in use, attempt to release the auto-claimed interface
 	auto_release(itransfer);
 }
@@ -2196,6 +2270,7 @@
 const char* hub_driver_names[] = {"USBHUB", "USBHUB3", "NUSB3HUB", "FLXHCIH", "TIHUB3", "ETRONHUB3", "VIAHUB3", "ASMTHUB3"};
 const char* composite_driver_names[] = {"USBCCGP"};
 const char* winusb_driver_names[] = {"WINUSB"};
+const char* hid_driver_names[] = {"HIDUSB", "MOUHID", "KBDHID"};
 const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = {
 	{
 		USB_API_UNSUPPORTED,
@@ -2281,6 +2356,27 @@
 		winusb_abort_control,
 		winusb_abort_transfers,
 		winusb_copy_transfer_data,
+	}, {
+		USB_API_HID,
+		"HID API",
+		&CLASS_GUID_HID,
+		hid_driver_names,
+		sizeof(hid_driver_names)/sizeof(hid_driver_names[0]),
+		hid_init,
+		hid_exit,
+		hid_open,
+		hid_close,
+		hid_claim_interface,
+		hid_set_interface_altsetting,
+		hid_release_interface,
+		hid_clear_halt,
+		hid_reset_device,
+		hid_submit_bulk_transfer,
+		unsupported_submit_iso_transfer,
+		hid_submit_control_transfer,
+		hid_abort_transfers,
+		hid_abort_transfers,
+		hid_copy_transfer_data,
 	},
 };
 
@@ -2509,16 +2605,23 @@
 /*
  * Return the first valid interface (of the same API type), for control transfers
  */
-static int winusb_get_valid_interface(struct libusb_device_handle *dev_handle)
+static int get_valid_interface(struct libusb_device_handle *dev_handle, int api_id)
 {
 	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
 	int i;
 
+	if ((api_id < USB_API_WINUSB) || (api_id > USB_API_HID)) {
+		usbi_dbg("unsupported API ID");
+		return -1;
+	}
+
 	for (i=0; i<USB_MAXINTERFACES; i++) {
 		if ( (handle_priv->interface_handle[i].dev_handle != 0)
 		  && (handle_priv->interface_handle[i].dev_handle != INVALID_HANDLE_VALUE)
 		  && (handle_priv->interface_handle[i].api_handle != 0)
-		  && (handle_priv->interface_handle[i].api_handle != INVALID_HANDLE_VALUE) ) {
+		  && (handle_priv->interface_handle[i].api_handle != INVALID_HANDLE_VALUE)
+		  && (priv->usb_interface[i].apib->id == api_id) ) {
 			return i;
 		}
 	}
@@ -2570,7 +2673,7 @@
 	if (size > MAX_CTRL_BUFFER_LENGTH)
 		return LIBUSB_ERROR_INVALID_PARAM;
 
-	current_interface = winusb_get_valid_interface(transfer->dev_handle);
+	current_interface = get_valid_interface(transfer->dev_handle, USB_API_WINUSB);
 	if (current_interface < 0) {
 		if (auto_claim(transfer, &current_interface, USB_API_WINUSB) != LIBUSB_SUCCESS) {
 			return LIBUSB_ERROR_NOT_FOUND;
@@ -2826,6 +2929,1043 @@
 	return LIBUSB_TRANSFER_COMPLETED;
 }
 
+/*
+ * Internal HID Support functions (from libusb-win32)
+ * Note that functions that complete data transfer synchronously must return
+ * LIBUSB_COMPLETED instead of LIBUSB_SUCCESS
+ */
+static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size);
+static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size);
+
+static int _hid_wcslen(WCHAR *str)
+{
+	int i = 0;
+	while (str[i] && (str[i] != 0x409)) {
+		i++;
+	}
+	return i;
+}
+
+static int _hid_get_device_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
+{
+	struct libusb_device_descriptor d;
+
+	d.bLength = LIBUSB_DT_DEVICE_SIZE;
+	d.bDescriptorType = LIBUSB_DT_DEVICE;
+	d.bcdUSB = 0x0200; /* 2.00 */
+	d.bDeviceClass = 0;
+	d.bDeviceSubClass = 0;
+	d.bDeviceProtocol = 0;
+	d.bMaxPacketSize0 = 64; /* fix this! */
+	d.idVendor = (uint16_t)dev->vid;
+	d.idProduct = (uint16_t)dev->pid;
+	d.bcdDevice = 0x0100;
+	d.iManufacturer = dev->string_index[0];
+	d.iProduct = dev->string_index[1];
+	d.iSerialNumber = dev->string_index[2];
+	d.bNumConfigurations = 1;
+
+	if (*size > LIBUSB_DT_DEVICE_SIZE)
+		*size = LIBUSB_DT_DEVICE_SIZE;
+	memcpy(data, &d, *size);
+	return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_config_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
+{
+	char num_endpoints = 0;
+	size_t config_total_len = 0;
+	char tmp[HID_MAX_CONFIG_DESC_SIZE];
+	struct libusb_config_descriptor *cd;
+	struct libusb_interface_descriptor *id;
+	struct libusb_hid_descriptor *hd;
+	struct libusb_endpoint_descriptor *ed;
+	size_t tmp_size;
+
+	if (dev->input_report_size)
+		num_endpoints++;
+	if (dev->output_report_size)
+		num_endpoints++;
+
+	config_total_len = LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE
+		+ LIBUSB_DT_HID_SIZE + num_endpoints * LIBUSB_DT_ENDPOINT_SIZE;
+
+
+	cd = (struct libusb_config_descriptor *)tmp;
+	id = (struct libusb_interface_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE);
+	hd = (struct libusb_hid_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE
+		+ LIBUSB_DT_INTERFACE_SIZE);
+	ed = (struct libusb_endpoint_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE
+		+ LIBUSB_DT_INTERFACE_SIZE
+		+ LIBUSB_DT_HID_SIZE);
+
+	cd->bLength = LIBUSB_DT_CONFIG_SIZE;
+	cd->bDescriptorType = LIBUSB_DT_CONFIG;
+	cd->wTotalLength = (uint16_t) config_total_len;
+	cd->bNumInterfaces = 1;
+	cd->bConfigurationValue = 1;
+	cd->iConfiguration = 0;
+	cd->bmAttributes = 1 << 7; /* bus powered */
+	cd->MaxPower = 50;
+
+	id->bLength = LIBUSB_DT_INTERFACE_SIZE;
+	id->bDescriptorType = LIBUSB_DT_INTERFACE;
+	id->bInterfaceNumber = 0;
+	id->bAlternateSetting = 0;
+	id->bNumEndpoints = num_endpoints;
+	id->bInterfaceClass = 3;
+	id->bInterfaceSubClass = 0;
+	id->bInterfaceProtocol = 0;
+	id->iInterface = 0;
+
+	tmp_size = LIBUSB_DT_HID_SIZE;
+	_hid_get_hid_descriptor(dev, hd, &tmp_size);
+
+	if (dev->input_report_size) {
+		ed->bLength = LIBUSB_DT_ENDPOINT_SIZE;
+		ed->bDescriptorType = LIBUSB_DT_ENDPOINT;
+		ed->bEndpointAddress = HID_IN_EP;
+		ed->bmAttributes = 3;
+		ed->wMaxPacketSize = dev->input_report_size - 1;
+		ed->bInterval = 10;
+
+		ed++;
+	}
+
+	if (dev->output_report_size) {
+		ed->bLength = LIBUSB_DT_ENDPOINT_SIZE;
+		ed->bDescriptorType = LIBUSB_DT_ENDPOINT;
+		ed->bEndpointAddress = HID_OUT_EP;
+		ed->bmAttributes = 3;
+		ed->wMaxPacketSize = dev->output_report_size - 1;
+		ed->bInterval = 10;
+	}
+
+	if (*size > config_total_len)
+		*size = config_total_len;
+	memcpy(data, tmp, *size);
+	return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_string_descriptor(struct hid_device_priv* dev, int _index,
+									  void *data, size_t *size)
+{
+	void *tmp = NULL;
+	size_t tmp_size = 0;
+	int i;
+
+	/* language ID, EN-US */
+	char string_langid[] = {
+		0x09,
+		0x04
+	};
+
+	if ((*size < 2) || (*size > 255)) {
+		return LIBUSB_ERROR_OVERFLOW;
+	}
+
+	if (_index == 0) {
+		tmp = string_langid;
+		tmp_size = sizeof(string_langid)+2;
+	} else {
+		for (i=0; i<3; i++) {
+			if (_index == (dev->string_index[i])) {
+				tmp = dev->string[i];
+				tmp_size = (_hid_wcslen(dev->string[i])+1) * sizeof(WCHAR);
+				break;
+			}
+		}
+		if (i == 3) {	// not found
+			return LIBUSB_ERROR_INVALID_PARAM;
+		}
+	}
+
+	if(!tmp_size) {
+		return LIBUSB_ERROR_INVALID_PARAM;
+	}
+
+	if (tmp_size < *size) {
+		*size = tmp_size;
+	}
+	// 2 byte header
+	((uint8_t*)data)[0] = (uint8_t)*size;
+	((uint8_t*)data)[1] = LIBUSB_DT_STRING;
+	memcpy((uint8_t*)data+2, tmp, *size-2);
+	return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
+{
+	struct libusb_hid_descriptor d;
+	uint8_t tmp[MAX_HID_DESCRIPTOR_SIZE];
+	size_t report_len = MAX_HID_DESCRIPTOR_SIZE;
+
+	_hid_get_report_descriptor(dev, tmp, &report_len);
+
+	d.bLength = LIBUSB_DT_HID_SIZE;
+	d.bDescriptorType = LIBUSB_DT_HID;
+	d.bcdHID = 0x0110; /* 1.10 */
+	d.bCountryCode = 0;
+	d.bNumDescriptors = 1;
+	d.bClassDescriptorType = LIBUSB_DT_REPORT;
+	d.wClassDescriptorLength = (uint16_t)report_len;
+
+	if (*size > LIBUSB_DT_HID_SIZE)
+		*size = LIBUSB_DT_HID_SIZE;
+	memcpy(data, &d, *size);
+	return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
+{
+	uint8_t d[MAX_HID_DESCRIPTOR_SIZE];
+	size_t i = 0;
+
+	/* usage page (0xFFA0 == vendor defined) */
+	d[i++] = 0x06; d[i++] = 0xA0; d[i++] = 0xFF;
+	/* usage (vendor defined) */
+	d[i++] = 0x09; d[i++] = 0x01;
+	/* start collection (application) */
+	d[i++] = 0xA1; d[i++] = 0x01;
+	/* input report */
+	if (dev->input_report_size) {
+		/* usage (vendor defined) */
+		d[i++] = 0x09; d[i++] = 0x01;
+		/* logical minimum (0) */
+		d[i++] = 0x15; d[i++] = 0x00;
+		/* logical maximum (255) */
+		d[i++] = 0x25; d[i++] = 0xFF;
+		/* report size (8 bits) */
+		d[i++] = 0x75; d[i++] = 0x08;
+		/* report count */
+		d[i++] = 0x95; d[i++] = (uint8_t)dev->input_report_size - 1;
+		/* input (data, variable, absolute) */
+		d[i++] = 0x81; d[i++] = 0x00;
+	}
+	/* output report */
+	if (dev->output_report_size) {
+		/* usage (vendor defined) */
+		d[i++] = 0x09; d[i++] = 0x02;
+		/* logical minimum (0) */
+		d[i++] = 0x15; d[i++] = 0x00;
+		/* logical maximum (255) */
+		d[i++] = 0x25; d[i++] = 0xFF;
+		/* report size (8 bits) */
+		d[i++] = 0x75; d[i++] = 0x08;
+		/* report count */
+		d[i++] = 0x95; d[i++] = (uint8_t)dev->output_report_size - 1;
+		/* output (data, variable, absolute) */
+		d[i++] = 0x91; d[i++] = 0x00;
+	}
+	/* feature report */
+	if (dev->feature_report_size) {
+		/* usage (vendor defined) */
+		d[i++] = 0x09; d[i++] = 0x03;
+		/* logical minimum (0) */
+		d[i++] = 0x15; d[i++] = 0x00;
+		/* logical maximum (255) */
+		d[i++] = 0x25; d[i++] = 0xFF;
+		/* report size (8 bits) */
+		d[i++] = 0x75; d[i++] = 0x08;
+		/* report count */
+		d[i++] = 0x95; d[i++] = (uint8_t)dev->feature_report_size - 1;
+		/* feature (data, variable, absolute) */
+		d[i++] = 0xb2; d[i++] = 0x02; d[i++] = 0x01;
+	}
+
+	/* end collection */
+	d[i++] = 0xC0;
+
+	if (*size > i)
+		*size = i;
+	memcpy(data, d, *size);
+	return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_descriptor(struct hid_device_priv* dev, HANDLE hid_handle, int recipient,
+							   int type, int _index, void *data, size_t *size)
+{
+	switch(type) {
+	case LIBUSB_DT_DEVICE:
+		usbi_dbg("LIBUSB_DT_DEVICE");
+		return _hid_get_device_descriptor(dev, data, size);
+	case LIBUSB_DT_CONFIG:
+		usbi_dbg("LIBUSB_DT_CONFIG");
+		if (!_index)
+			return _hid_get_config_descriptor(dev, data, size);
+		return LIBUSB_ERROR_INVALID_PARAM;
+	case LIBUSB_DT_STRING:
+		usbi_dbg("LIBUSB_DT_STRING");
+		return _hid_get_string_descriptor(dev, _index, data, size);
+	case LIBUSB_DT_HID:
+		usbi_dbg("LIBUSB_DT_HID");
+		if (!_index)
+			return _hid_get_hid_descriptor(dev, data, size);
+		return LIBUSB_ERROR_INVALID_PARAM;
+	case LIBUSB_DT_REPORT:
+		usbi_dbg("LIBUSB_DT_REPORT");
+		if (!_index)
+			return _hid_get_report_descriptor(dev, data, size);
+		return LIBUSB_ERROR_INVALID_PARAM;
+	case LIBUSB_DT_PHYSICAL:
+		usbi_dbg("LIBUSB_DT_PHYSICAL");
+		if (HidD_GetPhysicalDescriptor(hid_handle, data, (ULONG)*size))
+			return LIBUSB_COMPLETED;
+		return LIBUSB_ERROR_OTHER;
+	}
+	usbi_dbg("unsupported");
+	return LIBUSB_ERROR_INVALID_PARAM;
+}
+
+static int _hid_get_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data,
+						   struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped,
+						   int report_type)
+{
+	uint8_t *buf;
+	DWORD ioctl_code, read_size, expected_size = (DWORD)*size;
+	int r = LIBUSB_SUCCESS;
+
+	if (tp->hid_buffer != NULL) {
+		usbi_dbg("program assertion failed: hid_buffer is not NULL");
+	}
+
+	if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) {
+		usbi_dbg("invalid size (%d)", *size);
+		return LIBUSB_ERROR_INVALID_PARAM;
+	}
+
+	switch (report_type) {
+		case HID_REPORT_TYPE_INPUT:
+			ioctl_code = IOCTL_HID_GET_INPUT_REPORT;
+			break;
+		case HID_REPORT_TYPE_FEATURE:
+			ioctl_code = IOCTL_HID_GET_FEATURE;
+			break;
+		default:
+			usbi_dbg("unknown HID report type %d", report_type);
+			return LIBUSB_ERROR_INVALID_PARAM;
+	}
+
+	// Add a trailing byte to detect overflows
+	buf = (uint8_t*)calloc(expected_size+1, 1);
+	if (buf == NULL) {
+		return LIBUSB_ERROR_NO_MEM;
+	}
+	buf[0] = (uint8_t)id;	// Must be set always
+	usbi_dbg("report ID: 0x%02X", buf[0]);
+
+	tp->hid_expected_size = expected_size;
+	read_size = expected_size;
+
+	// NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0)
+	if (!DeviceIoControl(hid_handle, ioctl_code, buf, expected_size+1,
+		buf, expected_size+1, &read_size, overlapped)) {
+		if (GetLastError() != ERROR_IO_PENDING) {
+			usbi_dbg("Failed to Read HID Report: %s", windows_error_str(0));
+			safe_free(buf);
+			return LIBUSB_ERROR_IO;
+		}
+		// Asynchronous wait
+		tp->hid_buffer = buf;
+		tp->hid_dest = (uint8_t*)data; // copy dest, as not necessarily the start of the transfer buffer
+		return LIBUSB_SUCCESS;
+	}
+
+	// Transfer completed synchronously => copy and discard extra buffer
+	if (read_size == 0) {
+		usbi_warn(NULL, "program assertion failed - read completed synchronously, but no data was read");
+		*size = 0;
+	} else {
+		if (buf[0] != id) {
+			usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id);
+		}
+		if ((size_t)read_size > expected_size) {
+			r = LIBUSB_ERROR_OVERFLOW;
+			usbi_dbg("OVERFLOW!");
+		} else {
+			r = LIBUSB_COMPLETED;
+		}
+
+		*size = MIN((size_t)read_size, *size);
+		if (id == 0) {
+			// Discard report ID
+			memcpy(data, buf+1, *size);
+		} else {
+			memcpy(data, buf, *size);
+		}
+	}
+	safe_free(buf);
+	return r;
+}
+
+static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data,
+						   struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped,
+						   int report_type)
+{
+	uint8_t *buf = NULL;
+	DWORD ioctl_code, write_size= (DWORD)*size;
+
+	if (tp->hid_buffer != NULL) {
+		usbi_dbg("program assertion failed: hid_buffer is not NULL");
+	}
+
+	if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) {
+		usbi_dbg("invalid size (%d)", *size);
+		return LIBUSB_ERROR_INVALID_PARAM;
+	}
+
+	switch (report_type) {
+		case HID_REPORT_TYPE_OUTPUT:
+			ioctl_code = IOCTL_HID_SET_OUTPUT_REPORT;
+			break;
+		case HID_REPORT_TYPE_FEATURE:
+			ioctl_code = IOCTL_HID_SET_FEATURE;
+			break;
+		default:
+			usbi_dbg("unknown HID report type %d", report_type);
+			return LIBUSB_ERROR_INVALID_PARAM;
+	}
+
+	usbi_dbg("report ID: 0x%02X", id);
+	// When report IDs are not used (i.e. when id == 0), we must add
+	// a null report ID. Otherwise, we just use original data buffer
+	if (id == 0) {
+		write_size++;
+	}
+	buf = (uint8_t*) malloc(write_size);
+	if (buf == NULL) {
+		return LIBUSB_ERROR_NO_MEM;
+	}
+	if (id == 0) {
+		buf[0] = 0;
+		memcpy(buf + 1, data, *size);
+	} else {
+		// This seems like a waste, but if we don't duplicate the
+		// data, we'll get issues when freeing hid_buffer
+		memcpy(buf, data, *size);
+		if (buf[0] != id) {
+			usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id);
+		}
+	}
+
+	// NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0)
+	if (!DeviceIoControl(hid_handle, ioctl_code, buf, write_size,
+		buf, write_size, &write_size, overlapped)) {
+		if (GetLastError() != ERROR_IO_PENDING) {
+			usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0));
+			safe_free(buf);
+			return LIBUSB_ERROR_IO;
+		}
+		tp->hid_buffer = buf;
+		tp->hid_dest = NULL;
+		return LIBUSB_SUCCESS;
+	}
+
+	// Transfer completed synchronously
+	*size = write_size;
+	if (write_size == 0) {
+		usbi_dbg("program assertion failed - write completed synchronously, but no data was written");
+	}
+	safe_free(buf);
+	return LIBUSB_COMPLETED;
+}
+
+static int _hid_class_request(struct hid_device_priv* dev, HANDLE hid_handle, int request_type,
+							  int request, int value, int _index, void *data, struct windows_transfer_priv *tp,
+							  size_t *size, OVERLAPPED* overlapped)
+{
+	int report_type = (value >> 8) & 0xFF;
+	int report_id = value & 0xFF;
+
+	if ( (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_INTERFACE)
+	  && (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_DEVICE) )
+		return LIBUSB_ERROR_INVALID_PARAM;
+
+	if (LIBUSB_REQ_OUT(request_type) && request == HID_REQ_SET_REPORT)
+		return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type);
+
+	if (LIBUSB_REQ_IN(request_type) && request == HID_REQ_GET_REPORT)
+		return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type);
+
+	return LIBUSB_ERROR_INVALID_PARAM;
+}
+
+
+/*
+ * HID API functions
+ */
+static int hid_init(struct libusb_context *ctx)
+{
+	DLL_LOAD(hid.dll, HidD_GetAttributes, TRUE);
+	DLL_LOAD(hid.dll, HidD_GetHidGuid, TRUE);
+	DLL_LOAD(hid.dll, HidD_GetPreparsedData, TRUE);
+	DLL_LOAD(hid.dll, HidD_FreePreparsedData, TRUE);
+	DLL_LOAD(hid.dll, HidD_GetManufacturerString, TRUE);
+	DLL_LOAD(hid.dll, HidD_GetProductString, TRUE);
+	DLL_LOAD(hid.dll, HidD_GetSerialNumberString, TRUE);
+	DLL_LOAD(hid.dll, HidP_GetCaps, TRUE);
+	DLL_LOAD(hid.dll, HidD_SetNumInputBuffers, TRUE);
+	DLL_LOAD(hid.dll, HidD_SetFeature, TRUE);
+	DLL_LOAD(hid.dll, HidD_GetFeature, TRUE);
+	DLL_LOAD(hid.dll, HidD_GetPhysicalDescriptor, TRUE);
+	DLL_LOAD(hid.dll, HidD_GetInputReport, FALSE);
+	DLL_LOAD(hid.dll, HidD_SetOutputReport, FALSE);
+	DLL_LOAD(hid.dll, HidD_FlushQueue, TRUE);
+	DLL_LOAD(hid.dll, HidP_GetValueCaps, TRUE);
+
+	api_hid_available = true;
+	return LIBUSB_SUCCESS;
+}
+
+static int hid_exit(void)
+{
+	return LIBUSB_SUCCESS;
+}
+
+// NB: open and close must ensure that they only handle interface of
+// the right API type, as these functions can be called wholesale from
+// composite_open(), with interfaces belonging to different APIs
+static int hid_open(struct libusb_device_handle *dev_handle)
+{
+	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+
+	HIDD_ATTRIBUTES hid_attributes;
+	PHIDP_PREPARSED_DATA preparsed_data = NULL;
+	HIDP_CAPS capabilities;
+	HIDP_VALUE_CAPS *value_caps;
+
+	HANDLE hid_handle = INVALID_HANDLE_VALUE;
+	int i, j;
+	// report IDs handling
+	ULONG size[3];
+	char* type[3] = {"input", "output", "feature"};
+	int nb_ids[2];	// zero and nonzero report IDs
+
+	CHECK_HID_AVAILABLE;
+	if (priv->hid == NULL) {
+		usbi_err(ctx, "program assertion failed - private HID structure is unitialized");
+		return LIBUSB_ERROR_NOT_FOUND;
+	}
+
+	for (i = 0; i < USB_MAXINTERFACES; i++) {
+		if ( (priv->usb_interface[i].path != NULL)
+		  && (priv->usb_interface[i].apib->id == USB_API_HID) ) {
+			hid_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ,
+				NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+			/*
+			 * http://www.lvr.com/hidfaq.htm: Why do I receive "Access denied" when attempting to access my HID?
+			 * "Windows 2000 and later have exclusive read/write access to HIDs that are configured as a system
+			 * keyboards or mice. An application can obtain a handle to a system keyboard or mouse by not
+			 * requesting READ or WRITE access with CreateFile. Applications can then use HidD_SetFeature and
+			 * HidD_GetFeature (if the device supports Feature reports)."
+			 */
+			if (hid_handle == INVALID_HANDLE_VALUE) {
+				usbi_warn(ctx, "could not open HID device in R/W mode (keyboard or mouse?) - trying without");
+				hid_handle = CreateFileA(priv->usb_interface[i].path, 0, FILE_SHARE_WRITE | FILE_SHARE_READ,
+					NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+				if (hid_handle == INVALID_HANDLE_VALUE) {
+					usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0));
+					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;
+					}
+				}
+				priv->usb_interface[i].restricted_functionality = true;
+			}
+			handle_priv->interface_handle[i].api_handle = hid_handle;
+		}
+	}
+
+	hid_attributes.Size = sizeof(hid_attributes);
+	do {
+		if (!HidD_GetAttributes(hid_handle, &hid_attributes)) {
+			usbi_err(ctx, "could not gain access to HID top collection (HidD_GetAttributes)");
+			break;
+		}
+
+		priv->hid->vid = hid_attributes.VendorID;
+		priv->hid->pid = hid_attributes.ProductID;
+
+		// Set the maximum available input buffer size
+		for (i=32; HidD_SetNumInputBuffers(hid_handle, i); i*=2);
+		usbi_dbg("set maximum input buffer size to %d", i/2);
+
+		// Get the maximum input and output report size
+		if (!HidD_GetPreparsedData(hid_handle, &preparsed_data) || !preparsed_data) {
+			usbi_err(ctx, "could not read HID preparsed data (HidD_GetPreparsedData)");
+			break;
+		}
+		if (HidP_GetCaps(preparsed_data, &capabilities) != HIDP_STATUS_SUCCESS) {
+			usbi_err(ctx, "could not parse HID capabilities (HidP_GetCaps)");
+			break;
+		}
+
+		// Find out if interrupt will need report IDs
+		size[0] = capabilities.NumberInputValueCaps;
+		size[1] = capabilities.NumberOutputValueCaps;
+		size[2] = capabilities.NumberFeatureValueCaps;
+		for (j=0; j<3; j++) {
+			usbi_dbg("%d HID %s report value(s) found", size[j], type[j]);
+			priv->hid->uses_report_ids[j] = false;
+			if (size[j] > 0) {
+				value_caps = (HIDP_VALUE_CAPS*) calloc(size[j], sizeof(HIDP_VALUE_CAPS));
+				if ( (value_caps != NULL)
+				  && (HidP_GetValueCaps(j, value_caps, &size[j], preparsed_data) == HIDP_STATUS_SUCCESS)
+				  && (size[j] >= 1) ) {
+					nb_ids[0] = 0;
+					nb_ids[1] = 0;
+					for (i=0; i<(int)size[j]; i++) {
+						usbi_dbg("  Report ID: 0x%02X", value_caps[i].ReportID);
+						if (value_caps[i].ReportID != 0) {
+							nb_ids[1]++;
+						} else {
+							nb_ids[0]++;
+						}
+					}
+					if (nb_ids[1] != 0) {
+						if (nb_ids[0] != 0) {
+							usbi_warn(ctx, "program assertion failed: zero and nonzero report IDs used for %s",
+								type[j]);
+						}
+						priv->hid->uses_report_ids[j] = true;
+					}
+				} else {
+					usbi_warn(ctx, "  could not process %s report IDs", type[j]);
+				}
+				safe_free(value_caps);
+			}
+		}
+
+		// Set the report sizes
+		priv->hid->input_report_size = capabilities.InputReportByteLength;
+		priv->hid->output_report_size = capabilities.OutputReportByteLength;
+		priv->hid->feature_report_size = capabilities.FeatureReportByteLength;
+
+		// Fetch string descriptors
+		priv->hid->string_index[0] = priv->dev_descriptor.iManufacturer;
+		if (priv->hid->string_index[0] != 0) {
+			HidD_GetManufacturerString(hid_handle, priv->hid->string[0],
+				sizeof(priv->hid->string[0]));
+		} else {
+			priv->hid->string[0][0] = 0;
+		}
+		priv->hid->string_index[1] = priv->dev_descriptor.iProduct;
+		if (priv->hid->string_index[1] != 0) {
+			HidD_GetProductString(hid_handle, priv->hid->string[1],
+				sizeof(priv->hid->string[1]));
+		} else {
+			priv->hid->string[1][0] = 0;
+		}
+		priv->hid->string_index[2] = priv->dev_descriptor.iSerialNumber;
+		if (priv->hid->string_index[2] != 0) {
+			HidD_GetSerialNumberString(hid_handle, priv->hid->string[2],
+				sizeof(priv->hid->string[2]));
+		} else {
+			priv->hid->string[2][0] = 0;
+		}
+	} while(0);
+
+	if (preparsed_data) {
+		HidD_FreePreparsedData(preparsed_data);
+	}
+
+	return LIBUSB_SUCCESS;
+}
+
+static void hid_close(struct libusb_device_handle *dev_handle)
+{
+	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	HANDLE file_handle;
+	int i;
+
+	if (!api_hid_available)
+		return;
+
+	for (i = 0; i < USB_MAXINTERFACES; i++) {
+		if (priv->usb_interface[i].apib->id == USB_API_HID) {
+			file_handle = handle_priv->interface_handle[i].api_handle;
+			if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) {
+				CloseHandle(file_handle);
+			}
+		}
+	}
+}
+
+static int hid_claim_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+	CHECK_HID_AVAILABLE;
+
+	// NB: Disconnection detection is not possible in this function
+	if (priv->usb_interface[iface].path == NULL) {
+		return LIBUSB_ERROR_NOT_FOUND;	// invalid iface
+	}
+
+	// We use dev_handle as a flag for interface claimed
+	if (handle_priv->interface_handle[iface].dev_handle == INTERFACE_CLAIMED) {
+		return LIBUSB_ERROR_BUSY;	// already claimed
+	}
+
+	handle_priv->interface_handle[iface].dev_handle = INTERFACE_CLAIMED;
+
+	usbi_dbg("claimed interface %d", iface);
+	handle_priv->active_interface = iface;
+
+	return LIBUSB_SUCCESS;
+}
+
+static int hid_release_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+
+	CHECK_HID_AVAILABLE;
+
+	if (priv->usb_interface[iface].path == NULL) {
+		return LIBUSB_ERROR_NOT_FOUND;	// invalid iface
+	}
+
+	if (handle_priv->interface_handle[iface].dev_handle != INTERFACE_CLAIMED) {
+		return LIBUSB_ERROR_NOT_FOUND;	// invalid iface
+	}
+
+	handle_priv->interface_handle[iface].dev_handle = INVALID_HANDLE_VALUE;
+
+	return LIBUSB_SUCCESS;
+}
+
+static int hid_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+
+	CHECK_HID_AVAILABLE;
+
+	if (altsetting > 255) {
+		return LIBUSB_ERROR_INVALID_PARAM;
+	}
+
+	if (altsetting != 0) {
+		usbi_err(ctx, "set interface altsetting not supported for altsetting >0");
+		return LIBUSB_ERROR_NOT_SUPPORTED;
+	}
+
+	return LIBUSB_SUCCESS;
+}
+
+static int hid_submit_control_transfer(struct usbi_transfer *itransfer)
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+	struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+	struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+	WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *) transfer->buffer;
+	HANDLE hid_handle;
+	struct winfd wfd;
+	int current_interface, config;
+	size_t size;
+	int r = LIBUSB_ERROR_INVALID_PARAM;
+
+	CHECK_HID_AVAILABLE;
+
+	transfer_priv->pollable_fd = INVALID_WINFD;
+	safe_free(transfer_priv->hid_buffer);
+	transfer_priv->hid_dest = NULL;
+	size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
+
+	if (size > MAX_CTRL_BUFFER_LENGTH) {
+		return LIBUSB_ERROR_INVALID_PARAM;
+	}
+
+	current_interface = get_valid_interface(transfer->dev_handle, USB_API_HID);
+	if (current_interface < 0) {
+		if (auto_claim(transfer, &current_interface, USB_API_HID) != LIBUSB_SUCCESS) {
+			return LIBUSB_ERROR_NOT_FOUND;
+		}
+	}
+
+	usbi_dbg("will use interface %d", current_interface);
+	hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+	// Always use the handle returned from usbi_create_fd (wfd.handle)
+	wfd = usbi_create_fd(hid_handle, _O_RDONLY);
+	if (wfd.fd < 0) {
+		return LIBUSB_ERROR_NO_MEM;
+	}
+
+	switch(LIBUSB_REQ_TYPE(setup->request_type)) {
+	case LIBUSB_REQUEST_TYPE_STANDARD:
+		switch(setup->request) {
+		case LIBUSB_REQUEST_GET_DESCRIPTOR:
+			r = _hid_get_descriptor(priv->hid, wfd.handle, LIBUSB_REQ_RECIPIENT(setup->request_type),
+				(setup->value >> 8) & 0xFF, setup->value & 0xFF, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, &size);
+			break;
+		case LIBUSB_REQUEST_GET_CONFIGURATION:
+			r = windows_get_configuration(transfer->dev_handle, &config);
+			if (r == LIBUSB_SUCCESS) {
+				size = 1;
+				((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = (uint8_t)config;
+				r = LIBUSB_COMPLETED;
+			}
+			break;
+		case LIBUSB_REQUEST_SET_CONFIGURATION:
+			if (setup->value == priv->active_config) {
+				r = LIBUSB_COMPLETED;
+			} else {
+				usbi_warn(ctx, "cannot set configuration other than the default one");
+				r = LIBUSB_ERROR_INVALID_PARAM;
+			}
+			break;
+		case LIBUSB_REQUEST_GET_INTERFACE:
+			size = 1;
+			((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = 0;
+			r = LIBUSB_COMPLETED;
+			break;
+		case LIBUSB_REQUEST_SET_INTERFACE:
+			r = hid_set_interface_altsetting(transfer->dev_handle, setup->index, setup->value);
+			if (r == LIBUSB_SUCCESS) {
+				r = LIBUSB_COMPLETED;
+			}
+			break;
+		default:
+			usbi_warn(ctx, "unsupported HID control request");
+			r = LIBUSB_ERROR_INVALID_PARAM;
+			break;
+		}
+		break;
+	case LIBUSB_REQUEST_TYPE_CLASS:
+		r =_hid_class_request(priv->hid, wfd.handle, setup->request_type, setup->request, setup->value,
+			setup->index, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer_priv,
+			&size, wfd.overlapped);
+		break;
+	default:
+		usbi_warn(ctx, "unsupported HID control request");
+		r = LIBUSB_ERROR_INVALID_PARAM;
+		break;
+	}
+
+	if (r == LIBUSB_COMPLETED) {
+		// Force request to be completed synchronously. Transferred size has been set by previous call
+		wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+		// http://msdn.microsoft.com/en-us/library/ms684342%28VS.85%29.aspx
+		// set InternalHigh to the number of bytes transferred
+		wfd.overlapped->InternalHigh = (DWORD)size;
+		r = LIBUSB_SUCCESS;
+	}
+
+	if (r == LIBUSB_SUCCESS) {
+		// Use priv_transfer to store data needed for async polling
+		transfer_priv->pollable_fd = wfd;
+		transfer_priv->interface_number = (uint8_t)current_interface;
+	} else {
+		usbi_free_fd(wfd.fd);
+	}
+
+	return r;
+}
+
+static int hid_submit_bulk_transfer(struct usbi_transfer *itransfer) {
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+	struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+	struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+	struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	struct winfd wfd;
+	HANDLE hid_handle;
+	bool direction_in, ret;
+	int current_interface, length;
+	DWORD size;
+	int r = LIBUSB_SUCCESS;
+
+	CHECK_HID_AVAILABLE;
+
+	transfer_priv->pollable_fd = INVALID_WINFD;
+	transfer_priv->hid_dest = NULL;
+	safe_free(transfer_priv->hid_buffer);
+
+	current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint);
+	if (current_interface < 0) {
+		usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer");
+		return LIBUSB_ERROR_NOT_FOUND;
+	}
+
+	usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
+
+	hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+	direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN;
+
+	wfd = usbi_create_fd(hid_handle, direction_in?_O_RDONLY:_O_WRONLY);
+	// Always use the handle returned from usbi_create_fd (wfd.handle)
+	if (wfd.fd < 0) {
+		return LIBUSB_ERROR_NO_MEM;
+	}
+
+	// If report IDs are not in use, an extra prefix byte must be added
+	if ( ((direction_in) && (!priv->hid->uses_report_ids[0]))
+	  || ((!direction_in) && (!priv->hid->uses_report_ids[1])) ) {
+		length = transfer->length+1;
+	} else {
+		length = transfer->length;
+	}
+	// Add a trailing byte to detect overflows on input
+	transfer_priv->hid_buffer = (uint8_t*)calloc(length+1, 1);
+	if (transfer_priv->hid_buffer == NULL) {
+		return LIBUSB_ERROR_NO_MEM;
+	}
+	transfer_priv->hid_expected_size = length;
+
+	if (direction_in) {
+		transfer_priv->hid_dest = transfer->buffer;
+		usbi_dbg("reading %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]);
+		ret = ReadFile(wfd.handle, transfer_priv->hid_buffer, length+1, &size, wfd.overlapped);
+	} else {
+		if (!priv->hid->uses_report_ids[1]) {
+			memcpy(transfer_priv->hid_buffer+1, transfer->buffer, transfer->length);
+		} else {
+			// We could actually do without the calloc and memcpy in this case
+			memcpy(transfer_priv->hid_buffer, transfer->buffer, transfer->length);
+		}
+		usbi_dbg("writing %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]);
+		ret = WriteFile(wfd.handle, transfer_priv->hid_buffer, length, &size, wfd.overlapped);
+	}
+	if (!ret) {
+		if (GetLastError() != ERROR_IO_PENDING) {
+			usbi_err(ctx, "HID transfer failed: %s", windows_error_str(0));
+			usbi_free_fd(wfd.fd);
+			safe_free(transfer_priv->hid_buffer);
+			return LIBUSB_ERROR_IO;
+		}
+	} else {
+		// Only write operations that completed synchronously need to free up
+		// hid_buffer. For reads, copy_transfer_data() handles that process.
+		if (!direction_in) {
+			safe_free(transfer_priv->hid_buffer);
+		}
+		if (size == 0) {
+			usbi_err(ctx, "program assertion failed - no data was transferred");
+			size = 1;
+		}
+		if (size > (size_t)length) {
+			usbi_err(ctx, "OVERFLOW!");
+			r = LIBUSB_ERROR_OVERFLOW;
+		}
+		wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+		wfd.overlapped->InternalHigh = size;
+	}
+
+	transfer_priv->pollable_fd = wfd;
+	transfer_priv->interface_number = (uint8_t)current_interface;
+
+	return r;
+}
+
+static int hid_abort_transfers(struct usbi_transfer *itransfer)
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer);
+	struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+	HANDLE hid_handle;
+	int current_interface;
+
+	CHECK_HID_AVAILABLE;
+
+	current_interface = transfer_priv->interface_number;
+	hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+	CancelIo(hid_handle);
+
+	return LIBUSB_SUCCESS;
+}
+
+static int hid_reset_device(struct libusb_device_handle *dev_handle)
+{
+	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	HANDLE hid_handle;
+	int current_interface;
+
+	CHECK_HID_AVAILABLE;
+
+	// Flushing the queues on all interfaces is the best we can achieve
+	for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) {
+		hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+		if ((hid_handle != 0) && (hid_handle != INVALID_HANDLE_VALUE)) {
+			HidD_FlushQueue(hid_handle);
+		}
+	}
+	return LIBUSB_SUCCESS;
+}
+
+static int hid_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+	struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
+	HANDLE hid_handle;
+	int current_interface;
+
+	CHECK_HID_AVAILABLE;
+
+	current_interface = interface_by_endpoint(priv, handle_priv, endpoint);
+	if (current_interface < 0) {
+		usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear");
+		return LIBUSB_ERROR_NOT_FOUND;
+	}
+
+	usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface);
+	hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+
+	// No endpoint selection with Microsoft's implementation, so we try to flush the
+	// whole interface. Should be OK for most case scenarios
+	if (!HidD_FlushQueue(hid_handle)) {
+		usbi_err(ctx, "Flushing of HID queue failed: %s", windows_error_str(0));
+		// Device was probably disconnected
+		return LIBUSB_ERROR_NO_DEVICE;
+	}
+
+	return LIBUSB_SUCCESS;
+}
+
+// This extra function is only needed for HID
+static int hid_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) {
+	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);
+	int r = LIBUSB_TRANSFER_COMPLETED;
+	uint32_t corrected_size = io_size;
+
+	if (transfer_priv->hid_buffer != NULL) {
+		// If we have a valid hid_buffer, it means the transfer was async
+		if (transfer_priv->hid_dest != NULL) {	// Data readout
+			// First, check for overflow
+			if (corrected_size > transfer_priv->hid_expected_size) {
+				usbi_err(ctx, "OVERFLOW!");
+				corrected_size = (uint32_t)transfer_priv->hid_expected_size;
+				r = LIBUSB_TRANSFER_OVERFLOW;
+			}
+
+			if (transfer_priv->hid_buffer[0] == 0) {
+				// Discard the 1 byte report ID prefix
+				corrected_size--;
+				memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer+1, corrected_size);
+			} else {
+				memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer, corrected_size);
+			}
+			transfer_priv->hid_dest = NULL;
+		}
+		// For write, we just need to free the hid buffer
+		safe_free(transfer_priv->hid_buffer);
+	}
+	itransfer->transferred += corrected_size;
+	return r;
+}
+
 
 /*
  * Composite API functions
@@ -2896,12 +4036,20 @@
 	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 	struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
 	struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
-	int i;
+	int i, pass;
 
-	for (i=0; i<USB_MAXINTERFACES; i++) {
-		if (priv->usb_interface[i].path != NULL) {
-			usbi_dbg("using interface %d", i);
-			return priv->usb_interface[i].apib->submit_control_transfer(itransfer);
+	// Interface shouldn't matter for control, but it does in practice, with Windows'
+	// restrictions with regards to accessing HID keyboards and mice. Try a 2 pass approach
+	for (pass = 0; pass < 2; pass++) {
+		for (i=0; i<USB_MAXINTERFACES; i++) {
+			if (priv->usb_interface[i].path != NULL) {
+				if ((pass == 0) && (priv->usb_interface[i].restricted_functionality)) {
+					usbi_dbg("trying to skip restricted interface #%d (HID keyboard or mouse?)", i);
+					continue;
+				}
+				usbi_dbg("using interface %d", i);
+				return priv->usb_interface[i].apib->submit_control_transfer(itransfer);
+			}
 		}
 	}
 
diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h
index 4497efa..331f75c 100644
--- a/libusb/os/windows_usb.h
+++ b/libusb/os/windows_usb.h
@@ -80,6 +80,8 @@
 #define MAX_CTRL_BUFFER_LENGTH      4096
 #define MAX_USB_DEVICES             256
 #define MAX_USB_STRING_LENGTH       128
+#define MAX_HID_REPORT_SIZE         1024
+#define MAX_HID_DESCRIPTOR_SIZE     256
 #define MAX_GUID_STRING_LENGTH      40
 #define MAX_PATH_LENGTH             128
 #define MAX_KEY_LENGTH              256
@@ -89,6 +91,11 @@
 #define LIST_SEPARATOR              ';'
 #define HTAB_SIZE                   1021
 
+// Handle code for HID interface that have been claimed ("dibs")
+#define INTERFACE_CLAIMED           ((HANDLE)(intptr_t)0xD1B5)
+// Additional return code for HID operations that completed synchronously
+#define LIBUSB_COMPLETED            (LIBUSB_SUCCESS + 1)
+
 // http://msdn.microsoft.com/en-us/library/ff545978.aspx
 // http://msdn.microsoft.com/en-us/library/ff545972.aspx
 // http://msdn.microsoft.com/en-us/library/ff545982.aspx
@@ -111,9 +118,11 @@
 #define USB_API_HUB         1
 #define USB_API_COMPOSITE   2
 #define USB_API_WINUSB      3
-#define USB_API_MAX         4
+#define USB_API_HID         4
+#define USB_API_MAX         5
 
 #define CLASS_GUID_UNSUPPORTED      GUID_NULL
+const GUID CLASS_GUID_HID           = { 0x745A17A0, 0x74D3, 0x11D0, {0xB6, 0xFE, 0x00, 0xA0, 0xC9, 0x0F, 0x57, 0xDA} };
 const GUID CLASS_GUID_LIBUSB_WINUSB = { 0x78A1C341, 0x4539, 0x11D3, {0xB8, 0x8D, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71} };
 const GUID CLASS_GUID_COMPOSITE     = { 0x36FC9E60, 0xC465, 0x11cF, {0x80, 0x56, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} };
 
@@ -151,6 +160,71 @@
  * private structures definition
  * with inline pseudo constructors/destructors
  */
+
+// TODO (v2+): move hid desc to libusb.h?
+struct libusb_hid_descriptor {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint16_t bcdHID;
+	uint8_t  bCountryCode;
+	uint8_t  bNumDescriptors;
+	uint8_t  bClassDescriptorType;
+	uint16_t wClassDescriptorLength;
+};
+#define LIBUSB_DT_HID_SIZE              9
+#define HID_MAX_CONFIG_DESC_SIZE (LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE \
+	+ LIBUSB_DT_HID_SIZE + 2 * LIBUSB_DT_ENDPOINT_SIZE)
+#define HID_MAX_REPORT_SIZE             1024
+#define HID_IN_EP                       0x81
+#define HID_OUT_EP                      0x02
+#define LIBUSB_REQ_RECIPIENT(request_type) ((request_type) & 0x1F)
+#define LIBUSB_REQ_TYPE(request_type) ((request_type) & (0x03 << 5))
+#define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN)
+#define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type))
+
+// The following are used for HID reports IOCTLs
+#define HID_CTL_CODE(id) \
+  CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_NEITHER, FILE_ANY_ACCESS)
+#define HID_BUFFER_CTL_CODE(id) \
+  CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HID_IN_CTL_CODE(id) \
+  CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_IN_DIRECT, FILE_ANY_ACCESS)
+#define HID_OUT_CTL_CODE(id) \
+  CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+
+#define IOCTL_HID_GET_FEATURE                 HID_OUT_CTL_CODE(100)
+#define IOCTL_HID_GET_INPUT_REPORT            HID_OUT_CTL_CODE(104)
+#define IOCTL_HID_SET_FEATURE                 HID_IN_CTL_CODE(100)
+#define IOCTL_HID_SET_OUTPUT_REPORT           HID_IN_CTL_CODE(101)
+
+enum libusb_hid_request_type {
+	HID_REQ_GET_REPORT = 0x01,
+	HID_REQ_GET_IDLE = 0x02,
+	HID_REQ_GET_PROTOCOL = 0x03,
+	HID_REQ_SET_REPORT = 0x09,
+	HID_REQ_SET_IDLE = 0x0A,
+	HID_REQ_SET_PROTOCOL = 0x0B
+};
+
+enum libusb_hid_report_type {
+	HID_REPORT_TYPE_INPUT = 0x01,
+	HID_REPORT_TYPE_OUTPUT = 0x02,
+	HID_REPORT_TYPE_FEATURE = 0x03
+};
+
+struct hid_device_priv {
+	uint16_t vid;
+	uint16_t pid;
+	uint8_t config;
+	uint8_t nb_interfaces;
+	bool uses_report_ids[3];	// input, ouptput, feature
+	uint16_t input_report_size;
+	uint16_t output_report_size;
+	uint16_t feature_report_size;
+	WCHAR string[3][MAX_USB_STRING_LENGTH];
+	uint8_t string_index[3];	// man, prod, ser
+};
+
 typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
 struct windows_device_priv {
 	uint8_t depth;						// distance to HCD
@@ -163,8 +237,11 @@
 		struct windows_usb_api_backend const *apib; // an API backend (multiple drivers support),
 		int8_t nb_endpoints;			// and a set of endpoint addresses (USB_MAXENDPOINTS)
 		uint8_t *endpoint;
+		bool restricted_functionality;	// indicates if the interface functionality is restricted
+										// by Windows (eg. HID keyboards or mice cannot do R/W)
 	} usb_interface[USB_MAXINTERFACES];
-	uint8_t composite_api_flags;		// composite devices require additional data
+	uint8_t composite_api_flags;		// HID and composite devices require additional data
+	struct hid_device_priv *hid;
 	uint8_t active_config;
 	USB_DEVICE_DESCRIPTOR dev_descriptor;
 	unsigned char **config_descriptor;	// list of pointers to the cached config descriptors
@@ -183,6 +260,7 @@
 	p->path = NULL;
 	p->apib = &usb_api_backend[USB_API_UNSUPPORTED];
 	p->composite_api_flags = 0;
+	p->hid = NULL;
 	p->active_config = 0;
 	p->config_descriptor = NULL;
 	memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR));
@@ -191,6 +269,7 @@
 		p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED];
 		p->usb_interface[i].nb_endpoints = 0;
 		p->usb_interface[i].endpoint = NULL;
+		p->usb_interface[i].restricted_functionality = false;
 	}
 }
 
@@ -203,6 +282,7 @@
 			safe_free(p->config_descriptor[i]);
 	}
 	safe_free(p->config_descriptor);
+	safe_free(p->hid);
 	for (i=0; i<USB_MAXINTERFACES; i++) {
 		safe_free(p->usb_interface[i].path);
 		safe_free(p->usb_interface[i].endpoint);
@@ -230,6 +310,9 @@
 struct windows_transfer_priv {
 	struct winfd pollable_fd;
 	uint8_t interface_number;
+	uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID
+	uint8_t *hid_dest;   // transfer buffer destination, required for HID
+	size_t hid_expected_size;
 };
 
 // used to match a device driver (including filter drivers) against a supported API
@@ -606,3 +689,97 @@
 DLL_DECLARE(WINAPI, BOOL, WinUsb_ResetPipe, (WINUSB_INTERFACE_HANDLE, UCHAR));
 DLL_DECLARE(WINAPI, BOOL, WinUsb_AbortPipe, (WINUSB_INTERFACE_HANDLE, UCHAR));
 DLL_DECLARE(WINAPI, BOOL, WinUsb_FlushPipe, (WINUSB_INTERFACE_HANDLE, UCHAR));
+
+/* hid.dll interface */
+
+#define HIDP_STATUS_SUCCESS  0x110000
+typedef void* PHIDP_PREPARSED_DATA;
+
+#pragma pack(1)
+typedef struct {
+	ULONG Size;
+	USHORT VendorID;
+	USHORT ProductID;
+	USHORT VersionNumber;
+} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
+#pragma pack()
+
+typedef USHORT USAGE;
+typedef struct {
+  USAGE Usage;
+  USAGE UsagePage;
+  USHORT InputReportByteLength;
+  USHORT OutputReportByteLength;
+  USHORT FeatureReportByteLength;
+  USHORT Reserved[17];
+  USHORT NumberLinkCollectionNodes;
+  USHORT NumberInputButtonCaps;
+  USHORT NumberInputValueCaps;
+  USHORT NumberInputDataIndices;
+  USHORT NumberOutputButtonCaps;
+  USHORT NumberOutputValueCaps;
+  USHORT NumberOutputDataIndices;
+  USHORT NumberFeatureButtonCaps;
+  USHORT NumberFeatureValueCaps;
+  USHORT NumberFeatureDataIndices;
+} HIDP_CAPS, *PHIDP_CAPS;
+
+typedef enum _HIDP_REPORT_TYPE {
+  HidP_Input,
+  HidP_Output,
+  HidP_Feature
+} HIDP_REPORT_TYPE;
+
+typedef struct _HIDP_VALUE_CAPS {
+  USAGE  UsagePage;
+  UCHAR  ReportID;
+  BOOLEAN  IsAlias;
+  USHORT  BitField;
+  USHORT  LinkCollection;
+  USAGE  LinkUsage;
+  USAGE  LinkUsagePage;
+  BOOLEAN  IsRange;
+  BOOLEAN  IsStringRange;
+  BOOLEAN  IsDesignatorRange;
+  BOOLEAN  IsAbsolute;
+  BOOLEAN  HasNull;
+  UCHAR  Reserved;
+  USHORT  BitSize;
+  USHORT  ReportCount;
+  USHORT  Reserved2[5];
+  ULONG  UnitsExp;
+  ULONG  Units;
+  LONG  LogicalMin, LogicalMax;
+  LONG  PhysicalMin, PhysicalMax;
+	union {
+	  struct {
+		USAGE  UsageMin, UsageMax;
+		USHORT  StringMin, StringMax;
+		USHORT  DesignatorMin, DesignatorMax;
+		USHORT  DataIndexMin, DataIndexMax;
+	  } Range;
+	  struct {
+		USAGE  Usage, Reserved1;
+		USHORT  StringIndex, Reserved2;
+		USHORT  DesignatorIndex, Reserved3;
+		USHORT  DataIndex, Reserved4;
+	  } NotRange;
+	} u;
+} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
+
+DLL_DECLARE(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES));
+DLL_DECLARE(WINAPI, VOID, HidD_GetHidGuid, (LPGUID));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetPreparsedData, (HANDLE, PHIDP_PREPARSED_DATA *));
+DLL_DECLARE(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, LONG, HidP_GetCaps, (PHIDP_PREPARSED_DATA, PHIDP_CAPS));
+DLL_DECLARE(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_SetFeature, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetFeature, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetInputReport, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_SetOutputReport, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_FlushQueue, (HANDLE));
+DLL_DECLARE(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA));
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index c99cbbe..a07bacb 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 10511
+#define LIBUSB_NANO 10512