Line 0
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 |
* Copyright (c) 2015 - 2016 Rozhuk Ivan |
7 |
* |
8 |
* Permission is hereby granted, free of charge, to any person obtaining a |
9 |
* copy of this software and associated documentation files (the "Software"), |
10 |
* to deal in the Software without restriction, including without limitation |
11 |
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
12 |
* and/or sell copies of the Software, and to permit persons to whom the |
13 |
* Software is furnished to do so, subject to the following conditions: |
14 |
* |
15 |
* The above copyright notice and this permission notice (including the next |
16 |
* paragraph) shall be included in all copies or substantial portions of the |
17 |
* Software. |
18 |
* |
19 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
22 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
24 |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
25 |
* DEALINGS IN THE SOFTWARE. |
26 |
* |
27 |
* Author: Baptiste Daroussin <bapt@FreeBSD.org> |
28 |
*/ |
29 |
|
30 |
#ifdef HAVE_DIX_CONFIG_H |
31 |
#include <dix-config.h> |
32 |
#endif |
33 |
|
34 |
#include <sys/types.h> |
35 |
#include <sys/kbio.h> |
36 |
#include <sys/socket.h> |
37 |
#include <sys/stat.h> |
38 |
#include <sys/sysctl.h> |
39 |
#include <sys/un.h> |
40 |
#include <sys/mouse.h> |
41 |
#include <sys/consio.h> |
42 |
#include <linux/input.h> |
43 |
|
44 |
#include <ctype.h> |
45 |
#include <dirent.h> |
46 |
#include <errno.h> |
47 |
#include <fcntl.h> |
48 |
#include <stdlib.h> |
49 |
#include <stdio.h> |
50 |
#include <stdbool.h> |
51 |
#include <unistd.h> |
52 |
#include <string.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 |
|
62 |
/* from <linux/joystick.h> */ |
63 |
/* get identifier string */ |
64 |
#define JSIOCGNAME(len) _IOC(_IOC_READ, 'j', 0x13, len) |
65 |
|
66 |
#define WEBCAMD_IOCTL_GET_USB_VENDOR_ID _IOR('q', 250, unsigned short) |
67 |
#define WEBCAMD_IOCTL_GET_USB_PRODUCT_ID _IOR('q', 251, unsigned short) |
68 |
#define WEBCAMD_IOCTL_GET_USB_SPEED _IOR('q', 252, unsigned int) |
69 |
|
70 |
|
71 |
#define _PATH_DEV_LEN (sizeof(_PATH_DEV) - 1) |
72 |
#define DEVD_PATH_DEV "devd:" _PATH_DEV |
73 |
#define DEVD_PATH_DEV_LEN (sizeof(DEVD_PATH_DEV) - 1) |
74 |
|
75 |
#define DEVD_SOCK_PATH _PATH_VARRUN "devd.pipe" |
76 |
|
77 |
#define DEVD_EVENT_ADD '+' |
78 |
#define DEVD_EVENT_REMOVE '-' |
79 |
#define DEVD_EVENT_NOTIFY '!' |
80 |
|
81 |
#define RECONNECT_DELAY (5 * 1000) |
82 |
|
83 |
|
84 |
#define is_meuqual(__v1, __v1sz, __v2, __v2sz) \ |
85 |
((__v1sz) == (__v2sz) && NULL != (__v1) && NULL != (__v2) && \ |
86 |
0 == memcmp((__v1), (__v2), (__v1sz))) |
87 |
|
88 |
#define is_meuqual_cstr(__cstr, __v, __vsz) \ |
89 |
is_meuqual(__cstr, (sizeof(__cstr) - 1), __v, __vsz) |
90 |
|
91 |
#define is_de_euqual_cstr(__de, __cstr) \ |
92 |
(NULL != (__de) && \ |
93 |
is_meuqual((__de)->d_name, (__de)->d_namlen, __cstr, (sizeof(__cstr) - 1))) |
94 |
|
95 |
#define devd_get_val_cstr(__cstr, __buf, __bufsz, __valsz) \ |
96 |
devd_get_val((__buf), (__bufsz), __cstr, (sizeof(__cstr) - 1), \ |
97 |
(__valsz)) |
98 |
|
99 |
|
100 |
static int devd_skt; |
101 |
static char devd_buf[4096]; |
102 |
static size_t devd_buf_used = 0; |
103 |
static int is_kbdmux = 0; |
104 |
OsTimerPtr rtimer; |
105 |
|
106 |
|
107 |
/* Input devices. */ |
108 |
typedef struct hw_type_s { |
109 |
const char *dev_name; |
110 |
size_t dev_name_size; |
111 |
size_t path_offset; |
112 |
int flags; |
113 |
const char *xdriver; |
114 |
} hw_type_t, *hw_type_p; |
115 |
|
116 |
/* xdriver can be set via config "InputClass" section. |
117 |
* Do not set xdriver name if device have more than one |
118 |
* xf86-input-* drivers. |
119 |
* "input/event" can be hadled by: xf86-input-evdev and |
120 |
* xf86-input-wacom, let user chooses. |
121 |
*/ |
122 |
static hw_type_t hw_types[] = { |
123 |
{ "ukbd", 4, 0, ATTR_KEYBOARD, "kbd" }, |
124 |
{ "atkbd", 5, 0, ATTR_KEYBOARD, "kbd" }, |
125 |
{ "kbdmux", 6, 0, ATTR_KEYBOARD, "kbd" }, |
126 |
{ "sysmouse", 8, 0, ATTR_POINTER, "mouse" }, |
127 |
{ "ums", 3, 0, ATTR_POINTER, "mouse" }, |
128 |
{ "psm", 3, 0, ATTR_POINTER, "mouse" }, |
129 |
{ "vboxguest", 9, 0, ATTR_POINTER, "vboxmouse" }, |
130 |
{ "joy", 3, 0, ATTR_JOYSTICK, NULL }, |
131 |
{ "atp", 3, 0, ATTR_TOUCHPAD, NULL }, |
132 |
{ "uep", 3, 0, ATTR_TOUCHSCREEN, NULL }, |
133 |
{ "input/event",5, 6, ATTR_TOUCHPAD, NULL }, |
134 |
{ "input/js", 2, 6, ATTR_JOYSTICK, NULL }, |
135 |
{ NULL, 0, 0, 0, NULL }, |
136 |
}; |
137 |
|
138 |
/* Input devices paths. */ |
139 |
static hw_type_t hw_type_path[] = { |
140 |
{ "input/", 0, 6, 0, NULL }, |
141 |
{ NULL, 0, 0, 0, NULL }, |
142 |
}; |
143 |
|
144 |
|
145 |
|
146 |
static hw_type_p |
147 |
get_dev_type_by_name(const char *dev_name, size_t dev_name_size) |
148 |
{ |
149 |
size_t i; |
150 |
|
151 |
if (NULL == dev_name || 0 == dev_name_size) |
152 |
return (NULL); |
153 |
for (i = 0; NULL != hw_types[i].dev_name; i ++) { |
154 |
if (dev_name_size >= (hw_types[i].dev_name_size + hw_types[i].path_offset) && |
155 |
0 == memcmp(dev_name, hw_types[i].dev_name, |
156 |
(hw_types[i].path_offset + hw_types[i].dev_name_size))) { |
157 |
return (&hw_types[i]); |
158 |
} |
159 |
} |
160 |
return (NULL); |
161 |
} |
162 |
|
163 |
static hw_type_p |
164 |
get_dev_type_by_path(const char *dev_name, size_t dev_name_size, |
165 |
hw_type_p hw_type_cust) |
166 |
{ |
167 |
size_t i; |
168 |
|
169 |
if (NULL == dev_name || 0 == dev_name_size || NULL == hw_type_cust) |
170 |
return (NULL); |
171 |
for (i = 0; NULL != hw_type_path[i].dev_name; i ++) { |
172 |
if (dev_name_size <= hw_type_path[i].path_offset || |
173 |
0 != memcmp(dev_name, hw_type_path[i].dev_name, |
174 |
hw_type_path[i].path_offset)) |
175 |
continue; |
176 |
/* Path in white list. */ |
177 |
hw_type_cust->dev_name = dev_name; |
178 |
hw_type_cust->path_offset = hw_type_path[i].path_offset; |
179 |
for (i = hw_type_cust->path_offset; i < dev_name_size; i ++) { |
180 |
if (isdigit(dev_name[i])) |
181 |
break; |
182 |
} |
183 |
hw_type_cust->dev_name_size = (i - hw_type_cust->path_offset); |
184 |
hw_type_cust->flags = hw_type_path[i].flags; |
185 |
hw_type_cust->xdriver = hw_type_path[i].xdriver; |
186 |
return (hw_type_cust); |
187 |
} |
188 |
return (NULL); |
189 |
} |
190 |
|
191 |
static int |
192 |
is_kbdmux_enabled(void) |
193 |
{ |
194 |
/* Xorg uses /dev/ttyv0 as a console device */ |
195 |
/* const char device[]="/dev/console"; */ |
196 |
int fd; |
197 |
const char *device = _PATH_TTY "v0"; |
198 |
keyboard_info_t info; |
199 |
|
200 |
fd = open(device, O_RDONLY); |
201 |
if (0 > fd) |
202 |
return (0); |
203 |
if (-1 == ioctl(fd, KDGKBINFO, &info) || |
204 |
0 != memcmp(info.kb_name, "kbdmux", 6)) { |
205 |
close(fd); |
206 |
return (0); |
207 |
} |
208 |
close(fd); |
209 |
return (1); |
210 |
} |
211 |
|
212 |
static char * |
213 |
sysctl_get_str(const char *sysctlname, size_t *size_ret) |
214 |
{ |
215 |
char *dest; |
216 |
size_t len; |
217 |
|
218 |
if (NULL == sysctlname) |
219 |
return (NULL); |
220 |
if (0 != sysctlbyname(sysctlname, NULL, &len, NULL, 0)) |
221 |
return (NULL); |
222 |
dest = malloc(len + 4); |
223 |
if (NULL == dest) |
224 |
return (NULL); |
225 |
if (0 != sysctlbyname(sysctlname, dest, &len, NULL, 0)) { |
226 |
free(dest); |
227 |
return (NULL); |
228 |
} |
229 |
dest[len] = 0; |
230 |
if (NULL != size_ret) |
231 |
(*size_ret) = len; |
232 |
return (dest); |
233 |
} |
234 |
|
235 |
static char * |
236 |
devd_get_val(char *buf, size_t buf_size, const char *val_name, |
237 |
size_t val_name_size, size_t *val_size) |
238 |
{ |
239 |
char *ret, *buf_end, *ptr; |
240 |
|
241 |
buf_end = (buf + buf_size); |
242 |
for (ret = buf; ret != NULL && ret < buf_end;) { |
243 |
ret = memmem(ret, (buf_end - ret), val_name, val_name_size); |
244 |
if (ret == NULL) |
245 |
return (NULL); |
246 |
/* Found. */ |
247 |
/* Check: space before or buf+1. */ |
248 |
if ((buf + 1) < ret && ret[-1] != ' ') { |
249 |
ret += val_name_size; |
250 |
continue; |
251 |
} |
252 |
/* Check: = after name and size for value. */ |
253 |
ret += val_name_size; |
254 |
if ((ret + 1) >= buf_end) |
255 |
return (NULL); |
256 |
if ('=' != ret[0]) |
257 |
continue; |
258 |
ret ++; |
259 |
break; |
260 |
} |
261 |
if (ret == NULL || val_size == NULL) |
262 |
return (ret); |
263 |
/* Calc value data size. */ |
264 |
ptr = memchr(ret, ' ', (buf_end - ret)); |
265 |
if (ptr == NULL) /* End of string/last value. */ |
266 |
ptr = buf_end; |
267 |
(*val_size) = (ptr - ret); |
268 |
return (ret); |
269 |
} |
270 |
|
271 |
static void |
272 |
device_added(const char *dev_name, size_t dev_name_size, int allow_no_device) |
273 |
{ |
274 |
int fd; |
275 |
InputOption *options = NULL; |
276 |
InputAttributes attrs; |
277 |
DeviceIntPtr dev_iptr = NULL; |
278 |
keyboard_info_t kbdi; |
279 |
mousehw_t mshw; |
280 |
struct input_id iid; |
281 |
char *dev_path, config_info[(PATH_MAX + 32)]; |
282 |
char sysctlname[PATH_MAX], *sdata, *ptr_pid, *ptr_vid; |
283 |
char pnp_usb_id[PATH_MAX], vendor[PATH_MAX], product[PATH_MAX]; |
284 |
size_t sdata_size, pid_size, vid_size; |
285 |
hw_type_t *hwtype, hwtype_cust; |
286 |
unsigned short vid, pid; |
287 |
|
288 |
|
289 |
if (NULL == dev_name || 0 == dev_name_size || PATH_MAX < dev_name_size) |
290 |
return; |
291 |
/* Make dev_name null ended string. */ |
292 |
memcpy(config_info, DEVD_PATH_DEV, DEVD_PATH_DEV_LEN); |
293 |
memcpy((config_info + DEVD_PATH_DEV_LEN), dev_name, dev_name_size); |
294 |
config_info[(DEVD_PATH_DEV_LEN + dev_name_size)] = 0; |
295 |
/* Set / update pointers to dev_name and dev_path. */ |
296 |
dev_path = (config_info + 5); /* Skip: "devd:" */ |
297 |
dev_name = (dev_path + _PATH_DEV_LEN); /* Skip: "/dev/" */ |
298 |
|
299 |
/* Is known input device or path? */ |
300 |
hwtype = get_dev_type_by_name(dev_name, dev_name_size); |
301 |
if (NULL == hwtype) { |
302 |
hwtype = get_dev_type_by_path(dev_name, dev_name_size, |
303 |
&hwtype_cust); |
304 |
} |
305 |
if (NULL == hwtype) /* Not found in white list. */ |
306 |
return; |
307 |
/* Init and set attributes. */ |
308 |
memset(&attrs, 0, sizeof(attrs)); |
309 |
attrs.device = dev_path; |
310 |
attrs.flags = hwtype->flags; |
311 |
|
312 |
/* Skip keyboard devices if kbdmux is enabled */ |
313 |
if (is_kbdmux && 0 == allow_no_device && (hwtype->flags & ATTR_KEYBOARD)) { |
314 |
LogMessage(X_INFO, "config/devd: kbdmux is enabled, ignoring device %s\n", |
315 |
dev_name); |
316 |
return; |
317 |
} |
318 |
/* Skip duplicate devices. */ |
319 |
if (device_is_duplicate(config_info)) { |
320 |
LogMessage(X_WARNING, "config/devd: device %s already added. ignoring\n", |
321 |
dev_path); |
322 |
return; |
323 |
} |
324 |
|
325 |
/* Try to open device. */ |
326 |
fd = open(dev_path, O_RDONLY); |
327 |
if (0 > fd) { |
328 |
if (0 == (hwtype->flags & ATTR_KEYBOARD)) { |
329 |
LogMessage(X_INFO, "config/devd: device %s already opened\n", |
330 |
dev_path); |
331 |
/* |
332 |
* Fail if cannot open device, it breaks AllowMouseOpenFail, |
333 |
* but it should not matter when config/devd enabled |
334 |
*/ |
335 |
return; |
336 |
} |
337 |
if (0 == allow_no_device) { |
338 |
/* |
339 |
* There can be only one keyboard attached to console and |
340 |
* it is already added. |
341 |
*/ |
342 |
LogMessage(X_WARNING, "config/devd: console keyboard is " |
343 |
"already added, ignoring %s\n", |
344 |
dev_path); |
345 |
return; |
346 |
} |
347 |
/* |
348 |
* Don't pass "device" option if the keyboard is already |
349 |
* attached to the console (ie. open() fails). |
350 |
* This would activate a special logic in xf86-input-keyboard. |
351 |
* Prevent any other attached to console keyboards being |
352 |
* processed. There can be only one such device. |
353 |
*/ |
354 |
goto skip_ioctl; |
355 |
} |
356 |
|
357 |
/* Try to get device info via ioctl(). */ |
358 |
if (-1 != ioctl(fd, KDGKBINFO, &kbdi)) { /* Is this keyboard? */ |
359 |
memcpy(product, kbdi.kb_name, sizeof(kbdi.kb_name)); |
360 |
attrs.product = product; |
361 |
attrs.flags = ATTR_KEYBOARD; |
362 |
LogMessage(X_INFO, "config/devd: detected keyboard: %s, kb_index=%i, kb_unit=%i, kb_type=%i, kb_config=%i\n", |
363 |
kbdi.kb_name, kbdi.kb_index, kbdi.kb_unit, kbdi.kb_type, kbdi.kb_config); |
364 |
} else if (-1 != ioctl(fd, MOUSE_GETHWINFO, &mshw)) { /* Is this mouse? */ |
365 |
#ifdef notyet /* FreeBSD mouse drivers does not return real vid+pid. */ |
366 |
/* construct USB ID in lowercase hex - "0000:ffff" */ |
367 |
snprintf(pnp_usb_id, sizeof(pnp_usb_id), "%04x:%04x", |
368 |
mshw.hwid, mshw.model); |
369 |
attrs.usb_id = pnp_usb_id; |
370 |
#endif |
371 |
|
372 |
switch (mshw.type) { |
373 |
case MOUSE_MOUSE: |
374 |
case MOUSE_TRACKBALL: |
375 |
case MOUSE_STICK: |
376 |
attrs.flags = ATTR_POINTER; |
377 |
break; |
378 |
case MOUSE_PAD: |
379 |
attrs.flags = ATTR_TOUCHPAD; |
380 |
break; |
381 |
} |
382 |
LogMessage(X_INFO, "config/devd: detected mouse: hwid=%04x, model=%04x, type=%04x, iftype=%04x, buttons=%04x\n", |
383 |
mshw.hwid, mshw.model, mshw.type, mshw.iftype, mshw.buttons); |
384 |
} else if (-1 != ioctl(fd, JSIOCGNAME((sizeof(product) - 1)), product)) { /* Is this joystick? */ |
385 |
attrs.product = product; |
386 |
attrs.flags = ATTR_JOYSTICK; |
387 |
LogMessage(X_INFO, "config/devd: detected joystick: %s\n", |
388 |
product); |
389 |
} else if (-1 != ioctl(fd, EVIOCGID, &iid) && |
390 |
-1 != ioctl(fd, EVIOCGNAME((sizeof(product) - 1)), product)) { /* Is this event? */ |
391 |
/* construct USB ID in lowercase hex - "0000:ffff" */ |
392 |
snprintf(pnp_usb_id, sizeof(pnp_usb_id), "%04x:%04x", |
393 |
iid.vendor, iid.product); |
394 |
attrs.usb_id = pnp_usb_id; |
395 |
attrs.product = product; |
396 |
attrs.flags = (ATTR_POINTER | ATTR_TABLET | ATTR_TOUCHPAD); |
397 |
LogMessage(X_INFO, "config/devd: detected event: %s, bustype=%04x, vendor=%04x, product=%04x, version=%04x\n", |
398 |
product, iid.bustype, iid.vendor, iid.product, iid.version); |
399 |
} |
400 |
|
401 |
if (NULL == attrs.usb_id) { /* Is this webcamd device? */ |
402 |
if (-1 != ioctl(fd, WEBCAMD_IOCTL_GET_USB_VENDOR_ID, &vid) && |
403 |
-1 != ioctl(fd, WEBCAMD_IOCTL_GET_USB_PRODUCT_ID, &pid)) { |
404 |
snprintf(pnp_usb_id, sizeof(pnp_usb_id), |
405 |
"%04x:%04x", vid, pid); |
406 |
attrs.usb_id = pnp_usb_id; |
407 |
LogMessage(X_INFO, "config/devd: WebCamD device: %s\n", pnp_usb_id); |
408 |
} |
409 |
} |
410 |
|
411 |
close(fd); |
412 |
|
413 |
skip_ioctl: |
414 |
/* Try to get device info via sysctl(). */ |
415 |
if (NULL == attrs.usb_id && NULL == attrs.pnp_id) { |
416 |
snprintf(sysctlname, sizeof(sysctlname), "dev.%.*s.%s.%%pnpinfo", |
417 |
(int)hwtype->dev_name_size, (hwtype->dev_name + hwtype->path_offset), |
418 |
(dev_name + hwtype->path_offset + hwtype->dev_name_size)); |
419 |
sdata = sysctl_get_str(sysctlname, &sdata_size); |
420 |
if (NULL != sdata) { |
421 |
ptr_vid = devd_get_val_cstr("vendor", |
422 |
sdata, sdata_size, &vid_size); |
423 |
ptr_pid = devd_get_val_cstr("product", |
424 |
sdata, sdata_size, &pid_size); |
425 |
if (NULL != ptr_vid && NULL != ptr_pid) { /* usb_id */ |
426 |
ptr_vid[vid_size] = 0; |
427 |
ptr_pid[pid_size] = 0; |
428 |
snprintf(pnp_usb_id, sizeof(pnp_usb_id), |
429 |
"%s:%s", ptr_vid, ptr_pid); |
430 |
attrs.usb_id = pnp_usb_id; |
431 |
LogMessage(X_INFO, "config/devd: usb_id: %s\n", pnp_usb_id); |
432 |
} else { /* pnp_id */ |
433 |
strlcpy(pnp_usb_id, sdata, sizeof(pnp_usb_id)); |
434 |
attrs.pnp_id = pnp_usb_id; |
435 |
} |
436 |
free(sdata); |
437 |
} |
438 |
} |
439 |
if (NULL == attrs.vendor || NULL == attrs.product) { |
440 |
snprintf(sysctlname, sizeof(sysctlname), "dev.%.*s.%s.%%desc", |
441 |
(int)hwtype->dev_name_size, (hwtype->dev_name + hwtype->path_offset), |
442 |
(dev_name + hwtype->path_offset + hwtype->dev_name_size)); |
443 |
sdata = sysctl_get_str(sysctlname, &sdata_size); |
444 |
if (NULL != sdata) { |
445 |
/* Vendor. */ |
446 |
ptr_pid = memchr(sdata, ' ', sdata_size); |
447 |
if (NULL != ptr_pid) |
448 |
ptr_pid[0] = 0; |
449 |
strlcpy(vendor, sdata, sizeof(vendor)); |
450 |
attrs.vendor = vendor; |
451 |
/* Product. */ |
452 |
if (NULL == attrs.product && NULL != ptr_pid) { |
453 |
ptr_pid ++; |
454 |
ptr_vid = memchr(ptr_pid, ',', |
455 |
(sdata_size - (ptr_pid - sdata))); |
456 |
if (NULL != ptr_vid) |
457 |
ptr_vid[0] = 0; |
458 |
strlcpy(product, ptr_pid, sizeof(product)); |
459 |
attrs.product = product; |
460 |
} else { |
461 |
product[0] = 0; |
462 |
} |
463 |
free(sdata); |
464 |
LogMessage(X_INFO, "config/devd: vendor: %s, product: %s\n", vendor, product); |
465 |
} |
466 |
} |
467 |
|
468 |
/* Init options. */ |
469 |
options = input_option_new(NULL, "_source", "server/devd"); |
470 |
if (NULL == options) |
471 |
goto err_out; |
472 |
|
473 |
options = input_option_new(options, "config_info", config_info); |
474 |
if (NULL == options) |
475 |
goto err_out; |
476 |
|
477 |
options = input_option_new(options, "name", |
478 |
((NULL != attrs.product) ? attrs.product : dev_name)); |
479 |
if (NULL == options) |
480 |
goto err_out; |
481 |
|
482 |
if (fd >= 0) { |
483 |
options = input_option_new(options, "device", dev_path); |
484 |
if (NULL == options) |
485 |
goto err_out; |
486 |
} |
487 |
|
488 |
if (NULL != hwtype->xdriver) { |
489 |
options = input_option_new(options, "driver", hwtype->xdriver); |
490 |
if (NULL == options) |
491 |
goto err_out; |
492 |
} |
493 |
|
494 |
LogMessage(X_INFO, "config/devd: adding input device %s\n", dev_path); |
495 |
NewInputDeviceRequest(options, &attrs, &dev_iptr); |
496 |
goto done; |
497 |
|
498 |
err_out: |
499 |
LogMessage(X_INFO, "config/devd: error adding device '%s'\n", |
500 |
dev_path); |
501 |
done: |
502 |
input_option_free_list(&options); |
503 |
} |
504 |
|
505 |
static void |
506 |
device_removed(const char *dev_name, size_t dev_name_size) |
507 |
{ |
508 |
char config_info[(PATH_MAX + 32)]; |
509 |
hw_type_t hwtype_cust; |
510 |
|
511 |
if (NULL == dev_name || 0 == dev_name_size || PATH_MAX < dev_name_size) |
512 |
return; |
513 |
if (NULL == get_dev_type_by_name(dev_name, dev_name_size) && |
514 |
NULL == get_dev_type_by_path(dev_name, dev_name_size, &hwtype_cust)) |
515 |
return; /* Device not in list - unknown. */ |
516 |
memcpy(config_info, DEVD_PATH_DEV, DEVD_PATH_DEV_LEN); |
517 |
memcpy((config_info + DEVD_PATH_DEV_LEN), dev_name, dev_name_size); |
518 |
config_info[(DEVD_PATH_DEV_LEN + dev_name_size)] = 0; |
519 |
|
520 |
/* Skip non added devices. */ |
521 |
if (device_is_duplicate(config_info)) { |
522 |
LogMessage(X_INFO, "config/devd: removing input device '%s'\n", |
523 |
(config_info + DEVD_PATH_DEV_LEN)); |
524 |
} |
525 |
remove_devices("devd", config_info); |
526 |
} |
527 |
|
528 |
|
529 |
static void |
530 |
disconnect_devd(int sock) |
531 |
{ |
532 |
if (0 > sock) |
533 |
return; |
534 |
RemoveGeneralSocket(sock); |
535 |
close(sock); |
536 |
} |
537 |
|
538 |
static int |
539 |
connect_devd(void) |
540 |
{ |
541 |
int sock; |
542 |
struct sockaddr_un devd; |
543 |
|
544 |
sock = socket(AF_UNIX, SOCK_STREAM, 0); |
545 |
if (0 > sock) { |
546 |
LogMessage(X_ERROR, "config/devd: fail opening stream socket\n"); |
547 |
return (-1); |
548 |
} |
549 |
|
550 |
devd.sun_family = AF_UNIX; |
551 |
memcpy(devd.sun_path, DEVD_SOCK_PATH, sizeof(DEVD_SOCK_PATH)); |
552 |
if (0 > connect(sock, (struct sockaddr*)&devd, sizeof(devd))) { |
553 |
close(sock); |
554 |
LogMessage(X_ERROR, "config/devd: fail to connect to devd\n"); |
555 |
return (-1); |
556 |
} |
557 |
|
558 |
AddGeneralSocket(sock); |
559 |
|
560 |
return (sock); |
561 |
} |
562 |
|
563 |
static CARD32 |
564 |
reconnect_handler(OsTimerPtr timer, CARD32 time, void *arg) |
565 |
{ |
566 |
devd_buf_used = 0; |
567 |
devd_skt = connect_devd(); |
568 |
if (-1 == devd_skt) /* Try again after RECONNECT_DELAY */ |
569 |
return (RECONNECT_DELAY); |
570 |
TimerFree(rtimer); |
571 |
rtimer = NULL; |
572 |
LogMessage(X_INFO, "config/devd: reopening devd socket\n"); |
573 |
|
574 |
return (0); |
575 |
} |
576 |
|
577 |
static void |
578 |
wakeup_handler(void *data, int err, void *read_mask) |
579 |
{ |
580 |
int error; |
581 |
char *ptr, *line, *val, *cdev; |
582 |
size_t line_size, val_size, cdev_size; |
583 |
ssize_t ios; |
584 |
|
585 |
if (0 > err) |
586 |
return; |
587 |
if (!FD_ISSET(devd_skt, (fd_set*)read_mask)) |
588 |
return; |
589 |
/* Read new data. */ |
590 |
for (;;) { |
591 |
ios = recv(devd_skt, (devd_buf + devd_buf_used), |
592 |
(sizeof(devd_buf) - devd_buf_used), MSG_DONTWAIT); |
593 |
if (0 < ios) { /* Read OK. */ |
594 |
devd_buf_used += ios; |
595 |
continue; /* Try to read more. */ |
596 |
} |
597 |
/* Something wrong. */ |
598 |
error = errno; |
599 |
if (error == EAGAIN) |
600 |
break; /* All avaible data readed. */ |
601 |
if (error == EINTR) |
602 |
continue; |
603 |
if (sizeof(devd_buf) == devd_buf_used) { /* Message to long, reset buf. */ |
604 |
devd_buf_used = 0; |
605 |
continue; |
606 |
} |
607 |
/* devd socket is lost */ |
608 |
disconnect_devd(devd_skt); |
609 |
rtimer = TimerSet(NULL, 0, 1, reconnect_handler, NULL); |
610 |
LogMessage(X_WARNING, "config/devd: devd socket read error: %i - %s\n", error, strerror(error)); |
611 |
return; |
612 |
} |
613 |
ptr = memchr(devd_buf, '\n', devd_buf_used); |
614 |
if (NULL == ptr) |
615 |
return; |
616 |
|
617 |
/* Process data. */ |
618 |
line = (devd_buf + 1); |
619 |
for (;;) { |
620 |
line_size = (ptr - line); |
621 |
if (DEVD_EVENT_NOTIFY != (*(line - 1))) |
622 |
goto move_next_event; /* Handle only notify. */ |
623 |
/* Check: is system=DEVFS. */ |
624 |
val = devd_get_val_cstr("system", line, line_size, &val_size); |
625 |
if (!is_meuqual_cstr("DEVFS", val, val_size)) |
626 |
goto move_next_event; |
627 |
/* Check: is subsystem=CDEV. */ |
628 |
val = devd_get_val_cstr("subsystem", line, line_size, &val_size); |
629 |
if (!is_meuqual_cstr("CDEV", val, val_size)) |
630 |
goto move_next_event; |
631 |
/* Get device name. */ |
632 |
cdev = devd_get_val_cstr("cdev", line, line_size, &cdev_size); |
633 |
if (NULL == cdev) |
634 |
goto move_next_event; |
635 |
/* Get event type. */ |
636 |
val = devd_get_val_cstr("type", line, line_size, &val_size); |
637 |
if (is_meuqual_cstr("CREATE", val, val_size)) { |
638 |
device_added(cdev, cdev_size, 0); |
639 |
} else if (is_meuqual_cstr("DESTROY", val, val_size)) { |
640 |
device_removed(cdev, cdev_size); |
641 |
} |
642 |
|
643 |
move_next_event: |
644 |
line += (line_size + 2); /* Skip '\n' and event type byte. */ |
645 |
line_size = (line - devd_buf); |
646 |
if (devd_buf_used <= line_size) { |
647 |
devd_buf_used = 0; |
648 |
return; |
649 |
} |
650 |
ptr = memchr(line, '\n', (devd_buf_used - line_size)); |
651 |
if (NULL == ptr) |
652 |
break; |
653 |
} |
654 |
/* Save line without end marker. */ |
655 |
devd_buf_used -= (line_size - 1); |
656 |
memmove(devd_buf, (line - 1), devd_buf_used); |
657 |
} |
658 |
|
659 |
static void |
660 |
block_handler(void *data, struct timeval **tv, void *read_mask) |
661 |
{ |
662 |
} |
663 |
|
664 |
int |
665 |
config_devd_init(void) |
666 |
{ |
667 |
size_t i, j, dir_cnt, sdir_cnt, tm; |
668 |
char devicename[PATH_MAX]; |
669 |
struct dirent *de, **namelist, *sde, **snamelist; |
670 |
|
671 |
LogMessage(X_INFO, "config/devd: probing input devices...\n"); |
672 |
|
673 |
/* |
674 |
* Add fake keyboard and give up on keyboards management |
675 |
* if kbdmux is enabled |
676 |
*/ |
677 |
is_kbdmux = is_kbdmux_enabled(); |
678 |
if (is_kbdmux) |
679 |
device_added("kbdmux0", 7, 1); |
680 |
|
681 |
/* Scan /dev/ for devices. */ |
682 |
dir_cnt = scandir(_PATH_DEV, &namelist, 0, alphasort); |
683 |
for (i = 0; i < dir_cnt; i ++) { |
684 |
de = namelist[i]; |
685 |
if (is_de_euqual_cstr(de, ".") || |
686 |
is_de_euqual_cstr(de, "..")) { |
687 |
free(de); |
688 |
continue; |
689 |
} |
690 |
if (DT_DIR != de->d_type) { |
691 |
device_added(de->d_name, de->d_namlen, 0); |
692 |
} else { /* Sub folder. */ |
693 |
snprintf(devicename, sizeof(devicename), |
694 |
_PATH_DEV "%s", de->d_name); |
695 |
sdir_cnt = scandir(devicename, &snamelist, 0, alphasort); |
696 |
for (j = 0; j < sdir_cnt; j ++) { |
697 |
sde = snamelist[j]; |
698 |
if (!is_de_euqual_cstr(sde, ".") && |
699 |
!is_de_euqual_cstr(sde, "..") && |
700 |
DT_DIR != sde->d_type) { |
701 |
tm = snprintf(devicename, sizeof(devicename), |
702 |
"%s/%s", de->d_name, sde->d_name); |
703 |
device_added(devicename, tm, 0); |
704 |
} |
705 |
free(sde); |
706 |
} |
707 |
free(snamelist); |
708 |
} |
709 |
free(de); |
710 |
} |
711 |
free(namelist); |
712 |
|
713 |
devd_buf_used = 0; |
714 |
devd_skt = connect_devd(); |
715 |
if (-1 == devd_skt) |
716 |
return (0); |
717 |
|
718 |
/* Register wakeup handler */ |
719 |
RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); |
720 |
|
721 |
return (1); |
722 |
} |
723 |
|
724 |
void |
725 |
config_devd_fini(void) |
726 |
{ |
727 |
LogMessage(X_INFO, "config/devd: terminating backend...\n"); |
728 |
|
729 |
if (rtimer) { |
730 |
TimerFree(rtimer); |
731 |
rtimer = NULL; |
732 |
} |
733 |
|
734 |
disconnect_devd(devd_skt); |
735 |
|
736 |
RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); |
737 |
} |