v4l2-udev.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. /*
  2. Copyright (C) 2014 by Leonhard Oelke <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. #include <libudev.h>
  15. #include <util/threading.h>
  16. #include <util/darray.h>
  17. #include <obs.h>
  18. #include "v4l2-udev.h"
  19. /** udev action enum */
  20. enum udev_action {
  21. UDEV_ACTION_ADDED,
  22. UDEV_ACTION_REMOVED,
  23. UDEV_ACTION_UNKNOWN
  24. };
  25. static const char *udev_signals[] = {
  26. "void device_added(string device)",
  27. "void device_removed(string device)",
  28. NULL
  29. };
  30. /* global data */
  31. static uint_fast32_t udev_refs = 0;
  32. static pthread_mutex_t udev_mutex = PTHREAD_MUTEX_INITIALIZER;
  33. static pthread_t udev_thread;
  34. static os_event_t *udev_event;
  35. static signal_handler_t *udev_signalhandler = NULL;
  36. /**
  37. * udev gives us the device action as string, so we convert it here ...
  38. *
  39. * @param action the udev action as string
  40. * @return the udev action as enum value
  41. */
  42. static enum udev_action udev_action_to_enum(const char *action)
  43. {
  44. if (!action)
  45. return UDEV_ACTION_UNKNOWN;
  46. if (!strncmp("add", action, 3))
  47. return UDEV_ACTION_ADDED;
  48. if (!strncmp("remove", action, 6))
  49. return UDEV_ACTION_REMOVED;
  50. return UDEV_ACTION_UNKNOWN;
  51. }
  52. /**
  53. * Call all registered callbacks with the event
  54. *
  55. * @param dev udev device that had an event occuring
  56. */
  57. static inline void udev_signal_event(struct udev_device *dev)
  58. {
  59. const char *node;
  60. enum udev_action action;
  61. struct calldata data;
  62. pthread_mutex_lock(&udev_mutex);
  63. node = udev_device_get_devnode(dev);
  64. action = udev_action_to_enum(udev_device_get_action(dev));
  65. calldata_init(&data);
  66. calldata_set_string(&data, "device", node);
  67. switch (action) {
  68. case UDEV_ACTION_ADDED:
  69. signal_handler_signal(udev_signalhandler,
  70. "device_added", &data);
  71. break;
  72. case UDEV_ACTION_REMOVED:
  73. signal_handler_signal(udev_signalhandler,
  74. "device_removed", &data);
  75. break;
  76. default:
  77. break;
  78. }
  79. calldata_free(&data);
  80. pthread_mutex_unlock(&udev_mutex);
  81. }
  82. /**
  83. * Event listener thread
  84. */
  85. static void *udev_event_thread(void *vptr)
  86. {
  87. UNUSED_PARAMETER(vptr);
  88. int fd;
  89. fd_set fds;
  90. struct timeval tv;
  91. struct udev *udev;
  92. struct udev_monitor *mon;
  93. struct udev_device *dev;
  94. /* set up udev monitoring */
  95. udev = udev_new();
  96. mon = udev_monitor_new_from_netlink(udev, "udev");
  97. udev_monitor_filter_add_match_subsystem_devtype(
  98. mon, "video4linux", NULL);
  99. if (udev_monitor_enable_receiving(mon) < 0)
  100. return NULL;
  101. /* set up fds */
  102. fd = udev_monitor_get_fd(mon);
  103. while (os_event_try(udev_event) == EAGAIN) {
  104. FD_ZERO(&fds);
  105. FD_SET(fd, &fds);
  106. tv.tv_sec = 1;
  107. tv.tv_usec = 0;
  108. if (select(fd + 1, &fds, NULL, NULL, &tv) <= 0)
  109. continue;
  110. dev = udev_monitor_receive_device(mon);
  111. if (!dev)
  112. continue;
  113. udev_signal_event(dev);
  114. udev_device_unref(dev);
  115. }
  116. udev_monitor_unref(mon);
  117. udev_unref(udev);
  118. return NULL;
  119. }
  120. void v4l2_init_udev(void)
  121. {
  122. pthread_mutex_lock(&udev_mutex);
  123. /* set up udev */
  124. if (udev_refs == 0) {
  125. if (os_event_init(&udev_event, OS_EVENT_TYPE_MANUAL) != 0)
  126. goto fail;
  127. if (pthread_create(&udev_thread, NULL, udev_event_thread,
  128. NULL) != 0)
  129. goto fail;
  130. udev_signalhandler = signal_handler_create();
  131. if (!udev_signalhandler)
  132. goto fail;
  133. signal_handler_add_array(udev_signalhandler, udev_signals);
  134. }
  135. udev_refs++;
  136. fail:
  137. pthread_mutex_unlock(&udev_mutex);
  138. }
  139. void v4l2_unref_udev(void)
  140. {
  141. pthread_mutex_lock(&udev_mutex);
  142. /* unref udev monitor */
  143. if (udev_refs && --udev_refs == 0) {
  144. os_event_signal(udev_event);
  145. pthread_join(udev_thread, NULL);
  146. os_event_destroy(udev_event);
  147. if (udev_signalhandler)
  148. signal_handler_destroy(udev_signalhandler);
  149. udev_signalhandler = NULL;
  150. }
  151. pthread_mutex_unlock(&udev_mutex);
  152. }
  153. signal_handler_t *v4l2_get_udev_signalhandler(void)
  154. {
  155. return udev_signalhandler;
  156. }