summaryrefslogtreecommitdiff
path: root/rcm-server-udev.c
diff options
context:
space:
mode:
Diffstat (limited to 'rcm-server-udev.c')
-rw-r--r--rcm-server-udev.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/rcm-server-udev.c b/rcm-server-udev.c
new file mode 100644
index 0000000..d29b4d9
--- /dev/null
+++ b/rcm-server-udev.c
@@ -0,0 +1,332 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <libudev.h>
+#include <unistd.h>
+#include <string.h>
+#include <systemd/sd-bus.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "utils.h"
+#include "shared.h"
+#include "rcm-server-main.h"
+#include "rcm-server-keymap.h"
+#include "rcm-server-evdev.h"
+#include "rcm-server-udev.h"
+
+static void
+remove_device(struct manager *mgr, struct udev_device *udev)
+{
+ const char *name;
+ struct device *device;
+
+ name = udev_device_get_sysname(udev);
+ printf("Asked to remove device %s\n", name);
+
+ list_for_each_entry(device, &mgr->devices, list) {
+ if (strcmp(device->name, name))
+ continue;
+ list_del(&device->list);
+ sd_bus_emit_object_removed(mgr->bus, device->path);
+ mgr->num_devices--;
+ break;
+ }
+}
+
+static int
+lirc_read(sd_event_source *s, int fd, uint32_t revents, void *userdata)
+{
+ struct device *device = userdata;
+ uint8_t buf[100];
+ ssize_t bytes_read;
+
+ if (fd != device->lirc_fd)
+ fprintf(stderr, "lirc fd mismatch: %i != %i\n", device->lirc_fd, fd);
+
+ if (revents & EPOLLHUP) {
+ fprintf(stderr, "lirc connection closed!\n");
+ close(fd);
+ device->lirc_fd = -1;
+ return 0;
+ }
+
+ if (!(revents & EPOLLIN)) {
+ fprintf(stderr, "unexpected lirc event: %" PRIu32 "\n", revents);
+ return 0;
+ }
+
+ while ((bytes_read = read(fd, buf, sizeof(buf))) > 0)
+ printf("Read %zi bytes from lirc dev\n", bytes_read);
+
+ return 0;
+}
+
+static int
+lirc_setup(struct device *device, const char *path)
+{
+ if (!device)
+ return -EINVAL;
+
+ if (device->lirc_fd >= 0) {
+ printf("Multiple lirc devices!?\n");
+ return 0;
+ }
+
+ device->lirc_fd = open(path, O_RDWR | O_NONBLOCK);
+ if (device->lirc_fd < 0) {
+ printf("Failed to open lirc device %s: %s\n", path, strerror(errno));
+ return -errno;
+ }
+
+ if (sd_event_add_io(device->mgr->event, &device->lirc_ev,
+ device->lirc_fd, EPOLLIN, lirc_read, device) < 0) {
+ printf("Failed to add event source for lirc device %s: %s\n",
+ path, strerror(errno));
+ close(device->lirc_fd);
+ device->lirc_fd = -1;
+ return -errno;
+ }
+
+ return 0;
+}
+
+static void
+add_device(struct manager *mgr, struct udev_device *udev)
+{
+ const char *name;
+ const char *str;
+ char *path;
+ struct device *device;
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *devices, *dev_list_entry;
+ int r;
+
+ name = udev_device_get_sysname(udev);
+ if (asprintf(&path, "/org/gnome/RemoteControlManager/%s", name) < 0) {
+ fprintf(stderr, "asprintf failed: %m\n");
+ return;
+ }
+
+ device = malloc(sizeof(*device));
+ if (!device) {
+ fprintf(stderr, "malloc failed: %m\n");
+ free(path);
+ return;
+ }
+
+ list_init(&device->keymaps);
+ device->mgr = mgr;
+ device->path = path;
+ device->name = device->path + strlen("/org/gnome/RemoteControlManager/");
+ device->evdev_fd = -1;
+ device->lirc_fd = -1;
+ device->error = NULL;
+ device->input_name = NULL;
+ device->driver_name = NULL;
+ device->keymap_name = NULL;
+ device->dev_name = NULL;
+
+ if (keymaps_load(device) < 0) {
+ fprintf(stderr, "failed to load keymaps: %m\n");
+ free(path);
+ free(device);
+ return;
+ }
+
+ enumerate = udev_enumerate_new(mgr->udev);
+ udev_enumerate_add_match_parent(enumerate, udev);
+ udev_enumerate_add_match_sysname(enumerate, "event*");
+ udev_enumerate_add_match_sysname(enumerate, "lirc*");
+ udev_enumerate_scan_devices(enumerate);
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ const char *path;
+ struct udev_device *udev_dev;
+ const char *devnode;
+ const char *subsys;
+
+ path = udev_list_entry_get_name(dev_list_entry);
+ if (!path) {
+ printf("Failed to get udev name\n");
+ continue;
+ }
+
+ udev_dev = udev_device_new_from_syspath(mgr->udev, path);
+ if (!udev_dev) {
+ printf("Failed to create udev device\n");
+ continue;
+ }
+
+ devnode = udev_device_get_devnode(udev_dev);
+ if (!devnode) {
+ printf("Failed to determine udev_dev devnode\n");
+ goto next;
+ }
+
+ subsys = udev_device_get_subsystem(udev_dev);
+ if (!subsys) {
+ printf("Failed to determine udev_dev subsystem\n");
+ goto next;
+ }
+
+ if (!strcmp(subsys, "input")) {
+ r = evdev_setup(device, devnode);
+ if (r < 0) {
+ printf("Failed to setup evdev: %s\n", devnode);
+ device->error = "Error: Failed to setup evdev";
+ goto next;
+ }
+
+ } else if (!strcmp(subsys, "lirc")) {
+ r = lirc_setup(device, devnode);
+ if (r < 0) {
+ printf("Failed to setup lirc: %s\n", devnode);
+ device->error = "Error: Failed to setup lirc";
+ goto next;
+ }
+
+ } else {
+ printf("Unknown subsystem, ignored %s\n", devnode);
+ goto next;
+ }
+
+next:
+ udev_device_unref(udev_dev);
+ }
+ udev_enumerate_unref(enumerate);
+
+ str = udev_device_get_sysattr_value(udev, "uevent");
+ if (str) {
+ char tmp[strlen(str) + 1];
+ char *token;
+
+ strcpy(tmp, str);
+
+ for (token = strtok(tmp, "\n"); token; token = strtok(NULL, "\n")) {
+ if (!strncmp(token, "DRV_NAME=", strlen("DRV_NAME=")))
+ device->driver_name = strdup(token + strlen("DRV_NAME="));
+ else if (!strncmp(token, "NAME=", strlen("NAME=")))
+ device->keymap_name = strdup(token + strlen("NAME="));
+ else if (!strncmp(token, "DEVNAME=", strlen("DEVNAME=")))
+ device->dev_name = strdup(token + strlen("DEVNAME="));
+ else if (!strncmp(token, "MAJOR=", strlen("MAJOR=")))
+ continue;
+ else if (!strncmp(token, "MINOR=", strlen("MINOR=")))
+ continue;
+ else
+ printf("Unused uevent: %s\n", token);
+ }
+ }
+
+ printf("Adding Device Object\n");
+ printf("\tPath : %s\n", udev_device_get_syspath(udev));
+ printf("\tNode : %s\n", udev_device_get_devnode(udev));
+ printf("\tSubsystem : %s\n", udev_device_get_subsystem(udev));
+ printf("\tDevtype : %s\n", udev_device_get_devtype(udev));
+ printf("\tAction : %s\n", udev_device_get_action(udev));
+ printf("\tName : %s\n", device->name);
+ printf("\tInput name: %s\n", device->input_name);
+ printf("\tDriver : %s\n", device->driver_name);
+ printf("\tKernel map: %s\n", device->keymap_name);
+ printf("\tDev name : %s\n", device->dev_name);
+ printf("\tevdev fd : %i\n", device->evdev_fd);
+ printf("\tLIRC fd : %i\n", device->lirc_fd);
+ printf("\tDBUS path : %s\n", device->path);
+ printf("\tProtocols : %s\n",
+ udev_device_get_sysattr_value(udev, "protocols"));
+
+ list_add(&device->list, &mgr->devices);
+ mgr->num_devices++;
+
+ sd_bus_emit_object_added(mgr->bus, path);
+}
+
+static int
+udev_read(sd_event_source *s, int fd, uint32_t revents, void *userdata)
+{
+ struct manager *mgr = userdata;
+ struct udev_device *dev;
+
+ if (revents & EPOLLHUP) {
+ fprintf(stderr, "udev connection closed!\n");
+ return 0;
+ }
+
+ if (!(revents & EPOLLIN)) {
+ fprintf(stderr, "unexpected udev event: %" PRIu32 "\n", revents);
+ return 0;
+ }
+
+ while ((dev = udev_monitor_receive_device(mgr->udev_mon))) {
+ printf("Read device: %s\n", udev_device_get_syspath(dev));
+ if (!strcmp(udev_device_get_action(dev), "add"))
+ add_device(mgr, dev);
+ else if (!strcmp(udev_device_get_action(dev), "remove"))
+ remove_device(mgr, dev);
+ udev_device_unref(dev);
+ }
+
+ return 1;
+}
+
+int
+udev_setup(struct manager *mgr)
+{
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *devices, *dev_list_entry;
+ struct udev_device *dev;
+
+ mgr->udev = udev_new();
+ if (!mgr->udev)
+ return -ENOMEM;
+
+ mgr->udev_mon = udev_monitor_new_from_netlink(mgr->udev, "udev");
+ udev_monitor_filter_add_match_subsystem_devtype(mgr->udev_mon, "rc", NULL);
+ udev_monitor_enable_receiving(mgr->udev_mon);
+
+ enumerate = udev_enumerate_new(mgr->udev);
+ udev_enumerate_add_match_subsystem(enumerate, "rc");
+ udev_enumerate_scan_devices(enumerate);
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ const char *path;
+
+ path = udev_list_entry_get_name(dev_list_entry);
+ if (!path) {
+ printf("Failed to get udev name\n");
+ continue;
+ }
+
+ dev = udev_device_new_from_syspath(mgr->udev, path);
+ if (!dev) {
+ printf("Failed to create udev device\n");
+ continue;
+ }
+
+ add_device(mgr, dev);
+
+ udev_device_unref(dev);
+ }
+ udev_enumerate_unref(enumerate);
+
+ return sd_event_add_io(mgr->event, &mgr->udev_ev,
+ udev_monitor_get_fd(mgr->udev_mon),
+ EPOLLIN, udev_read, mgr);
+}
+
+void
+udev_close(struct manager *mgr)
+{
+ if (!mgr)
+ return;
+
+ udev_monitor_unref(mgr->udev_mon);
+ udev_unref(mgr->udev);
+}
+