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.
This commit is contained in:
Thomas Hindoe Paaboel Andersen
2015-10-09 21:01:47 +02:00
parent 5278d43429
commit 3605bede48
12 changed files with 204 additions and 181 deletions

View File

@@ -1,4 +1,4 @@
SUBDIRS = src doc test tools SUBDIRS = src doc test tools hwdb
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}

View File

@@ -56,6 +56,14 @@ fi
AC_SUBST(GCC_CFLAGS) AC_SUBST(GCC_CFLAGS)
AC_SUBST(GCC_CXXFLAGS) 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_ARG_ENABLE([documentation],
[AC_HELP_STRING([--enable-documentation], [AC_HELP_STRING([--enable-documentation],
[Enable building the documentation (default=auto)])], [Enable building the documentation (default=auto)])],
@@ -132,6 +140,7 @@ AM_CONDITIONAL(BUILD_DOCS, [test "x$build_documentation" = "xyes"])
AC_CONFIG_FILES([Makefile AC_CONFIG_FILES([Makefile
doc/Makefile doc/Makefile
doc/libratbag.doxygen doc/libratbag.doxygen
hwdb/Makefile
src/Makefile src/Makefile
src/libratbag.pc src/libratbag.pc
src/libratbag-version.h src/libratbag-version.h
@@ -143,6 +152,7 @@ AC_OUTPUT
AC_MSG_RESULT([ AC_MSG_RESULT([
Prefix ${prefix} Prefix ${prefix}
udev base directory: ${UDEV_BASE_DIR}
Build documentation ${build_documentation} Build documentation ${build_documentation}
Build tests ${build_tests} Build tests ${build_tests}

View File

@@ -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:<subsystem>:v<vid>p<pid>:name:<name>:
#
# Supported subsystems: usb, bluetooth
# vid/pid as 4-digit hex lowercase vendor/product
#
# if vid/pid is unavailable, use
# mouse:*:name:<name>:
# if name is unavailable, use
# mouse:<subsystem>:v<vid>p<pid>:*
#
# 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

View File

@@ -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:<subsystem>:v<vid>p<pid>:name:<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"

9
hwdb/Makefile.am Normal file
View File

@@ -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)

View File

@@ -763,15 +763,13 @@ etekcity_write_resolution_dpi(struct ratbag_resolution *resolution,
} }
static int static int
etekcity_probe(struct ratbag_device *device, const struct ratbag_id id) etekcity_probe(struct ratbag_device *device)
{ {
int rc; int rc;
struct ratbag_profile *profile; struct ratbag_profile *profile;
struct etekcity_data *drv_data; struct etekcity_data *drv_data;
int active_idx; int active_idx;
log_raw(device->ratbag, "data: %d\n", id.data);
rc = ratbag_open_hidraw(device); rc = ratbag_open_hidraw(device);
if (rc) { if (rc) {
log_error(device->ratbag, log_error(device->ratbag,
@@ -835,22 +833,9 @@ etekcity_remove(struct ratbag_device *device)
free(ratbag_get_drv_data(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 = { struct ratbag_driver etekcity_driver = {
.name = "EtekCity", .name = "EtekCity",
.table_ids = etekcity_table, .id = "etekcity",
.probe = etekcity_probe, .probe = etekcity_probe,
.remove = etekcity_remove, .remove = etekcity_remove,
.read_profile = etekcity_read_profile, .read_profile = etekcity_read_profile,

View File

@@ -198,7 +198,7 @@ hidpp10drv_test_hidraw(struct ratbag_device *device)
} }
static int static int
hidpp10drv_probe(struct ratbag_device *device, const struct ratbag_id id) hidpp10drv_probe(struct ratbag_device *device)
{ {
int rc; int rc;
struct hidpp10drv_data *drv_data = NULL; struct hidpp10drv_data *drv_data = NULL;
@@ -272,47 +272,9 @@ hidpp10drv_remove(struct ratbag_device *device)
free(drv_data); 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 = { struct ratbag_driver hidpp10_driver = {
.name = "Logitech HID++1.0", .name = "Logitech HID++1.0",
.table_ids = hidpp10drv_table, .id = "hidpp10",
.probe = hidpp10drv_probe, .probe = hidpp10drv_probe,
.remove = hidpp10drv_remove, .remove = hidpp10drv_remove,
.read_profile = hidpp10drv_read_profile, .read_profile = hidpp10drv_read_profile,

View File

@@ -406,7 +406,7 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
} }
static int 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; struct hidpp20_feature *feature_list;
int rc, i; int rc, i;
@@ -438,7 +438,7 @@ hidpp20drv_test_hidraw(struct ratbag_device *device)
} }
static int static int
hidpp20drv_probe(struct ratbag_device *device, const struct ratbag_id id) hidpp20drv_probe(struct ratbag_device *device)
{ {
int rc; int rc;
struct hidpp20drv_data *drv_data; 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); 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) { if (drv_data->proto_major >= 2) {
rc = hidpp20drv_20_probe(device, id); rc = hidpp20drv_20_probe(device);
if (rc) if (rc)
goto err; goto err;
} }
@@ -497,40 +497,9 @@ hidpp20drv_remove(struct ratbag_device *device)
free(drv_data); 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 = { struct ratbag_driver hidpp20_driver = {
.name = "Logitech HID++2.0", .name = "Logitech HID++2.0",
.table_ids = hidpp20drv_table, .id = "hidpp20",
.probe = hidpp20drv_probe, .probe = hidpp20drv_probe,
.remove = hidpp20drv_remove, .remove = hidpp20drv_remove,
.read_profile = hidpp20drv_read_profile, .read_profile = hidpp20drv_read_profile,

View File

@@ -901,7 +901,7 @@ roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
} }
static int static int
roccat_probe(struct ratbag_device *device, const struct ratbag_id id) roccat_probe(struct ratbag_device *device)
{ {
int rc; int rc;
struct ratbag_profile *profile; struct ratbag_profile *profile;
@@ -965,20 +965,9 @@ roccat_remove(struct ratbag_device *device)
free(ratbag_get_drv_data(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 = { struct ratbag_driver roccat_driver = {
.name = "Roccat Kone XTD", .name = "Roccat Kone XTD",
.table_ids = roccat_table, .id = "roccat",
.probe = roccat_probe, .probe = roccat_probe,
.remove = roccat_remove, .remove = roccat_remove,
.read_profile = roccat_read_profile, .read_profile = roccat_read_profile,

View File

@@ -26,17 +26,6 @@
#include "libratbag-private.h" #include "libratbag-private.h"
#include "libratbag-test.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 static int
test_has_capability(const struct ratbag_device *device, test_has_capability(const struct ratbag_device *device,
enum ratbag_device_capability cap) enum ratbag_device_capability cap)
@@ -92,17 +81,12 @@ test_write_profile(struct ratbag_profile *profile)
} }
static int 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; struct ratbag_test_device *test_device;
assert(id.id.bustype == 0x00); /* FIXME: after the move to hwdb the test_device is no
assert(id.id.vendor == 0x00); * longer passed in. */
assert(id.id.product == 0x00);
assert(id.id.version == 0x00);
assert(id.test_device != NULL);
test_device = id.test_device;
ratbag_set_drv_data(device, test_device); ratbag_set_drv_data(device, test_device);
ratbag_device_init_profiles(device, ratbag_device_init_profiles(device,
@@ -125,7 +109,6 @@ test_remove(struct ratbag_device *device)
struct ratbag_driver test_driver = { struct ratbag_driver test_driver = {
.name = "Test driver", .name = "Test driver",
.table_ids = test_table,
.probe = test_probe, .probe = test_probe,
.remove = test_remove, .remove = test_remove,
.read_profile = test_read_profile, .read_profile = test_read_profile,

View File

@@ -59,7 +59,6 @@ struct ratbag {
struct ratbag_device { struct ratbag_device {
char *name; char *name;
const char *svg_name;
void *userdata; void *userdata;
struct udev_device *udev_device; struct udev_device *udev_device;
@@ -79,25 +78,15 @@ struct ratbag_device {
struct list link; 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 - user space driver for a ratbag device
*/ */
struct ratbag_driver { struct ratbag_driver {
/** the name of the driver */ /** A human-readable name of the driver */
char *name; char *name;
/** /** The id of the driver used to match with RATBAG_DRIVER in udev */
* A list of devices supported by this driver. The last element char *id;
* must be empty to mark the end.
*/
const struct ratbag_id *table_ids;
/** /**
* Callback called while trying to open a device by libratbag. * 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 * Return -ENODEV to ignore the device and let other drivers
* probe the device. Any other error code will stop the probing. * 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 * Callback called right before the struct ratbag_device is

View File

@@ -284,15 +284,6 @@ out:
return rc; 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 * struct ratbag_driver *
ratbag_find_driver(struct ratbag_device *device, ratbag_find_driver(struct ratbag_device *device,
const struct input_id *dev_id, const struct input_id *dev_id,
@@ -300,44 +291,38 @@ ratbag_find_driver(struct ratbag_device *device,
{ {
struct ratbag *ratbag = device->ratbag; struct ratbag *ratbag = device->ratbag;
struct ratbag_driver *driver; struct ratbag_driver *driver;
const struct ratbag_id *matching_id;
struct ratbag_id matched_id;
int rc; 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) { list_for_each(driver, &ratbag->drivers, link) {
log_debug(ratbag, "trying driver '%s'\n", driver->name); if (!streq(driver->id, driver_name))
matching_id = driver->table_ids; continue;
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;
}
}
device->driver = NULL; device->driver = driver;
if (rc != -ENODEV) rc = driver->probe(device);
return NULL; 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 || device->driver = NULL;
matching_id->id.product != 0 ||
matching_id->id.version != 0); 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; return NULL;
} }
@@ -456,7 +441,7 @@ ratbag_device_get_name(const struct ratbag_device* device)
LIBRATBAG_EXPORT const char * LIBRATBAG_EXPORT const char *
ratbag_device_get_svg_name(const struct ratbag_device* device) ratbag_device_get_svg_name(const struct ratbag_device* device)
{ {
return device->svg_name; return udev_prop_value(device->udev_device, "RATBAG_SVG");
} }
void void
@@ -467,7 +452,7 @@ ratbag_register_driver(struct ratbag *ratbag, struct ratbag_driver *driver)
return; return;
} }
if (!driver->probe || !driver->remove || !driver->table_ids) { if (!driver->probe || !driver->remove) {
log_bug_libratbag(ratbag, "Driver %s is incomplete.\n"); log_bug_libratbag(ratbag, "Driver %s is incomplete.\n");
return; return;
} }