SDL_udev.c 17 KB

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