device.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. #include "device.h"
  2. #include <errno.h>
  3. #include <fcntl.h>
  4. #include <glib.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <sys/ioctl.h>
  8. #include <unistd.h>
  9. bool mp_find_device_path(struct media_v2_intf_devnode devnode, char *path, int length)
  10. {
  11. char uevent_path[256];
  12. snprintf(uevent_path, 256, "/sys/dev/char/%d:%d/uevent", devnode.major, devnode.minor);
  13. FILE *f = fopen(uevent_path, "r");
  14. if (!f) {
  15. return false;
  16. }
  17. char line[512];
  18. while (fgets(line, 512, f)) {
  19. if (strncmp(line, "DEVNAME=", 8) == 0) {
  20. // Drop newline
  21. int length = strlen(line);
  22. if (line[length - 1] == '\n')
  23. line[length - 1] = '\0';
  24. snprintf(path, length, "/dev/%s", line + 8);
  25. return true;
  26. }
  27. }
  28. fclose(f);
  29. return false;
  30. }
  31. struct _MPDevice {
  32. int fd;
  33. struct media_device_info info;
  34. struct media_v2_entity *entities;
  35. size_t num_entities;
  36. struct media_v2_interface *interfaces;
  37. size_t num_interfaces;
  38. struct media_v2_pad *pads;
  39. size_t num_pads;
  40. struct media_v2_link *links;
  41. size_t num_links;
  42. };
  43. static void errno_printerr(const char *s)
  44. {
  45. g_printerr("MPDevice: %s error %d, %s\n", s, errno, strerror(errno));
  46. }
  47. static int xioctl(int fd, int request, void *arg)
  48. {
  49. int r;
  50. do {
  51. r = ioctl(fd, request, arg);
  52. } while (r == -1 && errno == EINTR);
  53. return r;
  54. }
  55. MPDevice *mp_device_find(const char *driver_name)
  56. {
  57. MPDeviceList *list = mp_device_list_new();
  58. MPDevice *found_device = mp_device_list_find_remove(&list, driver_name);
  59. mp_device_list_free(list);
  60. return found_device;
  61. }
  62. MPDevice *mp_device_open(const char *path)
  63. {
  64. int fd = open(path, O_RDWR);
  65. if (fd == -1) {
  66. errno_printerr("open");
  67. return NULL;
  68. }
  69. return mp_device_new(fd);
  70. }
  71. MPDevice *mp_device_new(int fd)
  72. {
  73. // Get the topology of the media device
  74. struct media_v2_topology topology = {};
  75. if (xioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology) == -1
  76. || topology.num_entities == 0) {
  77. close(fd);
  78. return NULL;
  79. }
  80. // Create the device
  81. MPDevice *device = calloc(1, sizeof(MPDevice));
  82. device->fd = fd;
  83. device->entities = calloc(topology.num_entities, sizeof(struct media_v2_entity));
  84. device->num_entities = topology.num_entities;
  85. device->interfaces = calloc(topology.num_interfaces, sizeof(struct media_v2_interface));
  86. device->num_interfaces = topology.num_interfaces;
  87. device->pads = calloc(topology.num_pads, sizeof(struct media_v2_pad));
  88. device->num_pads = topology.num_pads;
  89. device->links = calloc(topology.num_links, sizeof(struct media_v2_link));
  90. device->num_links = topology.num_links;
  91. // Get the actual devices and interfaces
  92. topology.ptr_entities = (uint64_t)device->entities;
  93. topology.ptr_interfaces = (uint64_t)device->interfaces;
  94. topology.ptr_pads = (uint64_t)device->pads;
  95. topology.ptr_links = (uint64_t)device->links;
  96. if (xioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology) == -1) {
  97. errno_printerr("MEDIA_IOC_G_TOPOLOGY");
  98. mp_device_close(device);
  99. return NULL;
  100. }
  101. // Get device info
  102. if (xioctl(fd, MEDIA_IOC_DEVICE_INFO, &device->info) == -1) {
  103. errno_printerr("MEDIA_IOC_DEVICE_INFO");
  104. mp_device_close(device);
  105. return NULL;
  106. }
  107. return device;
  108. }
  109. void mp_device_close(MPDevice *device)
  110. {
  111. close(device->fd);
  112. free(device->entities);
  113. free(device->interfaces);
  114. free(device->pads);
  115. free(device->links);
  116. free(device);
  117. }
  118. bool mp_device_setup_link(MPDevice *device, uint32_t source_pad_id, uint32_t sink_pad_id, bool enabled)
  119. {
  120. const struct media_v2_pad *source_pad = mp_device_get_pad(device, source_pad_id);
  121. g_return_val_if_fail(source_pad, false);
  122. const struct media_v2_pad *sink_pad = mp_device_get_pad(device, sink_pad_id);
  123. g_return_val_if_fail(sink_pad, false);
  124. struct media_link_desc link = {};
  125. link.flags = enabled ? MEDIA_LNK_FL_ENABLED : 0;
  126. link.source.entity = source_pad->entity_id;
  127. link.source.index = 0;
  128. link.sink.entity = sink_pad->entity_id;
  129. link.sink.index = 0;
  130. if (xioctl(device->fd, MEDIA_IOC_SETUP_LINK, &link) == -1) {
  131. errno_printerr("MEDIA_IOC_SETUP_LINK");
  132. return false;
  133. }
  134. return true;
  135. }
  136. const struct media_v2_entity *mp_device_find_entity(const MPDevice *device, const char *driver_name)
  137. {
  138. int length = strlen(driver_name);
  139. // Find the entity from the name
  140. for (uint32_t i = 0; i < device->num_entities; ++i) {
  141. if (strncmp(device->entities[i].name, driver_name, length) == 0) {
  142. return &device->entities[i];
  143. }
  144. }
  145. return NULL;
  146. }
  147. const struct media_device_info *mp_device_get_info(const MPDevice *device)
  148. {
  149. return &device->info;
  150. }
  151. const struct media_v2_entity *mp_device_get_entity(const MPDevice *device, uint32_t id)
  152. {
  153. for (int i = 0; i < device->num_entities; ++i) {
  154. if (device->entities[i].id == id) {
  155. return &device->entities[i];
  156. }
  157. }
  158. return NULL;
  159. }
  160. const struct media_v2_entity *mp_device_get_entities(const MPDevice *device)
  161. {
  162. return device->entities;
  163. }
  164. size_t mp_device_get_num_entities(const MPDevice *device)
  165. {
  166. return device->num_entities;
  167. }
  168. const struct media_v2_interface *mp_device_find_entity_interface(const MPDevice *device, uint32_t entity_id)
  169. {
  170. // Find the interface through the link
  171. const struct media_v2_link *link = mp_device_find_link_to(device, entity_id);
  172. if (!link) {
  173. return NULL;
  174. }
  175. return mp_device_get_interface(device, link->source_id);
  176. }
  177. const struct media_v2_interface *mp_device_get_interface(const MPDevice *device, uint32_t id)
  178. {
  179. for (int i = 0; i < device->num_interfaces; ++i) {
  180. if (device->interfaces[i].id == id) {
  181. return &device->interfaces[i];
  182. }
  183. }
  184. return NULL;
  185. }
  186. const struct media_v2_interface *mp_device_get_interfaces(const MPDevice *device)
  187. {
  188. return device->interfaces;
  189. }
  190. size_t mp_device_get_num_interfaces(const MPDevice *device)
  191. {
  192. return device->num_interfaces;
  193. }
  194. const struct media_v2_pad *mp_device_get_pad_from_entity(const MPDevice *device, uint32_t entity_id)
  195. {
  196. for (int i = 0; i < device->num_pads; ++i) {
  197. if (device->pads[i].entity_id == entity_id) {
  198. return &device->pads[i];
  199. }
  200. }
  201. return NULL;
  202. }
  203. const struct media_v2_pad *mp_device_get_pad(const MPDevice *device, uint32_t id)
  204. {
  205. for (int i = 0; i < device->num_pads; ++i) {
  206. if (device->pads[i].id == id) {
  207. return &device->pads[i];
  208. }
  209. }
  210. return NULL;
  211. }
  212. const struct media_v2_pad *mp_device_get_pads(const MPDevice *device)
  213. {
  214. return device->pads;
  215. }
  216. size_t mp_device_get_num_pads(const MPDevice *device)
  217. {
  218. return device->num_pads;
  219. }
  220. const struct media_v2_link *mp_device_find_entity_link(const MPDevice *device, uint32_t entity_id)
  221. {
  222. const struct media_v2_pad *pad = mp_device_get_pad_from_entity(device, entity_id);
  223. const struct media_v2_link *link = mp_device_find_link_to(device, pad->id);
  224. if (link) {
  225. return link;
  226. }
  227. return mp_device_find_link_from(device, pad->id);
  228. }
  229. const struct media_v2_link *mp_device_find_link_from(const MPDevice *device, uint32_t source)
  230. {
  231. for (int i = 0; i < device->num_links; ++i) {
  232. if (device->links[i].source_id == source) {
  233. return &device->links[i];
  234. }
  235. }
  236. return NULL;
  237. }
  238. const struct media_v2_link *mp_device_find_link_to(const MPDevice *device, uint32_t sink)
  239. {
  240. for (int i = 0; i < device->num_links; ++i) {
  241. if (device->links[i].sink_id == sink) {
  242. return &device->links[i];
  243. }
  244. }
  245. return NULL;
  246. }
  247. const struct media_v2_link *mp_device_find_link_between(const MPDevice *device, uint32_t source, uint32_t sink)
  248. {
  249. for (int i = 0; i < device->num_links; ++i) {
  250. if (device->links[i].source_id == source
  251. && device->links[i].sink_id == sink) {
  252. return &device->links[i];
  253. }
  254. }
  255. return NULL;
  256. }
  257. const struct media_v2_link *mp_device_get_link(const MPDevice *device, uint32_t id)
  258. {
  259. for (int i = 0; i < device->num_links; ++i) {
  260. if (device->links[i].id == id) {
  261. return &device->links[i];
  262. }
  263. }
  264. return NULL;
  265. }
  266. const struct media_v2_link *mp_device_get_links(const MPDevice *device)
  267. {
  268. return device->links;
  269. }
  270. size_t mp_device_get_num_links(const MPDevice *device)
  271. {
  272. return device->num_links;
  273. }
  274. struct _MPDeviceList {
  275. MPDevice *device;
  276. MPDeviceList *next;
  277. };
  278. MPDeviceList *mp_device_list_new()
  279. {
  280. MPDeviceList *current = NULL;
  281. // Enumerate media device files
  282. struct dirent *dir;
  283. DIR *d = opendir("/dev");
  284. while ((dir = readdir(d)) != NULL) {
  285. if (strncmp(dir->d_name, "media", 5) == 0) {
  286. char path[261];
  287. snprintf(path, 261, "/dev/%s", dir->d_name);
  288. MPDevice *device = mp_device_open(path);
  289. if (device) {
  290. MPDeviceList *next = malloc(sizeof(MPDeviceList));
  291. next->device = device;
  292. next->next = current;
  293. current = next;
  294. }
  295. }
  296. }
  297. closedir(d);
  298. return current;
  299. }
  300. void mp_device_list_free(MPDeviceList *device_list)
  301. {
  302. while (device_list) {
  303. MPDeviceList *tmp = device_list;
  304. device_list = tmp->next;
  305. mp_device_close(tmp->device);
  306. free(tmp);
  307. }
  308. }
  309. MPDevice *mp_device_list_find_remove(MPDeviceList **list, const char *driver_name)
  310. {
  311. MPDevice *found_device = NULL;
  312. int length = strlen(driver_name);
  313. while (*list) {
  314. MPDevice *device = mp_device_list_get(*list);
  315. const struct media_device_info *info = mp_device_get_info(device);
  316. if (strncmp(info->driver, driver_name, length) == 0) {
  317. found_device = mp_device_list_remove(list);
  318. break;
  319. }
  320. list = &(*list)->next;
  321. }
  322. return found_device;
  323. }
  324. MPDevice *mp_device_list_remove(MPDeviceList **device_list)
  325. {
  326. MPDevice *device = (*device_list)->device;
  327. if ((*device_list)->next) {
  328. MPDeviceList *tmp = (*device_list)->next;
  329. **device_list = *tmp;
  330. free(tmp);
  331. } else {
  332. free(*device_list);
  333. *device_list = NULL;
  334. }
  335. return device;
  336. }
  337. MPDevice *mp_device_list_get(const MPDeviceList *device_list)
  338. {
  339. return device_list->device;
  340. }
  341. MPDeviceList *mp_device_list_next(const MPDeviceList *device_list)
  342. {
  343. return device_list->next;
  344. }