From d4968694e251f603e7c2b91608746cf4c382723a Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Thu, 3 Feb 2011 10:50:48 +0200 Subject: Use autotools Use autoconf and automake for building. Quick instructions are added in INSTALL. Application source files are moved to src subdirectory. Signed-off-by: Todor Tomov --- INSTALL | 26 ++ Makefile | 18 -- Makefile.am | 2 + configure.in | 61 +++++ main.c | 809 -------------------------------------------------------- media.c | 388 --------------------------- media.h | 161 ----------- options.c | 155 ----------- options.h | 42 --- src/Makefile.am | 3 + src/main.c | 809 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/media.c | 388 +++++++++++++++++++++++++++ src/media.h | 161 +++++++++++ src/options.c | 155 +++++++++++ src/options.h | 42 +++ src/subdev.c | 187 +++++++++++++ src/subdev.h | 162 ++++++++++++ src/tools.h | 27 ++ subdev.c | 187 ------------- subdev.h | 162 ------------ tools.h | 27 -- 21 files changed, 2023 insertions(+), 1949 deletions(-) create mode 100644 INSTALL delete mode 100644 Makefile create mode 100644 Makefile.am create mode 100644 configure.in delete mode 100644 main.c delete mode 100644 media.c delete mode 100644 media.h delete mode 100644 options.c delete mode 100644 options.h create mode 100644 src/Makefile.am create mode 100644 src/main.c create mode 100644 src/media.c create mode 100644 src/media.h create mode 100644 src/options.c create mode 100644 src/options.h create mode 100644 src/subdev.c create mode 100644 src/subdev.h create mode 100644 src/tools.h delete mode 100644 subdev.c delete mode 100644 subdev.h delete mode 100644 tools.h diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..98e1ef9 --- /dev/null +++ b/INSTALL @@ -0,0 +1,26 @@ +Building: +--------- + +Useful 'configure' options: + --with-kernel-headers=DIR path of Linux kernel headers, + default /usr/src/kernel-headers. + + --host=TYPE host platform for cross-compilation. + + +Building: +$ autoreconf --install +$ ./configure [--with-kernel-headers=DIR] [--host=TYPE] +$ make + + +Installing: +----------- + +$ sudo make install + + +Uninstalling: +------------- + +$ sudo make uninstall diff --git a/Makefile b/Makefile deleted file mode 100644 index a89b79c..0000000 --- a/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -CROSS_COMPILE ?= -KDIR ?= - -KINC := -I$(KDIR)/usr/include -CC := $(CROSS_COMPILE)gcc - -CFLAGS += -O2 -Wall -fpic -I. $(KINC) -OBJS = media.o main.o options.o subdev.o - -all: media-ctl - -media-ctl: $(OBJS) - $(CC) $(CFLAGS) -o media-ctl $(OBJS) - $(CROSS_COMPILE)strip media-ctl -clean: - rm -f $(OBJS) media-ctl - -.PHONY: clean all diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..f268924 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = src + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..1b627bc --- /dev/null +++ b/configure.in @@ -0,0 +1,61 @@ +AC_PREREQ([2.65]) +AC_INIT([media-ctl], [0.0.1], [laurent.pinchart@ideasonboard.com]) +AC_CONFIG_SRCDIR([src/main.c]) +AC_CONFIG_AUX_DIR([config]) +AC_CONFIG_HEADERS([config.h]) + +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. + +# Kernel headers path. +AC_ARG_WITH(kernel-headers, + [AC_HELP_STRING([--with-kernel-headers=DIR], + [specify path of Linux kernel headers [/usr/src/kernel-headers]])], + [case "${withval}" in + yes | no) AC_MSG_ERROR([bad value ${withval} for --with-kernel-headers]) ;; + *) KERNEL_HEADERS_DIR="${withval}" ;; + esac], + [KERNEL_HEADERS_DIR="/usr/src/kernel-headers"]) + +CPPFLAGS="-I$KERNEL_HEADERS_DIR/include" + +# Checks for header files. +AC_CHECK_HEADERS([linux/media.h \ + linux/types.h \ + linux/v4l2-mediabus.h \ + linux/v4l2-subdev.h \ + linux/videodev2.h], + [], + [echo "ERROR: Kernel header file not found or not usable!"; exit 1]) + +AC_CHECK_HEADERS([fcntl.h \ + stdlib.h \ + string.h \ + sys/ioctl.h \ + sys/time.h \ + unistd.h], + [], + [echo "ERROR: Header file not found or not usable!"; exit 1]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_INLINE +AC_TYPE_SIZE_T +AC_CHECK_MEMBERS([struct stat.st_rdev]) + +# Checks for library functions. +AC_HEADER_MAJOR +AC_FUNC_MALLOC +AC_FUNC_REALLOC +AC_CHECK_FUNCS([memset strerror strrchr strtoul]) + +AC_CONFIG_FILES([ + Makefile + src/Makefile +]) + +AC_OUTPUT + diff --git a/main.c b/main.c deleted file mode 100644 index aa4ced0..0000000 --- a/main.c +++ /dev/null @@ -1,809 +0,0 @@ -/* - * Media controller test application - * - * Copyright (C) 2010 Ideas on board SPRL - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "media.h" -#include "options.h" -#include "subdev.h" -#include "tools.h" - -/* ----------------------------------------------------------------------------- - * Printing - */ - -static struct { - const char *name; - enum v4l2_mbus_pixelcode code; -} mbus_formats[] = { - { "Y8", V4L2_MBUS_FMT_Y8_1X8}, - { "YUYV", V4L2_MBUS_FMT_YUYV8_1X16 }, - { "UYVY", V4L2_MBUS_FMT_UYVY8_1X16 }, - { "SGRBG10", V4L2_MBUS_FMT_SGRBG10_1X10 }, - { "SGRBG10_DPCM8", V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 }, -}; - -static const char *pixelcode_to_string(enum v4l2_mbus_pixelcode code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) { - if (mbus_formats[i].code == code) - return mbus_formats[i].name; - } - - return "unknown"; -} - -static enum v4l2_mbus_pixelcode string_to_pixelcode(const char *string, - unsigned int length) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) { - if (strncmp(mbus_formats[i].name, string, length) == 0) - break; - } - - if (i == ARRAY_SIZE(mbus_formats)) - return (enum v4l2_mbus_pixelcode)-1; - - return mbus_formats[i].code; -} - -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("[%s %ux%u", pixelcode_to_string(format.code), - format.width, format.height); - - ret = v4l2_subdev_get_crop(entity, &rect, pad, which); - if (ret == 0) - printf(" (%u,%u)/%ux%u", rect.left, rect.top, - rect.width, rect.height); - printf("]"); -} - -static const char *media_entity_type_to_string(unsigned type) -{ - static const struct { - __u32 type; - const char *name; - } types[] = { - { MEDIA_ENTITY_TYPE_NODE, "Node" }, - { MEDIA_ENTITY_TYPE_SUBDEV, "V4L2 subdev" }, - }; - - unsigned int i; - - type &= MEDIA_ENTITY_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_ENTITY_SUBTYPE_MASK; - - switch (type & MEDIA_ENTITY_TYPE_MASK) { - case MEDIA_ENTITY_TYPE_NODE: - if (subtype >= ARRAY_SIZE(node_types)) - subtype = 0; - return node_types[subtype]; - - case MEDIA_ENTITY_TYPE_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_FLAG_INPUT, "Input" }, - { MEDIA_PAD_FLAG_OUTPUT, "Output" }, - }; - - 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_dot(struct media_device *media) -{ - unsigned int i, j; - - printf("digraph board {\n"); - printf("\trankdir=TB\n"); - - for (i = 0; i < media->entities_count; ++i) { - struct media_entity *entity = &media->entities[i]; - unsigned int npads; - - switch (media_entity_type(entity)) { - case MEDIA_ENTITY_TYPE_NODE: - printf("\tn%08x [label=\"%s\\n%s\", shape=box, style=filled, " - "fillcolor=yellow]\n", - entity->info.id, entity->info.name, entity->devname); - break; - - case MEDIA_ENTITY_TYPE_SUBDEV: - printf("\tn%08x [label=\"{{", entity->info.id); - - for (j = 0, npads = 0; j < entity->info.pads; ++j) { - if (!(entity->pads[j].flags & MEDIA_PAD_FLAG_INPUT)) - continue; - - printf("%s %u", npads ? " | " : "", j, j); - npads++; - } - - printf("} | %s", entity->info.name); - if (entity->devname) - printf("\\n%s", entity->devname); - printf(" | {"); - - for (j = 0, npads = 0; j < entity->info.pads; ++j) { - if (!(entity->pads[j].flags & MEDIA_PAD_FLAG_OUTPUT)) - continue; - - printf("%s %u", npads ? " | " : "", j, j); - npads++; - } - - printf("}}\", shape=Mrecord, style=filled, fillcolor=green]\n"); - break; - - default: - continue; - } - - for (j = 0; j < entity->num_links; j++) { - struct media_link *link = &entity->links[j]; - - if (link->source->entity != entity) - continue; - - printf("\tn%08x", link->source->entity->info.id); - if (media_entity_type(link->source->entity) == MEDIA_ENTITY_TYPE_SUBDEV) - printf(":port%u", link->source->index); - printf(" -> "); - printf("n%08x", link->sink->entity->info.id); - if (media_entity_type(link->sink->entity) == MEDIA_ENTITY_TYPE_SUBDEV) - printf(":port%u", link->sink->index); - - if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE) - printf(" [style=bold]"); - else if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE)) - printf(" [style=dashed]"); - printf("\n"); - } - } - - printf("}\n"); -} - -static void media_print_topology_text(struct media_device *media) -{ - unsigned int i, j, k; - unsigned int padding; - - printf("Device topology\n"); - - for (i = 0; i < media->entities_count; ++i) { - struct media_entity *entity = &media->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->info.links, entity->info.links > 1 ? "s" : ""); - printf("%*ctype %s subtype %s\n", padding, ' ', - media_entity_type_to_string(entity->info.type), - media_entity_subtype_to_string(entity->info.type)); - 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 ", j, media_pad_type_to_string(pad->flags)); - - if (media_entity_type(entity) == MEDIA_ENTITY_TYPE_SUBDEV) - v4l2_subdev_print_format(entity, j, V4L2_SUBDEV_FORMAT_ACTIVE); - - printf("\n"); - - for (k = 0; k < entity->num_links; k++) { - struct media_link *link = &entity->links[k]; - - if (link->source->entity != entity || - link->source->index != j) - continue; - - printf("\t\t-> '%s':pad%u [", - link->sink->entity->info.name, link->sink->index); - - if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE) - printf("IMMUTABLE,"); - if (link->flags & MEDIA_LINK_FLAG_ACTIVE) - printf("ACTIVE"); - - printf("]\n"); - } - } - printf("\n"); - } -} - -void media_print_topology(struct media_device *media, int dot) -{ - if (dot) - media_print_topology_dot(media); - else - media_print_topology_text(media); -} - -/* ----------------------------------------------------------------------------- - * Links setup - */ - -static struct media_pad *parse_pad(struct media_device *media, const char *p, char **endp) -{ - unsigned int entity_id, pad; - struct media_entity *entity; - char *end; - - for (; isspace(*p); ++p); - - if (*p == '"') { - for (end = (char *)p + 1; *end && *end != '"'; ++end); - if (*end != '"') - return NULL; - - entity = media_get_entity_by_name(media, p + 1, end - p - 1); - if (entity == NULL) - return NULL; - - ++end; - } else { - entity_id = strtoul(p, &end, 10); - entity = media_get_entity_by_id(media, entity_id); - if (entity == NULL) - return NULL; - } - for (; isspace(*end); ++end); - - if (*end != ':') - return NULL; - for (p = end + 1; isspace(*p); ++p); - - pad = strtoul(p, &end, 10); - for (p = end; isspace(*p); ++p); - - if (pad >= entity->info.pads) - return NULL; - - for (p = end; isspace(*p); ++p); - if (endp) - *endp = (char *)p; - - return &entity->pads[pad]; -} - -static struct media_link *parse_link(struct media_device *media, const char *p, char **endp) -{ - struct media_link *link; - struct media_pad *source; - struct media_pad *sink; - unsigned int i; - char *end; - - source = parse_pad(media, p, &end); - if (source == NULL) - return NULL; - - if (end[0] != '-' || end[1] != '>') - return NULL; - p = end + 2; - - sink = parse_pad(media, p, &end); - if (sink == NULL) - return NULL; - - *endp = end; - - for (i = 0; i < source->entity->num_links; i++) { - link = &source->entity->links[i]; - - if (link->source == source && link->sink == sink) - return link; - } - - return NULL; -} - -static int setup_link(struct media_device *media, const char *p, char **endp) -{ - struct media_link *link; - __u32 flags; - char *end; - - link = parse_link(media, p, &end); - if (link == NULL) { - printf("Unable to parse link\n"); - return -EINVAL; - } - - p = end; - if (*p++ != '[') { - printf("Unable to parse link flags\n"); - return -EINVAL; - } - - flags = strtoul(p, &end, 10); - for (p = end; isspace(*p); p++); - if (*p++ != ']') { - printf("Unable to parse link flags\n"); - return -EINVAL; - } - - for (; isspace(*p); p++); - *endp = (char *)p; - - printf("Setting up link %u:%u -> %u:%u [%u]\n", - link->source->entity->info.id, link->source->index, - link->sink->entity->info.id, link->sink->index, - flags); - - return media_setup_link(media, link->source, link->sink, flags); -} - -static int setup_links(struct media_device *media, const char *p) -{ - char *end; - int ret; - - do { - ret = setup_link(media, p, &end); - if (ret < 0) - return ret; - - p = end + 1; - } while (*end == ','); - - return *end ? -EINVAL : 0; -} - -/* ----------------------------------------------------------------------------- - * Formats setup - */ - -static int parse_format(struct v4l2_mbus_framefmt *format, const char *p, char **endp) -{ - enum v4l2_mbus_pixelcode code; - unsigned int width, height; - char *end; - - for (; isspace(*p); ++p); - for (end = (char *)p; !isspace(*end) && *end != '\0'; ++end); - - code = string_to_pixelcode(p, end - p); - if (code == (enum v4l2_mbus_pixelcode)-1) - return -EINVAL; - - for (p = end; isspace(*p); ++p); - width = strtoul(p, &end, 10); - if (*end != 'x') - return -EINVAL; - - p = end + 1; - height = strtoul(p, &end, 10); - *endp = end; - - memset(format, 0, sizeof(*format)); - format->width = width; - format->height = height; - format->code = code; - - return 0; -} - -static int parse_crop(struct v4l2_rect *crop, const char *p, char **endp) -{ - char *end; - - if (*p++ != '(') - return -EINVAL; - - crop->left = strtoul(p, &end, 10); - if (*end != ',') - return -EINVAL; - - p = end + 1; - crop->top = strtoul(p, &end, 10); - if (*end++ != ')') - return -EINVAL; - if (*end != '/') - return -EINVAL; - - p = end + 1; - crop->width = strtoul(p, &end, 10); - if (*end != 'x') - return -EINVAL; - - p = end + 1; - crop->height = strtoul(p, &end, 10); - *endp = end; - - return 0; -} - -static int parse_frame_interval(struct v4l2_fract *interval, const char *p, char **endp) -{ - char *end; - - for (; isspace(*p); ++p); - - interval->numerator = strtoul(p, &end, 10); - - for (p = end; isspace(*p); ++p); - if (*p++ != '/') - return -EINVAL; - - for (; isspace(*p); ++p); - interval->denominator = strtoul(p, &end, 10); - - *endp = end; - return 0; -} - -static struct media_pad *parse_pad_format(struct media_device *media, - struct v4l2_mbus_framefmt *format, struct v4l2_rect *crop, - struct v4l2_fract *interval, const char *p, char **endp) -{ - struct media_pad *pad; - char *end; - int ret; - - for (; isspace(*p); ++p); - - pad = parse_pad(media, p, &end); - if (pad == NULL) - return NULL; - - for (p = end; isspace(*p); ++p); - if (*p++ != '[') - return NULL; - - for (; isspace(*p); ++p); - - if (isalnum(*p)) { - ret = parse_format(format, p, &end); - if (ret < 0) - return NULL; - - for (p = end; isspace(*p); p++); - } - - if (*p == '(') { - ret = parse_crop(crop, p, &end); - if (ret < 0) - return NULL; - - for (p = end; isspace(*p); p++); - } - - if (*p == '@') { - ret = parse_frame_interval(interval, ++p, &end); - if (ret < 0) - return NULL; - - for (p = end; isspace(*p); p++); - } - - if (*p != ']') - return NULL; - - *endp = (char *)p + 1; - return pad; -} - -static int set_format(struct media_pad *pad, struct v4l2_mbus_framefmt *format) -{ - int ret; - - if (format->width == 0 || format->height == 0) - return 0; - - printf("Setting up format %s %ux%u on pad %s/%u\n", - pixelcode_to_string(format->code), format->width, format->height, - pad->entity->info.name, pad->index); - - ret = v4l2_subdev_set_format(pad->entity, format, pad->index, - V4L2_SUBDEV_FORMAT_ACTIVE); - if (ret < 0) { - printf("Unable to set format: %s (%d)\n", strerror(-ret), ret); - return ret; - } - - printf("Format set: %s %ux%u\n", - pixelcode_to_string(format->code), format->width, format->height); - - return 0; -} - -static int set_crop(struct media_pad *pad, struct v4l2_rect *crop) -{ - int ret; - - if (crop->left == -1 || crop->top == -1) - return 0; - - printf("Setting up crop rectangle (%u,%u)/%ux%u on pad %s/%u\n", - crop->left, crop->top, crop->width, crop->height, - pad->entity->info.name, pad->index); - - ret = v4l2_subdev_set_crop(pad->entity, crop, pad->index, - V4L2_SUBDEV_FORMAT_ACTIVE); - if (ret < 0) { - printf("Unable to set crop rectangle: %s (%d)\n", strerror(-ret), ret); - return ret; - } - - printf("Crop rectangle set: (%u,%u)/%ux%u\n", - crop->left, crop->top, crop->width, crop->height); - - return 0; -} - -static int set_frame_interval(struct media_entity *entity, struct v4l2_fract *interval) -{ - int ret; - - if (interval->numerator == 0) - return 0; - - printf("Setting up frame interval %u/%u on entity %s\n", - interval->numerator, interval->denominator, entity->info.name); - - ret = v4l2_subdev_set_frame_interval(entity, interval); - if (ret < 0) { - printf("Unable to set frame interval: %s (%d)", strerror(-ret), ret); - return ret; - } - - printf("Frame interval set: %u/%u\n", - interval->numerator, interval->denominator); - - return 0; -} - - -static int setup_format(struct media_device *media, const char *p, char **endp) -{ - struct v4l2_mbus_framefmt format = { 0, 0, 0 }; - struct media_pad *pad; - struct v4l2_rect crop = { -1, -1, -1, -1 }; - struct v4l2_fract interval = { 0, 0 }; - unsigned int i; - char *end; - int ret; - - pad = parse_pad_format(media, &format, &crop, &interval, p, &end); - if (pad == NULL) { - printf("Unable to parse format\n"); - return -EINVAL; - } - - if (pad->flags & MEDIA_PAD_FLAG_OUTPUT) { - ret = set_crop(pad, &crop); - if (ret < 0) - return ret; - } - - ret = set_format(pad, &format); - if (ret < 0) - return ret; - - if (pad->flags & MEDIA_PAD_FLAG_INPUT) { - ret = set_crop(pad, &crop); - if (ret < 0) - return ret; - } - - ret = set_frame_interval(pad->entity, &interval); - if (ret < 0) - return ret; - - - /* If the pad is an output pad, automatically set the same format on - * the remote subdev input pads, if any. - */ - if (pad->flags & MEDIA_PAD_FLAG_OUTPUT) { - for (i = 0; i < pad->entity->num_links; ++i) { - struct media_link *link = &pad->entity->links[i]; - struct v4l2_mbus_framefmt remote_format; - - if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE)) - continue; - - if (link->source == pad && - link->sink->entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV) { - remote_format = format; - set_format(link->sink, &remote_format); - } - } - } - - *endp = end; - return 0; -} - -static int setup_formats(struct media_device *media, const char *p) -{ - char *end; - int ret; - - do { - ret = setup_format(media, p, &end); - if (ret < 0) - return ret; - - p = end + 1; - } while (*end == ','); - - return *end ? -EINVAL : 0; -} - -int main(int argc, char **argv) -{ - struct media_device *media; - int ret = -1; - - if (parse_cmdline(argc, argv)) - return EXIT_FAILURE; - - /* Open the media device and enumerate entities, pads and links. */ - media = media_open(media_opts.devname, media_opts.verbose); - if (media == NULL) - goto out; - - if (media_opts.entity) { - struct media_entity *entity; - - entity = media_get_entity_by_name(media, media_opts.entity, - strlen(media_opts.entity)); - if (entity == NULL) { - printf("Entity '%s' not found\n", media_opts.entity); - goto out; - } - - printf("%s\n", entity->devname); - } - - if (media_opts.pad) { - struct media_pad *pad; - - pad = parse_pad(media, media_opts.pad, NULL); - if (pad == NULL) { - printf("Pad '%s' not found\n", media_opts.pad); - goto out; - } - - v4l2_subdev_print_format(pad->entity, pad->index, - V4L2_SUBDEV_FORMAT_ACTIVE); - printf("\n"); - } - - if (media_opts.print || media_opts.print_dot) { - media_print_topology(media, media_opts.print_dot); - printf("\n"); - } - - if (media_opts.reset) { - printf("Resetting all links to inactive\n"); - media_reset_links(media); - } - - if (media_opts.links) - setup_links(media, media_opts.links); - - if (media_opts.formats) - setup_formats(media, media_opts.formats); - - if (media_opts.interactive) { - while (1) { - char buffer[32]; - char *end; - - printf("Enter a link to modify or enter to stop\n"); - if (fgets(buffer, sizeof buffer, stdin) == NULL) - break; - - if (buffer[0] == '\n') - break; - - setup_link(media, buffer, &end); - } - } - - ret = 0; - -out: - if (media) - media_close(media); - - return ret ? EXIT_FAILURE : EXIT_SUCCESS; -} - diff --git a/media.c b/media.c deleted file mode 100644 index 456b882..0000000 --- a/media.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Media controller test application - * - * Copyright (C) 2010 Ideas on board SPRL - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "media.h" -#include "tools.h" - -struct media_pad *media_entity_remote_source(struct media_pad *pad) -{ - unsigned int i; - - if (!(pad->flags & MEDIA_PAD_FLAG_INPUT)) - return NULL; - - for (i = 0; i < pad->entity->num_links; ++i) { - struct media_link *link = &pad->entity->links[i]; - - if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE)) - continue; - - if (link->sink == pad) - return link->source; - } - - return NULL; -} - -struct media_entity *media_get_entity_by_name(struct media_device *media, - const char *name, size_t length) -{ - unsigned int i; - - for (i = 0; i < media->entities_count; ++i) { - struct media_entity *entity = &media->entities[i]; - - if (strncmp(entity->info.name, name, length) == 0) - return entity; - } - - return NULL; -} - -struct media_entity *media_get_entity_by_id(struct media_device *media, - __u32 id) -{ - unsigned int i; - - for (i = 0; i < media->entities_count; ++i) { - struct media_entity *entity = &media->entities[i]; - - if (entity->info.id == id) - return entity; - } - - return NULL; -} - -int media_setup_link(struct media_device *media, - struct media_pad *source, - struct media_pad *sink, - __u32 flags) -{ - struct media_link *link; - struct media_link_desc ulink; - unsigned int i; - int ret; - - for (i = 0; i < source->entity->num_links; i++) { - link = &source->entity->links[i]; - - if (link->source->entity == source->entity && - link->source->index == source->index && - link->sink->entity == sink->entity && - link->sink->index == sink->index) - break; - } - - if (i == source->entity->num_links) { - printf("%s: Link not found\n", __func__); - return -EINVAL; - } - - /* source pad */ - ulink.source.entity = source->entity->info.id; - ulink.source.index = source->index; - ulink.source.flags = MEDIA_PAD_FLAG_OUTPUT; - - /* sink pad */ - ulink.sink.entity = sink->entity->info.id; - ulink.sink.index = sink->index; - ulink.sink.flags = MEDIA_PAD_FLAG_INPUT; - - ulink.flags = flags | (link->flags & MEDIA_LINK_FLAG_IMMUTABLE); - - ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink); - if (ret < 0) { - printf("%s: Unable to setup link (%s)\n", __func__, - strerror(errno)); - return ret; - } - - link->flags = flags; - return 0; -} - -int media_reset_links(struct media_device *media) -{ - unsigned int i, j; - int ret; - - for (i = 0; i < media->entities_count; ++i) { - struct media_entity *entity = &media->entities[i]; - - for (j = 0; j < entity->num_links; j++) { - struct media_link *link = &entity->links[j]; - - if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE || - link->source->entity != entity) - continue; - - ret = media_setup_link(media, link->source, link->sink, - link->flags & ~MEDIA_LINK_FLAG_ACTIVE); - if (ret < 0) - return ret; - } - } - - return 0; -} - -static struct media_link *media_entity_add_link(struct media_entity *entity) -{ - if (entity->num_links >= entity->max_links) { - struct media_link *links = entity->links; - unsigned int max_links = entity->max_links * 2; - unsigned int i; - - links = realloc(links, max_links * sizeof *links); - if (links == NULL) - return NULL; - - for (i = 0; i < entity->num_links; ++i) - links[i].twin->twin = &links[i]; - - entity->max_links = max_links; - entity->links = links; - } - - return &entity->links[entity->num_links++]; -} - -static int media_enum_links(struct media_device *media) -{ - __u32 id; - int ret = 0; - - for (id = 1; id <= media->entities_count; id++) { - struct media_entity *entity = &media->entities[id - 1]; - struct media_links_enum links; - unsigned int i; - - links.entity = entity->info.id; - links.pads = malloc(entity->info.pads * sizeof(struct media_pad_desc)); - links.links = malloc(entity->info.links * sizeof(struct media_link_desc)); - - if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) { - printf("%s: Unable to enumerate pads and links (%s).\n", - __func__, strerror(errno)); - free(links.pads); - free(links.links); - return -errno; - } - - for (i = 0; i < entity->info.pads; ++i) { - entity->pads[i].entity = entity; - entity->pads[i].index = links.pads[i].index; - entity->pads[i].flags = links.pads[i].flags; - } - - for (i = 0; i < entity->info.links; ++i) { - struct media_link_desc *link = &links.links[i]; - struct media_link *fwdlink; - struct media_link *backlink; - struct media_entity *source; - struct media_entity *sink; - - source = media_get_entity_by_id(media, link->source.entity); - sink = media_get_entity_by_id(media, link->sink.entity); - - if (source == NULL || sink == NULL) { - printf("WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n", - id, i, link->source.entity, link->source.index, - link->sink.entity, link->sink.index); - ret = -EINVAL; - } else { - fwdlink = media_entity_add_link(source); - fwdlink->source = &source->pads[link->source.index]; - fwdlink->sink = &sink->pads[link->sink.index]; - fwdlink->flags = links.links[i].flags; - - backlink = media_entity_add_link(sink); - backlink->source = &source->pads[link->source.index]; - backlink->sink = &sink->pads[link->sink.index]; - backlink->flags = links.links[i].flags; - - fwdlink->twin = backlink; - backlink->twin = fwdlink; - } - } - - free(links.pads); - free(links.links); - } - - return ret; -} - -static int media_enum_entities(struct media_device *media) -{ - struct media_entity *entity; - struct stat devstat; - unsigned int size; - char devname[32]; - char sysname[32]; - char target[1024]; - char *p; - __u32 id; - int ret; - - for (id = 0; ; id = entity->info.id) { - size = (media->entities_count + 1) * sizeof(*media->entities); - media->entities = realloc(media->entities, size); - - entity = &media->entities[media->entities_count]; - memset(entity, 0, sizeof(*entity)); - entity->fd = -1; - entity->info.id = id | MEDIA_ENTITY_ID_FLAG_NEXT; - - ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info); - if (ret < 0) { - if (errno == EINVAL) - break; - return -errno; - } - - /* Number of links (for outbound links) plus number of pads (for - * inbound links) is a good safe initial estimate of the total - * number of links. - */ - entity->max_links = entity->info.pads + entity->info.links; - - entity->pads = malloc(entity->info.pads * sizeof(*entity->pads)); - entity->links = malloc(entity->max_links * sizeof(*entity->links)); - if (entity->pads == NULL || entity->links == NULL) - return -ENOMEM; - - media->entities_count++; - - /* Find the corresponding device name. */ - if (media_entity_type(entity) != MEDIA_ENTITY_TYPE_NODE && - media_entity_type(entity) != MEDIA_ENTITY_TYPE_SUBDEV) - continue; - - sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major, - entity->info.v4l.minor); - ret = readlink(sysname, target, sizeof(target)); - if (ret < 0) - continue; - - target[ret] = '\0'; - p = strrchr(target, '/'); - if (p == NULL) - continue; - - sprintf(devname, "/dev/%s", p + 1); - ret = stat(devname, &devstat); - if (ret < 0) - continue; - - /* Sanity check: udev might have reordered the device nodes. - * Make sure the major/minor match. We should really use - * libudev. - */ - if (major(devstat.st_rdev) == entity->info.v4l.major && - minor(devstat.st_rdev) == entity->info.v4l.minor) - strcpy(entity->devname, devname); - } - - return 0; -} - -struct media_device *media_open(const char *name, int verbose) -{ - struct media_device *media; - int ret; - - media = malloc(sizeof(*media)); - if (media == NULL) { - printf("%s: unable to allocate memory\n", __func__); - return NULL; - } - memset(media, 0, sizeof(*media)); - - if (verbose) - printf("Opening media device %s\n", name); - media->fd = open(name, O_RDWR); - if (media->fd < 0) { - media_close(media); - printf("%s: Can't open media device %s\n", __func__, name); - return NULL; - } - - if (verbose) - printf("Enumerating entities\n"); - - ret = media_enum_entities(media); - if (ret < 0) { - printf("%s: Unable to enumerate entities for device %s (%s)\n", - __func__, name, strerror(-ret)); - media_close(media); - return NULL; - } - - if (verbose) { - printf("Found %u entities\n", media->entities_count); - printf("Enumerating pads and links\n"); - } - - ret = media_enum_links(media); - if (ret < 0) { - printf("%s: Unable to enumerate pads and linksfor device %s\n", - __func__, name); - media_close(media); - return NULL; - } - - return media; -} - -void media_close(struct media_device *media) -{ - unsigned int i; - - if (media->fd != -1) - close(media->fd); - - for (i = 0; i < media->entities_count; ++i) { - struct media_entity *entity = &media->entities[i]; - - free(entity->pads); - free(entity->links); - if (entity->fd != -1) - close(entity->fd); - } - - free(media->entities); - free(media); -} - diff --git a/media.h b/media.h deleted file mode 100644 index 80cf976..0000000 --- a/media.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Media controller test application - * - * Copyright (C) 2010 Ideas on board SPRL - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - */ - -#ifndef __MEDIA_H__ -#define __MEDIA_H__ - -#include - -struct media_link { - struct media_pad *source; - struct media_pad *sink; - struct media_link *twin; - __u32 flags; - __u32 padding[3]; -}; - -struct media_pad { - struct media_entity *entity; - __u32 index; - __u32 flags; - __u32 padding[3]; -}; - -struct media_entity { - struct media_entity_desc info; - struct media_pad *pads; - struct media_link *links; - unsigned int max_links; - unsigned int num_links; - - char devname[32]; - int fd; - __u32 padding[6]; -}; - -struct media_device { - int fd; - struct media_entity *entities; - unsigned int entities_count; - __u32 padding[6]; -}; - -/** - * @brief Open a media device. - * @param name - name (including path) of the device node. - * @param verbose - whether to print verbose information on the standard output. - * - * Open the media device referenced by @a name and enumerate entities, pads and - * links. - * - * @return A pointer to a newly allocated media_device structure instance on - * success and NULL on failure. The returned pointer must be freed with - * media_close when the device isn't needed anymore. - */ -struct media_device *media_open(const char *name, int verbose); - -/** - * @brief Close a media device. - * @param media - device instance. - * - * Close the @a media device instance and free allocated resources. Access to the - * device instance is forbidden after this function returns. - */ -void media_close(struct media_device *media); - -/** - * @brief Locate the pad at the other end of a link. - * @param pad - sink pad at one end of the link. - * - * Locate the source pad connected to @a pad through an enabled link. As only one - * link connected to a sink pad can be enabled at a time, the connected source - * pad is guaranteed to be unique. - * - * @return A pointer to the connected source pad, or NULL if all links connected - * to @a pad are disabled. Return NULL also if @a pad is not a sink pad. - */ -struct media_pad *media_entity_remote_source(struct media_pad *pad); - -/** - * @brief Get the type of an entity. - * @param entity - the entity. - * - * @return The type of @a entity. - */ -static inline unsigned int media_entity_type(struct media_entity *entity) -{ - return entity->info.type & MEDIA_ENTITY_TYPE_MASK; -} - -/** - * @brief Find an entity by its name. - * @param media - media device. - * @param name - entity name. - * @param length - size of @a name. - * - * Search for an entity with a name equal to @a name. - * - * @return A pointer to the entity if found, or NULL otherwise. - */ -struct media_entity *media_get_entity_by_name(struct media_device *media, - const char *name, size_t length); - -/** - * @brief Find an entity by its ID. - * @param media - media device. - * @param id - entity ID. - * - * Search for an entity with an ID equal to @a id. - * - * @return A pointer to the entity if found, or NULL otherwise. - */ -struct media_entity *media_get_entity_by_id(struct media_device *media, - __u32 id); - -/** - * @brief Configure a link. - * @param media - media device. - * @param source - source pad at the link origin. - * @param sink - sink pad at the link target. - * @param flags - configuration flags. - * - * Locate the link between @a source and @a sink, and configure it by applying - * the new @a flags. - * - * Only the MEDIA_LINK_FLAG_ENABLED flag is writable. - * - * @return 0 on success, or a negative error code on failure. - */ -int media_setup_link(struct media_device *media, - struct media_pad *source, struct media_pad *sink, - __u32 flags); - -/** - * @brief Reset all links to the disabled state. - * @param media - media device. - * - * Disable all links in the media device. This function is usually used after - * opening a media device to reset all links to a known state. - * - * @return 0 on success, or a negative error code on failure. - */ -int media_reset_links(struct media_device *media); - -#endif - diff --git a/options.c b/options.c deleted file mode 100644 index 186425e..0000000 --- a/options.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Media controller test application - * - * Copyright (C) 2010 Ideas on board SPRL - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - */ - -#include -#include -#include -#include - -#include "options.h" - -#define MEDIA_DEVNAME_DEFAULT "/dev/media0" - -struct media_options media_opts = { - .devname = MEDIA_DEVNAME_DEFAULT, -}; - -static void usage(const char *argv0, int verbose) -{ - printf("%s [options] device\n", argv0); - printf("-d, --device dev Media device name (default: %s)\n", MEDIA_DEVNAME_DEFAULT); - printf("-e, --entity name Print the device name associated with the given entity\n"); - printf("-f, --set-format Comma-separated list of formats to setup\n"); - printf(" --get-format pad Print the active format on a given pad\n"); - printf("-h, --help Show verbose help and exit\n"); - printf("-i, --interactive Modify links interactively\n"); - printf("-l, --links Comma-separated list of links descriptors to setup\n"); - printf("-p, --print-topology Print the device topology (implies -v)\n"); - printf(" --print-dot Print the device topology as a dot graph (implies -v)\n"); - printf("-r, --reset Reset all links to inactive\n"); - printf("-v, --verbose Be verbose\n"); - - if (!verbose) - return; - - printf("\n"); - printf("Links and formats are defined as\n"); - printf("\tlink = pad, '->', pad, '[', flags, ']' ;\n"); - printf("\tformat = pad, '[', fcc, ' ', size, [ ' ', crop ], [ ' ', '@', frame interval ], ']' ;\n"); - printf("\tpad = entity, ':', pad number ;\n"); - printf("\tentity = entity number | ( '\"', entity name, '\"' ) ;\n"); - printf("\tsize = width, 'x', height ;\n"); - printf("\tcrop = '(', left, ',', top, ')', '/', size ;\n"); - printf("\tframe interval = numerator, '/', denominator ;\n"); - printf("where the fields are\n"); - printf("\tentity number Entity numeric identifier\n"); - printf("\tentity name Entity name (string) \n"); - printf("\tpad number Pad numeric identifier\n"); - printf("\tflags Link flags (0: inactive, 1: active)\n"); - printf("\tfcc Format FourCC\n"); - printf("\twidth Image width in pixels\n"); - printf("\theight Image height in pixels\n"); - printf("\tnumerator Frame interval numerator\n"); - printf("\tdenominator Frame interval denominator\n"); -} - -#define OPT_PRINT_DOT 256 -#define OPT_GET_FORMAT 257 - -static struct option opts[] = { - {"device", 1, 0, 'd'}, - {"entity", 1, 0, 'e'}, - {"set-format", 1, 0, 'f'}, - {"get-format", 1, 0, OPT_GET_FORMAT}, - {"help", 0, 0, 'h'}, - {"interactive", 0, 0, 'i'}, - {"links", 1, 0, 'l'}, - {"print-dot", 0, 0, OPT_PRINT_DOT}, - {"print-topology", 0, 0, 'p'}, - {"reset", 0, 0, 'r'}, - {"verbose", 0, 0, 'v'}, -}; - -int parse_cmdline(int argc, char **argv) -{ - int opt; - - if (argc == 1) { - usage(argv[0], 0); - return 1; - } - - /* parse options */ - while ((opt = getopt_long(argc, argv, "d:e:f:hil:prv", opts, NULL)) != -1) { - switch (opt) { - case 'd': - media_opts.devname = optarg; - break; - - case 'e': - media_opts.entity = optarg; - break; - - case 'f': - media_opts.formats = optarg; - break; - - case 'h': - usage(argv[0], 1); - exit(0); - - case 'i': - media_opts.interactive = 1; - break; - - case 'l': - media_opts.links = optarg; - break; - - case 'p': - media_opts.print = 1; - media_opts.verbose = 1; - break; - - case 'r': - media_opts.reset = 1; - break; - - case 'v': - media_opts.verbose = 1; - break; - - case OPT_PRINT_DOT: - media_opts.print_dot = 1; - break; - - case OPT_GET_FORMAT: - media_opts.pad = optarg; - break; - - default: - printf("Invalid option -%c\n", opt); - printf("Run %s -h for help.\n", argv[0]); - return 1; - } - } - - return 0; -} - diff --git a/options.h b/options.h deleted file mode 100644 index 2467fbe..0000000 --- a/options.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Media controller test application - * - * Copyright (C) 2010 Ideas on board SPRL - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - */ - -#ifndef __OPTIONS_H -#define __OPTIONS_H - -struct media_options -{ - const char *devname; - unsigned int interactive:1, - print:1, - print_dot:1, - reset:1, - verbose:1; - const char *entity; - const char *formats; - const char *links; - const char *pad; -}; - -extern struct media_options media_opts; - -extern int parse_cmdline(int argc, char **argv); - -#endif /* __OPTIONS_H */ - diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..b112ba6 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,3 @@ +bin_PROGRAMS = media-ctl +media_ctl_SOURCES = main.c media.c media.h options.c options.h subdev.c subdev.h tools.h + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..aa4ced0 --- /dev/null +++ b/src/main.c @@ -0,0 +1,809 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "media.h" +#include "options.h" +#include "subdev.h" +#include "tools.h" + +/* ----------------------------------------------------------------------------- + * Printing + */ + +static struct { + const char *name; + enum v4l2_mbus_pixelcode code; +} mbus_formats[] = { + { "Y8", V4L2_MBUS_FMT_Y8_1X8}, + { "YUYV", V4L2_MBUS_FMT_YUYV8_1X16 }, + { "UYVY", V4L2_MBUS_FMT_UYVY8_1X16 }, + { "SGRBG10", V4L2_MBUS_FMT_SGRBG10_1X10 }, + { "SGRBG10_DPCM8", V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 }, +}; + +static const char *pixelcode_to_string(enum v4l2_mbus_pixelcode code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) { + if (mbus_formats[i].code == code) + return mbus_formats[i].name; + } + + return "unknown"; +} + +static enum v4l2_mbus_pixelcode string_to_pixelcode(const char *string, + unsigned int length) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) { + if (strncmp(mbus_formats[i].name, string, length) == 0) + break; + } + + if (i == ARRAY_SIZE(mbus_formats)) + return (enum v4l2_mbus_pixelcode)-1; + + return mbus_formats[i].code; +} + +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("[%s %ux%u", pixelcode_to_string(format.code), + format.width, format.height); + + ret = v4l2_subdev_get_crop(entity, &rect, pad, which); + if (ret == 0) + printf(" (%u,%u)/%ux%u", rect.left, rect.top, + rect.width, rect.height); + printf("]"); +} + +static const char *media_entity_type_to_string(unsigned type) +{ + static const struct { + __u32 type; + const char *name; + } types[] = { + { MEDIA_ENTITY_TYPE_NODE, "Node" }, + { MEDIA_ENTITY_TYPE_SUBDEV, "V4L2 subdev" }, + }; + + unsigned int i; + + type &= MEDIA_ENTITY_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_ENTITY_SUBTYPE_MASK; + + switch (type & MEDIA_ENTITY_TYPE_MASK) { + case MEDIA_ENTITY_TYPE_NODE: + if (subtype >= ARRAY_SIZE(node_types)) + subtype = 0; + return node_types[subtype]; + + case MEDIA_ENTITY_TYPE_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_FLAG_INPUT, "Input" }, + { MEDIA_PAD_FLAG_OUTPUT, "Output" }, + }; + + 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_dot(struct media_device *media) +{ + unsigned int i, j; + + printf("digraph board {\n"); + printf("\trankdir=TB\n"); + + for (i = 0; i < media->entities_count; ++i) { + struct media_entity *entity = &media->entities[i]; + unsigned int npads; + + switch (media_entity_type(entity)) { + case MEDIA_ENTITY_TYPE_NODE: + printf("\tn%08x [label=\"%s\\n%s\", shape=box, style=filled, " + "fillcolor=yellow]\n", + entity->info.id, entity->info.name, entity->devname); + break; + + case MEDIA_ENTITY_TYPE_SUBDEV: + printf("\tn%08x [label=\"{{", entity->info.id); + + for (j = 0, npads = 0; j < entity->info.pads; ++j) { + if (!(entity->pads[j].flags & MEDIA_PAD_FLAG_INPUT)) + continue; + + printf("%s %u", npads ? " | " : "", j, j); + npads++; + } + + printf("} | %s", entity->info.name); + if (entity->devname) + printf("\\n%s", entity->devname); + printf(" | {"); + + for (j = 0, npads = 0; j < entity->info.pads; ++j) { + if (!(entity->pads[j].flags & MEDIA_PAD_FLAG_OUTPUT)) + continue; + + printf("%s %u", npads ? " | " : "", j, j); + npads++; + } + + printf("}}\", shape=Mrecord, style=filled, fillcolor=green]\n"); + break; + + default: + continue; + } + + for (j = 0; j < entity->num_links; j++) { + struct media_link *link = &entity->links[j]; + + if (link->source->entity != entity) + continue; + + printf("\tn%08x", link->source->entity->info.id); + if (media_entity_type(link->source->entity) == MEDIA_ENTITY_TYPE_SUBDEV) + printf(":port%u", link->source->index); + printf(" -> "); + printf("n%08x", link->sink->entity->info.id); + if (media_entity_type(link->sink->entity) == MEDIA_ENTITY_TYPE_SUBDEV) + printf(":port%u", link->sink->index); + + if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE) + printf(" [style=bold]"); + else if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE)) + printf(" [style=dashed]"); + printf("\n"); + } + } + + printf("}\n"); +} + +static void media_print_topology_text(struct media_device *media) +{ + unsigned int i, j, k; + unsigned int padding; + + printf("Device topology\n"); + + for (i = 0; i < media->entities_count; ++i) { + struct media_entity *entity = &media->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->info.links, entity->info.links > 1 ? "s" : ""); + printf("%*ctype %s subtype %s\n", padding, ' ', + media_entity_type_to_string(entity->info.type), + media_entity_subtype_to_string(entity->info.type)); + 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 ", j, media_pad_type_to_string(pad->flags)); + + if (media_entity_type(entity) == MEDIA_ENTITY_TYPE_SUBDEV) + v4l2_subdev_print_format(entity, j, V4L2_SUBDEV_FORMAT_ACTIVE); + + printf("\n"); + + for (k = 0; k < entity->num_links; k++) { + struct media_link *link = &entity->links[k]; + + if (link->source->entity != entity || + link->source->index != j) + continue; + + printf("\t\t-> '%s':pad%u [", + link->sink->entity->info.name, link->sink->index); + + if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE) + printf("IMMUTABLE,"); + if (link->flags & MEDIA_LINK_FLAG_ACTIVE) + printf("ACTIVE"); + + printf("]\n"); + } + } + printf("\n"); + } +} + +void media_print_topology(struct media_device *media, int dot) +{ + if (dot) + media_print_topology_dot(media); + else + media_print_topology_text(media); +} + +/* ----------------------------------------------------------------------------- + * Links setup + */ + +static struct media_pad *parse_pad(struct media_device *media, const char *p, char **endp) +{ + unsigned int entity_id, pad; + struct media_entity *entity; + char *end; + + for (; isspace(*p); ++p); + + if (*p == '"') { + for (end = (char *)p + 1; *end && *end != '"'; ++end); + if (*end != '"') + return NULL; + + entity = media_get_entity_by_name(media, p + 1, end - p - 1); + if (entity == NULL) + return NULL; + + ++end; + } else { + entity_id = strtoul(p, &end, 10); + entity = media_get_entity_by_id(media, entity_id); + if (entity == NULL) + return NULL; + } + for (; isspace(*end); ++end); + + if (*end != ':') + return NULL; + for (p = end + 1; isspace(*p); ++p); + + pad = strtoul(p, &end, 10); + for (p = end; isspace(*p); ++p); + + if (pad >= entity->info.pads) + return NULL; + + for (p = end; isspace(*p); ++p); + if (endp) + *endp = (char *)p; + + return &entity->pads[pad]; +} + +static struct media_link *parse_link(struct media_device *media, const char *p, char **endp) +{ + struct media_link *link; + struct media_pad *source; + struct media_pad *sink; + unsigned int i; + char *end; + + source = parse_pad(media, p, &end); + if (source == NULL) + return NULL; + + if (end[0] != '-' || end[1] != '>') + return NULL; + p = end + 2; + + sink = parse_pad(media, p, &end); + if (sink == NULL) + return NULL; + + *endp = end; + + for (i = 0; i < source->entity->num_links; i++) { + link = &source->entity->links[i]; + + if (link->source == source && link->sink == sink) + return link; + } + + return NULL; +} + +static int setup_link(struct media_device *media, const char *p, char **endp) +{ + struct media_link *link; + __u32 flags; + char *end; + + link = parse_link(media, p, &end); + if (link == NULL) { + printf("Unable to parse link\n"); + return -EINVAL; + } + + p = end; + if (*p++ != '[') { + printf("Unable to parse link flags\n"); + return -EINVAL; + } + + flags = strtoul(p, &end, 10); + for (p = end; isspace(*p); p++); + if (*p++ != ']') { + printf("Unable to parse link flags\n"); + return -EINVAL; + } + + for (; isspace(*p); p++); + *endp = (char *)p; + + printf("Setting up link %u:%u -> %u:%u [%u]\n", + link->source->entity->info.id, link->source->index, + link->sink->entity->info.id, link->sink->index, + flags); + + return media_setup_link(media, link->source, link->sink, flags); +} + +static int setup_links(struct media_device *media, const char *p) +{ + char *end; + int ret; + + do { + ret = setup_link(media, p, &end); + if (ret < 0) + return ret; + + p = end + 1; + } while (*end == ','); + + return *end ? -EINVAL : 0; +} + +/* ----------------------------------------------------------------------------- + * Formats setup + */ + +static int parse_format(struct v4l2_mbus_framefmt *format, const char *p, char **endp) +{ + enum v4l2_mbus_pixelcode code; + unsigned int width, height; + char *end; + + for (; isspace(*p); ++p); + for (end = (char *)p; !isspace(*end) && *end != '\0'; ++end); + + code = string_to_pixelcode(p, end - p); + if (code == (enum v4l2_mbus_pixelcode)-1) + return -EINVAL; + + for (p = end; isspace(*p); ++p); + width = strtoul(p, &end, 10); + if (*end != 'x') + return -EINVAL; + + p = end + 1; + height = strtoul(p, &end, 10); + *endp = end; + + memset(format, 0, sizeof(*format)); + format->width = width; + format->height = height; + format->code = code; + + return 0; +} + +static int parse_crop(struct v4l2_rect *crop, const char *p, char **endp) +{ + char *end; + + if (*p++ != '(') + return -EINVAL; + + crop->left = strtoul(p, &end, 10); + if (*end != ',') + return -EINVAL; + + p = end + 1; + crop->top = strtoul(p, &end, 10); + if (*end++ != ')') + return -EINVAL; + if (*end != '/') + return -EINVAL; + + p = end + 1; + crop->width = strtoul(p, &end, 10); + if (*end != 'x') + return -EINVAL; + + p = end + 1; + crop->height = strtoul(p, &end, 10); + *endp = end; + + return 0; +} + +static int parse_frame_interval(struct v4l2_fract *interval, const char *p, char **endp) +{ + char *end; + + for (; isspace(*p); ++p); + + interval->numerator = strtoul(p, &end, 10); + + for (p = end; isspace(*p); ++p); + if (*p++ != '/') + return -EINVAL; + + for (; isspace(*p); ++p); + interval->denominator = strtoul(p, &end, 10); + + *endp = end; + return 0; +} + +static struct media_pad *parse_pad_format(struct media_device *media, + struct v4l2_mbus_framefmt *format, struct v4l2_rect *crop, + struct v4l2_fract *interval, const char *p, char **endp) +{ + struct media_pad *pad; + char *end; + int ret; + + for (; isspace(*p); ++p); + + pad = parse_pad(media, p, &end); + if (pad == NULL) + return NULL; + + for (p = end; isspace(*p); ++p); + if (*p++ != '[') + return NULL; + + for (; isspace(*p); ++p); + + if (isalnum(*p)) { + ret = parse_format(format, p, &end); + if (ret < 0) + return NULL; + + for (p = end; isspace(*p); p++); + } + + if (*p == '(') { + ret = parse_crop(crop, p, &end); + if (ret < 0) + return NULL; + + for (p = end; isspace(*p); p++); + } + + if (*p == '@') { + ret = parse_frame_interval(interval, ++p, &end); + if (ret < 0) + return NULL; + + for (p = end; isspace(*p); p++); + } + + if (*p != ']') + return NULL; + + *endp = (char *)p + 1; + return pad; +} + +static int set_format(struct media_pad *pad, struct v4l2_mbus_framefmt *format) +{ + int ret; + + if (format->width == 0 || format->height == 0) + return 0; + + printf("Setting up format %s %ux%u on pad %s/%u\n", + pixelcode_to_string(format->code), format->width, format->height, + pad->entity->info.name, pad->index); + + ret = v4l2_subdev_set_format(pad->entity, format, pad->index, + V4L2_SUBDEV_FORMAT_ACTIVE); + if (ret < 0) { + printf("Unable to set format: %s (%d)\n", strerror(-ret), ret); + return ret; + } + + printf("Format set: %s %ux%u\n", + pixelcode_to_string(format->code), format->width, format->height); + + return 0; +} + +static int set_crop(struct media_pad *pad, struct v4l2_rect *crop) +{ + int ret; + + if (crop->left == -1 || crop->top == -1) + return 0; + + printf("Setting up crop rectangle (%u,%u)/%ux%u on pad %s/%u\n", + crop->left, crop->top, crop->width, crop->height, + pad->entity->info.name, pad->index); + + ret = v4l2_subdev_set_crop(pad->entity, crop, pad->index, + V4L2_SUBDEV_FORMAT_ACTIVE); + if (ret < 0) { + printf("Unable to set crop rectangle: %s (%d)\n", strerror(-ret), ret); + return ret; + } + + printf("Crop rectangle set: (%u,%u)/%ux%u\n", + crop->left, crop->top, crop->width, crop->height); + + return 0; +} + +static int set_frame_interval(struct media_entity *entity, struct v4l2_fract *interval) +{ + int ret; + + if (interval->numerator == 0) + return 0; + + printf("Setting up frame interval %u/%u on entity %s\n", + interval->numerator, interval->denominator, entity->info.name); + + ret = v4l2_subdev_set_frame_interval(entity, interval); + if (ret < 0) { + printf("Unable to set frame interval: %s (%d)", strerror(-ret), ret); + return ret; + } + + printf("Frame interval set: %u/%u\n", + interval->numerator, interval->denominator); + + return 0; +} + + +static int setup_format(struct media_device *media, const char *p, char **endp) +{ + struct v4l2_mbus_framefmt format = { 0, 0, 0 }; + struct media_pad *pad; + struct v4l2_rect crop = { -1, -1, -1, -1 }; + struct v4l2_fract interval = { 0, 0 }; + unsigned int i; + char *end; + int ret; + + pad = parse_pad_format(media, &format, &crop, &interval, p, &end); + if (pad == NULL) { + printf("Unable to parse format\n"); + return -EINVAL; + } + + if (pad->flags & MEDIA_PAD_FLAG_OUTPUT) { + ret = set_crop(pad, &crop); + if (ret < 0) + return ret; + } + + ret = set_format(pad, &format); + if (ret < 0) + return ret; + + if (pad->flags & MEDIA_PAD_FLAG_INPUT) { + ret = set_crop(pad, &crop); + if (ret < 0) + return ret; + } + + ret = set_frame_interval(pad->entity, &interval); + if (ret < 0) + return ret; + + + /* If the pad is an output pad, automatically set the same format on + * the remote subdev input pads, if any. + */ + if (pad->flags & MEDIA_PAD_FLAG_OUTPUT) { + for (i = 0; i < pad->entity->num_links; ++i) { + struct media_link *link = &pad->entity->links[i]; + struct v4l2_mbus_framefmt remote_format; + + if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE)) + continue; + + if (link->source == pad && + link->sink->entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV) { + remote_format = format; + set_format(link->sink, &remote_format); + } + } + } + + *endp = end; + return 0; +} + +static int setup_formats(struct media_device *media, const char *p) +{ + char *end; + int ret; + + do { + ret = setup_format(media, p, &end); + if (ret < 0) + return ret; + + p = end + 1; + } while (*end == ','); + + return *end ? -EINVAL : 0; +} + +int main(int argc, char **argv) +{ + struct media_device *media; + int ret = -1; + + if (parse_cmdline(argc, argv)) + return EXIT_FAILURE; + + /* Open the media device and enumerate entities, pads and links. */ + media = media_open(media_opts.devname, media_opts.verbose); + if (media == NULL) + goto out; + + if (media_opts.entity) { + struct media_entity *entity; + + entity = media_get_entity_by_name(media, media_opts.entity, + strlen(media_opts.entity)); + if (entity == NULL) { + printf("Entity '%s' not found\n", media_opts.entity); + goto out; + } + + printf("%s\n", entity->devname); + } + + if (media_opts.pad) { + struct media_pad *pad; + + pad = parse_pad(media, media_opts.pad, NULL); + if (pad == NULL) { + printf("Pad '%s' not found\n", media_opts.pad); + goto out; + } + + v4l2_subdev_print_format(pad->entity, pad->index, + V4L2_SUBDEV_FORMAT_ACTIVE); + printf("\n"); + } + + if (media_opts.print || media_opts.print_dot) { + media_print_topology(media, media_opts.print_dot); + printf("\n"); + } + + if (media_opts.reset) { + printf("Resetting all links to inactive\n"); + media_reset_links(media); + } + + if (media_opts.links) + setup_links(media, media_opts.links); + + if (media_opts.formats) + setup_formats(media, media_opts.formats); + + if (media_opts.interactive) { + while (1) { + char buffer[32]; + char *end; + + printf("Enter a link to modify or enter to stop\n"); + if (fgets(buffer, sizeof buffer, stdin) == NULL) + break; + + if (buffer[0] == '\n') + break; + + setup_link(media, buffer, &end); + } + } + + ret = 0; + +out: + if (media) + media_close(media); + + return ret ? EXIT_FAILURE : EXIT_SUCCESS; +} + diff --git a/src/media.c b/src/media.c new file mode 100644 index 0000000..456b882 --- /dev/null +++ b/src/media.c @@ -0,0 +1,388 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "media.h" +#include "tools.h" + +struct media_pad *media_entity_remote_source(struct media_pad *pad) +{ + unsigned int i; + + if (!(pad->flags & MEDIA_PAD_FLAG_INPUT)) + return NULL; + + for (i = 0; i < pad->entity->num_links; ++i) { + struct media_link *link = &pad->entity->links[i]; + + if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE)) + continue; + + if (link->sink == pad) + return link->source; + } + + return NULL; +} + +struct media_entity *media_get_entity_by_name(struct media_device *media, + const char *name, size_t length) +{ + unsigned int i; + + for (i = 0; i < media->entities_count; ++i) { + struct media_entity *entity = &media->entities[i]; + + if (strncmp(entity->info.name, name, length) == 0) + return entity; + } + + return NULL; +} + +struct media_entity *media_get_entity_by_id(struct media_device *media, + __u32 id) +{ + unsigned int i; + + for (i = 0; i < media->entities_count; ++i) { + struct media_entity *entity = &media->entities[i]; + + if (entity->info.id == id) + return entity; + } + + return NULL; +} + +int media_setup_link(struct media_device *media, + struct media_pad *source, + struct media_pad *sink, + __u32 flags) +{ + struct media_link *link; + struct media_link_desc ulink; + unsigned int i; + int ret; + + for (i = 0; i < source->entity->num_links; i++) { + link = &source->entity->links[i]; + + if (link->source->entity == source->entity && + link->source->index == source->index && + link->sink->entity == sink->entity && + link->sink->index == sink->index) + break; + } + + if (i == source->entity->num_links) { + printf("%s: Link not found\n", __func__); + return -EINVAL; + } + + /* source pad */ + ulink.source.entity = source->entity->info.id; + ulink.source.index = source->index; + ulink.source.flags = MEDIA_PAD_FLAG_OUTPUT; + + /* sink pad */ + ulink.sink.entity = sink->entity->info.id; + ulink.sink.index = sink->index; + ulink.sink.flags = MEDIA_PAD_FLAG_INPUT; + + ulink.flags = flags | (link->flags & MEDIA_LINK_FLAG_IMMUTABLE); + + ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink); + if (ret < 0) { + printf("%s: Unable to setup link (%s)\n", __func__, + strerror(errno)); + return ret; + } + + link->flags = flags; + return 0; +} + +int media_reset_links(struct media_device *media) +{ + unsigned int i, j; + int ret; + + for (i = 0; i < media->entities_count; ++i) { + struct media_entity *entity = &media->entities[i]; + + for (j = 0; j < entity->num_links; j++) { + struct media_link *link = &entity->links[j]; + + if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE || + link->source->entity != entity) + continue; + + ret = media_setup_link(media, link->source, link->sink, + link->flags & ~MEDIA_LINK_FLAG_ACTIVE); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static struct media_link *media_entity_add_link(struct media_entity *entity) +{ + if (entity->num_links >= entity->max_links) { + struct media_link *links = entity->links; + unsigned int max_links = entity->max_links * 2; + unsigned int i; + + links = realloc(links, max_links * sizeof *links); + if (links == NULL) + return NULL; + + for (i = 0; i < entity->num_links; ++i) + links[i].twin->twin = &links[i]; + + entity->max_links = max_links; + entity->links = links; + } + + return &entity->links[entity->num_links++]; +} + +static int media_enum_links(struct media_device *media) +{ + __u32 id; + int ret = 0; + + for (id = 1; id <= media->entities_count; id++) { + struct media_entity *entity = &media->entities[id - 1]; + struct media_links_enum links; + unsigned int i; + + links.entity = entity->info.id; + links.pads = malloc(entity->info.pads * sizeof(struct media_pad_desc)); + links.links = malloc(entity->info.links * sizeof(struct media_link_desc)); + + if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) { + printf("%s: Unable to enumerate pads and links (%s).\n", + __func__, strerror(errno)); + free(links.pads); + free(links.links); + return -errno; + } + + for (i = 0; i < entity->info.pads; ++i) { + entity->pads[i].entity = entity; + entity->pads[i].index = links.pads[i].index; + entity->pads[i].flags = links.pads[i].flags; + } + + for (i = 0; i < entity->info.links; ++i) { + struct media_link_desc *link = &links.links[i]; + struct media_link *fwdlink; + struct media_link *backlink; + struct media_entity *source; + struct media_entity *sink; + + source = media_get_entity_by_id(media, link->source.entity); + sink = media_get_entity_by_id(media, link->sink.entity); + + if (source == NULL || sink == NULL) { + printf("WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n", + id, i, link->source.entity, link->source.index, + link->sink.entity, link->sink.index); + ret = -EINVAL; + } else { + fwdlink = media_entity_add_link(source); + fwdlink->source = &source->pads[link->source.index]; + fwdlink->sink = &sink->pads[link->sink.index]; + fwdlink->flags = links.links[i].flags; + + backlink = media_entity_add_link(sink); + backlink->source = &source->pads[link->source.index]; + backlink->sink = &sink->pads[link->sink.index]; + backlink->flags = links.links[i].flags; + + fwdlink->twin = backlink; + backlink->twin = fwdlink; + } + } + + free(links.pads); + free(links.links); + } + + return ret; +} + +static int media_enum_entities(struct media_device *media) +{ + struct media_entity *entity; + struct stat devstat; + unsigned int size; + char devname[32]; + char sysname[32]; + char target[1024]; + char *p; + __u32 id; + int ret; + + for (id = 0; ; id = entity->info.id) { + size = (media->entities_count + 1) * sizeof(*media->entities); + media->entities = realloc(media->entities, size); + + entity = &media->entities[media->entities_count]; + memset(entity, 0, sizeof(*entity)); + entity->fd = -1; + entity->info.id = id | MEDIA_ENTITY_ID_FLAG_NEXT; + + ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info); + if (ret < 0) { + if (errno == EINVAL) + break; + return -errno; + } + + /* Number of links (for outbound links) plus number of pads (for + * inbound links) is a good safe initial estimate of the total + * number of links. + */ + entity->max_links = entity->info.pads + entity->info.links; + + entity->pads = malloc(entity->info.pads * sizeof(*entity->pads)); + entity->links = malloc(entity->max_links * sizeof(*entity->links)); + if (entity->pads == NULL || entity->links == NULL) + return -ENOMEM; + + media->entities_count++; + + /* Find the corresponding device name. */ + if (media_entity_type(entity) != MEDIA_ENTITY_TYPE_NODE && + media_entity_type(entity) != MEDIA_ENTITY_TYPE_SUBDEV) + continue; + + sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major, + entity->info.v4l.minor); + ret = readlink(sysname, target, sizeof(target)); + if (ret < 0) + continue; + + target[ret] = '\0'; + p = strrchr(target, '/'); + if (p == NULL) + continue; + + sprintf(devname, "/dev/%s", p + 1); + ret = stat(devname, &devstat); + if (ret < 0) + continue; + + /* Sanity check: udev might have reordered the device nodes. + * Make sure the major/minor match. We should really use + * libudev. + */ + if (major(devstat.st_rdev) == entity->info.v4l.major && + minor(devstat.st_rdev) == entity->info.v4l.minor) + strcpy(entity->devname, devname); + } + + return 0; +} + +struct media_device *media_open(const char *name, int verbose) +{ + struct media_device *media; + int ret; + + media = malloc(sizeof(*media)); + if (media == NULL) { + printf("%s: unable to allocate memory\n", __func__); + return NULL; + } + memset(media, 0, sizeof(*media)); + + if (verbose) + printf("Opening media device %s\n", name); + media->fd = open(name, O_RDWR); + if (media->fd < 0) { + media_close(media); + printf("%s: Can't open media device %s\n", __func__, name); + return NULL; + } + + if (verbose) + printf("Enumerating entities\n"); + + ret = media_enum_entities(media); + if (ret < 0) { + printf("%s: Unable to enumerate entities for device %s (%s)\n", + __func__, name, strerror(-ret)); + media_close(media); + return NULL; + } + + if (verbose) { + printf("Found %u entities\n", media->entities_count); + printf("Enumerating pads and links\n"); + } + + ret = media_enum_links(media); + if (ret < 0) { + printf("%s: Unable to enumerate pads and linksfor device %s\n", + __func__, name); + media_close(media); + return NULL; + } + + return media; +} + +void media_close(struct media_device *media) +{ + unsigned int i; + + if (media->fd != -1) + close(media->fd); + + for (i = 0; i < media->entities_count; ++i) { + struct media_entity *entity = &media->entities[i]; + + free(entity->pads); + free(entity->links); + if (entity->fd != -1) + close(entity->fd); + } + + free(media->entities); + free(media); +} + diff --git a/src/media.h b/src/media.h new file mode 100644 index 0000000..80cf976 --- /dev/null +++ b/src/media.h @@ -0,0 +1,161 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#ifndef __MEDIA_H__ +#define __MEDIA_H__ + +#include + +struct media_link { + struct media_pad *source; + struct media_pad *sink; + struct media_link *twin; + __u32 flags; + __u32 padding[3]; +}; + +struct media_pad { + struct media_entity *entity; + __u32 index; + __u32 flags; + __u32 padding[3]; +}; + +struct media_entity { + struct media_entity_desc info; + struct media_pad *pads; + struct media_link *links; + unsigned int max_links; + unsigned int num_links; + + char devname[32]; + int fd; + __u32 padding[6]; +}; + +struct media_device { + int fd; + struct media_entity *entities; + unsigned int entities_count; + __u32 padding[6]; +}; + +/** + * @brief Open a media device. + * @param name - name (including path) of the device node. + * @param verbose - whether to print verbose information on the standard output. + * + * Open the media device referenced by @a name and enumerate entities, pads and + * links. + * + * @return A pointer to a newly allocated media_device structure instance on + * success and NULL on failure. The returned pointer must be freed with + * media_close when the device isn't needed anymore. + */ +struct media_device *media_open(const char *name, int verbose); + +/** + * @brief Close a media device. + * @param media - device instance. + * + * Close the @a media device instance and free allocated resources. Access to the + * device instance is forbidden after this function returns. + */ +void media_close(struct media_device *media); + +/** + * @brief Locate the pad at the other end of a link. + * @param pad - sink pad at one end of the link. + * + * Locate the source pad connected to @a pad through an enabled link. As only one + * link connected to a sink pad can be enabled at a time, the connected source + * pad is guaranteed to be unique. + * + * @return A pointer to the connected source pad, or NULL if all links connected + * to @a pad are disabled. Return NULL also if @a pad is not a sink pad. + */ +struct media_pad *media_entity_remote_source(struct media_pad *pad); + +/** + * @brief Get the type of an entity. + * @param entity - the entity. + * + * @return The type of @a entity. + */ +static inline unsigned int media_entity_type(struct media_entity *entity) +{ + return entity->info.type & MEDIA_ENTITY_TYPE_MASK; +} + +/** + * @brief Find an entity by its name. + * @param media - media device. + * @param name - entity name. + * @param length - size of @a name. + * + * Search for an entity with a name equal to @a name. + * + * @return A pointer to the entity if found, or NULL otherwise. + */ +struct media_entity *media_get_entity_by_name(struct media_device *media, + const char *name, size_t length); + +/** + * @brief Find an entity by its ID. + * @param media - media device. + * @param id - entity ID. + * + * Search for an entity with an ID equal to @a id. + * + * @return A pointer to the entity if found, or NULL otherwise. + */ +struct media_entity *media_get_entity_by_id(struct media_device *media, + __u32 id); + +/** + * @brief Configure a link. + * @param media - media device. + * @param source - source pad at the link origin. + * @param sink - sink pad at the link target. + * @param flags - configuration flags. + * + * Locate the link between @a source and @a sink, and configure it by applying + * the new @a flags. + * + * Only the MEDIA_LINK_FLAG_ENABLED flag is writable. + * + * @return 0 on success, or a negative error code on failure. + */ +int media_setup_link(struct media_device *media, + struct media_pad *source, struct media_pad *sink, + __u32 flags); + +/** + * @brief Reset all links to the disabled state. + * @param media - media device. + * + * Disable all links in the media device. This function is usually used after + * opening a media device to reset all links to a known state. + * + * @return 0 on success, or a negative error code on failure. + */ +int media_reset_links(struct media_device *media); + +#endif + diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..186425e --- /dev/null +++ b/src/options.c @@ -0,0 +1,155 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#include +#include +#include +#include + +#include "options.h" + +#define MEDIA_DEVNAME_DEFAULT "/dev/media0" + +struct media_options media_opts = { + .devname = MEDIA_DEVNAME_DEFAULT, +}; + +static void usage(const char *argv0, int verbose) +{ + printf("%s [options] device\n", argv0); + printf("-d, --device dev Media device name (default: %s)\n", MEDIA_DEVNAME_DEFAULT); + printf("-e, --entity name Print the device name associated with the given entity\n"); + printf("-f, --set-format Comma-separated list of formats to setup\n"); + printf(" --get-format pad Print the active format on a given pad\n"); + printf("-h, --help Show verbose help and exit\n"); + printf("-i, --interactive Modify links interactively\n"); + printf("-l, --links Comma-separated list of links descriptors to setup\n"); + printf("-p, --print-topology Print the device topology (implies -v)\n"); + printf(" --print-dot Print the device topology as a dot graph (implies -v)\n"); + printf("-r, --reset Reset all links to inactive\n"); + printf("-v, --verbose Be verbose\n"); + + if (!verbose) + return; + + printf("\n"); + printf("Links and formats are defined as\n"); + printf("\tlink = pad, '->', pad, '[', flags, ']' ;\n"); + printf("\tformat = pad, '[', fcc, ' ', size, [ ' ', crop ], [ ' ', '@', frame interval ], ']' ;\n"); + printf("\tpad = entity, ':', pad number ;\n"); + printf("\tentity = entity number | ( '\"', entity name, '\"' ) ;\n"); + printf("\tsize = width, 'x', height ;\n"); + printf("\tcrop = '(', left, ',', top, ')', '/', size ;\n"); + printf("\tframe interval = numerator, '/', denominator ;\n"); + printf("where the fields are\n"); + printf("\tentity number Entity numeric identifier\n"); + printf("\tentity name Entity name (string) \n"); + printf("\tpad number Pad numeric identifier\n"); + printf("\tflags Link flags (0: inactive, 1: active)\n"); + printf("\tfcc Format FourCC\n"); + printf("\twidth Image width in pixels\n"); + printf("\theight Image height in pixels\n"); + printf("\tnumerator Frame interval numerator\n"); + printf("\tdenominator Frame interval denominator\n"); +} + +#define OPT_PRINT_DOT 256 +#define OPT_GET_FORMAT 257 + +static struct option opts[] = { + {"device", 1, 0, 'd'}, + {"entity", 1, 0, 'e'}, + {"set-format", 1, 0, 'f'}, + {"get-format", 1, 0, OPT_GET_FORMAT}, + {"help", 0, 0, 'h'}, + {"interactive", 0, 0, 'i'}, + {"links", 1, 0, 'l'}, + {"print-dot", 0, 0, OPT_PRINT_DOT}, + {"print-topology", 0, 0, 'p'}, + {"reset", 0, 0, 'r'}, + {"verbose", 0, 0, 'v'}, +}; + +int parse_cmdline(int argc, char **argv) +{ + int opt; + + if (argc == 1) { + usage(argv[0], 0); + return 1; + } + + /* parse options */ + while ((opt = getopt_long(argc, argv, "d:e:f:hil:prv", opts, NULL)) != -1) { + switch (opt) { + case 'd': + media_opts.devname = optarg; + break; + + case 'e': + media_opts.entity = optarg; + break; + + case 'f': + media_opts.formats = optarg; + break; + + case 'h': + usage(argv[0], 1); + exit(0); + + case 'i': + media_opts.interactive = 1; + break; + + case 'l': + media_opts.links = optarg; + break; + + case 'p': + media_opts.print = 1; + media_opts.verbose = 1; + break; + + case 'r': + media_opts.reset = 1; + break; + + case 'v': + media_opts.verbose = 1; + break; + + case OPT_PRINT_DOT: + media_opts.print_dot = 1; + break; + + case OPT_GET_FORMAT: + media_opts.pad = optarg; + break; + + default: + printf("Invalid option -%c\n", opt); + printf("Run %s -h for help.\n", argv[0]); + return 1; + } + } + + return 0; +} + diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..2467fbe --- /dev/null +++ b/src/options.h @@ -0,0 +1,42 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#ifndef __OPTIONS_H +#define __OPTIONS_H + +struct media_options +{ + const char *devname; + unsigned int interactive:1, + print:1, + print_dot:1, + reset:1, + verbose:1; + const char *entity; + const char *formats; + const char *links; + const char *pad; +}; + +extern struct media_options media_opts; + +extern int parse_cmdline(int argc, char **argv); + +#endif /* __OPTIONS_H */ + diff --git a/src/subdev.c b/src/subdev.c new file mode 100644 index 0000000..3960d05 --- /dev/null +++ b/src/subdev.c @@ -0,0 +1,187 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "media.h" +#include "subdev.h" +#include "tools.h" + +int v4l2_subdev_open(struct media_entity *entity) +{ + if (entity->fd != -1) + return 0; + + entity->fd = open(entity->devname, O_RDWR); + if (entity->fd == -1) { + printf("%s: Failed to open subdev device node %s\n", __func__, + entity->devname); + return -errno; + } + + return 0; +} + +void v4l2_subdev_close(struct media_entity *entity) +{ + close(entity->fd); +} + +int v4l2_subdev_get_format(struct media_entity *entity, + struct v4l2_mbus_framefmt *format, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_subdev_format fmt; + int ret; + + ret = v4l2_subdev_open(entity); + if (ret < 0) + return ret; + + memset(&fmt, 0, sizeof(fmt)); + fmt.pad = pad; + fmt.which = which; + + ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FMT, &fmt); + if (ret < 0) + return -errno; + + *format = fmt.format; + return 0; +} + +int v4l2_subdev_set_format(struct media_entity *entity, + struct v4l2_mbus_framefmt *format, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_subdev_format fmt; + int ret; + + ret = v4l2_subdev_open(entity); + if (ret < 0) + return ret; + + memset(&fmt, 0, sizeof(fmt)); + fmt.pad = pad; + fmt.which = which; + fmt.format = *format; + + ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FMT, &fmt); + if (ret < 0) + return -errno; + + *format = fmt.format; + return 0; +} + +int v4l2_subdev_get_crop(struct media_entity *entity, struct v4l2_rect *rect, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + struct v4l2_subdev_crop crop; + int ret; + + ret = v4l2_subdev_open(entity); + if (ret < 0) + return ret; + + memset(&crop, 0, sizeof(crop)); + crop.pad = pad; + crop.which = which; + + ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_CROP, &crop); + if (ret < 0) + return -errno; + + *rect = crop.rect; + return 0; +} + +int v4l2_subdev_set_crop(struct media_entity *entity, struct v4l2_rect *rect, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + struct v4l2_subdev_crop crop; + int ret; + + ret = v4l2_subdev_open(entity); + if (ret < 0) + return ret; + + memset(&crop, 0, sizeof(crop)); + crop.pad = pad; + crop.which = which; + crop.rect = *rect; + + ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_CROP, &crop); + if (ret < 0) + return -errno; + + *rect = crop.rect; + return 0; +} + +int v4l2_subdev_get_frame_interval(struct media_entity *entity, + struct v4l2_fract *interval) +{ + struct v4l2_subdev_frame_interval ival; + int ret; + + ret = v4l2_subdev_open(entity); + if (ret < 0) + return ret; + + memset(&ival, 0, sizeof(ival)); + + ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &ival); + if (ret < 0) + return -errno; + + *interval = ival.interval; + return 0; +} + +int v4l2_subdev_set_frame_interval(struct media_entity *entity, + struct v4l2_fract *interval) +{ + struct v4l2_subdev_frame_interval ival; + int ret; + + ret = v4l2_subdev_open(entity); + if (ret < 0) + return ret; + + memset(&ival, 0, sizeof(ival)); + ival.interval = *interval; + + ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &ival); + if (ret < 0) + return -errno; + + *interval = ival.interval; + return 0; +} diff --git a/src/subdev.h b/src/subdev.h new file mode 100644 index 0000000..b5772e0 --- /dev/null +++ b/src/subdev.h @@ -0,0 +1,162 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#ifndef __SUBDEV_H__ +#define __SUBDEV_H__ + +#include + +struct media_entity; + +/** + * @brief Open a sub-device. + * @param entity - sub-device media entity. + * + * Open the V4L2 subdev device node associated with @a entity. The file + * descriptor is stored in the media_entity structure. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_open(struct media_entity *entity); + +/** + * @brief Close a sub-device. + * @param entity - sub-device media entity. + * + * Close the V4L2 subdev device node associated with the @a entity and opened by + * a previous call to v4l2_subdev_open() (either explicit or implicit). + */ +void v4l2_subdev_close(struct media_entity *entity); + +/** + * @brief Retrieve the format on a pad. + * @param entity - subdev-device media entity. + * @param format - format to be filled. + * @param pad - pad number. + * @param which - identifier of the format to get. + * + * Retrieve the current format on the @a entity @a pad and store it in the + * @a format structure. + * + * @a which is set to V4L2_SUBDEV_FORMAT_TRY to retrieve the try format stored + * in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to retrieve the current + * active format. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_get_format(struct media_entity *entity, + struct v4l2_mbus_framefmt *format, unsigned int pad, + enum v4l2_subdev_format_whence which); + +/** + * @brief Set the format on a pad. + * @param entity - subdev-device media entity. + * @param format - format. + * @param pad - pad number. + * @param which - identifier of the format to set. + * + * Set the format on the @a entity @a pad to @a format. The driver is allowed to + * modify the requested format, in which case @a format is updated with the + * modifications. + * + * @a which is set to V4L2_SUBDEV_FORMAT_TRY to set the try format stored in the + * file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to configure the device with an + * active format. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_set_format(struct media_entity *entity, + struct v4l2_mbus_framefmt *format, unsigned int pad, + enum v4l2_subdev_format_whence which); + +/** + * @brief Retrieve the crop rectangle on a pad. + * @param entity - subdev-device media entity. + * @param rect - crop rectangle to be filled. + * @param pad - pad number. + * @param which - identifier of the format to get. + * + * Retrieve the current crop rectangleon the @a entity @a pad and store it in + * the @a rect structure. + * + * @a which is set to V4L2_SUBDEV_FORMAT_TRY to retrieve the try crop rectangle + * stored in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to retrieve the + * current active crop rectangle. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_get_crop(struct media_entity *entity, struct v4l2_rect *rect, + unsigned int pad, enum v4l2_subdev_format_whence which); + +/** + * @brief Set the crop rectangle on a pad. + * @param entity - subdev-device media entity. + * @param rect - crop rectangle. + * @param pad - pad number. + * @param which - identifier of the format to set. + * + * Set the crop rectangle on the @a entity @a pad to @a rect. The driver is + * allowed to modify the requested rectangle, in which case @a rect is updated + * with the modifications. + * + * @a which is set to V4L2_SUBDEV_FORMAT_TRY to set the try crop rectangle + * stored in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to configure the + * device with an active crop rectangle. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_set_crop(struct media_entity *entity, struct v4l2_rect *rect, + unsigned int pad, enum v4l2_subdev_format_whence which); + +/** + * @brief Retrieve the frame interval on a sub-device. + * @param entity - subdev-device media entity. + * @param interval - frame interval to be filled. + * + * Retrieve the current frame interval on subdev @a entity and store it in the + * @a interval structure. + * + * Frame interval retrieving is usually supported only on devices at the + * beginning of video pipelines, such as sensors. + * + * @return 0 on success, or a negative error code on failure. + */ + +int v4l2_subdev_get_frame_interval(struct media_entity *entity, + struct v4l2_fract *interval); + +/** + * @brief Set the frame interval on a sub-device. + * @param entity - subdev-device media entity. + * @param interval - frame interval. + * + * Set the frame interval on subdev @a entity to @a interval. The driver is + * allowed to modify the requested frame interval, in which case @a interval is + * updated with the modifications. + * + * Frame interval setting is usually supported only on devices at the beginning + * of video pipelines, such as sensors. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_set_frame_interval(struct media_entity *entity, + struct v4l2_fract *interval); + +#endif + diff --git a/src/tools.h b/src/tools.h new file mode 100644 index 0000000..0a0a948 --- /dev/null +++ b/src/tools.h @@ -0,0 +1,27 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __TOOLS_H__ +#define __TOOLS_H__ + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) + +#endif /* __TOOLS_H__ */ + diff --git a/subdev.c b/subdev.c deleted file mode 100644 index 3960d05..0000000 --- a/subdev.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Media controller test application - * - * Copyright (C) 2010 Ideas on board SPRL - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "media.h" -#include "subdev.h" -#include "tools.h" - -int v4l2_subdev_open(struct media_entity *entity) -{ - if (entity->fd != -1) - return 0; - - entity->fd = open(entity->devname, O_RDWR); - if (entity->fd == -1) { - printf("%s: Failed to open subdev device node %s\n", __func__, - entity->devname); - return -errno; - } - - return 0; -} - -void v4l2_subdev_close(struct media_entity *entity) -{ - close(entity->fd); -} - -int v4l2_subdev_get_format(struct media_entity *entity, - struct v4l2_mbus_framefmt *format, unsigned int pad, - enum v4l2_subdev_format_whence which) -{ - struct v4l2_subdev_format fmt; - int ret; - - ret = v4l2_subdev_open(entity); - if (ret < 0) - return ret; - - memset(&fmt, 0, sizeof(fmt)); - fmt.pad = pad; - fmt.which = which; - - ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FMT, &fmt); - if (ret < 0) - return -errno; - - *format = fmt.format; - return 0; -} - -int v4l2_subdev_set_format(struct media_entity *entity, - struct v4l2_mbus_framefmt *format, unsigned int pad, - enum v4l2_subdev_format_whence which) -{ - struct v4l2_subdev_format fmt; - int ret; - - ret = v4l2_subdev_open(entity); - if (ret < 0) - return ret; - - memset(&fmt, 0, sizeof(fmt)); - fmt.pad = pad; - fmt.which = which; - fmt.format = *format; - - ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FMT, &fmt); - if (ret < 0) - return -errno; - - *format = fmt.format; - return 0; -} - -int v4l2_subdev_get_crop(struct media_entity *entity, struct v4l2_rect *rect, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - struct v4l2_subdev_crop crop; - int ret; - - ret = v4l2_subdev_open(entity); - if (ret < 0) - return ret; - - memset(&crop, 0, sizeof(crop)); - crop.pad = pad; - crop.which = which; - - ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_CROP, &crop); - if (ret < 0) - return -errno; - - *rect = crop.rect; - return 0; -} - -int v4l2_subdev_set_crop(struct media_entity *entity, struct v4l2_rect *rect, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - struct v4l2_subdev_crop crop; - int ret; - - ret = v4l2_subdev_open(entity); - if (ret < 0) - return ret; - - memset(&crop, 0, sizeof(crop)); - crop.pad = pad; - crop.which = which; - crop.rect = *rect; - - ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_CROP, &crop); - if (ret < 0) - return -errno; - - *rect = crop.rect; - return 0; -} - -int v4l2_subdev_get_frame_interval(struct media_entity *entity, - struct v4l2_fract *interval) -{ - struct v4l2_subdev_frame_interval ival; - int ret; - - ret = v4l2_subdev_open(entity); - if (ret < 0) - return ret; - - memset(&ival, 0, sizeof(ival)); - - ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &ival); - if (ret < 0) - return -errno; - - *interval = ival.interval; - return 0; -} - -int v4l2_subdev_set_frame_interval(struct media_entity *entity, - struct v4l2_fract *interval) -{ - struct v4l2_subdev_frame_interval ival; - int ret; - - ret = v4l2_subdev_open(entity); - if (ret < 0) - return ret; - - memset(&ival, 0, sizeof(ival)); - ival.interval = *interval; - - ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &ival); - if (ret < 0) - return -errno; - - *interval = ival.interval; - return 0; -} diff --git a/subdev.h b/subdev.h deleted file mode 100644 index b5772e0..0000000 --- a/subdev.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Media controller test application - * - * Copyright (C) 2010 Ideas on board SPRL - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - */ - -#ifndef __SUBDEV_H__ -#define __SUBDEV_H__ - -#include - -struct media_entity; - -/** - * @brief Open a sub-device. - * @param entity - sub-device media entity. - * - * Open the V4L2 subdev device node associated with @a entity. The file - * descriptor is stored in the media_entity structure. - * - * @return 0 on success, or a negative error code on failure. - */ -int v4l2_subdev_open(struct media_entity *entity); - -/** - * @brief Close a sub-device. - * @param entity - sub-device media entity. - * - * Close the V4L2 subdev device node associated with the @a entity and opened by - * a previous call to v4l2_subdev_open() (either explicit or implicit). - */ -void v4l2_subdev_close(struct media_entity *entity); - -/** - * @brief Retrieve the format on a pad. - * @param entity - subdev-device media entity. - * @param format - format to be filled. - * @param pad - pad number. - * @param which - identifier of the format to get. - * - * Retrieve the current format on the @a entity @a pad and store it in the - * @a format structure. - * - * @a which is set to V4L2_SUBDEV_FORMAT_TRY to retrieve the try format stored - * in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to retrieve the current - * active format. - * - * @return 0 on success, or a negative error code on failure. - */ -int v4l2_subdev_get_format(struct media_entity *entity, - struct v4l2_mbus_framefmt *format, unsigned int pad, - enum v4l2_subdev_format_whence which); - -/** - * @brief Set the format on a pad. - * @param entity - subdev-device media entity. - * @param format - format. - * @param pad - pad number. - * @param which - identifier of the format to set. - * - * Set the format on the @a entity @a pad to @a format. The driver is allowed to - * modify the requested format, in which case @a format is updated with the - * modifications. - * - * @a which is set to V4L2_SUBDEV_FORMAT_TRY to set the try format stored in the - * file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to configure the device with an - * active format. - * - * @return 0 on success, or a negative error code on failure. - */ -int v4l2_subdev_set_format(struct media_entity *entity, - struct v4l2_mbus_framefmt *format, unsigned int pad, - enum v4l2_subdev_format_whence which); - -/** - * @brief Retrieve the crop rectangle on a pad. - * @param entity - subdev-device media entity. - * @param rect - crop rectangle to be filled. - * @param pad - pad number. - * @param which - identifier of the format to get. - * - * Retrieve the current crop rectangleon the @a entity @a pad and store it in - * the @a rect structure. - * - * @a which is set to V4L2_SUBDEV_FORMAT_TRY to retrieve the try crop rectangle - * stored in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to retrieve the - * current active crop rectangle. - * - * @return 0 on success, or a negative error code on failure. - */ -int v4l2_subdev_get_crop(struct media_entity *entity, struct v4l2_rect *rect, - unsigned int pad, enum v4l2_subdev_format_whence which); - -/** - * @brief Set the crop rectangle on a pad. - * @param entity - subdev-device media entity. - * @param rect - crop rectangle. - * @param pad - pad number. - * @param which - identifier of the format to set. - * - * Set the crop rectangle on the @a entity @a pad to @a rect. The driver is - * allowed to modify the requested rectangle, in which case @a rect is updated - * with the modifications. - * - * @a which is set to V4L2_SUBDEV_FORMAT_TRY to set the try crop rectangle - * stored in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to configure the - * device with an active crop rectangle. - * - * @return 0 on success, or a negative error code on failure. - */ -int v4l2_subdev_set_crop(struct media_entity *entity, struct v4l2_rect *rect, - unsigned int pad, enum v4l2_subdev_format_whence which); - -/** - * @brief Retrieve the frame interval on a sub-device. - * @param entity - subdev-device media entity. - * @param interval - frame interval to be filled. - * - * Retrieve the current frame interval on subdev @a entity and store it in the - * @a interval structure. - * - * Frame interval retrieving is usually supported only on devices at the - * beginning of video pipelines, such as sensors. - * - * @return 0 on success, or a negative error code on failure. - */ - -int v4l2_subdev_get_frame_interval(struct media_entity *entity, - struct v4l2_fract *interval); - -/** - * @brief Set the frame interval on a sub-device. - * @param entity - subdev-device media entity. - * @param interval - frame interval. - * - * Set the frame interval on subdev @a entity to @a interval. The driver is - * allowed to modify the requested frame interval, in which case @a interval is - * updated with the modifications. - * - * Frame interval setting is usually supported only on devices at the beginning - * of video pipelines, such as sensors. - * - * @return 0 on success, or a negative error code on failure. - */ -int v4l2_subdev_set_frame_interval(struct media_entity *entity, - struct v4l2_fract *interval); - -#endif - diff --git a/tools.h b/tools.h deleted file mode 100644 index 0a0a948..0000000 --- a/tools.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Media controller test application - * - * Copyright (C) 2010 Ideas on board SPRL - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef __TOOLS_H__ -#define __TOOLS_H__ - -#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) - -#endif /* __TOOLS_H__ */ - -- cgit v1.2.3