/* * Copyright © 2009 Julien Cristau * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Author: Julien Cristau <jcristau@debian.org> */ #ifdef HAVE_DIX_CONFIG_H #include <dix-config.h> #endif #include <libudev.h> #include "input.h" #include "inputstr.h" #include "hotplug.h" #include "config-backends.h" #include "os.h" #define UDEV_XKB_PROP_KEY "xkb" static struct udev_monitor *udev_monitor; static void device_added(struct udev_device *udev_device) { const char *path, *name = NULL; char *config_info = NULL; const char *syspath; const char *key, *value, *tmp; InputOption *options = NULL, *tmpo; InputAttributes attrs = {}; DeviceIntPtr dev = NULL; struct udev_list_entry *set, *entry; struct udev_device *parent; int rc; path = udev_device_get_devnode(udev_device); syspath = udev_device_get_syspath(udev_device); if (!path || !syspath) return; if (!udev_device_get_property_value(udev_device, "ID_INPUT")) return; options = xcalloc(sizeof(*options), 1); if (!options) return; options->key = xstrdup("_source"); options->value = xstrdup("server/udev"); if (!options->key || !options->value) goto unwind; parent = udev_device_get_parent(udev_device); if (parent) name = udev_device_get_property_value(parent, "NAME"); if (!name) name = "(unnamed)"; else attrs.product = name; add_option(&options, "name", name); add_option(&options, "path", path); add_option(&options, "device", path); attrs.device = path; config_info = Xprintf("udev:%s", syspath); if (!config_info) goto unwind; if (device_is_duplicate(config_info)) { LogMessage(X_WARNING, "config/udev: device %s already added. " "Ignoring.\n", name); goto unwind; } set = udev_device_get_properties_list_entry(udev_device); udev_list_entry_foreach(entry, set) { key = udev_list_entry_get_name(entry); if (!key) continue; value = udev_list_entry_get_value(entry); if (!strncasecmp(key, UDEV_XKB_PROP_KEY, sizeof(UDEV_XKB_PROP_KEY) - 1)) { tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1; if (!strcasecmp(tmp, "rules")) add_option(&options, "xkb_rules", value); else if (!strcasecmp(tmp, "layout")) add_option(&options, "xkb_layout", value); else if (!strcasecmp(tmp, "variant")) add_option(&options, "xkb_variant", value); else if (!strcasecmp(tmp, "model")) add_option(&options, "xkb_model", value); else if (!strcasecmp(tmp, "options")) add_option(&options, "xkb_options", value); } else if (!strcmp(key, "ID_VENDOR")) { attrs.vendor = value; } else if (!strcmp(key, "ID_INPUT_KEY")) { attrs.flags |= ATTR_KEYBOARD; } else if (!strcmp(key, "ID_INPUT_MOUSE")) { attrs.flags |= ATTR_POINTER; } else if (!strcmp(key, "ID_INPUT_JOYSTICK")) { attrs.flags |= ATTR_JOYSTICK; } else if (!strcmp(key, "ID_INPUT_TABLET")) { attrs.flags |= ATTR_TABLET; } else if (!strcmp(key, "ID_INPUT_TOUCHPAD")) { attrs.flags |= ATTR_TOUCHPAD; } else if (!strcmp(key, "ID_INPUT_TOUCHSCREEN")) { attrs.flags |= ATTR_TOUCHSCREEN; } } LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n", name, path); rc = NewInputDeviceRequest(options, &attrs, &dev); if (rc != Success) goto unwind; for (; dev; dev = dev->next) { xfree(dev->config_info); dev->config_info = xstrdup(config_info); } unwind: xfree(config_info); while (!dev && (tmpo = options)) { options = tmpo->next; xfree(tmpo->key); xfree(tmpo->value); xfree(tmpo); } return; } static void device_removed(struct udev_device *device) { char *value; const char *syspath = udev_device_get_syspath(device); value = Xprintf("udev:%s", syspath); if (!value) return; remove_devices("udev", value); xfree(value); } static void wakeup_handler(pointer data, int err, pointer read_mask) { int udev_fd = udev_monitor_get_fd(udev_monitor); struct udev_device *udev_device; const char *action; if (err < 0) return; if (FD_ISSET(udev_fd, (fd_set *)read_mask)) { udev_device = udev_monitor_receive_device(udev_monitor); if (!udev_device) return; action = udev_device_get_action(udev_device); if (action) { if (!strcmp(action, "add")) device_added(udev_device); else if (!strcmp(action, "remove")) device_removed(udev_device); } udev_device_unref(udev_device); } } static void block_handler(pointer data, struct timeval **tv, pointer read_mask) { } int config_udev_init(void) { struct udev *udev; struct udev_enumerate *enumerate; struct udev_list_entry *devices, *device; int rc; udev = udev_new(); if (!udev) return 0; udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); if (!udev_monitor) return 0; rc = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "input", NULL); if (rc < 0) return 0; if (udev_monitor_enable_receiving(udev_monitor)) { ErrorF("config/udev: failed to bind the udev monitor\n"); return 0; } enumerate = udev_enumerate_new(udev); if (!enumerate) return 0; udev_enumerate_add_match_subsystem(enumerate, "input"); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(device, devices) { const char *syspath = udev_list_entry_get_name(device); struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath); device_added(udev_device); udev_device_unref(udev_device); } udev_enumerate_unref(enumerate); RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); AddGeneralSocket(udev_monitor_get_fd(udev_monitor)); return 1; } void config_udev_fini(void) { struct udev *udev; if (!udev_monitor) return; udev = udev_monitor_get_udev(udev_monitor); RemoveGeneralSocket(udev_monitor_get_fd(udev_monitor)); RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, udev_monitor); udev_monitor_unref(udev_monitor); udev_monitor = NULL; udev_unref(udev); }