1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 /*
23 * To list the properties of a device, try something like:
24 * udevadm info -a -n snd/hwC0D0 (for a sound card)
25 * udevadm info --query=all -n input/event3 (for a keyboard, mouse, etc)
26 * udevadm info --query=property -n input/event2
27 */
28 #include "SDL_udev.h"
29
30 #ifdef SDL_USE_LIBUDEV
31
32 #include <linux/input.h>
33
34 #include "SDL.h"
35
36 static const char* SDL_UDEV_LIBS[] = { "libudev.so.1", "libudev.so.0" };
37
38 #define _THIS SDL_UDEV_PrivateData *_this
39 static _THIS = NULL;
40
41 static SDL_bool SDL_UDEV_load_sym(const char *fn, void **addr);
42 static int SDL_UDEV_load_syms(void);
43 static SDL_bool SDL_UDEV_hotplug_update_available(void);
44 static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev);
45
46 static SDL_bool
SDL_UDEV_load_sym(const char * fn,void ** addr)47 SDL_UDEV_load_sym(const char *fn, void **addr)
48 {
49 *addr = SDL_LoadFunction(_this->udev_handle, fn);
50 if (*addr == NULL) {
51 /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
52 return SDL_FALSE;
53 }
54
55 return SDL_TRUE;
56 }
57
58 static int
SDL_UDEV_load_syms(void)59 SDL_UDEV_load_syms(void)
60 {
61 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
62 #define SDL_UDEV_SYM(x) \
63 if (!SDL_UDEV_load_sym(#x, (void **) (char *) & _this->x)) return -1
64
65 SDL_UDEV_SYM(udev_device_get_action);
66 SDL_UDEV_SYM(udev_device_get_devnode);
67 SDL_UDEV_SYM(udev_device_get_subsystem);
68 SDL_UDEV_SYM(udev_device_get_parent_with_subsystem_devtype);
69 SDL_UDEV_SYM(udev_device_get_property_value);
70 SDL_UDEV_SYM(udev_device_get_sysattr_value);
71 SDL_UDEV_SYM(udev_device_new_from_syspath);
72 SDL_UDEV_SYM(udev_device_unref);
73 SDL_UDEV_SYM(udev_enumerate_add_match_property);
74 SDL_UDEV_SYM(udev_enumerate_add_match_subsystem);
75 SDL_UDEV_SYM(udev_enumerate_get_list_entry);
76 SDL_UDEV_SYM(udev_enumerate_new);
77 SDL_UDEV_SYM(udev_enumerate_scan_devices);
78 SDL_UDEV_SYM(udev_enumerate_unref);
79 SDL_UDEV_SYM(udev_list_entry_get_name);
80 SDL_UDEV_SYM(udev_list_entry_get_next);
81 SDL_UDEV_SYM(udev_monitor_enable_receiving);
82 SDL_UDEV_SYM(udev_monitor_filter_add_match_subsystem_devtype);
83 SDL_UDEV_SYM(udev_monitor_get_fd);
84 SDL_UDEV_SYM(udev_monitor_new_from_netlink);
85 SDL_UDEV_SYM(udev_monitor_receive_device);
86 SDL_UDEV_SYM(udev_monitor_unref);
87 SDL_UDEV_SYM(udev_new);
88 SDL_UDEV_SYM(udev_unref);
89 SDL_UDEV_SYM(udev_device_new_from_devnum);
90 SDL_UDEV_SYM(udev_device_get_devnum);
91 #undef SDL_UDEV_SYM
92
93 return 0;
94 }
95
96 static SDL_bool
SDL_UDEV_hotplug_update_available(void)97 SDL_UDEV_hotplug_update_available(void)
98 {
99 if (_this->udev_mon != NULL) {
100 const int fd = _this->udev_monitor_get_fd(_this->udev_mon);
101 fd_set fds;
102 struct timeval tv;
103
104 FD_ZERO(&fds);
105 FD_SET(fd, &fds);
106 tv.tv_sec = 0;
107 tv.tv_usec = 0;
108 if ((select(fd+1, &fds, NULL, NULL, &tv) > 0) && (FD_ISSET(fd, &fds))) {
109 return SDL_TRUE;
110 }
111 }
112 return SDL_FALSE;
113 }
114
115
116 int
SDL_UDEV_Init(void)117 SDL_UDEV_Init(void)
118 {
119 int retval = 0;
120
121 if (_this == NULL) {
122 _this = (SDL_UDEV_PrivateData *) SDL_calloc(1, sizeof(*_this));
123 if(_this == NULL) {
124 return SDL_OutOfMemory();
125 }
126
127 retval = SDL_UDEV_LoadLibrary();
128 if (retval < 0) {
129 SDL_UDEV_Quit();
130 return retval;
131 }
132
133 /* Set up udev monitoring
134 * Listen for input devices (mouse, keyboard, joystick, etc) and sound devices
135 */
136
137 _this->udev = _this->udev_new();
138 if (_this->udev == NULL) {
139 SDL_UDEV_Quit();
140 return SDL_SetError("udev_new() failed");
141 }
142
143 _this->udev_mon = _this->udev_monitor_new_from_netlink(_this->udev, "udev");
144 if (_this->udev_mon == NULL) {
145 SDL_UDEV_Quit();
146 return SDL_SetError("udev_monitor_new_from_netlink() failed");
147 }
148
149 _this->udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "input", NULL);
150 _this->udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "sound", NULL);
151 _this->udev_monitor_enable_receiving(_this->udev_mon);
152
153 /* Do an initial scan of existing devices */
154 SDL_UDEV_Scan();
155
156 }
157
158 _this->ref_count += 1;
159
160 return retval;
161 }
162
163 void
SDL_UDEV_Quit(void)164 SDL_UDEV_Quit(void)
165 {
166 SDL_UDEV_CallbackList *item;
167
168 if (_this == NULL) {
169 return;
170 }
171
172 _this->ref_count -= 1;
173
174 if (_this->ref_count < 1) {
175
176 if (_this->udev_mon != NULL) {
177 _this->udev_monitor_unref(_this->udev_mon);
178 _this->udev_mon = NULL;
179 }
180 if (_this->udev != NULL) {
181 _this->udev_unref(_this->udev);
182 _this->udev = NULL;
183 }
184
185 /* Remove existing devices */
186 while (_this->first != NULL) {
187 item = _this->first;
188 _this->first = _this->first->next;
189 SDL_free(item);
190 }
191
192 SDL_UDEV_UnloadLibrary();
193 SDL_free(_this);
194 _this = NULL;
195 }
196 }
197
198 void
SDL_UDEV_Scan(void)199 SDL_UDEV_Scan(void)
200 {
201 struct udev_enumerate *enumerate = NULL;
202 struct udev_list_entry *devs = NULL;
203 struct udev_list_entry *item = NULL;
204
205 if (_this == NULL) {
206 return;
207 }
208
209 enumerate = _this->udev_enumerate_new(_this->udev);
210 if (enumerate == NULL) {
211 SDL_UDEV_Quit();
212 SDL_SetError("udev_monitor_new_from_netlink() failed");
213 return;
214 }
215
216 _this->udev_enumerate_add_match_subsystem(enumerate, "input");
217 _this->udev_enumerate_add_match_subsystem(enumerate, "sound");
218
219 _this->udev_enumerate_scan_devices(enumerate);
220 devs = _this->udev_enumerate_get_list_entry(enumerate);
221 for (item = devs; item; item = _this->udev_list_entry_get_next(item)) {
222 const char *path = _this->udev_list_entry_get_name(item);
223 struct udev_device *dev = _this->udev_device_new_from_syspath(_this->udev, path);
224 if (dev != NULL) {
225 device_event(SDL_UDEV_DEVICEADDED, dev);
226 _this->udev_device_unref(dev);
227 }
228 }
229
230 _this->udev_enumerate_unref(enumerate);
231 }
232
233
234 void
SDL_UDEV_UnloadLibrary(void)235 SDL_UDEV_UnloadLibrary(void)
236 {
237 if (_this == NULL) {
238 return;
239 }
240
241 if (_this->udev_handle != NULL) {
242 SDL_UnloadObject(_this->udev_handle);
243 _this->udev_handle = NULL;
244 }
245 }
246
247 int
SDL_UDEV_LoadLibrary(void)248 SDL_UDEV_LoadLibrary(void)
249 {
250 int retval = 0, i;
251
252 if (_this == NULL) {
253 return SDL_SetError("UDEV not initialized");
254 }
255
256
257 if (_this->udev_handle == NULL) {
258 for( i = 0 ; i < SDL_arraysize(SDL_UDEV_LIBS); i++) {
259 _this->udev_handle = SDL_LoadObject(SDL_UDEV_LIBS[i]);
260 if (_this->udev_handle != NULL) {
261 retval = SDL_UDEV_load_syms();
262 if (retval < 0) {
263 SDL_UDEV_UnloadLibrary();
264 }
265 else {
266 break;
267 }
268 }
269 }
270
271 if (_this->udev_handle == NULL) {
272 retval = -1;
273 /* Don't call SDL_SetError(): SDL_LoadObject already did. */
274 }
275 }
276
277 return retval;
278 }
279
280 #define BITS_PER_LONG (sizeof(unsigned long) * 8)
281 #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
282 #define OFF(x) ((x)%BITS_PER_LONG)
283 #define BIT(x) (1UL<<OFF(x))
284 #define LONG(x) ((x)/BITS_PER_LONG)
285 #define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
286
get_caps(struct udev_device * dev,struct udev_device * pdev,const char * attr,unsigned long * bitmask,size_t bitmask_len)287 static void get_caps(struct udev_device *dev, struct udev_device *pdev, const char *attr, unsigned long *bitmask, size_t bitmask_len)
288 {
289 const char *value;
290 char text[4096];
291 char *word;
292 int i;
293 unsigned long v;
294
295 SDL_memset(bitmask, 0, bitmask_len*sizeof(*bitmask));
296 value = _this->udev_device_get_sysattr_value(pdev, attr);
297 if (!value) {
298 return;
299 }
300
301 SDL_strlcpy(text, value, sizeof(text));
302 i = 0;
303 while ((word = SDL_strrchr(text, ' ')) != NULL) {
304 v = SDL_strtoul(word+1, NULL, 16);
305 if (i < bitmask_len) {
306 bitmask[i] = v;
307 }
308 ++i;
309 *word = '\0';
310 }
311 v = SDL_strtoul(text, NULL, 16);
312 if (i < bitmask_len) {
313 bitmask[i] = v;
314 }
315 }
316
317 static int
guess_device_class(struct udev_device * dev)318 guess_device_class(struct udev_device *dev)
319 {
320 int devclass = 0;
321 struct udev_device *pdev;
322 unsigned long bitmask_ev[NBITS(EV_MAX)];
323 unsigned long bitmask_abs[NBITS(ABS_MAX)];
324 unsigned long bitmask_key[NBITS(KEY_MAX)];
325 unsigned long bitmask_rel[NBITS(REL_MAX)];
326 unsigned long keyboard_mask;
327
328 /* walk up the parental chain until we find the real input device; the
329 * argument is very likely a subdevice of this, like eventN */
330 pdev = dev;
331 while (pdev && !_this->udev_device_get_sysattr_value(pdev, "capabilities/ev")) {
332 pdev = _this->udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL);
333 }
334 if (!pdev) {
335 return 0;
336 }
337
338 get_caps(dev, pdev, "capabilities/ev", bitmask_ev, SDL_arraysize(bitmask_ev));
339 get_caps(dev, pdev, "capabilities/abs", bitmask_abs, SDL_arraysize(bitmask_abs));
340 get_caps(dev, pdev, "capabilities/rel", bitmask_rel, SDL_arraysize(bitmask_rel));
341 get_caps(dev, pdev, "capabilities/key", bitmask_key, SDL_arraysize(bitmask_key));
342
343 if (test_bit(EV_ABS, bitmask_ev) &&
344 test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs)) {
345 if (test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key)) {
346 ; /* ID_INPUT_TABLET */
347 } else if (test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key)) {
348 ; /* ID_INPUT_TOUCHPAD */
349 } else if (test_bit(BTN_MOUSE, bitmask_key)) {
350 devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */
351 } else if (test_bit(BTN_TOUCH, bitmask_key)) {
352 /* TODO: better determining between touchscreen and multitouch touchpad,
353 see https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-input_id.c */
354 devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN; /* ID_INPUT_TOUCHSCREEN */
355 }
356
357 if (test_bit(BTN_TRIGGER, bitmask_key) ||
358 test_bit(BTN_A, bitmask_key) ||
359 test_bit(BTN_1, bitmask_key) ||
360 test_bit(ABS_RX, bitmask_abs) ||
361 test_bit(ABS_RY, bitmask_abs) ||
362 test_bit(ABS_RZ, bitmask_abs) ||
363 test_bit(ABS_THROTTLE, bitmask_abs) ||
364 test_bit(ABS_RUDDER, bitmask_abs) ||
365 test_bit(ABS_WHEEL, bitmask_abs) ||
366 test_bit(ABS_GAS, bitmask_abs) ||
367 test_bit(ABS_BRAKE, bitmask_abs)) {
368 devclass |= SDL_UDEV_DEVICE_JOYSTICK; /* ID_INPUT_JOYSTICK */
369 }
370 }
371
372 if (test_bit(EV_REL, bitmask_ev) &&
373 test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel) &&
374 test_bit(BTN_MOUSE, bitmask_key)) {
375 devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */
376 }
377
378 /* the first 32 bits are ESC, numbers, and Q to D; if we have any of
379 * those, consider it a keyboard device; do not test KEY_RESERVED, though */
380 keyboard_mask = 0xFFFFFFFE;
381 if ((bitmask_key[0] & keyboard_mask) != 0)
382 devclass |= SDL_UDEV_DEVICE_KEYBOARD; /* ID_INPUT_KEYBOARD */
383
384 return devclass;
385 }
386
387 static void
device_event(SDL_UDEV_deviceevent type,struct udev_device * dev)388 device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
389 {
390 const char *subsystem;
391 const char *val = NULL;
392 int devclass = 0;
393 const char *path;
394 SDL_UDEV_CallbackList *item;
395
396 path = _this->udev_device_get_devnode(dev);
397 if (path == NULL) {
398 return;
399 }
400
401 subsystem = _this->udev_device_get_subsystem(dev);
402 if (SDL_strcmp(subsystem, "sound") == 0) {
403 devclass = SDL_UDEV_DEVICE_SOUND;
404 } else if (SDL_strcmp(subsystem, "input") == 0) {
405 /* udev rules reference: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c */
406
407 val = _this->udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
408 if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
409 devclass |= SDL_UDEV_DEVICE_JOYSTICK;
410 }
411
412 val = _this->udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
413 if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
414 devclass |= SDL_UDEV_DEVICE_MOUSE;
415 }
416
417 val = _this->udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
418 if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
419 devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN;
420 }
421
422 /* The undocumented rule is:
423 - All devices with keys get ID_INPUT_KEY
424 - From this subset, if they have ESC, numbers, and Q to D, it also gets ID_INPUT_KEYBOARD
425
426 Ref: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c#n183
427 */
428 val = _this->udev_device_get_property_value(dev, "ID_INPUT_KEY");
429 if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
430 devclass |= SDL_UDEV_DEVICE_KEYBOARD;
431 }
432
433 if (devclass == 0) {
434 /* Fall back to old style input classes */
435 val = _this->udev_device_get_property_value(dev, "ID_CLASS");
436 if (val != NULL) {
437 if (SDL_strcmp(val, "joystick") == 0) {
438 devclass = SDL_UDEV_DEVICE_JOYSTICK;
439 } else if (SDL_strcmp(val, "mouse") == 0) {
440 devclass = SDL_UDEV_DEVICE_MOUSE;
441 } else if (SDL_strcmp(val, "kbd") == 0) {
442 devclass = SDL_UDEV_DEVICE_KEYBOARD;
443 } else {
444 return;
445 }
446 } else {
447 /* We could be linked with libudev on a system that doesn't have udev running */
448 devclass = guess_device_class(dev);
449 }
450 }
451 } else {
452 return;
453 }
454
455 /* Process callbacks */
456 for (item = _this->first; item != NULL; item = item->next) {
457 item->callback(type, devclass, path);
458 }
459 }
460
461 void
SDL_UDEV_Poll(void)462 SDL_UDEV_Poll(void)
463 {
464 struct udev_device *dev = NULL;
465 const char *action = NULL;
466
467 if (_this == NULL) {
468 return;
469 }
470
471 while (SDL_UDEV_hotplug_update_available()) {
472 dev = _this->udev_monitor_receive_device(_this->udev_mon);
473 if (dev == NULL) {
474 break;
475 }
476 action = _this->udev_device_get_action(dev);
477
478 if (SDL_strcmp(action, "add") == 0) {
479 /* Wait for the device to finish initialization */
480 SDL_Delay(100);
481
482 device_event(SDL_UDEV_DEVICEADDED, dev);
483 } else if (SDL_strcmp(action, "remove") == 0) {
484 device_event(SDL_UDEV_DEVICEREMOVED, dev);
485 }
486
487 _this->udev_device_unref(dev);
488 }
489 }
490
491 int
SDL_UDEV_AddCallback(SDL_UDEV_Callback cb)492 SDL_UDEV_AddCallback(SDL_UDEV_Callback cb)
493 {
494 SDL_UDEV_CallbackList *item;
495 item = (SDL_UDEV_CallbackList *) SDL_calloc(1, sizeof (SDL_UDEV_CallbackList));
496 if (item == NULL) {
497 return SDL_OutOfMemory();
498 }
499
500 item->callback = cb;
501
502 if (_this->last == NULL) {
503 _this->first = _this->last = item;
504 } else {
505 _this->last->next = item;
506 _this->last = item;
507 }
508
509 return 1;
510 }
511
512 void
SDL_UDEV_DelCallback(SDL_UDEV_Callback cb)513 SDL_UDEV_DelCallback(SDL_UDEV_Callback cb)
514 {
515 SDL_UDEV_CallbackList *item;
516 SDL_UDEV_CallbackList *prev = NULL;
517
518 for (item = _this->first; item != NULL; item = item->next) {
519 /* found it, remove it. */
520 if (item->callback == cb) {
521 if (prev != NULL) {
522 prev->next = item->next;
523 } else {
524 SDL_assert(_this->first == item);
525 _this->first = item->next;
526 }
527 if (item == _this->last) {
528 _this->last = prev;
529 }
530 SDL_free(item);
531 return;
532 }
533 prev = item;
534 }
535
536 }
537
538
539 #endif /* SDL_USE_LIBUDEV */
540