xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

udev.c (19212B)


      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 #ifdef HAVE_SYS_SYSMACROS_H
     43 #include <sys/sysmacros.h>
     44 #endif
     45 
     46 #define UDEV_XKB_PROP_KEY "xkb"
     47 
     48 #define LOG_PROPERTY(path, prop, val)                                   \
     49     LogMessageVerb(X_INFO, 10,                                          \
     50                    "config/udev: getting property %s on %s "            \
     51                    "returned \"%s\"\n",                                 \
     52                    (prop), (path), (val) ? (val) : "(null)")
     53 #define LOG_SYSATTR(path, attr, val)                                    \
     54     LogMessageVerb(X_INFO, 10,                                          \
     55                    "config/udev: getting attribute %s on %s "           \
     56                    "returned \"%s\"\n",                                 \
     57                    (attr), (path), (val) ? (val) : "(null)")
     58 
     59 static struct udev_monitor *udev_monitor;
     60 
     61 #ifdef CONFIG_UDEV_KMS
     62 static void
     63 config_udev_odev_setup_attribs(struct udev_device *udev_device, const char *path, const char *syspath,
     64                                int major, int minor,
     65                                config_odev_probe_proc_ptr probe_callback);
     66 #endif
     67 
     68 static char itoa_buf[16];
     69 
     70 static const char *itoa(int i)
     71 {
     72     snprintf(itoa_buf, sizeof(itoa_buf), "%d", i);
     73     return itoa_buf;
     74 }
     75 
     76 static Bool
     77 check_seat(struct udev_device *udev_device)
     78 {
     79     const char *dev_seat;
     80 
     81     dev_seat = udev_device_get_property_value(udev_device, "ID_SEAT");
     82     if (!dev_seat)
     83         dev_seat = "seat0";
     84 
     85     if (SeatId && strcmp(dev_seat, SeatId))
     86         return FALSE;
     87 
     88     if (!SeatId && strcmp(dev_seat, "seat0"))
     89         return FALSE;
     90 
     91     return TRUE;
     92 }
     93 
     94 static void
     95 device_added(struct udev_device *udev_device)
     96 {
     97     const char *path, *name = NULL;
     98     char *config_info = NULL;
     99     const char *syspath;
    100     const char *tags_prop;
    101     const char *key, *value, *tmp;
    102 #ifdef CONFIG_UDEV_KMS
    103     const char *subsys = NULL;
    104 #endif
    105     InputOption *input_options;
    106     InputAttributes attrs = { };
    107     DeviceIntPtr dev = NULL;
    108     struct udev_list_entry *set, *entry;
    109     struct udev_device *parent;
    110     int rc;
    111     dev_t devnum;
    112 
    113     path = udev_device_get_devnode(udev_device);
    114 
    115     syspath = udev_device_get_syspath(udev_device);
    116 
    117     if (!path || !syspath)
    118         return;
    119 
    120     if (!check_seat(udev_device))
    121         return;
    122 
    123     devnum = udev_device_get_devnum(udev_device);
    124 
    125 #ifdef CONFIG_UDEV_KMS
    126     subsys = udev_device_get_subsystem(udev_device);
    127 
    128     if (subsys && !strcmp(subsys, "drm")) {
    129         const char *sysname = udev_device_get_sysname(udev_device);
    130 
    131         if (strncmp(sysname, "card", 4) != 0)
    132             return;
    133 
    134         /* Check for devices already added through xf86platformProbe() */
    135         if (xf86_find_platform_device_by_devnum(major(devnum), minor(devnum)))
    136             return;
    137 
    138         LogMessage(X_INFO, "config/udev: Adding drm device (%s)\n", path);
    139 
    140         config_udev_odev_setup_attribs(udev_device, path, syspath, major(devnum),
    141                                        minor(devnum), NewGPUDeviceRequest);
    142         return;
    143     }
    144 #endif
    145 
    146     value = udev_device_get_property_value(udev_device, "ID_INPUT");
    147     if (!value || !strcmp(value, "0")) {
    148         LogMessageVerb(X_INFO, 10,
    149                        "config/udev: ignoring device %s without "
    150                        "property ID_INPUT set\n", path);
    151         return;
    152     }
    153 
    154     input_options = input_option_new(NULL, "_source", "server/udev");
    155     if (!input_options)
    156         return;
    157 
    158     parent = udev_device_get_parent(udev_device);
    159     if (parent) {
    160         const char *ppath = udev_device_get_devnode(parent);
    161         const char *product = udev_device_get_property_value(parent, "PRODUCT");
    162         const char *pnp_id = udev_device_get_sysattr_value(parent, "id");
    163         unsigned int usb_vendor, usb_model;
    164 
    165         name = udev_device_get_sysattr_value(parent, "name");
    166         LOG_SYSATTR(ppath, "name", name);
    167         if (!name) {
    168             name = udev_device_get_property_value(parent, "NAME");
    169             LOG_PROPERTY(ppath, "NAME", name);
    170         }
    171 
    172         /* construct USB ID in lowercase hex - "0000:ffff" */
    173         if (product &&
    174             sscanf(product, "%*x/%4x/%4x/%*x", &usb_vendor, &usb_model) == 2) {
    175             char *usb_id;
    176             if (asprintf(&usb_id, "%04x:%04x", usb_vendor, usb_model)
    177                 == -1)
    178                 usb_id = NULL;
    179             else
    180                 LOG_PROPERTY(ppath, "PRODUCT", product);
    181             attrs.usb_id = usb_id;
    182         }
    183 
    184         while (!pnp_id && (parent = udev_device_get_parent(parent))) {
    185             pnp_id = udev_device_get_sysattr_value(parent, "id");
    186             if (!pnp_id)
    187                 continue;
    188 
    189             attrs.pnp_id = strdup(pnp_id);
    190             ppath = udev_device_get_devnode(parent);
    191             LOG_SYSATTR(ppath, "id", pnp_id);
    192         }
    193 
    194     }
    195     if (!name)
    196         name = "(unnamed)";
    197     else
    198         attrs.product = strdup(name);
    199     input_options = input_option_new(input_options, "name", name);
    200     input_options = input_option_new(input_options, "path", path);
    201     input_options = input_option_new(input_options, "device", path);
    202     input_options = input_option_new(input_options, "major", itoa(major(devnum)));
    203     input_options = input_option_new(input_options, "minor", itoa(minor(devnum)));
    204     if (path)
    205         attrs.device = strdup(path);
    206 
    207     tags_prop = udev_device_get_property_value(udev_device, "ID_INPUT.tags");
    208     LOG_PROPERTY(path, "ID_INPUT.tags", tags_prop);
    209     attrs.tags = xstrtokenize(tags_prop, ",");
    210 
    211     if (asprintf(&config_info, "udev:%s", syspath) == -1) {
    212         config_info = NULL;
    213         goto unwind;
    214     }
    215 
    216     if (device_is_duplicate(config_info)) {
    217         LogMessage(X_WARNING, "config/udev: device %s already added. "
    218                    "Ignoring.\n", name);
    219         goto unwind;
    220     }
    221 
    222     set = udev_device_get_properties_list_entry(udev_device);
    223     udev_list_entry_foreach(entry, set) {
    224         key = udev_list_entry_get_name(entry);
    225         if (!key)
    226             continue;
    227         value = udev_list_entry_get_value(entry);
    228         if (!strncasecmp(key, UDEV_XKB_PROP_KEY, sizeof(UDEV_XKB_PROP_KEY) - 1)) {
    229             LOG_PROPERTY(path, key, value);
    230             tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1;
    231             if (!strcasecmp(tmp, "rules"))
    232                 input_options =
    233                     input_option_new(input_options, "xkb_rules", value);
    234             else if (!strcasecmp(tmp, "layout"))
    235                 input_options =
    236                     input_option_new(input_options, "xkb_layout", value);
    237             else if (!strcasecmp(tmp, "variant"))
    238                 input_options =
    239                     input_option_new(input_options, "xkb_variant", value);
    240             else if (!strcasecmp(tmp, "model"))
    241                 input_options =
    242                     input_option_new(input_options, "xkb_model", value);
    243             else if (!strcasecmp(tmp, "options"))
    244                 input_options =
    245                     input_option_new(input_options, "xkb_options", value);
    246         }
    247         else if (!strcmp(key, "ID_VENDOR")) {
    248             LOG_PROPERTY(path, key, value);
    249             attrs.vendor = strdup(value);
    250         } else if (!strncmp(key, "ID_INPUT_", 9)) {
    251             const struct pfmap {
    252                 const char *property;
    253                 unsigned int flag;
    254             } map[] = {
    255                 { "ID_INPUT_KEY", ATTR_KEY },
    256                 { "ID_INPUT_KEYBOARD", ATTR_KEYBOARD },
    257                 { "ID_INPUT_MOUSE", ATTR_POINTER },
    258                 { "ID_INPUT_JOYSTICK", ATTR_JOYSTICK },
    259                 { "ID_INPUT_TABLET", ATTR_TABLET },
    260                 { "ID_INPUT_TABLET_PAD", ATTR_TABLET_PAD },
    261                 { "ID_INPUT_TOUCHPAD", ATTR_TOUCHPAD },
    262                 { "ID_INPUT_TOUCHSCREEN", ATTR_TOUCHSCREEN },
    263                 { NULL, 0 },
    264             };
    265 
    266             /* Anything but the literal string "0" is considered a
    267              * boolean true. The empty string isn't a thing with udev
    268              * properties anyway */
    269             if (value && strcmp(value, "0")) {
    270                 const struct pfmap *m = map;
    271 
    272                 while (m->property != NULL) {
    273                     if (!strcmp(m->property, key)) {
    274                         LOG_PROPERTY(path, key, value);
    275                         attrs.flags |= m->flag;
    276                     }
    277                     m++;
    278                 }
    279             }
    280         }
    281     }
    282 
    283     input_options = input_option_new(input_options, "config_info", config_info);
    284 
    285     /* Default setting needed for non-seat0 seats */
    286     if (ServerIsNotSeat0())
    287         input_options = input_option_new(input_options, "GrabDevice", "on");
    288 
    289     LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n",
    290                name, path);
    291     rc = NewInputDeviceRequest(input_options, &attrs, &dev);
    292     if (rc != Success)
    293         goto unwind;
    294 
    295  unwind:
    296     free(config_info);
    297     input_option_free_list(&input_options);
    298 
    299     free(attrs.usb_id);
    300     free(attrs.pnp_id);
    301     free(attrs.product);
    302     free(attrs.device);
    303     free(attrs.vendor);
    304     if (attrs.tags) {
    305         char **tag = attrs.tags;
    306 
    307         while (*tag) {
    308             free(*tag);
    309             tag++;
    310         }
    311         free(attrs.tags);
    312     }
    313 
    314     return;
    315 }
    316 
    317 static void
    318 device_removed(struct udev_device *device)
    319 {
    320     char *value;
    321     const char *syspath = udev_device_get_syspath(device);
    322 
    323 #ifdef CONFIG_UDEV_KMS
    324     const char *subsys = udev_device_get_subsystem(device);
    325 
    326     if (subsys && !strcmp(subsys, "drm")) {
    327         const char *sysname = udev_device_get_sysname(device);
    328         const char *path = udev_device_get_devnode(device);
    329         dev_t devnum = udev_device_get_devnum(device);
    330 
    331         if ((strncmp(sysname,"card", 4) != 0) || (path == NULL))
    332             return;
    333 
    334         LogMessage(X_INFO, "config/udev: removing GPU device %s %s\n",
    335                    syspath, path);
    336         config_udev_odev_setup_attribs(device, path, syspath, major(devnum),
    337                                        minor(devnum), DeleteGPUDeviceRequest);
    338         /* Retry vtenter after a drm node removal */
    339         systemd_logind_vtenter();
    340         return;
    341     }
    342 #endif
    343 
    344     if (asprintf(&value, "udev:%s", syspath) == -1)
    345         return;
    346 
    347     remove_devices("udev", value);
    348 
    349     free(value);
    350 }
    351 
    352 static void
    353 socket_handler(int fd, int ready, void *data)
    354 {
    355     struct udev_device *udev_device;
    356     const char *action;
    357 
    358     input_lock();
    359     udev_device = udev_monitor_receive_device(udev_monitor);
    360     if (!udev_device) {
    361         input_unlock();
    362         return;
    363     }
    364     action = udev_device_get_action(udev_device);
    365     if (action) {
    366         if (!strcmp(action, "add")) {
    367             device_removed(udev_device);
    368             device_added(udev_device);
    369         } else if (!strcmp(action, "change")) {
    370             /* ignore change for the drm devices */
    371             const char *subsys = udev_device_get_subsystem(udev_device);
    372 
    373             if (subsys && strcmp(subsys, "drm")) {
    374                 device_removed(udev_device);
    375                 device_added(udev_device);
    376             }
    377         }
    378         else if (!strcmp(action, "remove"))
    379             device_removed(udev_device);
    380     }
    381     udev_device_unref(udev_device);
    382     input_unlock();
    383 }
    384 
    385 int
    386 config_udev_pre_init(void)
    387 {
    388     struct udev *udev;
    389 
    390     udev = udev_new();
    391     if (!udev)
    392         return 0;
    393 
    394     udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
    395     if (!udev_monitor)
    396         return 0;
    397 
    398     udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "input",
    399                                                     NULL);
    400     /* For Wacom serial devices */
    401     udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL);
    402 #ifdef CONFIG_UDEV_KMS
    403     /* For output GPU devices */
    404     udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "drm", NULL);
    405 #endif
    406 
    407 #ifdef HAVE_UDEV_MONITOR_FILTER_ADD_MATCH_TAG
    408     if (ServerIsNotSeat0())
    409         udev_monitor_filter_add_match_tag(udev_monitor, SeatId);
    410 #endif
    411     if (udev_monitor_enable_receiving(udev_monitor)) {
    412         ErrorF("config/udev: failed to bind the udev monitor\n");
    413         return 0;
    414     }
    415     return 1;
    416 }
    417 
    418 int
    419 config_udev_init(void)
    420 {
    421     struct udev *udev;
    422     struct udev_enumerate *enumerate;
    423     struct udev_list_entry *devices, *device;
    424 
    425     udev = udev_monitor_get_udev(udev_monitor);
    426     enumerate = udev_enumerate_new(udev);
    427     if (!enumerate)
    428         return 0;
    429 
    430     udev_enumerate_add_match_subsystem(enumerate, "input");
    431     udev_enumerate_add_match_subsystem(enumerate, "tty");
    432 #ifdef CONFIG_UDEV_KMS
    433     udev_enumerate_add_match_subsystem(enumerate, "drm");
    434 #endif
    435 
    436 #ifdef HAVE_UDEV_ENUMERATE_ADD_MATCH_TAG
    437     if (ServerIsNotSeat0())
    438         udev_enumerate_add_match_tag(enumerate, SeatId);
    439 #endif
    440 
    441     udev_enumerate_scan_devices(enumerate);
    442     devices = udev_enumerate_get_list_entry(enumerate);
    443     udev_list_entry_foreach(device, devices) {
    444         const char *syspath = udev_list_entry_get_name(device);
    445         struct udev_device *udev_device =
    446             udev_device_new_from_syspath(udev, syspath);
    447 
    448         /* Device might be gone by the time we try to open it */
    449         if (!udev_device)
    450             continue;
    451 
    452         device_added(udev_device);
    453         udev_device_unref(udev_device);
    454     }
    455     udev_enumerate_unref(enumerate);
    456 
    457     SetNotifyFd(udev_monitor_get_fd(udev_monitor), socket_handler, X_NOTIFY_READ, NULL);
    458 
    459     return 1;
    460 }
    461 
    462 void
    463 config_udev_fini(void)
    464 {
    465     struct udev *udev;
    466 
    467     if (!udev_monitor)
    468         return;
    469 
    470     udev = udev_monitor_get_udev(udev_monitor);
    471 
    472     RemoveNotifyFd(udev_monitor_get_fd(udev_monitor));
    473     udev_monitor_unref(udev_monitor);
    474     udev_monitor = NULL;
    475     udev_unref(udev);
    476 }
    477 
    478 #ifdef CONFIG_UDEV_KMS
    479 
    480 /* Find the last occurrence of the needle in haystack */
    481 static char *strrstr(const char *haystack, const char *needle)
    482 {
    483     char *prev, *last, *tmp;
    484 
    485     prev = strstr(haystack, needle);
    486     if (!prev)
    487         return NULL;
    488 
    489     last = prev;
    490     tmp = prev + 1;
    491 
    492     while (tmp) {
    493         last = strstr(tmp, needle);
    494         if (!last)
    495             return prev;
    496         else {
    497             prev = last;
    498             tmp = prev + 1;
    499         }
    500     }
    501 
    502     return last;
    503 }
    504 
    505 /* For certain devices udev does not create ID_PATH entry (which is presumably a bug
    506  * in udev). We work around that by implementing a minimal ID_PATH calculator
    507  * ourselves along the same logic that udev uses. This works only for the case of
    508  * a PCI device being directly connected to a PCI bus, but it will cover most end
    509  * users with e.g. a new laptop which only has beta hardware driver support.
    510  * See https://gitlab.freedesktop.org/xorg/xserver/-/issues/993 */
    511 static char*
    512 config_udev_get_fallback_bus_id(struct udev_device *udev_device)
    513 {
    514     const char *sysname;
    515     char *busid;
    516 
    517     udev_device = udev_device_get_parent(udev_device);
    518     if (udev_device == NULL)
    519         return NULL;
    520 
    521     if (strcmp(udev_device_get_subsystem(udev_device), "pci") != 0)
    522         return NULL;
    523 
    524     sysname = udev_device_get_sysname(udev_device);
    525     busid = XNFalloc(strlen(sysname) + 5);
    526     busid[0] = '\0';
    527     strcat(busid, "pci:");
    528     strcat(busid, sysname);
    529 
    530     return busid;
    531 }
    532 
    533 static void
    534 config_udev_odev_setup_attribs(struct udev_device *udev_device, const char *path, const char *syspath,
    535                                int major, int minor,
    536                                config_odev_probe_proc_ptr probe_callback)
    537 {
    538     struct OdevAttributes *attribs = config_odev_allocate_attributes();
    539     const char *value, *str;
    540 
    541     attribs->path = XNFstrdup(path);
    542     attribs->syspath = XNFstrdup(syspath);
    543     attribs->major = major;
    544     attribs->minor = minor;
    545 
    546     value = udev_device_get_property_value(udev_device, "ID_PATH");
    547     if (value && (str = strrstr(value, "pci-"))) {
    548         value = str;
    549 
    550         if ((str = strstr(value, "usb-")))
    551             value = str;
    552 
    553         attribs->busid = XNFstrdup(value);
    554         attribs->busid[3] = ':';
    555     }
    556 
    557     if (!value)
    558         attribs->busid = config_udev_get_fallback_bus_id(udev_device);
    559 
    560     /* ownership of attribs is passed to probe layer */
    561     probe_callback(attribs);
    562 }
    563 
    564 void
    565 config_udev_odev_probe(config_odev_probe_proc_ptr probe_callback)
    566 {
    567     struct udev *udev;
    568     struct udev_enumerate *enumerate;
    569     struct udev_list_entry *devices, *device;
    570 
    571     udev = udev_monitor_get_udev(udev_monitor);
    572     enumerate = udev_enumerate_new(udev);
    573     if (!enumerate)
    574         return;
    575 
    576     udev_enumerate_add_match_subsystem(enumerate, "drm");
    577     udev_enumerate_add_match_sysname(enumerate, "card[0-9]*");
    578 #ifdef HAVE_UDEV_ENUMERATE_ADD_MATCH_TAG
    579     if (ServerIsNotSeat0())
    580         udev_enumerate_add_match_tag(enumerate, SeatId);
    581 #endif
    582     udev_enumerate_scan_devices(enumerate);
    583     devices = udev_enumerate_get_list_entry(enumerate);
    584     udev_list_entry_foreach(device, devices) {
    585         const char *syspath = udev_list_entry_get_name(device);
    586         struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath);
    587         const char *path = udev_device_get_devnode(udev_device);
    588         const char *sysname = udev_device_get_sysname(udev_device);
    589         dev_t devnum = udev_device_get_devnum(udev_device);
    590         const char *subsys = udev_device_get_subsystem(udev_device);
    591 
    592         if (!path || !syspath || !subsys)
    593             goto no_probe;
    594         else if (strcmp(subsys, "drm") != 0)
    595             goto no_probe;
    596         else if (strncmp(sysname, "card", 4) != 0)
    597             goto no_probe;
    598         else if (!check_seat(udev_device))
    599             goto no_probe;
    600 
    601         config_udev_odev_setup_attribs(udev_device, path, syspath, major(devnum),
    602                                        minor(devnum), probe_callback);
    603     no_probe:
    604         udev_device_unref(udev_device);
    605     }
    606     udev_enumerate_unref(enumerate);
    607     return;
    608 }
    609 #endif
    610