v4l2-udev.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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 <sys/eventfd.h>
  15. #include <poll.h>
  16. #include <unistd.h>
  17. #include <libudev.h>
  18. #include <util/threading.h>
  19. #include <util/darray.h>
  20. #include <obs.h>
  21. #include "v4l2-udev.h"
  22. /** udev action enum */
  23. enum udev_action { UDEV_ACTION_ADDED, UDEV_ACTION_REMOVED, UDEV_ACTION_UNKNOWN };
  24. static const char *udev_signals[] = {"void device_added(string device)", "void device_removed(string device)", NULL};
  25. /* global data */
  26. static uint_fast32_t udev_refs = 0;
  27. static pthread_mutex_t udev_mutex = PTHREAD_MUTEX_INITIALIZER;
  28. static pthread_t udev_thread;
  29. static os_event_t *udev_event;
  30. static int udev_event_fd;
  31. static signal_handler_t *udev_signalhandler = NULL;
  32. /**
  33. * udev gives us the device action as string, so we convert it here ...
  34. *
  35. * @param action the udev action as string
  36. * @return the udev action as enum value
  37. */
  38. static enum udev_action udev_action_to_enum(const char *action)
  39. {
  40. if (!action)
  41. return UDEV_ACTION_UNKNOWN;
  42. if (!strncmp("add", action, 3))
  43. return UDEV_ACTION_ADDED;
  44. if (!strncmp("remove", action, 6))
  45. return UDEV_ACTION_REMOVED;
  46. return UDEV_ACTION_UNKNOWN;
  47. }
  48. /**
  49. * Call all registered callbacks with the event
  50. *
  51. * @param dev udev device that had an event occuring
  52. */
  53. static inline void udev_signal_event(struct udev_device *dev)
  54. {
  55. const char *node;
  56. enum udev_action action;
  57. struct calldata data;
  58. pthread_mutex_lock(&udev_mutex);
  59. node = udev_device_get_devnode(dev);
  60. action = udev_action_to_enum(udev_device_get_action(dev));
  61. calldata_init(&data);
  62. calldata_set_string(&data, "device", node);
  63. switch (action) {
  64. case UDEV_ACTION_ADDED:
  65. signal_handler_signal(udev_signalhandler, "device_added", &data);
  66. break;
  67. case UDEV_ACTION_REMOVED:
  68. signal_handler_signal(udev_signalhandler, "device_removed", &data);
  69. break;
  70. default:
  71. break;
  72. }
  73. calldata_free(&data);
  74. pthread_mutex_unlock(&udev_mutex);
  75. }
  76. /**
  77. * Event listener thread
  78. */
  79. static void *udev_event_thread(void *vptr)
  80. {
  81. UNUSED_PARAMETER(vptr);
  82. int fd;
  83. struct udev *udev;
  84. struct udev_monitor *mon;
  85. struct udev_device *dev;
  86. /* set up udev monitoring */
  87. os_set_thread_name("v4l2: udev");
  88. udev = udev_new();
  89. mon = udev_monitor_new_from_netlink(udev, "udev");
  90. udev_monitor_filter_add_match_subsystem_devtype(mon, "video4linux", NULL);
  91. if (udev_monitor_enable_receiving(mon) < 0)
  92. return NULL;
  93. /* set up fds */
  94. fd = udev_monitor_get_fd(mon);
  95. while (os_event_try(udev_event) == EAGAIN) {
  96. struct pollfd fds[2];
  97. fds[0].fd = fd;
  98. fds[0].events = POLLIN;
  99. fds[0].revents = 0;
  100. fds[1].fd = udev_event_fd;
  101. fds[1].events = POLLIN;
  102. if (poll(fds, 2, 1000) <= 0)
  103. continue;
  104. if (!(fds[0].revents & POLLIN))
  105. continue;
  106. dev = udev_monitor_receive_device(mon);
  107. if (!dev)
  108. continue;
  109. udev_signal_event(dev);
  110. udev_device_unref(dev);
  111. }
  112. udev_monitor_unref(mon);
  113. udev_unref(udev);
  114. return NULL;
  115. }
  116. void v4l2_init_udev(void)
  117. {
  118. pthread_mutex_lock(&udev_mutex);
  119. /* set up udev */
  120. if (udev_refs == 0) {
  121. if (os_event_init(&udev_event, OS_EVENT_TYPE_MANUAL) != 0)
  122. goto fail;
  123. if ((udev_event_fd = eventfd(0, EFD_CLOEXEC)) < 0)
  124. goto fail;
  125. if (pthread_create(&udev_thread, NULL, udev_event_thread, NULL) != 0) {
  126. close(udev_event_fd);
  127. goto fail;
  128. }
  129. udev_signalhandler = signal_handler_create();
  130. if (!udev_signalhandler) {
  131. close(udev_event_fd);
  132. goto fail;
  133. }
  134. signal_handler_add_array(udev_signalhandler, udev_signals);
  135. }
  136. udev_refs++;
  137. fail:
  138. pthread_mutex_unlock(&udev_mutex);
  139. }
  140. void v4l2_unref_udev(void)
  141. {
  142. pthread_mutex_lock(&udev_mutex);
  143. /* unref udev monitor */
  144. if (udev_refs && --udev_refs == 0) {
  145. os_event_signal(udev_event);
  146. eventfd_write(udev_event_fd, 1);
  147. pthread_join(udev_thread, NULL);
  148. os_event_destroy(udev_event);
  149. close(udev_event_fd);
  150. if (udev_signalhandler)
  151. signal_handler_destroy(udev_signalhandler);
  152. udev_signalhandler = NULL;
  153. }
  154. pthread_mutex_unlock(&udev_mutex);
  155. }
  156. signal_handler_t *v4l2_get_udev_signalhandler(void)
  157. {
  158. return udev_signalhandler;
  159. }