device.c 9.7 KB

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