Skip to content

Commit

Permalink
Introduce HID_API_MAX_REPORT_DESCRIPTOR_SIZE
Browse files Browse the repository at this point in the history
- first step is to use HID_API_MAX_REPORT_DESCRIPTOR_SIZE internally;
- port a few improvements from get-descriptor branch early;
  • Loading branch information
Youw committed Sep 10, 2022
1 parent 0f2cf88 commit 551ea31
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 67 deletions.
8 changes: 8 additions & 0 deletions hidapi/hidapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@
*/
#define HID_API_VERSION_STR HID_API_TO_VERSION_STR(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)

/** @brief Maximum expected HID Report descriptor size in bytes.
Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
@ingroup API
*/
#define HID_API_MAX_REPORT_DESCRIPTOR_SIZE 4096

#ifdef __cplusplus
extern "C" {
#endif
Expand Down
53 changes: 34 additions & 19 deletions libusb/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -564,28 +564,43 @@ int HID_API_EXPORT hid_exit(void)
return 0;
}

/**
* Requires an opened device with *claimed interface*.
*/
static void fill_device_info_usage(struct hid_device_info *cur_dev, libusb_device_handle *handle, int interface_num, uint16_t report_descriptor_size)
static int hid_get_report_descriptor_libusb(libusb_device_handle *handle, int interface_num, uint16_t expected_report_descriptor_size, unsigned char *buf, size_t buf_size)
{
unsigned char data[4096];
unsigned short page = 0, usage = 0;
unsigned char tmp[HID_API_MAX_REPORT_DESCRIPTOR_SIZE];

if (report_descriptor_size > 4096)
report_descriptor_size = 4096;
if (expected_report_descriptor_size > HID_API_MAX_REPORT_DESCRIPTOR_SIZE)
expected_report_descriptor_size = HID_API_MAX_REPORT_DESCRIPTOR_SIZE;

/* Get the HID Report Descriptor.
See USB HID Specificatin, sectin 7.1.1
*/
int res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8), interface_num, data, report_descriptor_size, 5000);
int res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8), interface_num, tmp, expected_report_descriptor_size, 5000);
if (res < 0) {
LOG("libusb_control_transfer() for getting the HID Report descriptor failed with %d: %s\n", res, libusb_error_name(res));
return -1;
}

if (res > buf_size)
res = (int)buf_size;

memcpy(buf, tmp, (size_t)res);
return res;
}

