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