Added
Link Here
|
1 |
/* |
2 |
* Copyright (c) 2012 Baptiste Daroussin |
3 |
* Copyright (c) 2013, 2014 Alex Kozlov |
4 |
* Copyright (c) 2014 Robert Millan |
5 |
* Copyright (c) 2014 Jean-Sebastien Pedron |
6 |
* |
7 |
* Permission is hereby granted, free of charge, to any person obtaining a |
8 |
* copy of this software and associated documentation files (the "Software"), |
9 |
* to deal in the Software without restriction, including without limitation |
10 |
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
11 |
* and/or sell copies of the Software, and to permit persons to whom the |
12 |
* Software is furnished to do so, subject to the following conditions: |
13 |
* |
14 |
* The above copyright notice and this permission notice (including the next |
15 |
* paragraph) shall be included in all copies or substantial portions of the |
16 |
* Software. |
17 |
* |
18 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
21 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
23 |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
24 |
* DEALINGS IN THE SOFTWARE. |
25 |
* |
26 |
* Author: Baptiste Daroussin <bapt@FreeBSD.org> |
27 |
*/ |
28 |
|
29 |
#ifdef HAVE_DIX_CONFIG_H |
30 |
#include <dix-config.h> |
31 |
#endif |
32 |
|
33 |
#include <sys/param.h> |
34 |
#include <sys/ioccom.h> |
35 |
#include <sys/kbio.h> |
36 |
#include <sys/queue.h> |
37 |
#include <sys/socket.h> |
38 |
#include <sys/stat.h> |
39 |
#include <sys/sysctl.h> |
40 |
#include <sys/un.h> |
41 |
|
42 |
#include <ctype.h> |
43 |
#include <dirent.h> |
44 |
#include <errno.h> |
45 |
#include <fcntl.h> |
46 |
#include <limits.h> |
47 |
#include <stddef.h> |
48 |
#include <stdlib.h> |
49 |
#include <stdio.h> |
50 |
#include <stdbool.h> |
51 |
#include <string.h> |
52 |
#include <unistd.h> |
53 |
#include <paths.h> |
54 |
|
55 |
#include "input.h" |
56 |
#include "inputstr.h" |
57 |
#include "hotplug.h" |
58 |
#include "config-backends.h" |
59 |
#include "os.h" |
60 |
|
61 |
#define DEVD_SOCK_PATH "/var/run/devd.pipe" |
62 |
|
63 |
#define RECONNECT_DELAY (5 * 1000) |
64 |
|
65 |
static int sock_devd; |
66 |
static bool is_kbdmux = false; |
67 |
static bool is_kernel_evdev = false; |
68 |
OsTimerPtr rtimer; |
69 |
|
70 |
struct hw_type { |
71 |
const char *driver; |
72 |
int flag; |
73 |
const char *xdriver; |
74 |
const char *sysctldesc; |
75 |
}; |
76 |
|
77 |
static const struct hw_type hw_types0[] = { |
78 |
{ _PATH_DEV "sysmouse", ATTR_POINTER, "mouse", NULL }, |
79 |
{ _PATH_DEV "vboxguest", ATTR_POINTER, "vboxmouse", NULL }, |
80 |
{ NULL, 0, NULL, NULL }, |
81 |
}; |
82 |
|
83 |
static const struct hw_type hw_types1[] = { |
84 |
{ _PATH_DEV "ukbd%d", ATTR_KEYBOARD, "kbd", "ukbd" }, |
85 |
{ _PATH_DEV "atkbd%d", ATTR_KEYBOARD, "kbd", "atkbd" }, |
86 |
{ _PATH_DEV "kbdmux%d", ATTR_KEYBOARD, "kbd", NULL }, |
87 |
{ _PATH_DEV "ums%d", ATTR_POINTER, "mouse", "ums" }, |
88 |
{ _PATH_DEV "psm%d", ATTR_POINTER, "mouse", "psm" }, |
89 |
{ _PATH_DEV "joy%d", ATTR_JOYSTICK, "mouse", "joy" }, |
90 |
{ _PATH_DEV "atp%d", ATTR_TOUCHPAD, "mouse", "atp" }, |
91 |
{ _PATH_DEV "wsp%d", ATTR_TOUCHPAD, "mouse", "wsp" }, |
92 |
{ _PATH_DEV "uep%d", ATTR_TOUCHSCREEN, "mouse", "uep" }, |
93 |
{ _PATH_DEV "input/event%d", 0, "evdev", NULL }, |
94 |
{ NULL, 0, NULL, NULL }, |
95 |
}; |
96 |
|
97 |
/* like basename() but returns a pointer to incoming string */ |
98 |
static char * |
99 |
bname(const char *path) |
100 |
{ |
101 |
char *bname = NULL; |
102 |
|
103 |
if (path != NULL) |
104 |
bname = strrchr(path, '/'); |
105 |
return (bname == NULL) ? (char *)path : bname + 1; |
106 |
} |
107 |
|
108 |
struct dev_entry { |
109 |
SLIST_ENTRY(dev_entry) next; |
110 |
char name[]; |
111 |
}; |
112 |
SLIST_HEAD(dev_list, dev_entry); |
113 |
|
114 |
#define dev_list_init(dev_list) SLIST_INIT(dev_list) |
115 |
#define dev_list_empty(dev_list) SLIST_EMPTY(dev_list) |
116 |
|
117 |
static void |
118 |
dev_list_insert(struct dev_list *devs, const char *devname) |
119 |
{ |
120 |
struct dev_entry *dev; |
121 |
|
122 |
dev = malloc(offsetof(struct dev_entry, name) + strlen(devname) + 1); |
123 |
if (dev != NULL) { |
124 |
strcpy(dev->name, devname); |
125 |
SLIST_INSERT_HEAD(devs, dev, next); |
126 |
} |
127 |
} |
128 |
|
129 |
static void |
130 |
dev_list_destroy(struct dev_list *devs) |
131 |
{ |
132 |
struct dev_entry *dev; |
133 |
|
134 |
while (!SLIST_EMPTY(devs)) { |
135 |
dev = SLIST_FIRST(devs); |
136 |
SLIST_REMOVE_HEAD(devs, next); |
137 |
free(dev); |
138 |
} |
139 |
} |
140 |
|
141 |
static bool |
142 |
dev_list_search(struct dev_list *devs, const char *devname) |
143 |
{ |
144 |
struct dev_entry *dev; |
145 |
|
146 |
if (devname != NULL) |
147 |
SLIST_FOREACH(dev, devs, next) |
148 |
if (strcmp(devname, dev->name) == 0) |
149 |
return true; |
150 |
return false; |
151 |
} |
152 |
|
153 |
/* Some definitions from linux/input.h */ |
154 |
struct input_id { |
155 |
uint16_t bustype; |
156 |
uint16_t vendor; |
157 |
uint16_t product; |
158 |
uint16_t version; |
159 |
}; |
160 |
|
161 |
#define EVIOCGBIT(ev,len) _IOC(IOC_OUT, 'E', 0x20 + (ev), len) |
162 |
#define EVIOCGID _IOR('E', 0x02, struct input_id) |
163 |
#define EVIOCGNAME(len) _IOC(IOC_OUT, 'E', 0x06, len) |
164 |
#define EVIOCGPHYS(len) _IOC(IOC_OUT, 'E', 0x07, len) |
165 |
|
166 |
#define EV_KEY 0x01 |
167 |
#define EV_REL 0x02 |
168 |
#define EV_ABS 0x03 |
169 |
#define BTN_MISC 0x100 |
170 |
#define BTN_LEFT 0x110 |
171 |
#define BTN_RIGHT 0x111 |
172 |
#define BTN_MIDDLE 0x112 |
173 |
#define BTN_JOYSTICK 0x120 |
174 |
#define BTN_TOOL_PEN 0x140 |
175 |
#define BTN_TOOL_FINGER 0x145 |
176 |
#define BTN_TOUCH 0x14a |
177 |
#define BTN_STYLUS 0x14b |
178 |
#define BTN_STYLUS2 0x14c |
179 |
#define KEY_MAX 0x2ff |
180 |
#define KEY_CNT (KEY_MAX+1) |
181 |
#define REL_X 0x00 |
182 |
#define REL_Y 0x01 |
183 |
#define REL_MAX 0x0f |
184 |
#define REL_CNT (REL_MAX+1) |
185 |
#define ABS_X 0x00 |
186 |
#define ABS_Y 0x01 |
187 |
#define ABS_PRESSURE 0x18 |
188 |
#define ABS_MT_SLOT 0x2f |
189 |
#define ABS_MAX 0x3f |
190 |
#define ABS_CNT (ABS_MAX+1) |
191 |
|
192 |
#define LONG_BITS (sizeof(long) * 8) |
193 |
#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) |
194 |
|
195 |
static inline bool |
196 |
bit_is_set(const unsigned long *array, int bit) |
197 |
{ |
198 |
return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS))); |
199 |
} |
200 |
|
201 |
static inline bool |
202 |
bit_find(const unsigned long *array, int start, int stop) |
203 |
{ |
204 |
int i; |
205 |
|
206 |
for (i = start; i < stop; i++) |
207 |
if (bit_is_set(array, i)) |
208 |
return true; |
209 |
|
210 |
return false; |
211 |
} |
212 |
|
213 |
/* |
214 |
* Event device type detection routine. |
215 |
* Derived from EvdevProbe() function of xf86-input-evdev driver |
216 |
*/ |
217 |
static void |
218 |
get_evdev_attrs(InputAttributes *attrs, const char *devicename) |
219 |
{ |
220 |
int fd, flags; |
221 |
bool has_keys, has_buttons, has_lmr; |
222 |
bool has_rel_axes, has_abs_axes, has_mt; |
223 |
unsigned long key_bits[NLONGS(KEY_CNT)]; |
224 |
unsigned long rel_bits[NLONGS(REL_CNT)]; |
225 |
unsigned long abs_bits[NLONGS(ABS_CNT)]; |
226 |
struct input_id id; |
227 |
char name[80]; |
228 |
|
229 |
flags = 0; |
230 |
|
231 |
fd = open(devicename, O_RDONLY | O_CLOEXEC); |
232 |
if (fd < 0) |
233 |
goto out; |
234 |
if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), name) < 0 || |
235 |
ioctl(fd, EVIOCGID, &id) < 0 || |
236 |
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)), rel_bits) < 0 || |
237 |
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits) < 0 || |
238 |
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits) < 0) { |
239 |
close(fd); |
240 |
goto out; |
241 |
} |
242 |
close(fd); |
243 |
|
244 |
*(strchrnul(name, ',')) = '\0'; /* strip name */ |
245 |
attrs->product = strdup(name); |
246 |
asprintf(&attrs->usb_id, "%04x:%04x", id.vendor, id.product); |
247 |
asprintf(&attrs->vendor, "0x%04x", id.vendor); |
248 |
|
249 |
has_keys = bit_find(key_bits, 0, BTN_MISC); |
250 |
has_buttons = bit_find(key_bits, BTN_MISC, BTN_JOYSTICK); |
251 |
has_lmr = bit_find(key_bits, BTN_LEFT, BTN_MIDDLE); |
252 |
has_rel_axes = bit_find(rel_bits, 0, REL_MAX); |
253 |
has_abs_axes = bit_find(abs_bits, 0, ABS_MAX); |
254 |
has_mt = bit_find(abs_bits, ABS_MT_SLOT, ABS_MAX); |
255 |
|
256 |
if (has_abs_axes) { |
257 |
if (has_mt) { |
258 |
if (!has_buttons) { |
259 |
/* |
260 |
* XXX: I'm not sure that joystick detection is |
261 |
* done right. xf86-evdev does not support them. |
262 |
*/ |
263 |
if (bit_is_set(key_bits, BTN_JOYSTICK)) { |
264 |
flags = ATTR_JOYSTICK; |
265 |
goto out; |
266 |
} else { |
267 |
has_buttons = true; |
268 |
} |
269 |
} |
270 |
} |
271 |
|
272 |
if (bit_is_set(abs_bits, ABS_X) && |
273 |
bit_is_set(abs_bits, ABS_Y)) { |
274 |
if (bit_is_set(key_bits, BTN_TOOL_PEN) || |
275 |
bit_is_set(key_bits, BTN_STYLUS) || |
276 |
bit_is_set(key_bits, BTN_STYLUS2)) { |
277 |
flags = ATTR_TABLET; |
278 |
goto out; |
279 |
} else if (bit_is_set(abs_bits, ABS_PRESSURE) || |
280 |
bit_is_set(key_bits, BTN_TOUCH)) { |
281 |
if (has_lmr || |
282 |
bit_is_set(key_bits, BTN_TOOL_FINGER)) { |
283 |
flags = ATTR_TOUCHPAD; |
284 |
} else { |
285 |
flags = ATTR_TOUCHSCREEN; |
286 |
} |
287 |
goto out; |
288 |
} else if (!(bit_is_set(rel_bits, REL_X) && |
289 |
bit_is_set(rel_bits, REL_Y)) && |
290 |
has_lmr) { |
291 |
/* some touchscreens use BTN_LEFT rather than BTN_TOUCH */ |
292 |
flags = ATTR_TOUCHSCREEN; |
293 |
goto out; |
294 |
} |
295 |
} |
296 |
} |
297 |
|
298 |
if (has_keys) |
299 |
flags = ATTR_KEYBOARD; |
300 |
else if (has_rel_axes || has_abs_axes || has_buttons) |
301 |
flags = ATTR_POINTER; |
302 |
|
303 |
out: |
304 |
attrs->flags |= flags; |
305 |
} |
306 |
|
307 |
/* Returns list of devices supporting evdev protocol */ |
308 |
static void |
309 |
get_evdev_blacklist(struct dev_list *devs) |
310 |
{ |
311 |
struct dirent *entry; |
312 |
DIR *dp; |
313 |
static const char ev_dir[] = _PATH_DEV "input"; |
314 |
#define EV_LEN nitems(ev_dir) |
315 |
char path[PATH_MAX + 1]; |
316 |
char phys[80]; |
317 |
int fd; |
318 |
|
319 |
snprintf(path, sizeof(path), "%s/", ev_dir); |
320 |
if ((dp = opendir(ev_dir)) == NULL) |
321 |
return; |
322 |
|
323 |
while ((entry = readdir(dp)) != NULL) { |
324 |
if (entry->d_type != DT_CHR) |
325 |
continue; |
326 |
if (strncmp(entry->d_name, "event", 5) != 0) |
327 |
continue; |
328 |
strlcpy(path + EV_LEN, entry->d_name, sizeof(path) - EV_LEN); |
329 |
fd = open(path, O_RDONLY | O_CLOEXEC); |
330 |
if (fd < 0) |
331 |
continue; |
332 |
/* XXX: Should uinput- and cuse-backed devices be skipped? */ |
333 |
if (ioctl(fd, EVIOCGPHYS(sizeof(phys) - 1), phys) == 0 && |
334 |
phys[0] != 0) |
335 |
dev_list_insert(devs, bname(phys)); |
336 |
close(fd); |
337 |
} |
338 |
closedir(dp); |
339 |
return; |
340 |
} |
341 |
|
342 |
static void |
343 |
get_usb_id(char **pptr, int fd) |
344 |
{ |
345 |
unsigned short vendor; |
346 |
unsigned short product; |
347 |
unsigned int speed; |
348 |
#define WEBCAMD_IOCTL_GET_USB_VENDOR_ID _IOR('q', 250, unsigned short) |
349 |
#define WEBCAMD_IOCTL_GET_USB_PRODUCT_ID _IOR('q', 251, unsigned short) |
350 |
#define WEBCAMD_IOCTL_GET_USB_SPEED _IOR('q', 252, unsigned int) |
351 |
if (ioctl(fd, WEBCAMD_IOCTL_GET_USB_VENDOR_ID, &vendor) == 0 && |
352 |
ioctl(fd, WEBCAMD_IOCTL_GET_USB_PRODUCT_ID, &product) == 0 && |
353 |
ioctl(fd, WEBCAMD_IOCTL_GET_USB_SPEED, &speed) == 0) { |
354 |
if (asprintf(pptr, "%04x:%04x", vendor, product) == -1) |
355 |
*pptr = NULL; |
356 |
} |
357 |
} |
358 |
|
359 |
static char * |
360 |
get_prop_value(const char *buf, const char *prop, size_t *len) |
361 |
{ |
362 |
char *prop_pos; |
363 |
size_t prop_len; |
364 |
|
365 |
prop_len = strlen(prop); |
366 |
prop_pos = strstr(buf, prop); |
367 |
if (prop_pos == NULL || |
368 |
(prop_pos != buf && prop_pos[-1] != ' ') || |
369 |
prop_pos[prop_len] != '=') |
370 |
return (NULL); |
371 |
|
372 |
*len = strchrnul(prop_pos + prop_len + 1, ' ') - prop_pos - prop_len - 1; |
373 |
return (prop_pos + prop_len + 1); |
374 |
} |
375 |
|
376 |
static void |
377 |
get_sysctl_attrs(InputAttributes *attrs, const char* driver, int unit) |
378 |
{ |
379 |
char mib[32]; |
380 |
char name[80], pnpinfo[1024], *pnp_id; |
381 |
const char *vendorstr, *prodstr, *devicestr; |
382 |
size_t len, vendorlen, prodlen, devicelen, pnplen; |
383 |
uint32_t product, vendor; |
384 |
|
385 |
snprintf(mib, sizeof(mib), "dev.%s.%d.%%desc", driver, unit); |
386 |
len = sizeof(name); |
387 |
if (sysctlbyname(mib, name, &len, NULL, 0) < 0) |
388 |
return; |
389 |
*(strchrnul(name, ',')) = '\0'; /* strip name */ |
390 |
attrs->product = strdup(name); |
391 |
|
392 |
snprintf(mib, sizeof(mib), "dev.%s.%d.%%pnpinfo", driver, unit); |
393 |
len = sizeof(pnpinfo); |
394 |
if (sysctlbyname(mib, pnpinfo, &len, NULL, 0) < 0) |
395 |
return; |
396 |
|
397 |
vendorstr = get_prop_value(pnpinfo, "vendor", &vendorlen); |
398 |
prodstr = get_prop_value(pnpinfo, "product", &prodlen); |
399 |
devicestr = get_prop_value(pnpinfo, "device", &devicelen); |
400 |
pnp_id = get_prop_value(pnpinfo, "_HID", &pnplen); |
401 |
if (pnp_id != NULL && pnplen == 4 && strncmp(pnp_id, "none", 4) == 0) |
402 |
pnp_id = NULL; |
403 |
if (pnp_id != NULL) { |
404 |
pnp_id[pnplen] = '\0'; |
405 |
attrs->pnp_id = strdup(pnp_id); |
406 |
} |
407 |
if (prodstr != NULL && vendorstr != NULL) { |
408 |
/* bus = BUS_USB; */ |
409 |
vendor = strtol(vendorstr, NULL, 0); |
410 |
product = strtol(prodstr, NULL, 0); |
411 |
} else if (devicestr != NULL && vendorstr != NULL) { |
412 |
/* bus = BUS_PCI; */ |
413 |
vendor = strtol(vendorstr, NULL, 0); |
414 |
product = strtol(devicestr, NULL, 0); |
415 |
} else |
416 |
return; |
417 |
|
418 |
asprintf(&attrs->usb_id, "%04x:%04x", vendor, product); |
419 |
asprintf(&attrs->vendor, "0x%04x", vendor); |
420 |
|
421 |
return; |
422 |
} |
423 |
|
424 |
static const char * |
425 |
skip_path_dev(const char *ptr) |
426 |
{ |
427 |
if (strstr(ptr, _PATH_DEV) == ptr) |
428 |
ptr += strlen(_PATH_DEV); |
429 |
return (ptr); |
430 |
} |
431 |
|
432 |
static void |
433 |
device_added(const char *devicename, struct dev_list *blacklist) |
434 |
{ |
435 |
InputAttributes attrs = { }; |
436 |
InputOption *options = NULL; |
437 |
char *config_info = NULL; |
438 |
DeviceIntPtr dev = NULL; |
439 |
struct hw_type hw_type; |
440 |
int unit = 0; |
441 |
int fd = -1; |
442 |
int i; |
443 |
bool allow_no_device = false; |
444 |
|
445 |
for (i = 0; hw_types0[i].driver != NULL; i++) { |
446 |
if (strcmp(devicename, hw_types0[i].driver) == 0) { |
447 |
hw_type = hw_types0[i]; |
448 |
goto found; |
449 |
} |
450 |
} |
451 |
for (i = 0; hw_types1[i].driver != NULL; i++) { |
452 |
if (sscanf(devicename, hw_types1[i].driver, &unit) == 1) { |
453 |
hw_type = hw_types1[i]; |
454 |
goto found; |
455 |
} |
456 |
} |
457 |
goto ignore; |
458 |
|
459 |
found: |
460 |
if (hw_type.xdriver == NULL) |
461 |
goto ignore; |
462 |
|
463 |
/* set flags, if any */ |
464 |
attrs.flags |= hw_type.flag; |
465 |
|
466 |
if (strcmp(hw_type.xdriver, "evdev") == 0) { |
467 |
get_evdev_attrs(&attrs, devicename); |
468 |
/* Set keyboard rules explicitly for libinput */ |
469 |
if (attrs.flags & ATTR_KEYBOARD) { |
470 |
options = input_option_new(options, "xkb_rules", |
471 |
"evdev"); |
472 |
if (options == NULL) |
473 |
goto error; |
474 |
} |
475 |
} else { |
476 |
if (is_kernel_evdev) { |
477 |
if (dev_list_empty(blacklist)) |
478 |
get_evdev_blacklist(blacklist); |
479 |
/* |
480 |
* Prefer evdev input kernel interface to native one. |
481 |
* Assume that both evdev 'physical path' and non-evdev |
482 |
* character device path endings are device names so |
483 |
* we can compare them and skip latter. |
484 |
*/ |
485 |
if (dev_list_search(blacklist, bname(devicename))) |
486 |
goto ignore; |
487 |
} |
488 |
} |
489 |
|
490 |
if (strcmp(hw_type.xdriver, "kbd") == 0) { |
491 |
bool match = (strstr(hw_type.driver, |
492 |
_PATH_DEV "kbdmux") == hw_type.driver); |
493 |
|
494 |
if (is_kbdmux) { |
495 |
allow_no_device = true; |
496 |
if (!match) |
497 |
goto ignore; |
498 |
} else { |
499 |
if (match) |
500 |
goto ignore; |
501 |
} |
502 |
} |
503 |
|
504 |
options = input_option_new(options, "_source", "server/devd"); |
505 |
if (options == NULL) |
506 |
goto error; |
507 |
|
508 |
if (hw_type.sysctldesc != NULL) |
509 |
get_sysctl_attrs(&attrs, hw_type.sysctldesc, unit); |
510 |
|
511 |
if (attrs.product == NULL) |
512 |
attrs.product = strdup(skip_path_dev(devicename)); |
513 |
|
514 |
options = input_option_new(options, "name", attrs.product); |
515 |
if (options == NULL) |
516 |
goto error; |
517 |
|
518 |
attrs.device = strdup(devicename); |
519 |
|
520 |
fd = open(devicename, O_RDONLY); |
521 |
if (fd > -1) { |
522 |
if (attrs.usb_id == NULL) |
523 |
get_usb_id(&attrs.usb_id, fd); |
524 |
close(fd); |
525 |
options = input_option_new(options, "device", devicename); |
526 |
if (options == NULL) |
527 |
goto error; |
528 |
} else if (allow_no_device) { |
529 |
/* |
530 |
* Don't pass "device" option if the keyboard is |
531 |
* already attached to the console (ie. open() fails). |
532 |
* This would activate a special logic in |
533 |
* xf86-input-keyboard. Prevent any other attached to |
534 |
* console keyboards being processed. There can be |
535 |
* only one such device. |
536 |
*/ |
537 |
} else { |
538 |
goto ignore; |
539 |
} |
540 |
|
541 |
options = input_option_new(options, "driver", hw_type.xdriver); |
542 |
if (options == NULL) |
543 |
goto error; |
544 |
|
545 |
if (asprintf(&config_info, "devd:%s", |
546 |
skip_path_dev(devicename)) == -1) { |
547 |
config_info = NULL; |
548 |
goto error; |
549 |
} |
550 |
|
551 |
if (device_is_duplicate(config_info)) |
552 |
goto duplicate; |
553 |
|
554 |
options = input_option_new(options, "config_info", config_info); |
555 |
if (options == NULL) |
556 |
goto error; |
557 |
|
558 |
LogMessage(X_INFO, "config/devd: adding input device '%s'\n", |
559 |
devicename); |
560 |
|
561 |
NewInputDeviceRequest(options, &attrs, &dev); |
562 |
goto done; |
563 |
|
564 |
duplicate: |
565 |
LogMessage(X_WARNING, "config/devd: device '%s' already " |
566 |
"added. Ignoring\n", devicename); |
567 |
goto done; |
568 |
|
569 |
error: |
570 |
LogMessage(X_INFO, "config/devd: error adding device '%s'\n", |
571 |
devicename); |
572 |
goto done; |
573 |
|
574 |
ignore: |
575 |
LogMessage(X_INFO, "config/devd: ignoring device '%s'\n", |
576 |
devicename); |
577 |
goto done; |
578 |
|
579 |
done: |
580 |
free(config_info); |
581 |
input_option_free_list(&options); |
582 |
free(attrs.usb_id); |
583 |
free(attrs.pnp_id); |
584 |
free(attrs.product); |
585 |
free(attrs.device); |
586 |
free(attrs.vendor); |
587 |
} |
588 |
|
589 |
static void |
590 |
devpath_scan_sub(char *path, int off, int rem, void *udata) |
591 |
{ |
592 |
struct dirent *entry; |
593 |
DIR *dp; |
594 |
|
595 |
if ((dp = opendir(path)) == NULL) { |
596 |
LogMessage(X_INFO, "Cannot open directory '%s'\n", path); |
597 |
return; |
598 |
} |
599 |
while ((entry = readdir(dp)) != NULL) { |
600 |
int len = strlen(entry->d_name); |
601 |
if (len > rem) |
602 |
continue; |
603 |
strcpy(path + off, entry->d_name); |
604 |
off += len; |
605 |
rem -= len; |
606 |
switch (entry->d_type) { |
607 |
case DT_DIR: |
608 |
if (strcmp(entry->d_name, ".") == 0 || |
609 |
strcmp(entry->d_name, "..") == 0) |
610 |
break; |
611 |
if (rem < 1) |
612 |
break; |
613 |
path[off] = '/'; |
614 |
path[off+1] = '\0'; |
615 |
off++; |
616 |
rem--; |
617 |
/* recurse */ |
618 |
devpath_scan_sub(path, off, rem, udata); |
619 |
off--; |
620 |
rem++; |
621 |
break; |
622 |
case DT_SOCK: |
623 |
case DT_FIFO: |
624 |
case DT_LNK: |
625 |
case DT_CHR: |
626 |
/* add device, if any */ |
627 |
device_added(path, udata); |
628 |
break; |
629 |
default: |
630 |
break; |
631 |
} |
632 |
off -= len; |
633 |
rem += len; |
634 |
} |
635 |
closedir(dp); |
636 |
} |
637 |
|
638 |
static void |
639 |
devpath_scan(void *udata) |
640 |
{ |
641 |
char path[PATH_MAX + 1]; |
642 |
|
643 |
strlcpy(path, _PATH_DEV, sizeof(path)); |
644 |
|
645 |
devpath_scan_sub(path, strlen(path), PATH_MAX - strlen(path), udata); |
646 |
} |
647 |
|
648 |
static void |
649 |
device_removed(char *devicename) |
650 |
{ |
651 |
char *config_info; |
652 |
|
653 |
if (asprintf(&config_info, "devd:%s", |
654 |
skip_path_dev(devicename)) == -1) |
655 |
return; |
656 |
|
657 |
if (device_is_duplicate(config_info)) { |
658 |
LogMessage(X_INFO, "config/devd: removing input device '%s'\n", |
659 |
devicename); |
660 |
} |
661 |
remove_devices("devd", config_info); |
662 |
|
663 |
free(config_info); |
664 |
} |
665 |
|
666 |
static bool is_kbdmux_enabled(void) |
667 |
{ |
668 |
/* Xorg uses /dev/ttyv0 as a console device */ |
669 |
static const char device[]= { _PATH_DEV "ttyv0" }; |
670 |
keyboard_info_t info; |
671 |
int fd; |
672 |
|
673 |
fd = open(device, O_RDONLY); |
674 |
|
675 |
if (fd < 0) |
676 |
return false; |
677 |
|
678 |
if (ioctl(fd, KDGKBINFO, &info) == -1) { |
679 |
close(fd); |
680 |
return false; |
681 |
} |
682 |
|
683 |
close(fd); |
684 |
|
685 |
if (!strncmp(info.kb_name, "kbdmux", 6)) |
686 |
return true; |
687 |
|
688 |
return false; |
689 |
} |
690 |
|
691 |
static void |
692 |
disconnect_devd(int sock) |
693 |
{ |
694 |
if (sock >= 0) { |
695 |
RemoveGeneralSocket(sock); |
696 |
close(sock); |
697 |
} |
698 |
} |
699 |
|
700 |
static int |
701 |
connect_devd(void) |
702 |
{ |
703 |
struct sockaddr_un devd; |
704 |
int sock; |
705 |
|
706 |
sock = socket(AF_UNIX, SOCK_STREAM, 0); |
707 |
if (sock < 0) { |
708 |
LogMessage(X_ERROR, "config/devd: fail opening stream socket\n"); |
709 |
return -1; |
710 |
} |
711 |
|
712 |
devd.sun_family = AF_UNIX; |
713 |
strlcpy(devd.sun_path, DEVD_SOCK_PATH, sizeof(devd.sun_path)); |
714 |
|
715 |
if (connect(sock, (struct sockaddr *) &devd, sizeof(devd)) < 0) { |
716 |
close(sock); |
717 |
LogMessage(X_ERROR, "config/devd: fail to connect to devd\n"); |
718 |
return -1; |
719 |
} |
720 |
|
721 |
AddGeneralSocket(sock); |
722 |
|
723 |
return sock; |
724 |
} |
725 |
|
726 |
static CARD32 |
727 |
reconnect_handler(OsTimerPtr timer, CARD32 time, void *arg) |
728 |
{ |
729 |
int newsock; |
730 |
|
731 |
if ((newsock = connect_devd()) > 0) { |
732 |
sock_devd = newsock; |
733 |
TimerFree(rtimer); |
734 |
rtimer = NULL; |
735 |
LogMessage(X_INFO, "config/devd: reopening devd socket\n"); |
736 |
return 0; |
737 |
} |
738 |
|
739 |
/* Try again after RECONNECT_DELAY */ |
740 |
return RECONNECT_DELAY; |
741 |
} |
742 |
|
743 |
static ssize_t |
744 |
socket_getline(int fd, char **out) |
745 |
{ |
746 |
char *buf, *newbuf; |
747 |
ssize_t ret, cap, sz = 0; |
748 |
char c; |
749 |
|
750 |
cap = 1024; |
751 |
buf = malloc(cap * sizeof(char)); |
752 |
if (!buf) |
753 |
return -1; |
754 |
|
755 |
for (;;) { |
756 |
ret = read(sock_devd, &c, 1); |
757 |
if (ret < 0) { |
758 |
if (errno == EINTR) |
759 |
continue; |
760 |
free(buf); |
761 |
return -1; |
762 |
/* EOF - devd socket is lost */ |
763 |
} else if (ret == 0) { |
764 |
disconnect_devd(sock_devd); |
765 |
rtimer = TimerSet(NULL, 0, 1, reconnect_handler, NULL); |
766 |
LogMessage(X_WARNING, "config/devd: devd socket is lost\n"); |
767 |
return -1; |
768 |
} |
769 |
if (c == '\n') |
770 |
break; |
771 |
|
772 |
if (sz + 1 >= cap) { |
773 |
cap *= 2; |
774 |
newbuf = realloc(buf, cap * sizeof(char)); |
775 |
if (!newbuf) { |
776 |
free(buf); |
777 |
return -1; |
778 |
} |
779 |
buf = newbuf; |
780 |
} |
781 |
buf[sz] = c; |
782 |
sz++; |
783 |
} |
784 |
|
785 |
buf[sz] = '\0'; |
786 |
if (sz >= 0) |
787 |
*out = buf; |
788 |
else |
789 |
free(buf); |
790 |
|
791 |
/* Number of bytes in the line, not counting the line break */ |
792 |
return sz; |
793 |
} |
794 |
|
795 |
static void |
796 |
wakeup_handler(void *data, int err, void *read_mask) |
797 |
{ |
798 |
static const char cdev_create[] = { "!system=DEVFS subsystem=CDEV type=CREATE cdev=" }; |
799 |
static const char cdev_destroy[] = { "!system=DEVFS subsystem=CDEV type=DESTROY cdev=" }; |
800 |
static const char cdev_path[] = { _PATH_DEV }; |
801 |
char *line = NULL; |
802 |
char *devicename; |
803 |
char *walk; |
804 |
struct dev_list blacklist; |
805 |
|
806 |
if (err < 0) |
807 |
return; |
808 |
|
809 |
if (FD_ISSET(sock_devd, (fd_set *) read_mask)) { |
810 |
if (socket_getline(sock_devd, &line) < 0) |
811 |
return; |
812 |
if (strstr(line, cdev_create) == line) { |
813 |
devicename = line + strlen(cdev_create) - strlen(cdev_path); |
814 |
memcpy(devicename, cdev_path, strlen(cdev_path)); |
815 |
walk = strchr(devicename, ' '); |
816 |
if (walk != NULL) |
817 |
walk[0] = '\0'; |
818 |
/* Blacklist is lazy-populated in device_added() */ |
819 |
dev_list_init(&blacklist); |
820 |
device_added(devicename, &blacklist); |
821 |
dev_list_destroy(&blacklist); |
822 |
} else if (strstr(line, cdev_destroy) == line) { |
823 |
devicename = line + strlen(cdev_destroy) - strlen(cdev_path); |
824 |
memcpy(devicename, cdev_path, strlen(cdev_path)); |
825 |
walk = strchr(devicename, ' '); |
826 |
if (walk != NULL) |
827 |
walk[0] = '\0'; |
828 |
device_removed(devicename); |
829 |
} |
830 |
free(line); |
831 |
} |
832 |
} |
833 |
|
834 |
static void |
835 |
block_handler(void *data, struct timeval **tv, void *read_mask) |
836 |
{ |
837 |
} |
838 |
|
839 |
int |
840 |
config_devd_init(void) |
841 |
{ |
842 |
struct dev_list blacklist; |
843 |
|
844 |
LogMessage(X_INFO, "config/devd: probing input devices...\n"); |
845 |
|
846 |
/* Blacklist is lazy-populated in device_added() */ |
847 |
dev_list_init(&blacklist); |
848 |
|
849 |
/* Check if kbdmux is enabled */ |
850 |
is_kbdmux = is_kbdmux_enabled(); |
851 |
|
852 |
/* Check if evdev support is compiled into kernel */ |
853 |
is_kernel_evdev = feature_present("evdev") != 0; |
854 |
|
855 |
/* Connect to devd, so that we don't loose any events */ |
856 |
if ((sock_devd = connect_devd()) < 0) |
857 |
return 0; |
858 |
|
859 |
/* Scan what is currently connected */ |
860 |
devpath_scan(&blacklist); |
861 |
|
862 |
/* Register wakeup handler */ |
863 |
RegisterBlockAndWakeupHandlers(block_handler, |
864 |
wakeup_handler, NULL); |
865 |
|
866 |
dev_list_destroy(&blacklist); |
867 |
|
868 |
return 1; |
869 |
} |
870 |
|
871 |
void |
872 |
config_devd_fini(void) |
873 |
{ |
874 |
LogMessage(X_INFO, "config/devd: terminating backend...\n"); |
875 |
|
876 |
if (rtimer) { |
877 |
TimerFree(rtimer); |
878 |
rtimer = NULL; |
879 |
} |
880 |
|
881 |
disconnect_devd(sock_devd); |
882 |
|
883 |
RemoveBlockAndWakeupHandlers(block_handler, |
884 |
wakeup_handler, NULL); |
885 |
} |