Discussion:
[PATCH/RFC v2 0/4] Libv4l: Add a plugin for the Exynos4 camera
Jacek Anaszewski
2014-10-17 14:54:38 UTC
Permalink
This is a second version of the patch series adding a plugin for the
Exynos4 camera.

================
Changes from v1:
================

- removed redundant mbus code negotiation
- split the parser, media device helpers and ioctl wrappers
to the separate modules
- added mechanism for querying extended controls
- applied various fixes and modifications

The plugin was tested on latest media-tree.git master with patches for
exynos4-is that fix failing open when a sensor sub-device is not
linked [1] [2] [3].

The plugin expects a configuration file:
/var/lib/libv4l/exynos4_capture_conf

Exemplary configuration file:

==========================================

link {
source_entity: s5p-mipi-csis.0
source_pad: 1
sink_entity: FIMC.0
sink_pad: 0
}

v4l2-controls {
Color Effects: fimc.0.capture
Saturation: S5C73M3
Image Stabilization: S5C73M3
White Balance, Auto & Preset: S5C73M3
Exposure, Metering Mode: S5C73M3
}

==========================================

With this settings the plugin can be tested on the exynos4412-trats2 board
using following gstreamer pipeline:

gst-launch-1.0 v4l2src device=/dev/video1 ! video/x-raw,width=960,height=720 ! fbdevsink

In order to avoid fbdevsink element failure the fix [4]
for exynos-drm driver is required.

Thanks,
Jacek Anaszewski

[1] https://patchwork.linuxtv.org/patch/26366/
[2] https://patchwork.linuxtv.org/patch/26367/
[3] https://patchwork.linuxtv.org/patch/26368/
[4] http://www.spinics.net/lists/dri-devel/msg66494.html

Jacek Anaszewski (4):
Add a media device configuration file parser.
Add media device related data structures and API.
Add wrappers for media device related ioctl calls.
Add a libv4l plugin for Exynos4 camera

configure.ac | 1 +
lib/Makefile.am | 5 +-
lib/include/libv4l2-mdev-ioctl.h | 45 +
lib/include/libv4l2-mdev.h | 195 +++++
lib/include/libv4l2-media-conf-parser.h | 148 ++++
lib/libv4l-exynos4-camera/Makefile.am | 8 +
lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c | 569 ++++++++++++
lib/libv4l2/Makefile.am | 5 +-
lib/libv4l2/libv4l2-mdev-ioctl.c | 329 +++++++
lib/libv4l2/libv4l2-mdev.c | 975 +++++++++++++++++++++
lib/libv4l2/libv4l2-media-conf-parser.c | 441 ++++++++++
11 files changed, 2718 insertions(+), 3 deletions(-)
create mode 100644 lib/include/libv4l2-mdev-ioctl.h
create mode 100644 lib/include/libv4l2-mdev.h
create mode 100644 lib/include/libv4l2-media-conf-parser.h
create mode 100644 lib/libv4l-exynos4-camera/Makefile.am
create mode 100644 lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
create mode 100644 lib/libv4l2/libv4l2-mdev-ioctl.c
create mode 100644 lib/libv4l2/libv4l2-mdev.c
create mode 100644 lib/libv4l2/libv4l2-media-conf-parser.c
--
1.7.9.5
Jacek Anaszewski
2014-10-17 14:54:39 UTC
Permalink
This patch adds a parser for a media device configuration
file. The parser expects the configuration file containing
links end v4l2-controls definitions as described in the
header file being added. The links describe connections
between media entities and v4l2-controls define the target
sub-devices for particular user controls related ioctl calls.

Signed-off-by: Jacek Anaszewski <***@samsung.com>
Acked-by: Kyungmin Park <***@samsung.com>
Cc: Mauro Carvalho Chehab <***@osg.samsung.com>
Cc: Hans Verkuil <***@cisco.com>
---
lib/include/libv4l2-media-conf-parser.h | 148 +++++++++++
lib/libv4l2/libv4l2-media-conf-parser.c | 441 +++++++++++++++++++++++++++++++
2 files changed, 589 insertions(+)
create mode 100644 lib/include/libv4l2-media-conf-parser.h
create mode 100644 lib/libv4l2/libv4l2-media-conf-parser.c

