From 67cc5f8c2cdc361204cb5d71fc4a48c344fe95f1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 30 Jul 2012 16:40:42 +0200 Subject: Initial version of the libmediadev library Signed-off-by: Laurent Pinchart --- .gitignore | 2 + Makefile | 19 ++ main.c | 289 ++++++++++++++++++++++++ media-enumerate.c | 649 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ media-enumerate.h | 114 ++++++++++ 5 files changed, 1073 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 main.c create mode 100644 media-enumerate.c create mode 100644 media-enumerate.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c78f915 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +media-enum diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..32a05da --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +CROSS_COMPILE ?= + +CC := $(CROSS_COMPILE)gcc +CFLAGS ?= -O2 -W -Wall -I$(HOME)/.local/include +LDFLAGS ?= -L$(HOME)/.local/lib64 +LIBS := -lmediactl -lv4l2subdev -ludev + +%.o : %.c + $(CC) $(CFLAGS) -c -o $@ $< + +all: media-enum + +media-enum: main.o media-enumerate.o + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +clean: + -rm -f *.o + -rm -f media-enum + diff --git a/main.c b/main.c new file mode 100644 index 0000000..2fc63d0 --- /dev/null +++ b/main.c @@ -0,0 +1,289 @@ +/* + * Media device discovery library + * + * Copyright (C) 2012 Ideas on board SPRL + * + * Contact: Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include +#include + +#include "media-enumerate.h" + +/* ----------------------------------------------------------------------------- + * Topology printing - copied from mediactl + */ + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) + +static void v4l2_subdev_print_format(struct media_entity *entity, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt format; + struct v4l2_rect rect; + int ret; + + ret = v4l2_subdev_get_format(entity, &format, pad, which); + if (ret != 0) + return; + + printf("\t\t[fmt:%s/%ux%u", + v4l2_subdev_pixelcode_to_string(format.code), + format.width, format.height); + + ret = v4l2_subdev_get_selection(entity, &rect, pad, + V4L2_SEL_TGT_CROP_BOUNDS, + which); + if (ret == 0) + printf("\n\t\t crop.bounds:(%u,%u)/%ux%u", rect.left, rect.top, + rect.width, rect.height); + + ret = v4l2_subdev_get_selection(entity, &rect, pad, + V4L2_SEL_TGT_CROP, + which); + if (ret == 0) + printf("\n\t\t crop:(%u,%u)/%ux%u", rect.left, rect.top, + rect.width, rect.height); + + ret = v4l2_subdev_get_selection(entity, &rect, pad, + V4L2_SEL_TGT_COMPOSE_BOUNDS, + which); + if (ret == 0) + printf("\n\t\t compose.bounds:(%u,%u)/%ux%u", + rect.left, rect.top, rect.width, rect.height); + + ret = v4l2_subdev_get_selection(entity, &rect, pad, + V4L2_SEL_TGT_COMPOSE, + which); + if (ret == 0) + printf("\n\t\t compose:(%u,%u)/%ux%u", + rect.left, rect.top, rect.width, rect.height); + + printf("]\n"); +} + +static const char *media_entity_type_to_string(unsigned type) +{ + static const struct { + __u32 type; + const char *name; + } types[] = { + { MEDIA_ENT_T_DEVNODE, "Node" }, + { MEDIA_ENT_T_V4L2_SUBDEV, "V4L2 subdev" }, + }; + + unsigned int i; + + type &= MEDIA_ENT_TYPE_MASK; + + for (i = 0; i < ARRAY_SIZE(types); i++) { + if (types[i].type == type) + return types[i].name; + } + + return "Unknown"; +} + +static const char *media_entity_subtype_to_string(unsigned type) +{ + static const char *node_types[] = { + "Unknown", + "V4L", + "FB", + "ALSA", + "DVB", + }; + static const char *subdev_types[] = { + "Unknown", + "Sensor", + "Flash", + "Lens", + }; + + unsigned int subtype = type & MEDIA_ENT_SUBTYPE_MASK; + + switch (type & MEDIA_ENT_TYPE_MASK) { + case MEDIA_ENT_T_DEVNODE: + if (subtype >= ARRAY_SIZE(node_types)) + subtype = 0; + return node_types[subtype]; + + case MEDIA_ENT_T_V4L2_SUBDEV: + if (subtype >= ARRAY_SIZE(subdev_types)) + subtype = 0; + return subdev_types[subtype]; + default: + return node_types[0]; + } +} + +static const char *media_pad_type_to_string(unsigned flag) +{ + static const struct { + __u32 flag; + const char *name; + } flags[] = { + { MEDIA_PAD_FL_SINK, "Sink" }, + { MEDIA_PAD_FL_SOURCE, "Source" }, + }; + + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(flags); i++) { + if (flags[i].flag & flag) + return flags[i].name; + } + + return "Unknown"; +} + +static void media_print_topology_text(struct media_device *media) +{ + static const struct { + __u32 flag; + char *name; + } link_flags[] = { + { MEDIA_LNK_FL_ENABLED, "ENABLED" }, + { MEDIA_LNK_FL_IMMUTABLE, "IMMUTABLE" }, + { MEDIA_LNK_FL_DYNAMIC, "DYNAMIC" }, + }; + + struct media_entity *entities = media_get_entities(media); + unsigned int nents = media_get_entities_count(media); + unsigned int i, j, k; + unsigned int padding; + + printf("Device topology\n"); + + for (i = 0; i < nents; ++i) { + struct media_entity *entity = &entities[i]; + + padding = printf("- entity %u: ", entity->info.id); + printf("%s (%u pad%s, %u link%s)\n", entity->info.name, + entity->info.pads, entity->info.pads > 1 ? "s" : "", + entity->num_links, entity->num_links > 1 ? "s" : ""); + printf("%*ctype %s subtype %s flags %x\n", padding, ' ', + media_entity_type_to_string(entity->info.type), + media_entity_subtype_to_string(entity->info.type), + entity->info.flags); + if (entity->devname[0]) + printf("%*cdevice node name %s\n", padding, ' ', entity->devname); + + for (j = 0; j < entity->info.pads; j++) { + struct media_pad *pad = &entity->pads[j]; + + printf("\tpad%u: %s\n", j, media_pad_type_to_string(pad->flags)); + + if (media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV) + v4l2_subdev_print_format(entity, j, V4L2_SUBDEV_FORMAT_ACTIVE); + + for (k = 0; k < entity->num_links; k++) { + struct media_link *link = &entity->links[k]; + struct media_pad *source = link->source; + struct media_pad *sink = link->sink; + bool first = true; + unsigned int i; + + if (source->entity == entity && source->index == j) + printf("\t\t-> \"%s\":%u [", + sink->entity->info.name, sink->index); + else if (sink->entity == entity && sink->index == j) + printf("\t\t<- \"%s\":%u [", + source->entity->info.name, source->index); + else + continue; + + for (i = 0; i < ARRAY_SIZE(link_flags); i++) { + if (!(link->flags & link_flags[i].flag)) + continue; + if (!first) + printf(","); + printf("%s", link_flags[i].name); + first = false; + } + + printf("]\n"); + } + } + printf("\n"); + } +} + +/* ----------------------------------------------------------------------------- + * Printing + */ + +int main(int argc, char *argv[]) +{ + struct media_enumerate *media_enum; + struct media_device **devices; + unsigned int n_devs; + unsigned int i; + int ret; + + media_enum = media_enumerate_new(); + if (media_enum == NULL) { + printf("Unable to create enumeration context\n"); + return EXIT_FAILURE; + } + + ret = media_enumerate_scan(media_enum); + if (ret < 0) { + goto done; + printf("Unable to enumerate media devices (%d)\n", ret); + } + + n_devs = media_enumerate_get_devices_count(media_enum); + if (n_devs == 0) { + printf("No devices found\n"); + goto done; + } + + devices = media_enumerate_get_devices(media_enum); + for (i = 0; i < n_devs; ++i) { + const struct media_device_info *info = media_get_info(devices[i]); + + printf("Media controller API version %u.%u.%u\n\n", + (info->media_version << 16) & 0xff, + (info->media_version << 8) & 0xff, + (info->media_version << 0) & 0xff); + printf("Media device information\n" + "------------------------\n" + "driver %s\n" + "model %s\n" + "serial %s\n" + "bus info %s\n" + "hw revision 0x%x\n" + "driver version %u.%u.%u\n\n", + info->driver, info->model, + info->serial, info->bus_info, + info->hw_revision, + (info->driver_version << 16) & 0xff, + (info->driver_version << 8) & 0xff, + (info->driver_version << 0) & 0xff); + + media_print_topology_text(devices[i]); + } + +done: + media_enumerate_unref(media_enum); + return EXIT_SUCCESS; +} diff --git a/media-enumerate.c b/media-enumerate.c new file mode 100644 index 0000000..1c3ab38 --- /dev/null +++ b/media-enumerate.c @@ -0,0 +1,649 @@ +/* + * Media device discovery library + * + * Copyright (C) 2012 Ideas on board SPRL + * + * Contact: Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#define HAVE_LIBUDEV + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBUDEV +#include +#endif + +#include + +#include "media-enumerate.h" + +struct media_enumerate { + unsigned int refcount; + + char **syspaths; + struct media_device **devices; + unsigned int devices_count; + unsigned int devices_max; + +#ifdef HAVE_LIBUDEV + struct udev *udev; +#endif +}; + +static int media_enumerate_add_device(struct media_enumerate *media_enum, + const char *syspath, + struct media_device *media) +{ + if (media_enum->devices_count == media_enum->devices_max) { + struct media_device **devices; + char **syspaths; + unsigned int count; + + count = media_enum->devices_max * 2; + count = count < 16 ? 16 : count; + + devices = realloc(media_enum->devices, count * sizeof *devices); + if (devices == NULL) + return -ENOMEM; + media_enum->devices = devices; + + syspaths = realloc(media_enum->syspaths, count * sizeof *syspaths); + if (syspaths == NULL) + return -ENOMEM; + media_enum->syspaths = syspaths; + + media_enum->devices_max = count; + } + + media_enum->devices[media_enum->devices_count] = media; + media_enum->syspaths[media_enum->devices_count] = strdup(syspath); + media_enum->devices_count++; + + return 0; +} + +typedef int(*media_enumerate_callback)(struct media_enumerate *, const char *, + const char *, dev_t); + +enum media_enumerate_bus_type { + BUS_TYPE_UNKNOWN, + BUS_TYPE_PCI, + BUS_TYPE_USB, +}; + +static enum media_enumerate_bus_type +media_enumerate_get_physical_bus(const char *syspath) +{ + char *modalias; + char type[4]; + int ret; + int fd; + + modalias = malloc(strlen(syspath) + 10); + if (modalias == NULL) + return BUS_TYPE_UNKNOWN; + + sprintf(modalias, "%s/modalias", syspath); + fd = open(modalias, O_RDONLY); + free(modalias); + + if (fd == -1) + return BUS_TYPE_UNKNOWN; + + ret = read(fd, type, 3); + close(fd); + + if (ret != 3) + return BUS_TYPE_UNKNOWN; + + type[3] = '\0'; + + if (!strcmp(type, "pci")) + return BUS_TYPE_PCI; + else if (!strcmp(type, "usb")) + return BUS_TYPE_USB; + else + return BUS_TYPE_UNKNOWN; +} + +static char *media_enumerate_get_physical_parent(const char *syspath) +{ + enum media_enumerate_bus_type bus_type; + char *phys; + char *p; + + phys = realpath(syspath, NULL); + if (phys == NULL) + return NULL; + + p = strstr(phys, "media"); + if (p) + *(p - 1) = '\0'; + p = strstr(phys, "sound"); + if (p) + *(p - 1) = '\0'; + p = strstr(phys, "video4linux"); + if (p) + *(p - 1) = '\0'; + + bus_type = media_enumerate_get_physical_bus(phys); + + switch (bus_type) { + case BUS_TYPE_PCI: + /* Remove the PCI function from the path if present. */ + p = strrchr(phys, '.'); + if (p) + *p = '\0'; + break; + case BUS_TYPE_USB: + /* Remove the USB interface from the path if present. */ + p = strrchr(phys, '/'); + if (p && strchr(p, ':')) + *p = '\0'; + break; + default: + break; + } + + return phys; +} + +#ifdef HAVE_LIBUDEV + +/* ----------------------------------------------------------------------------- + * udev helpers + */ + +static int media_enumerate_udev_scan_one(struct media_enumerate *media_enum, + const char *syspath, + media_enumerate_callback callback) +{ + struct udev_device *dev; + const char *devnode; + char *devpath = NULL; + dev_t devnum; + int ret = -ENODEV; + + dev = udev_device_new_from_syspath(media_enum->udev, syspath); + if (dev == NULL) + return -ENODEV; + + devnode = udev_device_get_devnode(dev); + if (devnode == NULL) + goto done; + + devpath = media_enumerate_get_physical_parent(syspath); + if (devpath == NULL) + goto done; + + devnum = udev_device_get_devnum(dev); + ret = callback(media_enum, devpath, devnode, devnum); + +done: + udev_device_unref(dev); + free(devpath); + return ret; +} + +static int media_enumerate_udev_scan(struct media_enumerate *media_enum, + const char *subsystem, const char *type, + media_enumerate_callback callback) +{ + struct udev_enumerate *udev_enum = NULL; + struct udev_list_entry *devs; + struct udev_list_entry *dev; + unsigned int len; + int ret; + + udev_enum = udev_enumerate_new(media_enum->udev); + if (udev_enum == NULL) { + ret = -ENOMEM; + goto done; + } + + ret = udev_enumerate_add_match_subsystem(udev_enum, subsystem); + if (ret < 0) + goto done; + + ret = udev_enumerate_scan_devices(udev_enum); + if (ret < 0) + goto done; + + devs = udev_enumerate_get_list_entry(udev_enum); + if (devs == NULL) + goto done; + + len = strlen(type); + + udev_list_entry_foreach(dev, devs) { + const char *syspath; + const char *p; + + syspath = udev_list_entry_get_name(dev); + + p = strrchr(syspath, '/'); + if (p == NULL || strncmp(p + 1, type, len)) + continue; + + media_enumerate_udev_scan_one(media_enum, syspath, callback); + } + + ret = 0; + +done: + udev_enumerate_unref(udev_enum); + return ret; +} + +#else + +/* ----------------------------------------------------------------------------- + * sysfs helpers + */ + +static int +media_enumerate_sysfs_scan_one(struct media_enumerate *media_enum, + const char *sysbase, const char *prefix, int idx, + media_enumerate_callback callback) +{ + unsigned int major; + unsigned int minor; + struct stat devstat; + unsigned int len; + char *devname; + char *devpath; + char dev[20]; + char *end; + int ret; + int fd; + + /* Read the device major and minor from sysfs. */ + len = strlen(sysbase) + strlen(prefix) + 17; + devname = malloc(len); + if (devname == NULL) + return -ENOMEM; + + sprintf(devname, "%s/%s%u", sysbase, prefix, idx); + devpath = media_enumerate_get_physical_parent(devname); + if (devpath == NULL) + goto done; + + sprintf(devname, "%s/%s%u/dev", sysbase, prefix, idx); + fd = open(devname, O_RDONLY); + if (fd == -1) { + ret = -ENODEV; + goto done; + } + + ret = read(fd, dev, sizeof dev - 1); + close(fd); + if (ret < 0) { + ret = -ENODEV; + goto done; + } + + dev[ret] = '\0'; + major = strtoul(dev, &end, 10); + if (*end != ':') { + ret = -ENODEV; + goto done; + } + + minor = strtoul(end + 1, &end, 10); + if (!isspace(*end) && end != '\0') { + ret = -ENODEV; + goto done; + } + + /* Verify that the device node exists. udev might have reordered the + * device nodes, make sure the major/minor match as a sanity check. + */ + if (!strcmp(prefix, "controlC")) + sprintf(devname, "/dev/snd/%s%u", prefix, idx); + else + sprintf(devname, "/dev/%s%u", prefix, idx); + + ret = stat(devname, &devstat); + if (ret < 0) { + ret = -ENODEV; + goto done; + } + + if (major(devstat.st_rdev) != major || minor(devstat.st_rdev) != minor) { + ret = -ENODEV; + goto done; + } + + ret = callback(media_enum, devpath, devname, makedev(major, minor)); + +done: + free(devname); + free(devpath); + return ret; +} + +static int +media_enumerate_sysfs_scan(struct media_enumerate *media_enum, + const char *sysbase, const char *prefix, + media_enumerate_callback callback) +{ + struct dirent *ent; + unsigned int len; + char *end; + DIR *dir; + + len = strlen(prefix); + + dir = opendir(sysbase); + if (dir == NULL) + return -ENODEV; + + while ((ent = readdir(dir)) != NULL) { + unsigned int idx; + + if (strncmp(ent->d_name, prefix, len)) + continue; + + idx = strtoul(ent->d_name + len, &end, 10); + if (*end != '\0') + continue; + + media_enumerate_sysfs_scan_one(media_enum, sysbase, prefix, idx, + callback); + } + + closedir(dir); + + return 0; +} + +#endif + +/* ----------------------------------------------------------------------------- + * Media devices enumeration + */ + +static int media_enumerate_match_one_media(struct media_enumerate *media_enum, + const char *syspath, const char *devname, + dev_t devnum __attribute__((__unused__))) +{ + struct media_device *media; + int ret; + + media = media_device_new(devname); + if (media == NULL) + return -ENODEV; + + ret = media_device_enumerate(media); + if (ret < 0) { + media_device_unref(media); + return -ENODEV; + } + + return media_enumerate_add_device(media_enum, syspath, media); +} + +static int media_enumerate_scan_media(struct media_enumerate *media_enum) +{ +#ifdef HAVE_LIBUDEV + return media_enumerate_udev_scan(media_enum, "media", "media", + media_enumerate_match_one_media); +#else + return media_enumerate_sysfs_scan(media_enum, "/sys/bus/media/devices", + "media", media_enumerate_match_one_media); +#endif /* HAVE_LIBUDEV */ +} + +/* ----------------------------------------------------------------------------- + * Video devices enumeration + */ + +static int media_enumerate_match_one_video(struct media_enumerate *media_enum, + const char *syspath, + const char *devname, dev_t devnum) +{ + struct media_entity_desc entity; + struct media_device_info info; + struct v4l2_capability cap; + struct media_device *media; + unsigned int i, j; + int ret; + int fd; + + /* Verify whether the video device node is part of an already enumerated + * media device. + */ + for (i = 0; i < media_enum->devices_count; ++i) { + struct media_entity *entities; + unsigned int nents; + + media = media_enum->devices[i]; + entities = media_get_entities(media); + nents = media_get_entities_count(media); + + for (j = 0; j < nents; ++j) { + struct media_entity *entity = &entities[j]; + dev_t dev; + + if (entity->info.type != MEDIA_ENT_T_DEVNODE_V4L) + continue; + + dev = makedev(entity->info.v4l.major, + entity->info.v4l.minor); + if (dev == devnum) + return 0; + } + } + + fd = open(devname, O_RDWR); + if (fd < 0) + return -ENODEV; + + memset(&cap, 0, sizeof cap); + ret = ioctl(fd, VIDIOC_QUERYCAP, &cap); + close(fd); + + if (ret < 0) + return -ENODEV; + + memset(&info, 0, sizeof info); + memcpy(info.driver, cap.driver, sizeof info.driver); + memcpy(info.model, cap.card, sizeof info.model); + memcpy(info.bus_info, cap.bus_info, sizeof info.bus_info); + info.driver_version = cap.version; + + media = media_device_new_emulated(&info); + if (media == NULL) + return -ENOMEM; + + memset(&entity, 0, sizeof entity); + memcpy(entity.name, cap.card, sizeof entity.name); + entity.type = MEDIA_ENT_T_DEVNODE_V4L; + entity.flags = MEDIA_ENT_FL_DEFAULT; + entity.v4l.major = major(devnum); + entity.v4l.minor = minor(devnum); + + ret = media_device_add_entity(media, &entity, devname); + if (ret < 0) { + media_device_unref(media); + return ret; + } + + return media_enumerate_add_device(media_enum, syspath, media); +} + +static int media_enumerate_scan_video(struct media_enumerate *media_enum) +{ +#ifdef HAVE_LIBUDEV + return media_enumerate_udev_scan(media_enum, "video4linux", "video", + media_enumerate_match_one_video); +#else + return media_enumerate_sysfs_scan(media_enum, "/sys/class/video4linux", + "video", media_enumerate_match_one_video); +#endif /* HAVE_LIBUDEV */ +} + +/* ----------------------------------------------------------------------------- + * Audio devices enumeration + */ + +static int media_enumerate_match_one_audio(struct media_enumerate *media_enum, + const char *syspath, const char *devname, + dev_t devnum __attribute__((__unused__))) +{ + struct media_entity_desc entity; + struct media_device *parent = NULL; + struct media_device *media; + unsigned int card; + unsigned int i, j; + char *endp; + + /* Verify whether the sound card is part of an already enumerated media + * device. + */ + card = strtoul(strrchr(devname, '/') + strlen("/controlC"), &endp, 10); + if (*endp != '\0') + return -EINVAL; + + for (i = 0; i < media_enum->devices_count; ++i) { + struct media_entity *entities; + unsigned int nents; + + media = media_enum->devices[i]; + entities = media_get_entities(media); + nents = media_get_entities_count(media); + + if (!strcmp(media_enum->syspaths[i], syspath)) + parent = media; + + for (j = 0; j < nents; ++j) { + struct media_entity *entity = &entities[j]; + + if (entity->info.type != MEDIA_ENT_T_DEVNODE_ALSA) + continue; + + if (entity->info.alsa.card == card) + return 0; + } + } + + if (!parent) + return 0; + + memset(&entity, 0, sizeof entity); + entity.type = MEDIA_ENT_T_DEVNODE_ALSA; + entity.flags = MEDIA_ENT_FL_DEFAULT; + snprintf(entity.name, sizeof entity.name, "ALSA %u/%u/%u", card, 0, 0); + entity.alsa.card = card; + entity.alsa.device = 0; + entity.alsa.subdevice = 0; + + return media_device_add_entity(parent, &entity, devname); +} + +static int media_enumerate_scan_audio(struct media_enumerate *media_enum) +{ +#ifdef HAVE_LIBUDEV + return media_enumerate_udev_scan(media_enum, "sound", "control", + media_enumerate_match_one_audio); +#else + return media_enumerate_sysfs_scan(media_enum, "/sys/class/sound", + "controlC", media_enumerate_match_one_audio); +#endif /* HAVE_LIBUDEV */ +} + +/* -------------------------------------------------------------------------- */ + +struct media_enumerate *media_enumerate_new(void) +{ + struct media_enumerate *media_enum; + + media_enum = calloc(1, sizeof *media_enum); + if (media_enum == NULL) + return NULL; + + media_enum->refcount = 1; + +#ifdef HAVE_LIBUDEV + media_enum->udev = udev_new(); + if (media_enum->udev == NULL) { + printf("Unable to get udev context\n"); + media_enumerate_unref(media_enum); + return NULL; + } +#endif + + return media_enum; +} + +struct media_enumerate *media_enumerate_ref(struct media_enumerate *media_enum) +{ + media_enum->refcount++; + return media_enum; +} + +void media_enumerate_unref(struct media_enumerate *media_enum) +{ + unsigned int i; + + media_enum->refcount--; + if (media_enum->refcount > 0) + return; + + for (i = 0; i < media_enum->devices_count; ++i) { + media_device_unref(media_enum->devices[i]); + free(media_enum->syspaths[i]); + } + +#ifdef HAVE_LIBUDEV + udev_unref(media_enum->udev); +#endif + + free(media_enum->devices); + free(media_enum->syspaths); + free(media_enum); +} + +int media_enumerate_scan(struct media_enumerate *media_enum) +{ + media_enumerate_scan_media(media_enum); + media_enumerate_scan_video(media_enum); + media_enumerate_scan_audio(media_enum); + + return 0; +} + +struct media_device **media_enumerate_get_devices(struct media_enumerate *media_enum) +{ + return media_enum->devices; +} + +unsigned int media_enumerate_get_devices_count(struct media_enumerate *media_enum) +{ + return media_enum->devices_count; +} diff --git a/media-enumerate.h b/media-enumerate.h new file mode 100644 index 0000000..ecd6b3d --- /dev/null +++ b/media-enumerate.h @@ -0,0 +1,114 @@ +/* + * Media device discovery library + * + * Copyright (C) 2012 Ideas on board SPRL + * + * Contact: Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef __MEDIA_ENUMERATE_H__ +#define __MEDIA_ENUMERATE_H__ + +struct media_enumerate; + +/** + * @brief Create a new media device enumeration context. + * + * Media device enumeration contexts are used to enumerate media devices. They + * store all context information required by enumeration and cache the + * enumeration results for later access. Enumeration itself is performed by the + * separate media_enumerate_scan() function. + * + * Enumeration contexts are reference-counted, see media_enumerate_ref() and + * media_enumerate_unref() for more information. + * + * @return A pointer to the new media device enumeration context or NULL if + * memory cannot be allocated. + */ +struct media_enumerate *media_enumerate_new(void); + +/** + * @brief Take a reference to the enumeration context. + * @param media_enum - enumeration context instance. + * + * Media device enumeration contexts are reference-counted. Taking a reference + * to an enumeration context prevents it and all the information it stores from + * being freed until all references are released. The reference count is + * initialized to 1 when the context is created. + * + * @return A pointer to @a media. + */ +struct media_enumerate *media_enumerate_ref(struct media_enumerate *media_enum); + +/** + * @brief Release a reference to the enumeration context. + * @param media_enum - enumeration context instance. + * + * Release a reference to the enumeration context. When the reference count + * reaches 0 this function frees the context, including all information it + * stores. + */ +void media_enumerate_unref(struct media_enumerate *media_enum); + +/** + * @brief Enumerate media devices in the system + * @param media_enum - enumeration context instance. + * + * Scan the system to locate all available media devices. This function must be + * called once and only once before accessing the list of enumerated devices. + * + * Devices are enumerated using libudev if available or through sysfs otherwise. + * sysfs-based enumeration requires device nodes to use the standard Linux + * device names and be numbered by the device sysfs device number. For instance + * the device node corresponding to /sys/class/video4linux/video0 must be named + * /dev/video0. libudev-based enumeration doesn't have this restriction. + * + * Media devices are enumerated by scanning media controller, V4L and ALSA + * kernel devices, in that order. Emulated media devices are created as needed + * for V4L devices that are not part of a media controller based device. ALSA + * devices without a corresponding media controller or V4L devices are skipped. + * + * @return Zero on success or a negative error code on failure. + */ +int media_enumerate_scan(struct media_enumerate *media_enum); + +/** + * @brief Get the number of media devices found during enumeration + * @param media_enum - enumeration context instance. + * + * This function returns the total number of media devices found in the system + * during enumeration. If media devices haven't been enumerated yet it will + * return 0. + * + * @return The number of media devices found in the system + */ +unsigned int media_enumerate_get_devices_count(struct media_enumerate *media_enum); + +/** + * @brief Get the media devices + * @param media_enum - enumeration context instance. + * + * This function returns a pointer to the array of enumerated media devices. If + * If media devices haven't been enumerated yet it will return NULL. + * + * The array of media devices is owned by the enumeration context and will be + * freed when the enumeration context is destroyed. + * + * @return A pointer to an array of media devices + */ +struct media_device **media_enumerate_get_devices(struct media_enumerate *media_enum); + +#endif /* __MEDIA_ENUMERATE_H__ */ -- cgit v1.2.3