From 26c534cc742dfdbb14a889287f7771063be834cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Sat, 14 Dec 2019 21:13:22 +0000 Subject: [PATCH] hidpp20: check supported report types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #854 Signed-off-by: Filipe LaĆ­ns --- src/driver-hidpp20.c | 2 +- src/hidpp-generic.c | 28 ++++++++++++++++++++++++++++ src/hidpp-generic.h | 15 +++++++++++++++ src/hidpp20.c | 18 +++++++++++++++++- src/hidpp20.h | 3 ++- tools/hidpp20-dump-page.c | 2 +- tools/hidpp20-reset.c | 2 +- 7 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/driver-hidpp20.c b/src/driver-hidpp20.c index 5d04c5cb..9d58d385 100644 --- a/src/driver-hidpp20.c +++ b/src/driver-hidpp20.c @@ -1575,7 +1575,7 @@ hidpp20drv_probe(struct ratbag_device *device) * If there is a special need like for G900, we can add this in the * device data file. */ - dev = hidpp20_device_new(&base, device_idx); + dev = hidpp20_device_new(&base, device_idx, (struct hidpp_hid_report*) device->hidraw[0].reports, device->hidraw[0].num_reports); if (!dev) { rc = -ENODEV; goto err; diff --git a/src/hidpp-generic.c b/src/hidpp-generic.c index 8977be8e..e66fe0cd 100644 --- a/src/hidpp-generic.c +++ b/src/hidpp-generic.c @@ -306,6 +306,33 @@ hidpp_read_response(struct hidpp_device *dev, uint8_t *buf, size_t size) return rc >= 0 ? rc : -errno; } +void +hidpp_get_supported_report_types(struct hidpp_device *dev, struct hidpp_hid_report *reports, unsigned int num_reports) +{ + if (!reports) { + hidpp_log_debug(dev, "hidpp: we don't have information about the hid reports, ignoring checks\n"); + dev->supported_report_types = 0xffff; + return; + } + + /* reset the bits we are gonna check */ + dev->supported_report_types &= (0xff & ~HIDPP_REPORT_SHORT); + dev->supported_report_types &= (0xff & ~HIDPP_REPORT_LONG); + + for (unsigned i = 0; i < num_reports; i++) + if ((reports[i].usage_page & 0xff00) == 0xff00) /* vendor defined usage page (0xff00-0xffff) */ + switch (reports[i].report_id) { + case REPORT_ID_SHORT: + hidpp_log_debug(dev, "hidpp: device supports short reports\n"); + dev->supported_report_types |= HIDPP_REPORT_SHORT; + break; + case REPORT_ID_LONG: + hidpp_log_debug(dev, "hidpp: device supports long reports\n"); + dev->supported_report_types |= HIDPP_REPORT_LONG; + break; + } +} + void hidpp_log(struct hidpp_device *dev, enum hidpp_log_priority priority, @@ -363,6 +390,7 @@ hidpp_device_init(struct hidpp_device *dev, int fd) { dev->hidraw_fd = fd; hidpp_device_set_log_handler(dev, simple_log, HIDPP_LOG_PRIORITY_INFO, NULL); + dev->supported_report_types = 0; } void diff --git a/src/hidpp-generic.h b/src/hidpp-generic.h index 113dbc36..7a818e44 100644 --- a/src/hidpp-generic.h +++ b/src/hidpp-generic.h @@ -95,13 +95,23 @@ typedef void (*hidpp_log_handler)(void *userdata, const char *format, va_list args) __attribute__ ((format (printf, 3, 0))); +struct hidpp_hid_report { + unsigned int report_id; + unsigned int usage_page; + unsigned int usage; +}; + struct hidpp_device { int hidraw_fd; void *userdata; hidpp_log_handler log_handler; enum hidpp_log_priority log_priority; + uint8_t supported_report_types; }; +#define HIDPP_REPORT_SHORT (1 << 0) +#define HIDPP_REPORT_LONG (1 << 1) + void hidpp_device_init(struct hidpp_device *dev, int fd); void @@ -140,6 +150,11 @@ hidpp_write_command(struct hidpp_device *dev, uint8_t *cmd, int size); int hidpp_read_response(struct hidpp_device *dev, uint8_t *buf, size_t size); +void +hidpp_get_supported_report_types(struct hidpp_device *dev, + struct hidpp_hid_report *reports, + unsigned int num_reports); + void hidpp_log(struct hidpp_device *dev, enum hidpp_log_priority priority, diff --git a/src/hidpp20.c b/src/hidpp20.c index 685d6f08..baafceaa 100644 --- a/src/hidpp20.c +++ b/src/hidpp20.c @@ -133,6 +133,16 @@ hidpp20_request_command_allow_error(struct hidpp20_device *device, union hidpp20 } msg->msg.address |= DEVICE_SW_ID; + /* some mice don't support short reports */ + if (msg->msg.report_id == REPORT_ID_SHORT && !(device->base.supported_report_types & HIDPP_REPORT_SHORT)) + msg->msg.report_id = REPORT_ID_LONG; + + /* sanity check */ + if (msg->msg.report_id == REPORT_ID_LONG && !(device->base.supported_report_types & HIDPP_REPORT_LONG)) { + hidpp_log_error(&device->base, "hidpp20: trying to use unsupported report type\n"); + return -EINVAL; + } + msg_len = msg->msg.report_id == REPORT_ID_SHORT ? SHORT_MESSAGE_LENGTH : LONG_MESSAGE_LENGTH; /* Send the message to the Device */ @@ -2871,7 +2881,7 @@ hidpp20_onboard_profiles_get_code_from_special(enum ratbag_button_action_special /* -------------------------------------------------------------------------- */ struct hidpp20_device * -hidpp20_device_new(const struct hidpp_device *base, unsigned int idx) +hidpp20_device_new(const struct hidpp_device *base, unsigned int idx, struct hidpp_hid_report *reports, unsigned int num_reports) { struct hidpp20_device *dev; int rc; @@ -2884,6 +2894,12 @@ hidpp20_device_new(const struct hidpp_device *base, unsigned int idx) dev->proto_major = 1; dev->proto_minor = 0; + hidpp_get_supported_report_types(&(dev->base), reports, num_reports); + + if (!(dev->base.supported_report_types & HIDPP_REPORT_SHORT) && + !(dev->base.supported_report_types & HIDPP_REPORT_LONG)) + goto err; + rc = hidpp20_root_get_protocol_version(dev, &dev->proto_major, &dev->proto_minor); if (rc) { /* communication error, best to ignore the device */ diff --git a/src/hidpp20.h b/src/hidpp20.h index bb1d3b0c..aa7bc30e 100644 --- a/src/hidpp20.h +++ b/src/hidpp20.h @@ -80,7 +80,8 @@ const char *hidpp20_get_quirk_string(enum hidpp20_quirk quirk); /* -------------------------------------------------------------------------- */ struct hidpp20_device * -hidpp20_device_new(const struct hidpp_device *base, unsigned int idx); +hidpp20_device_new(const struct hidpp_device *base, unsigned int idx, + struct hidpp_hid_report *reports, unsigned int num_reports); void hidpp20_device_destroy(struct hidpp20_device *device); diff --git a/tools/hidpp20-dump-page.c b/tools/hidpp20-dump-page.c index 7ac88668..76ccd3d1 100644 --- a/tools/hidpp20-dump-page.c +++ b/tools/hidpp20-dump-page.c @@ -115,7 +115,7 @@ main(int argc, char **argv) error(1, errno, "Failed to open path %s", path); hidpp_device_init(&base, fd); - dev = hidpp20_device_new(&base, 0xff); + dev = hidpp20_device_new(&base, 0xff, NULL, 0); if (!dev) error(1, 0, "Failed to open %s as a HID++ 2.0 device", path); diff --git a/tools/hidpp20-reset.c b/tools/hidpp20-reset.c index 1a579fa3..f31f11e3 100644 --- a/tools/hidpp20-reset.c +++ b/tools/hidpp20-reset.c @@ -91,7 +91,7 @@ main(int argc, char **argv) error(1, errno, "Failed to open path %s", path); hidpp_device_init(&base, fd); - dev = hidpp20_device_new(&base, 0xff); + dev = hidpp20_device_new(&base, 0xff, NULL, 0); if (!dev) error(1, 0, "Failed to open %s as a HID++ 2.0 device", path);