From b98477a7ec667444e35836f6bcdfdf6e863b2efd Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 14 Aug 2015 16:26:25 -0400 Subject: [PATCH] Retrieve through udev the hidraw node associated to the given input device Most gaming mice use a specific HID protocol to configure the device. Allow to retrieve the hidraw node associated to the mouse so we can talk to it. Signed-off-by: Benjamin Tissoires --- configure.ac | 1 + src/Makefile.am | 3 +- src/libratbag-private.h | 2 + src/libratbag.c | 122 +++++++++++++++++++++++++++++++++++++++- tools/Makefile.am | 4 +- 5 files changed, 128 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index a5d1e5f5..6840f642 100644 --- a/configure.ac +++ b/configure.ac @@ -46,6 +46,7 @@ LT_PREREQ([2.2]) LT_INIT PKG_PROG_PKG_CONFIG() +PKG_CHECK_MODULES(LIBUDEV, [libudev]) PKG_CHECK_MODULES(LIBEVDEV, [libevdev >= 0.4]) if test "x$GCC" = "xyes"; then diff --git a/src/Makefile.am b/src/Makefile.am index 1b2e4a7c..21c0d4d5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,10 +11,11 @@ libratbag_la_SOURCES = \ libratbag-private.h \ libratbag-util.h -libratbag_la_LIBADD = $(LIBEVDEV_LIBS) +libratbag_la_LIBADD = $(LIBEVDEV_LIBS) $(LIBUDEV_LIBS) libratbag_la_CFLAGS = -I$(top_srcdir)/include \ $(LIBEVDEV_CFLAGS) \ + $(LIBUDEV_CFLAGS) \ $(GCC_CFLAGS) EXTRA_libratbag_la_DEPENDENCIES = $(srcdir)/libratbag.sym diff --git a/src/libratbag-private.h b/src/libratbag-private.h index c405fd46..7adabbc9 100644 --- a/src/libratbag-private.h +++ b/src/libratbag-private.h @@ -41,6 +41,8 @@ struct ratbag_driver; struct ratbag { char *name; + struct udev_device *udev_device; + struct udev_device *udev_hidraw; int evdev_fd; int refcount; struct ratbag_driver *driver; diff --git a/src/libratbag.c b/src/libratbag.c index 979e84c4..9b777a91 100644 --- a/src/libratbag.c +++ b/src/libratbag.c @@ -24,6 +24,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include "libratbag-util.h" struct libratbag { + struct udev *udev; struct list drivers; int refcount; @@ -81,6 +83,109 @@ log_msg(struct libratbag *libratbag, va_end(args); } +static inline struct udev_device * +udev_device_from_devnode(struct libratbag *libratbag, + struct ratbag *ratbag, + int fd) +{ + struct udev_device *dev; + struct stat st; + size_t count = 0; + struct udev *udev = libratbag->udev; + + if (fstat(fd, &st) < 0) + return NULL; + + dev = udev_device_new_from_devnum(libratbag->udev, 'c', st.st_rdev); + + while (dev && !udev_device_get_is_initialized(dev)) { + udev_device_unref(dev); + msleep(10); + dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev); + + count++; + if (count > 50) { + log_bug_libratbag(libratbag, + "udev device never initialized\n"); + break; + } + } + + return dev; +} + +static struct udev_device * +udev_find_hidraw(struct libratbag *libratbag, struct ratbag *ratbag) +{ + struct udev_enumerate *e; + struct udev_list_entry *entry; + struct udev_device *device; + const char *path, *sysname; + struct udev_device *hid_udev; + struct udev_device *hidraw_udev = NULL; + struct udev *udev = libratbag->udev; + + hid_udev = udev_device_get_parent_with_subsystem_devtype(ratbag->udev_device, "hid", NULL); + + e = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(e, "hidraw"); + udev_enumerate_add_match_parent(e, hid_udev); + udev_enumerate_scan_devices(e); + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { + path = udev_list_entry_get_name(entry); + device = udev_device_new_from_syspath(udev, path); + if (!device) + continue; + + sysname = udev_device_get_sysname(device); + if (strncmp("hidraw", sysname, 6) != 0) { + udev_device_unref(device); + continue; + } + + hidraw_udev = udev_device_ref(device); + + udev_device_unref(device); + goto out; + } + +out: + udev_enumerate_unref(e); + + return hidraw_udev; +} + +static int +ratbag_device_init_udev(struct libratbag *libratbag, struct ratbag *ratbag, int fd) +{ + struct udev_device *udev_device; + struct udev_device *hidraw_udev; + int rc = -ENODEV; + + udev_device = udev_device_from_devnode(libratbag, ratbag, fd); + if (!udev_device) { + log_bug_client(libratbag, "Invalid path %s\n", fd); + return -ENODEV; + } + ratbag->udev_device = udev_device_ref(udev_device); + + hidraw_udev = udev_find_hidraw(libratbag, ratbag); + if (!hidraw_udev) + goto out; + + ratbag->udev_hidraw = udev_device_ref(hidraw_udev); + + log_debug(libratbag, + "%s is associated to '%s'.\n", + udev_device_get_devnode(ratbag->udev_hidraw), "the device"); + + rc = 0; + udev_device_unref(hidraw_udev); +out: + udev_device_unref(udev_device); + return rc; +} + void ratbag_device_init(struct ratbag *rb, int fd) { @@ -155,17 +260,23 @@ ratbag_new_from_fd(struct libratbag *libratbag, int fd) } ratbag_device_init(ratbag, fd); + rc = ratbag_device_init_udev(libratbag, ratbag, fd); + if (rc) + goto out_err; driver = ratbag_find_driver(libratbag, &ids); if (!driver) { errno = ENOTSUP; - goto out_err; + goto err_udev; } ratbag->driver = driver; return ratbag; +err_udev: + udev_device_unref(ratbag->udev_device); + udev_device_unref(ratbag->udev_hidraw); out_err: if (ratbag) free(ratbag->name); @@ -183,6 +294,8 @@ ratbag_unref(struct ratbag *ratbag) ratbag->refcount--; if (ratbag->refcount > 0) return ratbag; + udev_device_unref(ratbag->udev_device); + udev_device_unref(ratbag->udev_hidraw); free(ratbag->name); free(ratbag); @@ -213,6 +326,12 @@ libratbag_create_context(void) libratbag->refcount = 1; list_init(&libratbag->drivers); + libratbag->udev = udev_new(); + if (!libratbag->udev) { + free(libratbag); + return NULL; + } + libratbag->log_handler = libratbag_default_log_func; libratbag->log_priority = LIBRATBAG_LOG_PRIORITY_DEBUG; @@ -239,6 +358,7 @@ libratbag_unref(struct libratbag *libratbag) if (libratbag->refcount > 0) return libratbag; + libratbag->udev = udev_unref(libratbag->udev); free(libratbag); return NULL; diff --git a/tools/Makefile.am b/tools/Makefile.am index b57eec19..19a17c5b 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -7,6 +7,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/include \ -I$(top_builddir)/src # for libratbag-version.h ratbag_command_SOURCES = ratbag-command.c -ratbag_command_LDADD = ../src/libratbag.la +ratbag_command_LDADD = ../src/libratbag.la $(LIBUDEV_LIBS) ratbag_command_LDFLAGS = -no-install -ratbag_command_CFLAGS = +ratbag_command_CFLAGS = $(LIBUDEV_CFLAGS)