You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
402 lines
10 KiB
402 lines
10 KiB
#include "device.h" |
|
|
|
#include <errno.h> |
|
#include <fcntl.h> |
|
#include <glib.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <sys/ioctl.h> |
|
#include <unistd.h> |
|
|
|
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) |
|
{ |
|
MPDevice *found_device = NULL; |
|
|
|
int length = strlen(driver_name); |
|
MPDeviceList *list = mp_device_list_new(); |
|
|
|
for (MPDeviceList *item = list; item; item = mp_device_list_next(item)) { |
|
MPDevice *device = mp_device_list_get(item); |
|
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(&item); |
|
break; |
|
} |
|
} |
|
|
|
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_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_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; |
|
}
|
|
|