device.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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_v2_entity *mp_device_find_entity_type(const MPDevice *device, const uint32_t type)
  148. {
  149. // Find the entity from the entity type
  150. for (uint32_t i = 0; i < device->num_entities; ++i) {
  151. if (device->entities[i].function == type) {
  152. return &device->entities[i];
  153. }
  154. }
  155. return NULL;
  156. }
  157. const struct media_device_info *mp_device_get_info(const MPDevice *device)
  158. {
  159. return &device->info;
  160. }
  161. const struct media_v2_entity *mp_device_get_entity(const MPDevice *device, uint32_t id)
  162. {
  163. for (int i = 0; i < device->num_entities; ++i) {
  164. if (device->entities[i].id == id) {
  165. return &device->entities[i];
  166. }
  167. }
  168. return NULL;
  169. }
  170. const struct media_v2_entity *mp_device_get_entities(const MPDevice *device)
  171. {
  172. return device->entities;
  173. }
  174. size_t mp_device_get_num_entities(const MPDevice *device)
  175. {
  176. return device->num_entities;
  177. }
  178. const struct media_v2_interface *mp_device_find_entity_interface(const MPDevice *device, uint32_t entity_id)
  179. {
  180. // Find the interface through the link
  181. const struct media_v2_link *link = mp_device_find_link_to(device, entity_id);
  182. if (!link) {
  183. return NULL;
  184. }
  185. return mp_device_get_interface(device, link->source_id);
  186. }
  187. const struct media_v2_interface *mp_device_get_interface(const MPDevice *device, uint32_t id)
  188. {
  189. for (int i = 0; i < device->num_interfaces; ++i) {
  190. if (device->interfaces[i].id == id) {
  191. return &device->interfaces[i];
  192. }
  193. }
  194. return NULL;
  195. }
  196. const struct media_v2_interface *mp_device_get_interfaces(const MPDevice *device)
  197. {
  198. return device->interfaces;
  199. }
  200. size_t mp_device_get_num_interfaces(const MPDevice *device)
  201. {
  202. return device->num_interfaces;
  203. }
  204. const struct media_v2_pad *mp_device_get_pad_from_entity(const MPDevice *device, uint32_t entity_id)
  205. {
  206. for (int i = 0; i < device->num_pads; ++i) {
  207. if (device->pads[i].entity_id == entity_id) {
  208. return &device->pads[i];
  209. }
  210. }
  211. return NULL;
  212. }
  213. const struct media_v2_pad *mp_device_get_pad(const MPDevice *device, uint32_t id)
  214. {
  215. for (int i = 0; i < device->num_pads; ++i) {
  216. if (device->pads[i].id == id) {
  217. return &device->pads[i];
  218. }
  219. }
  220. return NULL;
  221. }
  222. const struct media_v2_pad *mp_device_get_pads(const MPDevice *device)
  223. {
  224. return device->pads;
  225. }
  226. size_t mp_device_get_num_pads(const MPDevice *device)
  227. {
  228. return device->num_pads;
  229. }
  230. const struct media_v2_link *mp_device_find_entity_link(const MPDevice *device, uint32_t entity_id)
  231. {
  232. const struct media_v2_pad *pad = mp_device_get_pad_from_entity(device, entity_id);
  233. const struct media_v2_link *link = mp_device_find_link_to(device, pad->id);
  234. if (link) {
  235. return link;
  236. }
  237. return mp_device_find_link_from(device, pad->id);
  238. }
  239. const struct media_v2_link *mp_device_find_link_from(const MPDevice *device, uint32_t source)
  240. {
  241. for (int i = 0; i < device->num_links; ++i) {
  242. if (device->links[i].source_id == source) {
  243. return &device->links[i];
  244. }
  245. }
  246. return NULL;
  247. }
  248. const struct media_v2_link *mp_device_find_link_to(const MPDevice *device, uint32_t sink)
  249. {
  250. for (int i = 0; i < device->num_links; ++i) {
  251. if (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_find_link_between(const MPDevice *device, uint32_t source, uint32_t sink)
  258. {
  259. for (int i = 0; i < device->num_links; ++i) {
  260. if (device->links[i].source_id == source
  261. && device->links[i].sink_id == sink) {
  262. return &device->links[i];
  263. }
  264. }
  265. return NULL;
  266. }
  267. const struct media_v2_link *mp_device_get_link(const MPDevice *device, uint32_t id)
  268. {
  269. for (int i = 0; i < device->num_links; ++i) {
  270. if (device->links[i].id == id) {
  271. return &device->links[i];
  272. }
  273. }
  274. return NULL;
  275. }
  276. const struct media_v2_link *mp_device_get_links(const MPDevice *device)
  277. {
  278. return device->links;
  279. }
  280. size_t mp_device_get_num_links(const MPDevice *device)
  281. {
  282. return device->num_links;
  283. }
  284. struct _MPDeviceList {
  285. MPDevice *device;
  286. MPDeviceList *next;
  287. };
  288. MPDeviceList *mp_device_list_new()
  289. {
  290. MPDeviceList *current = NULL;
  291. // Enumerate media device files
  292. struct dirent *dir;
  293. DIR *d = opendir("/dev");
  294. while ((dir = readdir(d)) != NULL) {
  295. if (strncmp(dir->d_name, "media", 5) == 0) {
  296. char path[261];
  297. snprintf(path, 261, "/dev/%s", dir->d_name);
  298. MPDevice *device = mp_device_open(path);
  299. if (device) {
  300. MPDeviceList *next = malloc(sizeof(MPDeviceList));
  301. next->device = device;
  302. next->next = current;
  303. current = next;
  304. }
  305. }
  306. }
  307. closedir(d);
  308. return current;
  309. }
  310. void mp_device_list_free(MPDeviceList *device_list)
  311. {
  312. while (device_list) {
  313. MPDeviceList *tmp = device_list;
  314. device_list = tmp->next;
  315. mp_device_close(tmp->device);
  316. free(tmp);
  317. }
  318. }
  319. MPDevice *mp_device_list_find_remove(MPDeviceList **list, const char *driver_name)
  320. {
  321. MPDevice *found_device = NULL;
  322. int length = strlen(driver_name);
  323. while (*list) {
  324. MPDevice *device = mp_device_list_get(*list);
  325. const struct media_device_info *info = mp_device_get_info(device);
  326. if (strncmp(info->driver, driver_name, length) == 0) {
  327. found_device = mp_device_list_remove(list);
  328. break;
  329. }
  330. list = &(*list)->next;
  331. }
  332. return found_device;
  333. }
  334. MPDevice *mp_device_list_remove(MPDeviceList **device_list)
  335. {
  336. MPDevice *device = (*device_list)->device;
  337. if ((*device_list)->next) {
  338. MPDeviceList *tmp = (*device_list)->next;
  339. **device_list = *tmp;
  340. free(tmp);
  341. } else {
  342. free(*device_list);
  343. *device_list = NULL;
  344. }
  345. return device;
  346. }
  347. MPDevice *mp_device_list_get(const MPDeviceList *device_list)
  348. {
  349. return device_list->device;
  350. }
  351. MPDeviceList *mp_device_list_next(const MPDeviceList *device_list)
  352. {
  353. return device_list->next;
  354. }