Imported Upstream version 1.15.1
[deb_xorg-server.git] / config / udev.c
CommitLineData
a09e091a
JB
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
33#include "input.h"
34#include "inputstr.h"
35#include "hotplug.h"
36#include "config-backends.h"
37#include "os.h"
38#include "globals.h"
39
40#define UDEV_XKB_PROP_KEY "xkb"
41
42#define LOG_PROPERTY(path, prop, val) \
43 LogMessageVerb(X_INFO, 10, \
44 "config/udev: getting property %s on %s " \
45 "returned \"%s\"\n", \
46 (prop), (path), (val) ? (val) : "(null)")
47#define LOG_SYSATTR(path, attr, val) \
48 LogMessageVerb(X_INFO, 10, \
49 "config/udev: getting attribute %s on %s " \
50 "returned \"%s\"\n", \
51 (attr), (path), (val) ? (val) : "(null)")
52
53static struct udev_monitor *udev_monitor;
54
55#ifdef CONFIG_UDEV_KMS
56static Bool
57config_udev_odev_setup_attribs(const char *path, const char *syspath,
58 config_odev_probe_proc_ptr probe_callback);
59#endif
60
61static void
62device_added(struct udev_device *udev_device)
63{
64 const char *path, *name = NULL;
65 char *config_info = NULL;
66 const char *syspath;
67 const char *tags_prop;
68 const char *key, *value, *tmp;
69 InputOption *input_options;
70 InputAttributes attrs = { };
71 DeviceIntPtr dev = NULL;
72 struct udev_list_entry *set, *entry;
73 struct udev_device *parent;
74 int rc;
75 const char *dev_seat;
76
77 path = udev_device_get_devnode(udev_device);
78
79 syspath = udev_device_get_syspath(udev_device);
80
81 if (!path || !syspath)
82 return;
83
84 dev_seat = udev_device_get_property_value(udev_device, "ID_SEAT");
85 if (!dev_seat)
86 dev_seat = "seat0";
87
88 if (SeatId && strcmp(dev_seat, SeatId))
89 return;
90
91 if (!SeatId && strcmp(dev_seat, "seat0"))
92 return;
93
94#ifdef CONFIG_UDEV_KMS
95 if (!strcmp(udev_device_get_subsystem(udev_device), "drm")) {
96 const char *sysname = udev_device_get_sysname(udev_device);
97
98 if (strncmp(sysname, "card", 4) != 0)
99 return;
100
101 LogMessage(X_INFO, "config/udev: Adding drm device (%s)\n", path);
102
103 config_udev_odev_setup_attribs(path, syspath, NewGPUDeviceRequest);
104 return;
105 }
106#endif
107
108 if (!udev_device_get_property_value(udev_device, "ID_INPUT")) {
109 LogMessageVerb(X_INFO, 10,
110 "config/udev: ignoring device %s without "
111 "property ID_INPUT set\n", path);
112 return;
113 }
114
115 input_options = input_option_new(NULL, "_source", "server/udev");
116 if (!input_options)
117 return;
118
119 parent = udev_device_get_parent(udev_device);
120 if (parent) {
121 const char *ppath = udev_device_get_devnode(parent);
122 const char *product = udev_device_get_property_value(parent, "PRODUCT");
123 const char *pnp_id = udev_device_get_sysattr_value(parent, "id");
124 unsigned int usb_vendor, usb_model;
125
126 name = udev_device_get_sysattr_value(parent, "name");
127 LOG_SYSATTR(ppath, "name", name);
128 if (!name) {
129 name = udev_device_get_property_value(parent, "NAME");
130 LOG_PROPERTY(ppath, "NAME", name);
131 }
132
133 /* construct USB ID in lowercase hex - "0000:ffff" */
134 if (product &&
135 sscanf(product, "%*x/%4x/%4x/%*x", &usb_vendor, &usb_model) == 2) {
136 if (asprintf(&attrs.usb_id, "%04x:%04x", usb_vendor, usb_model)
137 == -1)
138 attrs.usb_id = NULL;
139 else
140 LOG_PROPERTY(ppath, "PRODUCT", product);
141 }
142
143 while (!pnp_id && (parent = udev_device_get_parent(parent))) {
144 pnp_id = udev_device_get_sysattr_value(parent, "id");
145 if (!pnp_id)
146 continue;
147
148 attrs.pnp_id = strdup(pnp_id);
149 ppath = udev_device_get_devnode(parent);
150 LOG_SYSATTR(ppath, "id", pnp_id);
151 }
152
153 }
154 if (!name)
155 name = "(unnamed)";
156 else
157 attrs.product = strdup(name);
158 input_options = input_option_new(input_options, "name", name);
159 input_options = input_option_new(input_options, "path", path);
160 input_options = input_option_new(input_options, "device", path);
161 if (path)
162 attrs.device = strdup(path);
163
164 tags_prop = udev_device_get_property_value(udev_device, "ID_INPUT.tags");
165 LOG_PROPERTY(path, "ID_INPUT.tags", tags_prop);
166 attrs.tags = xstrtokenize(tags_prop, ",");
167
168 if (asprintf(&config_info, "udev:%s", syspath) == -1) {
169 config_info = NULL;
170 goto unwind;
171 }
172
173 if (device_is_duplicate(config_info)) {
174 LogMessage(X_WARNING, "config/udev: device %s already added. "
175 "Ignoring.\n", name);
176 goto unwind;
177 }
178
179 set = udev_device_get_properties_list_entry(udev_device);
180 udev_list_entry_foreach(entry, set) {
181 key = udev_list_entry_get_name(entry);
182 if (!key)
183 continue;
184 value = udev_list_entry_get_value(entry);
185 if (!strncasecmp(key, UDEV_XKB_PROP_KEY, sizeof(UDEV_XKB_PROP_KEY) - 1)) {
186 LOG_PROPERTY(path, key, value);
187 tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1;
188 if (!strcasecmp(tmp, "rules"))
189 input_options =
190 input_option_new(input_options, "xkb_rules", value);
191 else if (!strcasecmp(tmp, "layout"))
192 input_options =
193 input_option_new(input_options, "xkb_layout", value);
194 else if (!strcasecmp(tmp, "variant"))
195 input_options =
196 input_option_new(input_options, "xkb_variant", value);
197 else if (!strcasecmp(tmp, "model"))
198 input_options =
199 input_option_new(input_options, "xkb_model", value);
200 else if (!strcasecmp(tmp, "options"))
201 input_options =
202 input_option_new(input_options, "xkb_options", value);
203 }
204 else if (!strcmp(key, "ID_VENDOR")) {
205 LOG_PROPERTY(path, key, value);
206 attrs.vendor = strdup(value);
207 }
208 else if (!strcmp(key, "ID_INPUT_KEY")) {
209 LOG_PROPERTY(path, key, value);
210 attrs.flags |= ATTR_KEYBOARD;
211 }
212 else if (!strcmp(key, "ID_INPUT_MOUSE")) {
213 LOG_PROPERTY(path, key, value);
214 attrs.flags |= ATTR_POINTER;
215 }
216 else if (!strcmp(key, "ID_INPUT_JOYSTICK")) {
217 LOG_PROPERTY(path, key, value);
218 attrs.flags |= ATTR_JOYSTICK;
219 }
220 else if (!strcmp(key, "ID_INPUT_TABLET")) {
221 LOG_PROPERTY(path, key, value);
222 attrs.flags |= ATTR_TABLET;
223 }
224 else if (!strcmp(key, "ID_INPUT_TOUCHPAD")) {
225 LOG_PROPERTY(path, key, value);
226 attrs.flags |= ATTR_TOUCHPAD;
227 }
228 else if (!strcmp(key, "ID_INPUT_TOUCHSCREEN")) {
229 LOG_PROPERTY(path, key, value);
230 attrs.flags |= ATTR_TOUCHSCREEN;
231 }
232 }
233
234 input_options = input_option_new(input_options, "config_info", config_info);
235
236 /* Default setting needed for non-seat0 seats */
237 if (ServerIsNotSeat0())
238 input_options = input_option_new(input_options, "GrabDevice", "on");
239
240 LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n",
241 name, path);
242 rc = NewInputDeviceRequest(input_options, &attrs, &dev);
243 if (rc != Success)
244 goto unwind;
245
246 unwind:
247 free(config_info);
248 input_option_free_list(&input_options);
249
250 free(attrs.usb_id);
251 free(attrs.pnp_id);
252 free(attrs.product);
253 free(attrs.device);
254 free(attrs.vendor);
255 if (attrs.tags) {
256 char **tag = attrs.tags;
257
258 while (*tag) {
259 free(*tag);
260 tag++;
261 }
262 free(attrs.tags);
263 }
264
265 return;
266}
267
268static void
269device_removed(struct udev_device *device)
270{
271 char *value;
272 const char *syspath = udev_device_get_syspath(device);
273
274#ifdef CONFIG_UDEV_KMS
275 if (!strcmp(udev_device_get_subsystem(device), "drm")) {
276 const char *sysname = udev_device_get_sysname(device);
277 const char *path = udev_device_get_devnode(device);
278
279 if (strncmp(sysname,"card", 4) != 0)
280 return;
281 ErrorF("removing GPU device %s %s\n", syspath, path);
282 if (!path)
283 return;
284
285 config_udev_odev_setup_attribs(path, syspath, DeleteGPUDeviceRequest);
286 return;
287 }
288#endif
289
290 if (asprintf(&value, "udev:%s", syspath) == -1)
291 return;
292
293 remove_devices("udev", value);
294
295 free(value);
296}
297
298static void
299wakeup_handler(pointer data, int err, pointer read_mask)
300{
301 int udev_fd = udev_monitor_get_fd(udev_monitor);
302 struct udev_device *udev_device;
303 const char *action;
304
305 if (err < 0)
306 return;
307
308 if (FD_ISSET(udev_fd, (fd_set *) read_mask)) {
309 udev_device = udev_monitor_receive_device(udev_monitor);
310 if (!udev_device)
311 return;
312 action = udev_device_get_action(udev_device);
313 if (action) {
314 if (!strcmp(action, "add")) {
315 device_removed(udev_device);
316 device_added(udev_device);
317 } else if (!strcmp(action, "change")) {
318 /* ignore change for the drm devices */
319 if (strcmp(udev_device_get_subsystem(udev_device), "drm")) {
320 device_removed(udev_device);
321 device_added(udev_device);
322 }
323 }
324 else if (!strcmp(action, "remove"))
325 device_removed(udev_device);
326 }
327 udev_device_unref(udev_device);
328 }
329}
330
331static void
332block_handler(pointer data, struct timeval **tv, pointer read_mask)
333{
334}
335
336int
337config_udev_pre_init(void)
338{
339 struct udev *udev;
340
341 udev = udev_new();
342 if (!udev)
343 return 0;
344
345 udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
346 if (!udev_monitor)
347 return 0;
348
349 udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "input",
350 NULL);
351 /* For Wacom serial devices */
352 udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL);
353#ifdef CONFIG_UDEV_KMS
354 /* For output GPU devices */
355 udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "drm", NULL);
356#endif
357
358#ifdef HAVE_UDEV_MONITOR_FILTER_ADD_MATCH_TAG
359 if (ServerIsNotSeat0())
360 udev_monitor_filter_add_match_tag(udev_monitor, SeatId);
361#endif
362 if (udev_monitor_enable_receiving(udev_monitor)) {
363 ErrorF("config/udev: failed to bind the udev monitor\n");
364 return 0;
365 }
366 return 1;
367}
368
369int
370config_udev_init(void)
371{
372 struct udev *udev;
373 struct udev_enumerate *enumerate;
374 struct udev_list_entry *devices, *device;
375
376 udev = udev_monitor_get_udev(udev_monitor);
377 enumerate = udev_enumerate_new(udev);
378 if (!enumerate)
379 return 0;
380
381 udev_enumerate_add_match_subsystem(enumerate, "input");
382 udev_enumerate_add_match_subsystem(enumerate, "tty");
383#ifdef CONFIG_UDEV_KMS
384 udev_enumerate_add_match_subsystem(enumerate, "drm");
385#endif
386
387#ifdef HAVE_UDEV_ENUMERATE_ADD_MATCH_TAG
388 if (ServerIsNotSeat0())
389 udev_enumerate_add_match_tag(enumerate, SeatId);
390#endif
391
392 udev_enumerate_scan_devices(enumerate);
393 devices = udev_enumerate_get_list_entry(enumerate);
394 udev_list_entry_foreach(device, devices) {
395 const char *syspath = udev_list_entry_get_name(device);
396 struct udev_device *udev_device =
397 udev_device_new_from_syspath(udev, syspath);
398
399 /* Device might be gone by the time we try to open it */
400 if (!udev_device)
401 continue;
402
403 device_added(udev_device);
404 udev_device_unref(udev_device);
405 }
406 udev_enumerate_unref(enumerate);
407
408 RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL);
409 AddGeneralSocket(udev_monitor_get_fd(udev_monitor));
410
411 return 1;
412}
413
414void
415config_udev_fini(void)
416{
417 struct udev *udev;
418
419 if (!udev_monitor)
420 return;
421
422 udev = udev_monitor_get_udev(udev_monitor);
423
424 RemoveGeneralSocket(udev_monitor_get_fd(udev_monitor));
425 RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL);
426 udev_monitor_unref(udev_monitor);
427 udev_monitor = NULL;
428 udev_unref(udev);
429}
430
431#ifdef CONFIG_UDEV_KMS
432
433static Bool
434config_udev_odev_setup_attribs(const char *path, const char *syspath,
435 config_odev_probe_proc_ptr probe_callback)
436{
437 struct OdevAttributes *attribs = config_odev_allocate_attribute_list();
438 int ret;
439
440 if (!attribs)
441 return FALSE;
442
443 ret = config_odev_add_attribute(attribs, ODEV_ATTRIB_PATH, path);
444 if (ret == FALSE)
445 goto fail;
446
447 ret = config_odev_add_attribute(attribs, ODEV_ATTRIB_SYSPATH, syspath);
448 if (ret == FALSE)
449 goto fail;
450
451 /* ownership of attribs is passed to probe layer */
452 probe_callback(attribs);
453 return TRUE;
454fail:
455 config_odev_free_attributes(attribs);
456 free(attribs);
457 return FALSE;
458}
459
460void
461config_udev_odev_probe(config_odev_probe_proc_ptr probe_callback)
462{
463 struct udev *udev;
464 struct udev_enumerate *enumerate;
465 struct udev_list_entry *devices, *device;
466
467 udev = udev_monitor_get_udev(udev_monitor);
468 enumerate = udev_enumerate_new(udev);
469 if (!enumerate)
470 return;
471
472 udev_enumerate_add_match_subsystem(enumerate, "drm");
473 udev_enumerate_add_match_sysname(enumerate, "card[0-9]*");
474#ifdef HAVE_UDEV_ENUMERATE_ADD_MATCH_TAG
475 if (ServerIsNotSeat0())
476 udev_enumerate_add_match_tag(enumerate, SeatId);
477#endif
478 udev_enumerate_scan_devices(enumerate);
479 devices = udev_enumerate_get_list_entry(enumerate);
480 udev_list_entry_foreach(device, devices) {
481 const char *syspath = udev_list_entry_get_name(device);
482 struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath);
483 const char *path = udev_device_get_devnode(udev_device);
484 const char *sysname = udev_device_get_sysname(udev_device);
485
486 if (!path || !syspath)
487 goto no_probe;
488 else if (strcmp(udev_device_get_subsystem(udev_device), "drm") != 0)
489 goto no_probe;
490 else if (strncmp(sysname, "card", 4) != 0)
491 goto no_probe;
492
493 config_udev_odev_setup_attribs(path, syspath, probe_callback);
494
495 no_probe:
496 udev_device_unref(udev_device);
497 }
498 udev_enumerate_unref(enumerate);
499 return;
500}
501#endif
502