From 3605bede480cf7339c093e87794dff0fc70db56e Mon Sep 17 00:00:00 2001 From: Thomas Hindoe Paaboel Andersen Date: Fri, 9 Oct 2015 21:01:47 +0200 Subject: [PATCH] Use udev rules/hwdb to match mice to drivers and SVGs This adds udev rules/hwdb that sets RATBAG_DRIVER and RATBAG_SVG properties to the event devices. We then use this property to select the correct driver instead of checking the hardcoded list in the drivers. Having this in a hwdb makes it easier for users to test if drivers match their mouse with a system installed libratbag. Each driver now has an "id" which is used to match the driver set in RATBAG_DRIVER. A SVG can optionally be set as well. All entries in the hwdb are currently without a name as that information was not available in the hardcoded lists. Note that this patch breaks the test-driver. --- Makefile.am | 2 +- configure.ac | 10 +++ hwdb/70-libratbag-mouse.hwdb | 124 ++++++++++++++++++++++++++++++++++ hwdb/70-libratbag-mouse.rules | 18 +++++ hwdb/Makefile.am | 9 +++ src/driver-etekcity.c | 19 +----- src/driver-hidpp10.c | 42 +----------- src/driver-hidpp20.c | 39 ++--------- src/driver-roccat.c | 15 +--- src/driver-test.c | 23 +------ src/libratbag-private.h | 19 ++---- src/libratbag.c | 65 +++++++----------- 12 files changed, 204 insertions(+), 181 deletions(-) create mode 100644 hwdb/70-libratbag-mouse.hwdb create mode 100644 hwdb/70-libratbag-mouse.rules create mode 100644 hwdb/Makefile.am diff --git a/Makefile.am b/Makefile.am index 05698ba5..bb86c619 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = src doc test tools +SUBDIRS = src doc test tools hwdb ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} diff --git a/configure.ac b/configure.ac index daccb479..9302917c 100644 --- a/configure.ac +++ b/configure.ac @@ -56,6 +56,14 @@ fi AC_SUBST(GCC_CFLAGS) AC_SUBST(GCC_CXXFLAGS) +AC_ARG_WITH(udev-base-dir, AS_HELP_STRING([--with-udev-base-dir=DIR], [where udev base directory is])) +if test -n "$with_udev_base_dir" ; then + UDEV_BASE_DIR="$with_udev_base_dir" +else + UDEV_BASE_DIR="/usr/lib/udev" +fi +AC_SUBST(UDEV_BASE_DIR) + AC_ARG_ENABLE([documentation], [AC_HELP_STRING([--enable-documentation], [Enable building the documentation (default=auto)])], @@ -132,6 +140,7 @@ AM_CONDITIONAL(BUILD_DOCS, [test "x$build_documentation" = "xyes"]) AC_CONFIG_FILES([Makefile doc/Makefile doc/libratbag.doxygen + hwdb/Makefile src/Makefile src/libratbag.pc src/libratbag-version.h @@ -143,6 +152,7 @@ AC_OUTPUT AC_MSG_RESULT([ Prefix ${prefix} + udev base directory: ${UDEV_BASE_DIR} Build documentation ${build_documentation} Build tests ${build_tests} diff --git a/hwdb/70-libratbag-mouse.hwdb b/hwdb/70-libratbag-mouse.hwdb new file mode 100644 index 00000000..0208a756 --- /dev/null +++ b/hwdb/70-libratbag-mouse.hwdb @@ -0,0 +1,124 @@ +# Database for libratbag to select the correct driver to use for gaming mice. +# +# The lookup keys are composed in: +# 70-libratbag-mouse.rules +# +# Match string format: +# mouse::vp:name:: +# +# Supported subsystems: usb, bluetooth +# vid/pid as 4-digit hex lowercase vendor/product +# +# if vid/pid is unavailable, use +# mouse:*:name:: +# if name is unavailable, use +# mouse::vp:* +# +# For example, the following 5 matches all match the same mouse: +# mouse:usb:v17efp6019:name:Lenovo Optical USB Mouse: +# mouse:usb:*:name:Lenovo Optical USB Mouse: +# mouse:usb:v17efp6019:* +# mouse:*:name:Lenovo Optical USB Mouse: +# +# To add local entries, create a new file +# /etc/udev/hwdb.d/71-libratbat-mouse-local.hwdb +# and add your rules there. To load the new rules execute (as root): +# udevadm hwdb --update +# udevadm trigger /dev/input/eventXX +# where /dev/input/eventXX is the mouse in question. If in +# doubt, simply use /dev/input/event* to reload all input rules. +# +# If your changes are generally applicable, preferably send them as a pull +# request to +# https://github.com/libratbag/libratbag +# or create a bug report on https://github.com/libratbag/libratbag/issues and +# include your new rules, a description of the device, and the output of +# udevadm info /dev/input/eventXX +# (or /dev/input/event*). +# +# Allowed properties are: +# RATBAG_DRIVER +# RATBAG_SVG + +# Sort by brand, driver + + +########################################## +# Etekcity +########################################## + +mouse:usb:v1ea7p4011:name:*: + RATBAG_DRIVER=etekcity + RATBAG_SVG=etekcity.svg + +########################################## +# Logitech +########################################## + +# M705 +mouse:usb:v046dp101b:name:*: + RATBAG_DRIVER=hidpp10 + +# M570 +mouse:usb:v046dp1028:name:*: + RATBAG_DRIVER=hidpp10 + +# G5 +mouse:usb:v046dpc041:name:*: + RATBAG_DRIVER=hidpp10 + +# G5 2007 +mouse:usb:v046dpc049:name:*: + RATBAG_DRIVER=hidpp10 + +# G9 +mouse:usb:v046dpc048:name:*: + RATBAG_DRIVER=hidpp10 + +# G9x [Original] +mouse:usb:v046dpc066:name:*: + RATBAG_DRIVER=hidpp10 + +# G9x [Call of Duty MW3 Edition] +mouse:usb:v046dpc249:name:*: + RATBAG_DRIVER=hidpp10 + +# G500 +mouse:usb:v046dpc068:name:*: + RATBAG_DRIVER=hidpp10 + +# G500s +mouse:usb:v046dpc24e:name:*: + RATBAG_DRIVER=hidpp10 + RATBAG_SVG=logitech-g500s.svg + +# G502 over USB +mouse:usb:v046dpc07d:name:*: + RATBAG_DRIVER=hidpp20 + +# MX Master over unifying +mouse:usb:v046dp4041:name:*: +# MX Master over bluetooth +mouse:usb:v046dpb012:name:*: + RATBAG_DRIVER=hidpp20 + RATBAG_SVG=logitech-mx_master.svg + +# T650 over unifying +mouse:usb:v046dp4101:name:*: + RATBAG_DRIVER=hidpp20 + +# M325 over unifying +mouse:usb:v046dp400a:name:*: + RATBAG_DRIVER=hidpp20 + +# G303 over USB +mouse:usb:v046dpc080:name:*: + RATBAG_DRIVER=hidpp20 + +########################################## +# Roccat +########################################## + +mouse:usb:v1e7dp2e22:name:*: + RATBAG_DRIVER=roccat + RATBAG_SVG=roccat-kone-xtd.svg diff --git a/hwdb/70-libratbag-mouse.rules b/hwdb/70-libratbag-mouse.rules new file mode 100644 index 00000000..44e54584 --- /dev/null +++ b/hwdb/70-libratbag-mouse.rules @@ -0,0 +1,18 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="mouse_end" +KERNEL!="event*", GOTO="mouse_end" +ENV{ID_INPUT}=="", GOTO="mouse_end" + +# mouse::vp:name::* +KERNELS=="input*", ENV{ID_BUS}=="usb", \ + IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \ + GOTO="mouse_end" +KERNELS=="input*", ENV{ID_BUS}=="bluetooth", \ + IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \ + GOTO="mouse_end" +DRIVERS=="psmouse", SUBSYSTEMS=="serio", \ + IMPORT{builtin}="hwdb 'mouse:ps2::name:$attr{device/name}:'", \ + GOTO="mouse_end" + +LABEL="mouse_end" diff --git a/hwdb/Makefile.am b/hwdb/Makefile.am new file mode 100644 index 00000000..5b1949a0 --- /dev/null +++ b/hwdb/Makefile.am @@ -0,0 +1,9 @@ +udevrulesdir = $(UDEV_BASE_DIR)/rules.d +udevrules_DATA = 70-libratbag-mouse.rules + +hwdbdir = $(UDEV_BASE_DIR)/hwdb.d +hwdb_DATA = 70-libratbag-mouse.hwdb + +EXTRA_DIST = \ + $(udevrules_DATA) \ + $(hwdb_DATA) diff --git a/src/driver-etekcity.c b/src/driver-etekcity.c index a6a70b37..8447a6cc 100644 --- a/src/driver-etekcity.c +++ b/src/driver-etekcity.c @@ -763,15 +763,13 @@ etekcity_write_resolution_dpi(struct ratbag_resolution *resolution, } static int -etekcity_probe(struct ratbag_device *device, const struct ratbag_id id) +etekcity_probe(struct ratbag_device *device) { int rc; struct ratbag_profile *profile; struct etekcity_data *drv_data; int active_idx; - log_raw(device->ratbag, "data: %d\n", id.data); - rc = ratbag_open_hidraw(device); if (rc) { log_error(device->ratbag, @@ -835,22 +833,9 @@ etekcity_remove(struct ratbag_device *device) free(ratbag_get_drv_data(device)); } -#define USB_VENDOR_ID_ETEKCITY 0x1ea7 - -static const struct ratbag_id etekcity_table[] = { - {.id = { .bustype = BUS_USB, - .vendor = USB_VENDOR_ID_ETEKCITY, - .product = 0x4011, - .version = VERSION_ANY }, - .svg_filename = "etekcity.svg", - }, - - { }, -}; - struct ratbag_driver etekcity_driver = { .name = "EtekCity", - .table_ids = etekcity_table, + .id = "etekcity", .probe = etekcity_probe, .remove = etekcity_remove, .read_profile = etekcity_read_profile, diff --git a/src/driver-hidpp10.c b/src/driver-hidpp10.c index 90d1844f..b7f5d4c4 100644 --- a/src/driver-hidpp10.c +++ b/src/driver-hidpp10.c @@ -198,7 +198,7 @@ hidpp10drv_test_hidraw(struct ratbag_device *device) } static int -hidpp10drv_probe(struct ratbag_device *device, const struct ratbag_id id) +hidpp10drv_probe(struct ratbag_device *device) { int rc; struct hidpp10drv_data *drv_data = NULL; @@ -272,47 +272,9 @@ hidpp10drv_remove(struct ratbag_device *device) free(drv_data); } -#define LOGITECH_DEVICE(_bus, _pid) \ - { .bustype = (_bus), \ - .vendor = USB_VENDOR_ID_LOGITECH, \ - .product = (_pid), \ - .version = VERSION_ANY } - -static const struct ratbag_id hidpp10drv_table[] = { - /* M705 */ - { .id = LOGITECH_DEVICE(BUS_USB, 0x101b) }, - - /* M570 */ - { .id = LOGITECH_DEVICE(BUS_USB, 0x1028) }, - - /* G5 */ - { .id = LOGITECH_DEVICE(BUS_USB, 0xc041) }, - - /* G5 2007 */ - { .id = LOGITECH_DEVICE(BUS_USB, 0xc049) }, - - /* G9 */ - { .id = LOGITECH_DEVICE(BUS_USB, 0xc048) }, - - /* G9x [Original] */ - { .id = LOGITECH_DEVICE(BUS_USB, 0xc066) }, - - /* G9x [Call of Duty MW3 Edition] */ - { .id = LOGITECH_DEVICE(BUS_USB, 0xc249) }, - - /* G500 */ - { .id = LOGITECH_DEVICE(BUS_USB, 0xc068) }, - - /* G500s */ - { .id = LOGITECH_DEVICE(BUS_USB, 0xc24e), - .svg_filename = "logitech-g500s.svg" }, - - { }, -}; - struct ratbag_driver hidpp10_driver = { .name = "Logitech HID++1.0", - .table_ids = hidpp10drv_table, + .id = "hidpp10", .probe = hidpp10drv_probe, .remove = hidpp10drv_remove, .read_profile = hidpp10drv_read_profile, diff --git a/src/driver-hidpp20.c b/src/driver-hidpp20.c index 1cd896e8..3c515354 100644 --- a/src/driver-hidpp20.c +++ b/src/driver-hidpp20.c @@ -406,7 +406,7 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature) } static int -hidpp20drv_20_probe(struct ratbag_device *device, const struct ratbag_id id) +hidpp20drv_20_probe(struct ratbag_device *device) { struct hidpp20_feature *feature_list; int rc, i; @@ -438,7 +438,7 @@ hidpp20drv_test_hidraw(struct ratbag_device *device) } static int -hidpp20drv_probe(struct ratbag_device *device, const struct ratbag_id id) +hidpp20drv_probe(struct ratbag_device *device) { int rc; struct hidpp20drv_data *drv_data; @@ -470,7 +470,7 @@ hidpp20drv_probe(struct ratbag_device *device, const struct ratbag_id id) log_debug(device->ratbag, "'%s' is using protocol v%d.%d\n", ratbag_device_get_name(device), drv_data->proto_major, drv_data->proto_minor); if (drv_data->proto_major >= 2) { - rc = hidpp20drv_20_probe(device, id); + rc = hidpp20drv_20_probe(device); if (rc) goto err; } @@ -497,40 +497,9 @@ hidpp20drv_remove(struct ratbag_device *device) free(drv_data); } -#define USB_VENDOR_ID_LOGITECH 0x046d -#define LOGITECH_DEVICE(_bus, _pid) \ - { .bustype = (_bus), \ - .vendor = USB_VENDOR_ID_LOGITECH, \ - .product = (_pid), \ - .version = VERSION_ANY } - -static const struct ratbag_id hidpp20drv_table[] = { - /* MX Master over unifying */ - { .id = LOGITECH_DEVICE(BUS_USB, 0x4041), - .svg_filename = "logitech-mx_master.svg" }, - - /* MX Master over bluetooth */ - { .id = LOGITECH_DEVICE(BUS_BLUETOOTH, 0xb012), - .svg_filename = "logitech-mx_master.svg" }, - - /* T650 over unifying */ - { .id = LOGITECH_DEVICE(BUS_USB, 0x4101) }, - - /* M325 over unifying */ - { .id = LOGITECH_DEVICE(BUS_USB, 0x400a) }, - - /* G502 over USB */ - { .id = LOGITECH_DEVICE(BUS_USB, 0xc07d) }, - - /* G303 over USB */ - { .id = LOGITECH_DEVICE(BUS_USB, 0xc080) }, - - { }, -}; - struct ratbag_driver hidpp20_driver = { .name = "Logitech HID++2.0", - .table_ids = hidpp20drv_table, + .id = "hidpp20", .probe = hidpp20drv_probe, .remove = hidpp20drv_remove, .read_profile = hidpp20drv_read_profile, diff --git a/src/driver-roccat.c b/src/driver-roccat.c index d114d3c2..f0a4093a 100644 --- a/src/driver-roccat.c +++ b/src/driver-roccat.c @@ -901,7 +901,7 @@ roccat_write_resolution_dpi(struct ratbag_resolution *resolution, } static int -roccat_probe(struct ratbag_device *device, const struct ratbag_id id) +roccat_probe(struct ratbag_device *device) { int rc; struct ratbag_profile *profile; @@ -965,20 +965,9 @@ roccat_remove(struct ratbag_device *device) free(ratbag_get_drv_data(device)); } -static const struct ratbag_id roccat_table[] = { - {.id = { .bustype = BUS_USB, - .vendor = 0x1e7d, - .product = 0x2e22, - .version = VERSION_ANY }, - .svg_filename = "roccat-kone-xtd.svg", - }, - - { }, -}; - struct ratbag_driver roccat_driver = { .name = "Roccat Kone XTD", - .table_ids = roccat_table, + .id = "roccat", .probe = roccat_probe, .remove = roccat_remove, .read_profile = roccat_read_profile, diff --git a/src/driver-test.c b/src/driver-test.c index 3e72c18c..cb10ad83 100644 --- a/src/driver-test.c +++ b/src/driver-test.c @@ -26,17 +26,6 @@ #include "libratbag-private.h" #include "libratbag-test.h" - -static const struct ratbag_id test_table[] = { - {.id = { .bustype = 0x00, - .vendor = 0x00, - .product = 0x00, - .version = 0x00 }, - .data = 1, - }, - { }, -}; - static int test_has_capability(const struct ratbag_device *device, enum ratbag_device_capability cap) @@ -92,17 +81,12 @@ test_write_profile(struct ratbag_profile *profile) } static int -test_probe(struct ratbag_device *device, const struct ratbag_id id) +test_probe(struct ratbag_device *device) { struct ratbag_test_device *test_device; - assert(id.id.bustype == 0x00); - assert(id.id.vendor == 0x00); - assert(id.id.product == 0x00); - assert(id.id.version == 0x00); - assert(id.test_device != NULL); - - test_device = id.test_device; + /* FIXME: after the move to hwdb the test_device is no + * longer passed in. */ ratbag_set_drv_data(device, test_device); ratbag_device_init_profiles(device, @@ -125,7 +109,6 @@ test_remove(struct ratbag_device *device) struct ratbag_driver test_driver = { .name = "Test driver", - .table_ids = test_table, .probe = test_probe, .remove = test_remove, .read_profile = test_read_profile, diff --git a/src/libratbag-private.h b/src/libratbag-private.h index 7b55b53d..0a97e929 100644 --- a/src/libratbag-private.h +++ b/src/libratbag-private.h @@ -59,7 +59,6 @@ struct ratbag { struct ratbag_device { char *name; - const char *svg_name; void *userdata; struct udev_device *udev_device; @@ -79,25 +78,15 @@ struct ratbag_device { struct list link; }; -struct ratbag_id { - struct input_id id; - const char *svg_filename; - unsigned long data; - struct ratbag_test_device *test_device; /**< Test suite only */ -}; - /** * struct ratbag_driver - user space driver for a ratbag device */ struct ratbag_driver { - /** the name of the driver */ + /** A human-readable name of the driver */ char *name; - /** - * A list of devices supported by this driver. The last element - * must be empty to mark the end. - */ - const struct ratbag_id *table_ids; + /** The id of the driver used to match with RATBAG_DRIVER in udev */ + char *id; /** * Callback called while trying to open a device by libratbag. @@ -107,7 +96,7 @@ struct ratbag_driver { * Return -ENODEV to ignore the device and let other drivers * probe the device. Any other error code will stop the probing. */ - int (*probe)(struct ratbag_device *device, const struct ratbag_id id); + int (*probe)(struct ratbag_device *device); /** * Callback called right before the struct ratbag_device is diff --git a/src/libratbag.c b/src/libratbag.c index f66a8b5c..3da09fc0 100644 --- a/src/libratbag.c +++ b/src/libratbag.c @@ -284,15 +284,6 @@ out: return rc; } -static inline bool -ratbag_match_id(const struct input_id *dev_id, const struct input_id *match_id) -{ - return (match_id->bustype == BUS_ANY || match_id->bustype == dev_id->bustype) && - (match_id->vendor == VENDOR_ANY || match_id->vendor == dev_id->vendor) && - (match_id->product == PRODUCT_ANY || match_id->product == dev_id->product) && - (match_id->version == VERSION_ANY || match_id->version == dev_id->version); -} - struct ratbag_driver * ratbag_find_driver(struct ratbag_device *device, const struct input_id *dev_id, @@ -300,44 +291,38 @@ ratbag_find_driver(struct ratbag_device *device, { struct ratbag *ratbag = device->ratbag; struct ratbag_driver *driver; - const struct ratbag_id *matching_id; - struct ratbag_id matched_id; int rc; + const char *driver_name; - matched_id.test_device = test_device; + driver_name = udev_prop_value(device->udev_device, "RATBAG_DRIVER"); + if (!driver_name) + return NULL; list_for_each(driver, &ratbag->drivers, link) { - log_debug(ratbag, "trying driver '%s'\n", driver->name); - matching_id = driver->table_ids; - do { - if (ratbag_match_id(dev_id, &matching_id->id)) { - matched_id.id = *dev_id; - matched_id.data = matching_id->data; - matched_id.svg_filename = matching_id->svg_filename; - device->driver = driver; - rc = driver->probe(device, matched_id); - if (rc == 0) { - if (!ratbag_sanity_check_device(device)) { - return NULL; - } else { - log_debug(ratbag, "driver match found\n"); - device->svg_name = matching_id->svg_filename; - return driver; - } - } + if (!streq(driver->id, driver_name)) + continue; - device->driver = NULL; + device->driver = driver; - if (rc != -ENODEV) - return NULL; + rc = driver->probe(device); + if (rc == 0) { + if (!ratbag_sanity_check_device(device)) { + return NULL; + } else { + log_debug(ratbag, "driver match found: %s\n", driver->name); + return driver; } - matching_id++; - } while (matching_id->id.bustype != 0 || - matching_id->id.vendor != 0 || - matching_id->id.product != 0 || - matching_id->id.version != 0); + } + + device->driver = NULL; + + if (rc != -ENODEV) { + log_error(ratbag, "%s: no hidraw device found", device->name); + return NULL; + } } + log_error(ratbag, "%s: driver specified in hwdb not found: %s\n", device->name, driver_name); return NULL; } @@ -456,7 +441,7 @@ ratbag_device_get_name(const struct ratbag_device* device) LIBRATBAG_EXPORT const char * ratbag_device_get_svg_name(const struct ratbag_device* device) { - return device->svg_name; + return udev_prop_value(device->udev_device, "RATBAG_SVG"); } void @@ -467,7 +452,7 @@ ratbag_register_driver(struct ratbag *ratbag, struct ratbag_driver *driver) return; } - if (!driver->probe || !driver->remove || !driver->table_ids) { + if (!driver->probe || !driver->remove) { log_bug_libratbag(ratbag, "Driver %s is incomplete.\n"); return; }