diff options
author | Pete Batard <pbatard@gmail.com> | 2010-09-06 16:54:41 +0100 |
---|---|---|
committer | Pete Batard <pbatard@gmail.com> | 2010-09-06 16:54:41 +0100 |
commit | 35f01dd9b1763d73b61f362e21ffea6f7248530f (patch) | |
tree | 6dcf80d10b2bdb6e046187292499170c14be1e34 /libusb | |
parent | 16cb5e2e87fd9ce270ef88f893ec8d0fac4d6925 (diff) | |
download | libusb-35f01dd9b1763d73b61f362e21ffea6f7248530f.tar.gz |
use IOCTLs for HID reports (input, output, feature)
* fixes feature reports not providing actual read size (reported by Axel Rohde)
* removes the USE_HIDD_FOR_REPORTS macro
* IOCTL usage inspired from HIDAPI by Alan Ott
Diffstat (limited to 'libusb')
-rw-r--r-- | libusb/os/windows_usb.c | 197 | ||||
-rw-r--r-- | libusb/os/windows_usb.h | 15 |
2 files changed, 59 insertions, 153 deletions
diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c index 777b2b4..0e7f3ff 100644 --- a/libusb/os/windows_usb.c +++ b/libusb/os/windows_usb.c @@ -3,6 +3,7 @@ * Copyright (c) 2009-2010 Pete Batard <pbatard@gmail.com> * With contributions from Michael Plante, Orin Eman et al. * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software * Major code testing contribution by Xiaofan Chen * * This library is free software; you can redistribute it and/or @@ -21,13 +22,6 @@ */ // COMPILATION OPTIONS: -// - Use HidD_(G/S)et(In/Out)putReport instead of (Read/Write)File for HID -// Note that http://msdn.microsoft.com/en-us/library/ms789883.aspx: -// "In addition, some devices might not support HidD_GetInputReport, -// and will become unresponsive if this routine is used." -// => Don't blame libusb if you can't read or write HID reports when the -// option below is enabled. -#define USE_HIDD_FOR_REPORTS // - Should libusb automatically claim (and release) the interfaces it requires? #define AUTO_CLAIM // - Forces instant overlapped completion on timeouts: can prevents extensive @@ -3262,10 +3256,11 @@ static int _hid_get_descriptor(struct hid_device_priv* dev, HANDLE hid_handle, i } 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) + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped, + int report_type) { uint8_t *buf; - DWORD read_size, expected_size = (DWORD)*size; + DWORD ioctl_code, read_size, expected_size = (DWORD)*size; int r = LIBUSB_SUCCESS; if (tp->hid_buffer != NULL) { @@ -3277,6 +3272,18 @@ static int _hid_get_report(struct hid_device_priv* dev, HANDLE hid_handle, int i 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; + } + // When report IDs are not in use, add an extra byte for the report ID if (id==0) { expected_size++; @@ -3290,16 +3297,12 @@ static int _hid_get_report(struct hid_device_priv* dev, HANDLE hid_handle, int i buf[0] = (uint8_t)id; // Must be set always usbi_dbg("report ID: 0x%02X", buf[0]); - // NB: HidD_GetInputReport sends an request to the device for the Input Report - // (and blocks until response) whereas ReadFile waits for input to be generated - // asynchronously -#if !defined(USE_HIDD_FOR_REPORTS) - // Use ReadFile instead of HidD_GetInputReport for async I/O - // TODO: send a request paquet? tp->hid_expected_size = expected_size; - if (!ReadFile(hid_handle, buf, expected_size+1, &read_size, overlapped)) { + + 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 Input Report: %s", windows_error_str(0)); + usbi_dbg("Failed to Read HID Report: %s", windows_error_str(0)); safe_free(buf); return LIBUSB_ERROR_IO; } @@ -3308,15 +3311,7 @@ static int _hid_get_report(struct hid_device_priv* dev, HANDLE hid_handle, int i tp->hid_dest = data; // copy dest, as not necessarily the start of the transfer buffer return LIBUSB_SUCCESS; } -#else - // Synchronous request for the Input Report - if (!HidD_GetInputReport(hid_handle, buf, expected_size)) { - usbi_dbg("Failed to Read HID Input Report: %s", windows_error_str(0)); - safe_free(buf); - return LIBUSB_ERROR_IO; - } - read_size = expected_size; // Can't detect overflows with this API -#endif + // Transfer completed synchronously => copy and discard extra buffer if (read_size == 0) { usbi_dbg("program assertion failed - read completed synchronously, but no data was read"); @@ -3346,10 +3341,11 @@ static int _hid_get_report(struct hid_device_priv* dev, HANDLE hid_handle, int i } 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) + struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped, + int report_type) { uint8_t *buf = NULL; - DWORD write_size= (DWORD)*size; + DWORD ioctl_code, write_size= (DWORD)*size; if (tp->hid_buffer != NULL) { usbi_dbg("program assertion failed: hid_buffer is not NULL"); @@ -3360,6 +3356,18 @@ static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int i 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 @@ -3382,9 +3390,8 @@ static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int i } } -#if !defined(USE_HIDD_FOR_REPORTS) - // Une WriteFile instead of HidD_SetOutputReport for async I/O - if (!WriteFile(hid_handle, buf, write_size, &write_size, overlapped)) { + 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); @@ -3394,15 +3401,7 @@ static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int i tp->hid_dest = NULL; return LIBUSB_SUCCESS; } -#else - if (!HidD_SetOutputReport(hid_handle, buf, write_size)) { - usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0)); - if (id == 0) { - safe_free(buf); - } - return LIBUSB_ERROR_IO; - } -#endif + // Transfer completed synchronously if (write_size == 0) { usbi_dbg("program assertion failed - write completed synchronously, but no data was written"); @@ -3414,100 +3413,6 @@ static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int i return LIBUSB_COMPLETED; } -static int _hid_get_feature(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, size_t *size) -{ - uint8_t *buf = (uint8_t*)data; // default with report ID is to use data - ULONG read_size = (ULONG)*size; - int r = LIBUSB_ERROR_OTHER; - uint32_t err; - - if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { - usbi_dbg("invalid size (%d)", *size); - return LIBUSB_ERROR_INVALID_PARAM; - } - - // When report IDs are not in use, we must prefix an extra zero ID - if (id == 0) { - read_size++; - buf = (uint8_t*)calloc(1, read_size); - if (buf == NULL) { - return LIBUSB_ERROR_NO_MEM; - } - } - buf[0] = (uint8_t)id; - usbi_dbg("report ID: 0x%02X", buf[0]); - - if (HidD_GetFeature(hid_handle, buf, read_size)) { - if (buf[0] != id) { - usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); - } - if (id == 0) { - memcpy(data, buf+1, *size); - } - r = LIBUSB_COMPLETED; - } else { - err = GetLastError(); - switch (err) { - case ERROR_INVALID_FUNCTION: - r = LIBUSB_ERROR_NOT_FOUND; - break; - default: - usbi_dbg("error %s", windows_error_str(err)); - r = LIBUSB_ERROR_OTHER; - break; - } - } - if (id == 0) { - safe_free(buf); - } - return r; -} - -static int _hid_set_feature(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, size_t *size) -{ - uint8_t *buf = (uint8_t*)data; - uint32_t err; - int r = LIBUSB_ERROR_OTHER; - ULONG write_size = (ULONG)*size; - - if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) { - usbi_dbg("invalid size (%d)", *size); - return LIBUSB_ERROR_INVALID_PARAM; - } - - if (id == 0) { - write_size++; - buf = (uint8_t*)calloc(write_size, 1); - if (buf == NULL) { - return LIBUSB_ERROR_NO_MEM; - } - memcpy(buf+1, data, *size); - buf[0] = (uint8_t)id; - } else if (buf[0] != id) { - usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id); - return LIBUSB_ERROR_INVALID_PARAM; - } - - usbi_dbg("report ID: 0x%02X", buf[0]); - - if (HidD_SetFeature(hid_handle, buf, write_size)) { - r = LIBUSB_COMPLETED; - } else { - err = GetLastError(); - switch (err) { - case ERROR_INVALID_FUNCTION: - r = LIBUSB_ERROR_NOT_FOUND; - default: - usbi_dbg("error %s", windows_error_str(err)); - r = LIBUSB_ERROR_OTHER; - } - } - if (id == 0) { - safe_free(buf); - } - return r; -} - 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) @@ -3519,25 +3424,11 @@ static int _hid_class_request(struct hid_device_priv* dev, HANDLE hid_handle, in && (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_DEVICE) ) return LIBUSB_ERROR_INVALID_PARAM; - if (LIBUSB_REQ_OUT(request_type) - && request == HID_REQ_SET_REPORT - && report_type == HID_REPORT_TYPE_OUTPUT) - return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped); - - if (LIBUSB_REQ_IN(request_type) - && request == HID_REQ_GET_REPORT - && report_type == HID_REPORT_TYPE_INPUT) - return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped); - - if (LIBUSB_REQ_OUT(request_type) - && request == HID_REQ_SET_REPORT - && report_type == HID_REPORT_TYPE_FEATURE) - return _hid_set_feature(dev, hid_handle, report_id, data, size); - - if (LIBUSB_REQ_IN(request_type) - && request == HID_REQ_GET_REPORT - && report_type == HID_REPORT_TYPE_FEATURE) - return _hid_get_feature(dev, hid_handle, report_id, data, size); + 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; } diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h index fce75af..b37fb56 100644 --- a/libusb/os/windows_usb.h +++ b/libusb/os/windows_usb.h @@ -199,6 +199,21 @@ struct libusb_hid_descriptor { #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, |