v4l2-udev.c 4.2 KB

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