megapixels/device.c

464 lines
9.7 KiB
C

#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)
{
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;
}