diff --git a/lib/include/libv4l2-media-conf-parser.h b/lib/include/libv4l2-media-conf-parser.h
new file mode 100644
index 0000000..b2dba3a
--- /dev/null
+++ b/lib/include/libv4l2-media-conf-parser.h
@@ -0,0 +1,148 @@
+/*
+ * Parser of media device configuration file.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jacek Anaszewski <***@samsung.com>
+ *
+ * The configuration file has to comply with following format:
+ *
+ * Link description entry format:
+ *
+ * link {
+ * <TAB>source_entity: <entity_name><LF>
+ * <TAB>source_pad: <pad_id><LF>
+ * <TAB>sink_entity: <entity_name><LF>
+ * <TAB>sink_pad: <pad_id><LF>
+ * }
+ *
+ * The V4L2 control group format:
+ *
+ * v4l2-controls {
+ * <TAB><control1_name>: <entity_name><LF>
+ * <TAB><control2_name>: <entity_name><LF>
+ * ...
+ * <TAB><controlN_name>: <entity_name><LF>
+ * }
+ *
+ * Example:
+ *
+ * link {
+ * source_entity: s5p-mipi-csis.0
+ * source_pad: 1
+ * sink_entity: FIMC.0
+ * sink_pad: 0
+ * }
+ *
+ * v4l2-controls {
+ * Color Effects: S5C73M3
+ * Saturation: S5C73M3
+ * }
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifndef __LIBV4L_MEDIA_CONF_PARSER_H
+#define __LIBV4L_MEDIA_CONF_PARSER_H
+
+#include <libv4l2.h>
+
+#ifdef DEBUG
+#define V4L2_MDCFG_PARSER_DBG(format, ARG...)\
+ printf("[%s:%d] [%s] " format " \n", __FILE__, __LINE__, __func__, ##ARG)
+#else
+#define V4L2_MDCFG_PARSER_DBG(format, ARG...)
+#endif
+
+#define V4L2_MDCFG_PARSER_ERR(format, ARG...)\
+ fprintf(stderr, "Libv4l device config parser: "format "\n", ##ARG)
+
+#define V4L2_MDCFG_PARSER_LOG(format, ARG...)\
+ fprintf(stdout, "Libv4l device config parser: "format "\n", ##ARG)
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+/*
+ * struct libv4l2_media_link_conf - media entity link configuration
+ * @source_entity: source entity of the link
+ * @source_pad: source pad id
+ * @sink_entity: sink entity of the link
+ * @sink_pad: sink pad id
+ * @next: pointer to the next data structure in the list
+ */
+struct libv4l2_media_link_conf {
+ char *source_entity;
+ int source_pad;
+ char *sink_entity;
+ int sink_pad;
+ struct libv4l2_media_link_conf *next;
+};
+
+/*
+ * struct libv4l2_media_ctrl_conf - user control to media entity configuration
+ * @control_name: user control name
+ * @entity_name: media entity name
+ * @entity: media entity matched by entity_name
+ * @cid: user control id
+ * @next: pointer to the next data structure in the list
+ */
+struct libv4l2_media_ctrl_conf {
+ char *control_name;
+ char *entity_name;
+ struct media_entity *entity;
+ int cid;
+ struct libv4l2_media_ctrl_conf *next;
+};
+
+/*
+ * struct libv4l2_media_device_conf - media device config
+ * @links: media entity link config
+ * @controls: user control to media entity config
+ */
+struct libv4l2_media_device_conf {
+ struct libv4l2_media_link_conf *links;
+ struct libv4l2_media_ctrl_conf *controls;
+};
+
+/*
+ * struct libv4l2_conf_parser_ctx - parser context
+ * @line_start_pos: start position of the current line in the file buffer
+ * @line_end: end position of the current line in the file buffer
+ * @buf_pos: file buffer position of the currently analyzed character
+ * @buf: config file buffer
+ * @buf_size: number of characters in the file buffer
+ */
+struct libv4l2_conf_parser_ctx {
+ int line_start_pos;
+ int line_end_pos;
+ int buf_pos;
+ char *buf;
+ int buf_size;
+};
+
+/*
+ * Read configuration file and initialize config argument with the parsed data.
+ * The config's links and controls fields must be released with use of
+ * libv4l2_media_conf_release_links and libv4l2_media_conf_release_controls
+ * functions respectively.
+ */
+int libv4l2_media_conf_read(char *fname,
+ struct libv4l2_media_device_conf *config);
+
+/* Release links configuration */
+void libv4l2_media_conf_release_links(struct libv4l2_media_link_conf *cfg);
+
+/* Release controls configuration */
+void libv4l2_media_conf_release_controls(struct libv4l2_media_ctrl_conf *cfg);
+
+#endif /* __LIBV4L_MEDIA_CONF_PARSER_H */
diff --git a/lib/libv4l2/libv4l2-media-conf-parser.c b/lib/libv4l2/libv4l2-media-conf-parser.c
new file mode 100644
index 0000000..03a0b43
--- /dev/null
+++ b/lib/libv4l2/libv4l2-media-conf-parser.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jacek Anaszewski <***@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <libv4l2-media-conf-parser.h>
+
+static int get_line(struct libv4l2_conf_parser_ctx *ctx)
+{
+ int i;
+
+ if (ctx->buf_pos == ctx->buf_size)
+ return -EINVAL;
+
+ ctx->line_start_pos = ctx->buf_pos;
+
+ for (i = ctx->buf_pos; i < ctx->buf_size; ++i) {
+ if (ctx->buf[i] == '\n') {
+ ctx->buf_pos = i + 1;
+ break;
+ }
+ }
+
+ ctx->line_end_pos = i - 1;
+
+ return 0;
+}
+
+static int parse_field_value(struct libv4l2_conf_parser_ctx *ctx,
+ const char *field_name, char **field_value)
+{
+ char *value;
+ int line_offset = ctx->line_start_pos, i;
+ char *line_buf = ctx->buf + line_offset;
+ int field_name_len = strlen(field_name);
+ int field_value_pos = field_name_len + 3;
+ int field_value_len = ctx->line_end_pos - line_offset
+ - field_value_pos + 1;
+
+ if (line_buf[0] != '\t') {
+ V4L2_MDCFG_PARSER_ERR("Lack of leading tab.");
+ return -EINVAL;
+ }
+
+ if (strncmp(line_buf + 1, field_name, field_name_len) != 0) {
+ V4L2_MDCFG_PARSER_ERR("Invalid field name.");
+ return -EINVAL;
+ }
+
+ if (line_buf[field_value_pos - 1] != ' ') {
+ V4L2_MDCFG_PARSER_ERR("Lack of space after colon.");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < field_value_len; ++i)
+ if (line_buf[field_value_pos + i] == ' ') {
+ V4L2_MDCFG_PARSER_ERR("Field value must not include spaces.");
+ return -EINVAL;
+ }
+
+ value = malloc(sizeof(char) * (field_value_len + 1));
+ if (value == NULL)
+ return -ENOMEM;
+
+ strncpy(value, line_buf + field_value_pos, field_value_len);
+ value[field_value_len] = '\0';
+
+ *field_value = value;
+
+ return 0;
+}
+
+static int parse_link(struct libv4l2_conf_parser_ctx *ctx,
+ struct libv4l2_media_link_conf **link)
+{
+ int *l_start = &ctx->line_start_pos, i;
+ int *l_end = &ctx->line_end_pos;
+ struct libv4l2_media_link_conf *ret_link = NULL;
+ int ret;
+ static const char *link_fields[] = {
+ "source_entity",
+ "source_pad",
+ "sink_entity",
+ "sink_pad"
+ };
+ char *field_values[4];
+
+ memset(field_values, 0, sizeof(field_values));
+
+ ctx->line_start_pos = 0;
+ ctx->line_end_pos = 0;
+
+ /* look for link beginning signature */
+ for (;;) {
+ ret = get_line(ctx);
+ if (ret < 0)
+ goto err_parser;
+
+ /* handling empty line case */
+ if (*l_end - *l_start <= 1)
+ continue;
+
+ ret = strncmp(ctx->buf + *l_start,
+ "link {\n", *l_end - *l_start);
+ if (ret == 0)
+ break;
+ }
+
+ /* read link fields */
+ for (i = 0; i < ARRAY_SIZE(link_fields); ++i) {
+ ret = get_line(ctx);
+ if (ret < 0) {
+ V4L2_MDCFG_PARSER_ERR("Link entry incomplete.");
+ goto err_parser;
+ }
+
+ ret = parse_field_value(ctx, link_fields[i], &field_values[i]);
+ if (ret < 0) {
+ V4L2_MDCFG_PARSER_ERR("Link field format error (%s)",
+ link_fields[i]);
+ goto err_parser;
+ }
+ }
+
+ /* look for link end */
+ ret = get_line(ctx);
+ if (ret < 0) {
+ V4L2_MDCFG_PARSER_ERR("EOF reached, link end not found.");
+ goto err_parser;
+ }
+
+ if (ctx->buf[*l_start] != '}') {
+ V4L2_MDCFG_PARSER_ERR("Link closing marker not found");
+ goto err_parser;
+ }
+
+ ret_link = malloc(sizeof(*ret_link));
+ if (ret_link == NULL) {
+ V4L2_MDCFG_PARSER_ERR("Could not allocate memory for a link.");
+ goto err_parser;
+ }
+
+ ret_link->source_entity = field_values[0];
+ ret_link->source_pad = atoi(field_values[1]);
+ ret_link->sink_entity = field_values[2];
+ ret_link->sink_pad = atoi(field_values[3]);
+
+ free(field_values[1]);
+ free(field_values[3]);
+
+ *link = ret_link;
+
+ return 1;
+
+err_parser:
+ for (i = 0; i < ARRAY_SIZE(field_values); ++i) {
+ if (field_values[i] != NULL)
+ free(field_values[i]);
+ }
+
+ if (ret_link != NULL)
+ free(ret_link);
+
+ return 0;
+}
+
+static int parse_property(struct libv4l2_conf_parser_ctx *ctx,
+ char **key, char **value)
+{
+ int line_offset = ctx->line_start_pos,
+ line_length = ctx->line_end_pos - ctx->line_start_pos + 1,
+ val_length, i;
+ char *line_buf = ctx->buf + line_offset, *k, *v;
+
+ if (line_buf[0] != '\t') {
+ V4L2_MDCFG_PARSER_ERR("Lack of leading tab.");
+ return -EINVAL;
+ }
+
+ /* Parse key segment of a property */
+ for (i = 1; i < line_length; ++i)
+ if (line_buf[i] == ':')
+ break;
+
+ if (i == line_length) {
+ V4L2_MDCFG_PARSER_ERR("Property format error - lack of semicolon");
+ return -EINVAL;
+ }
+
+ /* At least one character should be left for value segment */
+ if (i >= line_length - 2) {
+ V4L2_MDCFG_PARSER_ERR("Property format error - no value segment");
+ return -EINVAL;
+ }
+
+ k = malloc(sizeof(char) * i);
+ if (k == NULL)
+ return -ENOMEM;
+
+ strncpy(k, line_buf + 1, i - 1);
+ k[i - 1] = '\0';
+
+ val_length = line_length - i - 2;
+
+ v = malloc(sizeof(char) * (val_length + 1));
+ if (v == NULL)
+ return -ENOMEM;
+
+ strncpy(v, line_buf + i + 2, val_length);
+ v[val_length] = '\0';
+
+ *key = k;
+ *value = v;
+
+ return 0;
+}
+
+static int parse_controls(struct libv4l2_conf_parser_ctx *ctx,
+ struct libv4l2_media_ctrl_conf **controls)
+{
+ int *l_start = &ctx->line_start_pos;
+ int *l_end = &ctx->line_end_pos;
+ struct libv4l2_media_ctrl_conf *head = NULL, *tmp_ctrl, *c = NULL;
+ int ret;
+ char *control_name = NULL, *entity_name = NULL;
+
+ if (controls == NULL)
+ return -EINVAL;
+
+ ctx->buf_pos = 0;
+ ctx->line_start_pos = 0;
+ ctx->line_end_pos = 0;
+
+ /* look for controls beginning signature */
+ for (;;) {
+ ret = get_line(ctx);
+ if (ret < 0) {
+ V4L2_MDCFG_PARSER_LOG("Controls configuration not found");
+ return 0;
+ }
+
+ /* handling empty line case */
+ if (*l_end - *l_start <= 1)
+ continue;
+
+ ret = strncmp(ctx->buf + *l_start,
+ "v4l2-controls {\n", *l_end - *l_start);
+ if (ret == 0)
+ break;
+ }
+
+ /* read control-entity pairs */
+ for (;;) {
+ ret = get_line(ctx);
+ if (ret < 0) {
+ V4L2_MDCFG_PARSER_ERR("Controls closing marker not found");
+ goto err_parser;
+ }
+
+ if (ctx->buf[*l_start] == '}')
+ break;
+
+ ret = parse_property(ctx, &control_name, &entity_name);
+ if (ret < 0) {
+ V4L2_MDCFG_PARSER_ERR("Control property parsing error");
+ goto err_parser;
+ }
+
+ tmp_ctrl = calloc(1, sizeof(*tmp_ctrl));
+ if (tmp_ctrl == NULL) {
+ ret = -ENOMEM;
+ goto err_parser;
+ }
+
+ tmp_ctrl->entity_name = entity_name;
+ tmp_ctrl->control_name = control_name;
+
+ if (head == NULL) {
+ head = tmp_ctrl;
+ c = head;
+ } else {
+ c->next = tmp_ctrl;
+ c = c->next;
+ }
+ }
+
+ *controls = head;
+
+ return 0;
+
+err_parser:
+ libv4l2_media_conf_release_controls(head);
+ return ret;
+}
+
+static int parse_links(struct libv4l2_conf_parser_ctx *ctx,
+ struct libv4l2_media_link_conf **links)
+{
+ int cnt = 0;
+ struct libv4l2_media_link_conf *l = NULL, *head = NULL, *tmp = NULL;
+
+ ctx->line_start_pos = 0;
+ ctx->buf_pos = 0;
+
+ while (parse_link(ctx, &tmp)) {
+ if (head == NULL) {
+ head = tmp;
+ head->next = NULL;
+ l = head;
+ } else {
+ l->next = tmp;
+ l = l->next;
+ l->next = NULL;
+ }
+ ++cnt;
+ }
+
+ if (cnt == 0) {
+ V4L2_MDCFG_PARSER_ERR("No links have been found!");
+ goto err_no_data;
+ }
+
+ *links = head;
+
+ return 0;
+
+err_no_data:
+ libv4l2_media_conf_release_links(head);
+ return -EINVAL;
+
+}
+
+void libv4l2_media_conf_release_links(struct libv4l2_media_link_conf *cfg)
+{
+ struct libv4l2_media_link_conf *tmp;
+
+ while (cfg) {
+ tmp = cfg->next;
+ free(cfg->source_entity);
+ free(cfg->sink_entity);
+ free(cfg);
+ cfg = tmp;
+ }
+}
+
+void libv4l2_media_conf_release_controls(struct libv4l2_media_ctrl_conf *cfg)
+{
+ struct libv4l2_media_ctrl_conf *tmp;
+
+ while (cfg) {
+ tmp = cfg->next;
+ free(cfg->entity_name);
+ free(cfg->control_name);
+ free(cfg);
+ cfg = tmp;
+ }
+}
+
+int libv4l2_media_conf_read(char *fname,
+ struct libv4l2_media_device_conf *config)
+{
+ struct libv4l2_media_link_conf *links;
+ struct libv4l2_media_ctrl_conf *controls = NULL;
+ struct stat st;
+ struct libv4l2_conf_parser_ctx ctx;
+ int fd, ret;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ /* read config file to a buffer */
+
+ fd = open(fname, O_RDONLY);
+
+ if (fd < 0) {
+ V4L2_MDCFG_PARSER_ERR("Could not open config file");
+ return -EINVAL;
+ }
+
+ ret = fstat(fd, &st);
+ if (ret < 0) {
+ V4L2_MDCFG_PARSER_ERR("Could not get config file statistics");
+ goto err_fstat;
+ }
+
+ ctx.buf_size = st.st_size;
+ ctx.buf = malloc(ctx.buf_size);
+ if (ctx.buf == NULL) {
+ V4L2_MDCFG_PARSER_ERR("Could not allocate file buffer");
+ ret = -ENOMEM;
+ goto err_fstat;
+ }
+
+ ret = read(fd, ctx.buf, ctx.buf_size);
+ if (ret < 0)
+ goto err_config_read;
+
+ /* parse file buffer */
+
+ ret = parse_links(&ctx, &links);
+ if (ret < 0)
+ goto err_config_read;
+
+ ret = parse_controls(&ctx, &controls);
+ if (ret < 0)
+ goto err_parse_controls;
+
+ config->links = links;
+ config->controls = controls;
+
+ free(ctx.buf);
+
+ return ret;
+
+err_parse_controls:
+ libv4l2_media_conf_release_links(links);
+err_config_read:
+ if (ctx.buf != NULL)
+ free(ctx.buf);
+err_fstat:
+ close(fd);
+
+ return ret;
+}
+
--
1.7.9.5
Sakari Ailus
2014-10-20 21:44:15 UTC
Permalink
Hi Jacek,
Post by Jacek Anaszewski
This patch adds a parser for a media device configuration
file. The parser expects the configuration file containing
links end v4l2-controls definitions as described in the
header file being added. The links describe connections
between media entities and v4l2-controls define the target
sub-devices for particular user controls related ioctl calls.
---
lib/include/libv4l2-media-conf-parser.h | 148 +++++++++++
lib/libv4l2/libv4l2-media-conf-parser.c | 441 +++++++++++++++++++++++++++++++
2 files changed, 589 insertions(+)
create mode 100644 lib/include/libv4l2-media-conf-parser.h
create mode 100644 lib/libv4l2/libv4l2-media-conf-parser.c
diff --git a/lib/include/libv4l2-media-conf-parser.h b/lib/include/libv4l2-media-conf-parser.h
new file mode 100644
index 0000000..b2dba3a
--- /dev/null
+++ b/lib/include/libv4l2-media-conf-parser.h
@@ -0,0 +1,148 @@
+/*
+ * Parser of media device configuration file.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ *
+ *
+ *
+ * link {
+ * <TAB>source_entity: <entity_name><LF>
+ * <TAB>source_pad: <pad_id><LF>
+ * <TAB>sink_entity: <entity_name><LF>
+ * <TAB>sink_pad: <pad_id><LF>
+ * }
Could you use the existing libmediactl format? The parser exists as well.

As a matter of fact, I have a few patches to make it easier to user in a
library.

libmediactl appears to be located under utils/media-ctl. Perhaps it's be
better placed under lib. Cc Laurent.
Post by Jacek Anaszewski
+ *
+ * v4l2-controls {
+ * <TAB><control1_name>: <entity_name><LF>
+ * <TAB><control2_name>: <entity_name><LF>
+ * ...
+ * <TAB><controlN_name>: <entity_name><LF>
+ * }
I didn't know you were working on this.

I have a small library which does essentially the same. The implementation
is incomplete, that's why I hadn't posted it to the list. We could perhaps
discuss this a little bit tomorrow. When would you be available, in case you
are?

What would you think of using a little bit more condensed format for this,
similar to that of libmediactl?
--
Kind regards,

Sakari Ailus
e-mail: ***@iki.fi XMPP: ***@retiisi.org.uk
Jacek Anaszewski
2014-10-21 07:17:00 UTC
Permalink
Hi Sakari,
Post by Sakari Ailus
Hi Jacek,
Post by Jacek Anaszewski
This patch adds a parser for a media device configuration
file. The parser expects the configuration file containing
links end v4l2-controls definitions as described in the
header file being added. The links describe connections
between media entities and v4l2-controls define the target
sub-devices for particular user controls related ioctl calls.
---
lib/include/libv4l2-media-conf-parser.h | 148 +++++++++++
lib/libv4l2/libv4l2-media-conf-parser.c | 441 +++++++++++++++++++++++++++++++
2 files changed, 589 insertions(+)
create mode 100644 lib/include/libv4l2-media-conf-parser.h
create mode 100644 lib/libv4l2/libv4l2-media-conf-parser.c
diff --git a/lib/include/libv4l2-media-conf-parser.h b/lib/include/libv4l2-media-conf-parser.h
new file mode 100644
index 0000000..b2dba3a
--- /dev/null
+++ b/lib/include/libv4l2-media-conf-parser.h
@@ -0,0 +1,148 @@
+/*
+ * Parser of media device configuration file.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ *
+ *
+ *
+ * link {
+ * <TAB>source_entity: <entity_name><LF>
+ * <TAB>source_pad: <pad_id><LF>
+ * <TAB>sink_entity: <entity_name><LF>
+ * <TAB>sink_pad: <pad_id><LF>
+ * }
Could you use the existing libmediactl format? The parser exists as well.
Of course, I will switch to using it.
Post by Sakari Ailus
As a matter of fact, I have a few patches to make it easier to user in a
library.
libmediactl appears to be located under utils/media-ctl. Perhaps it's be
better placed under lib. Cc Laurent.
Post by Jacek Anaszewski
+ *
+ * v4l2-controls {
+ * <TAB><control1_name>: <entity_name><LF>
+ * <TAB><control2_name>: <entity_name><LF>
+ * ...
+ * <TAB><controlN_name>: <entity_name><LF>
+ * }
I didn't know you were working on this.
Actually I did the main part of work around 1,5 year ago as a part
of familiarizing myself with V4L2 media controller API.
Post by Sakari Ailus
I have a small library which does essentially the same. The implementation
is incomplete, that's why I hadn't posted it to the list. We could perhaps
discuss this a little bit tomorrow. When would you be available, in case you
are?
I will be available around 8 hours from now on.
Post by Sakari Ailus
What would you think of using a little bit more condensed format for this,
similar to that of libmediactl?
Could you spot a place where the format is defined?

Best Regards,
Jacek Anaszewski
Sakari Ailus
2014-10-21 09:26:24 UTC
Permalink
Hi Jacek,

On Tue, Oct 21, 2014 at 09:17:00AM +0200, Jacek Anaszewski wrote:
...
Post by Jacek Anaszewski
Post by Sakari Ailus
Post by Jacek Anaszewski
+ *
+ * v4l2-controls {
+ * <TAB><control1_name>: <entity_name><LF>
+ * <TAB><control2_name>: <entity_name><LF>
+ * ...
+ * <TAB><controlN_name>: <entity_name><LF>
+ * }
I didn't know you were working on this.
Actually I did the main part of work around 1,5 year ago as a part
of familiarizing myself with V4L2 media controller API.
:-D

I think it's about time we get things like this to libv4l.
Post by Jacek Anaszewski
Post by Sakari Ailus
I have a small library which does essentially the same. The implementation
is incomplete, that's why I hadn't posted it to the list. We could perhaps
discuss this a little bit tomorrow. When would you be available, in case you
are?
I will be available around 8 hours from now on.
I couldn't see you on #v4l, would an hour from now (13:30 Finnish time) be
ok for you?
Post by Jacek Anaszewski
Post by Sakari Ailus
What would you think of using a little bit more condensed format for this,
similar to that of libmediactl?
Could you spot a place where the format is defined?
At the moment there's none, but I thought of a similar format used by
libmediactl.
--
Cheers,

Sakari Ailus
e-mail: ***@iki.fi XMPP: ***@retiisi.org.uk
Jacek Anaszewski
2014-10-21 10:11:36 UTC
Permalink
Hi Sakari,
Post by Sakari Ailus
Hi Jacek,
...
Post by Jacek Anaszewski
Post by Sakari Ailus
Post by Jacek Anaszewski
+ *
+ * v4l2-controls {
+ * <TAB><control1_name>: <entity_name><LF>
+ * <TAB><control2_name>: <entity_name><LF>
+ * ...
+ * <TAB><controlN_name>: <entity_name><LF>
+ * }
I didn't know you were working on this.
Actually I did the main part of work around 1,5 year ago as a part
of familiarizing myself with V4L2 media controller API.
:-D
I think it's about time we get things like this to libv4l.
Definitely :)
Post by Sakari Ailus
Post by Jacek Anaszewski
Post by Sakari Ailus
I have a small library which does essentially the same. The implementation
is incomplete, that's why I hadn't posted it to the list. We could perhaps
discuss this a little bit tomorrow. When would you be available, in case you
are?
I will be available around 8 hours from now on.
I couldn't see you on #v4l, would an hour from now (13:30 Finnish time) be
ok for you?
What about 14:00 Finnish time?
Post by Sakari Ailus
Post by Jacek Anaszewski
Post by Sakari Ailus
What would you think of using a little bit more condensed format for this,
similar to that of libmediactl?
Could you spot a place where the format is defined?
At the moment there's none, but I thought of a similar format used by
libmediactl.
OK, to be discussed.

Best Regards,
Jacek Anaszewski
Jacek Anaszewski
2014-10-17 14:54:40 UTC
Permalink
Add helpers for retrieving media device topology and manipulating
its configuration.

Signed-off-by: Jacek Anaszewski <***@samsung.com>
Acked-by: Kyungmin Park <***@samsung.com>
Cc: Mauro Carvalho Chehab <***@osg.samsung.com>
Cc: Hans Verkuil <***@cisco.com>
---
lib/include/libv4l2-mdev.h | 195 +++++++++
lib/libv4l2/libv4l2-mdev.c | 975 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1170 insertions(+)
create mode 100644 lib/include/libv4l2-mdev.h
create mode 100644 lib/libv4l2/libv4l2-mdev.c

diff --git a/lib/include/libv4l2-mdev.h b/lib/include/libv4l2-mdev.h
new file mode 100644
index 0000000..cb28835
--- /dev/null
+++ b/lib/include/libv4l2-mdev.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jacek Anaszewski <***@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifndef __LIBV4L2_MDEV_H
+#define __LIBV4L2_MDEV_H
+
+#include <libv4l2-media-conf-parser.h>
+#include <linux/v4l2-subdev.h>
+#include <linux/videodev2.h>
+#include <sys/syscall.h>
+
+#define SYS_IOCTL(fd, cmd, arg) \
+ syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg))
+
+#ifdef DEBUG
+#define V4L2_MDEV_DBG(format, ARG...)\
+ printf("[%s:%d] [%s] " format " \n", __FILE__, __LINE__, __func__, ##ARG)
+#else
+#define V4L2_MDEV_DBG(format, ARG...)
+#endif
+
+#define V4L2_MDEV_ERR(format, ARG...)\
+ fprintf(stderr, "Libv4l media device: "format "\n", ##ARG)
+
+#define V4L2_MDEV_LOG(format, ARG...)\
+ fprintf(stdout, "Libv4l media device: "format "\n", ##ARG)
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+/*
+ * struct media_entity - media device entity data
+ * @id: media entity id within media controller
+ * @name: media entity name
+ * @node_name: media entity related device node name
+ * @pads: array of media_entity pads
+ * @num_pads: number of elements in the pads array
+ * @links: array of media_entity links
+ * @num_links: number of elements in the links array
+ * @subdev_fmt: related sub-device format
+ * @fd: related sub-device node file descriptor
+ * @src_pad_id: source pad id when entity is linked
+ * @sink_pad_id: sink pad id when entity is linked
+ * @next: pointer to the next data structure in the list
+ */
+struct media_entity {
+ int id;
+ char name[32];
+ char node_name[32];
+ struct media_pad_desc *pads;
+ int num_pads;
+ struct media_link_desc *links;
+ int num_links;
+ struct v4l2_subdev_format subdev_fmt;
+ int fd;
+ int src_pad_id;
+ int sink_pad_id;
+ struct media_entity *next;
+};
+
+/*
+ * struct media_device - media device comprising the opened video device
+ * @entities: media entities comprised by a video device
+ * @num_entities: number of media entities within a video device
+ * @pipeline: pipeline of media entities from sensor to the video node
+ * @media_fd: file descriptor of the media device this
+ * video device belongs to
+ * @config: media device configuration
+ * @vid_fd: file descriptor of the opened video device node
+ */
+struct media_device {
+ struct media_entity *entities;
+ int num_entities;
+ struct media_entity *pipeline;
+ int media_fd;
+ struct libv4l2_media_device_conf config;
+ int vid_fd;
+};
+
+int mdev_get_node_by_devnum(unsigned int major, unsigned int minor,
+ char *node_name);
+
+int mdev_get_node_by_fd(int fd, char *node_name);
+
+int mdev_enumerate_links(struct media_device *mdev);
+
+int mdev_release_entities(struct media_device *mdev);
+
+int mdev_get_device_topology(struct media_device *mdev);
+
+int mdev_has_device_node(struct media_device *mdev, char *entity_node,
+ char **entity_name);
+
+int mdev_get_media_node(struct media_device *mdev, int capture_fd,
+ char **entity_name);
+
+int mdev_get_pad_parent_name(struct media_device *mdev,
+ struct media_pad_desc *pad, char **parent_name);
+
+int mdev_entity_get_id_by_name(struct media_device *mdev, char *name, int *id);
+
+int mdev_has_link_pad(struct media_link_desc *link,
+ struct media_pad_desc *pad);
+
+int mdev_pad_busy(struct media_device *mdev, struct media_pad_desc *pad,
+ struct media_link_desc **link);
+
+int mdev_print_link_log(char *message, struct media_device *mdev,
+ struct media_link_desc *link);
+
+int mdev_disable_link(struct media_device *mdev,
+ struct media_link_desc *link);
+
+int mdev_get_v4l2_pad(struct media_device *mdev, char *entity_name,
+ int pad_id, struct media_pad_desc *pad);
+
+int mdev_same_link(struct media_link_desc *link1,
+ struct media_link_desc *link2);
+
+int mdev_link_enabled(struct media_device *mdev,
+ struct media_link_desc *link);
+
+int mdev_get_entity_by_pad(struct media_device *mdev,
+ struct media_pad_desc *pad,
+ struct media_entity **entity);
+
+int mdev_setup_config_links(struct media_device *mdev,
+ struct libv4l2_media_link_conf *links);
+
+struct media_entity *mdev_get_entity_by_name(struct media_device *mdev,
+ char *name);
+
+struct media_entity *mdev_conf_get_entity_by_cid(
+ struct libv4l2_media_ctrl_conf *ctrl_cfg,
+ int cid);
+
+int mdev_is_control_supported(struct media_device *mdev,
+ struct libv4l2_media_ctrl_conf *ctrl_cfg);
+
+int mdev_validate_control_config(struct media_device *mdev,
+ struct libv4l2_media_ctrl_conf *ctrl_cfg);
+
+int mdev_get_entity_by_fd(struct media_device *mdev, int fd,
+ struct media_entity **entity);
+
+int mdev_get_pads_by_entity(struct media_entity *entity,
+ struct media_pad_desc **pads,
+ int *num_pads, unsigned int type);
+
+int mdev_get_src_entity_by_link(struct media_device *mdev,
+ struct media_link_desc *link,
+ struct media_entity **entity);
+
+int mdev_get_link_by_sink_pad(struct media_device *mdev,
+ struct media_pad_desc *pad,
+ struct media_link_desc **link);
+
+int mdev_get_link_by_source_pad(struct media_entity *entity,
+ struct media_pad_desc *pad,
+ struct media_link_desc **link);
+
+int mdev_get_busy_pads_by_entity(struct media_device *mdev,
+ struct media_entity *entity,
+ struct media_pad_desc **busy_pads,
+ int *num_busy_pads,
+ unsigned int type);
+
+int mdev_get_pad_by_index(struct media_pad_desc *pads, int num_pads,
+ int index, struct media_pad_desc *out_pad);
+
+int mdev_discover_pipeline_by_fd(struct media_device *mdev, int fd);
+
+void mdev_close_pipeline_subdevs(struct media_entity *pipeline);
+
+int mdev_open_pipeline_subdevs(struct media_entity *pipeline);
+
+int mdev_verify_format(struct v4l2_mbus_framefmt *fmt1,
+ struct v4l2_mbus_framefmt *fmt2);
+
+int mdev_has_pipeline_entity(struct media_entity *pipeline, char *entity);
+
+#endif /* __LIBV4L2_MDEV_H */
diff --git a/lib/libv4l2/libv4l2-mdev.c b/lib/libv4l2/libv4l2-mdev.c
new file mode 100644
index 0000000..ed05fd8
--- /dev/null
+++ b/lib/libv4l2/libv4l2-mdev.c
@@ -0,0 +1,975 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jacek Anaszewski <***@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/kdev_t.h>
+#include <linux/media.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <libv4l2-mdev.h>
+
+/*
+ * If there was an entry for the cid defined in the controls
+ * config then this function returns related entity. Otherwise
+ * NULL is returned.
+ */
+struct media_entity *mdev_conf_get_entity_by_cid(
+ struct libv4l2_media_ctrl_conf *ctrl_cfg,
+ int cid)
+{
+ if (ctrl_cfg == NULL)
+ return NULL;
+
+ while (ctrl_cfg) {
+ if (ctrl_cfg->cid == cid)
+ return ctrl_cfg->entity;
+ ctrl_cfg = ctrl_cfg->next;
+ }
+
+ return NULL;
+}
+
+int mdev_get_node_by_devnum(unsigned int major, unsigned int minor,
+ char *node_name)
+{
+ struct stat devstat;
+ char devname[32];
+ char sysname[32];
+ char target[1024];
+ char *p;
+ int ret;
+
+ if (node_name == NULL)
+ return -EINVAL;
+
+ sprintf(sysname, "/sys/dev/char/%u:%u", major, minor);
+ ret = readlink(sysname, target, sizeof(target));
+ if (ret < 0)
+ return -EINVAL;
+
+ target[ret] = '\0';
+ p = strrchr(target, '/');
+ if (p == NULL)
+ return -EINVAL;
+
+ sprintf(devname, "/dev/%s", p + 1);
+ ret = stat(devname, &devstat);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (major(devstat.st_rdev) == major &&
+ minor(devstat.st_rdev) == minor)
+ strcpy(node_name, devname);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+int mdev_get_node_by_fd(int fd, char *node_name)
+{
+ struct stat stat;
+ int major_num, minor_num;
+ int ret;
+
+ if (node_name == NULL)
+ return -EINVAL;
+
+ ret = fstat(fd, &stat);
+ if (ret < 0)
+ return -EINVAL;
+
+ major_num = MAJOR(stat.st_rdev);
+ minor_num = MINOR(stat.st_rdev);
+
+ ret = mdev_get_node_by_devnum(major_num, minor_num, node_name);
+ if (ret < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+int mdev_enumerate_entites(struct media_device *mdev)
+{
+ struct media_entity *entity, *entity_buf;
+ struct media_entity_desc entity_desc;
+ unsigned int entities_cnt = 0, entity_buf_size;
+ int ret, id;
+
+ if (mdev == NULL)
+ return -EINVAL;
+
+ entity_buf = calloc(1, sizeof(*entity));
+ memset(&entity_desc, 0, sizeof(entity_desc));
+
+ for (id = 0;; id = entity_desc.id, ++entities_cnt) {
+ entity_buf_size = (entities_cnt + 1) * sizeof(*entity);
+ entity_buf = realloc(entity_buf, entity_buf_size);
+
+ entity = &entity_buf[entities_cnt];
+ memset(entity, 0, sizeof(*entity));
+
+ entity_desc.id = id | MEDIA_ENT_ID_FLAG_NEXT;
+ ret = SYS_IOCTL(mdev->media_fd, MEDIA_IOC_ENUM_ENTITIES,
+ &entity_desc);
+ if (ret < 0) {
+ ret = errno != EINVAL ? -errno : 0;
+ break;
+ }
+ entity->id = entity_desc.id;
+ entity->num_pads = entity_desc.pads;
+ entity->num_links = entity_desc.links;
+ strcpy(entity->name, entity_desc.name);
+
+ if (!(entity_desc.type & MEDIA_ENT_T_DEVNODE) &&
+ !(entity_desc.type & MEDIA_ENT_T_V4L2_SUBDEV))
+ continue;
+
+ ret = mdev_get_node_by_devnum(entity_desc.v4l.major,
+ entity_desc.v4l.minor,
+ entity->node_name);
+ if (ret < 0)
+ goto err_media_dev;
+ }
+
+ mdev->num_entities = entities_cnt;
+ mdev->entities = entity_buf;
+
+ return ret;
+
+err_media_dev:
+ free(entity_buf);
+ return -EINVAL;
+}
+
+int mdev_enumerate_links(struct media_device *mdev)
+{
+ struct media_entity *entities = mdev->entities;
+ struct media_links_enum links_enum;
+ int i, j, ret;
+
+ if (mdev == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < mdev->num_entities; ++i) {
+ if (entities[i].num_pads == 0)
+ continue;
+ links_enum.entity = entities[i].id;
+ links_enum.pads = malloc(entities[i].num_pads *
+ sizeof(struct media_pad_desc));
+ links_enum.links = malloc(entities[i].num_links *
+ sizeof(struct media_link_desc));
+ ret = SYS_IOCTL(mdev->media_fd, MEDIA_IOC_ENUM_LINKS,
+ &links_enum);
+ if (ret < 0) {
+ ret = -errno;
+ goto err_enum_links;
+ }
+
+ entities[i].pads = links_enum.pads;
+ entities[i].links = links_enum.links;
+ }
+
+ return 0;
+
+err_enum_links:
+ for (j = 0; j < i; ++j) {
+ free(entities[j].pads);
+ free(entities[j].links);
+ }
+
+ return ret;
+}
+
+int mdev_release_entities(struct media_device *mdev)
+{
+ int i;
+
+ if (mdev == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < mdev->num_entities; ++i) {
+ free(mdev->entities[i].links);
+ free(mdev->entities[i].pads);
+ }
+
+ free(mdev->entities);
+
+ return 0;
+}
+
+int mdev_get_device_topology(struct media_device *mdev)
+{
+ int ret;
+
+ if (mdev == NULL)
+ return -EINVAL;
+
+ ret = mdev_enumerate_entites(mdev);
+ if (ret < 0) {
+ V4L2_MDEV_ERR("Failed to enumerate video entities.");
+ return ret;
+ }
+
+ ret = mdev_enumerate_links(mdev);
+ if (ret < 0) {
+ V4L2_MDEV_ERR("Failed to enumerate links.");
+ return ret;
+ }
+
+ return 0;
+}
+
+int mdev_has_device_node(struct media_device *mdev, char *entity_node,
+ char **entity_name)
+{
+ int i;
+
+ if (mdev == NULL || entity_node == NULL || entity_name == NULL)
+ return 0;
+
+ for (i = 0; i < mdev->num_entities; ++i) {
+ if (!strcmp(mdev->entities[i].node_name, entity_node)) {
+ *entity_name = mdev->entities[i].name;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int mdev_get_media_node(struct media_device *mdev, int capture_fd,
+ char **entity_name)
+{
+ char media_dev_node[32], capture_dev_node[32];
+ int i, ret;
+
+ if (mdev == NULL)
+ return -EINVAL;
+
+ ret = mdev_get_node_by_fd(capture_fd, capture_dev_node);
+ if (ret < 0)
+ return -EINVAL;
+
+ /* query all available media devices */
+ for (i = 0;; ++i) {
+ sprintf(media_dev_node, "/dev/media%d", i);
+
+ mdev->media_fd = open(media_dev_node, O_RDWR);
+ if (mdev->media_fd < 0) {
+ close(mdev->media_fd);
+ return -EINVAL;
+ }
+
+ ret = mdev_get_device_topology(mdev);
+ if (ret < 0)
+ goto err_get_topology;
+
+ if (mdev_has_device_node(mdev, capture_dev_node, entity_name))
+ return 0;
+
+ mdev_release_entities(mdev);
+ close(mdev->media_fd);
+ }
+
+ ret = -EINVAL;
+
+err_get_topology:
+ close(mdev->media_fd);
+ return ret;
+}
+
+int mdev_get_pad_parent_name(struct media_device *mdev,
+ struct media_pad_desc *pad,
+ char **parent_name)
+{
+ int i;
+
+ if (mdev == NULL || pad == NULL || parent_name == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < mdev->num_entities; ++i) {
+ if (mdev->entities[i].id == pad->entity) {
+ *parent_name = mdev->entities[i].name;
+ break;
+ }
+ }
+
+ if (i == mdev->num_entities)
+ return -EINVAL;
+
+ return 0;
+}
+
+int mdev_entity_get_id_by_name(struct media_device *mdev, char *name,
+ int *id)
+{
+ int i;
+
+ for (i = 0; i < mdev->num_entities; ++i) {
+ if (strcmp(mdev->entities[i].name, name) == 0) {
+ *id = mdev->entities[i].id;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+
+}
+
+int mdev_has_link_pad(struct media_link_desc *link,
+ struct media_pad_desc *pad)
+{
+ if (link == NULL || pad == NULL)
+ return -EINVAL;
+
+ if (link->source.entity == pad->entity &&
+ link->source.index == pad->index)
+ return 1;
+ if (link->sink.entity == pad->entity &&
+ link->sink.index == pad->index)
+ return 1;
+
+ return 0;
+}
+
+int mdev_pad_busy(struct media_device *mdev, struct media_pad_desc *pad,
+ struct media_link_desc **link)
+{
+ struct media_link_desc *cur_link;
+ int i, j;
+
+ if (mdev == NULL || link == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < mdev->num_entities; ++i) {
+ for (j = 0; j < mdev->entities[i].num_links; ++j) {
+ cur_link = &mdev->entities[i].links[j];
+ if ((cur_link->flags & MEDIA_LNK_FL_ENABLED) &&
+ mdev_has_link_pad(cur_link, pad)) {
+ *link = cur_link;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int mdev_print_link_log(char *message, struct media_device *mdev,
+ struct media_link_desc *link)
+{
+ char *src_entity = NULL, *sink_entity = NULL;
+ int ret;
+
+ if (message == NULL || mdev == NULL || link == NULL)
+ return -EINVAL;
+
+ ret = mdev_get_pad_parent_name(mdev, &link->source, &src_entity);
+ if (ret < 0)
+ return ret;
+
+ ret = mdev_get_pad_parent_name(mdev, &link->sink, &sink_entity);
+ if (ret < 0)
+ return ret;
+
+ V4L2_MDEV_LOG("%s: [%s]:%d -> [%s]:%d", message, src_entity,
+ link->source.index, sink_entity, link->sink.index);
+
+ return 0;
+}
+
+int mdev_disable_link(struct media_device *mdev,
+ struct media_link_desc *link)
+{
+ int ret = -1;
+
+ if (mdev == NULL || link == NULL)
+ return -EINVAL;
+
+ if (link->flags & MEDIA_LNK_FL_IMMUTABLE) {
+ V4L2_MDEV_ERR("Can't disable immutable link.");
+ return -EINVAL;
+ }
+
+ link->flags &= ~MEDIA_LNK_FL_ENABLED;
+ ret = SYS_IOCTL(mdev->media_fd, MEDIA_IOC_SETUP_LINK, link);
+ if (ret) {
+ V4L2_MDEV_ERR("MEDIA_IOC_SETUP_LINK ioctl failed.");
+ return ret;
+ }
+
+ mdev_print_link_log("Disabled link.", mdev, link);
+
+ return 0;
+}
+
+int mdev_get_v4l2_pad(struct media_device *mdev, char *entity_name,
+ int pad_id, struct media_pad_desc *pad)
+{
+ int ret = -1, entity_id;
+
+ if (mdev == NULL || entity_name == NULL || pad == NULL)
+ return -EINVAL;
+
+ ret = mdev_entity_get_id_by_name(mdev, entity_name, &entity_id);
+ if (ret < 0)
+ return ret;
+
+ pad->entity = entity_id;
+ pad->index = pad_id;
+
+ return 0;
+}
+
+int mdev_same_link(struct media_link_desc *link1,
+ struct media_link_desc *link2)
+{
+ if (link1 == NULL || link2 == NULL)
+ return 0;
+
+ if (link1->source.entity == link2->source.entity &&
+ link1->source.index == link2->source.index &&
+ link1->sink.entity == link2->sink.entity &&
+ link1->sink.index == link2->sink.index)
+ return 1;
+
+ return 0;
+}
+
+int mdev_link_enabled(struct media_device *mdev,
+ struct media_link_desc *link)
+{
+ struct media_link_desc *cur_link;
+ int i, j;
+
+ if (mdev == NULL || link == NULL)
+ return 0;
+
+ for (i = 0; i < mdev->num_entities; ++i) {
+ for (j = 0; j < mdev->entities[i].num_links; ++j) {
+ cur_link = &mdev->entities[i].links[j];
+ if ((cur_link->flags & MEDIA_LNK_FL_ENABLED) &&
+ mdev_same_link(link, cur_link)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int mdev_get_entity_by_pad(struct media_device *mdev,
+ struct media_pad_desc *pad,
+ struct media_entity **entity)
+{
+ int i;
+
+ if (mdev == NULL || pad == NULL || entity == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < mdev->num_entities; ++i) {
+ if (pad->entity == mdev->entities[i].id) {
+ *entity = &mdev->entities[i];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+int mdev_setup_config_links(struct media_device *mdev,
+ struct libv4l2_media_link_conf *links)
+{
+ struct media_link_desc new_link, *colliding_link;
+ struct media_entity *entity;
+ int i, ret;
+
+ if (mdev == NULL || links == NULL)
+ return -EINVAL;
+
+ while (links) {
+ ret = mdev_get_v4l2_pad(mdev, links->source_entity,
+ links->source_pad, &new_link.source);
+ if (ret < 0)
+ return ret;
+ ret = mdev_get_v4l2_pad(mdev, links->sink_entity,
+ links->sink_pad, &new_link.sink);
+ if (ret < 0)
+ return ret;
+
+ if (mdev_link_enabled(mdev, &new_link)) {
+ mdev_print_link_log("Link already enabled.", mdev,
+ &new_link);
+
+ links = links->next;
+ continue;
+ }
+
+ ret = mdev_get_entity_by_pad(mdev, &new_link.sink, &entity);
+ if (ret < 0)
+ return ret;
+
+ /* Disable all links occupying sink pads of the entity */
+ for (i = 0; i < entity->num_pads; ++i) {
+ if (entity->pads[i].flags & MEDIA_PAD_FL_SINK) {
+ if (mdev_pad_busy(mdev, &entity->pads[i],
+ &colliding_link)) {
+ ret = mdev_disable_link(mdev, colliding_link);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+
+ new_link.flags = MEDIA_LNK_FL_ENABLED;
+
+ mdev_print_link_log("Linking entities...", mdev, &new_link);
+
+ ret = SYS_IOCTL(mdev->media_fd, MEDIA_IOC_SETUP_LINK,
+ &new_link);
+ if (ret < 0)
+ return ret;
+
+ V4L2_MDEV_LOG("Link has been set up successfuly.");
+
+ links = links->next;
+ }
+
+ return 0;
+}
+
+struct media_entity *mdev_get_entity_by_name(struct media_device *mdev,
+ char *name)
+{
+ int i;
+
+ for (i = 0; i < mdev->num_entities; ++i)
+ if (!strcmp(mdev->entities[i].name, name))
+ return &mdev->entities[i];
+
+ return NULL;
+}
+
+int mdev_is_control_supported(struct media_device *mdev,
+ struct libv4l2_media_ctrl_conf *ctrl_cfg)
+{
+ struct v4l2_query_ext_ctrl queryctrl;
+ struct media_entity *entity;
+
+ entity = mdev_get_entity_by_name(mdev, ctrl_cfg->entity_name);
+ if (entity == NULL)
+ return 0;
+
+ /* Iterate through control ids */
+
+ queryctrl.id = V4L2_CID_BASE | V4L2_CTRL_FLAG_NEXT_CTRL;
+
+ while (!SYS_IOCTL(entity->fd, VIDIOC_QUERY_EXT_CTRL, &queryctrl)) {
+ if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+ continue;
+
+ if (!strcmp((char *) queryctrl.name, ctrl_cfg->control_name)) {
+ ctrl_cfg->cid = queryctrl.id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
+ ctrl_cfg->entity = entity;
+ V4L2_MDEV_DBG("Validated config control \"%s\" (id: %x).",
+ queryctrl.name,
+ ctrl_cfg->cid);
+
+ return 1;
+ }
+
+ queryctrl.id = queryctrl.id | V4L2_CTRL_FLAG_NEXT_CTRL;
+ }
+
+ return 0;
+}
+
+int mdev_validate_control_config(struct media_device *mdev,
+ struct libv4l2_media_ctrl_conf *ctrl_cfg)
+{
+ if (mdev == NULL || ctrl_cfg == NULL)
+ return -EINVAL;
+
+ while (ctrl_cfg) {
+ if (!mdev_is_control_supported(mdev, ctrl_cfg)) {
+ V4L2_MDEV_ERR("Control \"%s\" is unsupported on %s.",
+ ctrl_cfg->control_name,
+ ctrl_cfg->entity_name);
+ return -EINVAL;
+ }
+
+ ctrl_cfg = ctrl_cfg->next;
+ }
+
+ return 0;
+}
+
+int mdev_get_entity_by_fd(struct media_device *mdev, int fd,
+ struct media_entity **entity)
+{
+ char node_name[32];
+ int i, ret;
+
+ if (mdev == NULL || entity == NULL)
+ return -EINVAL;
+
+ ret = mdev_get_node_by_fd(fd, node_name);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < mdev->num_entities; ++i) {
+ if (strcmp(mdev->entities[i].node_name, node_name) == 0) {
+ *entity = &mdev->entities[i];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+int mdev_get_pads_by_entity(struct media_entity *entity,
+ struct media_pad_desc **pads,
+ int *num_pads, unsigned int type)
+{
+ struct media_pad_desc *entity_pads;
+ int cnt_pads, i;
+
+ if (entity == NULL || pads == NULL || num_pads == NULL)
+ return -EINVAL;
+
+ entity_pads = malloc(sizeof(*entity_pads));
+ cnt_pads = 0;
+
+ for (i = 0; i < entity->num_pads; ++i) {
+ if (entity->pads[i].flags & type) {
+ entity_pads = realloc(entity_pads, (i + 1) *
+ sizeof(*entity_pads));
+ entity_pads[cnt_pads++] = entity->pads[i];
+ }
+ }
+
+ if (cnt_pads == 0)
+ free(entity_pads);
+
+ *pads = entity_pads;
+ *num_pads = cnt_pads;
+
+ return 0;
+}
+
+int mdev_get_src_entity_by_link(struct media_device *mdev,
+ struct media_link_desc *link,
+ struct media_entity **entity)
+{
+ int i;
+
+ if (mdev == NULL || link == NULL || entity == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < mdev->num_entities; ++i) {
+ if (mdev->entities[i].id == link->source.entity) {
+ *entity = &mdev->entities[i];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+int mdev_get_link_by_sink_pad(struct media_device *mdev,
+ struct media_pad_desc *pad,
+ struct media_link_desc **link)
+{
+ struct media_link_desc *cur_link = NULL;
+ struct media_entity *entity;
+ int i, j, ret;
+
+ if (mdev == NULL || pad == NULL || link == NULL)
+ return -EINVAL;
+
+ ret = mdev_get_entity_by_pad(mdev, pad, &entity);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < mdev->num_entities; ++i) {
+ for (j = 0; j < mdev->entities[i].num_links; ++j) {
+ cur_link = &mdev->entities[i].links[j];
+ if ((cur_link->flags & MEDIA_LNK_FL_ENABLED) &&
+ (cur_link->sink.entity == pad->entity) &&
+ (cur_link->sink.index == pad->index)) {
+ *link = cur_link;
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+int mdev_get_link_by_source_pad(struct media_entity *entity,
+ struct media_pad_desc *pad,
+ struct media_link_desc **link)
+{
+ int i;
+
+ if (entity == NULL || pad == NULL || link == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < entity->num_links; ++i) {
+ if ((entity->links[i].flags & MEDIA_LNK_FL_ENABLED) &&
+ (entity->links[i].source.index == pad->index)) {
+ *link = &entity->links[i];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+int mdev_get_busy_pads_by_entity(struct media_device *mdev,
+ struct media_entity *entity,
+ struct media_pad_desc **busy_pads,
+ int *num_busy_pads,
+ unsigned int type)
+{
+ struct media_pad_desc *bpads, *pads;
+ struct media_link_desc *link;
+ int cnt_bpads = 0, num_pads, i, ret;
+
+ if (entity == NULL || busy_pads == NULL || num_busy_pads == NULL ||
+ (type == MEDIA_PAD_FL_SINK && mdev == NULL))
+ return -EINVAL;
+
+ ret = mdev_get_pads_by_entity(entity, &pads, &num_pads, type);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (num_pads == 0)
+ goto done;
+
+ bpads = malloc(sizeof(*pads));
+ if (bpads == NULL)
+ goto error_ret;
+
+ for (i = 0; i < num_pads; ++i) {
+ if (type == MEDIA_PAD_FL_SINK)
+ ret = mdev_get_link_by_sink_pad(mdev, &pads[i], &link);
+ else
+ ret = mdev_get_link_by_source_pad(entity, &pads[i], &link);
+ if (ret == 0) {
+ bpads = realloc(bpads, (i + 1) *
+ sizeof(*bpads));
+ bpads[cnt_bpads++] = pads[i];
+ }
+ }
+ if (num_pads > 0)
+ free(pads);
+
+ if (cnt_bpads == 0)
+ free(bpads);
+
+done:
+ *busy_pads = bpads;
+ *num_busy_pads = cnt_bpads;
+
+ return 0;
+
+error_ret:
+ return -EINVAL;
+}
+
+int mdev_get_pad_by_index(struct media_pad_desc *pads, int num_pads,
+ int index, struct media_pad_desc *out_pad)
+
+{
+ int i;
+
+ if (pads == NULL || out_pad == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < num_pads; ++i) {
+ if (pads[i].index == index) {
+ *out_pad = pads[i];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+int mdev_discover_pipeline_by_fd(struct media_device *mdev, int fd)
+{
+ struct media_entity *entity, *pipe_head = NULL;
+ struct media_pad_desc *sink_pads, sink_pad;
+ struct media_link_desc *link;
+ int num_sink_pads, prev_link_src_pad = -1, ret;
+
+ if (mdev == NULL)
+ return -EINVAL;
+
+ /* get sink pipeline entity */
+ ret = mdev_get_entity_by_fd(mdev, fd, &entity);
+ if (ret < 0)
+ return ret;
+
+ if (entity == NULL)
+ return -EINVAL;
+
+ entity->fd = fd;
+
+ for (;;) {
+ if (pipe_head == NULL) {
+ pipe_head = entity;
+ } else {
+ entity->next = pipe_head;
+ pipe_head = entity;
+ }
+
+ entity->src_pad_id = prev_link_src_pad;
+ ret = mdev_get_busy_pads_by_entity(mdev, entity,
+ &sink_pads,
+ &num_sink_pads,
+ MEDIA_PAD_FL_SINK);
+ if (ret < 0)
+ return ret;
+
+ /* check if pipeline source entity has been reached */
+ if (num_sink_pads > 1) {
+ /* Case for two parallel active links */
+ ret = mdev_get_pad_by_index(sink_pads, num_sink_pads,
+ 0, &sink_pad);
+ if (ret < 0)
+ return ret;
+ } else if (num_sink_pads == 1) {
+ sink_pad = sink_pads[0];
+ } else {
+ break;
+ }
+ if (num_sink_pads > 0)
+ free(sink_pads);
+
+ ret = mdev_get_link_by_sink_pad(mdev, &sink_pad,
+ &link);
+
+ prev_link_src_pad = link->source.index;
+ entity->sink_pad_id = link->sink.index;
+
+ ret = mdev_get_src_entity_by_link(mdev, link, &entity);
+ if (ret || entity == NULL)
+ return ret;
+ }
+
+ mdev->pipeline = pipe_head;
+
+ return 0;
+}
+
+void mdev_close_pipeline_subdevs(struct media_entity *pipeline)
+{
+ while (pipeline) {
+ close(pipeline->fd);
+ pipeline = pipeline->next;
+ if (pipeline->next == NULL)
+ break;
+ }
+}
+
+int mdev_open_pipeline_subdevs(struct media_entity *pipeline)
+{
+ struct media_entity *entity = pipeline;
+
+ if (pipeline == NULL)
+ return -EINVAL;
+
+ while (entity) {
+ entity->fd = open(entity->node_name, O_RDWR);
+ if (entity->fd < 0) {
+ V4L2_MDEV_DBG("Could not open device %s", entity->node_name);
+ goto err_open_subdev;
+ }
+
+ entity = entity->next;
+ if (entity->next == NULL)
+ break;
+ }
+
+ return 0;
+
+err_open_subdev:
+ if (pipeline == entity)
+ return 0;
+ mdev_close_pipeline_subdevs(pipeline);
+
+ return -EINVAL;
+}
+
+int mdev_verify_format(struct v4l2_mbus_framefmt *fmt1,
+ struct v4l2_mbus_framefmt *fmt2)
+{
+ if (fmt1 == NULL || fmt2 == NULL)
+ return 0;
+
+ if (fmt1->width != fmt2->width) {
+ V4L2_MDEV_DBG("width mismatch");
+ return 0;
+ }
+
+ if (fmt1->height != fmt2->height) {
+ V4L2_MDEV_DBG("height mismatch");
+ return 0;
+ }
+
+ if (fmt1->code != fmt2->code) {
+ V4L2_MDEV_DBG("code mismatch");
+ return 0;
+ }
+
+ if (fmt1->field != fmt2->field) {
+ V4L2_MDEV_DBG("field mismatch");
+ return 0;
+ }
+
+ if (fmt1->colorspace != fmt2->colorspace) {
+ V4L2_MDEV_DBG("colorspace mismatch");
+ return 0;
+ }
+
+ return 1;
+}
+
+int mdev_has_pipeline_entity(struct media_entity *pipeline, char *entity)
+{
+ if (pipeline == NULL || entity == NULL)
+ return -EINVAL;
+
+ while (pipeline) {
+ if (!strcmp(pipeline->name, entity))
+ return 1;
+ pipeline = pipeline->next;
+ }
+
+ return 0;
+}
--
1.7.9.5
Sakari Ailus
2014-10-22 10:03:02 UTC
Permalink
Hi Jacek,

On Fri, Oct 17, 2014 at 04:54:40PM +0200, Jacek Anaszewski wrote:
...
Post by Jacek Anaszewski
+/*
+ * struct media_entity - media device entity data
+ */
+struct media_entity {
+ int id;
+ char name[32];
+ char node_name[32];
+ struct media_pad_desc *pads;
+ int num_pads;
+ struct media_link_desc *links;
+ int num_links;
+ struct v4l2_subdev_format subdev_fmt;
+ int fd;
+ int src_pad_id;
+ int sink_pad_id;
+ struct media_entity *next;
+};
Could you use libmediactl and libv4l2subdev instead here as well? They do
actually implement much of what you do here. Feel free to comment on the
API. The libraries have a little bit different background than this one.
Obviously there's functionality in this library what's not in the two; some
of this might belong to either of the two libraries.

I think we'll need V4L2 sub-device related information stored next to the
media entities as well, so that's something to be added.
--
Kind regards,

Sakari Ailus
e-mail: ***@iki.fi XMPP: ***@retiisi.org.uk
Laurent Pinchart
2014-10-22 18:45:47 UTC
Permalink
Hi Sakari,
Post by Sakari Ailus
...
Post by Jacek Anaszewski
+/*
+ * struct media_entity - media device entity data
+ */
+struct media_entity {
+ int id;
+ char name[32];
+ char node_name[32];
+ struct media_pad_desc *pads;
+ int num_pads;
+ struct media_link_desc *links;
+ int num_links;
+ struct v4l2_subdev_format subdev_fmt;
+ int fd;
+ int src_pad_id;
+ int sink_pad_id;
+ struct media_entity *next;
+};
Could you use libmediactl and libv4l2subdev instead here as well? They do
actually implement much of what you do here. Feel free to comment on the
API. The libraries have a little bit different background than this one.
Obviously there's functionality in this library what's not in the two; some
of this might belong to either of the two libraries.
I think we'll need V4L2 sub-device related information stored next to the
media entities as well, so that's something to be added.
I generic mechanism to attach subsystem-specific data to entities sounds good
to me. The fd field could then be moved out of struct media_entity.
--
Regards,

Laurent Pinchart
Jacek Anaszewski
2014-10-17 14:54:41 UTC
Permalink
Some ioctl calls predestined for a media device have to
be separately executed on each sub-device of a pipeline
or redirected to the predefined one basing on the
configuration data. This patch adds wrappers that adjust
intercepted ioctl calls and execute them on every
required sub-device.

Signed-off-by: Jacek Anaszewski <***@samsung.com>
Acked-by: Kyungmin Park <***@samsung.com>
Cc: Mauro Carvalho Chehab <***@osg.samsung.com>
Cc: Hans Verkuil <***@cisco.com>
---
lib/include/libv4l2-mdev-ioctl.h | 45 ++++++
lib/libv4l2/libv4l2-mdev-ioctl.c | 329 ++++++++++++++++++++++++++++++++++++++
2 files changed, 374 insertions(+)
create mode 100644 lib/include/libv4l2-mdev-ioctl.h
create mode 100644 lib/libv4l2/libv4l2-mdev-ioctl.c

diff --git a/lib/include/libv4l2-mdev-ioctl.h b/lib/include/libv4l2-mdev-ioctl.h
new file mode 100644
index 0000000..10326be
--- /dev/null
+++ b/lib/include/libv4l2-mdev-ioctl.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jacek Anaszewski <***@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifndef __LIBV4L2_MDEV_IOCTL_H
+#define __LIBV4L2_MDEV_IOCTL_H
+
+#include <linux/videodev2.h>
+#include <libv4l2-mdev.h>
+
+int mdev_ioctl_set_fmt(struct media_device *mdev,
+ struct v4l2_format *fmt);
+
+int mdev_ioctl_ctrl(struct media_device *mdev, int request,
+ struct v4l2_control *arg);
+
+int mdev_ioctl_ext_ctrl(struct media_device *mdev, int request,
+ struct v4l2_ext_controls *arg);
+
+int mdev_ioctl_single_ext_ctrl(struct media_device *mdev,
+ int request, struct v4l2_ext_controls *arg);
+
+int mdev_ioctl_queryctrl(struct media_device *mdev,
+ struct v4l2_queryctrl *arg);
+
+int mdev_ioctl_query_ext_ctrl(struct media_device *mdev,
+ struct v4l2_query_ext_ctrl *arg);
+
+int mdev_ioctl_querymenu(struct media_device *mdev,
+ struct v4l2_querymenu *arg);
+
+#endif /* __LIBV4L2_MDEV_IOCTL_H */
diff --git a/lib/libv4l2/libv4l2-mdev-ioctl.c b/lib/libv4l2/libv4l2-mdev-ioctl.c
new file mode 100644
index 0000000..0347f37
--- /dev/null
+++ b/lib/libv4l2/libv4l2-mdev-ioctl.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jacek Anaszewski <***@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include <errno.h>
+#include <linux/videodev2.h>
+#include <sys/ioctl.h>
+
+#include <libv4l2-mdev-ioctl.h>
+
+int mdev_ioctl_set_fmt(struct media_device *mdev,
+ struct v4l2_format *fmt)
+{
+ struct v4l2_subdev_format sd_fmt = { 0 };
+ struct media_entity *pipeline = mdev->pipeline;
+ int ret;
+
+ while (pipeline) {
+ sd_fmt = pipeline->subdev_fmt;
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = SYS_IOCTL(pipeline->fd, VIDIOC_SUBDEV_S_FMT,
+ &sd_fmt);
+ if (ret < 0)
+ return ret;
+
+ pipeline = pipeline->next;
+
+ /* Last entity in the pipeline is not a sub-device */
+ if (pipeline->next == NULL)
+ break;
+ }
+
+ ret = SYS_IOCTL(pipeline->fd, VIDIOC_S_FMT, fmt);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int mdev_ioctl_ctrl(struct media_device *mdev, int request,
+ struct v4l2_control *arg)
+{
+ struct media_entity *pipeline = mdev->pipeline, *entity;
+ struct v4l2_control ctrl = *arg;
+ struct v4l2_queryctrl queryctrl;
+ int ret = -EINVAL;
+
+ /*
+ * The control has to be reset to the default value
+ * on all of the pipeline entities, prior setting a new
+ * value. This is required in cases when the control config
+ * is changed between subsequent calls to VIDIOC_S_CTRL,
+ * to avoid the situation when a control is set on more
+ * than one sub-device.
+ */
+ if (request == VIDIOC_S_CTRL) {
+ while (pipeline) {
+ queryctrl.id = ctrl.id;
+
+ ret = SYS_IOCTL(pipeline->fd, VIDIOC_QUERYCTRL,
+ &queryctrl);
+ if (ret < 0) {
+ pipeline = pipeline->next;
+ continue;
+ }
+
+ ctrl.value = queryctrl.default_value;
+ ret = SYS_IOCTL(pipeline->fd, VIDIOC_S_CTRL, &ctrl);
+ if (ret < 0)
+ return -EINVAL;
+
+ pipeline = pipeline->next;
+ }
+
+ ctrl.value = arg->value;
+ }
+
+ entity = mdev_conf_get_entity_by_cid(mdev->config.controls, ctrl.id);
+ if (entity) {
+ ret = SYS_IOCTL(entity->fd, request, &ctrl);
+ V4L2_MDEV_DBG("Ioctl result for user control %x on %s: %d.",
+ ctrl.id, entity->name, ret);
+ goto exit;
+ }
+
+ V4L2_MDEV_DBG("No config for control id %x.", ctrl.id);
+
+ /* Walk the pipeline until the request succeeds */
+ pipeline = mdev->pipeline;
+
+ while (pipeline) {
+ ret = SYS_IOCTL(pipeline->fd, request, &ctrl);
+ if (!ret) {
+ V4L2_MDEV_DBG("Ioctl succeeded for user control %x on %s.",
+ ctrl.id, pipeline->name);
+ goto exit;
+ }
+
+ pipeline = pipeline->next;
+ }
+
+exit:
+ *arg = ctrl;
+ return ret;
+}
+
+int mdev_ioctl_single_ext_ctrl(struct media_device *mdev,
+ int request, struct v4l2_ext_controls *arg)
+{
+ struct media_entity *pipeline = mdev->pipeline, *entity;
+ struct v4l2_ext_controls ctrls = *arg;
+ struct v4l2_ext_control *ctrl;
+ struct v4l2_query_ext_ctrl queryctrl;
+ int ret = -EINVAL;
+
+ ctrl = &ctrls.controls[0];
+
+ /*
+ * The control has to be reset to the default value
+ * on all of the pipeline entities, prior setting a new
+ * value. This is required in cases when the control config
+ * is changed between subsequent calls to VIDIOC_S_EXT_CTRLS,
+ * to avoid the situation when a control is set on more
+ * than one sub-device.
+ */
+ if (request == VIDIOC_S_EXT_CTRLS) {
+ while (pipeline) {
+ queryctrl.id = ctrl->id;
+
+ ret = SYS_IOCTL(pipeline->fd, VIDIOC_QUERY_EXT_CTRL,
+ &queryctrl);
+ if (ret < 0) {
+ pipeline = pipeline->next;
+ continue;
+ }
+
+ ctrl->value64 = queryctrl.default_value;
+
+ ret = SYS_IOCTL(pipeline->fd, VIDIOC_S_EXT_CTRLS, &ctrls);
+ if (ret < 0)
+ return -EINVAL;
+
+ pipeline = pipeline->next;
+ }
+
+ ctrl->value64 = arg->controls[0].value64;
+ }
+
+ entity = mdev_conf_get_entity_by_cid(mdev->config.controls, ctrl->id);
+ if (entity) {
+ ret = SYS_IOCTL(entity->fd, request, &ctrls);
+ V4L2_MDEV_DBG("Ioctl result for extended control %x on %s: %d.",
+ ctrl->id, entity->name, ret);
+ goto exit;
+ }
+
+ V4L2_MDEV_DBG("No config for control id %x.", ctrl->id);
+
+ /* Walk the pipeline until the request succeeds */
+ pipeline = mdev->pipeline;
+
+ while (pipeline) {
+ ret = SYS_IOCTL(pipeline->fd, request, &ctrls);
+ if (!ret) {
+ V4L2_MDEV_DBG("Ioctl succeeded for extended control %x on %s.",
+ ctrl->id, pipeline->name);
+ goto exit;
+ }
+
+
+ pipeline = pipeline->next;
+ }
+
+exit:
+ *arg = ctrls;
+ return ret;
+}
+
+int mdev_ioctl_ext_ctrl(struct media_device *mdev, int request,
+ struct v4l2_ext_controls *arg)
+{
+ struct v4l2_ext_controls out_ctrls = *arg, ctrls = *arg;
+ int ret = -EINVAL, i;
+
+ ctrls.count = 1;
+
+ /*
+ * Split cluster to individual ioctl calls for each control
+ * from the array, to make possible redirection of every
+ * single control to different sub-device, according to the
+ * configuration settings.
+ */
+ for (i = 0; i < arg->count; ++i) {
+ ctrls.controls = &arg->controls[i];
+
+ ret = mdev_ioctl_single_ext_ctrl(mdev, request, &ctrls);
+ out_ctrls.controls[i] = ctrls.controls[i];
+ if (ret < 0) {
+ if (ctrls.error_idx == 1)
+ out_ctrls.error_idx = ctrls.count;
+ else
+ out_ctrls.error_idx = i;
+ goto exit;
+ }
+ }
+
+exit:
+ *arg = out_ctrls;
+ return ret;
+}
+
+int mdev_ioctl_queryctrl(struct media_device *mdev,
+ struct v4l2_queryctrl *arg)
+{
+ struct media_entity *pipeline = mdev->pipeline, *entity;
+ struct v4l2_queryctrl queryctrl = *arg;
+ int ret = -EINVAL;
+
+ while (pipeline) {
+ ret = SYS_IOCTL(pipeline->fd, VIDIOC_QUERYCTRL, &queryctrl);
+ if (!ret) {
+ entity = mdev_conf_get_entity_by_cid(mdev->config.controls,
+ queryctrl.id);
+ /*
+ * If the control is in the config, then
+ * query the associated sub-device.
+ */
+ if (entity) {
+ V4L2_MDEV_DBG("Control \"%s\" found in config.", queryctrl.name);
+ /* Check if the control hasn't been already queried */
+ if (entity->fd != pipeline->fd)
+ ret = SYS_IOCTL(entity->fd, VIDIOC_QUERYCTRL, &queryctrl);
+ }
+
+ V4L2_MDEV_DBG("Queryctrl ext control \"%s\" on %s (%d).",
+ queryctrl.name, entity ? entity->name :
+ pipeline->name,
+ ret);
+ break;
+ }
+
+ pipeline = pipeline->next;
+ }
+
+ *arg = queryctrl;
+ return ret;
+}
+
+int mdev_ioctl_query_ext_ctrl(struct media_device *mdev,
+ struct v4l2_query_ext_ctrl *arg)
+{
+ struct media_entity *pipeline = mdev->pipeline, *entity;
+ struct v4l2_query_ext_ctrl query_ext_ctrl = *arg;
+ int ret = -EINVAL;
+
+ while (pipeline) {
+ ret = SYS_IOCTL(pipeline->fd, VIDIOC_QUERY_EXT_CTRL, &query_ext_ctrl);
+ if (!ret) {
+ entity = mdev_conf_get_entity_by_cid(mdev->config.controls,
+ query_ext_ctrl.id);
+ /*
+ * If the control is in the config, then
+ * query the associated sub-device.
+ */
+ if (entity) {
+ V4L2_MDEV_DBG("Control \"%s\" found in config.", query_ext_ctrl.name);
+ /* Check if the control hasn't been already queried */
+ if (entity->fd != pipeline->fd)
+ ret = SYS_IOCTL(entity->fd, VIDIOC_QUERYCTRL, &query_ext_ctrl);
+ }
+
+ V4L2_MDEV_DBG("Queryctrl ext control \"%s\" on %s (%d).",
+ query_ext_ctrl.name, entity ?
+ entity->name :
+ pipeline->name,
+ ret);
+ break;
+ }
+
+ pipeline = pipeline->next;
+ }
+
+ *arg = query_ext_ctrl;
+ return ret;
+}
+
+int mdev_ioctl_querymenu(struct media_device *mdev,
+ struct v4l2_querymenu *arg)
+{
+ struct media_entity *pipeline = mdev->pipeline, *entity;
+ struct v4l2_querymenu querymenu = *arg;
+ int ret = -EINVAL;
+
+ entity = mdev_conf_get_entity_by_cid(mdev->config.controls, querymenu.id);
+ if (entity) {
+ ret = SYS_IOCTL(entity->fd, VIDIOC_QUERYMENU, &querymenu);
+ V4L2_MDEV_DBG("Querymenu result for the control %x on %s: %d.",
+ querymenu.id, entity->name, ret);
+ goto exit;
+ }
+
+ while (pipeline) {
+ ret = SYS_IOCTL(pipeline->fd, VIDIOC_QUERYMENU, &querymenu);
+ if (!ret) {
+ V4L2_MDEV_DBG("Querymenu succeeded for the control %x on %s.",
+ querymenu.id, pipeline->name);
+ goto exit;
+ }
+
+ pipeline = pipeline->next;
+ }
+
+exit:
+ *arg = querymenu;
+ return ret;
+}
--
1.7.9.5
Jacek Anaszewski
2014-10-17 14:54:42 UTC
Permalink
The plugin provides support for the media device on Exynos4 SoC.
It performs single plane <-> multi plane API conversion,
video pipeline linking and takes care of automatic data format
negotiation for the whole pipeline, after intercepting
VIDIOC_S_FMT or VIDIOC_TRY_FMT ioctls.

Signed-off-by: Jacek Anaszewski <***@samsung.com>
Acked-by: Kyungmin Park <***@samsung.com>
Cc: Mauro Carvalho Chehab <***@osg.samsung.com>
Cc: Hans Verkuil <***@cisco.com>
---
configure.ac | 1 +
lib/Makefile.am | 5 +-
lib/libv4l-exynos4-camera/Makefile.am | 8 +
lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c | 569 +++++++++++++++++++++
lib/libv4l2/Makefile.am | 5 +-
5 files changed, 585 insertions(+), 3 deletions(-)
create mode 100644 lib/libv4l-exynos4-camera/Makefile.am
create mode 100644 lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c

diff --git a/configure.ac b/configure.ac
index c9b0524..ae653b9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,6 +17,7 @@ AC_CONFIG_FILES([Makefile
lib/libdvbv5/Makefile
lib/libv4l2rds/Makefile
lib/libv4l-mplane/Makefile
+ lib/libv4l-exynos4-camera/Makefile

utils/Makefile
utils/libv4l2util/Makefile
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 3a0e19c..29455ab 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -3,9 +3,10 @@ SUBDIRS = \
libv4l2 \
libv4l1 \
libv4l2rds \
- libv4l-mplane
+ libv4l-mplane \
+ libv4l-exynos4-camera

if LINUX_OS
SUBDIRS += \
libdvbv5
-endif
\ No newline at end of file
+endif
diff --git a/lib/libv4l-exynos4-camera/Makefile.am b/lib/libv4l-exynos4-camera/Makefile.am
new file mode 100644
index 0000000..a83c3f2
--- /dev/null
+++ b/lib/libv4l-exynos4-camera/Makefile.am
@@ -0,0 +1,8 @@
+if WITH_V4L_PLUGINS
+libv4l2plugin_LTLIBRARIES = libv4l-exynos4-camera.la
+endif
+
+libv4l_exynos4_camera_la_SOURCES = libv4l-exynos4-camera.c
+libv4l_exynos4_camera_la_CPPFLAGS = -fvisibility=hidden -std=gnu99
+libv4l_exynos4_camera_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -lpthread
+libv4l_exynos4_camera_la_LIBADD = ../libv4l2/libv4l2-mdev.la
diff --git a/lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c b/lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
new file mode 100644
index 0000000..150c700
--- /dev/null
+++ b/lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Jacek Anaszewski <***@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <libv4l2-mdev.h>
+#include <libv4l2-mdev-ioctl.h>
+#include <libv4l2-media-conf-parser.h>
+#include <linux/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include "libv4l-plugin.h"
+
+/*
+ * struct exynos4_camera_plugin - libv4l exynos4 camera plugin
+ * @mdev: media device comprising the vid_fd related video device
+ */
+struct exynos4_camera_plugin {
+ struct media_device mdev;
+};
+
+#ifdef DEBUG
+#define V4L2_EXYNOS4_DBG(format, ARG...)\
+ printf("[%s:%d] [%s] " format " \n", __FILE__, __LINE__, __func__, ##ARG)
+#else
+#define V4L2_EXYNOS4_DBG(format, ARG...)
+#endif
+
+#define V4L2_EXYNOS4_ERR(format, ARG...)\
+ fprintf(stderr, "Libv4l Exynos4 camera plugin: "format "\n", ##ARG)
+
+#define V4L2_EXYNOS4_LOG(format, ARG...)\
+ fprintf(stdout, "Libv4l Exynos4 camera plugin: "format "\n", ##ARG)
+
+#if HAVE_VISIBILITY
+#define PLUGIN_PUBLIC __attribute__ ((visibility("default")))
+#else
+#define PLUGIN_PUBLIC
+#endif
+
+#define SIMPLE_CONVERT_IOCTL(fd, cmd, arg, __struc) ({ \
+ int __ret; \
+ struct __struc *req = arg; \
+ uint32_t type = req->type; \
+ req->type = convert_type(type); \
+ __ret = SYS_IOCTL(fd, cmd, arg); \
+ req->type = type; \
+ __ret; \
+ })
+
+#define EXYNOS4_FIMC_DRV "exynos4-fimc"
+#define EXYNOS4_FIMC_LITE_DRV "exynos-fimc-lit"
+#define EXYNOS4_FIMC_IS_ISP_DRV "exynos4-fimc-is"
+#define ENTITY_CAPTURE_SEGMENT "capture"
+#define EXYNOS4_CAPTURE_CONF "/var/lib/libv4l/exynos4_capture_conf"
+#define EXYNOS4_FIMC_IS_ISP "FIMC-IS-ISP"
+#define EXYNOS4_FIMC_PREFIX "FIMC."
+#define MAX_FMT_NEGO_NUM 50
+
+
+static int __capture_entity(char *name)
+{
+ int cap_segment_pos;
+
+ if (name == NULL)
+ return 0;
+
+ cap_segment_pos = strlen(name) - strlen(ENTITY_CAPTURE_SEGMENT);
+
+ if (strcmp(name + cap_segment_pos, ENTITY_CAPTURE_SEGMENT) == 0)
+ return 1;
+
+ return 0;
+}
+
+static int __adjust_format_to_fimc_is_isp(struct v4l2_mbus_framefmt *mbus_fmt)
+{
+ if (mbus_fmt == NULL)
+ return -EINVAL;
+
+ mbus_fmt->width += 16;
+ mbus_fmt->height += 12;
+
+ return 0;
+}
+
+static int negotiate_pipeline_fmt(struct media_entity *pipeline,
+ struct v4l2_format *dev_fmt)
+{
+ struct media_entity *vid_pipe = pipeline;
+ struct v4l2_subdev_format subdev_fmt = { 0 };
+ struct v4l2_mbus_framefmt mbus_fmt = { 0 }, common_fmt;
+ int repeat_negotiation, cnt_negotiation = 0, ret;
+
+ if (pipeline == NULL || dev_fmt == NULL)
+ return -EINVAL;
+
+ mbus_fmt.width = dev_fmt->fmt.pix_mp.width;
+ mbus_fmt.height = dev_fmt->fmt.pix_mp.height;
+ mbus_fmt.field = dev_fmt->fmt.pix_mp.field;
+ mbus_fmt.colorspace = dev_fmt->fmt.pix_mp.colorspace;
+
+ subdev_fmt.which = V4L2_SUBDEV_FORMAT_TRY;
+
+ if (mdev_has_pipeline_entity(vid_pipe, EXYNOS4_FIMC_IS_ISP)) {
+ ret = __adjust_format_to_fimc_is_isp(&mbus_fmt);
+ if (ret < 0)
+ return ret;
+ }
+
+ subdev_fmt.format = mbus_fmt;
+
+ for (;;) {
+ repeat_negotiation = 0;
+ vid_pipe = pipeline;
+
+ subdev_fmt.pad = vid_pipe->src_pad_id;
+
+ ret = SYS_IOCTL(vid_pipe->fd, VIDIOC_SUBDEV_S_FMT,
+ &subdev_fmt);
+ if (ret < 0)
+ return ret;
+
+ common_fmt = subdev_fmt.format;
+ vid_pipe->subdev_fmt = subdev_fmt;
+
+ vid_pipe = vid_pipe->next;
+
+ while (vid_pipe) {
+ subdev_fmt.pad = vid_pipe->sink_pad_id;
+
+ /* Set format on the entity src pad */
+ ret =
+ SYS_IOCTL(vid_pipe->fd, VIDIOC_SUBDEV_S_FMT,
+ &subdev_fmt);
+ if (ret < 0)
+ return ret;
+
+ if (!mdev_verify_format(&subdev_fmt.format, &common_fmt)) {
+ repeat_negotiation = 1;
+ break;
+ }
+
+ vid_pipe->subdev_fmt = subdev_fmt;
+
+ /*
+ * Do not check format on FIMC.[n] source pad
+ * and stop negotiation.
+ */
+ if (!strncmp(vid_pipe->name,
+ EXYNOS4_FIMC_PREFIX,
+ strlen(EXYNOS4_FIMC_PREFIX)))
+ break;
+
+ subdev_fmt.pad = vid_pipe->src_pad_id;
+
+ /* Get format on the entity sink pad */
+ ret =
+ SYS_IOCTL(vid_pipe->fd, VIDIOC_SUBDEV_G_FMT,
+ &subdev_fmt);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (!strcmp(vid_pipe->name,
+ EXYNOS4_FIMC_IS_ISP)) {
+ common_fmt.code = subdev_fmt.format.code;
+ common_fmt.colorspace =
+ subdev_fmt.format.colorspace;
+ common_fmt.width -= 16;
+ common_fmt.height -= 12;
+ }
+ /* Bring back source pad id to the subdev format */
+ subdev_fmt.pad = vid_pipe->sink_pad_id;
+
+ if (!mdev_verify_format(&subdev_fmt.format, &common_fmt)) {
+ repeat_negotiation = 1;
+ break;
+ }
+
+ vid_pipe = vid_pipe->next;
+ if (vid_pipe->next == NULL)
+ break;
+ }
+
+ if (!repeat_negotiation) {
+ break;
+ } else if (++cnt_negotiation > MAX_FMT_NEGO_NUM) {
+ V4L2_EXYNOS4_DBG("Pipeline format negotiation failed!");
+ return -EINVAL;
+ }
+ }
+
+ dev_fmt->fmt.pix_mp.width = subdev_fmt.format.width;
+ dev_fmt->fmt.pix_mp.height = subdev_fmt.format.height;
+ dev_fmt->fmt.pix_mp.field = subdev_fmt.format.field;
+ dev_fmt->fmt.pix_mp.colorspace = subdev_fmt.format.colorspace;
+
+ return 0;
+}
+
+static int convert_type(int type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ default:
+ return type;
+ }
+}
+
+static int set_fmt_ioctl(struct media_device *mdev,
+ unsigned long int cmd,
+ struct v4l2_format *arg,
+ enum v4l2_subdev_format_whence set_mode)
+{
+ struct v4l2_format fmt = { 0 };
+ struct v4l2_format *org = arg;
+ int ret;
+
+ fmt.type = convert_type(arg->type);
+ if (fmt.type != arg->type) {
+ fmt.fmt.pix_mp.width = org->fmt.pix.width;
+ fmt.fmt.pix_mp.height = org->fmt.pix.height;
+ fmt.fmt.pix_mp.pixelformat = org->fmt.pix.pixelformat;
+ fmt.fmt.pix_mp.field = org->fmt.pix.field;
+ fmt.fmt.pix_mp.colorspace = org->fmt.pix.colorspace;
+ fmt.fmt.pix_mp.num_planes = 1;
+ fmt.fmt.pix_mp.flags = org->fmt.pix.flags;
+ fmt.fmt.pix_mp.plane_fmt[0].bytesperline = org->fmt.pix.bytesperline;
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage = org->fmt.pix.sizeimage;
+ } else {
+ fmt = *org;
+ }
+
+ ret = negotiate_pipeline_fmt(mdev->pipeline, &fmt);
+ if (ret < 0)
+ return ret;
+
+ if (set_mode == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = mdev_ioctl_set_fmt(mdev, &fmt);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (fmt.type != arg->type) {
+ org->fmt.pix.width = fmt.fmt.pix_mp.width;
+ org->fmt.pix.height = fmt.fmt.pix_mp.height;
+ org->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
+ org->fmt.pix.field = fmt.fmt.pix_mp.field;
+ org->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
+ org->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
+ org->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+ org->fmt.pix.flags = fmt.fmt.pix_mp.flags;
+ } else {
+ *org = fmt;
+ }
+
+ return 0;
+}
+
+static int get_fmt_ioctl(int fd,
+ unsigned long int cmd,
+ struct v4l2_format *arg)
+{
+ struct v4l2_format fmt = { 0 };
+ struct v4l2_format *org = arg;
+ int ret;
+
+ fmt.type = convert_type(arg->type);
+
+ if (fmt.type == arg->type)
+ return SYS_IOCTL(fd, cmd, arg);
+
+ ret = SYS_IOCTL(fd, cmd, &fmt);
+ if (ret < 0)
+ return ret;
+
+ memset(&org->fmt.pix, 0, sizeof(org->fmt.pix));
+ org->fmt.pix.width = fmt.fmt.pix_mp.width;
+ org->fmt.pix.height = fmt.fmt.pix_mp.height;
+ org->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
+ org->fmt.pix.field = fmt.fmt.pix_mp.field;
+ org->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
+ org->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
+ org->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+ org->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
+ org->fmt.pix.flags = fmt.fmt.pix_mp.flags;
+
+ /*
+ * If the device doesn't support just one plane, there's
+ * nothing we can do, except return an error condition.
+ */
+ if (fmt.fmt.pix_mp.num_planes > 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+
+ return ret;
+}
+
+static int buf_ioctl(int fd,
+ unsigned long int cmd,
+ struct v4l2_buffer *arg)
+{
+ struct v4l2_buffer buf = *arg;
+ struct v4l2_plane plane = { 0 };
+ int ret;
+
+ buf.type = convert_type(arg->type);
+
+ if (buf.type == arg->type)
+ return SYS_IOCTL(fd, cmd, arg);
+
+ memcpy(&plane.m, &arg->m, sizeof(plane.m));
+ plane.length = arg->length;
+ plane.bytesused = arg->bytesused;
+
+ buf.m.planes = &plane;
+ buf.length = 1;
+
+ ret = SYS_IOCTL(fd, cmd, &buf);
+
+ arg->index = buf.index;
+ arg->memory = buf.memory;
+ arg->flags = buf.flags;
+ arg->field = buf.field;
+ arg->timestamp = buf.timestamp;
+ arg->timecode = buf.timecode;
+ arg->sequence = buf.sequence;
+
+ arg->length = plane.length;
+ arg->bytesused = plane.bytesused;
+ memcpy(&arg->m, &plane.m, sizeof(arg->m));
+
+ return ret;
+}
+
+static int querycap_ioctl(struct media_device *mdev,
+ struct v4l2_capability *arg)
+{
+ int ret;
+
+ ret = SYS_IOCTL(mdev->vid_fd, VIDIOC_QUERYCAP, arg);
+ if (ret < 0)
+ return ret;
+
+ arg->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
+ arg->capabilities |= V4L2_CAP_VIDEO_CAPTURE;
+
+ return ret;
+}
+
+static void *plugin_init(int fd)
+{
+ struct v4l2_capability cap;
+ struct exynos4_camera_plugin plugin, *ret_plugin = NULL;
+ char *media_entity_name;
+ struct media_device *mdev = &plugin.mdev;
+ int ret;
+
+ memset(&plugin, 0, sizeof(plugin));
+
+ memset(&cap, 0, sizeof(cap));
+ ret = SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap);
+ if (ret < 0) {
+ V4L2_EXYNOS4_ERR("Failed to query video capabilities.");
+ return NULL;
+ }
+
+ if (strcmp((char *) cap.driver, EXYNOS4_FIMC_DRV) &&
+ strcmp((char *) cap.driver, EXYNOS4_FIMC_LITE_DRV) &&
+ strcmp((char *) cap.driver, EXYNOS4_FIMC_IS_ISP_DRV)) {
+ V4L2_EXYNOS4_ERR("Not an Exynos4 device.");
+ return NULL;
+ }
+
+ /* Get media node for the device */
+ ret = mdev_get_media_node(mdev, fd, &media_entity_name);
+ if (ret < 0)
+ return NULL;
+
+ /* Check if video entity is of capture type, not m2m */
+ if (!__capture_entity(media_entity_name)) {
+ V4L2_EXYNOS4_ERR("Device not of capture type.");
+ close(mdev->media_fd);
+ return NULL;
+ }
+
+ ret = libv4l2_media_conf_read(EXYNOS4_CAPTURE_CONF, &mdev->config);
+ if (ret < 0) {
+ V4L2_EXYNOS4_ERR("Error reading media device configuration.");
+ close(mdev->media_fd);
+ return NULL;
+ }
+
+ ret = mdev_setup_config_links(mdev, mdev->config.links);
+ /* Release links as they will not be used anymore */
+ libv4l2_media_conf_release_links(mdev->config.links);
+
+ if (ret < 0) {
+ V4L2_EXYNOS4_ERR("Video entities linking failed.");
+ close(mdev->media_fd);
+ return NULL;
+ }
+
+ /* refresh device topology data after linking */
+ mdev_release_entities(mdev);
+
+ ret = mdev_get_device_topology(mdev);
+
+ /* close media device fd as it won't be longer required */
+ close(mdev->media_fd);
+
+ if (ret < 0)
+ goto err_get_dev_topology;
+
+ /* discover a pipeline for the capture device */
+ ret = mdev_discover_pipeline_by_fd(mdev, fd);
+ if (ret < 0) {
+ V4L2_EXYNOS4_ERR("Error discovering video pipeline.");
+ goto err_discover_pipeline;
+ }
+
+ ret = mdev_open_pipeline_subdevs(mdev->pipeline);
+ if (ret < 0) {
+ V4L2_EXYNOS4_ERR("Error opening video pipeline.");
+ goto err_discover_pipeline;
+ }
+
+ if (mdev->config.controls) {
+ ret = mdev_validate_control_config(mdev, mdev->config.controls);
+ if (ret < 0) {
+ V4L2_EXYNOS4_ERR("Error validating control configuration.");
+ goto err_validate_controls;
+ }
+ }
+
+ /* Allocate and initialize private data */
+ ret_plugin = calloc(1, sizeof(*ret_plugin));
+ if (!ret_plugin)
+ goto err_validate_controls;
+
+ mdev->vid_fd = fd;
+
+ V4L2_EXYNOS4_LOG("Initialized exynos4-camera plugin.");
+
+ *ret_plugin = plugin;
+
+ return ret_plugin;
+
+err_validate_controls:
+ mdev_close_pipeline_subdevs(mdev->pipeline);
+err_discover_pipeline:
+ mdev_release_entities(mdev);
+err_get_dev_topology:
+ libv4l2_media_conf_release_controls(mdev->config.controls);
+ return NULL;
+}
+
+static void plugin_close(void *dev_ops_priv)
+{
+ struct exynos4_camera_plugin *plugin;
+ struct media_device *mdev;
+
+ if (dev_ops_priv == NULL)
+ return;
+
+ plugin = (struct exynos4_camera_plugin *) dev_ops_priv;
+ mdev = &plugin->mdev;
+
+ mdev_close_pipeline_subdevs(mdev->pipeline);
+ mdev_release_entities(mdev);
+ libv4l2_media_conf_release_controls(mdev->config.controls);
+
+ free(plugin);
+}
+
+static int plugin_ioctl(void *dev_ops_priv, int fd, unsigned long int cmd,
+ void *arg)
+{
+ struct exynos4_camera_plugin *plugin = dev_ops_priv;
+ struct media_device *mdev;
+
+ if (plugin == NULL || arg == NULL)
+ return -EINVAL;
+
+ mdev = &plugin->mdev;
+
+ if (mdev == NULL)
+ return -EINVAL;
+
+ switch (cmd) {
+ case VIDIOC_S_CTRL:
+ case VIDIOC_G_CTRL:
+ return mdev_ioctl_ctrl(mdev, cmd, arg);
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ return mdev_ioctl_ext_ctrl(mdev, cmd, arg);
+ case VIDIOC_QUERYCTRL:
+ return mdev_ioctl_queryctrl(mdev, arg);
+ case VIDIOC_QUERY_EXT_CTRL:
+ return mdev_ioctl_query_ext_ctrl(mdev, arg);
+ case VIDIOC_QUERYMENU:
+ return mdev_ioctl_querymenu(mdev, arg);
+ case VIDIOC_TRY_FMT:
+ return set_fmt_ioctl(mdev, cmd, arg,
+ V4L2_SUBDEV_FORMAT_TRY);
+ case VIDIOC_S_FMT:
+ return set_fmt_ioctl(mdev, cmd, arg,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ case VIDIOC_G_FMT:
+ return get_fmt_ioctl(mdev->vid_fd, cmd, arg);
+ case VIDIOC_QUERYCAP:
+ return querycap_ioctl(mdev, arg);
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF:
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_PREPARE_BUF:
+ return buf_ioctl(mdev->vid_fd, cmd, arg);
+ case VIDIOC_REQBUFS:
+ return SIMPLE_CONVERT_IOCTL(fd, cmd, arg,
+ v4l2_requestbuffers);
+ case VIDIOC_ENUM_FMT:
+ return SIMPLE_CONVERT_IOCTL(fd, cmd, arg, v4l2_fmtdesc);
+ case VIDIOC_CROPCAP:
+ return SIMPLE_CONVERT_IOCTL(fd, cmd, arg, v4l2_cropcap);
+ case VIDIOC_STREAMON:
+ case VIDIOC_STREAMOFF:
+ {
+ int *arg_type = (int *) arg;
+ int type;
+
+ type = convert_type(*arg_type);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ V4L2_EXYNOS4_ERR("Invalid buffer type.");
+ return -EINVAL;
+ }
+
+ return SYS_IOCTL(fd, cmd, &type);
+ }
+
+ default:
+ return SYS_IOCTL(fd, cmd, arg);
+ }
+}
+
+PLUGIN_PUBLIC const struct libv4l_dev_ops libv4l2_plugin = {
+ .init = &plugin_init,
+ .close = &plugin_close,
+ .ioctl = &plugin_ioctl,
+};
diff --git a/lib/libv4l2/Makefile.am b/lib/libv4l2/Makefile.am
index c60f89b..868135a 100644
--- a/lib/libv4l2/Makefile.am
+++ b/lib/libv4l2/Makefile.am
@@ -2,6 +2,7 @@ if WITH_LIBV4L
lib_LTLIBRARIES = libv4l2.la
include_HEADERS = ../include/libv4l2.h ../include/libv4l-plugin.h
pkgconfig_DATA = libv4l2.pc
+noinst_LTLIBRARIES = libv4l2-mdev.la
LIBV4L2_VERSION = -version-info 0
if WITH_V4L_WRAPPERS
libv4l2priv_LTLIBRARIES = v4l2convert.la
@@ -12,7 +13,7 @@ install-exec-hook:

endif
else
-noinst_LTLIBRARIES = libv4l2.la
+noinst_LTLIBRARIES = libv4l2.la libv4l2-mdev.la
endif

libv4l2_la_SOURCES = libv4l2.c v4l2-plugin.c log.c libv4l2-priv.h
@@ -24,3 +25,5 @@ v4l2convert_la_SOURCES = v4l2convert.c
v4l2convert_la_LIBADD = libv4l2.la
v4l2convert_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
v4l2convert_la_LIBTOOLFLAGS = --tag=disable-static
+
+libv4l2_mdev_la_SOURCES = libv4l2-mdev.c libv4l2-media-conf-parser.c ../include/libv4l2-mdev.h ../include/libv4l-media-conf-parser.h libv4l2-mdev-ioctl.c ../include/libv4l2-mdev-ioctl.h
--
1.7.9.5
Loading...