From 975c91ae54d01f946ea4983337aa0df16b247171 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 7 Oct 2015 17:28:10 -0400 Subject: [PATCH] hidraw: allow opening of sibling devices Some mice have a separate hid interface for configuring the advanced features. It occurs that this is a common enough use case to be handled directly by libratbag-hidraw instead of having a per-device quirk. The idea is to allow opening the sibling hidraw devices during .probe() and to give a matching callback. If the callback succeeds, then the hidraw node will be used. With this, one device might be opened several times, this has to be fixed in a separate commit. Fixes issue #19 Signed-off-by: Benjamin Tissoires --- src/driver-hidpp10.c | 17 ++++++----- src/driver-hidpp20.c | 17 ++++++----- src/libratbag-hidraw.c | 64 ++++++++++++++++++++++++++++++++++++++++++ src/libratbag-hidraw.h | 13 +++++++++ 4 files changed, 97 insertions(+), 14 deletions(-) diff --git a/src/driver-hidpp10.c b/src/driver-hidpp10.c index 6533dfd4..90d1844f 100644 --- a/src/driver-hidpp10.c +++ b/src/driver-hidpp10.c @@ -191,6 +191,12 @@ hidpp10drv_fill_from_profile(struct ratbag_device *device, struct hidpp10_device return 0; } +static int +hidpp10drv_test_hidraw(struct ratbag_device *device) +{ + return ratbag_hidraw_has_report(device, REPORT_ID_SHORT); +} + static int hidpp10drv_probe(struct ratbag_device *device, const struct ratbag_id id) { @@ -198,8 +204,10 @@ hidpp10drv_probe(struct ratbag_device *device, const struct ratbag_id id) struct hidpp10drv_data *drv_data = NULL; struct hidpp10_device *dev = NULL; - rc = ratbag_open_hidraw(device); - if (rc) { + rc = ratbag_find_hidraw(device, hidpp10drv_test_hidraw); + if (rc == -ENODEV) { + return rc; + } else if (rc) { log_error(device->ratbag, "Can't open corresponding hidraw node: '%s' (%d)\n", strerror(-rc), @@ -208,11 +216,6 @@ hidpp10drv_probe(struct ratbag_device *device, const struct ratbag_id id) goto err; } - if (!ratbag_hidraw_has_report(device, REPORT_ID_SHORT)) { - ratbag_close_hidraw(device); - return -ENODEV; - } - drv_data = zalloc(sizeof(*drv_data)); /* We can treat all devices as wired devices here. If we talk to the diff --git a/src/driver-hidpp20.c b/src/driver-hidpp20.c index 3823ea95..21eff1da 100644 --- a/src/driver-hidpp20.c +++ b/src/driver-hidpp20.c @@ -431,14 +431,22 @@ hidpp20drv_20_probe(struct ratbag_device *device, const struct ratbag_id id) } +static int +hidpp20drv_test_hidraw(struct ratbag_device *device) +{ + return ratbag_hidraw_has_report(device, REPORT_ID_LONG); +} + static int hidpp20drv_probe(struct ratbag_device *device, const struct ratbag_id id) { int rc; struct hidpp20drv_data *drv_data; - rc = ratbag_open_hidraw(device); - if (rc) { + rc = ratbag_find_hidraw(device, hidpp20drv_test_hidraw); + if (rc == -ENODEV) { + return rc; + } else if (rc) { log_error(device->ratbag, "Can't open corresponding hidraw node: '%s' (%d)\n", strerror(-rc), @@ -446,11 +454,6 @@ hidpp20drv_probe(struct ratbag_device *device, const struct ratbag_id id) return -ENODEV; } - if (!ratbag_hidraw_has_report(device, REPORT_ID_LONG)) { - ratbag_close_hidraw(device); - return -ENODEV; - } - drv_data = zalloc(sizeof(*drv_data)); ratbag_set_drv_data(device, drv_data); diff --git a/src/libratbag-hidraw.c b/src/libratbag-hidraw.c index ce6839a7..a1f70290 100644 --- a/src/libratbag-hidraw.c +++ b/src/libratbag-hidraw.c @@ -212,6 +212,70 @@ ratbag_open_hidraw(struct ratbag_device *device) return rc; } +int +ratbag_find_hidraw(struct ratbag_device *device, int (*match)(struct ratbag_device *device)) +{ + struct ratbag *ratbag = device->ratbag; + struct udev_enumerate *e; + struct udev_list_entry *entry; + struct udev_device *udev_device; + const char *path, *sysname; + struct udev_device *hid_udev; + struct udev_device *parent_udev; + struct udev *udev = ratbag->udev; + int rc = -ENODEV; + int matched; + + assert(match); + + hid_udev = udev_device_get_parent_with_subsystem_devtype(device->udev_device, "hid", NULL); + + if (!hid_udev) + return -ENODEV; + + if (device->ids.bustype == BUS_USB) + /* using the parent usb_device to match siblings */ + parent_udev = udev_device_get_parent_with_subsystem_devtype(hid_udev, "usb", "usb_device"); + else + parent_udev = hid_udev; + + e = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(e, "hidraw"); + udev_enumerate_add_match_parent(e, parent_udev); + udev_enumerate_scan_devices(e); + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { + path = udev_list_entry_get_name(entry); + udev_device = udev_device_new_from_syspath(udev, path); + if (!udev_device) + continue; + + sysname = udev_device_get_sysname(udev_device); + if (!strneq("hidraw", sysname, 6)) { + udev_device_unref(udev_device); + continue; + } + + rc = ratbag_open_hidraw_node(device, udev_device); + if (rc) + ratbag_close_hidraw(device); + + matched = match(device); + udev_device_unref(udev_device); + + if (matched == 1) { + rc = 0; + goto out; + } + + rc = -ENODEV; + } + +out: + udev_enumerate_unref(e); + + return rc; +} + int ratbag_hidraw_has_report(struct ratbag_device *device, uint8_t report_id) { diff --git a/src/libratbag-hidraw.h b/src/libratbag-hidraw.h index 80d2e24c..00e61dd7 100644 --- a/src/libratbag-hidraw.h +++ b/src/libratbag-hidraw.h @@ -49,6 +49,19 @@ struct ratbag_hidraw { */ int ratbag_open_hidraw(struct ratbag_device *device); +/** + * Find and open the hidraw device associated with the device by using the + * given matching function. + * + * @param device the ratbag device + * @param match the matching test (return 1 if matched, 0 if not) + * + * @return 0 on success or a negative errno on error + */ +int +ratbag_find_hidraw(struct ratbag_device *device, + int (*match)(struct ratbag_device *device)); + /** * Close the hidraw device associated with the device. *