diff --git a/src/driver-hidpp10.c b/src/driver-hidpp10.c index 9ff0ae44..f478bf71 100644 --- a/src/driver-hidpp10.c +++ b/src/driver-hidpp10.c @@ -35,8 +35,11 @@ #include "config.h" +#define USB_VENDOR_ID_LOGITECH 0x046d + #include #include +#include #include #include #include @@ -96,12 +99,93 @@ hidpp10drv_write_profile(struct ratbag_profile *profile) return -1; } +static bool +hidpp10drv_is_unifying_receiver(struct ratbag_device *device) +{ + struct udev_device *hidraw; + struct input_id id; + const char *product; + int rc; + + hidraw = device->udev_hidraw; + /* Note: PRODUCT on the hid device is missing the bustype! */ + product = udev_prop_value(hidraw, "PRODUCT"); + rc = sscanf(product, "%hx/%hx/%hx", &id.vendor, &id.product, &id.version); + if (rc != 3) + return false; + + if (id.vendor == USB_VENDOR_ID_LOGITECH && + (id.product == 0xc52b || id.product == 0xc532)) + return true; + + return false; +} + +static int +hidpp10drv_find_unifying_hidraw(struct ratbag_device *device) +{ + struct udev_device *receiver, *hid, *hidraw; + struct udev *udev; + struct udev_enumerate *e; + struct udev_list_entry *entry; + + hidraw = device->udev_hidraw; + + /* the receiver device is two up */ + hid = udev_device_get_parent_with_subsystem_devtype(hidraw, "hid", NULL); + receiver = udev_device_get_parent_with_subsystem_devtype(hid, "hid", NULL); + if (!receiver) + return -1; /* should not happen */ + + udev = udev_device_get_udev(hidraw); + e = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(e, "hidraw"); + udev_enumerate_add_match_parent(e, receiver); + udev_enumerate_scan_devices(e); + + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { + const char *path, *sysname; + + path = udev_list_entry_get_name(entry); + hidraw = udev_device_new_from_syspath(udev, path); + if (!hidraw) + continue; + + sysname = udev_device_get_sysname(hidraw); + if (!strneq("hidraw", sysname, 6)) { + udev_device_unref(hidraw); + continue; + } + + /* The receiver has multiple hidraw devices, we only want + * the one where the direct parent is the receiver */ + hid = udev_device_get_parent_with_subsystem_devtype(hidraw, "hid", NULL); + if (!streq(udev_device_get_syspath(hid), udev_device_get_syspath(receiver))) + continue; + + ratbag_device_set_hidraw_device(device, hidraw); + + udev_device_unref(hidraw); + goto out; + } + +out: + udev_enumerate_unref(e); + + return 0; +} + static int hidpp10drv_probe(struct ratbag_device *device, const struct ratbag_id id) { int rc; struct hidpp10drv_data *drv_data; + /* check if the device is a unifying receiver first so we can update + * the hidraw path before we open it */ + if (hidpp10drv_is_unifying_receiver(device)) + hidpp10drv_find_unifying_hidraw(device); + rc = ratbag_open_hidraw(device); if (rc) { log_error(device->ratbag, @@ -132,7 +216,6 @@ hidpp10drv_remove(struct ratbag_device *device) free(ratbag_get_drv_data(device)); } -#define USB_VENDOR_ID_LOGITECH 0x046d #define LOGITECH_DEVICE(_bus, _pid) \ { .bustype = (_bus), \ .vendor = USB_VENDOR_ID_LOGITECH, \ diff --git a/src/libratbag-private.h b/src/libratbag-private.h index cefadbe3..d9fedef0 100644 --- a/src/libratbag-private.h +++ b/src/libratbag-private.h @@ -234,6 +234,12 @@ ratbag_profile_get_drv_data(struct ratbag_profile *profile) return profile->drv_data; } +/** + * Override the auto-picked hidraw device. + */ +void +ratbag_device_set_hidraw_device(struct ratbag_device *device, + struct udev_device *hidraw); void log_msg_va(struct ratbag *ratbag, diff --git a/src/libratbag.c b/src/libratbag.c index e857b272..29faa08a 100644 --- a/src/libratbag.c +++ b/src/libratbag.c @@ -140,6 +140,17 @@ ratbag_log_set_handler(struct ratbag *ratbag, ratbag->log_handler = log_handler; } +void +ratbag_device_set_hidraw_device(struct ratbag_device *device, + struct udev_device *hidraw) +{ + udev_device_unref(device->udev_hidraw); + device->udev_hidraw = udev_device_ref(hidraw); + + log_debug(device->ratbag, + "Overriding hidraw device with %s\n", + udev_device_get_syspath(hidraw)); +} static inline struct udev_device * udev_device_from_devnode(struct ratbag *ratbag, int fd) @@ -228,6 +239,7 @@ ratbag_device_init_udev(struct ratbag_device *device, if (!hidraw_udev) goto out; + /* Note: may be overridden later */ device->udev_hidraw = udev_device_ref(hidraw_udev); log_debug(device->ratbag,