Permalink
Browse files

Windows: use IOCTLs for HID input, output and feature reports

* fixes feature reports not providing actual read size
  (reported by Axel Rohde http://marc.info/?m=127033070021994)
* removes the USE_HIDD_FOR_REPORTS macro
* IOCTL usage inspired from HIDAPI by Alan Ott
  • Loading branch information...
1 parent d7c2545 commit 1feadb5f78e1d2cdfd2aaf0011e041c2848bae37 Pete Batard committed with Peter Stuge Oct 5, 2010
Showing with 62 additions and 155 deletions.
  1. +45 −153 libusb/os/windows_usb.c
  2. +17 −2 libusb/os/windows_usb.h
View
@@ -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
@@ -45,6 +39,7 @@
#include <stdio.h>
#include <inttypes.h>
#include <objbase.h> // for string to GUID conv. requires libole32.a
+#include <winioctl.h>
#include <libusbi.h>
#include "poll_windows.h"
@@ -3197,10 +3192,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) {
@@ -3212,6 +3208,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++;
@@ -3225,16 +3233,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;
}
@@ -3243,15 +3247,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");
@@ -3281,10 +3277,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");
@@ -3295,6 +3292,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
@@ -3317,9 +3326,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);
@@ -3329,15 +3337,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");
@@ -3349,100 +3349,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)
@@ -3454,25 +3360,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;
}
View
@@ -197,6 +197,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,
@@ -345,7 +360,7 @@ struct windows_transfer_priv {
typedef DWORD DEVNODE, DEVINST;
typedef DEVNODE *PDEVNODE, *PDEVINST;
typedef DWORD RETURN_TYPE;
-typedef RETURN_TYPE CONFIGRET;
+typedef RETURN_TYPE CONFIGRET;
#define CR_SUCCESS 0x00000000
#define CR_NO_SUCH_DEVNODE 0x0000000D
@@ -468,7 +483,7 @@ typedef struct _USB_INTERFACE_DESCRIPTOR {
typedef struct _USB_CONFIGURATION_DESCRIPTOR {
UCHAR bLength;
UCHAR bDescriptorType;
- USHORT wTotalLength;
+ USHORT wTotalLength;
UCHAR bNumInterfaces;
UCHAR bConfigurationValue;
UCHAR iConfiguration;

0 comments on commit 1feadb5

Please sign in to comment.