Edit

IABSD.fr/xenocara/xserver/config/udev.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2012-06-10 13:21:05
    Hash : e60da745
    Message : Update to xserver 1.12.2. tested by naddy@, krw@, mpi@.

  • xserver/config/udev.c
  • /*
     * 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 <ctype.h>
    
    #include "input.h"
    #include "inputstr.h"
    #include "hotplug.h"
    #include "config-backends.h"
    #include "os.h"
    #include "globals.h"
    
    #define UDEV_XKB_PROP_KEY "xkb"
    
    #define LOG_PROPERTY(path, prop, val)                                   \
        LogMessageVerb(X_INFO, 10,                                          \
                       "config/udev: getting property %s on %s "            \
                       "returned \"%s\"\n",                                 \
                       (prop), (path), (val) ? (val) : "(null)")
    #define LOG_SYSATTR(path, attr, val)                                    \
        LogMessageVerb(X_INFO, 10,                                          \
                       "config/udev: getting attribute %s on %s "           \
                       "returned \"%s\"\n",                                 \
                       (attr), (path), (val) ? (val) : "(null)")
    
    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 *tags_prop;
        const char *key, *value, *tmp;
        InputOption *input_options;
        InputAttributes attrs = { };
        DeviceIntPtr dev = NULL;
        struct udev_list_entry *set, *entry;
        struct udev_device *parent;
        int rc;
        const char *dev_seat;
    
        path = udev_device_get_devnode(udev_device);
    
        syspath = udev_device_get_syspath(udev_device);
    
        if (!path || !syspath)
            return;
    
        dev_seat = udev_device_get_property_value(udev_device, "ID_SEAT");
        if (!dev_seat)
            dev_seat = "seat0";
    
        if (SeatId && strcmp(dev_seat, SeatId))
            return;
    
        if (!SeatId && strcmp(dev_seat, "seat0"))
            return;
    
        if (!udev_device_get_property_value(udev_device, "ID_INPUT")) {
            LogMessageVerb(X_INFO, 10,
                           "config/udev: ignoring device %s without "
                           "property ID_INPUT set\n", path);
            return;
        }
    
        input_options = input_option_new(NULL, "_source", "server/udev");
        if (!input_options)
            return;
    
        parent = udev_device_get_parent(udev_device);
        if (parent) {
            const char *ppath = udev_device_get_devnode(parent);
            const char *product = udev_device_get_property_value(parent, "PRODUCT");
            const char *pnp_id = udev_device_get_sysattr_value(parent, "id");
            unsigned int usb_vendor, usb_model;
    
            name = udev_device_get_sysattr_value(parent, "name");
            LOG_SYSATTR(ppath, "name", name);
            if (!name) {
                name = udev_device_get_property_value(parent, "NAME");
                LOG_PROPERTY(ppath, "NAME", name);
            }
    
            if (pnp_id)
                attrs.pnp_id = strdup(pnp_id);
            LOG_SYSATTR(ppath, "id", pnp_id);
    
            /* construct USB ID in lowercase hex - "0000:ffff" */
            if (product &&
                sscanf(product, "%*x/%4x/%4x/%*x", &usb_vendor, &usb_model) == 2) {
                if (asprintf(&attrs.usb_id, "%04x:%04x", usb_vendor, usb_model)
                    == -1)
                    attrs.usb_id = NULL;
                else
                    LOG_PROPERTY(ppath, "PRODUCT", product);
            }
        }
        if (!name)
            name = "(unnamed)";
        else
            attrs.product = strdup(name);
        input_options = input_option_new(input_options, "name", name);
        input_options = input_option_new(input_options, "path", path);
        input_options = input_option_new(input_options, "device", path);
        if (path)
            attrs.device = strdup(path);
    
        tags_prop = udev_device_get_property_value(udev_device, "ID_INPUT.tags");
        LOG_PROPERTY(path, "ID_INPUT.tags", tags_prop);
        attrs.tags = xstrtokenize(tags_prop, ",");
    
        if (asprintf(&config_info, "udev:%s", syspath) == -1) {
            config_info = NULL;
            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)) {
                LOG_PROPERTY(path, key, value);
                tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1;
                if (!strcasecmp(tmp, "rules"))
                    input_options =
                        input_option_new(input_options, "xkb_rules", value);
                else if (!strcasecmp(tmp, "layout"))
                    input_options =
                        input_option_new(input_options, "xkb_layout", value);
                else if (!strcasecmp(tmp, "variant"))
                    input_options =
                        input_option_new(input_options, "xkb_variant", value);
                else if (!strcasecmp(tmp, "model"))
                    input_options =
                        input_option_new(input_options, "xkb_model", value);
                else if (!strcasecmp(tmp, "options"))
                    input_options =
                        input_option_new(input_options, "xkb_options", value);
            }
            else if (!strcmp(key, "ID_VENDOR")) {
                LOG_PROPERTY(path, key, value);
                attrs.vendor = strdup(value);
            }
            else if (!strcmp(key, "ID_INPUT_KEY")) {
                LOG_PROPERTY(path, key, value);
                attrs.flags |= ATTR_KEYBOARD;
            }
            else if (!strcmp(key, "ID_INPUT_MOUSE")) {
                LOG_PROPERTY(path, key, value);
                attrs.flags |= ATTR_POINTER;
            }
            else if (!strcmp(key, "ID_INPUT_JOYSTICK")) {
                LOG_PROPERTY(path, key, value);
                attrs.flags |= ATTR_JOYSTICK;
            }
            else if (!strcmp(key, "ID_INPUT_TABLET")) {
                LOG_PROPERTY(path, key, value);
                attrs.flags |= ATTR_TABLET;
            }
            else if (!strcmp(key, "ID_INPUT_TOUCHPAD")) {
                LOG_PROPERTY(path, key, value);
                attrs.flags |= ATTR_TOUCHPAD;
            }
            else if (!strcmp(key, "ID_INPUT_TOUCHSCREEN")) {
                LOG_PROPERTY(path, key, value);
                attrs.flags |= ATTR_TOUCHSCREEN;
            }
        }
    
        input_options = input_option_new(input_options, "config_info", config_info);
    
        LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n",
                   name, path);
        rc = NewInputDeviceRequest(input_options, &attrs, &dev);
        if (rc != Success)
            goto unwind;
    
     unwind:
        free(config_info);
        input_option_free_list(&input_options);
    
        free(attrs.usb_id);
        free(attrs.pnp_id);
        free(attrs.product);
        free(attrs.device);
        free(attrs.vendor);
        if (attrs.tags) {
            char **tag = attrs.tags;
    
            while (*tag) {
                free(*tag);
                tag++;
            }
            free(attrs.tags);
        }
    
        return;
    }
    
    static void
    device_removed(struct udev_device *device)
    {
        char *value;
        const char *syspath = udev_device_get_syspath(device);
    
        if (asprintf(&value, "udev:%s", syspath) == -1)
            return;
    
        remove_devices("udev", value);
    
        free(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") || !strcmp(action, "change")) {
                    device_removed(udev_device);
                    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;
    
        udev = udev_new();
        if (!udev)
            return 0;
        udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
        if (!udev_monitor)
            return 0;
    
        udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "input",
                                                        NULL);
        udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL); /* For Wacom serial devices */
    
    #ifdef HAVE_UDEV_MONITOR_FILTER_ADD_MATCH_TAG
        if (SeatId && strcmp(SeatId, "seat0"))
            udev_monitor_filter_add_match_tag(udev_monitor, SeatId);
    #endif
    
        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_add_match_subsystem(enumerate, "tty");
    
    #ifdef HAVE_UDEV_ENUMERATE_ADD_MATCH_TAG
        if (SeatId && strcmp(SeatId, "seat0"))
            udev_enumerate_add_match_tag(enumerate, SeatId);
    #endif
    
        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 might be gone by the time we try to open it */
            if (!udev_device)
                continue;
    
            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, NULL);
        udev_monitor_unref(udev_monitor);
        udev_monitor = NULL;
        udev_unref(udev);
    }