udev.c revision 6747b715
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 39#define UDEV_XKB_PROP_KEY "xkb" 40 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)") 51 52static struct udev_monitor *udev_monitor; 53 54static void 55device_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; 68 69 path = udev_device_get_devnode(udev_device); 70 71 syspath = udev_device_get_syspath(udev_device); 72 73 if (!path || !syspath) 74 return; 75 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 } 83 84 options = calloc(sizeof(*options), 1); 85 if (!options) 86 return; 87 88 options->key = strdup("_source"); 89 options->value = strdup("server/udev"); 90 if (!options->key || !options->value) 91 goto unwind; 92 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 unsigned int usb_vendor, usb_model; 98 99 name = udev_device_get_sysattr_value(parent, "name"); 100 LOG_SYSATTR(ppath, "name", name); 101 if (!name) { 102 name = udev_device_get_property_value(parent, "NAME"); 103 LOG_PROPERTY(ppath, "NAME", name); 104 } 105 106 attrs.pnp_id = udev_device_get_sysattr_value(parent, "id"); 107 LOG_SYSATTR(ppath, "id", attrs.pnp_id); 108 109 /* construct USB ID in lowercase hex - "0000:ffff" */ 110 if (product && sscanf(product, "%*x/%4x/%4x/%*x", &usb_vendor, &usb_model) == 2) { 111 attrs.usb_id = Xprintf("%04x:%04x", usb_vendor, usb_model); 112 if (attrs.usb_id) 113 LOG_PROPERTY(path, "PRODUCT", product); 114 } 115 } 116 if (!name) 117 name = "(unnamed)"; 118 else 119 attrs.product = name; 120 add_option(&options, "name", name); 121 122 add_option(&options, "path", path); 123 add_option(&options, "device", path); 124 attrs.device = path; 125 126 tags_prop = udev_device_get_property_value(udev_device, "ID_INPUT.tags"); 127 LOG_PROPERTY(path, "ID_INPUT.tags", tags_prop); 128 attrs.tags = xstrtokenize(tags_prop, ","); 129 130 config_info = Xprintf("udev:%s", syspath); 131 if (!config_info) 132 goto unwind; 133 134 if (device_is_duplicate(config_info)) { 135 LogMessage(X_WARNING, "config/udev: device %s already added. " 136 "Ignoring.\n", name); 137 goto unwind; 138 } 139 140 set = udev_device_get_properties_list_entry(udev_device); 141 udev_list_entry_foreach(entry, set) { 142 key = udev_list_entry_get_name(entry); 143 if (!key) 144 continue; 145 value = udev_list_entry_get_value(entry); 146 if (!strncasecmp(key, UDEV_XKB_PROP_KEY, 147 sizeof(UDEV_XKB_PROP_KEY) - 1)) { 148 LOG_PROPERTY(path, key, value); 149 tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1; 150 if (!strcasecmp(tmp, "rules")) 151 add_option(&options, "xkb_rules", value); 152 else if (!strcasecmp(tmp, "layout")) 153 add_option(&options, "xkb_layout", value); 154 else if (!strcasecmp(tmp, "variant")) 155 add_option(&options, "xkb_variant", value); 156 else if (!strcasecmp(tmp, "model")) 157 add_option(&options, "xkb_model", value); 158 else if (!strcasecmp(tmp, "options")) 159 add_option(&options, "xkb_options", value); 160 } else if (!strcmp(key, "ID_VENDOR")) { 161 LOG_PROPERTY(path, key, value); 162 attrs.vendor = value; 163 } else if (!strcmp(key, "ID_INPUT_KEY")) { 164 LOG_PROPERTY(path, key, value); 165 attrs.flags |= ATTR_KEYBOARD; 166 } else if (!strcmp(key, "ID_INPUT_MOUSE")) { 167 LOG_PROPERTY(path, key, value); 168 attrs.flags |= ATTR_POINTER; 169 } else if (!strcmp(key, "ID_INPUT_JOYSTICK")) { 170 LOG_PROPERTY(path, key, value); 171 attrs.flags |= ATTR_JOYSTICK; 172 } else if (!strcmp(key, "ID_INPUT_TABLET")) { 173 LOG_PROPERTY(path, key, value); 174 attrs.flags |= ATTR_TABLET; 175 } else if (!strcmp(key, "ID_INPUT_TOUCHPAD")) { 176 LOG_PROPERTY(path, key, value); 177 attrs.flags |= ATTR_TOUCHPAD; 178 } else if (!strcmp(key, "ID_INPUT_TOUCHSCREEN")) { 179 LOG_PROPERTY(path, key, value); 180 attrs.flags |= ATTR_TOUCHSCREEN; 181 } 182 } 183 184 LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n", 185 name, path); 186 rc = NewInputDeviceRequest(options, &attrs, &dev); 187 if (rc != Success) 188 goto unwind; 189 190 for (; dev; dev = dev->next) { 191 free(dev->config_info); 192 dev->config_info = strdup(config_info); 193 } 194 195 unwind: 196 free(config_info); 197 while (!dev && (tmpo = options)) { 198 options = tmpo->next; 199 free(tmpo->key); 200 free(tmpo->value); 201 free(tmpo); 202 } 203 204 free(attrs.usb_id); 205 if (attrs.tags) { 206 char **tag = attrs.tags; 207 while (*tag) { 208 free(*tag); 209 tag++; 210 } 211 free(attrs.tags); 212 } 213 214 return; 215} 216 217static void 218device_removed(struct udev_device *device) 219{ 220 char *value; 221 const char *syspath = udev_device_get_syspath(device); 222 223 value = Xprintf("udev:%s", syspath); 224 if (!value) 225 return; 226 227 remove_devices("udev", value); 228 229 free(value); 230} 231 232static void 233wakeup_handler(pointer data, int err, pointer read_mask) 234{ 235 int udev_fd = udev_monitor_get_fd(udev_monitor); 236 struct udev_device *udev_device; 237 const char *action; 238 239 if (err < 0) 240 return; 241 242 if (FD_ISSET(udev_fd, (fd_set *)read_mask)) { 243 udev_device = udev_monitor_receive_device(udev_monitor); 244 if (!udev_device) 245 return; 246 action = udev_device_get_action(udev_device); 247 if (action) { 248 if (!strcmp(action, "add")) 249 device_added(udev_device); 250 else if (!strcmp(action, "remove")) 251 device_removed(udev_device); 252 } 253 udev_device_unref(udev_device); 254 } 255} 256 257static void 258block_handler(pointer data, struct timeval **tv, pointer read_mask) 259{ 260} 261 262int 263config_udev_init(void) 264{ 265 struct udev *udev; 266 struct udev_enumerate *enumerate; 267 struct udev_list_entry *devices, *device; 268 269 udev = udev_new(); 270 if (!udev) 271 return 0; 272 udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); 273 if (!udev_monitor) 274 return 0; 275 276 if (udev_monitor_enable_receiving(udev_monitor)) { 277 ErrorF("config/udev: failed to bind the udev monitor\n"); 278 return 0; 279 } 280 281 enumerate = udev_enumerate_new(udev); 282 if (!enumerate) 283 return 0; 284 udev_enumerate_scan_devices(enumerate); 285 devices = udev_enumerate_get_list_entry(enumerate); 286 udev_list_entry_foreach(device, devices) { 287 const char *syspath = udev_list_entry_get_name(device); 288 struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath); 289 device_added(udev_device); 290 udev_device_unref(udev_device); 291 } 292 udev_enumerate_unref(enumerate); 293 294 RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); 295 AddGeneralSocket(udev_monitor_get_fd(udev_monitor)); 296 297 return 1; 298} 299 300void 301config_udev_fini(void) 302{ 303 struct udev *udev; 304 305 if (!udev_monitor) 306 return; 307 308 udev = udev_monitor_get_udev(udev_monitor); 309 310 RemoveGeneralSocket(udev_monitor_get_fd(udev_monitor)); 311 RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, udev_monitor); 312 udev_monitor_unref(udev_monitor); 313 udev_monitor = NULL; 314 udev_unref(udev); 315} 316