xref: /OK3568_Linux_fs/external/xserver/config/udev.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 
55 static struct udev_monitor *udev_monitor;
56 
57 #ifdef CONFIG_UDEV_KMS
58 static void
59 config_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 
64 static char itoa_buf[16];
65 
itoa(int i)66 static const char *itoa(int i)
67 {
68     snprintf(itoa_buf, sizeof(itoa_buf), "%d", i);
69     return itoa_buf;
70 }
71 
72 static Bool
check_seat(struct udev_device * udev_device)73 check_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 
90 static void
device_added(struct udev_device * udev_device)91 device_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 
308 static void
device_removed(struct udev_device * device)309 device_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 
341 static void
socket_handler(int fd,int ready,void * data)342 socket_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 
372 int
config_udev_pre_init(void)373 config_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 
405 int
config_udev_init(void)406 config_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 
449 void
config_udev_fini(void)450 config_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 
467 static void
config_udev_odev_setup_attribs(const char * path,const char * syspath,int major,int minor,config_odev_probe_proc_ptr probe_callback)468 config_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 
483 void
config_udev_odev_probe(config_odev_probe_proc_ptr probe_callback)484 config_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