udev.c revision ed6184df
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 59static struct udev_monitor *udev_monitor; 60 61#ifdef CONFIG_UDEV_KMS 62static void 63config_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 68static char itoa_buf[16]; 69 70static const char *itoa(int i) 71{ 72 snprintf(itoa_buf, sizeof(itoa_buf), "%d", i); 73 return itoa_buf; 74} 75 76static Bool 77check_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 94static void 95device_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 317static void 318device_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 352static void 353socket_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 385int 386config_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 418int 419config_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 462void 463config_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 */ 481static 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 */ 511static char* 512config_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 533static void 534config_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 564void 565config_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 611