diff options
Diffstat (limited to 'rcm-server-udev.c')
-rw-r--r-- | rcm-server-udev.c | 332 |
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); +} + |