/**
* Requires an opened device with *claimed interface*.
*/
static void fill_device_info_usage(struct hid_device_info *cur_dev, libusb_device_handle *handle, int interface_num, uint16_t expected_report_descriptor_size)
{
unsigned char hid_report[HID_API_MAX_REPORT_DESCRIPTOR_SIZE];
unsigned short page = 0, usage = 0;

int res = hid_get_report_descriptor_libusb(handle, interface_num, expected_report_descriptor_size, hid_report, sizeof(hid_report));
if (res >= 0) {
/* Parse the usage and usage page
out of the report descriptor. */
get_usage(data, res, &page, &usage);
get_usage(hid_report, res, &page, &usage);
}
else
LOG("libusb_control_transfer() for getting the HID report descriptor failed with %d: %s\n", res, libusb_error_name(res));

cur_dev->usage_page = page;
cur_dev->usage = usage;
Expand Down Expand Up @@ -619,7 +634,7 @@ static void invasive_fill_device_info_usage(struct hid_device_info *cur_dev, lib
LOG("Can't release the interface.\n");
}
else
LOG("Can't claim interface %d\n", res);
LOG("Can't claim interface: (%d) %s\n", res, libusb_error_name(res));

#ifdef DETACH_KERNEL_DRIVER
/* Re-attach kernel driver if necessary. */
Expand Down Expand Up @@ -675,7 +690,7 @@ static uint16_t get_report_descriptor_size_from_interface_descriptors(const stru
{
int i = 0;
int found_hid_report_descriptor = 0;
uint16_t result = 4096;
uint16_t result = HID_API_MAX_REPORT_DESCRIPTOR_SIZE;
const unsigned char *extra = intf_desc->extra;
int extra_length = intf_desc->extra_length;

Expand Down Expand Up @@ -943,7 +958,7 @@ static void read_callback(struct libusb_transfer *transfer)
/* Re-submit the transfer object. */
res = libusb_submit_transfer(transfer);
if (res != 0) {
LOG("Unable to submit URB. libusb error code: %d\n", res);
LOG("Unable to submit URB: (%d) %s\n", res, libusb_error_name(res));
dev->shutdown_thread = 1;
dev->transfer_loop_finished = 1;
}
Expand Down Expand Up @@ -981,7 +996,7 @@ static void *read_thread(void *param)
res = libusb_handle_events(usb_context);
if (res < 0) {
/* There was an error. */
LOG("read_thread(): libusb reports error # %d\n", res);
LOG("read_thread(): (%d) %s\n", res, libusb_error_name(res));

/* Break out of this loop only on fatal error.*/
if (res != LIBUSB_ERROR_BUSY &&
Expand Down Expand Up @@ -1036,7 +1051,7 @@ static int hidapi_initialize_device(hid_device *dev, int config_number, const st
if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) {
res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber);
if (res < 0) {
LOG("Unable to detach Kernel Driver\n");
LOG("Unable to detach Kernel Driver: (%d) %s\n", res, libusb_error_name(res));
return 0;
}
else {
Expand All @@ -1047,13 +1062,13 @@ static int hidapi_initialize_device(hid_device *dev, int config_number, const st
#endif
res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber);
if (res < 0) {
LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res);
LOG("can't claim interface %d: (%d) %s\n", intf_desc->bInterfaceNumber, res, libusb_error_name(res));

#ifdef DETACH_KERNEL_DRIVER
if (dev->is_driver_detached) {
res = libusb_attach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber);
if (res < 0)
LOG("Failed to reattach the driver to kernel.\n");
LOG("Failed to reattach the driver to kernel: (%d) %s\n", res, libusb_error_name(res));
}
#endif
return 0;
Expand Down
60 changes: 12 additions & 48 deletions linux/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
struct hid_device_ {
int device_handle;
int blocking;
int uses_numbered_reports;
wchar_t *last_error_str;
struct hid_device_info* device_info;
};
Expand All @@ -88,9 +87,12 @@ static wchar_t *last_global_error_str = NULL;
static hid_device *new_hid_device(void)
{
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
if (dev == NULL) {
return NULL;
}

dev->device_handle = -1;
dev->blocking = 1;
dev->uses_numbered_reports = 0;
dev->last_error_str = NULL;
dev->device_info = NULL;

Expand Down Expand Up @@ -242,34 +244,6 @@ static int get_hid_item_size(__u8 *report_descriptor, unsigned int pos, __u32 si
return 0;
}

/* uses_numbered_reports() returns 1 if report_descriptor describes a device
which contains numbered reports. */
static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) {
unsigned int i = 0;
int data_len, key_size;

while (i < size) {
int key = report_descriptor[i];

/* Check for the Report ID key */
if (key == 0x85/*Report ID*/) {
/* This device has a Report ID, which means it uses
numbered reports. */
return 1;
}

/* Determine data_len and key_size */
if (!get_hid_item_size(report_descriptor, i, size, &data_len, &key_size))
return 0; /* malformed report */

/* Skip over this key and its associated data */
i += data_len + key_size;
}

/* Didn't find a Report ID key. Device doesn't use numbered reports. */
return 0;
}

/*
* Get bytes from a HID Report Descriptor.
* Only call with a num_bytes of 0, 1, 2, or 4.
Expand Down Expand Up @@ -988,32 +962,22 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
/* register_global_error: global error is reset by hid_init */

dev = new_hid_device();
if (!dev) {
register_global_error("Couldn't allocate memory");
return NULL;
}

dev->device_handle = open(path, O_RDWR | O_CLOEXEC);

/* If we have a good handle, return it. */
if (dev->device_handle >= 0) {
/* Get the report descriptor */
int res, desc_size = 0;
struct hidraw_report_descriptor rpt_desc;

memset(&rpt_desc, 0x0, sizeof(rpt_desc));

/* Get Report Descriptor Size */
/* Make sure this is a HIDRAW device - responds to HIDIOCGRDESCSIZE */
res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size);
if (res < 0)
register_device_error_format(dev, "ioctl (GRDESCSIZE): %s", strerror(errno));

/* Get Report Descriptor */
rpt_desc.size = desc_size;
res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc);
if (res < 0) {
register_device_error_format(dev, "ioctl (GRDESC): %s", strerror(errno));
} else {
/* Determine if this device uses numbered reports. */
dev->uses_numbered_reports =
uses_numbered_reports(rpt_desc.value,
rpt_desc.size);
hid_close(dev);
register_device_error_format(dev, "ioctl(GRDESCSIZE) error for '%s', not a HIDRAW device?: %s", path, strerror(errno));
return NULL;
}

return dev;
Expand Down

0 comments on commit 551ea31

Please sign in to comment.