2 * Copyright © 2009 Julien Cristau
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:
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
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.
23 * Author: Julien Cristau <jcristau@debian.org>
26 #ifdef HAVE_DIX_CONFIG_H
27 #include <dix-config.h>
36 #include "config-backends.h"
40 #define UDEV_XKB_PROP_KEY "xkb"
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)")
53 static struct udev_monitor
*udev_monitor
;
55 #ifdef CONFIG_UDEV_KMS
57 config_udev_odev_setup_attribs(const char *path
, const char *syspath
,
58 config_odev_probe_proc_ptr probe_callback
);
62 device_added(struct udev_device
*udev_device
)
64 const char *path
, *name
= NULL
;
65 char *config_info
= NULL
;
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
;
77 path
= udev_device_get_devnode(udev_device
);
79 syspath
= udev_device_get_syspath(udev_device
);
81 if (!path
|| !syspath
)
84 dev_seat
= udev_device_get_property_value(udev_device
, "ID_SEAT");
88 if (SeatId
&& strcmp(dev_seat
, SeatId
))
91 if (!SeatId
&& strcmp(dev_seat
, "seat0"))
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
);
98 if (strncmp(sysname
, "card", 4) != 0)
101 LogMessage(X_INFO
, "config/udev: Adding drm device (%s)\n", path
);
103 config_udev_odev_setup_attribs(path
, syspath
, NewGPUDeviceRequest
);
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
);
115 input_options
= input_option_new(NULL
, "_source", "server/udev");
119 parent
= udev_device_get_parent(udev_device
);
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
;
126 name
= udev_device_get_sysattr_value(parent
, "name");
127 LOG_SYSATTR(ppath
, "name", name
);
129 name
= udev_device_get_property_value(parent
, "NAME");
130 LOG_PROPERTY(ppath
, "NAME", name
);
133 /* construct USB ID in lowercase hex - "0000:ffff" */
135 sscanf(product
, "%*x/%4x/%4x/%*x", &usb_vendor
, &usb_model
) == 2) {
136 if (asprintf(&attrs
.usb_id
, "%04x:%04x", usb_vendor
, usb_model
)
140 LOG_PROPERTY(ppath
, "PRODUCT", product
);
143 while (!pnp_id
&& (parent
= udev_device_get_parent(parent
))) {
144 pnp_id
= udev_device_get_sysattr_value(parent
, "id");
148 attrs
.pnp_id
= strdup(pnp_id
);
149 ppath
= udev_device_get_devnode(parent
);
150 LOG_SYSATTR(ppath
, "id", pnp_id
);
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
);
162 attrs
.device
= strdup(path
);
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
, ",");
168 if (asprintf(&config_info
, "udev:%s", syspath
) == -1) {
173 if (device_is_duplicate(config_info
)) {
174 LogMessage(X_WARNING
, "config/udev: device %s already added. "
175 "Ignoring.\n", name
);
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
);
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"))
190 input_option_new(input_options
, "xkb_rules", value
);
191 else if (!strcasecmp(tmp
, "layout"))
193 input_option_new(input_options
, "xkb_layout", value
);
194 else if (!strcasecmp(tmp
, "variant"))
196 input_option_new(input_options
, "xkb_variant", value
);
197 else if (!strcasecmp(tmp
, "model"))
199 input_option_new(input_options
, "xkb_model", value
);
200 else if (!strcasecmp(tmp
, "options"))
202 input_option_new(input_options
, "xkb_options", value
);
204 else if (!strcmp(key
, "ID_VENDOR")) {
205 LOG_PROPERTY(path
, key
, value
);
206 attrs
.vendor
= strdup(value
);
208 else if (!strcmp(key
, "ID_INPUT_KEY")) {
209 LOG_PROPERTY(path
, key
, value
);
210 attrs
.flags
|= ATTR_KEYBOARD
;
212 else if (!strcmp(key
, "ID_INPUT_MOUSE")) {
213 LOG_PROPERTY(path
, key
, value
);
214 attrs
.flags
|= ATTR_POINTER
;
216 else if (!strcmp(key
, "ID_INPUT_JOYSTICK")) {
217 LOG_PROPERTY(path
, key
, value
);
218 attrs
.flags
|= ATTR_JOYSTICK
;
220 else if (!strcmp(key
, "ID_INPUT_TABLET")) {
221 LOG_PROPERTY(path
, key
, value
);
222 attrs
.flags
|= ATTR_TABLET
;
224 else if (!strcmp(key
, "ID_INPUT_TOUCHPAD")) {
225 LOG_PROPERTY(path
, key
, value
);
226 attrs
.flags
|= ATTR_TOUCHPAD
;
228 else if (!strcmp(key
, "ID_INPUT_TOUCHSCREEN")) {
229 LOG_PROPERTY(path
, key
, value
);
230 attrs
.flags
|= ATTR_TOUCHSCREEN
;
234 input_options
= input_option_new(input_options
, "config_info", config_info
);
236 /* Default setting needed for non-seat0 seats */
237 if (ServerIsNotSeat0())
238 input_options
= input_option_new(input_options
, "GrabDevice", "on");
240 LogMessage(X_INFO
, "config/udev: Adding input device %s (%s)\n",
242 rc
= NewInputDeviceRequest(input_options
, &attrs
, &dev
);
248 input_option_free_list(&input_options
);
256 char **tag
= attrs
.tags
;
269 device_removed(struct udev_device
*device
)
272 const char *syspath
= udev_device_get_syspath(device
);
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
);
279 if (strncmp(sysname
,"card", 4) != 0)
281 ErrorF("removing GPU device %s %s\n", syspath
, path
);
285 config_udev_odev_setup_attribs(path
, syspath
, DeleteGPUDeviceRequest
);
290 if (asprintf(&value
, "udev:%s", syspath
) == -1)
293 remove_devices("udev", value
);
299 wakeup_handler(pointer data
, int err
, pointer read_mask
)
301 int udev_fd
= udev_monitor_get_fd(udev_monitor
);
302 struct udev_device
*udev_device
;
308 if (FD_ISSET(udev_fd
, (fd_set
*) read_mask
)) {
309 udev_device
= udev_monitor_receive_device(udev_monitor
);
312 action
= udev_device_get_action(udev_device
);
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
);
324 else if (!strcmp(action
, "remove"))
325 device_removed(udev_device
);
327 udev_device_unref(udev_device
);
332 block_handler(pointer data
, struct timeval
**tv
, pointer read_mask
)
337 config_udev_pre_init(void)
345 udev_monitor
= udev_monitor_new_from_netlink(udev
, "udev");
349 udev_monitor_filter_add_match_subsystem_devtype(udev_monitor
, "input",
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
);
358 #ifdef HAVE_UDEV_MONITOR_FILTER_ADD_MATCH_TAG
359 if (ServerIsNotSeat0())
360 udev_monitor_filter_add_match_tag(udev_monitor
, SeatId
);
362 if (udev_monitor_enable_receiving(udev_monitor
)) {
363 ErrorF("config/udev: failed to bind the udev monitor\n");
370 config_udev_init(void)
373 struct udev_enumerate
*enumerate
;
374 struct udev_list_entry
*devices
, *device
;
376 udev
= udev_monitor_get_udev(udev_monitor
);
377 enumerate
= udev_enumerate_new(udev
);
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");
387 #ifdef HAVE_UDEV_ENUMERATE_ADD_MATCH_TAG
388 if (ServerIsNotSeat0())
389 udev_enumerate_add_match_tag(enumerate
, SeatId
);
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
);
399 /* Device might be gone by the time we try to open it */
403 device_added(udev_device
);
404 udev_device_unref(udev_device
);
406 udev_enumerate_unref(enumerate
);
408 RegisterBlockAndWakeupHandlers(block_handler
, wakeup_handler
, NULL
);
409 AddGeneralSocket(udev_monitor_get_fd(udev_monitor
));
415 config_udev_fini(void)
422 udev
= udev_monitor_get_udev(udev_monitor
);
424 RemoveGeneralSocket(udev_monitor_get_fd(udev_monitor
));
425 RemoveBlockAndWakeupHandlers(block_handler
, wakeup_handler
, NULL
);
426 udev_monitor_unref(udev_monitor
);
431 #ifdef CONFIG_UDEV_KMS
434 config_udev_odev_setup_attribs(const char *path
, const char *syspath
,
435 config_odev_probe_proc_ptr probe_callback
)
437 struct OdevAttributes
*attribs
= config_odev_allocate_attribute_list();
443 ret
= config_odev_add_attribute(attribs
, ODEV_ATTRIB_PATH
, path
);
447 ret
= config_odev_add_attribute(attribs
, ODEV_ATTRIB_SYSPATH
, syspath
);
451 /* ownership of attribs is passed to probe layer */
452 probe_callback(attribs
);
455 config_odev_free_attributes(attribs
);
461 config_udev_odev_probe(config_odev_probe_proc_ptr probe_callback
)
464 struct udev_enumerate
*enumerate
;
465 struct udev_list_entry
*devices
, *device
;
467 udev
= udev_monitor_get_udev(udev_monitor
);
468 enumerate
= udev_enumerate_new(udev
);
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
);
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
);
486 if (!path
|| !syspath
)
488 else if (strcmp(udev_device_get_subsystem(udev_device
), "drm") != 0)
490 else if (strncmp(sysname
, "card", 4) != 0)
493 config_udev_odev_setup_attribs(path
, syspath
, probe_callback
);
496 udev_device_unref(udev_device
);
498 udev_enumerate_unref(enumerate
);