]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/xserver.git/blob - config/udev.c
42713e92dc4653a80d4b29095cc88b6bd3f5ccca
[glsdk/xserver.git] / config / udev.c
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  */
26 #ifdef HAVE_DIX_CONFIG_H
27 #include <dix-config.h>
28 #endif
30 #include <libudev.h>
31 #include <ctype.h>
33 #include "input.h"
34 #include "inputstr.h"
35 #include "hotplug.h"
36 #include "config-backends.h"
37 #include "os.h"
39 #define UDEV_XKB_PROP_KEY "xkb"
41 #define LOG_PROPERTY(path, prop, val)                                   \
42     LogMessageVerb(X_INFO, 10,                                          \
43                    "config/udev: getting property %s on %s "            \
44                    "returned \"%s\"\n",                                 \
45                    (prop), (path), (val) ? (val) : "(null)")
46 #define LOG_SYSATTR(path, attr, val)                                    \
47     LogMessageVerb(X_INFO, 10,                                          \
48                    "config/udev: getting attribute %s on %s "           \
49                    "returned \"%s\"\n",                                 \
50                    (attr), (path), (val) ? (val) : "(null)")
52 static struct udev_monitor *udev_monitor;
54 static void
55 device_added(struct udev_device *udev_device)
56 {
57     const char *path, *name = NULL;
58     char *config_info = NULL;
59     const char *syspath;
60     const char *tags_prop;
61     const char *key, *value, *tmp;
62     InputOption *options = NULL, *tmpo;
63     InputAttributes attrs = {};
64     DeviceIntPtr dev = NULL;
65     struct udev_list_entry *set, *entry;
66     struct udev_device *parent;
67     int rc;
69     path = udev_device_get_devnode(udev_device);
71     syspath = udev_device_get_syspath(udev_device);
73     if (!path || !syspath)
74         return;
76     if (!udev_device_get_property_value(udev_device, "ID_INPUT")) {
77         LogMessageVerb(X_INFO, 10,
78                        "config/udev: ignoring device %s without "
79                        "property ID_INPUT set\n",
80                        path);
81         return;
82     }
84     options = calloc(sizeof(*options), 1);
85     if (!options)
86         return;
88     options->key = strdup("_source");
89     options->value = strdup("server/udev");
90     if (!options->key || !options->value)
91         goto unwind;
93     parent = udev_device_get_parent(udev_device);
94     if (parent) {
95         const char *ppath = udev_device_get_devnode(parent);
96         const char *product = udev_device_get_property_value(parent, "PRODUCT");
97         const char *pnp_id = udev_device_get_sysattr_value(parent, "id");
98         unsigned int usb_vendor, usb_model;
100         name = udev_device_get_sysattr_value(parent, "name");
101         LOG_SYSATTR(ppath, "name", name);
102         if (!name) {
103             name = udev_device_get_property_value(parent, "NAME");
104             LOG_PROPERTY(ppath, "NAME", name);
105         }
107         if (pnp_id)
108             attrs.pnp_id = strdup(pnp_id);
109         LOG_SYSATTR(ppath, "id", pnp_id);
111         /* construct USB ID in lowercase hex - "0000:ffff" */
112         if (product && sscanf(product, "%*x/%4x/%4x/%*x", &usb_vendor, &usb_model) == 2) {
113             if (asprintf(&attrs.usb_id, "%04x:%04x", usb_vendor, usb_model)
114                 == -1)
115                 attrs.usb_id = NULL;
116             else
117                 LOG_PROPERTY(ppath, "PRODUCT", product);
118         }
119     }
120     if (!name)
121         name = "(unnamed)";
122     else
123         attrs.product = strdup(name);
124     add_option(&options, "name", name);
126     add_option(&options, "path", path);
127     add_option(&options, "device", path);
128     if (path)
129         attrs.device = strdup(path);
131     tags_prop = udev_device_get_property_value(udev_device, "ID_INPUT.tags");
132     LOG_PROPERTY(path, "ID_INPUT.tags", tags_prop);
133     attrs.tags = xstrtokenize(tags_prop, ",");
135     if (asprintf(&config_info, "udev:%s", syspath) == -1) {
136         config_info = NULL;
137         goto unwind;
138     }
140     if (device_is_duplicate(config_info)) {
141         LogMessage(X_WARNING, "config/udev: device %s already added. "
142                               "Ignoring.\n", name);
143         goto unwind;
144     }
146     set = udev_device_get_properties_list_entry(udev_device);
147     udev_list_entry_foreach(entry, set) {
148         key = udev_list_entry_get_name(entry);
149         if (!key)
150             continue;
151         value = udev_list_entry_get_value(entry);
152         if (!strncasecmp(key, UDEV_XKB_PROP_KEY,
153                                 sizeof(UDEV_XKB_PROP_KEY) - 1)) {
154             LOG_PROPERTY(path, key, value);
155             tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1;
156             if (!strcasecmp(tmp, "rules"))
157                 add_option(&options, "xkb_rules", value);
158             else if (!strcasecmp(tmp, "layout"))
159                 add_option(&options, "xkb_layout", value);
160             else if (!strcasecmp(tmp, "variant"))
161                 add_option(&options, "xkb_variant", value);
162             else if (!strcasecmp(tmp, "model"))
163                 add_option(&options, "xkb_model", value);
164             else if (!strcasecmp(tmp, "options"))
165                 add_option(&options, "xkb_options", value);
166         } else if (!strcmp(key, "ID_VENDOR")) {
167             LOG_PROPERTY(path, key, value);
168             attrs.vendor = strdup(value);
169         } else if (!strcmp(key, "ID_INPUT_KEY")) {
170             LOG_PROPERTY(path, key, value);
171             attrs.flags |= ATTR_KEYBOARD;
172         } else if (!strcmp(key, "ID_INPUT_MOUSE")) {
173             LOG_PROPERTY(path, key, value);
174             attrs.flags |= ATTR_POINTER;
175         } else if (!strcmp(key, "ID_INPUT_JOYSTICK")) {
176             LOG_PROPERTY(path, key, value);
177             attrs.flags |= ATTR_JOYSTICK;
178         } else if (!strcmp(key, "ID_INPUT_TABLET")) {
179             LOG_PROPERTY(path, key, value);
180             attrs.flags |= ATTR_TABLET;
181         } else if (!strcmp(key, "ID_INPUT_TOUCHPAD")) {
182             LOG_PROPERTY(path, key, value);
183             attrs.flags |= ATTR_TOUCHPAD;
184         } else if (!strcmp(key, "ID_INPUT_TOUCHSCREEN")) {
185             LOG_PROPERTY(path, key, value);
186             attrs.flags |= ATTR_TOUCHSCREEN;
187         }
188     }
190     add_option(&options, "config_info", config_info);
192     LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n",
193                name, path);
194     rc = NewInputDeviceRequest(options, &attrs, &dev);
195     if (rc != Success)
196         goto unwind;
198  unwind:
199     free(config_info);
200     while ((tmpo = options)) {
201         options = tmpo->next;
202         free(tmpo->key);        /* NULL if dev != NULL */
203         free(tmpo->value);      /* NULL if dev != NULL */
204         free(tmpo);
205     }
207     free(attrs.usb_id);
208     free(attrs.pnp_id);
209     free(attrs.product);
210     free(attrs.device);
211     free(attrs.vendor);
212     if (attrs.tags) {
213         char **tag = attrs.tags;
214         while (*tag) {
215             free(*tag);
216             tag++;
217         }
218         free(attrs.tags);
219     }
221     return;
224 static void
225 device_removed(struct udev_device *device)
227     char *value;
228     const char *syspath = udev_device_get_syspath(device);
230     if (asprintf(&value, "udev:%s", syspath) == -1)
231         return;
233     remove_devices("udev", value);
235     free(value);
238 static void
239 wakeup_handler(pointer data, int err, pointer read_mask)
241     int udev_fd = udev_monitor_get_fd(udev_monitor);
242     struct udev_device *udev_device;
243     const char *action;
245     if (err < 0)
246         return;
248     if (FD_ISSET(udev_fd, (fd_set *)read_mask)) {
249         udev_device = udev_monitor_receive_device(udev_monitor);
250         if (!udev_device)
251             return;
252         action = udev_device_get_action(udev_device);
253         if (action) {
254             if (!strcmp(action, "add") || !strcmp(action, "change")) {
255                 device_removed(udev_device);
256                 device_added(udev_device);
257             }
258             else if (!strcmp(action, "remove"))
259                 device_removed(udev_device);
260         }
261         udev_device_unref(udev_device);
262     }
265 static void
266 block_handler(pointer data, struct timeval **tv, pointer read_mask)
270 int
271 config_udev_init(void)
273     struct udev *udev;
274     struct udev_enumerate *enumerate;
275     struct udev_list_entry *devices, *device;
277     udev = udev_new();
278     if (!udev)
279         return 0;
280     udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
281     if (!udev_monitor)
282         return 0;
284     udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "input", NULL);
285     udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL); /* For Wacom serial devices */
287     if (udev_monitor_enable_receiving(udev_monitor)) {
288         ErrorF("config/udev: failed to bind the udev monitor\n");
289         return 0;
290     }
292     enumerate = udev_enumerate_new(udev);
293     if (!enumerate)
294         return 0;
296     udev_enumerate_add_match_subsystem(enumerate, "input");
297     udev_enumerate_add_match_subsystem(enumerate, "tty");
299     udev_enumerate_scan_devices(enumerate);
300     devices = udev_enumerate_get_list_entry(enumerate);
301     udev_list_entry_foreach(device, devices) {
302         const char *syspath = udev_list_entry_get_name(device);
303         struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath);
305         /* Device might be gone by the time we try to open it */
306         if (!udev_device)
307             continue;
309         device_added(udev_device);
310         udev_device_unref(udev_device);
311     }
312     udev_enumerate_unref(enumerate);
314     RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL);
315     AddGeneralSocket(udev_monitor_get_fd(udev_monitor));
317     return 1;
320 void
321 config_udev_fini(void)
323     struct udev *udev;
325     if (!udev_monitor)
326         return;
328     udev = udev_monitor_get_udev(udev_monitor);
330     RemoveGeneralSocket(udev_monitor_get_fd(udev_monitor));
331     RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL);
332     udev_monitor_unref(udev_monitor);
333     udev_monitor = NULL;
334     udev_unref(udev);