diff options
author | Pete Batard <pete@akeo.ie> | 2012-05-11 23:34:03 +0100 |
---|---|---|
committer | Pete Batard <pete@akeo.ie> | 2012-05-28 10:57:51 +0100 |
commit | 3fae93a941669be8a87ca49dd09f836d2d9647aa (patch) | |
tree | 43e3a8db0401e349e19b5bdd389cae0ce325b41d | |
parent | 500b037e7dd6784f9db0bab5f1c3ff417ed875e2 (diff) | |
download | libusb-3fae93a941669be8a87ca49dd09f836d2d9647aa.tar.gz |
Windows: Restore HID support
-rw-r--r-- | examples/xusb.c | 171 | ||||
-rw-r--r-- | libusb/os/windows_usb.c | 1180 | ||||
-rw-r--r-- | libusb/os/windows_usb.h | 181 | ||||
-rw-r--r-- | libusb/version_nano.h | 2 |
4 files changed, 1513 insertions, 21 deletions
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 @@ static int perr(char const *format, ...) // 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 @@ enum test_type { USE_PS3, USE_XBOX, USE_SCSI, + USE_HID, } test_mode; uint16_t VID, PID; @@ -525,6 +531,157 @@ static int test_mass_storage(libusb_device_handle *handle, uint8_t endpoint_in, 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 @@ static int test_device(uint16_t vid, uint16_t pid) 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 @@ static int test_device(uint16_t vid, uint16_t pid) 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 @@ int main(int argc, char** argv) 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 @@ int main(int argc, char** argv) 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_transfers(struct usbi_transfer *itransfer); 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 @@ HANDLE timer_response = NULL; // 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 @@ static int auto_claim(struct libusb_transfer *transfer, int *interface_number, i 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 @@ static int set_composite_interface(struct libusb_context* ctx, struct libusb_dev 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 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered 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 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered // 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 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered 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 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered } } 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 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered 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 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered 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 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered 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 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered } // 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 @@ static void windows_clear_transfer_priv(struct usbi_transfer *itransfer) 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 @@ static int unsupported_copy_transfer_data(struct usbi_transfer *itransfer, uint3 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 @@ const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { 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 @@ static int winusb_release_interface(struct libusb_device_handle *dev_handle, int /* * 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 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer) 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, ¤t_interface, USB_API_WINUSB) != LIBUSB_SUCCESS) { return LIBUSB_ERROR_NOT_FOUND; @@ -2826,6 +2929,1043 @@ static int winusb_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t i 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, ¤t_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 @@ static int composite_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_device_priv *priv = _device_priv(transfer->dev_handle->dev); - int i; - - 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); + int i, pass; + + // 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 @@ static inline void upperize(char* str) { #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 @@ static inline void upperize(char* str) { #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 @@ const GUID GUID_NULL = { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x #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 @@ extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX]; * 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_device_priv { 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 @@ static inline void windows_device_priv_init(libusb_device* dev) { 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 @@ static inline void windows_device_priv_init(libusb_device* dev) { 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 @@ static inline void windows_device_priv_release(libusb_device* dev) { 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 @@ static inline struct windows_device_handle_priv *_device_handle_priv( 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_ControlTransfer, (WINUSB_INTERFACE_HANDLE, WINU 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 |