#include "device.h" #include #include #include #include #include #include #include bool mp_find_device_path(struct media_v2_intf_devnode devnode, char *path, int length) { char uevent_path[256]; snprintf(uevent_path, 256, "/sys/dev/char/%d:%d/uevent", devnode.major, devnode.minor); FILE *f = fopen(uevent_path, "r"); if (!f) { return false; } char line[512]; while (fgets(line, 512, f)) { if (strncmp(line, "DEVNAME=", 8) == 0) { // Drop newline int length = strlen(line); if (line[length - 1] == '\n') line[length - 1] = '\0'; snprintf(path, length, "/dev/%s", line + 8); return true; } } fclose(f); return false; } struct _MPDevice { int fd; struct media_device_info info; struct media_v2_entity *entities; size_t num_entities; struct media_v2_interface *interfaces; size_t num_interfaces; struct media_v2_pad *pads; size_t num_pads; struct media_v2_link *links; size_t num_links; }; static void errno_printerr(const char *s) { g_printerr("MPDevice: %s error %d, %s\n", s, errno, strerror(errno)); } static int xioctl(int fd, int request, void *arg) { int r; do { r = ioctl(fd, request, arg); } while (r == -1 && errno == EINTR); return r; } MPDevice *mp_device_find(const char *driver_name) { MPDeviceList *list = mp_device_list_new(); MPDevice *found_device = mp_device_list_find_remove(&list, driver_name); mp_device_list_free(list); return found_device; } MPDevice *mp_device_open(const char *path) { int fd = open(path, O_RDWR); if (fd == -1) { errno_printerr("open"); return NULL; } return mp_device_new(fd); } MPDevice *mp_device_new(int fd) { // Get the topology of the media device struct media_v2_topology topology = {}; if (xioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology) == -1 || topology.num_entities == 0) { close(fd); return NULL; } // Create the device MPDevice *device = calloc(1, sizeof(MPDevice)); device->fd = fd; device->entities = calloc(topology.num_entities, sizeof(struct media_v2_entity)); device->num_entities = topology.num_entities; device->interfaces = calloc(topology.num_interfaces, sizeof(struct media_v2_interface)); device->num_interfaces = topology.num_interfaces; device->pads = calloc(topology.num_pads, sizeof(struct media_v2_pad)); device->num_pads = topology.num_pads; device->links = calloc(topology.num_links, sizeof(struct media_v2_link)); device->num_links = topology.num_links; // Get the actual devices and interfaces topology.ptr_entities = (uint64_t)device->entities; topology.ptr_interfaces = (uint64_t)device->interfaces; topology.ptr_pads = (uint64_t)device->pads; topology.ptr_links = (uint64_t)device->links; if (xioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology) == -1) { errno_printerr("MEDIA_IOC_G_TOPOLOGY"); mp_device_close(device); return NULL; } // Get device info if (xioctl(fd, MEDIA_IOC_DEVICE_INFO, &device->info) == -1) { errno_printerr("MEDIA_IOC_DEVICE_INFO"); mp_device_close(device); return NULL; } return device; } void mp_device_close(MPDevice *device) { close(device->fd); free(device->entities); free(device->interfaces); free(device->pads); free(device->links); free(device); } bool mp_device_setup_link(MPDevice *device, uint32_t source_pad_id, uint32_t sink_pad_id, bool enabled) { const struct media_v2_pad *source_pad = mp_device_get_pad(device, source_pad_id); g_return_val_if_fail(source_pad, false); const struct media_v2_pad *sink_pad = mp_device_get_pad(device, sink_pad_id); g_return_val_if_fail(sink_pad, false); struct media_link_desc link = {}; link.flags = enabled ? MEDIA_LNK_FL_ENABLED : 0; link.source.entity = source_pad->entity_id; link.source.index = 0; link.sink.entity = sink_pad->entity_id; link.sink.index = 0; if (xioctl(device->fd, MEDIA_IOC_SETUP_LINK, &link) == -1) { errno_printerr("MEDIA_IOC_SETUP_LINK"); return false; } return true; } const struct media_v2_entity *mp_device_find_entity(const MPDevice *device, const char *driver_name) { int length = strlen(driver_name); // Find the entity from the name for (uint32_t i = 0; i < device->num_entities; ++i) { if (strncmp(device->entities[i].name, driver_name, length) == 0) { return &device->entities[i]; } } return NULL; } const struct media_v2_entity *mp_device_find_entity_type(const MPDevice *device, const uint32_t type) { // Find the entity from the entity type for (uint32_t i = 0; i < device->num_entities; ++i) { if (device->entities[i].function == type) { return &device->entities[i]; } } return NULL; } const struct media_device_info *mp_device_get_info(const MPDevice *device) { return &device->info; } const struct media_v2_entity *mp_device_get_entity(const MPDevice *device, uint32_t id) { for (int i = 0; i < device->num_entities; ++i) { if (device->entities[i].id == id) { return &device->entities[i]; } } return NULL; } const struct media_v2_entity *mp_device_get_entities(const MPDevice *device) { return device->entities; } size_t mp_device_get_num_entities(const MPDevice *device) { return device->num_entities; } const struct media_v2_interface *mp_device_find_entity_interface(const MPDevice *device, uint32_t entity_id) { // Find the interface through the link const struct media_v2_link *link = mp_device_find_link_to(device, entity_id); if (!link) { return NULL; } return mp_device_get_interface(device, link->source_id); } const struct media_v2_interface *mp_device_get_interface(const MPDevice *device, uint32_t id) { for (int i = 0; i < device->num_interfaces; ++i) { if (device->interfaces[i].id == id) { return &device->interfaces[i]; } } return NULL; } const struct media_v2_interface *mp_device_get_interfaces(const MPDevice *device) { return device->interfaces; } size_t mp_device_get_num_interfaces(const MPDevice *device) { return device->num_interfaces; } const struct media_v2_pad *mp_device_get_pad_from_entity(const MPDevice *device, uint32_t entity_id) { for (int i = 0; i < device->num_pads; ++i) { if (device->pads[i].entity_id == entity_id) { return &device->pads[i]; } } return NULL; } const struct media_v2_pad *mp_device_get_pad(const MPDevice *device, uint32_t id) { for (int i = 0; i < device->num_pads; ++i) { if (device->pads[i].id == id) { return &device->pads[i]; } } return NULL; } const struct media_v2_pad *mp_device_get_pads(const MPDevice *device) { return device->pads; } size_t mp_device_get_num_pads(const MPDevice *device) { return device->num_pads; } const struct media_v2_link *mp_device_find_entity_link(const MPDevice *device, uint32_t entity_id) { const struct media_v2_pad *pad = mp_device_get_pad_from_entity(device, entity_id); const struct media_v2_link *link = mp_device_find_link_to(device, pad->id); if (link) { return link; } return mp_device_find_link_from(device, pad->id); } const struct media_v2_link *mp_device_find_link_from(const MPDevice *device, uint32_t source) { for (int i = 0; i < device->num_links; ++i) { if (device->links[i].source_id == source) { return &device->links[i]; } } return NULL; } const struct media_v2_link *mp_device_find_link_to(const MPDevice *device, uint32_t sink) { for (int i = 0; i < device->num_links; ++i) { if (device->links[i].sink_id == sink) { return &device->links[i]; } } return NULL; } const struct media_v2_link *mp_device_find_link_between(const MPDevice *device, uint32_t source, uint32_t sink) { for (int i = 0; i < device->num_links; ++i) { if (device->links[i].source_id == source && device->links[i].sink_id == sink) { return &device->links[i]; } } return NULL; } const struct media_v2_link *mp_device_get_link(const MPDevice *device, uint32_t id) { for (int i = 0; i < device->num_links; ++i) { if (device->links[i].id == id) { return &device->links[i]; } } return NULL; } const struct media_v2_link *mp_device_get_links(const MPDevice *device) { return device->links; } size_t mp_device_get_num_links(const MPDevice *device) { return device->num_links; } struct _MPDeviceList { MPDevice *device; MPDeviceList *next; }; MPDeviceList *mp_device_list_new() { MPDeviceList *current = NULL; // Enumerate media device files struct dirent *dir; DIR *d = opendir("/dev"); while ((dir = readdir(d)) != NULL) { if (strncmp(dir->d_name, "media", 5) == 0) { char path[261]; snprintf(path, 261, "/dev/%s", dir->d_name); MPDevice *device = mp_device_open(path); if (device) { MPDeviceList *next = malloc(sizeof(MPDeviceList)); next->device = device; next->next = current; current = next; } } } closedir(d); return current; } void mp_device_list_free(MPDeviceList *device_list) { while (device_list) { MPDeviceList *tmp = device_list; device_list = tmp->next; mp_device_close(tmp->device); free(tmp); } } MPDevice *mp_device_list_find_remove(MPDeviceList **list, const char *driver_name) { MPDevice *found_device = NULL; int length = strlen(driver_name); while (*list) { MPDevice *device = mp_device_list_get(*list); const struct media_device_info *info = mp_device_get_info(device); if (strncmp(info->driver, driver_name, length) == 0) { found_device = mp_device_list_remove(list); break; } list = &(*list)->next; } return found_device; } MPDevice *mp_device_list_remove(MPDeviceList **device_list) { MPDevice *device = (*device_list)->device; if ((*device_list)->next) { MPDeviceList *tmp = (*device_list)->next; **device_list = *tmp; free(tmp); } else { free(*device_list); *device_list = NULL; } return device; } MPDevice *mp_device_list_get(const MPDeviceList *device_list) { return device_list->device; } MPDeviceList *mp_device_list_next(const MPDeviceList *device_list) { return device_list->next; }