udev.c revision 1b5d61b8
1/*
2 * Copyright © 2009 Julien Cristau
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Author: Julien Cristau <jcristau@debian.org>
24 */
25
26#ifdef HAVE_DIX_CONFIG_H
27#include <dix-config.h>
28#endif
29
30#include <libudev.h>
31#include <ctype.h>
32#include <unistd.h>
33
34#include "input.h"
35#include "inputstr.h"
36#include "hotplug.h"
37#include "config-backends.h"
38#include "os.h"
39#include "globals.h"
40#include "systemd-logind.h"
41
42#define UDEV_XKB_PROP_KEY "xkb"
43
44#define LOG_PROPERTY(path, prop, val)                                   \
45    LogMessageVerb(X_INFO, 10,                                          \
46                   "config/udev: getting property %s on %s "            \
47                   "returned \"%s\"\n",                                 \
48                   (prop), (path), (val) ? (val) : "(null)")
49#define LOG_SYSATTR(path, attr, val)                                    \
50    LogMessageVerb(X_INFO, 10,                                          \
51                   "config/udev: getting attribute %s on %s "           \
52                   "returned \"%s\"\n",                                 \
53                   (attr), (path), (val) ? (val) : "(null)")
54
55static struct udev_monitor *udev_monitor;
56
57#ifdef CONFIG_UDEV_KMS
58static void
59config_udev_odev_setup_attribs(const char *path, const char *syspath,
60                               int major, int minor,
61                               config_odev_probe_proc_ptr probe_callback);
62#endif
63
64static char itoa_buf[16];
65
66static const char *itoa(int i)
67{
68    snprintf(itoa_buf, sizeof(itoa_buf), "%d", i);
69    return itoa_buf;
70}
71
72static Bool
73check_seat(struct udev_device *udev_device)
74{
75    const char *dev_seat;
76
77    dev_seat = udev_device_get_property_value(udev_device, "ID_SEAT");
78    if (!dev_seat)
79        dev_seat = "seat0";
80
81    if (SeatId && strcmp(dev_seat, SeatId))
82        return FALSE;
83
84    if (!SeatId && strcmp(dev_seat, "seat0"))
85        return FALSE;
86
87    return TRUE;
88}
89
90static void
91device_added(struct udev_device *udev_device)
92{
93    const char *path, *name = NULL;
94    char *config_info = NULL;
95    const char *syspath;
96    const char *tags_prop;
97    const char *key, *value, *tmp;
98    InputOption *input_options;
99    InputAttributes attrs = { };
100    DeviceIntPtr dev = NULL;
101    struct udev_list_entry *set, *entry;
102    struct udev_device *parent;
103    int rc;
104    dev_t devnum;
105
106    path = udev_device_get_devnode(udev_device);
107
108    syspath = udev_device_get_syspath(udev_device);
109
110    if (!path || !syspath)
111        return;
112
113    if (!check_seat(udev_device))
114        return;
115
116    devnum = udev_device_get_devnum(udev_device);
117
118#ifdef CONFIG_UDEV_KMS
119    if (!strcmp(udev_device_get_subsystem(udev_device), "drm")) {
120        const char *sysname = udev_device_get_sysname(udev_device);
121
122        if (strncmp(sysname, "card", 4) != 0)
123            return;
124
125        /* Check for devices already added through xf86platformProbe() */
126        if (xf86_find_platform_device_by_devnum(major(devnum), minor(devnum)))
127            return;
128
129        LogMessage(X_INFO, "config/udev: Adding drm device (%s)\n", path);
130
131        config_udev_odev_setup_attribs(path, syspath, major(devnum),
132                                       minor(devnum), NewGPUDeviceRequest);
133        return;
134    }
135#endif
136
137    value = udev_device_get_property_value(udev_device, "ID_INPUT");
138    if (!value || !strcmp(value, "0")) {
139        LogMessageVerb(X_INFO, 10,
140                       "config/udev: ignoring device %s without "
141                       "property ID_INPUT set\n", path);
142        return;
143    }
144
145    input_options = input_option_new(NULL, "_source", "server/udev");
146    if (!input_options)
147        return;
148
149    parent = udev_device_get_parent(udev_device);
150    if (parent) {
151        const char *ppath = udev_device_get_devnode(parent);
152        const char *product = udev_device_get_property_value(parent, "PRODUCT");
153        const char *pnp_id = udev_device_get_sysattr_value(parent, "id");
154        unsigned int usb_vendor, usb_model;
155
156        name = udev_device_get_sysattr_value(parent, "name");
157        LOG_SYSATTR(ppath, "name", name);
158        if (!name) {
159            name = udev_device_get_property_value(parent, "NAME");
160            LOG_PROPERTY(ppath, "NAME", name);
161        }
162
163        /* construct USB ID in lowercase hex - "0000:ffff" */
164        if (product &&
165            sscanf(product, "%*x/%4x/%4x/%*x", &usb_vendor, &usb_model) == 2) {
166            char *usb_id;
167            if (asprintf(&usb_id, "%04x:%04x", usb_vendor, usb_model)
168                == -1)
169                usb_id = NULL;
170            else
171                LOG_PROPERTY(ppath, "PRODUCT", product);
172            attrs.usb_id = usb_id;
173        }
174
175        while (!pnp_id && (parent = udev_device_get_parent(parent))) {
176            pnp_id = udev_device_get_sysattr_value(parent, "id");
177            if (!pnp_id)
178                continue;
179
180            attrs.pnp_id = strdup(pnp_id);
181            ppath = udev_device_get_devnode(parent);
182            LOG_SYSATTR(ppath, "id", pnp_id);
183        }
184
185    }
186    if (!name)
187        name = "(unnamed)";
188    else
189        attrs.product = strdup(name);
190    input_options = input_option_new(input_options, "name", name);
191    input_options = input_option_new(input_options, "path", path);
192    input_options = input_option_new(input_options, "device", path);
193    input_options = input_option_new(input_options, "major", itoa(major(devnum)));
194    input_options = input_option_new(input_options, "minor", itoa(minor(devnum)));
195    if (path)
196        attrs.device = strdup(path);
197
198    tags_prop = udev_device_get_property_value(udev_device, "ID_INPUT.tags");
199    LOG_PROPERTY(path, "ID_INPUT.tags", tags_prop);
200    attrs.tags = xstrtokenize(tags_prop, ",");
201
202    if (asprintf(&config_info, "udev:%s", syspath) == -1) {
203        config_info = NULL;
204        goto unwind;
205    }
206
207    if (device_is_duplicate(config_info)) {
208        LogMessage(X_WARNING, "config/udev: device %s already added. "
209                   "Ignoring.\n", name);
210        goto unwind;
211    }
212
213    set = udev_device_get_properties_list_entry(udev_device);
214    udev_list_entry_foreach(entry, set) {
215        key = udev_list_entry_get_name(entry);
216        if (!key)
217            continue;
218        value = udev_list_entry_get_value(entry);
219        if (!strncasecmp(key, UDEV_XKB_PROP_KEY, sizeof(UDEV_XKB_PROP_KEY) - 1)) {
220            LOG_PROPERTY(path, key, value);
221            tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1;
222            if (!strcasecmp(tmp, "rules"))
223                input_options =
224                    input_option_new(input_options, "xkb_rules", value);
225            else if (!strcasecmp(tmp, "layout"))
226                input_options =
227                    input_option_new(input_options, "xkb_layout", value);
228            else if (!strcasecmp(tmp, "variant"))
229                input_options =
230                    input_option_new(input_options, "xkb_variant", value);
231            else if (!strcasecmp(tmp, "model"))
232                input_options =
233                    input_option_new(input_options, "xkb_model", value);
234            else if (!strcasecmp(tmp, "options"))
235                input_options =
236                    input_option_new(input_options, "xkb_options", value);
237        }
238        else if (!strcmp(key, "ID_VENDOR")) {
239            LOG_PROPERTY(path, key, value);
240            attrs.vendor = strdup(value);
241        } else if (!strncmp(key, "ID_INPUT_", 9)) {
242            const struct pfmap {
243                const char *property;
244                unsigned int flag;
245            } map[] = {
246                { "ID_INPUT_KEY", ATTR_KEY },
247                { "ID_INPUT_KEYBOARD", ATTR_KEYBOARD },
248                { "ID_INPUT_MOUSE", ATTR_POINTER },
249                { "ID_INPUT_JOYSTICK", ATTR_JOYSTICK },
250                { "ID_INPUT_TABLET", ATTR_TABLET },
251                { "ID_INPUT_TABLET_PAD", ATTR_TABLET_PAD },
252                { "ID_INPUT_TOUCHPAD", ATTR_TOUCHPAD },
253                { "ID_INPUT_TOUCHSCREEN", ATTR_TOUCHSCREEN },
254                { NULL, 0 },
255            };
256
257            /* Anything but the literal string "0" is considered a
258             * boolean true. The empty string isn't a thing with udev
259             * properties anyway */
260            if (value && strcmp(value, "0")) {
261                const struct pfmap *m = map;
262
263                while (m->property != NULL) {
264                    if (!strcmp(m->property, key)) {
265                        LOG_PROPERTY(path, key, value);
266                        attrs.flags |= m->flag;
267                    }
268                    m++;
269                }
270            }
271        }
272    }
273
274    input_options = input_option_new(input_options, "config_info", config_info);
275
276    /* Default setting needed for non-seat0 seats */
277    if (ServerIsNotSeat0())
278        input_options = input_option_new(input_options, "GrabDevice", "on");
279
280    LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n",
281               name, path);
282    rc = NewInputDeviceRequest(input_options, &attrs, &dev);
283    if (rc != Success)
284        goto unwind;
285
286 unwind:
287    free(config_info);
288    input_option_free_list(&input_options);
289
290    free(attrs.usb_id);
291    free(attrs.pnp_id);
292    free(attrs.product);
293    free(attrs.device);
294    free(attrs.vendor);
295    if (attrs.tags) {
296        char **tag = attrs.tags;
297
298        while (*tag) {
299            free(*tag);
300            tag++;
301        }
302        free(attrs.tags);
303    }
304
305    return;
306}
307
308static void
309device_removed(struct udev_device *device)
310{
311    char *value;
312    const char *syspath = udev_device_get_syspath(device);
313
314#ifdef CONFIG_UDEV_KMS
315    if (!strcmp(udev_device_get_subsystem(device), "drm")) {
316        const char *sysname = udev_device_get_sysname(device);
317        const char *path = udev_device_get_devnode(device);
318        dev_t devnum = udev_device_get_devnum(device);
319
320        if ((strncmp(sysname,"card", 4) != 0) || (path == NULL))
321            return;
322
323        LogMessage(X_INFO, "config/udev: removing GPU device %s %s\n",
324                   syspath, path);
325        config_udev_odev_setup_attribs(path, syspath, major(devnum),
326                                       minor(devnum), DeleteGPUDeviceRequest);
327        /* Retry vtenter after a drm node removal */
328        systemd_logind_vtenter();
329        return;
330    }
331#endif
332
333    if (asprintf(&value, "udev:%s", syspath) == -1)
334        return;
335
336    remove_devices("udev", value);
337
338    free(value);
339}
340
341static void
342socket_handler(int fd, int ready, void *data)
343{
344    struct udev_device *udev_device;
345    const char *action;
346
347    input_lock();
348    udev_device = udev_monitor_receive_device(udev_monitor);
349    if (!udev_device) {
350        input_unlock();
351        return;
352    }
353    action = udev_device_get_action(udev_device);
354    if (action) {
355        if (!strcmp(action, "add")) {
356            device_removed(udev_device);
357            device_added(udev_device);
358        } else if (!strcmp(action, "change")) {
359            /* ignore change for the drm devices */
360            if (strcmp(udev_device_get_subsystem(udev_device), "drm")) {
361                device_removed(udev_device);
362                device_added(udev_device);
363            }
364        }
365        else if (!strcmp(action, "remove"))
366            device_removed(udev_device);
367    }
368    udev_device_unref(udev_device);
369    input_unlock();
370}
371
372int
373config_udev_pre_init(void)
374{
375    struct udev *udev;
376
377    udev = udev_new();
378    if (!udev)
379        return 0;
380
381    udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
382    if (!udev_monitor)
383        return 0;
384
385    udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "input",
386                                                    NULL);
387    /* For Wacom serial devices */
388    udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL);
389#ifdef CONFIG_UDEV_KMS
390    /* For output GPU devices */
391    udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "drm", NULL);
392#endif
393
394#ifdef HAVE_UDEV_MONITOR_FILTER_ADD_MATCH_TAG
395    if (ServerIsNotSeat0())
396        udev_monitor_filter_add_match_tag(udev_monitor, SeatId);
397#endif
398    if (udev_monitor_enable_receiving(udev_monitor)) {
399        ErrorF("config/udev: failed to bind the udev monitor\n");
400        return 0;
401    }
402    return 1;
403}
404
405int
406config_udev_init(void)
407{
408    struct udev *udev;
409    struct udev_enumerate *enumerate;
410    struct udev_list_entry *devices, *device;
411
412    udev = udev_monitor_get_udev(udev_monitor);
413    enumerate = udev_enumerate_new(udev);
414    if (!enumerate)
415        return 0;
416
417    udev_enumerate_add_match_subsystem(enumerate, "input");
418    udev_enumerate_add_match_subsystem(enumerate, "tty");
419#ifdef CONFIG_UDEV_KMS
420    udev_enumerate_add_match_subsystem(enumerate, "drm");
421#endif
422
423#ifdef HAVE_UDEV_ENUMERATE_ADD_MATCH_TAG
424    if (ServerIsNotSeat0())
425        udev_enumerate_add_match_tag(enumerate, SeatId);
426#endif
427
428    udev_enumerate_scan_devices(enumerate);
429    devices = udev_enumerate_get_list_entry(enumerate);
430    udev_list_entry_foreach(device, devices) {
431        const char *syspath = udev_list_entry_get_name(device);
432        struct udev_device *udev_device =
433            udev_device_new_from_syspath(udev, syspath);
434
435        /* Device might be gone by the time we try to open it */
436        if (!udev_device)
437            continue;
438
439        device_added(udev_device);
440        udev_device_unref(udev_device);
441    }
442    udev_enumerate_unref(enumerate);
443
444    SetNotifyFd(udev_monitor_get_fd(udev_monitor), socket_handler, X_NOTIFY_READ, NULL);
445
446    return 1;
447}
448
449void
450config_udev_fini(void)
451{
452    struct udev *udev;
453
454    if (!udev_monitor)
455        return;
456
457    udev = udev_monitor_get_udev(udev_monitor);
458
459    RemoveNotifyFd(udev_monitor_get_fd(udev_monitor));
460    udev_monitor_unref(udev_monitor);
461    udev_monitor = NULL;
462    udev_unref(udev);
463}
464
465#ifdef CONFIG_UDEV_KMS
466
467static void
468config_udev_odev_setup_attribs(const char *path, const char *syspath,
469                               int major, int minor,
470                               config_odev_probe_proc_ptr probe_callback)
471{
472    struct OdevAttributes *attribs = config_odev_allocate_attributes();
473
474    attribs->path = XNFstrdup(path);
475    attribs->syspath = XNFstrdup(syspath);
476    attribs->major = major;
477    attribs->minor = minor;
478
479    /* ownership of attribs is passed to probe layer */
480    probe_callback(attribs);
481}
482
483void
484config_udev_odev_probe(config_odev_probe_proc_ptr probe_callback)
485{
486    struct udev *udev;
487    struct udev_enumerate *enumerate;
488    struct udev_list_entry *devices, *device;
489
490    udev = udev_monitor_get_udev(udev_monitor);
491    enumerate = udev_enumerate_new(udev);
492    if (!enumerate)
493        return;
494
495    udev_enumerate_add_match_subsystem(enumerate, "drm");
496    udev_enumerate_add_match_sysname(enumerate, "card[0-9]*");
497#ifdef HAVE_UDEV_ENUMERATE_ADD_MATCH_TAG
498    if (ServerIsNotSeat0())
499        udev_enumerate_add_match_tag(enumerate, SeatId);
500#endif
501    udev_enumerate_scan_devices(enumerate);
502    devices = udev_enumerate_get_list_entry(enumerate);
503    udev_list_entry_foreach(device, devices) {
504        const char *syspath = udev_list_entry_get_name(device);
505        struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath);
506        const char *path = udev_device_get_devnode(udev_device);
507        const char *sysname = udev_device_get_sysname(udev_device);
508        dev_t devnum = udev_device_get_devnum(udev_device);
509
510        if (!path || !syspath)
511            goto no_probe;
512        else if (strcmp(udev_device_get_subsystem(udev_device), "drm") != 0)
513            goto no_probe;
514        else if (strncmp(sysname, "card", 4) != 0)
515            goto no_probe;
516        else if (!check_seat(udev_device))
517            goto no_probe;
518
519        config_udev_odev_setup_attribs(path, syspath, major(devnum),
520                                       minor(devnum), probe_callback);
521    no_probe:
522        udev_device_unref(udev_device);
523    }
524    udev_enumerate_unref(enumerate);
525    return;
526}
527#endif
528
529