* [PATCH/RFC v2 0/4] Libv4l: Add a plugin for the Exynos4 camera
@ 2014-10-17 14:54 Jacek Anaszewski
2014-10-17 14:54 ` [PATCH/RFC v2 1/4] Add a media device configuration file parser Jacek Anaszewski
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2014-10-17 14:54 UTC (permalink / raw)
To: linux-media; +Cc: s.nawrocki, b.zolnierkie, kyungmin.park, Jacek Anaszewski
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
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH/RFC v2 1/4] Add a media device configuration file parser.
2014-10-17 14:54 [PATCH/RFC v2 0/4] Libv4l: Add a plugin for the Exynos4 camera Jacek Anaszewski
@ 2014-10-17 14:54 ` Jacek Anaszewski
2014-10-20 21:44 ` Sakari Ailus
2014-10-17 14:54 ` [PATCH/RFC v2 2/4] Add media device related data structures and API Jacek Anaszewski
` (2 subsequent siblings)
3 siblings, 1 reply; 11+ messages in thread
From: Jacek Anaszewski @ 2014-10-17 14:54 UTC (permalink / raw)
To: linux-media
Cc: s.nawrocki, b.zolnierkie, kyungmin.park, Jacek Anaszewski,
Mauro Carvalho Chehab, Hans Verkuil
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 <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Cc: Hans Verkuil <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 <j.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 <j.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
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH/RFC v2 2/4] Add media device related data structures and API.
2014-10-17 14:54 [PATCH/RFC v2 0/4] Libv4l: Add a plugin for the Exynos4 camera Jacek Anaszewski
2014-10-17 14:54 ` [PATCH/RFC v2 1/4] Add a media device configuration file parser Jacek Anaszewski
@ 2014-10-17 14:54 ` Jacek Anaszewski
2014-10-22 10:03 ` Sakari Ailus
2014-10-17 14:54 ` [PATCH/RFC v2 3/4] Add wrappers for media device related ioctl calls Jacek Anaszewski
2014-10-17 14:54 ` [PATCH/RFC v2 4/4] Add a libv4l plugin for Exynos4 camera Jacek Anaszewski
3 siblings, 1 reply; 11+ messages in thread
From: Jacek Anaszewski @ 2014-10-17 14:54 UTC (permalink / raw)
To: linux-media
Cc: s.nawrocki, b.zolnierkie, kyungmin.park, Jacek Anaszewski,
Mauro Carvalho Chehab, Hans Verkuil
Add helpers for retrieving media device topology and manipulating
its configuration.
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Cc: Hans Verkuil <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 <j.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 <j.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
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH/RFC v2 3/4] Add wrappers for media device related ioctl calls.
2014-10-17 14:54 [PATCH/RFC v2 0/4] Libv4l: Add a plugin for the Exynos4 camera Jacek Anaszewski
2014-10-17 14:54 ` [PATCH/RFC v2 1/4] Add a media device configuration file parser Jacek Anaszewski
2014-10-17 14:54 ` [PATCH/RFC v2 2/4] Add media device related data structures and API Jacek Anaszewski
@ 2014-10-17 14:54 ` Jacek Anaszewski
2014-10-17 14:54 ` [PATCH/RFC v2 4/4] Add a libv4l plugin for Exynos4 camera Jacek Anaszewski
3 siblings, 0 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2014-10-17 14:54 UTC (permalink / raw)
To: linux-media
Cc: s.nawrocki, b.zolnierkie, kyungmin.park, Jacek Anaszewski,
Mauro Carvalho Chehab, Hans Verkuil
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 <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Cc: Hans Verkuil <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 <j.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 <j.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
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH/RFC v2 4/4] Add a libv4l plugin for Exynos4 camera
2014-10-17 14:54 [PATCH/RFC v2 0/4] Libv4l: Add a plugin for the Exynos4 camera Jacek Anaszewski
` (2 preceding siblings ...)
2014-10-17 14:54 ` [PATCH/RFC v2 3/4] Add wrappers for media device related ioctl calls Jacek Anaszewski
@ 2014-10-17 14:54 ` Jacek Anaszewski
3 siblings, 0 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2014-10-17 14:54 UTC (permalink / raw)
To: linux-media
Cc: s.nawrocki, b.zolnierkie, kyungmin.park, Jacek Anaszewski,
Mauro Carvalho Chehab, Hans Verkuil
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 <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Cc: Hans Verkuil <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 <j.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
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH/RFC v2 1/4] Add a media device configuration file parser.
2014-10-17 14:54 ` [PATCH/RFC v2 1/4] Add a media device configuration file parser Jacek Anaszewski
@ 2014-10-20 21:44 ` Sakari Ailus
2014-10-21 7:17 ` Jacek Anaszewski
0 siblings, 1 reply; 11+ messages in thread
From: Sakari Ailus @ 2014-10-20 21:44 UTC (permalink / raw)
To: Jacek Anaszewski
Cc: linux-media, s.nawrocki, b.zolnierkie, kyungmin.park,
Mauro Carvalho Chehab, Hans Verkuil, laurent.pinchart
Hi Jacek,
On Fri, Oct 17, 2014 at 04:54:39PM +0200, Jacek Anaszewski wrote:
> 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 <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
> Cc: Hans Verkuil <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 <j.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>
> + * }
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.
> + * 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>
> + * }
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: sakari.ailus@iki.fi XMPP: sailus@retiisi.org.uk
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH/RFC v2 1/4] Add a media device configuration file parser.
2014-10-20 21:44 ` Sakari Ailus
@ 2014-10-21 7:17 ` Jacek Anaszewski
2014-10-21 9:26 ` Sakari Ailus
0 siblings, 1 reply; 11+ messages in thread
From: Jacek Anaszewski @ 2014-10-21 7:17 UTC (permalink / raw)
To: Sakari Ailus
Cc: linux-media, s.nawrocki, b.zolnierkie, kyungmin.park,
Mauro Carvalho Chehab, Hans Verkuil, laurent.pinchart
Hi Sakari,
On 10/20/2014 11:44 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Fri, Oct 17, 2014 at 04:54:39PM +0200, Jacek Anaszewski wrote:
>> 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 <j.anaszewski@samsung.com>
>> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
>> Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
>> Cc: Hans Verkuil <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 <j.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>
>> + * }
>
> Could you use the existing libmediactl format? The parser exists as well.
Of course, I will switch to using it.
> 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.
>
>> + * 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>
>> + * }
>
> 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.
>
> 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.
> 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
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH/RFC v2 1/4] Add a media device configuration file parser.
2014-10-21 7:17 ` Jacek Anaszewski
@ 2014-10-21 9:26 ` Sakari Ailus
2014-10-21 10:11 ` Jacek Anaszewski
0 siblings, 1 reply; 11+ messages in thread
From: Sakari Ailus @ 2014-10-21 9:26 UTC (permalink / raw)
To: Jacek Anaszewski
Cc: linux-media, s.nawrocki, b.zolnierkie, kyungmin.park,
Mauro Carvalho Chehab, Hans Verkuil, laurent.pinchart
Hi Jacek,
On Tue, Oct 21, 2014 at 09:17:00AM +0200, Jacek Anaszewski wrote:
...
> >>+ * 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>
> >>+ * }
> >
> >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.
> >
> >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 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: sakari.ailus@iki.fi XMPP: sailus@retiisi.org.uk
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH/RFC v2 1/4] Add a media device configuration file parser.
2014-10-21 9:26 ` Sakari Ailus
@ 2014-10-21 10:11 ` Jacek Anaszewski
0 siblings, 0 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2014-10-21 10:11 UTC (permalink / raw)
To: Sakari Ailus
Cc: linux-media, s.nawrocki, b.zolnierkie, kyungmin.park,
Mauro Carvalho Chehab, Hans Verkuil, laurent.pinchart
Hi Sakari,
On 10/21/2014 11:26 AM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Tue, Oct 21, 2014 at 09:17:00AM +0200, Jacek Anaszewski wrote:
> ...
>>>> + * 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>
>>>> + * }
>>>
>>> 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 :)
>>>
>>> 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?
>>> 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
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH/RFC v2 2/4] Add media device related data structures and API.
2014-10-17 14:54 ` [PATCH/RFC v2 2/4] Add media device related data structures and API Jacek Anaszewski
@ 2014-10-22 10:03 ` Sakari Ailus
2014-10-22 18:45 ` Laurent Pinchart
0 siblings, 1 reply; 11+ messages in thread
From: Sakari Ailus @ 2014-10-22 10:03 UTC (permalink / raw)
To: Jacek Anaszewski
Cc: linux-media, s.nawrocki, b.zolnierkie, kyungmin.park,
Mauro Carvalho Chehab, Hans Verkuil, laurent.pinchart
Hi Jacek,
On Fri, Oct 17, 2014 at 04:54:40PM +0200, Jacek Anaszewski wrote:
...
> +/*
> + * 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;
> +};
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: sakari.ailus@iki.fi XMPP: sailus@retiisi.org.uk
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH/RFC v2 2/4] Add media device related data structures and API.
2014-10-22 10:03 ` Sakari Ailus
@ 2014-10-22 18:45 ` Laurent Pinchart
0 siblings, 0 replies; 11+ messages in thread
From: Laurent Pinchart @ 2014-10-22 18:45 UTC (permalink / raw)
To: Sakari Ailus
Cc: Jacek Anaszewski, linux-media, s.nawrocki, b.zolnierkie,
kyungmin.park, Mauro Carvalho Chehab, Hans Verkuil
Hi Sakari,
On Wednesday 22 October 2014 13:03:02 Sakari Ailus wrote:
> On Fri, Oct 17, 2014 at 04:54:40PM +0200, Jacek Anaszewski wrote:
> ...
>
> > +/*
> > + * 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;
> > +};
>
> 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
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2014-10-22 20:17 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-10-17 14:54 [PATCH/RFC v2 0/4] Libv4l: Add a plugin for the Exynos4 camera Jacek Anaszewski
2014-10-17 14:54 ` [PATCH/RFC v2 1/4] Add a media device configuration file parser Jacek Anaszewski
2014-10-20 21:44 ` Sakari Ailus
2014-10-21 7:17 ` Jacek Anaszewski
2014-10-21 9:26 ` Sakari Ailus
2014-10-21 10:11 ` Jacek Anaszewski
2014-10-17 14:54 ` [PATCH/RFC v2 2/4] Add media device related data structures and API Jacek Anaszewski
2014-10-22 10:03 ` Sakari Ailus
2014-10-22 18:45 ` Laurent Pinchart
2014-10-17 14:54 ` [PATCH/RFC v2 3/4] Add wrappers for media device related ioctl calls Jacek Anaszewski
2014-10-17 14:54 ` [PATCH/RFC v2 4/4] Add a libv4l plugin for Exynos4 camera Jacek Anaszewski
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.