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