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