v4l2-udev.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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. os_set_thread_name("v4l2: udev");
  94. udev = udev_new();
  95. mon = udev_monitor_new_from_netlink(udev, "udev");
  96. udev_monitor_filter_add_match_subsystem_devtype(mon, "video4linux",
  97. NULL);
  98. if (udev_monitor_enable_receiving(mon) < 0)
  99. return NULL;
  100. /* set up fds */
  101. fd = udev_monitor_get_fd(mon);
  102. while (os_event_try(udev_event) == EAGAIN) {
  103. FD_ZERO(&fds);
  104. FD_SET(fd, &fds);
  105. tv.tv_sec = 1;
  106. tv.tv_usec = 0;
  107. if (select(fd + 1, &fds, NULL, NULL, &tv) <= 0)
  108. continue;
  109. dev = udev_monitor_receive_device(mon);
  110. if (!dev)
  111. continue;
  112. udev_signal_event(dev);
  113. udev_device_unref(dev);
  114. }
  115. udev_monitor_unref(mon);
  116. udev_unref(udev);
  117. return NULL;
  118. }
  119. void v4l2_init_udev(void)
  120. {
  121. pthread_mutex_lock(&udev_mutex);
  122. /* set up udev */
  123. if (udev_refs == 0) {
  124. if (os_event_init(&udev_event, OS_EVENT_TYPE_MANUAL) != 0)
  125. goto fail;
  126. if (pthread_create(&udev_thread, NULL, udev_event_thread,
  127. NULL) != 0)
  128. goto fail;
  129. udev_signalhandler = signal_handler_create();
  130. if (!udev_signalhandler)
  131. goto fail;
  132. signal_handler_add_array(udev_signalhandler, udev_signals);
  133. }
  134. udev_refs++;
  135. fail:
  136. pthread_mutex_unlock(&udev_mutex);
  137. }
  138. void v4l2_unref_udev(void)
  139. {
  140. pthread_mutex_lock(&udev_mutex);
  141. /* unref udev monitor */
  142. if (udev_refs && --udev_refs == 0) {
  143. os_event_signal(udev_event);
  144. pthread_join(udev_thread, NULL);
  145. os_event_destroy(udev_event);
  146. if (udev_signalhandler)
  147. signal_handler_destroy(udev_signalhandler);
  148. udev_signalhandler = NULL;
  149. }
  150. pthread_mutex_unlock(&udev_mutex);
  151. }
  152. signal_handler_t *v4l2_get_udev_signalhandler(void)
  153. {
  154. return udev_signalhandler;
  155. }