Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fill in the Usage Page and Usage on Linux with hidraw #1

Merged
merged 2 commits into from
Apr 4, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 144 additions & 15 deletions linux/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,128 @@ static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) {
return 0;
}

/*
* Get bytes from a HID Report Descriptor.
* Only call with a num_bytes of 0, 1, 2, or 4.
*/
static __u32 get_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_t cur)
{
/* Return if there aren't enough bytes. */
if (cur + num_bytes >= len)
return 0;

if (num_bytes == 0)
return 0;
else if (num_bytes == 1)
return rpt[cur + 1];
else if (num_bytes == 2)
return (rpt[cur + 2] * 256 + rpt[cur + 1]);
else if (num_bytes == 4)
return (rpt[cur + 4] * 0x01000000 +
rpt[cur + 3] * 0x00010000 +
rpt[cur + 2] * 0x00000100 +
rpt[cur + 1] * 0x00000001);
else
return 0;
}

/*
* Retrieves the device's Usage Page and Usage from the report
* descriptor. The algorithm is simple, as it just returns the first
* Usage and Usage Page that it finds in the descriptor.
* The return value is 0 on success and -1 on failure.
*/
static int get_usage(__u8 *report_descriptor, size_t size, unsigned short *usage_page, unsigned short *usage)
{
int i = 0;
int size_code;
int data_len, key_size;
int usage_found = 0, usage_page_found = 0;

while (i < size) {
int key = report_descriptor[i];
int key_cmd = key & 0xfc;

if ((key & 0xf0) == 0xf0) {
/*
* This is a Long Item. The next byte contains the
* length of the data section (value) for this key.
* See the HID specification, version 1.11, section
* 6.2.2.3, titled "Long Items."
*/
if (i + 1 < size)
data_len = report_descriptor[i + 1];
else
data_len = 0; /* malformed report */
key_size = 3;
}
else {
/*
* This is a Short Item. The bottom two bits of the
* key contain the size code for the data section
* (value) for this key. Refer to the HID
* specification, version 1.11, section 6.2.2.2,
* titled "Short Items."
*/
size_code = key & 0x3;
switch (size_code) {
case 0:
case 1:
case 2:
data_len = size_code;
break;
case 3:
data_len = 4;
break;
default:
/* Can't ever happen since size_code is & 0x3 */
data_len = 0;
break;
};
key_size = 1;
}

if (key_cmd == 0x4) {
*usage_page = get_bytes(report_descriptor, size, data_len, i);
usage_page_found = 1;
}
if (key_cmd == 0x8) {
*usage = get_bytes(report_descriptor, size, data_len, i);
usage_found = 1;
}

if (usage_page_found && usage_found)
return 0; /* success */

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

return -1; /* failure */
}

static int get_report_descriptor(int device_handle, struct hidraw_report_descriptor *rpt_desc)
{
int res, desc_size = 0;

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

/* Get Report Descriptor Size */
res = ioctl(device_handle, HIDIOCGRDESCSIZE, &desc_size);
if (res < 0) {
perror("HIDIOCGRDESCSIZE");
return res;
}

/* Get Report Descriptor */
rpt_desc->size = desc_size;
res = ioctl(device_handle, HIDIOCGRDESC, rpt_desc);
if (res < 0)
perror("HIDIOCGRDESC");

return res;
}

/*
* The caller is responsible for free()ing the (newly-allocated) character
* strings pointed to by serial_number_utf8 and product_name_utf8 after use.
Expand Down Expand Up @@ -523,6 +645,25 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]);
cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]);

/* Usage Page and Usage */
int res;
struct hidraw_report_descriptor rpt_desc;
int device_handle = open(dev_path, O_RDWR);
if (device_handle > 0) {
res = get_report_descriptor(device_handle, &rpt_desc);
if (res >= 0) {
unsigned short page = 0, usage = 0;
/*
* Parse the usage and usage page
* out of the report descriptor.
*/
get_usage(rpt_desc.value, rpt_desc.size, &page, &usage);
cur_dev->usage_page = page;
cur_dev->usage = usage;
}
close(device_handle);
}

/* Release Number */
str = udev_device_get_sysattr_value(usb_dev, "bcdDevice");
cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0;
Expand Down Expand Up @@ -632,23 +773,11 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
if (dev->device_handle > 0) {

/* Get the report descriptor */
int res, desc_size = 0;
int res;
struct hidraw_report_descriptor rpt_desc;

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

/* Get Report Descriptor Size */
res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size);
if (res < 0)
perror("HIDIOCGRDESCSIZE");


/* Get Report Descriptor */
rpt_desc.size = desc_size;
res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc);
if (res < 0) {
perror("HIDIOCGRDESC");
} else {
res = get_report_descriptor(dev->device_handle, &rpt_desc);
if (res >= 0) {
/* Determine if this device uses numbered reports. */
dev->uses_numbered_reports =
uses_numbered_reports(rpt_desc.value,
Expand Down