All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera
@ 2016-10-12 14:35 Jacek Anaszewski
  2016-10-12 14:35 ` [PATCH v4l-utils v7 1/7] mediactl: Add support for v4l2-ctrl-binding config Jacek Anaszewski
                   ` (7 more replies)
  0 siblings, 8 replies; 35+ messages in thread
From: Jacek Anaszewski @ 2016-10-12 14:35 UTC (permalink / raw)
  To: linux-media
  Cc: sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki,
	Jacek Anaszewski

This is a seventh version of the patch series adding a plugin for the 
Exynos4 camera. Last version [0] of the patch set was posted in
January.

The plugin doesn't link against libmediactl, but has its sources
compiled in. Currently utils are built after the plugins, but
libv4l-exynos4-camera plugin depends on the utils. In order to link
the plugin against libmediactl the build system would have to be
modified.

================
Changes from v6:
================

- close v4l2 sub-devices on media device release
- moved non-generic code from libmediactl to the plugin
- resigned from adding libmedia_ioctl library and moved all its
  code to the plugin, since it depended on pipeline representation,
  which was not generic for all possible media device topologies
- used media_get_info()->name instead of adding media_entity_get_name
- renamed media_get_backlinks_by_entity() to media_entity_get_backlinks(()
- moved pipeline from struct media_device to the plugin
- changed the way of associating video device file descriptor with media device
- switched to using auto-generated media-bus-format-names.h header file
- renamed v4l2-ctrl-redir config entry name to v4l2-ctrl-binding

================
Changes from v5:
================

- fixed and tested use cases with S5K6A3 sensor and FIMC-IS-ISP
- added conversion "colorspace id to string"

================
Changes from v4:
================

- removed some redundant functions for traversing media device graph
  and switched over to using existing ones
- avoided accessing struct v4l2_subdev from libmediactl
- applied various improvements

================
Changes from v3:
================

- added struct v4l2_subdev and put entity fd and 
  information about supported controls to it
- improved functions for negotiating and setting
  pipeline format by using available libv4lsubdev API
- applied minor improvements and cleanups

================
Changes from v2:
================

- switched to using mediatext library for parsing
  the media device configuration
- extended libmediactl
- switched to using libmediactl

================
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 v4.8-rc2 (exynos4-is driver doesn't proble properly
with current master branch of linux-media.git) with patches fixing several
issues for Exynos4 camera: [1], [2], [3].

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

Exemplary configuration file for pipeline with sensor
S5C73M3 (rear camera):

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

link-conf "s5p-mipi-csis.0":1 -> "FIMC.0":0 [1]
v4l2-ctrl-binding 0x0098091f -> "fimc.0.capture"
v4l2-ctrl-binding 0x00980902 -> "S5C73M3"
v4l2-ctrl-binding 0x00980922 -> "fimc.0.capture"
v4l2-ctrl-binding 0x009a0914 -> "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 extra-controls="c,rotate=90,color_effects=2" ! video/x-raw,width=960,height=720 ! fbdevsink

Exemplary configuration file for pipeline with sensor
S5K6A3 (front camera):

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

link-conf "s5p-mipi-csis.1":1 -> "FIMC-LITE.1":0 [1]
link-conf "FIMC-LITE.1":2 -> "FIMC-IS-ISP":0 [1]
link-conf "FIMC-IS-ISP":1 -> "FIMC.0":1 [1]

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

gst-launch-1.0 v4l2src device=/dev/video1 extra-controls="c,rotate=270,color_effects=2,horizontal_flip=1" ! video/x-raw,width=960,height=920 ! fbdevsink

Best Regards,
Jacek Anaszewski

[0] http://www.spinics.net/lists/linux-media/msg96510.html
[1] https://patchwork.kernel.org/patch/9335197/
[2] https://patchwork.kernel.org/patch/9270985/
[3] https://patchwork.kernel.org/patch/9308923/
[4] https://patchwork.kernel.org/patch/9335273/


Jacek Anaszewski (7):
  mediactl: Add support for v4l2-ctrl-binding config
  mediatext: Add library
  mediactl: Add media_entity_get_backlinks()
  mediactl: Add media_device creation helpers
  mediactl: libv4l2subdev: Add colorspace logging
  mediactl: libv4l2subdev: add support for comparing mbus formats
  Add a libv4l plugin for Exynos4 camera

 configure.ac                                      |    1 +
 lib/Makefile.am                                   |    5 +
 lib/libv4l-exynos4-camera/Makefile.am             |   19 +
 lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c | 1325 +++++++++++++++++++++
 utils/media-ctl/Makefile.am                       |   10 +-
 utils/media-ctl/libmediactl.c                     |  152 ++-
 utils/media-ctl/libmediatext.pc.in                |   10 +
 utils/media-ctl/libv4l2subdev.c                   |  106 ++
 utils/media-ctl/mediactl.h                        |   42 +
 utils/media-ctl/mediatext-test.c                  |   64 +
 utils/media-ctl/mediatext.c                       |  312 +++++
 utils/media-ctl/mediatext.h                       |   52 +
 utils/media-ctl/v4l2subdev.h                      |   50 +
 13 files changed, 2144 insertions(+), 4 deletions(-)
 create mode 100644 lib/libv4l-exynos4-camera/Makefile.am
 create mode 100644 lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
 create mode 100644 utils/media-ctl/libmediatext.pc.in
 create mode 100644 utils/media-ctl/mediatext-test.c
 create mode 100644 utils/media-ctl/mediatext.c
 create mode 100644 utils/media-ctl/mediatext.h

-- 
1.9.1


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v4l-utils v7 1/7] mediactl: Add support for v4l2-ctrl-binding config
  2016-10-12 14:35 [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera Jacek Anaszewski
@ 2016-10-12 14:35 ` Jacek Anaszewski
  2016-11-24 14:23   ` Sakari Ailus
  2016-10-12 14:35 ` [PATCH v4l-utils v7 2/7] mediatext: Add library Jacek Anaszewski
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-10-12 14:35 UTC (permalink / raw)
  To: linux-media
  Cc: sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki,
	Jacek Anaszewski

Make struct v4l2_subdev capable of aggregating v4l2-ctrl-bindings -
media device configuration entries. Added are also functions for
validating support for the control on given media entity and checking
whether a v4l2-ctrl-binding has been defined for a media entity.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 utils/media-ctl/libv4l2subdev.c | 32 ++++++++++++++++++++++++++++++++
 utils/media-ctl/v4l2subdev.h    | 19 +++++++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c
index c3439d7..4f8ee7f 100644
--- a/utils/media-ctl/libv4l2subdev.c
+++ b/utils/media-ctl/libv4l2subdev.c
@@ -50,7 +50,15 @@ int v4l2_subdev_create(struct media_entity *entity)
 
 	entity->sd->fd = -1;
 
+	entity->sd->v4l2_ctrl_bindings = malloc(sizeof(__u32));
+	if (entity->sd->v4l2_ctrl_bindings == NULL)
+		goto err_v4l2_ctrl_bindings_alloc;
+
 	return 0;
+
+err_v4l2_ctrl_bindings_alloc:
+	free(entity->sd);
+	return -ENOMEM;
 }
 
 int v4l2_subdev_create_opened(struct media_entity *entity, int fd)
@@ -102,6 +110,7 @@ void v4l2_subdev_close(struct media_entity *entity)
 	if (entity->sd->fd_owner)
 		close(entity->sd->fd);
 
+	free(entity->sd->v4l2_ctrl_bindings);
 	free(entity->sd);
 }
 
@@ -884,3 +893,26 @@ const enum v4l2_mbus_pixelcode *v4l2_subdev_pixelcode_list(unsigned int *length)
 
 	return mbus_codes;
 }
+
+int v4l2_subdev_supports_v4l2_ctrl(struct media_device *media,
+				   struct media_entity *entity,
+				   __u32 ctrl_id)
+{
+	struct v4l2_queryctrl queryctrl = {};
+	int ret;
+
+	ret = v4l2_subdev_open(entity);
+	if (ret < 0)
+		return ret;
+
+	queryctrl.id = ctrl_id;
+
+	ret = ioctl(entity->sd->fd, VIDIOC_QUERYCTRL, &queryctrl);
+	if (ret < 0)
+		return ret;
+
+	media_dbg(media, "Validated control \"%s\" (0x%8.8x) on entity %s\n",
+		  queryctrl.name, queryctrl.id, entity->info.name);
+
+	return 0;
+}
diff --git a/utils/media-ctl/v4l2subdev.h b/utils/media-ctl/v4l2subdev.h
index 011fab1..4dee6b1 100644
--- a/utils/media-ctl/v4l2subdev.h
+++ b/utils/media-ctl/v4l2subdev.h
@@ -26,10 +26,14 @@
 
 struct media_device;
 struct media_entity;
+struct media_device;
 
 struct v4l2_subdev {
 	int fd;
 	unsigned int fd_owner:1;
+
+	__u32 *v4l2_ctrl_bindings;
+	unsigned int num_v4l2_ctrl_bindings;
 };
 
 /**
@@ -314,4 +318,19 @@ enum v4l2_field v4l2_subdev_string_to_field(const char *string);
 const enum v4l2_mbus_pixelcode *v4l2_subdev_pixelcode_list(
 	unsigned int *length);
 
+/**
+ * @brief Check if sub-device supports given v4l2 control
+ * @param media - media device.
+ * @param entity - media entity.
+ * @param ctrl_id - id of the v4l2 control to check.
+ *
+ * Verify if the sub-device associated with given media entity
+ * supports v4l2-control with given ctrl_id.
+ *
+ * @return 1 if the control is supported, 0 otherwise.
+ */
+int v4l2_subdev_supports_v4l2_ctrl(struct media_device *device,
+				   struct media_entity *entity,
+				   __u32 ctrl_id);
+
 #endif
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4l-utils v7 2/7] mediatext: Add library
  2016-10-12 14:35 [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera Jacek Anaszewski
  2016-10-12 14:35 ` [PATCH v4l-utils v7 1/7] mediactl: Add support for v4l2-ctrl-binding config Jacek Anaszewski
@ 2016-10-12 14:35 ` Jacek Anaszewski
  2016-11-24 13:01   ` Sakari Ailus
  2016-10-12 14:35 ` [PATCH v4l-utils v7 3/7] mediactl: Add media_entity_get_backlinks() Jacek Anaszewski
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-10-12 14:35 UTC (permalink / raw)
  To: linux-media
  Cc: sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki,
	Jacek Anaszewski, Teemu Tuominen

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 14367 bytes --]

libmediatext is a helper library for converting configurations (Media
controller links, V4L2 controls and V4L2 sub-device media bus formats and
selections) from text-based form into IOCTLs.

libmediatext depends on libv4l2subdev and libmediactl.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Teemu Tuominen <teemu.tuominen@intel.com>
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
---
 utils/media-ctl/Makefile.am        |  10 +-
 utils/media-ctl/libmediatext.pc.in |  10 ++
 utils/media-ctl/mediatext-test.c   |  64 ++++++++
 utils/media-ctl/mediatext.c        | 312 +++++++++++++++++++++++++++++++++++++
 utils/media-ctl/mediatext.h        |  52 +++++++
 5 files changed, 446 insertions(+), 2 deletions(-)
 create mode 100644 utils/media-ctl/libmediatext.pc.in
 create mode 100644 utils/media-ctl/mediatext-test.c
 create mode 100644 utils/media-ctl/mediatext.c
 create mode 100644 utils/media-ctl/mediatext.h

diff --git a/utils/media-ctl/Makefile.am b/utils/media-ctl/Makefile.am
index ee7dcc9..2f12357 100644
--- a/utils/media-ctl/Makefile.am
+++ b/utils/media-ctl/Makefile.am
@@ -1,4 +1,4 @@
-noinst_LTLIBRARIES = libmediactl.la libv4l2subdev.la
+noinst_LTLIBRARIES = libmediactl.la libv4l2subdev.la libmediatext.la
 
 libmediactl_la_SOURCES = libmediactl.c mediactl-priv.h
 libmediactl_la_CFLAGS = -static $(LIBUDEV_CFLAGS)
@@ -21,9 +21,15 @@ libv4l2subdev_la_LIBADD = libmediactl.la
 libv4l2subdev_la_CFLAGS = -static
 libv4l2subdev_la_LDFLAGS = -static
 
+libmediatext_la_SOURCES = mediatext.c
+libmediatext_la_CFLAGS = -static $(LIBUDEV_CFLAGS)
+libmediatext_la_LDFLAGS = -static $(LIBUDEV_LIBS)
+
 mediactl_includedir=$(includedir)/mediactl
 noinst_HEADERS = mediactl.h v4l2subdev.h
 
-bin_PROGRAMS = media-ctl
+bin_PROGRAMS = media-ctl mediatext-test
 media_ctl_SOURCES = media-ctl.c options.c options.h tools.h
 media_ctl_LDADD = libmediactl.la libv4l2subdev.la
+mediatext_test_SOURCES = mediatext-test.c
+mediatext_test_LDADD = libmediatext.la libmediactl.la libv4l2subdev.la
diff --git a/utils/media-ctl/libmediatext.pc.in b/utils/media-ctl/libmediatext.pc.in
new file mode 100644
index 0000000..6aa6353
--- /dev/null
+++ b/utils/media-ctl/libmediatext.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libmediatext
+Description: Media controller and V4L2 text-based configuration library
+Version: @PACKAGE_VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -lmediatext
diff --git a/utils/media-ctl/mediatext-test.c b/utils/media-ctl/mediatext-test.c
new file mode 100644
index 0000000..b8b9282
--- /dev/null
+++ b/utils/media-ctl/mediatext-test.c
@@ -0,0 +1,64 @@
+/*
+ * libmediatext test program
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@linux.intel.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.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mediactl.h"
+#include "mediatext.h"
+
+int main(int argc, char *argv[])
+{
+	struct media_device *device;
+	int rval;
+
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s <media device> <string>\n\n", argv[0]);
+		fprintf(stderr, "\tstring := [ v4l2-ctrl | v4l2-mbus | link-reset | link-conf]\n\n");
+		fprintf(stderr, "\tv4l2-ctrl := \"entity\" ctrl_type ctrl_id ctrl_value\n");
+		fprintf(stderr, "\tctrl_type := [ int | int64 | bitmask ]\n");
+		fprintf(stderr, "\tctrl_value := [ %%d | %%PRId64 | bitmask_value ]\n");
+		fprintf(stderr, "\tbitmask_value := b<binary_number>\n\n");
+		fprintf(stderr, "\tv4l2-mbus := \n");
+		fprintf(stderr, "\tlink-conf := \"entity\":pad -> \"entity\":pad[link-flags]\n");
+		fprintf(stderr, "\tv4l2-ctrl-binding := ctrl_id -> \"entity\"\n");
+		return EXIT_FAILURE;
+	}
+
+	device = media_device_new(argv[1]);
+	if (!device)
+		return EXIT_FAILURE;
+
+	rval = media_device_enumerate(device);
+	if (rval)
+		return EXIT_FAILURE;
+
+	rval = mediatext_parse(device, argv[2]);
+	if (rval) {
+		fprintf(stderr, "bad string %s (%s)\n", argv[2], strerror(-rval));
+		return EXIT_FAILURE;
+	}
+
+	media_device_unref(device);
+
+	return EXIT_SUCCESS;
+}
diff --git a/utils/media-ctl/mediatext.c b/utils/media-ctl/mediatext.c
new file mode 100644
index 0000000..9faf0db
--- /dev/null
+++ b/utils/media-ctl/mediatext.c
@@ -0,0 +1,312 @@
+/*
+ * Media controller text-based configuration library
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@linux.intel.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.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <linux/types.h>
+
+#include "mediactl.h"
+#include "mediactl-priv.h"
+#include "tools.h"
+#include "v4l2subdev.h"
+
+struct parser {
+	char *prefix;
+	int (*parse)(struct media_device *media, const struct parser *p,
+		     char *string);
+	struct parser *next;
+	bool no_args;
+};
+
+static int parse(struct media_device *media, const struct parser *p, char *string)
+{
+	for (; p->prefix; p++) {
+		size_t len = strlen(p->prefix);
+
+		if (strncmp(p->prefix, string, len) || string[len] != ' ')
+			continue;
+
+		string += len;
+
+		for (; isspace(*string); string++);
+
+		if (p->no_args)
+			return p->parse(media, p->next, NULL);
+
+		if (strlen(string) == 0)
+			return -ENOEXEC;
+
+		return p->parse(media, p->next, string);
+	}
+
+	media_dbg(media, "Unknown parser prefix\n");
+
+	return -ENOENT;
+}
+
+struct ctrl_type {
+	uint32_t type;
+	char *str;
+} ctrltypes[] = {
+	{ V4L2_CTRL_TYPE_INTEGER, "int" },
+	{ V4L2_CTRL_TYPE_MENU, "menu" },
+	{ V4L2_CTRL_TYPE_INTEGER_MENU, "intmenu" },
+	{ V4L2_CTRL_TYPE_BITMASK, "bitmask" },
+	{ V4L2_CTRL_TYPE_INTEGER64, "int64" },
+};
+
+static int parse_v4l2_ctrl_id(struct media_device *media, const struct parser *p,
+			      char *string, char **endp, __u32 *ctrl_id)
+{
+	int rval;
+
+	for (; isspace(*string); string++);
+	rval = sscanf(string, "0x%" PRIx32, ctrl_id);
+	if (rval <= 0)
+		return -EINVAL;
+
+	for (; !isspace(*string) && *string; string++);
+	for (; isspace(*string); string++);
+
+	*endp = string;
+
+	return 0;
+}
+
+/* adapted from yavta.c */
+static int parse_v4l2_ctrl(struct media_device *media, const struct parser *p,
+			   char *string)
+{
+	struct v4l2_ext_control ctrl = { 0 };
+	struct v4l2_ext_controls ctrls = { .count = 1,
+					   .controls = &ctrl };
+	int64_t val;
+	int rval;
+	struct media_entity *entity;
+	struct ctrl_type *ctype;
+	unsigned int i;
+
+	entity = media_parse_entity(media, string, &string);
+	if (!entity)
+		return -ENOENT;
+
+	for (i = 0; i < ARRAY_SIZE(ctrltypes); i++)
+		if (!strncmp(string, ctrltypes[i].str,
+			     strlen(ctrltypes[i].str)))
+			break;
+
+	if (i == ARRAY_SIZE(ctrltypes))
+		return -ENOENT;
+
+	ctype = &ctrltypes[i];
+
+	string += strlen(ctrltypes[i].str);
+
+	rval = parse_v4l2_ctrl_id(media, p, string, &string, &ctrl.id);
+	if (rval < 0)
+		return -EINVAL;
+
+	ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(ctrl.id);
+
+	switch (ctype->type) {
+	case V4L2_CTRL_TYPE_BITMASK:
+		if (*string++ != 'b')
+			return -EINVAL;
+		while (*string == '1' || *string == '0') {
+			val <<= 1;
+			if (*string == '1')
+				val++;
+			string++;
+		}
+		break;
+	default:
+		rval = sscanf(string, "%" PRId64, &val);
+		break;
+	}
+	if (rval <= 0)
+		return -EINVAL;
+
+	media_dbg(media, "Setting control 0x%8.8x (type %s), value %" PRId64 "\n",
+		  ctrl.id, ctype->str, val);
+
+	if (ctype->type == V4L2_CTRL_TYPE_INTEGER64)
+		ctrl.value64 = val;
+	else
+		ctrl.value = val;
+
+	rval = v4l2_subdev_open(entity);
+	if (rval < 0)
+		return rval;
+
+	rval = ioctl(entity->sd->fd, VIDIOC_S_EXT_CTRLS, &ctrls);
+	if (ctype->type != V4L2_CTRL_TYPE_INTEGER64) {
+		if (rval != -1) {
+			ctrl.value64 = ctrl.value;
+		} else if (ctype->type != V4L2_CTRL_TYPE_STRING &&
+			   (errno == EINVAL || errno == ENOTTY)) {
+			struct v4l2_control old = { .id = ctrl.id,
+						    .value = val };
+
+			rval = ioctl(entity->sd->fd, VIDIOC_S_CTRL, &old);
+			if (rval != -1)
+				ctrl.value64 = old.value;
+		}
+	}
+	if (rval == -1) {
+		media_dbg(media,
+			  "Failed setting control 0x%8.8x: %s (%d) to value %"
+			  PRId64 "\n", ctrl.id, strerror(errno), errno, val);
+		return -errno;
+	}
+
+	if (val != ctrl.value64)
+		media_dbg(media, "Asking for %" PRId64 ", got %" PRId64 "\n",
+			  val, ctrl.value64);
+
+	return 0;
+}
+
+/*
+
+parse_name(string, end)
+
+*/
+
+int parse_v4l2_ctrl_binding(struct media_device *media, const struct parser *p,
+			    char *string)
+{
+	struct media_entity *entity;
+	struct v4l2_subdev *sd;
+	__u32 ctrl_id;
+	int rval;
+
+	rval = parse_v4l2_ctrl_id(media, p, string, &string, &ctrl_id);
+	if (rval < 0)
+		return -EINVAL;
+
+	for (; isspace(*string); ++string);
+
+	if (string[0] != '-' || string[1] != '>') {
+		media_dbg(media, "Expected '->'\n");
+		return -EINVAL;
+	}
+
+	string += 2;
+
+	entity = media_parse_entity(media, string, &string);
+	if (!entity)
+		return -ENOENT;
+
+	rval = v4l2_subdev_supports_v4l2_ctrl(media, entity, ctrl_id);
+	if (rval < 0) {
+		media_dbg(media,
+			  "Failed to check v4l2 control support for entity %s\n",
+			  entity->info.name);
+		return rval;
+	}
+
+	sd = entity->sd;
+
+	sd->v4l2_ctrl_bindings = realloc(sd->v4l2_ctrl_bindings,
+					 sizeof(*sd->v4l2_ctrl_bindings) *
+					 (sd->num_v4l2_ctrl_bindings + 1));
+	if (!sd->v4l2_ctrl_bindings)
+		return -ENOMEM;
+
+	sd->v4l2_ctrl_bindings[sd->num_v4l2_ctrl_bindings] = ctrl_id;
+	++sd->num_v4l2_ctrl_bindings;
+
+	return 0;
+}
+
+static int parse_v4l2_mbus(struct media_device *media, const struct parser *p,
+			   char *string)
+{
+	media_dbg(media, "Media bus format setup: %s\n", string);
+	return v4l2_subdev_parse_setup_formats(media, string);
+}
+
+static int parse_link_reset(struct media_device *media, const struct parser *p,
+			    char *string)
+{
+	media_dbg(media, "Resetting links\n");
+	return media_reset_links(media);
+}
+
+static int parse_link_conf(struct media_device *media, const struct parser *p,
+			   char *string)
+{
+	media_dbg(media, "Configuring links: %s\n", string);
+	return media_parse_setup_links(media, string);
+}
+
+static const struct parser parsers[] = {
+	{ "v4l2-ctrl", parse_v4l2_ctrl },
+	{ "v4l2-ctrl-binding", parse_v4l2_ctrl_binding },
+	{ "v4l2-mbus", parse_v4l2_mbus },
+	{ "link-reset", parse_link_reset, NULL, true },
+	{ "link-conf", parse_link_conf },
+	{ 0 }
+};
+
+int mediatext_parse(struct media_device *media, char *string)
+{
+	return parse(media, parsers, string);
+}
+
+int mediatext_parse_setup_config(struct media_device *device, const char *conf_path)
+{
+	char *line;
+	size_t n = 0;
+	FILE *f;
+	int ret;
+
+	if (conf_path == NULL)
+		return -EINVAL;
+
+	f = fopen(conf_path, "r");
+	if (!f)
+		return -EINVAL;
+
+	while (getline(&line, &n, f) != -1) {
+		ret = mediatext_parse(device, line);
+		if (ret < 0)
+			goto err_parse;
+		free(line);
+		line = NULL;
+		n = 0;
+	}
+
+err_parse:
+	fclose(f);
+	return ret;
+}
diff --git a/utils/media-ctl/mediatext.h b/utils/media-ctl/mediatext.h
new file mode 100644
index 0000000..7dfbaf6
--- /dev/null
+++ b/utils/media-ctl/mediatext.h
@@ -0,0 +1,52 @@
+/*
+ * Media controller text-based configuration library
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@linux.intel.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.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __MEDIATEXT_H__
+#define __MEDIATEXT_H__
+
+struct media_device;
+
+/**
+ * @brief Parse and apply media device command
+ * @param device - media device
+ * @param string - string to parse
+ *
+ * Parse media device command and apply it to the media device
+ * passed in the device argument.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+int mediatext_parse(struct media_device *device, char *string);
+
+/**
+ * @brief Parse and apply media device configuration
+ * @param media - media device
+ * @param conf_path - path to the configuration file
+ *
+ * Parse the media device commands listed in the file under
+ * conf_path and apply them to the media device passed in the
+ * device argument.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+int mediatext_parse_setup_config(struct media_device *device, const char *conf_path);
+
+#endif /* __MEDIATEXT_H__ */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4l-utils v7 3/7] mediactl: Add media_entity_get_backlinks()
  2016-10-12 14:35 [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera Jacek Anaszewski
  2016-10-12 14:35 ` [PATCH v4l-utils v7 1/7] mediactl: Add support for v4l2-ctrl-binding config Jacek Anaszewski
  2016-10-12 14:35 ` [PATCH v4l-utils v7 2/7] mediatext: Add library Jacek Anaszewski
@ 2016-10-12 14:35 ` Jacek Anaszewski
  2016-11-24 12:40   ` Sakari Ailus
  2016-10-12 14:35 ` [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers Jacek Anaszewski
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-10-12 14:35 UTC (permalink / raw)
  To: linux-media
  Cc: sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki,
	Jacek Anaszewski

Add a new graph helper useful for discovering video pipeline.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 utils/media-ctl/libmediactl.c | 21 +++++++++++++++++++++
 utils/media-ctl/mediactl.h    | 15 +++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c
index 91ed003..155b65f 100644
--- a/utils/media-ctl/libmediactl.c
+++ b/utils/media-ctl/libmediactl.c
@@ -36,6 +36,7 @@
 #include <unistd.h>
 
 #include <linux/media.h>
+#include <linux/kdev_t.h>
 #include <linux/videodev2.h>
 
 #include "mediactl.h"
@@ -172,6 +173,26 @@ const struct media_entity_desc *media_entity_get_info(struct media_entity *entit
 	return &entity->info;
 }
 
+int media_entity_get_backlinks(struct media_entity *entity,
+				struct media_link **backlinks,
+				unsigned int *num_backlinks)
+{
+	unsigned int num_bklinks = 0;
+	int i;
+
+	if (entity == NULL || backlinks == NULL || num_backlinks == NULL)
+		return -EINVAL;
+
+	for (i = 0; i < entity->num_links; ++i)
+		if ((entity->links[i].flags & MEDIA_LNK_FL_ENABLED) &&
+		    (entity->links[i].sink->entity == entity))
+			backlinks[num_bklinks++] = &entity->links[i];
+
+	*num_backlinks = num_bklinks;
+
+	return 0;
+}
+
 /* -----------------------------------------------------------------------------
  * Open/close
  */
diff --git a/utils/media-ctl/mediactl.h b/utils/media-ctl/mediactl.h
index 336cbf9..b1f33cd 100644
--- a/utils/media-ctl/mediactl.h
+++ b/utils/media-ctl/mediactl.h
@@ -434,6 +434,20 @@ int media_parse_setup_link(struct media_device *media,
 int media_parse_setup_links(struct media_device *media, const char *p);
 
 /**
+ * @brief Get entity's enabled backlinks
+ * @param entity - media entity.
+ * @param backlinks - array of pointers to matching backlinks.
+ * @param num_backlinks - number of matching backlinks.
+ *
+ * Get links that are connected to the entity sink pads.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+int media_entity_get_backlinks(struct media_entity *entity,
+				struct media_link **backlinks,
+				unsigned int *num_backlinks);
+
+/**
  * @brief Get v4l2_subdev for the entity
  * @param entity - media entity
  *
@@ -443,4 +457,5 @@ int media_parse_setup_links(struct media_device *media, const char *p);
  */
 struct v4l2_subdev *media_entity_get_v4l2_subdev(struct media_entity *entity);
 
+
 #endif
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers
  2016-10-12 14:35 [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera Jacek Anaszewski
                   ` (2 preceding siblings ...)
  2016-10-12 14:35 ` [PATCH v4l-utils v7 3/7] mediactl: Add media_entity_get_backlinks() Jacek Anaszewski
@ 2016-10-12 14:35 ` Jacek Anaszewski
  2016-11-24 12:17   ` Sakari Ailus
  2016-10-12 14:35 ` [PATCH v4l-utils v7 5/7] mediactl: libv4l2subdev: Add colorspace logging Jacek Anaszewski
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-10-12 14:35 UTC (permalink / raw)
  To: linux-media
  Cc: sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki,
	Jacek Anaszewski

Add helper functions that allow for easy instantiation of media_device
object basing on whether the media device contains v4l2 subdev with
given file descriptor.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 utils/media-ctl/libmediactl.c | 131 +++++++++++++++++++++++++++++++++++++++++-
 utils/media-ctl/mediactl.h    |  27 +++++++++
 2 files changed, 156 insertions(+), 2 deletions(-)

diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c
index 155b65f..d347a40 100644
--- a/utils/media-ctl/libmediactl.c
+++ b/utils/media-ctl/libmediactl.c
@@ -27,6 +27,7 @@
 #include <sys/sysmacros.h>
 
 #include <ctype.h>
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdbool.h>
@@ -440,8 +441,9 @@ static int media_get_devname_udev(struct udev *udev,
 		return -EINVAL;
 
 	devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor);
-	media_dbg(entity->media, "looking up device: %u:%u\n",
-		  major(devnum), minor(devnum));
+	if (entity->media)
+		media_dbg(entity->media, "looking up device: %u:%u\n",
+			  major(devnum), minor(devnum));
 	device = udev_device_new_from_devnum(udev, 'c', devnum);
 	if (device) {
 		p = udev_device_get_devnode(device);
@@ -523,6 +525,7 @@ static int media_get_devname_sysfs(struct media_entity *entity)
 	return 0;
 }
 
+
 static int media_enum_entities(struct media_device *media)
 {
 	struct media_entity *entity;
@@ -707,6 +710,92 @@ struct media_device *media_device_new(const char *devnode)
 	return media;
 }
 
+struct media_device *media_device_new_by_subdev_fd(int fd, struct media_entity **fd_entity)
+{
+	char video_devname[32], device_dir_path[256], media_dev_path[256], media_major_minor[10];
+	struct media_device *media = NULL;
+	struct dirent *entry;
+	struct media_entity tmp_entity;
+	DIR *device_dir;
+	struct udev *udev;
+	char *p;
+	int ret, i;
+
+	if (fd_entity == NULL)
+		return NULL;
+
+	ret = media_get_devname_by_fd(fd, video_devname);
+	if (ret < 0)
+		return NULL;
+
+	p = strrchr(video_devname, '/');
+	if (p == NULL)
+		return NULL;
+
+	ret = media_udev_open(&udev);
+	if (ret < 0)
+		return NULL;
+
+	sprintf(device_dir_path, "/sys/class/video4linux/%s/device/", p + 1);
+
+	device_dir = opendir(device_dir_path);
+	if (device_dir == NULL)
+		return NULL;
+
+	while ((entry = readdir(device_dir))) {
+		if (strncmp(entry->d_name, "media", 4))
+			continue;
+
+		sprintf(media_dev_path, "%s%s/dev", device_dir_path, entry->d_name);
+
+		fd = open(media_dev_path, O_RDONLY);
+		if (fd < 0)
+			continue;
+
+		ret = read(fd, media_major_minor, sizeof(media_major_minor));
+		if (ret < 0)
+			continue;
+
+		sscanf(media_major_minor, "%d:%d", &tmp_entity.info.dev.major, &tmp_entity.info.dev.minor);
+
+		/* Try to get the device name via udev */
+		if (media_get_devname_udev(udev, &tmp_entity)) {
+			/* Fall back to get the device name via sysfs */
+			if (media_get_devname_sysfs(&tmp_entity))
+				continue;
+		}
+
+		media = media_device_new(tmp_entity.devname);
+		if (media == NULL)
+			continue;
+
+		ret = media_device_enumerate(media);
+		if (ret < 0) {
+			media_dbg(media, "Failed to enumerate %s (%d)\n",
+				  tmp_entity.devname, ret);
+			media_device_unref(media);
+			media = NULL;
+			continue;
+		}
+
+		/* Get the entity associated with given fd */
+		for (i = 0; i < media->entities_count; i++) {
+			struct media_entity *entity = &media->entities[i];
+
+			if (!strcmp(entity->devname, video_devname)) {
+				*fd_entity = &media->entities[i];
+				break;
+			}
+		}
+
+		break;
+	}
+
+	media_udev_close(udev);
+
+	return media;
+}
+
 struct media_device *media_device_new_emulated(struct media_device_info *info)
 {
 	struct media_device *media;
@@ -748,6 +837,44 @@ void media_device_unref(struct media_device *media)
 	free(media);
 }
 
+int media_get_devname_by_fd(int fd, char *node_name)
+{
+	struct udev *udev;
+	struct media_entity tmp_entity;
+	struct stat stat;
+	int ret, ret_udev;
+
+	if (node_name == NULL)
+		return -EINVAL;
+
+	ret = fstat(fd, &stat);
+	if (ret < 0)
+		return -errno;
+
+	tmp_entity.info.v4l.major = MAJOR(stat.st_rdev);
+	tmp_entity.info.v4l.minor = MINOR(stat.st_rdev);
+
+	ret_udev = media_udev_open(&udev);
+	if (ret_udev < 0)
+		printf("Can't get udev context\n");
+
+	/* Try to get the device name via udev */
+	ret = media_get_devname_udev(udev, &tmp_entity);
+	if (!ret)
+		goto out;
+
+	ret = media_get_devname_sysfs(&tmp_entity);
+	if (ret < 0)
+		goto err_get_devname;
+
+out:
+	strncpy(node_name, tmp_entity.devname, sizeof(tmp_entity.devname));
+err_get_devname:
+	if (!ret_udev)
+		media_udev_close(udev);
+	return ret;
+}
+
 int media_device_add_entity(struct media_device *media,
 			    const struct media_entity_desc *desc,
 			    const char *devnode)
diff --git a/utils/media-ctl/mediactl.h b/utils/media-ctl/mediactl.h
index b1f33cd..580a25a 100644
--- a/utils/media-ctl/mediactl.h
+++ b/utils/media-ctl/mediactl.h
@@ -76,6 +76,21 @@ struct media_device *media_device_new(const char *devnode);
 struct media_device *media_device_new_emulated(struct media_device_info *info);
 
 /**
+ * @brief Create a new media device contatning entity associated with v4l2 subdev fd.
+ * @param fd - file descriptor of a v4l2 subdev.
+ * @param fd_entity - media entity associated with the v4l2 subdev.
+ *
+ * Create a representation of the media device referenced by the v4l2-subdev.
+ * The media device instance is initialized with enumerated entities and links.
+ *
+ * Media devices are reference-counted, see media_device_ref() and
+ * media_device_unref() for more information.
+ *
+ * @return A pointer to the new media device or NULL if error occurred.
+ */
+struct media_device *media_device_new_by_subdev_fd(int fd, struct media_entity **fd_entity);
+
+/**
  * @brief Take a reference to the device.
  * @param media - device instance.
  *
@@ -231,6 +246,18 @@ const struct media_link *media_entity_get_link(struct media_entity *entity,
 const char *media_entity_get_devname(struct media_entity *entity);
 
 /**
+ * @brief Get the device node name by its file descriptor
+ * @param fd - file descriptor of a device.
+ * @param node_name - output device node name string.
+ *
+ * This function returns the full path and name to the device node corresponding
+ * to the given file descriptor.
+ *
+ * @return 0 on success, or a negative error code on failure.
+ */
+int media_get_devname_by_fd(int fd, char *node_name);
+
+/**
  * @brief Get the type of an entity.
  * @param entity - the entity.
  *
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4l-utils v7 5/7] mediactl: libv4l2subdev: Add colorspace logging
  2016-10-12 14:35 [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera Jacek Anaszewski
                   ` (3 preceding siblings ...)
  2016-10-12 14:35 ` [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers Jacek Anaszewski
@ 2016-10-12 14:35 ` Jacek Anaszewski
  2016-11-24 12:28   ` Sakari Ailus
  2016-10-12 14:35 ` [PATCH v4l-utils v7 6/7] mediactl: libv4l2subdev: add support for comparing mbus formats Jacek Anaszewski
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-10-12 14:35 UTC (permalink / raw)
  To: linux-media
  Cc: sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki,
	Jacek Anaszewski

Add a function for obtaining colorspace name by id.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 utils/media-ctl/libv4l2subdev.c | 32 ++++++++++++++++++++++++++++++++
 utils/media-ctl/v4l2subdev.h    | 10 ++++++++++
 2 files changed, 42 insertions(+)

diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c
index 4f8ee7f..31393bb 100644
--- a/utils/media-ctl/libv4l2subdev.c
+++ b/utils/media-ctl/libv4l2subdev.c
@@ -33,6 +33,7 @@
 #include <unistd.h>
 
 #include <linux/v4l2-subdev.h>
+#include <linux/videodev2.h>
 
 #include "mediactl.h"
 #include "mediactl-priv.h"
@@ -831,6 +832,37 @@ const char *v4l2_subdev_pixelcode_to_string(enum v4l2_mbus_pixelcode code)
 	return "unknown";
 }
 
+static struct {
+	const char *name;
+	enum v4l2_colorspace cs;
+} colorspaces[] = {
+        { "DEFAULT", V4L2_COLORSPACE_DEFAULT },
+        { "SMPTE170M", V4L2_COLORSPACE_SMPTE170M },
+        { "SMPTE240M", V4L2_COLORSPACE_SMPTE240M },
+        { "REC709", V4L2_COLORSPACE_REC709 },
+        { "BT878", V4L2_COLORSPACE_BT878 },
+        { "470_SYSTEM_M", V4L2_COLORSPACE_470_SYSTEM_M },
+        { "470_SYSTEM_BG", V4L2_COLORSPACE_470_SYSTEM_BG },
+        { "JPEG", V4L2_COLORSPACE_JPEG },
+        { "SRGB", V4L2_COLORSPACE_SRGB },
+        { "ADOBERGB", V4L2_COLORSPACE_ADOBERGB },
+        { "BT2020", V4L2_COLORSPACE_BT2020 },
+        { "RAW", V4L2_COLORSPACE_RAW },
+        { "DCI_P3", V4L2_COLORSPACE_DCI_P3 },
+};
+
+const char *v4l2_subdev_colorspace_to_string(enum v4l2_colorspace cs)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(colorspaces); ++i) {
+		if (colorspaces[i].cs == cs)
+			return colorspaces[i].name;
+	}
+
+	return "unknown";
+}
+
 enum v4l2_mbus_pixelcode v4l2_subdev_string_to_pixelcode(const char *string)
 {
 	unsigned int i;
diff --git a/utils/media-ctl/v4l2subdev.h b/utils/media-ctl/v4l2subdev.h
index 4dee6b1..cf1250d 100644
--- a/utils/media-ctl/v4l2subdev.h
+++ b/utils/media-ctl/v4l2subdev.h
@@ -278,6 +278,16 @@ int v4l2_subdev_parse_setup_formats(struct media_device *media, const char *p);
 const char *v4l2_subdev_pixelcode_to_string(enum v4l2_mbus_pixelcode code);
 
 /**
+ * @brief Convert colorspace to string.
+ * @param code - input string
+ *
+ * Convert colorspace @a to a human-readable string.
+ *
+ * @return A pointer to a string on success, NULL on failure.
+ */
+const char *v4l2_subdev_colorspace_to_string(enum v4l2_colorspace cs);
+
+/**
  * @brief Parse string to media bus pixel code.
  * @param string - nul terminalted string, textual media bus pixel code
  *
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4l-utils v7 6/7] mediactl: libv4l2subdev: add support for comparing mbus formats
  2016-10-12 14:35 [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera Jacek Anaszewski
                   ` (4 preceding siblings ...)
  2016-10-12 14:35 ` [PATCH v4l-utils v7 5/7] mediactl: libv4l2subdev: Add colorspace logging Jacek Anaszewski
@ 2016-10-12 14:35 ` Jacek Anaszewski
  2016-11-24 14:36   ` Sakari Ailus
  2016-10-12 14:35 ` [PATCH v4l-utils v7 7/7] Add a libv4l plugin for Exynos4 camera Jacek Anaszewski
  2016-11-03 11:51 ` [PATCH v4l-utils v7 0/7] Add a " Hans Verkuil
  7 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-10-12 14:35 UTC (permalink / raw)
  To: linux-media
  Cc: sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki,
	Jacek Anaszewski

This patch adds a function for checking whether two mbus formats
are compatible.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 utils/media-ctl/libv4l2subdev.c | 42 +++++++++++++++++++++++++++++++++++++++++
 utils/media-ctl/v4l2subdev.h    | 21 +++++++++++++++++++++
 2 files changed, 63 insertions(+)

diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c
index 31393bb..2ec9b5e 100644
--- a/utils/media-ctl/libv4l2subdev.c
+++ b/utils/media-ctl/libv4l2subdev.c
@@ -948,3 +948,45 @@ int v4l2_subdev_supports_v4l2_ctrl(struct media_device *media,
 
 	return 0;
 }
+
+enum v4l2_subdev_fmt_mismatch v4l2_subdev_format_compare(
+				struct v4l2_mbus_framefmt *fmt1,
+				struct v4l2_mbus_framefmt *fmt2)
+{
+	if (fmt1 == NULL || fmt2 == NULL)
+		return 0;
+
+	if (fmt1->width != fmt2->width) {
+		printf("width mismatch (fmt1: %d, fmt2: %d)\n",
+		       fmt1->width, fmt2->width);
+		return FMT_MISMATCH_WIDTH;
+	}
+
+	if (fmt1->height != fmt2->height) {
+		printf("height mismatch (fmt1: %d, fmt2: %d)\n",
+		       fmt1->height, fmt2->height);
+		return FMT_MISMATCH_HEIGHT;
+	}
+
+	if (fmt1->code != fmt2->code) {
+		printf("mbus code mismatch (fmt1: %s, fmt2: %s)\n",
+			v4l2_subdev_pixelcode_to_string(fmt1->code),
+			v4l2_subdev_pixelcode_to_string(fmt2->code));
+		return FMT_MISMATCH_CODE;
+	}
+
+	if (fmt1->field != fmt2->field) {
+		printf("field mismatch (fmt1: %d, fmt2: %d)\n",
+		       fmt1->field, fmt2->field);
+		return FMT_MISMATCH_FIELD;
+	}
+
+	if (fmt1->colorspace != fmt2->colorspace) {
+		printf("colorspace mismatch (fmt1: %s, fmt2: %s)\n",
+			v4l2_subdev_colorspace_to_string(fmt1->colorspace),
+			v4l2_subdev_colorspace_to_string(fmt2->colorspace));
+		return FMT_MISMATCH_COLORSPACE;
+	}
+
+	return FMT_MISMATCH_NONE;
+}
diff --git a/utils/media-ctl/v4l2subdev.h b/utils/media-ctl/v4l2subdev.h
index cf1250d..c438f71 100644
--- a/utils/media-ctl/v4l2subdev.h
+++ b/utils/media-ctl/v4l2subdev.h
@@ -28,6 +28,15 @@ struct media_device;
 struct media_entity;
 struct media_device;
 
+enum v4l2_subdev_fmt_mismatch {
+	FMT_MISMATCH_NONE = 0,
+	FMT_MISMATCH_WIDTH,
+	FMT_MISMATCH_HEIGHT,
+	FMT_MISMATCH_CODE,
+	FMT_MISMATCH_FIELD,
+	FMT_MISMATCH_COLORSPACE,
+};
+
 struct v4l2_subdev {
 	int fd;
 	unsigned int fd_owner:1;
@@ -342,5 +351,17 @@ const enum v4l2_mbus_pixelcode *v4l2_subdev_pixelcode_list(
 int v4l2_subdev_supports_v4l2_ctrl(struct media_device *device,
 				   struct media_entity *entity,
 				   __u32 ctrl_id);
+/**
+ * @brief Compare mbus formats
+ * @param fmt1 - 1st mbus format to compare.
+ * @param fmt2 - 2nd mbus format to compare.
+ *
+ * Check whether two mbus formats are compatible.
+ *
+ * @return 1 if formats are compatible, 0 otherwise.
+ */
+enum v4l2_subdev_fmt_mismatch v4l2_subdev_format_compare(
+				struct v4l2_mbus_framefmt *fmt1,
+				struct v4l2_mbus_framefmt *fmt2);
 
 #endif
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v4l-utils v7 7/7] Add a libv4l plugin for Exynos4 camera
  2016-10-12 14:35 [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera Jacek Anaszewski
                   ` (5 preceding siblings ...)
  2016-10-12 14:35 ` [PATCH v4l-utils v7 6/7] mediactl: libv4l2subdev: add support for comparing mbus formats Jacek Anaszewski
@ 2016-10-12 14:35 ` Jacek Anaszewski
  2016-11-24 15:11   ` Sakari Ailus
  2016-11-03 11:51 ` [PATCH v4l-utils v7 0/7] Add a " Hans Verkuil
  7 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-10-12 14:35 UTC (permalink / raw)
  To: linux-media
  Cc: sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki,
	Jacek Anaszewski

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>
---
 configure.ac                                      |    1 +
 lib/Makefile.am                                   |    5 +
 lib/libv4l-exynos4-camera/Makefile.am             |   19 +
 lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c | 1325 +++++++++++++++++++++
 4 files changed, 1350 insertions(+)
 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 8447f05..692718a 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 a105c95..b5e52db 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -5,6 +5,11 @@ SUBDIRS = \
 	libv4l2rds \
 	libv4l-mplane
 
+if WITH_V4LUTILS
+SUBDIRS += \
+	libv4l-exynos4-camera
+endif
+
 if WITH_LIBDVBV5
 SUBDIRS += \
 	libdvbv5
diff --git a/lib/libv4l-exynos4-camera/Makefile.am b/lib/libv4l-exynos4-camera/Makefile.am
new file mode 100644
index 0000000..c38b7f6
--- /dev/null
+++ b/lib/libv4l-exynos4-camera/Makefile.am
@@ -0,0 +1,19 @@
+if WITH_V4L_PLUGINS
+libv4l2plugin_LTLIBRARIES = libv4l-exynos4-camera.la
+endif
+
+media-bus-format-names.h: ../../include/linux/media-bus-format.h
+	sed -e '/#define MEDIA_BUS_FMT/ ! d; s/.*FMT_//; /FIXED/ d; s/\t.*//; s/.*/{ \"&\", MEDIA_BUS_FMT_& },/;' \
+	< $< > $@
+
+media-bus-format-codes.h: ../../include/linux/media-bus-format.h
+	sed -e '/#define MEDIA_BUS_FMT/ ! d; s/.*#define //; /FIXED/ d; s/\t.*//; s/.*/ &,/;' \
+	< $< > $@
+
+BUILT_SOURCES = media-bus-format-names.h media-bus-format-codes.h
+CLEANFILES = $(BUILT_SOURCES)
+
+nodist_libv4l_exynos4_camera_la_SOURCES = $(BUILT_SOURCES)
+libv4l_exynos4_camera_la_SOURCES = libv4l-exynos4-camera.c ../../utils/media-ctl/libmediactl.c ../../utils/media-ctl/libv4l2subdev.c ../../utils/media-ctl/mediatext.c
+libv4l_exynos4_camera_la_CFLAGS = -fvisibility=hidden -std=gnu99
+libv4l_exynos4_camera_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -lpthread
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..c219fe5
--- /dev/null
+++ b/lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
@@ -0,0 +1,1325 @@
+/*
+ * Copyright (c) 2016 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 <linux/types.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "../../utils/media-ctl/mediactl.h"
+#include "../../utils/media-ctl/mediatext.h"
+#include "../../utils/media-ctl/v4l2subdev.h"
+#include "libv4l-plugin.h"
+
+#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)
+
+#define VIDIOC_CTRL(type)				\
+	((type) == VIDIOC_S_CTRL ? "VIDIOC_S_CTRL" :	\
+				   "VIDIOC_G_CTRL")
+
+#if HAVE_VISIBILITY
+#define PLUGIN_PUBLIC __attribute__ ((visibility("default")))
+#else
+#define PLUGIN_PUBLIC
+#endif
+
+#define SYS_IOCTL(fd, cmd, arg) \
+	syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg))
+
+#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;                                          \
+	})
+
+#ifndef min
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#define EXYNOS4_FIMC_DRV	"exynos4-fimc"
+#define EXYNOS4_FIMC_LITE_DRV	"exynos-fimc-lit"
+#define EXYNOS4_FIMC_IS_ISP_DRV	"exynos4-fimc-is"
+#define EXYNOS4_CAPTURE_CONF	"/var/lib/libv4l/exynos4_capture_conf"
+#define EXYNOS4_FIMC_IS_ISP	"FIMC-IS-ISP"
+#define EXYNOS4_S5K6A3		"S5K6A3"
+#define EXYNOS4_FIMC_LITE_PREFIX "FIMC-LITE."
+#define EXYNOS4_FIMC_PREFIX	"FIMC."
+#define EXYNOS4_MAX_FMT_NEGO_NUM 50
+#define EXYNOS4_MAX_PIPELINE_LEN 7
+
+struct media_device;
+struct media_entity;
+
+/*
+ * struct pipeline_entity - linked media device pipeline
+ * @entity:		linked entity
+ * @sink_pad:		inbound link pad of the entity
+ * @src_pad:		outbound link pad of the entity
+ */
+struct pipeline_entity {
+	struct media_entity *entity;
+	struct media_pad *sink_pad;
+	struct media_pad *src_pad;
+};
+
+/*
+ * struct media_entity_to_cid - entity to control map
+ * @entity:		media entity
+ * @sink_pad:		inbound link pad of the entity
+ * @src_pad:		outbound link pad of the entity
+ */
+struct media_entity_to_cid {
+	struct media_entity *entity;
+	union {
+		struct v4l2_queryctrl queryctrl;
+		struct v4l2_query_ext_ctrl query_ext_ctrl;
+	};
+};
+
+/*
+ * struct exynos4_camera_plugin - libv4l exynos4 camera plugin
+ * @media:		media device comprising the opened video device
+ * @pipeline:		video pipeline, element 0 is the source entity
+ * @pipeline_len:	length of the video pipeline
+ */
+struct exynos4_camera_plugin {
+	struct media_device *media;
+	struct pipeline_entity pipeline[EXYNOS4_MAX_PIPELINE_LEN];
+	unsigned int pipeline_len;
+};
+
+/* -----------------------------------------------------------------------------
+ * Pipeline operations
+ */
+
+/**
+ * @brief Discover video pipeline for given sink entity
+ * @param plugin - this plugin.
+ * @param entity - sink entity of the pipeline.
+ *
+ * Discover the video pipeline, by walking starting from the
+ * sink entity upwards until a source entity is encountered.
+ *
+ * @return 0 if the sensor entity was detected,
+ * 	   or negative error code on failure.
+ */
+static int discover_pipeline_by_entity(struct exynos4_camera_plugin *plugin,
+				       struct media_entity *entity)
+{
+	struct pipeline_entity reverse_pipeline[EXYNOS4_MAX_PIPELINE_LEN];
+	struct media_pad *src_pad;
+	struct media_link *link = NULL, *backlinks[2];
+	unsigned int num_backlinks, cur_pipe_pos = 0;
+	int i, j;
+	int ret;
+
+	if (entity == NULL)
+		return -EINVAL;
+
+	for (;;) {
+		/* Cache the recently discovered entity. */
+		reverse_pipeline[cur_pipe_pos].entity = entity;
+
+		/* Cache the source pad used for linking the entity. */
+		if (link)
+			reverse_pipeline[cur_pipe_pos].src_pad = link->source;
+
+		ret = media_entity_get_backlinks(entity, backlinks,
+						 &num_backlinks);
+		if (ret < 0)
+			return ret;
+
+		/* Check if pipeline source entity has been reached. */
+		if (num_backlinks > 2) {
+			V4L2_EXYNOS4_DBG("Unexpected number of busy sink pads (%d)",
+					 num_backlinks);
+			return -EINVAL;
+		} else if (num_backlinks == 2) {
+			/*
+			 * Allow two active pads only in case of
+			 * S5C73M3-OIF entity.
+			 */
+			if (strcmp(media_entity_get_info(entity)->name,
+				   "S5C73M3-OIF")) {
+				V4L2_EXYNOS4_DBG("Ambiguous media device topology: "
+						 "two busy sink pads");
+				return -EINVAL;
+			}
+			/*
+			 * Two active links are allowed betwen S5C73M3-OIF and
+			 * S5C73M3 entities. In such a case route through the
+			 * pad with id == 0 has to be chosen.
+			 */
+			link = NULL;
+			for (i = 0; i < num_backlinks; i++)
+				if (backlinks[i]->sink->index == 0)
+					link = backlinks[i];
+			if (link == NULL)
+				return -EINVAL;
+		} else if (num_backlinks == 1) {
+			link = backlinks[0];
+		} else {
+			reverse_pipeline[cur_pipe_pos].sink_pad = NULL;
+			break;
+		}
+
+		/* Cache the sink pad used for linking the entity. */
+		reverse_pipeline[cur_pipe_pos].sink_pad = link->sink;
+
+		V4L2_EXYNOS4_DBG("Discovered sink pad %d for the %s entity",
+				 reverse_pipeline[cur_pipe_pos].sink_pad->index,
+				 media_entity_get_info(entity)->name);
+
+		src_pad = media_entity_remote_source(link->sink);
+		if (!src_pad)
+			return -EINVAL;
+
+		entity = src_pad->entity;
+		if (++cur_pipe_pos == EXYNOS4_MAX_PIPELINE_LEN)
+			return -EINVAL;
+	}
+
+	/*
+	 * Reorder discovered pipeline elements so that the sensor
+	 * entity was the pipeline head.
+	 */
+	j = 0;
+	for (i = cur_pipe_pos; i >= 0; i--)
+		plugin->pipeline[j++] = reverse_pipeline[i];
+
+	plugin->pipeline_len = j;
+
+	return 0;
+}
+
+/**
+ * @brief Check if the entity belongs to the plugin video pipeline
+ * @param plugin - this plugin.
+ * @param entity_name - name of the entity to look for.
+ *
+ * Check if the entity belongs to the pipeline whose sink element
+ * is the video device represented by the file descriptor passed
+ * to plugin_init().
+ *
+ * @return True if the entity belongs to the pipeline,
+ *         and false otherwise.
+ */
+static bool has_pipeline_entity(struct exynos4_camera_plugin *plugin,
+				char *entity_name)
+{
+	struct pipeline_entity *pipeline = plugin->pipeline;
+	struct media_entity *entity;
+	unsigned int i;
+
+	if (pipeline == NULL || entity_name == NULL)
+		return -EINVAL;
+
+	for (i = 0; i < plugin->pipeline_len; i++) {
+		entity = pipeline[i].entity;
+		if (!strncmp(media_entity_get_info(entity)->name, entity_name,
+			     strlen(entity_name)))
+			return true;
+	}
+
+	return false;
+}
+
+/**
+ * @brief Adjust mbus format to FIMC_IS_ISP limitations.
+ * @param mbus_fmt - format to adjust.
+ *
+ * FIMC_IS_ISP shears the video frame off, effectively the format set
+ * on the preceding entities has to be suitably greater to achieve
+ * the frame size requested by the user.
+ *
+ * Also the miminum frame size supported by FIMC_IS_ISP is 128x128.
+ */
+static void adjust_format_to_fimc_is_isp(struct v4l2_mbus_framefmt *mbus_fmt)
+{
+	mbus_fmt->width = max(128, mbus_fmt->width);
+	mbus_fmt->height = max(128, mbus_fmt->height);
+
+	mbus_fmt->width += 16;
+	mbus_fmt->height += 12;
+}
+
+/**
+ * @brief Negotiate format acceptable for all pipeline entities.
+ * @param plugin - this plugin.
+ * @param dev_fmt - requested video device format.
+ *
+ * Negotiate common format acceptable by all the pipeline entities,
+ * that is closest to the requested one.
+ *
+ * @return 0 if the negotiation succeeded,
+ * 	   or negative error code on failure.
+ */
+static int negotiate_pipeline_fmt(struct exynos4_camera_plugin *plugin,
+				  struct v4l2_format *dev_fmt)
+{
+	struct v4l2_mbus_framefmt mbus_fmt = { 0 }, common_fmt;
+	int repeat_negotiation, cnt_negotiation = 0, ret, pad_id, i;
+	struct pipeline_entity *pipeline = plugin->pipeline;
+	enum v4l2_subdev_fmt_mismatch fmt_err;
+	struct media_entity *entity;
+
+	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;
+
+	if (has_pipeline_entity(plugin, EXYNOS4_FIMC_IS_ISP))
+		adjust_format_to_fimc_is_isp(&mbus_fmt);
+
+	V4L2_EXYNOS4_DBG("Begin pipeline format negotiation...");
+
+	for (;;) {
+		repeat_negotiation = 0;
+		entity = pipeline[0].entity;
+
+		pad_id = pipeline[0].src_pad->index;
+
+		V4L2_EXYNOS4_DBG("Setting format on the source entity pad %s:%d",
+				 media_entity_get_info(entity)->name, pad_id);
+
+		ret = v4l2_subdev_set_format(entity, &mbus_fmt,
+					     pad_id, V4L2_SUBDEV_FORMAT_TRY);
+		if (ret < 0)
+			return ret;
+
+		V4L2_EXYNOS4_DBG("Format set on the pad %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
+				 media_entity_get_info(entity)->name, pad_id,
+				 v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
+				 v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
+				 mbus_fmt.width, mbus_fmt.height);
+
+		common_fmt = mbus_fmt;
+
+		/* Stop iterating on last but one entity as it is not a sub-device. */
+		for (i = 1; i < plugin->pipeline_len - 1; i++) {
+			entity = pipeline[i].entity;
+
+			pad_id = pipeline[i].sink_pad->index;
+
+			V4L2_EXYNOS4_DBG("Setting format on the pad %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
+					 media_entity_get_info(entity)->name, pad_id,
+					 v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
+					 v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
+					 mbus_fmt.width, mbus_fmt.height);
+
+			/* Set format on the entity sink pad. */
+			ret = v4l2_subdev_set_format(entity, &mbus_fmt, pad_id,
+							V4L2_SUBDEV_FORMAT_TRY);
+			if (ret < 0)
+				return ret;
+
+			fmt_err = v4l2_subdev_format_compare(&mbus_fmt, &common_fmt);
+			if (fmt_err) {
+				if (fmt_err == FMT_MISMATCH_WIDTH &&
+				     !strncmp(media_entity_get_info(entity)->name,
+					      EXYNOS4_FIMC_LITE_PREFIX,
+					      strlen(EXYNOS4_FIMC_LITE_PREFIX))) {
+					/*
+					 * Align width downwards, according to the FIMC-LITE
+					 * width step. Otherwise pipeline format negotiation
+					 * wouldn't succeed for widths excessing maximum sensor
+					 * frame width, which is probed by GStreamer, no matter
+					 * what actual frame size is to be set.
+					 */
+					mbus_fmt.width -= 8;
+				}
+				repeat_negotiation = 1;
+				break;
+			}
+
+			/*
+			 * Do not check format on FIMC.[n] source pad
+			 * and stop negotiation.
+			 */
+			if (!strncmp(media_entity_get_info(entity)->name,
+				     EXYNOS4_FIMC_PREFIX,
+				     strlen(EXYNOS4_FIMC_PREFIX)))
+				break;
+
+			pad_id = pipeline[i].src_pad->index;
+
+			/* Get format on the entity src pad */
+			ret = v4l2_subdev_get_format(entity, &mbus_fmt, pad_id,
+							V4L2_SUBDEV_FORMAT_TRY);
+			if (ret < 0)
+				return -EINVAL;
+
+			V4L2_EXYNOS4_DBG("Format propagated to the pad %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
+					 media_entity_get_info(entity)->name, pad_id,
+					 v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
+					 v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
+					 mbus_fmt.width, mbus_fmt.height);
+
+			if (!strcmp(media_entity_get_info(entity)->name,
+				    EXYNOS4_FIMC_IS_ISP)) {
+				common_fmt.code = mbus_fmt.code;
+				common_fmt.colorspace = mbus_fmt.colorspace;
+				common_fmt.width -= 16;
+				common_fmt.height -= 12;
+			}
+
+			if (v4l2_subdev_format_compare(&mbus_fmt, &common_fmt)) {
+				repeat_negotiation = 1;
+				break;
+			}
+		}
+
+		if (!repeat_negotiation) {
+			break;
+		} else if (++cnt_negotiation > EXYNOS4_MAX_FMT_NEGO_NUM) {
+			V4L2_EXYNOS4_DBG("Pipeline format negotiation failed!");
+			return -EINVAL;
+		}
+	}
+
+	dev_fmt->fmt.pix_mp.width = mbus_fmt.width;
+	dev_fmt->fmt.pix_mp.height = mbus_fmt.height;
+	dev_fmt->fmt.pix_mp.field = mbus_fmt.field;
+	dev_fmt->fmt.pix_mp.colorspace = mbus_fmt.colorspace;
+
+	V4L2_EXYNOS4_DBG("Pipeline format successfuly negotiated");
+
+	return 0;
+}
+
+/**
+ * @brief Apply previously negotiated pipeline format
+ * @param plugin - this plugin.
+ *
+ * Apply the format, previously negotiated with negotiate_pipeline_fmt(),
+ * on all the pipeline v4l2 sub-devices.
+ *
+ * @return 0 if the format was successfuly applied,
+ * 	   or negative error code on failure.
+ */
+static int apply_pipeline_fmt(struct exynos4_camera_plugin *plugin,
+			      struct v4l2_format *fmt)
+{
+	struct v4l2_mbus_framefmt mbus_fmt = { 0 };
+	struct pipeline_entity *pipeline = plugin->pipeline;
+	struct media_entity *entity;
+	struct media_pad *pad;
+	unsigned int i;
+	int ret;
+
+	if (pipeline == NULL)
+		return -EINVAL;
+
+	for (i = 0; i < plugin->pipeline_len - 1; i++) {
+		entity = pipeline[i].entity;
+		/*
+		 * Source entity is linked only through a source pad
+		 * and this pad should be used for setting the format.
+		 * For other entities set the format on a sink pad.
+		 */
+		pad = pipeline[i].sink_pad ? pipeline[i].sink_pad :
+					pipeline[i].src_pad;
+		if (pad == NULL)
+			return -EINVAL;
+
+		ret = v4l2_subdev_get_format(entity, &mbus_fmt, pad->index,
+					     V4L2_SUBDEV_FORMAT_TRY);
+
+		if (ret < 0)
+			return ret;
+
+		V4L2_EXYNOS4_DBG("VIDIOC_SUBDEV_G_FMT %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
+			  media_entity_get_info(entity)->name, pad->index,
+			  v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
+			  v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
+			  mbus_fmt.width, mbus_fmt.height);
+
+		ret = v4l2_subdev_set_format(entity, &mbus_fmt, pad->index,
+					     V4L2_SUBDEV_FORMAT_ACTIVE);
+		if (ret < 0)
+			return ret;
+
+		V4L2_EXYNOS4_DBG("VIDIOC_SUBDEV_S_FMT %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
+			  media_entity_get_info(entity)->name, pad->index,
+			  v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
+			  v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
+			  mbus_fmt.width, mbus_fmt.height);
+	}
+
+	/*
+	 * Sink entity represents /dev/videoN node and is not
+	 * a sub-device. Nonetheless because it has associated
+	 * file descriptor and can expose v4l2-controls the
+	 * v4l2-subdev structure is used for caching the
+	 * related data.
+	 */
+	struct v4l2_subdev *sd =
+		media_entity_get_v4l2_subdev(pipeline[i].entity);
+	if (!sd)
+		return -EINVAL;
+
+	ret = SYS_IOCTL(sd->fd, VIDIOC_S_FMT, fmt);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/**
+ * @brief Open all v4l2 sub-devices associated with pipeline entities
+ * @param plugin - this plugin.
+ *
+ * Open all v4l2 sub-devices associated with the entities of the pipeline
+ * discovered with discover_pipeline_by_entity().
+ *
+ * @return 0 if all v4l2 sub-devices were opened successfuly,
+ * 	   or negative error code on failure.
+ */
+static int open_pipeline(struct exynos4_camera_plugin *plugin)
+{
+	struct pipeline_entity *pipeline = plugin->pipeline;
+	struct media_entity *entity;
+	unsigned int i;
+	int ret;
+
+	if (pipeline == NULL)
+		return -EINVAL;
+
+	/*
+	 * Stop walking the pipeline on the last but one entity, because
+	 * the sink entity was already opened by libv4l2 core.
+	 */
+	for (i = 0; i < plugin->pipeline_len - 1; i++) {
+		entity = pipeline[i].entity;
+		V4L2_EXYNOS4_DBG("Opening sub-device: %s",
+				 media_entity_get_info(entity)->name);
+		ret = v4l2_subdev_open(entity);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * @brief Check if there was a v4l2_ctrl binding defined for the entity
+ * @param entity - media entity.
+ * @param ctrl_id - v4l2 control identifier.
+ *
+ * Check if there was a v4l2-ctrl-binding entry defined for the entity.
+ *
+ * @return true if the binding exists, false otherwise.
+ */
+static bool has_v4l2_ctrl_binding(struct media_entity *entity,
+				       int ctrl_id)
+{
+	struct v4l2_subdev *sd = media_entity_get_v4l2_subdev(entity);
+	int i;
+
+	if (!sd)
+		return false;
+
+	for (i = 0; i < sd->num_v4l2_ctrl_bindings; ++i)
+		if (sd->v4l2_ctrl_bindings[i] == ctrl_id)
+			return true;
+
+	return false;
+}
+
+/**
+ * @brief Get the first pipeline entity with matching v4l2-ctrl-binding.
+ * @param plugin - this plugin.
+ * @param cid - v4l2-control identifier.
+ *
+ * Get the first pipeline entity for which v4l2-control-binding
+ * with given cid was defined.
+ *
+ * @return associated entity if defined, or NULL if no matching
+ *         v4l2-ctrl-binding was defined for any entity
+ *         in the pipeline.
+ */
+static struct media_entity *get_pipeline_entity_by_cid(
+				struct exynos4_camera_plugin *plugin,
+				int cid)
+{
+	struct pipeline_entity *pipeline = plugin->pipeline;
+	struct media_entity *entity;
+	unsigned int i;
+
+	if (pipeline == NULL)
+		return NULL;
+
+	for (i = 0; i < plugin->pipeline_len; i++) {
+		entity = pipeline[i].entity;
+		if (has_v4l2_ctrl_binding(entity, cid))
+			return entity;
+	}
+
+	return NULL;
+}
+
+/**
+ * @brief Check if the entity is of capture type
+ * @param entity_name - name of the entity to check
+ *
+ * Check if the entity name ends with a "capture", which
+ * gives a hint that it is of capture type.
+ *
+ * @return True if the entity name ends with a "capture"
+ *         string, and false otherwise.
+ */
+static bool is_capture_entity(const char *entity_name)
+{
+	const char capture_segment[] = "capture";
+	int cap_segment_pos;
+
+	if (entity_name == NULL)
+		return false;
+
+	cap_segment_pos = strlen(entity_name) - strlen(capture_segment);
+
+	if (strcmp(entity_name + cap_segment_pos, capture_segment) == 0)
+		return true;
+
+	return false;
+}
+
+static __u32 convert_type(__u32 type)
+{
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	default:
+		return type;
+	}
+}
+
+/* -----------------------------------------------------------------------------
+ * Ioctl handlers
+ */
+
+static int set_fmt_ioctl(struct exynos4_camera_plugin *plugin,
+				unsigned long int cmd,
+				struct v4l2_format *arg,
+				enum v4l2_subdev_format_whence set_mode)
+{
+	struct v4l2_format fmt = { 0 };
+	int ret;
+
+	fmt.type = convert_type(arg->type);
+	if (fmt.type != arg->type) {
+		fmt.fmt.pix_mp.width = arg->fmt.pix.width;
+		fmt.fmt.pix_mp.height = arg->fmt.pix.height;
+		fmt.fmt.pix_mp.pixelformat = arg->fmt.pix.pixelformat;
+		fmt.fmt.pix_mp.field = arg->fmt.pix.field;
+		fmt.fmt.pix_mp.colorspace = arg->fmt.pix.colorspace;
+		fmt.fmt.pix_mp.num_planes = 1;
+		fmt.fmt.pix_mp.flags = arg->fmt.pix.flags;
+		fmt.fmt.pix_mp.plane_fmt[0].bytesperline = arg->fmt.pix.bytesperline;
+		fmt.fmt.pix_mp.plane_fmt[0].sizeimage = arg->fmt.pix.sizeimage;
+	} else {
+		fmt = *arg;
+	}
+
+	ret = negotiate_pipeline_fmt(plugin, &fmt);
+	if (ret < 0)
+		return ret;
+
+	if (set_mode == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		ret = apply_pipeline_fmt(plugin, &fmt);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (fmt.type != arg->type) {
+		arg->fmt.pix.width = fmt.fmt.pix_mp.width;
+		arg->fmt.pix.height = fmt.fmt.pix_mp.height;
+		arg->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
+		arg->fmt.pix.field = fmt.fmt.pix_mp.field;
+		arg->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
+		arg->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
+		arg->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+		arg->fmt.pix.flags = fmt.fmt.pix_mp.flags;
+	} else {
+		*arg = fmt;
+	}
+
+	return 0;
+}
+
+static int get_fmt_ioctl(int fd,
+			 unsigned long int cmd,
+			 struct v4l2_format *arg)
+{
+	struct v4l2_format fmt = { 0 };
+	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(&arg->fmt.pix, 0, sizeof(arg->fmt.pix));
+	arg->fmt.pix.width = fmt.fmt.pix_mp.width;
+	arg->fmt.pix.height = fmt.fmt.pix_mp.height;
+	arg->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
+	arg->fmt.pix.field = fmt.fmt.pix_mp.field;
+	arg->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
+	arg->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
+	arg->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+	arg->fmt.pix.flags = fmt.fmt.pix_mp.flags;
+
+	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(int fd, struct v4l2_capability *arg)
+{
+	int ret;
+
+	ret = SYS_IOCTL(fd, VIDIOC_QUERYCAP, arg);
+	if (ret < 0)
+		return ret;
+
+	arg->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
+	arg->capabilities |= V4L2_CAP_VIDEO_CAPTURE;
+
+	return ret;
+}
+
+int ctrl_ioctl(struct exynos4_camera_plugin *plugin,
+	       unsigned long int request, struct v4l2_control *arg)
+{
+	struct pipeline_entity *pipeline = plugin->pipeline;
+	struct media_entity *entity;
+	struct v4l2_control ctrl = *arg;
+	struct v4l2_queryctrl queryctrl = { 0 };
+	bool ctrl_found = false;
+	int i, ret;
+
+	if (pipeline == NULL)
+		return -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) {
+		for (i = 0; i < plugin->pipeline_len; i++) {
+			struct v4l2_control ctrl_def = ctrl;
+
+			entity = pipeline[i].entity;
+
+			queryctrl.id = ctrl.id;
+
+			/* query default control value */
+			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+					VIDIOC_QUERYCTRL, &queryctrl);
+			if (ret < 0)
+				continue;
+
+			ctrl_found = true;
+
+			if (queryctrl.type & V4L2_CTRL_TYPE_BUTTON)
+				break;
+
+			ctrl_def.value = queryctrl.default_value;
+			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+					VIDIOC_S_CTRL, &ctrl_def);
+			if (ret < 0)
+				return -EINVAL;
+		}
+
+		if (!ctrl_found) {
+			ret = -EINVAL;
+			goto exit;
+		}
+	}
+
+	entity = get_pipeline_entity_by_cid(plugin, ctrl.id);
+
+	if (entity) {
+		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+				request, &ctrl);
+	} else {
+		/* Walk the pipeline until the request succeeds */
+		ret = -ENOENT;
+
+		for (i = 0; i < plugin->pipeline_len; i++) {
+			entity = pipeline[i].entity;
+
+			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+					request, &ctrl);
+			if (!ret)
+				break;
+		}
+	}
+
+exit:
+	*arg = ctrl;
+
+	V4L2_EXYNOS4_DBG("%s [id: 0x%8.8x, name: %s, entity: %s] (%d)",
+			 VIDIOC_CTRL(request), ctrl.id,
+			 ret ? NULL : queryctrl.name,
+			 entity ? media_entity_get_info(entity)->name :
+				  NULL, ret);
+
+
+	return ret;
+}
+
+static int single_ext_ctrl_ioctl(struct exynos4_camera_plugin *plugin,
+				 unsigned long int request,
+				 struct v4l2_ext_controls *arg)
+{
+	struct pipeline_entity *pipeline = plugin->pipeline;
+	struct media_entity *entity;
+	struct v4l2_ext_controls ctrls = *arg;
+	struct v4l2_ext_control *ctrl = &ctrls.controls[0];
+	struct v4l2_query_ext_ctrl queryctrl;
+	bool ctrl_found = 0;
+	int i, 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_EXT_CTRLS,
+	 * to avoid the situation when a control is set on more
+	 * than one sub-device.
+	 */
+	if (request == VIDIOC_S_EXT_CTRLS) {
+		for (i = 0; i < plugin->pipeline_len; i++) {
+			struct v4l2_ext_controls ctrls_def = ctrls;
+			struct v4l2_ext_control *ctrl_def = &ctrls_def.controls[0];
+
+			entity = pipeline[i].entity;
+
+			queryctrl.id = ctrl->id;
+
+			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+					VIDIOC_QUERY_EXT_CTRL, &queryctrl);
+			if (ret < 0)
+				continue;
+
+			ctrl_found = true;
+
+			if (queryctrl.type & V4L2_CTRL_TYPE_BUTTON)
+				break;
+
+			ctrl_def->value64 = queryctrl.default_value;
+
+			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+					VIDIOC_S_EXT_CTRLS, &ctrls_def);
+			if (ret < 0)
+				return -EINVAL;
+		}
+
+		if (!ctrl_found) {
+			ret = -EINVAL;
+			goto exit;
+		}
+	}
+
+	entity = get_pipeline_entity_by_cid(plugin, ctrl->id);
+
+	if (entity) {
+		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+				request, &ctrls);
+	} else {
+		/* Walk the pipeline until the request succeeds */
+		for (i = 0; i < plugin->pipeline_len; i++) {
+			entity = pipeline[i].entity;
+
+			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+					request, &ctrls);
+			if (!ret)
+				break;
+		}
+	}
+
+exit:
+	*arg = ctrls;
+
+	V4L2_EXYNOS4_DBG("%s [id: 0x%8.8x, name: %s, entity: %s] (%d)",
+			 VIDIOC_CTRL(request), ctrl->id,
+			 ret ? NULL : queryctrl.name,
+			 entity ? media_entity_get_info(entity)->name :
+				  NULL, ret);
+
+	return ret;
+}
+
+int ext_ctrl_ioctl(struct exynos4_camera_plugin *plugin,
+		   unsigned long 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 = single_ext_ctrl_ioctl(plugin, 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;
+			break;
+		}
+	}
+
+	*arg = out_ctrls;
+	return ret;
+}
+
+int sort_ctrls(const void * a, const void * b)
+{
+	const struct media_entity_to_cid *ctrl_a = a, *ctrl_b = b;
+
+	return ctrl_a->queryctrl.id - ctrl_b->queryctrl.id;
+}
+
+int queryctrl_ioctl(struct exynos4_camera_plugin *plugin,
+		    struct v4l2_queryctrl *arg)
+{
+	struct pipeline_entity *pipeline = plugin->pipeline;
+	struct media_entity *entity, *target_entity;
+	struct v4l2_queryctrl queryctrl = *arg;
+	struct media_entity_to_cid *ctrls_found;
+	int i, ret = -EINVAL, num_ctrls = 0;
+
+	/*
+	 * If id is or'ed with V4L2_CTRL_FLAG_NEXT_CTRL then the control to
+	 * be found is the one with the next lowest id among all entities
+	 * in the pipeline.
+	 */
+	if (queryctrl.id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+		ctrls_found = malloc(sizeof(*ctrls_found));
+
+		for (i = 0; i < plugin->pipeline_len; i++) {
+			entity = pipeline[i].entity;
+
+			queryctrl = *arg;
+
+			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+					VIDIOC_QUERYCTRL, &queryctrl);
+			if (!ret) {
+				ctrls_found = realloc(ctrls_found,
+					sizeof(*ctrls_found) * (num_ctrls + 1));
+				ctrls_found[num_ctrls].queryctrl = queryctrl;
+				ctrls_found[num_ctrls].entity = entity;
+				++num_ctrls;
+			}
+		}
+
+		if (num_ctrls == 0) {
+			ret = -EINVAL;
+			entity = NULL;
+			goto done;
+		}
+
+		qsort(ctrls_found, num_ctrls, sizeof(*ctrls_found), sort_ctrls);
+
+		queryctrl = ctrls_found[0].queryctrl;
+		target_entity = ctrls_found[0].entity;
+
+		free(ctrls_found);
+	}
+
+	entity = get_pipeline_entity_by_cid(plugin, queryctrl.id);
+	if (entity)
+		target_entity = entity;
+
+	ret = SYS_IOCTL(media_entity_get_v4l2_subdev(target_entity)->fd,
+			VIDIOC_QUERYCTRL, &queryctrl);
+
+done:
+	V4L2_EXYNOS4_DBG(
+		"VIDIOC_QUERYCTRL [id: 0x%8.8x, name: %s, entity: %s] (%d)",
+		ret ? arg->id : queryctrl.id, ret ? NULL : queryctrl.name,
+		target_entity ? media_entity_get_info(target_entity)->name :
+				NULL, ret);
+
+	*arg = queryctrl;
+
+	return ret;
+}
+
+int query_ext_ctrl_ioctl(struct exynos4_camera_plugin *plugin,
+			 struct v4l2_query_ext_ctrl *arg)
+{
+	struct pipeline_entity *pipeline = plugin->pipeline;
+	struct media_entity *entity, *target_entity;
+	struct v4l2_query_ext_ctrl query_ext_ctrl = *arg;
+	struct media_entity_to_cid *ctrls_found;
+	int i, ret = -EINVAL, num_ctrls = 0;
+
+	/*
+	 * If id is or'ed with V4L2_CTRL_FLAG_NEXT_CTRL then the control to
+	 * be found is the one with the next lowest id among all entities
+	 * in the pipeline.
+	 */
+	if (query_ext_ctrl.id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+		ctrls_found = malloc(sizeof(*ctrls_found));
+
+		for (i = 0; i < plugin->pipeline_len; i++) {
+			entity = pipeline[i].entity;
+
+			query_ext_ctrl = *arg;
+
+			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+					VIDIOC_QUERY_EXT_CTRL, &query_ext_ctrl.id);
+			if (!ret) {
+				ctrls_found = realloc(ctrls_found,
+					sizeof(*ctrls_found) * (num_ctrls + 1));
+				ctrls_found[num_ctrls].query_ext_ctrl =
+								query_ext_ctrl;
+				ctrls_found[num_ctrls].entity = entity;
+				++num_ctrls;
+			}
+		}
+
+		if (num_ctrls == 0) {
+			ret = -EINVAL;
+			entity = NULL;
+			goto done;
+		}
+
+		qsort(ctrls_found, num_ctrls, sizeof(*ctrls_found), sort_ctrls);
+
+		query_ext_ctrl = ctrls_found[0].query_ext_ctrl;
+		target_entity = ctrls_found[0].entity;
+
+		free(ctrls_found);
+	}
+
+	entity = get_pipeline_entity_by_cid(plugin, query_ext_ctrl.id);
+	if (entity)
+		target_entity = entity;
+
+	ret = SYS_IOCTL(media_entity_get_v4l2_subdev(target_entity)->fd,
+			VIDIOC_QUERYCTRL, &query_ext_ctrl);
+
+done:
+	V4L2_EXYNOS4_DBG(
+		"VIDIOC_QUERY_EXT_CTRL [id: 0x%8.8x, name: %s, entity: %s] (%d)",
+		ret ? arg->id : query_ext_ctrl.id,
+		ret ? NULL : query_ext_ctrl.name,
+		target_entity ? media_entity_get_info(target_entity)->name :
+				NULL, ret);
+
+	*arg = query_ext_ctrl;
+
+	return ret;
+}
+
+int querymenu_ioctl(struct exynos4_camera_plugin *plugin,
+		    struct v4l2_querymenu *arg)
+{
+	struct pipeline_entity *pipeline = plugin->pipeline;
+	struct media_entity *entity;
+	struct v4l2_querymenu querymenu = *arg;
+	int i, ret = -EINVAL;
+
+	entity = get_pipeline_entity_by_cid(plugin, querymenu.id);
+	if (entity) {
+		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+			        VIDIOC_QUERYMENU, &querymenu);
+		goto exit;
+	}
+
+	for (i = 0; i < plugin->pipeline_len; i++) {
+		entity = pipeline[i].entity;
+
+		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
+				VIDIOC_QUERYMENU, &querymenu);
+		if (!ret)
+			break;
+	}
+
+exit:
+	*arg = querymenu;
+
+	V4L2_EXYNOS4_DBG(
+		"VIDIOC_QUERYMENU [id: 0x%8.8x, name: %s, entity: %s] (%d)",
+		querymenu.id, ret ? NULL : querymenu.name,
+		entity ? media_entity_get_info(entity)->name : NULL, ret);
+
+	return ret;
+}
+
+static void *plugin_init(int fd)
+{
+	struct v4l2_capability cap = { 0 };
+	struct exynos4_camera_plugin *plugin = NULL;
+	const char *sink_entity_name;
+	struct media_device *media;
+	struct media_entity *sink_entity;
+	int ret;
+
+	ret = SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap);
+	if (ret < 0) {
+		V4L2_EXYNOS4_ERR("Failed to query video capabilities.");
+		return NULL;
+	}
+
+	/* Check if this is Exynos4 media device */
+	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 media device.");
+		return NULL;
+	}
+
+	/*
+	 * Create a representation of the media device
+	 * containing the opened video device.
+	 */
+	media = media_device_new_by_subdev_fd(fd, &sink_entity);
+	if (media == NULL) {
+		V4L2_EXYNOS4_ERR("Failed to create media device.");
+		return NULL;
+	}
+
+#ifdef DEBUG
+	media_debug_set_handler(media, (void (*)(void *, ...))fprintf, stdout);
+#endif
+
+	sink_entity_name = media_entity_get_info(sink_entity)->name;
+
+	/* Check if video entity is of capture type, not m2m */
+	if (!is_capture_entity(sink_entity_name)) {
+		V4L2_EXYNOS4_ERR("Device not of capture type.");
+		goto err_get_sink_entity;
+	}
+
+	/*
+	 * Initialize sink_entity subdev fd with the one opened
+	 * by the libv4l core to avoid losing it on pipeline opening.
+	 */
+	v4l2_subdev_create_opened(sink_entity, fd);
+
+	/* Parse media configuration file and apply its settings */
+	ret = mediatext_parse_setup_config(media, EXYNOS4_CAPTURE_CONF);
+	if (ret < 0) {
+		V4L2_EXYNOS4_ERR("Media config parser error.");
+		goto err_get_sink_entity;
+	}
+
+	/* Allocate private data */
+	plugin = calloc(1, sizeof(*plugin));
+	if (!plugin)
+		goto err_get_sink_entity;
+
+	plugin->media = media;
+
+	/*
+	 * Discover the pipeline of sub-devices from a camera sensor
+	 * to the opened video device.
+	 */
+	ret = discover_pipeline_by_entity(plugin, sink_entity);
+	if (ret < 0) {
+		V4L2_EXYNOS4_ERR("Error discovering video pipeline.");
+		goto err_discover_pipeline;
+	}
+
+	/* Open all sub-devices in the discovered pipeline */
+	ret = open_pipeline(plugin);
+	if (ret < 0) {
+		V4L2_EXYNOS4_ERR("Error opening video pipeline.");
+		goto err_discover_pipeline;
+	}
+
+	V4L2_EXYNOS4_LOG("Initialized exynos4-camera plugin.");
+
+	return plugin;
+
+err_discover_pipeline:
+	free(plugin);
+err_get_sink_entity:
+	if (media)
+		media_device_unref(media);
+	return NULL;
+}
+
+static void plugin_close(void *dev_ops_priv)
+{
+	struct exynos4_camera_plugin *plugin = dev_ops_priv;
+
+	if (plugin == NULL)
+		return;
+
+	media_device_unref(plugin->media);
+
+	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;
+
+	if (plugin == NULL || arg == NULL)
+		return -EINVAL;
+
+	switch (cmd) {
+	case VIDIOC_S_CTRL:
+	case VIDIOC_G_CTRL:
+		return ctrl_ioctl(plugin, cmd, arg);
+	case VIDIOC_S_EXT_CTRLS:
+	case VIDIOC_G_EXT_CTRLS:
+	case VIDIOC_TRY_EXT_CTRLS:
+		return ext_ctrl_ioctl(plugin, cmd, arg);
+	case VIDIOC_QUERYCTRL:
+		return queryctrl_ioctl(plugin, arg);
+	case VIDIOC_QUERY_EXT_CTRL:
+		return query_ext_ctrl_ioctl(plugin, arg);
+	case VIDIOC_QUERYMENU:
+		return querymenu_ioctl(plugin, arg);
+	case VIDIOC_TRY_FMT:
+		return set_fmt_ioctl(plugin, cmd, arg,
+				     V4L2_SUBDEV_FORMAT_TRY);
+	case VIDIOC_S_FMT:
+		return set_fmt_ioctl(plugin, cmd, arg,
+				     V4L2_SUBDEV_FORMAT_ACTIVE);
+	case VIDIOC_G_FMT:
+		return get_fmt_ioctl(fd, cmd, arg);
+	case VIDIOC_QUERYCAP:
+		return querycap_ioctl(fd, arg);
+	case VIDIOC_QBUF:
+	case VIDIOC_DQBUF:
+	case VIDIOC_QUERYBUF:
+	case VIDIOC_PREPARE_BUF:
+		return buf_ioctl(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;
+			__u32 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,
+};
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera
  2016-10-12 14:35 [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera Jacek Anaszewski
                   ` (6 preceding siblings ...)
  2016-10-12 14:35 ` [PATCH v4l-utils v7 7/7] Add a libv4l plugin for Exynos4 camera Jacek Anaszewski
@ 2016-11-03 11:51 ` Hans Verkuil
  2016-11-03 12:13   ` Jacek Anaszewski
  7 siblings, 1 reply; 35+ messages in thread
From: Hans Verkuil @ 2016-11-03 11:51 UTC (permalink / raw)
  To: Jacek Anaszewski, linux-media
  Cc: sakari.ailus, mchehab, m.szyprowski, s.nawrocki, Laurent Pinchart

Hi all,

Is there anything that blocks me from merging this?

This plugin work has been ongoing for years and unless there are serious
objections I propose that this is merged.

Jacek, is there anything missing that would prevent merging this?

Regards,

	Hans

On 12/10/16 16:35, Jacek Anaszewski wrote:
> This is a seventh version of the patch series adding a plugin for the
> Exynos4 camera. Last version [0] of the patch set was posted in
> January.
>
> The plugin doesn't link against libmediactl, but has its sources
> compiled in. Currently utils are built after the plugins, but
> libv4l-exynos4-camera plugin depends on the utils. In order to link
> the plugin against libmediactl the build system would have to be
> modified.
>
> ================
> Changes from v6:
> ================
>
> - close v4l2 sub-devices on media device release
> - moved non-generic code from libmediactl to the plugin
> - resigned from adding libmedia_ioctl library and moved all its
>   code to the plugin, since it depended on pipeline representation,
>   which was not generic for all possible media device topologies
> - used media_get_info()->name instead of adding media_entity_get_name
> - renamed media_get_backlinks_by_entity() to media_entity_get_backlinks(()
> - moved pipeline from struct media_device to the plugin
> - changed the way of associating video device file descriptor with media device
> - switched to using auto-generated media-bus-format-names.h header file
> - renamed v4l2-ctrl-redir config entry name to v4l2-ctrl-binding
>
> ================
> Changes from v5:
> ================
>
> - fixed and tested use cases with S5K6A3 sensor and FIMC-IS-ISP
> - added conversion "colorspace id to string"
>
> ================
> Changes from v4:
> ================
>
> - removed some redundant functions for traversing media device graph
>   and switched over to using existing ones
> - avoided accessing struct v4l2_subdev from libmediactl
> - applied various improvements
>
> ================
> Changes from v3:
> ================
>
> - added struct v4l2_subdev and put entity fd and
>   information about supported controls to it
> - improved functions for negotiating and setting
>   pipeline format by using available libv4lsubdev API
> - applied minor improvements and cleanups
>
> ================
> Changes from v2:
> ================
>
> - switched to using mediatext library for parsing
>   the media device configuration
> - extended libmediactl
> - switched to using libmediactl
>
> ================
> 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 v4.8-rc2 (exynos4-is driver doesn't proble properly
> with current master branch of linux-media.git) with patches fixing several
> issues for Exynos4 camera: [1], [2], [3].
>
> The plugin expects a configuration file:
> /var/lib/libv4l/exynos4_capture_conf
>
> Exemplary configuration file for pipeline with sensor
> S5C73M3 (rear camera):
>
> ==========================================
>
> link-conf "s5p-mipi-csis.0":1 -> "FIMC.0":0 [1]
> v4l2-ctrl-binding 0x0098091f -> "fimc.0.capture"
> v4l2-ctrl-binding 0x00980902 -> "S5C73M3"
> v4l2-ctrl-binding 0x00980922 -> "fimc.0.capture"
> v4l2-ctrl-binding 0x009a0914 -> "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 extra-controls="c,rotate=90,color_effects=2" ! video/x-raw,width=960,height=720 ! fbdevsink
>
> Exemplary configuration file for pipeline with sensor
> S5K6A3 (front camera):
>
> ==========================================
>
> link-conf "s5p-mipi-csis.1":1 -> "FIMC-LITE.1":0 [1]
> link-conf "FIMC-LITE.1":2 -> "FIMC-IS-ISP":0 [1]
> link-conf "FIMC-IS-ISP":1 -> "FIMC.0":1 [1]
>
> ==========================================
>
> gst-launch-1.0 v4l2src device=/dev/video1 extra-controls="c,rotate=270,color_effects=2,horizontal_flip=1" ! video/x-raw,width=960,height=920 ! fbdevsink
>
> Best Regards,
> Jacek Anaszewski
>
> [0] http://www.spinics.net/lists/linux-media/msg96510.html
> [1] https://patchwork.kernel.org/patch/9335197/
> [2] https://patchwork.kernel.org/patch/9270985/
> [3] https://patchwork.kernel.org/patch/9308923/
> [4] https://patchwork.kernel.org/patch/9335273/
>
>
> Jacek Anaszewski (7):
>   mediactl: Add support for v4l2-ctrl-binding config
>   mediatext: Add library
>   mediactl: Add media_entity_get_backlinks()
>   mediactl: Add media_device creation helpers
>   mediactl: libv4l2subdev: Add colorspace logging
>   mediactl: libv4l2subdev: add support for comparing mbus formats
>   Add a libv4l plugin for Exynos4 camera
>
>  configure.ac                                      |    1 +
>  lib/Makefile.am                                   |    5 +
>  lib/libv4l-exynos4-camera/Makefile.am             |   19 +
>  lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c | 1325 +++++++++++++++++++++
>  utils/media-ctl/Makefile.am                       |   10 +-
>  utils/media-ctl/libmediactl.c                     |  152 ++-
>  utils/media-ctl/libmediatext.pc.in                |   10 +
>  utils/media-ctl/libv4l2subdev.c                   |  106 ++
>  utils/media-ctl/mediactl.h                        |   42 +
>  utils/media-ctl/mediatext-test.c                  |   64 +
>  utils/media-ctl/mediatext.c                       |  312 +++++
>  utils/media-ctl/mediatext.h                       |   52 +
>  utils/media-ctl/v4l2subdev.h                      |   50 +
>  13 files changed, 2144 insertions(+), 4 deletions(-)
>  create mode 100644 lib/libv4l-exynos4-camera/Makefile.am
>  create mode 100644 lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
>  create mode 100644 utils/media-ctl/libmediatext.pc.in
>  create mode 100644 utils/media-ctl/mediatext-test.c
>  create mode 100644 utils/media-ctl/mediatext.c
>  create mode 100644 utils/media-ctl/mediatext.h
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera
  2016-11-03 11:51 ` [PATCH v4l-utils v7 0/7] Add a " Hans Verkuil
@ 2016-11-03 12:13   ` Jacek Anaszewski
  2016-11-23 21:51     ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-11-03 12:13 UTC (permalink / raw)
  To: Hans Verkuil, linux-media
  Cc: sakari.ailus, mchehab, m.szyprowski, s.nawrocki, Laurent Pinchart

Hi Hans,

On 11/03/2016 12:51 PM, Hans Verkuil wrote:
> Hi all,
>
> Is there anything that blocks me from merging this?
>
> This plugin work has been ongoing for years and unless there are serious
> objections I propose that this is merged.
>
> Jacek, is there anything missing that would prevent merging this?

There were issues raised by Sakari during last review, related to
the way how v4l2 control bindings are defined. That discussion wasn't
finished, so I stayed by my approach. Other than that - I've tested it
and it works fine both with GStreamer and my test app.

Best regards,
Jacek Anaszewski


> On 12/10/16 16:35, Jacek Anaszewski wrote:
>> This is a seventh version of the patch series adding a plugin for the
>> Exynos4 camera. Last version [0] of the patch set was posted in
>> January.
>>
>> The plugin doesn't link against libmediactl, but has its sources
>> compiled in. Currently utils are built after the plugins, but
>> libv4l-exynos4-camera plugin depends on the utils. In order to link
>> the plugin against libmediactl the build system would have to be
>> modified.
>>
>> ================
>> Changes from v6:
>> ================
>>
>> - close v4l2 sub-devices on media device release
>> - moved non-generic code from libmediactl to the plugin
>> - resigned from adding libmedia_ioctl library and moved all its
>>   code to the plugin, since it depended on pipeline representation,
>>   which was not generic for all possible media device topologies
>> - used media_get_info()->name instead of adding media_entity_get_name
>> - renamed media_get_backlinks_by_entity() to
>> media_entity_get_backlinks(()
>> - moved pipeline from struct media_device to the plugin
>> - changed the way of associating video device file descriptor with
>> media device
>> - switched to using auto-generated media-bus-format-names.h header file
>> - renamed v4l2-ctrl-redir config entry name to v4l2-ctrl-binding
>>
>> ================
>> Changes from v5:
>> ================
>>
>> - fixed and tested use cases with S5K6A3 sensor and FIMC-IS-ISP
>> - added conversion "colorspace id to string"
>>
>> ================
>> Changes from v4:
>> ================
>>
>> - removed some redundant functions for traversing media device graph
>>   and switched over to using existing ones
>> - avoided accessing struct v4l2_subdev from libmediactl
>> - applied various improvements
>>
>> ================
>> Changes from v3:
>> ================
>>
>> - added struct v4l2_subdev and put entity fd and
>>   information about supported controls to it
>> - improved functions for negotiating and setting
>>   pipeline format by using available libv4lsubdev API
>> - applied minor improvements and cleanups
>>
>> ================
>> Changes from v2:
>> ================
>>
>> - switched to using mediatext library for parsing
>>   the media device configuration
>> - extended libmediactl
>> - switched to using libmediactl
>>
>> ================
>> 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 v4.8-rc2 (exynos4-is driver doesn't proble
>> properly
>> with current master branch of linux-media.git) with patches fixing
>> several
>> issues for Exynos4 camera: [1], [2], [3].
>>
>> The plugin expects a configuration file:
>> /var/lib/libv4l/exynos4_capture_conf
>>
>> Exemplary configuration file for pipeline with sensor
>> S5C73M3 (rear camera):
>>
>> ==========================================
>>
>> link-conf "s5p-mipi-csis.0":1 -> "FIMC.0":0 [1]
>> v4l2-ctrl-binding 0x0098091f -> "fimc.0.capture"
>> v4l2-ctrl-binding 0x00980902 -> "S5C73M3"
>> v4l2-ctrl-binding 0x00980922 -> "fimc.0.capture"
>> v4l2-ctrl-binding 0x009a0914 -> "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
>> extra-controls="c,rotate=90,color_effects=2" !
>> video/x-raw,width=960,height=720 ! fbdevsink
>>
>> Exemplary configuration file for pipeline with sensor
>> S5K6A3 (front camera):
>>
>> ==========================================
>>
>> link-conf "s5p-mipi-csis.1":1 -> "FIMC-LITE.1":0 [1]
>> link-conf "FIMC-LITE.1":2 -> "FIMC-IS-ISP":0 [1]
>> link-conf "FIMC-IS-ISP":1 -> "FIMC.0":1 [1]
>>
>> ==========================================
>>
>> gst-launch-1.0 v4l2src device=/dev/video1
>> extra-controls="c,rotate=270,color_effects=2,horizontal_flip=1" !
>> video/x-raw,width=960,height=920 ! fbdevsink
>>
>> Best Regards,
>> Jacek Anaszewski
>>
>> [0] http://www.spinics.net/lists/linux-media/msg96510.html
>> [1] https://patchwork.kernel.org/patch/9335197/
>> [2] https://patchwork.kernel.org/patch/9270985/
>> [3] https://patchwork.kernel.org/patch/9308923/
>> [4] https://patchwork.kernel.org/patch/9335273/
>>
>>
>> Jacek Anaszewski (7):
>>   mediactl: Add support for v4l2-ctrl-binding config
>>   mediatext: Add library
>>   mediactl: Add media_entity_get_backlinks()
>>   mediactl: Add media_device creation helpers
>>   mediactl: libv4l2subdev: Add colorspace logging
>>   mediactl: libv4l2subdev: add support for comparing mbus formats
>>   Add a libv4l plugin for Exynos4 camera
>>
>>  configure.ac                                      |    1 +
>>  lib/Makefile.am                                   |    5 +
>>  lib/libv4l-exynos4-camera/Makefile.am             |   19 +
>>  lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c | 1325
>> +++++++++++++++++++++
>>  utils/media-ctl/Makefile.am                       |   10 +-
>>  utils/media-ctl/libmediactl.c                     |  152 ++-
>>  utils/media-ctl/libmediatext.pc.in                |   10 +
>>  utils/media-ctl/libv4l2subdev.c                   |  106 ++
>>  utils/media-ctl/mediactl.h                        |   42 +
>>  utils/media-ctl/mediatext-test.c                  |   64 +
>>  utils/media-ctl/mediatext.c                       |  312 +++++
>>  utils/media-ctl/mediatext.h                       |   52 +
>>  utils/media-ctl/v4l2subdev.h                      |   50 +
>>  13 files changed, 2144 insertions(+), 4 deletions(-)
>>  create mode 100644 lib/libv4l-exynos4-camera/Makefile.am
>>  create mode 100644 lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
>>  create mode 100644 utils/media-ctl/libmediatext.pc.in
>>  create mode 100644 utils/media-ctl/mediatext-test.c
>>  create mode 100644 utils/media-ctl/mediatext.c
>>  create mode 100644 utils/media-ctl/mediatext.h
>>
>
>
>



^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera
  2016-11-03 12:13   ` Jacek Anaszewski
@ 2016-11-23 21:51     ` Mauro Carvalho Chehab
  2016-11-24  8:10       ` Jacek Anaszewski
  0 siblings, 1 reply; 35+ messages in thread
From: Mauro Carvalho Chehab @ 2016-11-23 21:51 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Hans Verkuil, linux-media, sakari.ailus, mchehab, m.szyprowski,
	s.nawrocki, Laurent Pinchart

Em Thu, 03 Nov 2016 13:13:12 +0100
Jacek Anaszewski <j.anaszewski@samsung.com> escreveu:

> Hi Hans,
> 
> On 11/03/2016 12:51 PM, Hans Verkuil wrote:
> > Hi all,
> >
> > Is there anything that blocks me from merging this?
> >
> > This plugin work has been ongoing for years and unless there are serious
> > objections I propose that this is merged.
> >
> > Jacek, is there anything missing that would prevent merging this?  
> 
> There were issues raised by Sakari during last review, related to
> the way how v4l2 control bindings are defined. That discussion wasn't
> finished, so I stayed by my approach.

Are those things something that could be fixed later without
breaking binary apps? If not, then perhaps it is time to merge
this.

> Other than that - I've tested it
> and it works fine both with GStreamer and my test app.
> 
> Best regards,
> Jacek Anaszewski
> 
> 
> > On 12/10/16 16:35, Jacek Anaszewski wrote:  
> >> This is a seventh version of the patch series adding a plugin for the
> >> Exynos4 camera. Last version [0] of the patch set was posted in
> >> January.
> >>
> >> The plugin doesn't link against libmediactl, but has its sources
> >> compiled in. Currently utils are built after the plugins, but
> >> libv4l-exynos4-camera plugin depends on the utils. In order to link
> >> the plugin against libmediactl the build system would have to be
> >> modified.
> >>
> >> ================
> >> Changes from v6:
> >> ================
> >>
> >> - close v4l2 sub-devices on media device release
> >> - moved non-generic code from libmediactl to the plugin
> >> - resigned from adding libmedia_ioctl library and moved all its
> >>   code to the plugin, since it depended on pipeline representation,
> >>   which was not generic for all possible media device topologies
> >> - used media_get_info()->name instead of adding media_entity_get_name
> >> - renamed media_get_backlinks_by_entity() to
> >> media_entity_get_backlinks(()
> >> - moved pipeline from struct media_device to the plugin
> >> - changed the way of associating video device file descriptor with
> >> media device
> >> - switched to using auto-generated media-bus-format-names.h header file
> >> - renamed v4l2-ctrl-redir config entry name to v4l2-ctrl-binding
> >>
> >> ================
> >> Changes from v5:
> >> ================
> >>
> >> - fixed and tested use cases with S5K6A3 sensor and FIMC-IS-ISP
> >> - added conversion "colorspace id to string"
> >>
> >> ================
> >> Changes from v4:
> >> ================
> >>
> >> - removed some redundant functions for traversing media device graph
> >>   and switched over to using existing ones
> >> - avoided accessing struct v4l2_subdev from libmediactl
> >> - applied various improvements
> >>
> >> ================
> >> Changes from v3:
> >> ================
> >>
> >> - added struct v4l2_subdev and put entity fd and
> >>   information about supported controls to it
> >> - improved functions for negotiating and setting
> >>   pipeline format by using available libv4lsubdev API
> >> - applied minor improvements and cleanups
> >>
> >> ================
> >> Changes from v2:
> >> ================
> >>
> >> - switched to using mediatext library for parsing
> >>   the media device configuration
> >> - extended libmediactl
> >> - switched to using libmediactl
> >>
> >> ================
> >> 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 v4.8-rc2 (exynos4-is driver doesn't proble
> >> properly
> >> with current master branch of linux-media.git) with patches fixing
> >> several
> >> issues for Exynos4 camera: [1], [2], [3].
> >>
> >> The plugin expects a configuration file:
> >> /var/lib/libv4l/exynos4_capture_conf
> >>
> >> Exemplary configuration file for pipeline with sensor
> >> S5C73M3 (rear camera):
> >>
> >> ==========================================
> >>
> >> link-conf "s5p-mipi-csis.0":1 -> "FIMC.0":0 [1]
> >> v4l2-ctrl-binding 0x0098091f -> "fimc.0.capture"
> >> v4l2-ctrl-binding 0x00980902 -> "S5C73M3"
> >> v4l2-ctrl-binding 0x00980922 -> "fimc.0.capture"
> >> v4l2-ctrl-binding 0x009a0914 -> "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
> >> extra-controls="c,rotate=90,color_effects=2" !
> >> video/x-raw,width=960,height=720 ! fbdevsink
> >>
> >> Exemplary configuration file for pipeline with sensor
> >> S5K6A3 (front camera):
> >>
> >> ==========================================
> >>
> >> link-conf "s5p-mipi-csis.1":1 -> "FIMC-LITE.1":0 [1]
> >> link-conf "FIMC-LITE.1":2 -> "FIMC-IS-ISP":0 [1]
> >> link-conf "FIMC-IS-ISP":1 -> "FIMC.0":1 [1]
> >>
> >> ==========================================
> >>
> >> gst-launch-1.0 v4l2src device=/dev/video1
> >> extra-controls="c,rotate=270,color_effects=2,horizontal_flip=1" !
> >> video/x-raw,width=960,height=920 ! fbdevsink
> >>
> >> Best Regards,
> >> Jacek Anaszewski
> >>
> >> [0] http://www.spinics.net/lists/linux-media/msg96510.html
> >> [1] https://patchwork.kernel.org/patch/9335197/
> >> [2] https://patchwork.kernel.org/patch/9270985/
> >> [3] https://patchwork.kernel.org/patch/9308923/
> >> [4] https://patchwork.kernel.org/patch/9335273/
> >>
> >>
> >> Jacek Anaszewski (7):
> >>   mediactl: Add support for v4l2-ctrl-binding config
> >>   mediatext: Add library
> >>   mediactl: Add media_entity_get_backlinks()
> >>   mediactl: Add media_device creation helpers
> >>   mediactl: libv4l2subdev: Add colorspace logging
> >>   mediactl: libv4l2subdev: add support for comparing mbus formats
> >>   Add a libv4l plugin for Exynos4 camera
> >>
> >>  configure.ac                                      |    1 +
> >>  lib/Makefile.am                                   |    5 +
> >>  lib/libv4l-exynos4-camera/Makefile.am             |   19 +
> >>  lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c | 1325
> >> +++++++++++++++++++++
> >>  utils/media-ctl/Makefile.am                       |   10 +-
> >>  utils/media-ctl/libmediactl.c                     |  152 ++-
> >>  utils/media-ctl/libmediatext.pc.in                |   10 +
> >>  utils/media-ctl/libv4l2subdev.c                   |  106 ++
> >>  utils/media-ctl/mediactl.h                        |   42 +
> >>  utils/media-ctl/mediatext-test.c                  |   64 +
> >>  utils/media-ctl/mediatext.c                       |  312 +++++
> >>  utils/media-ctl/mediatext.h                       |   52 +
> >>  utils/media-ctl/v4l2subdev.h                      |   50 +
> >>  13 files changed, 2144 insertions(+), 4 deletions(-)
> >>  create mode 100644 lib/libv4l-exynos4-camera/Makefile.am
> >>  create mode 100644 lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
> >>  create mode 100644 utils/media-ctl/libmediatext.pc.in
> >>  create mode 100644 utils/media-ctl/mediatext-test.c
> >>  create mode 100644 utils/media-ctl/mediatext.c
> >>  create mode 100644 utils/media-ctl/mediatext.h
> >>  
> >
> >
> >  
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Thanks,
Mauro

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera
  2016-11-23 21:51     ` Mauro Carvalho Chehab
@ 2016-11-24  8:10       ` Jacek Anaszewski
  2016-11-24  9:10         ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-11-24  8:10 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Hans Verkuil, linux-media, sakari.ailus, mchehab, m.szyprowski,
	s.nawrocki, Laurent Pinchart

On 11/23/2016 10:51 PM, Mauro Carvalho Chehab wrote:
> Em Thu, 03 Nov 2016 13:13:12 +0100
> Jacek Anaszewski <j.anaszewski@samsung.com> escreveu:
>
>> Hi Hans,
>>
>> On 11/03/2016 12:51 PM, Hans Verkuil wrote:
>>> Hi all,
>>>
>>> Is there anything that blocks me from merging this?
>>>
>>> This plugin work has been ongoing for years and unless there are serious
>>> objections I propose that this is merged.
>>>
>>> Jacek, is there anything missing that would prevent merging this?
>>
>> There were issues raised by Sakari during last review, related to
>> the way how v4l2 control bindings are defined. That discussion wasn't
>> finished, so I stayed by my approach.
>
> Are those things something that could be fixed later without
> breaking binary apps? If not, then perhaps it is time to merge
> this.

They are related to the format of configuration file being introduced
by this patch set, so there are no binary apps depending on it.

Few days ago I had a discussion with Sakari on IRC, and he pointed
me to his patch set adding an extended version of his mediatext
library RFC [0], the first version of which I included to my patch set,
and extended a bit. A decision should be made if I should adapt my
API to the mediatext RFC. RFC is not a stable ground though.

[0] https://www.spinics.net/lists/linux-media/msg103242.html

>> Other than that - I've tested it
>> and it works fine both with GStreamer and my test app.
>>
>> Best regards,
>> Jacek Anaszewski
>>
>>
>>> On 12/10/16 16:35, Jacek Anaszewski wrote:
>>>> This is a seventh version of the patch series adding a plugin for the
>>>> Exynos4 camera. Last version [0] of the patch set was posted in
>>>> January.
>>>>
>>>> The plugin doesn't link against libmediactl, but has its sources
>>>> compiled in. Currently utils are built after the plugins, but
>>>> libv4l-exynos4-camera plugin depends on the utils. In order to link
>>>> the plugin against libmediactl the build system would have to be
>>>> modified.
>>>>
>>>> ================
>>>> Changes from v6:
>>>> ================
>>>>
>>>> - close v4l2 sub-devices on media device release
>>>> - moved non-generic code from libmediactl to the plugin
>>>> - resigned from adding libmedia_ioctl library and moved all its
>>>>   code to the plugin, since it depended on pipeline representation,
>>>>   which was not generic for all possible media device topologies
>>>> - used media_get_info()->name instead of adding media_entity_get_name
>>>> - renamed media_get_backlinks_by_entity() to
>>>> media_entity_get_backlinks(()
>>>> - moved pipeline from struct media_device to the plugin
>>>> - changed the way of associating video device file descriptor with
>>>> media device
>>>> - switched to using auto-generated media-bus-format-names.h header file
>>>> - renamed v4l2-ctrl-redir config entry name to v4l2-ctrl-binding
>>>>
>>>> ================
>>>> Changes from v5:
>>>> ================
>>>>
>>>> - fixed and tested use cases with S5K6A3 sensor and FIMC-IS-ISP
>>>> - added conversion "colorspace id to string"
>>>>
>>>> ================
>>>> Changes from v4:
>>>> ================
>>>>
>>>> - removed some redundant functions for traversing media device graph
>>>>   and switched over to using existing ones
>>>> - avoided accessing struct v4l2_subdev from libmediactl
>>>> - applied various improvements
>>>>
>>>> ================
>>>> Changes from v3:
>>>> ================
>>>>
>>>> - added struct v4l2_subdev and put entity fd and
>>>>   information about supported controls to it
>>>> - improved functions for negotiating and setting
>>>>   pipeline format by using available libv4lsubdev API
>>>> - applied minor improvements and cleanups
>>>>
>>>> ================
>>>> Changes from v2:
>>>> ================
>>>>
>>>> - switched to using mediatext library for parsing
>>>>   the media device configuration
>>>> - extended libmediactl
>>>> - switched to using libmediactl
>>>>
>>>> ================
>>>> 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 v4.8-rc2 (exynos4-is driver doesn't proble
>>>> properly
>>>> with current master branch of linux-media.git) with patches fixing
>>>> several
>>>> issues for Exynos4 camera: [1], [2], [3].
>>>>
>>>> The plugin expects a configuration file:
>>>> /var/lib/libv4l/exynos4_capture_conf
>>>>
>>>> Exemplary configuration file for pipeline with sensor
>>>> S5C73M3 (rear camera):
>>>>
>>>> ==========================================
>>>>
>>>> link-conf "s5p-mipi-csis.0":1 -> "FIMC.0":0 [1]
>>>> v4l2-ctrl-binding 0x0098091f -> "fimc.0.capture"
>>>> v4l2-ctrl-binding 0x00980902 -> "S5C73M3"
>>>> v4l2-ctrl-binding 0x00980922 -> "fimc.0.capture"
>>>> v4l2-ctrl-binding 0x009a0914 -> "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
>>>> extra-controls="c,rotate=90,color_effects=2" !
>>>> video/x-raw,width=960,height=720 ! fbdevsink
>>>>
>>>> Exemplary configuration file for pipeline with sensor
>>>> S5K6A3 (front camera):
>>>>
>>>> ==========================================
>>>>
>>>> link-conf "s5p-mipi-csis.1":1 -> "FIMC-LITE.1":0 [1]
>>>> link-conf "FIMC-LITE.1":2 -> "FIMC-IS-ISP":0 [1]
>>>> link-conf "FIMC-IS-ISP":1 -> "FIMC.0":1 [1]
>>>>
>>>> ==========================================
>>>>
>>>> gst-launch-1.0 v4l2src device=/dev/video1
>>>> extra-controls="c,rotate=270,color_effects=2,horizontal_flip=1" !
>>>> video/x-raw,width=960,height=920 ! fbdevsink
>>>>
>>>> Best Regards,
>>>> Jacek Anaszewski
>>>>
>>>> [0] http://www.spinics.net/lists/linux-media/msg96510.html
>>>> [1] https://patchwork.kernel.org/patch/9335197/
>>>> [2] https://patchwork.kernel.org/patch/9270985/
>>>> [3] https://patchwork.kernel.org/patch/9308923/
>>>> [4] https://patchwork.kernel.org/patch/9335273/
>>>>
>>>>
>>>> Jacek Anaszewski (7):
>>>>   mediactl: Add support for v4l2-ctrl-binding config
>>>>   mediatext: Add library
>>>>   mediactl: Add media_entity_get_backlinks()
>>>>   mediactl: Add media_device creation helpers
>>>>   mediactl: libv4l2subdev: Add colorspace logging
>>>>   mediactl: libv4l2subdev: add support for comparing mbus formats
>>>>   Add a libv4l plugin for Exynos4 camera
>>>>
>>>>  configure.ac                                      |    1 +
>>>>  lib/Makefile.am                                   |    5 +
>>>>  lib/libv4l-exynos4-camera/Makefile.am             |   19 +
>>>>  lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c | 1325
>>>> +++++++++++++++++++++
>>>>  utils/media-ctl/Makefile.am                       |   10 +-
>>>>  utils/media-ctl/libmediactl.c                     |  152 ++-
>>>>  utils/media-ctl/libmediatext.pc.in                |   10 +
>>>>  utils/media-ctl/libv4l2subdev.c                   |  106 ++
>>>>  utils/media-ctl/mediactl.h                        |   42 +
>>>>  utils/media-ctl/mediatext-test.c                  |   64 +
>>>>  utils/media-ctl/mediatext.c                       |  312 +++++
>>>>  utils/media-ctl/mediatext.h                       |   52 +
>>>>  utils/media-ctl/v4l2subdev.h                      |   50 +
>>>>  13 files changed, 2144 insertions(+), 4 deletions(-)
>>>>  create mode 100644 lib/libv4l-exynos4-camera/Makefile.am
>>>>  create mode 100644 lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
>>>>  create mode 100644 utils/media-ctl/libmediatext.pc.in
>>>>  create mode 100644 utils/media-ctl/mediatext-test.c
>>>>  create mode 100644 utils/media-ctl/mediatext.c
>>>>  create mode 100644 utils/media-ctl/mediatext.h
>>>>
>>>
>>>
>>>
>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-media" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>
> Thanks,
> Mauro
>
>
>


-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera
  2016-11-24  8:10       ` Jacek Anaszewski
@ 2016-11-24  9:10         ` Mauro Carvalho Chehab
  2016-11-24 12:20           ` Jacek Anaszewski
  0 siblings, 1 reply; 35+ messages in thread
From: Mauro Carvalho Chehab @ 2016-11-24  9:10 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Hans Verkuil, linux-media, sakari.ailus, mchehab, m.szyprowski,
	s.nawrocki, Laurent Pinchart

Em Thu, 24 Nov 2016 09:10:28 +0100
Jacek Anaszewski <j.anaszewski@samsung.com> escreveu:

> On 11/23/2016 10:51 PM, Mauro Carvalho Chehab wrote:
> > Em Thu, 03 Nov 2016 13:13:12 +0100
> > Jacek Anaszewski <j.anaszewski@samsung.com> escreveu:
> >  
> >> Hi Hans,
> >>
> >> On 11/03/2016 12:51 PM, Hans Verkuil wrote:  
> >>> Hi all,
> >>>
> >>> Is there anything that blocks me from merging this?
> >>>
> >>> This plugin work has been ongoing for years and unless there are serious
> >>> objections I propose that this is merged.
> >>>
> >>> Jacek, is there anything missing that would prevent merging this?  
> >>
> >> There were issues raised by Sakari during last review, related to
> >> the way how v4l2 control bindings are defined. That discussion wasn't
> >> finished, so I stayed by my approach.  
> >
> > Are those things something that could be fixed later without
> > breaking binary apps? If not, then perhaps it is time to merge
> > this.  
> 
> They are related to the format of configuration file being introduced
> by this patch set, so there are no binary apps depending on it.

Ok. Yet, changing the configuration file format could be a pain
for the users. The best would be to add a format that would be
extensible enough to be able to add support for other needs in
the future.

> Few days ago I had a discussion with Sakari on IRC, and he pointed
> me to his patch set adding an extended version of his mediatext
> library RFC [0], the first version of which I included to my patch set,
> and extended a bit. A decision should be made if I should adapt my
> API to the mediatext RFC. RFC is not a stable ground though.
> 
> [0] https://www.spinics.net/lists/linux-media/msg103242.html

If I understood well, you're proposing to use this format for
the configuration file:

	link-conf "s5p-mipi-csis.0":1 -> "FIMC.0":0 [1]
	ctrl-to-subdev-conf 0x0098091f -> "fimc.0.capture"
	ctrl-to-subdev-conf 0x00980902 -> "S5C73M3"
	ctrl-to-subdev-conf 0x00980922 -> "fimc.0.capture"
	ctrl-to-subdev-conf 0x009a0914 -> "S5C73M3"

What if, instead, you use something like:

[group]
	key = value

type of configuration file?

This way, if one wants to extend it, all it is needed is to add
other groups or more key types.

The users with an older configuration won't suffer if newer
groups and newer key types were added.

E. g something like:

[link]
	source = "s5p-mipi-csis.0":1
	sink = "FIMC.0":0 [1]

[ctrl-to-subdev]
	ctrl_id = 0x0098091f
	subdev= "fimc.0.capture"

[ctrl-to-subdev]
	ctrl_id = 0x00980902
	subdev = "S5C73M3"

...


This way, Sakari's patches could use the same format to add mediatext
entries. E. g. something like (the actual content actually depends on
what is here - I'm just wild guessing from the tree diagram at the
RFC e-mail):

[mediatext]
	shell_script = foo
	devnode = /dev/video*
	devnode = /dev/mediaX
	devnode = /dev/v4l-subdev*

without needing to change the format of the file, and keeping it
backward compatible.

Regards,
Mauro
> 
> >> Other than that - I've tested it
> >> and it works fine both with GStreamer and my test app.
> >>
> >> Best regards,
> >> Jacek Anaszewski
> >>
> >>  
> >>> On 12/10/16 16:35, Jacek Anaszewski wrote:  
> >>>> This is a seventh version of the patch series adding a plugin for the
> >>>> Exynos4 camera. Last version [0] of the patch set was posted in
> >>>> January.
> >>>>
> >>>> The plugin doesn't link against libmediactl, but has its sources
> >>>> compiled in. Currently utils are built after the plugins, but
> >>>> libv4l-exynos4-camera plugin depends on the utils. In order to link
> >>>> the plugin against libmediactl the build system would have to be
> >>>> modified.
> >>>>
> >>>> ================
> >>>> Changes from v6:
> >>>> ================
> >>>>
> >>>> - close v4l2 sub-devices on media device release
> >>>> - moved non-generic code from libmediactl to the plugin
> >>>> - resigned from adding libmedia_ioctl library and moved all its
> >>>>   code to the plugin, since it depended on pipeline representation,
> >>>>   which was not generic for all possible media device topologies
> >>>> - used media_get_info()->name instead of adding media_entity_get_name
> >>>> - renamed media_get_backlinks_by_entity() to
> >>>> media_entity_get_backlinks(()
> >>>> - moved pipeline from struct media_device to the plugin
> >>>> - changed the way of associating video device file descriptor with
> >>>> media device
> >>>> - switched to using auto-generated media-bus-format-names.h header file
> >>>> - renamed v4l2-ctrl-redir config entry name to v4l2-ctrl-binding
> >>>>
> >>>> ================
> >>>> Changes from v5:
> >>>> ================
> >>>>
> >>>> - fixed and tested use cases with S5K6A3 sensor and FIMC-IS-ISP
> >>>> - added conversion "colorspace id to string"
> >>>>
> >>>> ================
> >>>> Changes from v4:
> >>>> ================
> >>>>
> >>>> - removed some redundant functions for traversing media device graph
> >>>>   and switched over to using existing ones
> >>>> - avoided accessing struct v4l2_subdev from libmediactl
> >>>> - applied various improvements
> >>>>
> >>>> ================
> >>>> Changes from v3:
> >>>> ================
> >>>>
> >>>> - added struct v4l2_subdev and put entity fd and
> >>>>   information about supported controls to it
> >>>> - improved functions for negotiating and setting
> >>>>   pipeline format by using available libv4lsubdev API
> >>>> - applied minor improvements and cleanups
> >>>>
> >>>> ================
> >>>> Changes from v2:
> >>>> ================
> >>>>
> >>>> - switched to using mediatext library for parsing
> >>>>   the media device configuration
> >>>> - extended libmediactl
> >>>> - switched to using libmediactl
> >>>>
> >>>> ================
> >>>> 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 v4.8-rc2 (exynos4-is driver doesn't proble
> >>>> properly
> >>>> with current master branch of linux-media.git) with patches fixing
> >>>> several
> >>>> issues for Exynos4 camera: [1], [2], [3].
> >>>>
> >>>> The plugin expects a configuration file:
> >>>> /var/lib/libv4l/exynos4_capture_conf
> >>>>
> >>>> Exemplary configuration file for pipeline with sensor
> >>>> S5C73M3 (rear camera):
> >>>>
> >>>> ==========================================
> >>>>
> >>>> link-conf "s5p-mipi-csis.0":1 -> "FIMC.0":0 [1]
> >>>> v4l2-ctrl-binding 0x0098091f -> "fimc.0.capture"
> >>>> v4l2-ctrl-binding 0x00980902 -> "S5C73M3"
> >>>> v4l2-ctrl-binding 0x00980922 -> "fimc.0.capture"
> >>>> v4l2-ctrl-binding 0x009a0914 -> "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
> >>>> extra-controls="c,rotate=90,color_effects=2" !
> >>>> video/x-raw,width=960,height=720 ! fbdevsink
> >>>>
> >>>> Exemplary configuration file for pipeline with sensor
> >>>> S5K6A3 (front camera):
> >>>>
> >>>> ==========================================
> >>>>
> >>>> link-conf "s5p-mipi-csis.1":1 -> "FIMC-LITE.1":0 [1]
> >>>> link-conf "FIMC-LITE.1":2 -> "FIMC-IS-ISP":0 [1]
> >>>> link-conf "FIMC-IS-ISP":1 -> "FIMC.0":1 [1]
> >>>>
> >>>> ==========================================
> >>>>
> >>>> gst-launch-1.0 v4l2src device=/dev/video1
> >>>> extra-controls="c,rotate=270,color_effects=2,horizontal_flip=1" !
> >>>> video/x-raw,width=960,height=920 ! fbdevsink
> >>>>
> >>>> Best Regards,
> >>>> Jacek Anaszewski
> >>>>
> >>>> [0] http://www.spinics.net/lists/linux-media/msg96510.html
> >>>> [1] https://patchwork.kernel.org/patch/9335197/
> >>>> [2] https://patchwork.kernel.org/patch/9270985/
> >>>> [3] https://patchwork.kernel.org/patch/9308923/
> >>>> [4] https://patchwork.kernel.org/patch/9335273/
> >>>>
> >>>>
> >>>> Jacek Anaszewski (7):
> >>>>   mediactl: Add support for v4l2-ctrl-binding config
> >>>>   mediatext: Add library
> >>>>   mediactl: Add media_entity_get_backlinks()
> >>>>   mediactl: Add media_device creation helpers
> >>>>   mediactl: libv4l2subdev: Add colorspace logging
> >>>>   mediactl: libv4l2subdev: add support for comparing mbus formats
> >>>>   Add a libv4l plugin for Exynos4 camera
> >>>>
> >>>>  configure.ac                                      |    1 +
> >>>>  lib/Makefile.am                                   |    5 +
> >>>>  lib/libv4l-exynos4-camera/Makefile.am             |   19 +
> >>>>  lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c | 1325
> >>>> +++++++++++++++++++++
> >>>>  utils/media-ctl/Makefile.am                       |   10 +-
> >>>>  utils/media-ctl/libmediactl.c                     |  152 ++-
> >>>>  utils/media-ctl/libmediatext.pc.in                |   10 +
> >>>>  utils/media-ctl/libv4l2subdev.c                   |  106 ++
> >>>>  utils/media-ctl/mediactl.h                        |   42 +
> >>>>  utils/media-ctl/mediatext-test.c                  |   64 +
> >>>>  utils/media-ctl/mediatext.c                       |  312 +++++
> >>>>  utils/media-ctl/mediatext.h                       |   52 +
> >>>>  utils/media-ctl/v4l2subdev.h                      |   50 +
> >>>>  13 files changed, 2144 insertions(+), 4 deletions(-)
> >>>>  create mode 100644 lib/libv4l-exynos4-camera/Makefile.am
> >>>>  create mode 100644 lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
> >>>>  create mode 100644 utils/media-ctl/libmediatext.pc.in
> >>>>  create mode 100644 utils/media-ctl/mediatext-test.c
> >>>>  create mode 100644 utils/media-ctl/mediatext.c
> >>>>  create mode 100644 utils/media-ctl/mediatext.h
> >>>>  
> >>>
> >>>
> >>>  
> >>
> >>
> >> --
> >> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> >> the body of a message to majordomo@vger.kernel.org
> >> More majordomo info at  http://vger.kernel.org/majordomo-info.html  
> >
> >
> > Thanks,
> > Mauro
> >
> >
> >  
> 
> 



Thanks,
Mauro

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers
  2016-10-12 14:35 ` [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers Jacek Anaszewski
@ 2016-11-24 12:17   ` Sakari Ailus
  2016-11-24 13:50     ` Jacek Anaszewski
  2016-12-08 22:04     ` Jacek Anaszewski
  0 siblings, 2 replies; 35+ messages in thread
From: Sakari Ailus @ 2016-11-24 12:17 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki

Hi Jacek,

Thanks for the patchset.

On Wed, Oct 12, 2016 at 04:35:19PM +0200, Jacek Anaszewski wrote:
> Add helper functions that allow for easy instantiation of media_device
> object basing on whether the media device contains v4l2 subdev with
> given file descriptor.

Doesn't this work with video nodes as well? That's what you seem to be using
it for later on. And I think that's actually more useful.

The existing implementation uses udev to look up devices. Could you use
libudev device enumeration API to find the media devices, and fall back to
sysfs if udev doesn't work? There seems to be a reasonable-looking example
here:

<URL:http://stackoverflow.com/questions/25361042/how-to-list-usb-mass-storage-devices-programatically-using-libudev-in-linux>

> 
> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  utils/media-ctl/libmediactl.c | 131 +++++++++++++++++++++++++++++++++++++++++-
>  utils/media-ctl/mediactl.h    |  27 +++++++++
>  2 files changed, 156 insertions(+), 2 deletions(-)
> 
> diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c
> index 155b65f..d347a40 100644
> --- a/utils/media-ctl/libmediactl.c
> +++ b/utils/media-ctl/libmediactl.c
> @@ -27,6 +27,7 @@
>  #include <sys/sysmacros.h>
>  
>  #include <ctype.h>
> +#include <dirent.h>
>  #include <errno.h>
>  #include <fcntl.h>
>  #include <stdbool.h>
> @@ -440,8 +441,9 @@ static int media_get_devname_udev(struct udev *udev,
>  		return -EINVAL;
>  
>  	devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor);
> -	media_dbg(entity->media, "looking up device: %u:%u\n",
> -		  major(devnum), minor(devnum));
> +	if (entity->media)
> +		media_dbg(entity->media, "looking up device: %u:%u\n",
> +			  major(devnum), minor(devnum));
>  	device = udev_device_new_from_devnum(udev, 'c', devnum);
>  	if (device) {
>  		p = udev_device_get_devnode(device);
> @@ -523,6 +525,7 @@ static int media_get_devname_sysfs(struct media_entity *entity)
>  	return 0;
>  }
>  
> +

Unrelated change.

>  static int media_enum_entities(struct media_device *media)
>  {
>  	struct media_entity *entity;
> @@ -707,6 +710,92 @@ struct media_device *media_device_new(const char *devnode)
>  	return media;
>  }
>  
> +struct media_device *media_device_new_by_subdev_fd(int fd, struct media_entity **fd_entity)
> +{
> +	char video_devname[32], device_dir_path[256], media_dev_path[256], media_major_minor[10];
> +	struct media_device *media = NULL;
> +	struct dirent *entry;
> +	struct media_entity tmp_entity;
> +	DIR *device_dir;
> +	struct udev *udev;
> +	char *p;
> +	int ret, i;
> +
> +	if (fd_entity == NULL)
> +		return NULL;
> +
> +	ret = media_get_devname_by_fd(fd, video_devname);
> +	if (ret < 0)
> +		return NULL;
> +
> +	p = strrchr(video_devname, '/');
> +	if (p == NULL)
> +		return NULL;
> +
> +	ret = media_udev_open(&udev);
> +	if (ret < 0)
> +		return NULL;
> +
> +	sprintf(device_dir_path, "/sys/class/video4linux/%s/device/", p + 1);
> +
> +	device_dir = opendir(device_dir_path);
> +	if (device_dir == NULL)
> +		return NULL;
> +
> +	while ((entry = readdir(device_dir))) {
> +		if (strncmp(entry->d_name, "media", 4))

Why 4? And isn't entry->d_name nul-terminated, so you could use strcmp()?

> +			continue;
> +
> +		sprintf(media_dev_path, "%s%s/dev", device_dir_path, entry->d_name);
> +
> +		fd = open(media_dev_path, O_RDONLY);
> +		if (fd < 0)
> +			continue;
> +
> +		ret = read(fd, media_major_minor, sizeof(media_major_minor));
> +		if (ret < 0)
> +			continue;
> +
> +		sscanf(media_major_minor, "%d:%d", &tmp_entity.info.dev.major, &tmp_entity.info.dev.minor);

This would be better split on two lines.

> +
> +		/* Try to get the device name via udev */
> +		if (media_get_devname_udev(udev, &tmp_entity)) {
> +			/* Fall back to get the device name via sysfs */
> +			if (media_get_devname_sysfs(&tmp_entity))
> +				continue;
> +		}
> +
> +		media = media_device_new(tmp_entity.devname);
> +		if (media == NULL)
> +			continue;
> +
> +		ret = media_device_enumerate(media);
> +		if (ret < 0) {
> +			media_dbg(media, "Failed to enumerate %s (%d)\n",
> +				  tmp_entity.devname, ret);
> +			media_device_unref(media);
> +			media = NULL;
> +			continue;
> +		}
> +
> +		/* Get the entity associated with given fd */
> +		for (i = 0; i < media->entities_count; i++) {
> +			struct media_entity *entity = &media->entities[i];
> +
> +			if (!strcmp(entity->devname, video_devname)) {
> +				*fd_entity = &media->entities[i];
> +				break;
> +			}
> +		}

What if you exit the loop without finding the entity you were looking for?

> +
> +		break;
> +	}
> +
> +	media_udev_close(udev);
> +
> +	return media;
> +}
> +
>  struct media_device *media_device_new_emulated(struct media_device_info *info)
>  {
>  	struct media_device *media;
> @@ -748,6 +837,44 @@ void media_device_unref(struct media_device *media)
>  	free(media);
>  }
>  
> +int media_get_devname_by_fd(int fd, char *node_name)
> +{
> +	struct udev *udev;
> +	struct media_entity tmp_entity;
> +	struct stat stat;
> +	int ret, ret_udev;
> +
> +	if (node_name == NULL)
> +		return -EINVAL;
> +
> +	ret = fstat(fd, &stat);
> +	if (ret < 0)
> +		return -errno;
> +
> +	tmp_entity.info.v4l.major = MAJOR(stat.st_rdev);
> +	tmp_entity.info.v4l.minor = MINOR(stat.st_rdev);
> +
> +	ret_udev = media_udev_open(&udev);
> +	if (ret_udev < 0)
> +		printf("Can't get udev context\n");
> +
> +	/* Try to get the device name via udev */
> +	ret = media_get_devname_udev(udev, &tmp_entity);
> +	if (!ret)
> +		goto out;
> +
> +	ret = media_get_devname_sysfs(&tmp_entity);
> +	if (ret < 0)
> +		goto err_get_devname;
> +
> +out:
> +	strncpy(node_name, tmp_entity.devname, sizeof(tmp_entity.devname));

This seems risky. How does the caller know the maximum size? I'd simply
allocate the string, and document the caller is responsible for releasing
it.

> +err_get_devname:
> +	if (!ret_udev)
> +		media_udev_close(udev);
> +	return ret;
> +}
> +
>  int media_device_add_entity(struct media_device *media,
>  			    const struct media_entity_desc *desc,
>  			    const char *devnode)
> diff --git a/utils/media-ctl/mediactl.h b/utils/media-ctl/mediactl.h
> index b1f33cd..580a25a 100644
> --- a/utils/media-ctl/mediactl.h
> +++ b/utils/media-ctl/mediactl.h
> @@ -76,6 +76,21 @@ struct media_device *media_device_new(const char *devnode);
>  struct media_device *media_device_new_emulated(struct media_device_info *info);
>  
>  /**
> + * @brief Create a new media device contatning entity associated with v4l2 subdev fd.
> + * @param fd - file descriptor of a v4l2 subdev.
> + * @param fd_entity - media entity associated with the v4l2 subdev.
> + *
> + * Create a representation of the media device referenced by the v4l2-subdev.
> + * The media device instance is initialized with enumerated entities and links.
> + *
> + * Media devices are reference-counted, see media_device_ref() and
> + * media_device_unref() for more information.
> + *
> + * @return A pointer to the new media device or NULL if error occurred.
> + */
> +struct media_device *media_device_new_by_subdev_fd(int fd, struct media_entity **fd_entity);

I'd drop the "subdev_" part as both V4L2 device nodes and sub-devices work.

If you wish to keep this V4L2 specific, I suggest ...by_v4l2_fd().

> +
> +/**
>   * @brief Take a reference to the device.
>   * @param media - device instance.
>   *
> @@ -231,6 +246,18 @@ const struct media_link *media_entity_get_link(struct media_entity *entity,
>  const char *media_entity_get_devname(struct media_entity *entity);
>  
>  /**
> + * @brief Get the device node name by its file descriptor
> + * @param fd - file descriptor of a device.
> + * @param node_name - output device node name string.
> + *
> + * This function returns the full path and name to the device node corresponding
> + * to the given file descriptor.
> + *
> + * @return 0 on success, or a negative error code on failure.
> + */
> +int media_get_devname_by_fd(int fd, char *node_name);
> +
> +/**
>   * @brief Get the type of an entity.
>   * @param entity - the entity.
>   *

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera
  2016-11-24  9:10         ` Mauro Carvalho Chehab
@ 2016-11-24 12:20           ` Jacek Anaszewski
  0 siblings, 0 replies; 35+ messages in thread
From: Jacek Anaszewski @ 2016-11-24 12:20 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Hans Verkuil, linux-media, sakari.ailus, mchehab, m.szyprowski,
	s.nawrocki, Laurent Pinchart

On 11/24/2016 10:10 AM, Mauro Carvalho Chehab wrote:
> Em Thu, 24 Nov 2016 09:10:28 +0100
> Jacek Anaszewski <j.anaszewski@samsung.com> escreveu:
>
>> On 11/23/2016 10:51 PM, Mauro Carvalho Chehab wrote:
>>> Em Thu, 03 Nov 2016 13:13:12 +0100
>>> Jacek Anaszewski <j.anaszewski@samsung.com> escreveu:
>>>
>>>> Hi Hans,
>>>>
>>>> On 11/03/2016 12:51 PM, Hans Verkuil wrote:
>>>>> Hi all,
>>>>>
>>>>> Is there anything that blocks me from merging this?
>>>>>
>>>>> This plugin work has been ongoing for years and unless there are serious
>>>>> objections I propose that this is merged.
>>>>>
>>>>> Jacek, is there anything missing that would prevent merging this?
>>>>
>>>> There were issues raised by Sakari during last review, related to
>>>> the way how v4l2 control bindings are defined. That discussion wasn't
>>>> finished, so I stayed by my approach.
>>>
>>> Are those things something that could be fixed later without
>>> breaking binary apps? If not, then perhaps it is time to merge
>>> this.
>>
>> They are related to the format of configuration file being introduced
>> by this patch set, so there are no binary apps depending on it.
>
> Ok. Yet, changing the configuration file format could be a pain
> for the users. The best would be to add a format that would be
> extensible enough to be able to add support for other needs in
> the future.
>
>> Few days ago I had a discussion with Sakari on IRC, and he pointed
>> me to his patch set adding an extended version of his mediatext
>> library RFC [0], the first version of which I included to my patch set,
>> and extended a bit. A decision should be made if I should adapt my
>> API to the mediatext RFC. RFC is not a stable ground though.
>>
>> [0] https://www.spinics.net/lists/linux-media/msg103242.html
>
> If I understood well, you're proposing to use this format for
> the configuration file:
>
> 	link-conf "s5p-mipi-csis.0":1 -> "FIMC.0":0 [1]
> 	ctrl-to-subdev-conf 0x0098091f -> "fimc.0.capture"
> 	ctrl-to-subdev-conf 0x00980902 -> "S5C73M3"
> 	ctrl-to-subdev-conf 0x00980922 -> "fimc.0.capture"
> 	ctrl-to-subdev-conf 0x009a0914 -> "S5C73M3"
>
> What if, instead, you use something like:
>
> [group]
> 	key = value
>
> type of configuration file?
>
> This way, if one wants to extend it, all it is needed is to add
> other groups or more key types.
>
> The users with an older configuration won't suffer if newer
> groups and newer key types were added.
>
> E. g something like:
>
> [link]
> 	source = "s5p-mipi-csis.0":1
> 	sink = "FIMC.0":0 [1]
>
> [ctrl-to-subdev]
> 	ctrl_id = 0x0098091f
> 	subdev= "fimc.0.capture"
>
> [ctrl-to-subdev]
> 	ctrl_id = 0x00980902
> 	subdev = "S5C73M3"
>
> ...
>
>
> This way, Sakari's patches could use the same format to add mediatext
> entries. E. g. something like (the actual content actually depends on
> what is here - I'm just wild guessing from the tree diagram at the
> RFC e-mail):
>
> [mediatext]
> 	shell_script = foo
> 	devnode = /dev/video*
> 	devnode = /dev/mediaX
> 	devnode = /dev/v4l-subdev*
>
> without needing to change the format of the file, and keeping it
> backward compatible.

First version of this patch set used similar configuration format
but Sakari asked me to switch to the format accepted by media-ctl,
which the first version of mediatext adhered to.

Sakari has pledged to review this patch set, so we will be able
to decide on how to proceed no sooner than that.

Best regards,
Jacek Anaszewski


> Regards,
> Mauro
>>
>>>> Other than that - I've tested it
>>>> and it works fine both with GStreamer and my test app.
>>>>
>>>> Best regards,
>>>> Jacek Anaszewski
>>>>
>>>>
>>>>> On 12/10/16 16:35, Jacek Anaszewski wrote:
>>>>>> This is a seventh version of the patch series adding a plugin for the
>>>>>> Exynos4 camera. Last version [0] of the patch set was posted in
>>>>>> January.
>>>>>>
>>>>>> The plugin doesn't link against libmediactl, but has its sources
>>>>>> compiled in. Currently utils are built after the plugins, but
>>>>>> libv4l-exynos4-camera plugin depends on the utils. In order to link
>>>>>> the plugin against libmediactl the build system would have to be
>>>>>> modified.
>>>>>>
>>>>>> ================
>>>>>> Changes from v6:
>>>>>> ================
>>>>>>
>>>>>> - close v4l2 sub-devices on media device release
>>>>>> - moved non-generic code from libmediactl to the plugin
>>>>>> - resigned from adding libmedia_ioctl library and moved all its
>>>>>>   code to the plugin, since it depended on pipeline representation,
>>>>>>   which was not generic for all possible media device topologies
>>>>>> - used media_get_info()->name instead of adding media_entity_get_name
>>>>>> - renamed media_get_backlinks_by_entity() to
>>>>>> media_entity_get_backlinks(()
>>>>>> - moved pipeline from struct media_device to the plugin
>>>>>> - changed the way of associating video device file descriptor with
>>>>>> media device
>>>>>> - switched to using auto-generated media-bus-format-names.h header file
>>>>>> - renamed v4l2-ctrl-redir config entry name to v4l2-ctrl-binding
>>>>>>
>>>>>> ================
>>>>>> Changes from v5:
>>>>>> ================
>>>>>>
>>>>>> - fixed and tested use cases with S5K6A3 sensor and FIMC-IS-ISP
>>>>>> - added conversion "colorspace id to string"
>>>>>>
>>>>>> ================
>>>>>> Changes from v4:
>>>>>> ================
>>>>>>
>>>>>> - removed some redundant functions for traversing media device graph
>>>>>>   and switched over to using existing ones
>>>>>> - avoided accessing struct v4l2_subdev from libmediactl
>>>>>> - applied various improvements
>>>>>>
>>>>>> ================
>>>>>> Changes from v3:
>>>>>> ================
>>>>>>
>>>>>> - added struct v4l2_subdev and put entity fd and
>>>>>>   information about supported controls to it
>>>>>> - improved functions for negotiating and setting
>>>>>>   pipeline format by using available libv4lsubdev API
>>>>>> - applied minor improvements and cleanups
>>>>>>
>>>>>> ================
>>>>>> Changes from v2:
>>>>>> ================
>>>>>>
>>>>>> - switched to using mediatext library for parsing
>>>>>>   the media device configuration
>>>>>> - extended libmediactl
>>>>>> - switched to using libmediactl
>>>>>>
>>>>>> ================
>>>>>> 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 v4.8-rc2 (exynos4-is driver doesn't proble
>>>>>> properly
>>>>>> with current master branch of linux-media.git) with patches fixing
>>>>>> several
>>>>>> issues for Exynos4 camera: [1], [2], [3].
>>>>>>
>>>>>> The plugin expects a configuration file:
>>>>>> /var/lib/libv4l/exynos4_capture_conf
>>>>>>
>>>>>> Exemplary configuration file for pipeline with sensor
>>>>>> S5C73M3 (rear camera):
>>>>>>
>>>>>> ==========================================
>>>>>>
>>>>>> link-conf "s5p-mipi-csis.0":1 -> "FIMC.0":0 [1]
>>>>>> v4l2-ctrl-binding 0x0098091f -> "fimc.0.capture"
>>>>>> v4l2-ctrl-binding 0x00980902 -> "S5C73M3"
>>>>>> v4l2-ctrl-binding 0x00980922 -> "fimc.0.capture"
>>>>>> v4l2-ctrl-binding 0x009a0914 -> "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
>>>>>> extra-controls="c,rotate=90,color_effects=2" !
>>>>>> video/x-raw,width=960,height=720 ! fbdevsink
>>>>>>
>>>>>> Exemplary configuration file for pipeline with sensor
>>>>>> S5K6A3 (front camera):
>>>>>>
>>>>>> ==========================================
>>>>>>
>>>>>> link-conf "s5p-mipi-csis.1":1 -> "FIMC-LITE.1":0 [1]
>>>>>> link-conf "FIMC-LITE.1":2 -> "FIMC-IS-ISP":0 [1]
>>>>>> link-conf "FIMC-IS-ISP":1 -> "FIMC.0":1 [1]
>>>>>>
>>>>>> ==========================================
>>>>>>
>>>>>> gst-launch-1.0 v4l2src device=/dev/video1
>>>>>> extra-controls="c,rotate=270,color_effects=2,horizontal_flip=1" !
>>>>>> video/x-raw,width=960,height=920 ! fbdevsink
>>>>>>
>>>>>> Best Regards,
>>>>>> Jacek Anaszewski
>>>>>>
>>>>>> [0] http://www.spinics.net/lists/linux-media/msg96510.html
>>>>>> [1] https://patchwork.kernel.org/patch/9335197/
>>>>>> [2] https://patchwork.kernel.org/patch/9270985/
>>>>>> [3] https://patchwork.kernel.org/patch/9308923/
>>>>>> [4] https://patchwork.kernel.org/patch/9335273/
>>>>>>
>>>>>>
>>>>>> Jacek Anaszewski (7):
>>>>>>   mediactl: Add support for v4l2-ctrl-binding config
>>>>>>   mediatext: Add library
>>>>>>   mediactl: Add media_entity_get_backlinks()
>>>>>>   mediactl: Add media_device creation helpers
>>>>>>   mediactl: libv4l2subdev: Add colorspace logging
>>>>>>   mediactl: libv4l2subdev: add support for comparing mbus formats
>>>>>>   Add a libv4l plugin for Exynos4 camera
>>>>>>
>>>>>>  configure.ac                                      |    1 +
>>>>>>  lib/Makefile.am                                   |    5 +
>>>>>>  lib/libv4l-exynos4-camera/Makefile.am             |   19 +
>>>>>>  lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c | 1325
>>>>>> +++++++++++++++++++++
>>>>>>  utils/media-ctl/Makefile.am                       |   10 +-
>>>>>>  utils/media-ctl/libmediactl.c                     |  152 ++-
>>>>>>  utils/media-ctl/libmediatext.pc.in                |   10 +
>>>>>>  utils/media-ctl/libv4l2subdev.c                   |  106 ++
>>>>>>  utils/media-ctl/mediactl.h                        |   42 +
>>>>>>  utils/media-ctl/mediatext-test.c                  |   64 +
>>>>>>  utils/media-ctl/mediatext.c                       |  312 +++++
>>>>>>  utils/media-ctl/mediatext.h                       |   52 +
>>>>>>  utils/media-ctl/v4l2subdev.h                      |   50 +
>>>>>>  13 files changed, 2144 insertions(+), 4 deletions(-)
>>>>>>  create mode 100644 lib/libv4l-exynos4-camera/Makefile.am
>>>>>>  create mode 100644 lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
>>>>>>  create mode 100644 utils/media-ctl/libmediatext.pc.in
>>>>>>  create mode 100644 utils/media-ctl/mediatext-test.c
>>>>>>  create mode 100644 utils/media-ctl/mediatext.c
>>>>>>  create mode 100644 utils/media-ctl/mediatext.h
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-media" in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>>
>>> Thanks,
>>> Mauro
>>>
>>>
>>>
>>
>>
>
>
>
> Thanks,
> Mauro
>
>
>



^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 5/7] mediactl: libv4l2subdev: Add colorspace logging
  2016-10-12 14:35 ` [PATCH v4l-utils v7 5/7] mediactl: libv4l2subdev: Add colorspace logging Jacek Anaszewski
@ 2016-11-24 12:28   ` Sakari Ailus
  0 siblings, 0 replies; 35+ messages in thread
From: Sakari Ailus @ 2016-11-24 12:28 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki


Hi Jacek,

On Wed, Oct 12, 2016 at 04:35:20PM +0200, Jacek Anaszewski wrote:
> Add a function for obtaining colorspace name by id.
> 
> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  utils/media-ctl/libv4l2subdev.c | 32 ++++++++++++++++++++++++++++++++
>  utils/media-ctl/v4l2subdev.h    | 10 ++++++++++
>  2 files changed, 42 insertions(+)
> 
> diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c
> index 4f8ee7f..31393bb 100644
> --- a/utils/media-ctl/libv4l2subdev.c
> +++ b/utils/media-ctl/libv4l2subdev.c
> @@ -33,6 +33,7 @@
>  #include <unistd.h>
>  
>  #include <linux/v4l2-subdev.h>
> +#include <linux/videodev2.h>
>  
>  #include "mediactl.h"
>  #include "mediactl-priv.h"
> @@ -831,6 +832,37 @@ const char *v4l2_subdev_pixelcode_to_string(enum v4l2_mbus_pixelcode code)
>  	return "unknown";
>  }
>  
> +static struct {

static const

> +	const char *name;
> +	enum v4l2_colorspace cs;
> +} colorspaces[] = {
> +        { "DEFAULT", V4L2_COLORSPACE_DEFAULT },
> +        { "SMPTE170M", V4L2_COLORSPACE_SMPTE170M },
> +        { "SMPTE240M", V4L2_COLORSPACE_SMPTE240M },
> +        { "REC709", V4L2_COLORSPACE_REC709 },
> +        { "BT878", V4L2_COLORSPACE_BT878 },
> +        { "470_SYSTEM_M", V4L2_COLORSPACE_470_SYSTEM_M },
> +        { "470_SYSTEM_BG", V4L2_COLORSPACE_470_SYSTEM_BG },
> +        { "JPEG", V4L2_COLORSPACE_JPEG },
> +        { "SRGB", V4L2_COLORSPACE_SRGB },
> +        { "ADOBERGB", V4L2_COLORSPACE_ADOBERGB },
> +        { "BT2020", V4L2_COLORSPACE_BT2020 },
> +        { "RAW", V4L2_COLORSPACE_RAW },
> +        { "DCI_P3", V4L2_COLORSPACE_DCI_P3 },
> +};
> +
> +const char *v4l2_subdev_colorspace_to_string(enum v4l2_colorspace cs)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(colorspaces); ++i) {
> +		if (colorspaces[i].cs == cs)
> +			return colorspaces[i].name;
> +	}
> +
> +	return "unknown";
> +}
> +
>  enum v4l2_mbus_pixelcode v4l2_subdev_string_to_pixelcode(const char *string)
>  {
>  	unsigned int i;
> diff --git a/utils/media-ctl/v4l2subdev.h b/utils/media-ctl/v4l2subdev.h
> index 4dee6b1..cf1250d 100644
> --- a/utils/media-ctl/v4l2subdev.h
> +++ b/utils/media-ctl/v4l2subdev.h
> @@ -278,6 +278,16 @@ int v4l2_subdev_parse_setup_formats(struct media_device *media, const char *p);
>  const char *v4l2_subdev_pixelcode_to_string(enum v4l2_mbus_pixelcode code);
>  
>  /**
> + * @brief Convert colorspace to string.
> + * @param code - input string
> + *
> + * Convert colorspace @a to a human-readable string.

@a code ?

With these,

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

> + *
> + * @return A pointer to a string on success, NULL on failure.
> + */
> +const char *v4l2_subdev_colorspace_to_string(enum v4l2_colorspace cs);
> +
> +/**
>   * @brief Parse string to media bus pixel code.
>   * @param string - nul terminalted string, textual media bus pixel code
>   *

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 3/7] mediactl: Add media_entity_get_backlinks()
  2016-10-12 14:35 ` [PATCH v4l-utils v7 3/7] mediactl: Add media_entity_get_backlinks() Jacek Anaszewski
@ 2016-11-24 12:40   ` Sakari Ailus
  2016-11-24 14:00     ` Jacek Anaszewski
  0 siblings, 1 reply; 35+ messages in thread
From: Sakari Ailus @ 2016-11-24 12:40 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski,
	s.nawrocki, laurent.pinchart

Hi Jacek,

On Wed, Oct 12, 2016 at 04:35:18PM +0200, Jacek Anaszewski wrote:
> Add a new graph helper useful for discovering video pipeline.
> 
> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  utils/media-ctl/libmediactl.c | 21 +++++++++++++++++++++
>  utils/media-ctl/mediactl.h    | 15 +++++++++++++++
>  2 files changed, 36 insertions(+)
> 
> diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c
> index 91ed003..155b65f 100644
> --- a/utils/media-ctl/libmediactl.c
> +++ b/utils/media-ctl/libmediactl.c
> @@ -36,6 +36,7 @@
>  #include <unistd.h>
>  
>  #include <linux/media.h>
> +#include <linux/kdev_t.h>

Is there something that needs this one in the patch?

>  #include <linux/videodev2.h>
>  
>  #include "mediactl.h"
> @@ -172,6 +173,26 @@ const struct media_entity_desc *media_entity_get_info(struct media_entity *entit
>  	return &entity->info;
>  }
>  
> +int media_entity_get_backlinks(struct media_entity *entity,
> +				struct media_link **backlinks,
> +				unsigned int *num_backlinks)
> +{
> +	unsigned int num_bklinks = 0;
> +	int i;
> +
> +	if (entity == NULL || backlinks == NULL || num_backlinks == NULL)
> +		return -EINVAL;
> +

If you have an interface that accesses a memory buffer of unknown size, you
need to verify that the user has provided a buffer large enough.

How about using the num_backlinks argument to provide the maximum size to
the function, and passing the actual number to the user, the latter of which
you already do?

Alternatively, an iterator style API could be nice as well. Up to you.

I wonder what Laurent thinks.

> +	for (i = 0; i < entity->num_links; ++i)
> +		if ((entity->links[i].flags & MEDIA_LNK_FL_ENABLED) &&
> +		    (entity->links[i].sink->entity == entity))
> +			backlinks[num_bklinks++] = &entity->links[i];
> +
> +	*num_backlinks = num_bklinks;
> +
> +	return 0;
> +}
> +
>  /* -----------------------------------------------------------------------------
>   * Open/close
>   */
> diff --git a/utils/media-ctl/mediactl.h b/utils/media-ctl/mediactl.h
> index 336cbf9..b1f33cd 100644
> --- a/utils/media-ctl/mediactl.h
> +++ b/utils/media-ctl/mediactl.h
> @@ -434,6 +434,20 @@ int media_parse_setup_link(struct media_device *media,
>  int media_parse_setup_links(struct media_device *media, const char *p);
>  
>  /**
> + * @brief Get entity's enabled backlinks
> + * @param entity - media entity.
> + * @param backlinks - array of pointers to matching backlinks.
> + * @param num_backlinks - number of matching backlinks.
> + *
> + * Get links that are connected to the entity sink pads.
> + *
> + * @return 0 on success, or a negative error code on failure.
> + */
> +int media_entity_get_backlinks(struct media_entity *entity,
> +				struct media_link **backlinks,
> +				unsigned int *num_backlinks);
> +
> +/**
>   * @brief Get v4l2_subdev for the entity
>   * @param entity - media entity
>   *
> @@ -443,4 +457,5 @@ int media_parse_setup_links(struct media_device *media, const char *p);
>   */
>  struct v4l2_subdev *media_entity_get_v4l2_subdev(struct media_entity *entity);
>  
> +

Unrelated change.

>  #endif

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 2/7] mediatext: Add library
  2016-10-12 14:35 ` [PATCH v4l-utils v7 2/7] mediatext: Add library Jacek Anaszewski
@ 2016-11-24 13:01   ` Sakari Ailus
  2016-11-24 14:04     ` Jacek Anaszewski
  0 siblings, 1 reply; 35+ messages in thread
From: Sakari Ailus @ 2016-11-24 13:01 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski,
	s.nawrocki, Teemu Tuominen

Hi Jacek,

On Wed, Oct 12, 2016 at 04:35:17PM +0200, Jacek Anaszewski wrote:
> libmediatext is a helper library for converting configurations (Media
> controller links, V4L2 controls and V4L2 sub-device media bus formats and
> selections) from text-based form into IOCTLs.
> 
> libmediatext depends on libv4l2subdev and libmediactl.

I'm not very happy with the plugin using the mediatext library as it was in
2013. I've heavily changed (and extended) the library since to cover
additional use cases such as the request API:

<URL:http://www.spinics.net/lists/linux-media/msg103242.html>

This version is not meaningfully extensible in forward-compatible way. In
order to resolve the matter quickly without making merging the further
developed library difficult, I propose merging the code to the plugin
itself. We could later check if it could be refactored.

How about that?

> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> Signed-off-by: Teemu Tuominen <teemu.tuominen@intel.com>
> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> ---
>  utils/media-ctl/Makefile.am        |  10 +-
>  utils/media-ctl/libmediatext.pc.in |  10 ++
>  utils/media-ctl/mediatext-test.c   |  64 ++++++++
>  utils/media-ctl/mediatext.c        | 312 +++++++++++++++++++++++++++++++++++++
>  utils/media-ctl/mediatext.h        |  52 +++++++
>  5 files changed, 446 insertions(+), 2 deletions(-)
>  create mode 100644 utils/media-ctl/libmediatext.pc.in
>  create mode 100644 utils/media-ctl/mediatext-test.c
>  create mode 100644 utils/media-ctl/mediatext.c
>  create mode 100644 utils/media-ctl/mediatext.h
> 
> diff --git a/utils/media-ctl/Makefile.am b/utils/media-ctl/Makefile.am
> index ee7dcc9..2f12357 100644
> --- a/utils/media-ctl/Makefile.am
> +++ b/utils/media-ctl/Makefile.am
> @@ -1,4 +1,4 @@
> -noinst_LTLIBRARIES = libmediactl.la libv4l2subdev.la
> +noinst_LTLIBRARIES = libmediactl.la libv4l2subdev.la libmediatext.la
>  
>  libmediactl_la_SOURCES = libmediactl.c mediactl-priv.h
>  libmediactl_la_CFLAGS = -static $(LIBUDEV_CFLAGS)
> @@ -21,9 +21,15 @@ libv4l2subdev_la_LIBADD = libmediactl.la
>  libv4l2subdev_la_CFLAGS = -static
>  libv4l2subdev_la_LDFLAGS = -static
>  
> +libmediatext_la_SOURCES = mediatext.c
> +libmediatext_la_CFLAGS = -static $(LIBUDEV_CFLAGS)
> +libmediatext_la_LDFLAGS = -static $(LIBUDEV_LIBS)
> +
>  mediactl_includedir=$(includedir)/mediactl
>  noinst_HEADERS = mediactl.h v4l2subdev.h
>  
> -bin_PROGRAMS = media-ctl
> +bin_PROGRAMS = media-ctl mediatext-test
>  media_ctl_SOURCES = media-ctl.c options.c options.h tools.h
>  media_ctl_LDADD = libmediactl.la libv4l2subdev.la
> +mediatext_test_SOURCES = mediatext-test.c
> +mediatext_test_LDADD = libmediatext.la libmediactl.la libv4l2subdev.la
> diff --git a/utils/media-ctl/libmediatext.pc.in b/utils/media-ctl/libmediatext.pc.in
> new file mode 100644
> index 0000000..6aa6353
> --- /dev/null
> +++ b/utils/media-ctl/libmediatext.pc.in
> @@ -0,0 +1,10 @@
> +prefix=@prefix@
> +exec_prefix=@exec_prefix@
> +libdir=@libdir@
> +includedir=@includedir@
> +
> +Name: libmediatext
> +Description: Media controller and V4L2 text-based configuration library
> +Version: @PACKAGE_VERSION@
> +Cflags: -I${includedir}
> +Libs: -L${libdir} -lmediatext
> diff --git a/utils/media-ctl/mediatext-test.c b/utils/media-ctl/mediatext-test.c
> new file mode 100644
> index 0000000..b8b9282
> --- /dev/null
> +++ b/utils/media-ctl/mediatext-test.c
> @@ -0,0 +1,64 @@
> +/*
> + * libmediatext test program
> + *
> + * Copyright (C) 2013 Intel Corporation
> + *
> + * Contact: Sakari Ailus <sakari.ailus@linux.intel.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.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "mediactl.h"
> +#include "mediatext.h"
> +
> +int main(int argc, char *argv[])
> +{
> +	struct media_device *device;
> +	int rval;
> +
> +	if (argc != 3) {
> +		fprintf(stderr, "usage: %s <media device> <string>\n\n", argv[0]);
> +		fprintf(stderr, "\tstring := [ v4l2-ctrl |?v4l2-mbus | link-reset | link-conf]\n\n");
> +		fprintf(stderr, "\tv4l2-ctrl := \"entity\" ctrl_type ctrl_id ctrl_value\n");
> +		fprintf(stderr, "\tctrl_type := [ int | int64 | bitmask ]\n");
> +		fprintf(stderr, "\tctrl_value := [ %%d | %%PRId64 | bitmask_value ]\n");
> +		fprintf(stderr, "\tbitmask_value := b<binary_number>\n\n");
> +		fprintf(stderr, "\tv4l2-mbus := \n");
> +		fprintf(stderr, "\tlink-conf := \"entity\":pad -> \"entity\":pad[link-flags]\n");
> +		fprintf(stderr, "\tv4l2-ctrl-binding := ctrl_id -> \"entity\"\n");
> +		return EXIT_FAILURE;
> +	}
> +
> +	device = media_device_new(argv[1]);
> +	if (!device)
> +		return EXIT_FAILURE;
> +
> +	rval = media_device_enumerate(device);
> +	if (rval)
> +		return EXIT_FAILURE;
> +
> +	rval = mediatext_parse(device, argv[2]);
> +	if (rval) {
> +		fprintf(stderr, "bad string %s (%s)\n", argv[2], strerror(-rval));
> +		return EXIT_FAILURE;
> +	}
> +
> +	media_device_unref(device);
> +
> +	return EXIT_SUCCESS;
> +}
> diff --git a/utils/media-ctl/mediatext.c b/utils/media-ctl/mediatext.c
> new file mode 100644
> index 0000000..9faf0db
> --- /dev/null
> +++ b/utils/media-ctl/mediatext.c
> @@ -0,0 +1,312 @@
> +/*
> + * Media controller text-based configuration library
> + *
> + * Copyright (C) 2013 Intel Corporation
> + *
> + * Contact: Sakari Ailus <sakari.ailus@linux.intel.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.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <sys/ioctl.h>
> +
> +#include <ctype.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <inttypes.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <sys/stat.h>
> +
> +#include <linux/types.h>
> +
> +#include "mediactl.h"
> +#include "mediactl-priv.h"
> +#include "tools.h"
> +#include "v4l2subdev.h"
> +
> +struct parser {
> +	char *prefix;
> +	int (*parse)(struct media_device *media, const struct parser *p,
> +		     char *string);
> +	struct parser *next;
> +	bool no_args;
> +};
> +
> +static int parse(struct media_device *media, const struct parser *p, char *string)
> +{
> +	for (; p->prefix; p++) {
> +		size_t len = strlen(p->prefix);
> +
> +		if (strncmp(p->prefix, string, len) || string[len] != ' ')
> +			continue;
> +
> +		string += len;
> +
> +		for (; isspace(*string); string++);
> +
> +		if (p->no_args)
> +			return p->parse(media, p->next, NULL);
> +
> +		if (strlen(string) == 0)
> +			return -ENOEXEC;
> +
> +		return p->parse(media, p->next, string);
> +	}
> +
> +	media_dbg(media, "Unknown parser prefix\n");
> +
> +	return -ENOENT;
> +}
> +
> +struct ctrl_type {
> +	uint32_t type;
> +	char *str;
> +} ctrltypes[] = {
> +	{ V4L2_CTRL_TYPE_INTEGER, "int" },
> +	{ V4L2_CTRL_TYPE_MENU, "menu" },
> +	{ V4L2_CTRL_TYPE_INTEGER_MENU, "intmenu" },
> +	{ V4L2_CTRL_TYPE_BITMASK, "bitmask" },
> +	{ V4L2_CTRL_TYPE_INTEGER64, "int64" },
> +};
> +
> +static int parse_v4l2_ctrl_id(struct media_device *media, const struct parser *p,
> +			      char *string, char **endp, __u32 *ctrl_id)
> +{
> +	int rval;
> +
> +	for (; isspace(*string); string++);
> +	rval = sscanf(string, "0x%" PRIx32, ctrl_id);
> +	if (rval <= 0)
> +		return -EINVAL;
> +
> +	for (; !isspace(*string) && *string; string++);
> +	for (; isspace(*string); string++);
> +
> +	*endp = string;
> +
> +	return 0;
> +}
> +
> +/* adapted from yavta.c */
> +static int parse_v4l2_ctrl(struct media_device *media, const struct parser *p,
> +			   char *string)
> +{
> +	struct v4l2_ext_control ctrl = { 0 };
> +	struct v4l2_ext_controls ctrls = { .count = 1,
> +					   .controls = &ctrl };
> +	int64_t val;
> +	int rval;
> +	struct media_entity *entity;
> +	struct ctrl_type *ctype;
> +	unsigned int i;
> +
> +	entity = media_parse_entity(media, string, &string);
> +	if (!entity)
> +		return -ENOENT;
> +
> +	for (i = 0; i < ARRAY_SIZE(ctrltypes); i++)
> +		if (!strncmp(string, ctrltypes[i].str,
> +			     strlen(ctrltypes[i].str)))
> +			break;
> +
> +	if (i == ARRAY_SIZE(ctrltypes))
> +		return -ENOENT;
> +
> +	ctype = &ctrltypes[i];
> +
> +	string += strlen(ctrltypes[i].str);
> +
> +	rval = parse_v4l2_ctrl_id(media, p, string, &string, &ctrl.id);
> +	if (rval < 0)
> +		return -EINVAL;
> +
> +	ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(ctrl.id);
> +
> +	switch (ctype->type) {
> +	case V4L2_CTRL_TYPE_BITMASK:
> +		if (*string++ != 'b')
> +			return -EINVAL;
> +		while (*string == '1' || *string == '0') {
> +			val <<= 1;
> +			if (*string == '1')
> +				val++;
> +			string++;
> +		}
> +		break;
> +	default:
> +		rval = sscanf(string, "%" PRId64, &val);
> +		break;
> +	}
> +	if (rval <= 0)
> +		return -EINVAL;
> +
> +	media_dbg(media, "Setting control 0x%8.8x (type %s), value %" PRId64 "\n",
> +		  ctrl.id, ctype->str, val);
> +
> +	if (ctype->type == V4L2_CTRL_TYPE_INTEGER64)
> +		ctrl.value64 = val;
> +	else
> +		ctrl.value = val;
> +
> +	rval = v4l2_subdev_open(entity);
> +	if (rval < 0)
> +		return rval;
> +
> +	rval = ioctl(entity->sd->fd, VIDIOC_S_EXT_CTRLS, &ctrls);
> +	if (ctype->type != V4L2_CTRL_TYPE_INTEGER64) {
> +		if (rval != -1) {
> +			ctrl.value64 = ctrl.value;
> +		} else if (ctype->type != V4L2_CTRL_TYPE_STRING &&
> +			   (errno == EINVAL || errno == ENOTTY)) {
> +			struct v4l2_control old = { .id = ctrl.id,
> +						    .value = val };
> +
> +			rval = ioctl(entity->sd->fd, VIDIOC_S_CTRL, &old);
> +			if (rval != -1)
> +				ctrl.value64 = old.value;
> +		}
> +	}
> +	if (rval == -1) {
> +		media_dbg(media,
> +			  "Failed setting control 0x%8.8x: %s (%d) to value %"
> +			  PRId64 "\n", ctrl.id, strerror(errno), errno, val);
> +		return -errno;
> +	}
> +
> +	if (val != ctrl.value64)
> +		media_dbg(media, "Asking for %" PRId64 ", got %" PRId64 "\n",
> +			  val, ctrl.value64);
> +
> +	return 0;
> +}
> +
> +/*
> +
> +parse_name(string, end)
> +
> +*/
> +
> +int parse_v4l2_ctrl_binding(struct media_device *media, const struct parser *p,
> +			    char *string)
> +{
> +	struct media_entity *entity;
> +	struct v4l2_subdev *sd;
> +	__u32 ctrl_id;
> +	int rval;
> +
> +	rval = parse_v4l2_ctrl_id(media, p, string, &string, &ctrl_id);
> +	if (rval < 0)
> +		return -EINVAL;
> +
> +	for (; isspace(*string); ++string);
> +
> +	if (string[0] != '-' || string[1] != '>') {
> +		media_dbg(media, "Expected '->'\n");
> +		return -EINVAL;
> +	}
> +
> +	string += 2;
> +
> +	entity = media_parse_entity(media, string, &string);
> +	if (!entity)
> +		return -ENOENT;
> +
> +	rval = v4l2_subdev_supports_v4l2_ctrl(media, entity, ctrl_id);
> +	if (rval < 0) {
> +		media_dbg(media,
> +			  "Failed to check v4l2 control support for entity %s\n",
> +			  entity->info.name);
> +		return rval;
> +	}
> +
> +	sd = entity->sd;
> +
> +	sd->v4l2_ctrl_bindings = realloc(sd->v4l2_ctrl_bindings,
> +					 sizeof(*sd->v4l2_ctrl_bindings) *
> +					 (sd->num_v4l2_ctrl_bindings + 1));
> +	if (!sd->v4l2_ctrl_bindings)
> +		return -ENOMEM;
> +
> +	sd->v4l2_ctrl_bindings[sd->num_v4l2_ctrl_bindings] = ctrl_id;
> +	++sd->num_v4l2_ctrl_bindings;
> +
> +	return 0;
> +}
> +
> +static int parse_v4l2_mbus(struct media_device *media, const struct parser *p,
> +			   char *string)
> +{
> +	media_dbg(media, "Media bus format setup: %s\n", string);
> +	return v4l2_subdev_parse_setup_formats(media, string);
> +}
> +
> +static int parse_link_reset(struct media_device *media, const struct parser *p,
> +			    char *string)
> +{
> +	media_dbg(media, "Resetting links\n");
> +	return media_reset_links(media);
> +}
> +
> +static int parse_link_conf(struct media_device *media, const struct parser *p,
> +			   char *string)
> +{
> +	media_dbg(media, "Configuring links: %s\n", string);
> +	return media_parse_setup_links(media, string);
> +}
> +
> +static const struct parser parsers[] = {
> +	{ "v4l2-ctrl", parse_v4l2_ctrl },
> +	{ "v4l2-ctrl-binding", parse_v4l2_ctrl_binding },
> +	{ "v4l2-mbus", parse_v4l2_mbus },
> +	{ "link-reset", parse_link_reset, NULL, true },
> +	{ "link-conf", parse_link_conf },
> +	{ 0 }
> +};
> +
> +int mediatext_parse(struct media_device *media, char *string)
> +{
> +	return parse(media, parsers, string);
> +}
> +
> +int mediatext_parse_setup_config(struct media_device *device, const char *conf_path)
> +{
> +	char *line;
> +	size_t n = 0;
> +	FILE *f;
> +	int ret;
> +
> +	if (conf_path == NULL)
> +		return -EINVAL;
> +
> +	f = fopen(conf_path, "r");
> +	if (!f)
> +		return -EINVAL;
> +
> +	while (getline(&line, &n, f) != -1) {
> +		ret = mediatext_parse(device, line);
> +		if (ret < 0)
> +			goto err_parse;
> +		free(line);
> +		line = NULL;
> +		n = 0;
> +	}
> +
> +err_parse:
> +	fclose(f);
> +	return ret;
> +}
> diff --git a/utils/media-ctl/mediatext.h b/utils/media-ctl/mediatext.h
> new file mode 100644
> index 0000000..7dfbaf6
> --- /dev/null
> +++ b/utils/media-ctl/mediatext.h
> @@ -0,0 +1,52 @@
> +/*
> + * Media controller text-based configuration library
> + *
> + * Copyright (C) 2013 Intel Corporation
> + *
> + * Contact: Sakari Ailus <sakari.ailus@linux.intel.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.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __MEDIATEXT_H__
> +#define __MEDIATEXT_H__
> +
> +struct media_device;
> +
> +/**
> + * @brief Parse and apply media device command
> + * @param device - media device
> + * @param string - string to parse
> + *
> + * Parse media device command and apply it to the media device
> + * passed in the device argument.
> + *
> + * @return 0 on success, or a negative error code on failure.
> + */
> +int mediatext_parse(struct media_device *device, char *string);
> +
> +/**
> + * @brief Parse and apply media device configuration
> + * @param media - media device
> + * @param conf_path - path to the configuration file
> + *
> + * Parse the media device commands listed in the file under
> + * conf_path and apply them to the media device passed in the
> + * device argument.
> + *
> + * @return 0 on success, or a negative error code on failure.
> + */
> +int mediatext_parse_setup_config(struct media_device *device, const char *conf_path);
> +
> +#endif /* __MEDIATEXT_H__ */

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers
  2016-11-24 12:17   ` Sakari Ailus
@ 2016-11-24 13:50     ` Jacek Anaszewski
  2016-11-24 14:32       ` Sakari Ailus
  2016-12-08 22:04     ` Jacek Anaszewski
  1 sibling, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-11-24 13:50 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki

Hi Sakari,

Thanks for the review.

On 11/24/2016 01:17 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> Thanks for the patchset.
>
> On Wed, Oct 12, 2016 at 04:35:19PM +0200, Jacek Anaszewski wrote:
>> Add helper functions that allow for easy instantiation of media_device
>> object basing on whether the media device contains v4l2 subdev with
>> given file descriptor.
>
> Doesn't this work with video nodes as well? That's what you seem to be using
> it for later on. And I think that's actually more useful.

Exactly, thanks for spotting this.

s/v4l2 subdev/video device opened/

>
> The existing implementation uses udev to look up devices. Could you use
> libudev device enumeration API to find the media devices, and fall back to
> sysfs if udev doesn't work? There seems to be a reasonable-looking example
> here:
>
> <URL:http://stackoverflow.com/questions/25361042/how-to-list-usb-mass-storage-devices-programatically-using-libudev-in-linux>

I'll check that, thanks.

>>
>> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
>> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>  utils/media-ctl/libmediactl.c | 131 +++++++++++++++++++++++++++++++++++++++++-
>>  utils/media-ctl/mediactl.h    |  27 +++++++++
>>  2 files changed, 156 insertions(+), 2 deletions(-)
>>
>> diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c
>> index 155b65f..d347a40 100644
>> --- a/utils/media-ctl/libmediactl.c
>> +++ b/utils/media-ctl/libmediactl.c
>> @@ -27,6 +27,7 @@
>>  #include <sys/sysmacros.h>
>>
>>  #include <ctype.h>
>> +#include <dirent.h>
>>  #include <errno.h>
>>  #include <fcntl.h>
>>  #include <stdbool.h>
>> @@ -440,8 +441,9 @@ static int media_get_devname_udev(struct udev *udev,
>>  		return -EINVAL;
>>
>>  	devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor);
>> -	media_dbg(entity->media, "looking up device: %u:%u\n",
>> -		  major(devnum), minor(devnum));
>> +	if (entity->media)
>> +		media_dbg(entity->media, "looking up device: %u:%u\n",
>> +			  major(devnum), minor(devnum));
>>  	device = udev_device_new_from_devnum(udev, 'c', devnum);
>>  	if (device) {
>>  		p = udev_device_get_devnode(device);
>> @@ -523,6 +525,7 @@ static int media_get_devname_sysfs(struct media_entity *entity)
>>  	return 0;
>>  }
>>
>> +
>
> Unrelated change.
>
>>  static int media_enum_entities(struct media_device *media)
>>  {
>>  	struct media_entity *entity;
>> @@ -707,6 +710,92 @@ struct media_device *media_device_new(const char *devnode)
>>  	return media;
>>  }
>>
>> +struct media_device *media_device_new_by_subdev_fd(int fd, struct media_entity **fd_entity)
>> +{
>> +	char video_devname[32], device_dir_path[256], media_dev_path[256], media_major_minor[10];
>> +	struct media_device *media = NULL;
>> +	struct dirent *entry;
>> +	struct media_entity tmp_entity;
>> +	DIR *device_dir;
>> +	struct udev *udev;
>> +	char *p;
>> +	int ret, i;
>> +
>> +	if (fd_entity == NULL)
>> +		return NULL;
>> +
>> +	ret = media_get_devname_by_fd(fd, video_devname);
>> +	if (ret < 0)
>> +		return NULL;
>> +
>> +	p = strrchr(video_devname, '/');
>> +	if (p == NULL)
>> +		return NULL;
>> +
>> +	ret = media_udev_open(&udev);
>> +	if (ret < 0)
>> +		return NULL;
>> +
>> +	sprintf(device_dir_path, "/sys/class/video4linux/%s/device/", p + 1);
>> +
>> +	device_dir = opendir(device_dir_path);
>> +	if (device_dir == NULL)
>> +		return NULL;
>> +
>> +	while ((entry = readdir(device_dir))) {
>> +		if (strncmp(entry->d_name, "media", 4))
>
> Why 4? And isn't entry->d_name nul-terminated, so you could use strcmp()?

Media devices, as other devices, have numerical postfix, which is
not of our interest.

>> +			continue;
>> +
>> +		sprintf(media_dev_path, "%s%s/dev", device_dir_path, entry->d_name);
>> +
>> +		fd = open(media_dev_path, O_RDONLY);
>> +		if (fd < 0)
>> +			continue;
>> +
>> +		ret = read(fd, media_major_minor, sizeof(media_major_minor));
>> +		if (ret < 0)
>> +			continue;
>> +
>> +		sscanf(media_major_minor, "%d:%d", &tmp_entity.info.dev.major, &tmp_entity.info.dev.minor);
>
> This would be better split on two lines.

OK.

>> +
>> +		/* Try to get the device name via udev */
>> +		if (media_get_devname_udev(udev, &tmp_entity)) {
>> +			/* Fall back to get the device name via sysfs */
>> +			if (media_get_devname_sysfs(&tmp_entity))
>> +				continue;
>> +		}
>> +
>> +		media = media_device_new(tmp_entity.devname);
>> +		if (media == NULL)
>> +			continue;
>> +
>> +		ret = media_device_enumerate(media);
>> +		if (ret < 0) {
>> +			media_dbg(media, "Failed to enumerate %s (%d)\n",
>> +				  tmp_entity.devname, ret);
>> +			media_device_unref(media);
>> +			media = NULL;
>> +			continue;
>> +		}
>> +
>> +		/* Get the entity associated with given fd */
>> +		for (i = 0; i < media->entities_count; i++) {
>> +			struct media_entity *entity = &media->entities[i];
>> +
>> +			if (!strcmp(entity->devname, video_devname)) {
>> +				*fd_entity = &media->entities[i];
>> +				break;
>> +			}
>> +		}
>
> What if you exit the loop without finding the entity you were looking for?

Ah, right, this case is unhandled.

Adding below condition should cover that:

if (i == media->entities_count)
     media = NULL;

>> +
>> +		break;

This break should be removed and the one in the inner for loop above
should be replaced with goto here. Are you OK with that?

>> +	}
>> +
>> +	media_udev_close(udev);
>> +
>> +	return media;
>> +}
>> +
>>  struct media_device *media_device_new_emulated(struct media_device_info *info)
>>  {
>>  	struct media_device *media;
>> @@ -748,6 +837,44 @@ void media_device_unref(struct media_device *media)
>>  	free(media);
>>  }
>>
>> +int media_get_devname_by_fd(int fd, char *node_name)
>> +{
>> +	struct udev *udev;
>> +	struct media_entity tmp_entity;
>> +	struct stat stat;
>> +	int ret, ret_udev;
>> +
>> +	if (node_name == NULL)
>> +		return -EINVAL;
>> +
>> +	ret = fstat(fd, &stat);
>> +	if (ret < 0)
>> +		return -errno;
>> +
>> +	tmp_entity.info.v4l.major = MAJOR(stat.st_rdev);
>> +	tmp_entity.info.v4l.minor = MINOR(stat.st_rdev);
>> +
>> +	ret_udev = media_udev_open(&udev);
>> +	if (ret_udev < 0)
>> +		printf("Can't get udev context\n");
>> +
>> +	/* Try to get the device name via udev */
>> +	ret = media_get_devname_udev(udev, &tmp_entity);
>> +	if (!ret)
>> +		goto out;
>> +
>> +	ret = media_get_devname_sysfs(&tmp_entity);
>> +	if (ret < 0)
>> +		goto err_get_devname;
>> +
>> +out:
>> +	strncpy(node_name, tmp_entity.devname, sizeof(tmp_entity.devname));
>
> This seems risky. How does the caller know the maximum size? I'd simply
> allocate the string, and document the caller is responsible for releasing
> it.

OK.

>> +err_get_devname:
>> +	if (!ret_udev)
>> +		media_udev_close(udev);
>> +	return ret;
>> +}
>> +
>>  int media_device_add_entity(struct media_device *media,
>>  			    const struct media_entity_desc *desc,
>>  			    const char *devnode)
>> diff --git a/utils/media-ctl/mediactl.h b/utils/media-ctl/mediactl.h
>> index b1f33cd..580a25a 100644
>> --- a/utils/media-ctl/mediactl.h
>> +++ b/utils/media-ctl/mediactl.h
>> @@ -76,6 +76,21 @@ struct media_device *media_device_new(const char *devnode);
>>  struct media_device *media_device_new_emulated(struct media_device_info *info);
>>
>>  /**
>> + * @brief Create a new media device contatning entity associated with v4l2 subdev fd.
>> + * @param fd - file descriptor of a v4l2 subdev.
>> + * @param fd_entity - media entity associated with the v4l2 subdev.
>> + *
>> + * Create a representation of the media device referenced by the v4l2-subdev.
>> + * The media device instance is initialized with enumerated entities and links.
>> + *
>> + * Media devices are reference-counted, see media_device_ref() and
>> + * media_device_unref() for more information.
>> + *
>> + * @return A pointer to the new media device or NULL if error occurred.
>> + */
>> +struct media_device *media_device_new_by_subdev_fd(int fd, struct media_entity **fd_entity);
>
> I'd drop the "subdev_" part as both V4L2 device nodes and sub-devices work.
>
> If you wish to keep this V4L2 specific, I suggest ...by_v4l2_fd().

Right, I will rename it.

>> +
>> +/**
>>   * @brief Take a reference to the device.
>>   * @param media - device instance.
>>   *
>> @@ -231,6 +246,18 @@ const struct media_link *media_entity_get_link(struct media_entity *entity,
>>  const char *media_entity_get_devname(struct media_entity *entity);
>>
>>  /**
>> + * @brief Get the device node name by its file descriptor
>> + * @param fd - file descriptor of a device.
>> + * @param node_name - output device node name string.
>> + *
>> + * This function returns the full path and name to the device node corresponding
>> + * to the given file descriptor.
>> + *
>> + * @return 0 on success, or a negative error code on failure.
>> + */
>> +int media_get_devname_by_fd(int fd, char *node_name);
>> +
>> +/**
>>   * @brief Get the type of an entity.
>>   * @param entity - the entity.
>>   *
>


-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 3/7] mediactl: Add media_entity_get_backlinks()
  2016-11-24 12:40   ` Sakari Ailus
@ 2016-11-24 14:00     ` Jacek Anaszewski
  2016-11-24 14:26       ` Sakari Ailus
  0 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-11-24 14:00 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski,
	s.nawrocki, laurent.pinchart

Hi Sakari,

Thanks for the review.

On 11/24/2016 01:40 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Wed, Oct 12, 2016 at 04:35:18PM +0200, Jacek Anaszewski wrote:
>> Add a new graph helper useful for discovering video pipeline.
>>
>> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
>> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>  utils/media-ctl/libmediactl.c | 21 +++++++++++++++++++++
>>  utils/media-ctl/mediactl.h    | 15 +++++++++++++++
>>  2 files changed, 36 insertions(+)
>>
>> diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c
>> index 91ed003..155b65f 100644
>> --- a/utils/media-ctl/libmediactl.c
>> +++ b/utils/media-ctl/libmediactl.c
>> @@ -36,6 +36,7 @@
>>  #include <unistd.h>
>>
>>  #include <linux/media.h>
>> +#include <linux/kdev_t.h>
>
> Is there something that needs this one in the patch?

MAJOR and MINOR macros.

>
>>  #include <linux/videodev2.h>
>>
>>  #include "mediactl.h"
>> @@ -172,6 +173,26 @@ const struct media_entity_desc *media_entity_get_info(struct media_entity *entit
>>  	return &entity->info;
>>  }
>>
>> +int media_entity_get_backlinks(struct media_entity *entity,
>> +				struct media_link **backlinks,
>> +				unsigned int *num_backlinks)
>> +{
>> +	unsigned int num_bklinks = 0;
>> +	int i;
>> +
>> +	if (entity == NULL || backlinks == NULL || num_backlinks == NULL)
>> +		return -EINVAL;
>> +
>
> If you have an interface that accesses a memory buffer of unknown size, you
> need to verify that the user has provided a buffer large enough.
>
> How about using the num_backlinks argument to provide the maximum size to
> the function, and passing the actual number to the user, the latter of which
> you already do?

Sounds reasonable.

> Alternatively, an iterator style API could be nice as well. Up to you.

It would probably need an addition of some generic infrastructure.
I suppose that there is no such a feature in v4l-utils?

>
> I wonder what Laurent thinks.
>
>> +	for (i = 0; i < entity->num_links; ++i)
>> +		if ((entity->links[i].flags & MEDIA_LNK_FL_ENABLED) &&
>> +		    (entity->links[i].sink->entity == entity))
>> +			backlinks[num_bklinks++] = &entity->links[i];
>> +
>> +	*num_backlinks = num_bklinks;
>> +
>> +	return 0;
>> +}
>> +
>>  /* -----------------------------------------------------------------------------
>>   * Open/close
>>   */
>> diff --git a/utils/media-ctl/mediactl.h b/utils/media-ctl/mediactl.h
>> index 336cbf9..b1f33cd 100644
>> --- a/utils/media-ctl/mediactl.h
>> +++ b/utils/media-ctl/mediactl.h
>> @@ -434,6 +434,20 @@ int media_parse_setup_link(struct media_device *media,
>>  int media_parse_setup_links(struct media_device *media, const char *p);
>>
>>  /**
>> + * @brief Get entity's enabled backlinks
>> + * @param entity - media entity.
>> + * @param backlinks - array of pointers to matching backlinks.
>> + * @param num_backlinks - number of matching backlinks.
>> + *
>> + * Get links that are connected to the entity sink pads.
>> + *
>> + * @return 0 on success, or a negative error code on failure.
>> + */
>> +int media_entity_get_backlinks(struct media_entity *entity,
>> +				struct media_link **backlinks,
>> +				unsigned int *num_backlinks);
>> +
>> +/**
>>   * @brief Get v4l2_subdev for the entity
>>   * @param entity - media entity
>>   *
>> @@ -443,4 +457,5 @@ int media_parse_setup_links(struct media_device *media, const char *p);
>>   */
>>  struct v4l2_subdev *media_entity_get_v4l2_subdev(struct media_entity *entity);
>>
>> +
>
> Unrelated change.
>
>>  #endif
>


-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 2/7] mediatext: Add library
  2016-11-24 13:01   ` Sakari Ailus
@ 2016-11-24 14:04     ` Jacek Anaszewski
  0 siblings, 0 replies; 35+ messages in thread
From: Jacek Anaszewski @ 2016-11-24 14:04 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski,
	s.nawrocki, Teemu Tuominen

Hi Sakari,

On 11/24/2016 02:01 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Wed, Oct 12, 2016 at 04:35:17PM +0200, Jacek Anaszewski wrote:
>> libmediatext is a helper library for converting configurations (Media
>> controller links, V4L2 controls and V4L2 sub-device media bus formats and
>> selections) from text-based form into IOCTLs.
>>
>> libmediatext depends on libv4l2subdev and libmediactl.
>
> I'm not very happy with the plugin using the mediatext library as it was in
> 2013. I've heavily changed (and extended) the library since to cover
> additional use cases such as the request API:
>
> <URL:http://www.spinics.net/lists/linux-media/msg103242.html>
>
> This version is not meaningfully extensible in forward-compatible way. In
> order to resolve the matter quickly without making merging the further
> developed library difficult, I propose merging the code to the plugin
> itself. We could later check if it could be refactored.
>
> How about that?

It works for me.

Thanks,
Jacek Anaszewski

>> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>> Signed-off-by: Teemu Tuominen <teemu.tuominen@intel.com>
>> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
>> ---
>>  utils/media-ctl/Makefile.am        |  10 +-
>>  utils/media-ctl/libmediatext.pc.in |  10 ++
>>  utils/media-ctl/mediatext-test.c   |  64 ++++++++
>>  utils/media-ctl/mediatext.c        | 312 +++++++++++++++++++++++++++++++++++++
>>  utils/media-ctl/mediatext.h        |  52 +++++++
>>  5 files changed, 446 insertions(+), 2 deletions(-)
>>  create mode 100644 utils/media-ctl/libmediatext.pc.in
>>  create mode 100644 utils/media-ctl/mediatext-test.c
>>  create mode 100644 utils/media-ctl/mediatext.c
>>  create mode 100644 utils/media-ctl/mediatext.h
>>
>> diff --git a/utils/media-ctl/Makefile.am b/utils/media-ctl/Makefile.am
>> index ee7dcc9..2f12357 100644
>> --- a/utils/media-ctl/Makefile.am
>> +++ b/utils/media-ctl/Makefile.am
>> @@ -1,4 +1,4 @@
>> -noinst_LTLIBRARIES = libmediactl.la libv4l2subdev.la
>> +noinst_LTLIBRARIES = libmediactl.la libv4l2subdev.la libmediatext.la
>>
>>  libmediactl_la_SOURCES = libmediactl.c mediactl-priv.h
>>  libmediactl_la_CFLAGS = -static $(LIBUDEV_CFLAGS)
>> @@ -21,9 +21,15 @@ libv4l2subdev_la_LIBADD = libmediactl.la
>>  libv4l2subdev_la_CFLAGS = -static
>>  libv4l2subdev_la_LDFLAGS = -static
>>
>> +libmediatext_la_SOURCES = mediatext.c
>> +libmediatext_la_CFLAGS = -static $(LIBUDEV_CFLAGS)
>> +libmediatext_la_LDFLAGS = -static $(LIBUDEV_LIBS)
>> +
>>  mediactl_includedir=$(includedir)/mediactl
>>  noinst_HEADERS = mediactl.h v4l2subdev.h
>>
>> -bin_PROGRAMS = media-ctl
>> +bin_PROGRAMS = media-ctl mediatext-test
>>  media_ctl_SOURCES = media-ctl.c options.c options.h tools.h
>>  media_ctl_LDADD = libmediactl.la libv4l2subdev.la
>> +mediatext_test_SOURCES = mediatext-test.c
>> +mediatext_test_LDADD = libmediatext.la libmediactl.la libv4l2subdev.la
>> diff --git a/utils/media-ctl/libmediatext.pc.in b/utils/media-ctl/libmediatext.pc.in
>> new file mode 100644
>> index 0000000..6aa6353
>> --- /dev/null
>> +++ b/utils/media-ctl/libmediatext.pc.in
>> @@ -0,0 +1,10 @@
>> +prefix=@prefix@
>> +exec_prefix=@exec_prefix@
>> +libdir=@libdir@
>> +includedir=@includedir@
>> +
>> +Name: libmediatext
>> +Description: Media controller and V4L2 text-based configuration library
>> +Version: @PACKAGE_VERSION@
>> +Cflags: -I${includedir}
>> +Libs: -L${libdir} -lmediatext
>> diff --git a/utils/media-ctl/mediatext-test.c b/utils/media-ctl/mediatext-test.c
>> new file mode 100644
>> index 0000000..b8b9282
>> --- /dev/null
>> +++ b/utils/media-ctl/mediatext-test.c
>> @@ -0,0 +1,64 @@
>> +/*
>> + * libmediatext test program
>> + *
>> + * Copyright (C) 2013 Intel Corporation
>> + *
>> + * Contact: Sakari Ailus <sakari.ailus@linux.intel.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.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public License
>> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +
>> +#include "mediactl.h"
>> +#include "mediatext.h"
>> +
>> +int main(int argc, char *argv[])
>> +{
>> +	struct media_device *device;
>> +	int rval;
>> +
>> +	if (argc != 3) {
>> +		fprintf(stderr, "usage: %s <media device> <string>\n\n", argv[0]);
>> +		fprintf(stderr, "\tstring := [ v4l2-ctrl |?v4l2-mbus | link-reset | link-conf]\n\n");
>> +		fprintf(stderr, "\tv4l2-ctrl := \"entity\" ctrl_type ctrl_id ctrl_value\n");
>> +		fprintf(stderr, "\tctrl_type := [ int | int64 | bitmask ]\n");
>> +		fprintf(stderr, "\tctrl_value := [ %%d | %%PRId64 | bitmask_value ]\n");
>> +		fprintf(stderr, "\tbitmask_value := b<binary_number>\n\n");
>> +		fprintf(stderr, "\tv4l2-mbus := \n");
>> +		fprintf(stderr, "\tlink-conf := \"entity\":pad -> \"entity\":pad[link-flags]\n");
>> +		fprintf(stderr, "\tv4l2-ctrl-binding := ctrl_id -> \"entity\"\n");
>> +		return EXIT_FAILURE;
>> +	}
>> +
>> +	device = media_device_new(argv[1]);
>> +	if (!device)
>> +		return EXIT_FAILURE;
>> +
>> +	rval = media_device_enumerate(device);
>> +	if (rval)
>> +		return EXIT_FAILURE;
>> +
>> +	rval = mediatext_parse(device, argv[2]);
>> +	if (rval) {
>> +		fprintf(stderr, "bad string %s (%s)\n", argv[2], strerror(-rval));
>> +		return EXIT_FAILURE;
>> +	}
>> +
>> +	media_device_unref(device);
>> +
>> +	return EXIT_SUCCESS;
>> +}
>> diff --git a/utils/media-ctl/mediatext.c b/utils/media-ctl/mediatext.c
>> new file mode 100644
>> index 0000000..9faf0db
>> --- /dev/null
>> +++ b/utils/media-ctl/mediatext.c
>> @@ -0,0 +1,312 @@
>> +/*
>> + * Media controller text-based configuration library
>> + *
>> + * Copyright (C) 2013 Intel Corporation
>> + *
>> + * Contact: Sakari Ailus <sakari.ailus@linux.intel.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.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public License
>> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <sys/ioctl.h>
>> +
>> +#include <ctype.h>
>> +#include <errno.h>
>> +#include <fcntl.h>
>> +#include <inttypes.h>
>> +#include <stdbool.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +#include <unistd.h>
>> +#include <sys/stat.h>
>> +
>> +#include <linux/types.h>
>> +
>> +#include "mediactl.h"
>> +#include "mediactl-priv.h"
>> +#include "tools.h"
>> +#include "v4l2subdev.h"
>> +
>> +struct parser {
>> +	char *prefix;
>> +	int (*parse)(struct media_device *media, const struct parser *p,
>> +		     char *string);
>> +	struct parser *next;
>> +	bool no_args;
>> +};
>> +
>> +static int parse(struct media_device *media, const struct parser *p, char *string)
>> +{
>> +	for (; p->prefix; p++) {
>> +		size_t len = strlen(p->prefix);
>> +
>> +		if (strncmp(p->prefix, string, len) || string[len] != ' ')
>> +			continue;
>> +
>> +		string += len;
>> +
>> +		for (; isspace(*string); string++);
>> +
>> +		if (p->no_args)
>> +			return p->parse(media, p->next, NULL);
>> +
>> +		if (strlen(string) == 0)
>> +			return -ENOEXEC;
>> +
>> +		return p->parse(media, p->next, string);
>> +	}
>> +
>> +	media_dbg(media, "Unknown parser prefix\n");
>> +
>> +	return -ENOENT;
>> +}
>> +
>> +struct ctrl_type {
>> +	uint32_t type;
>> +	char *str;
>> +} ctrltypes[] = {
>> +	{ V4L2_CTRL_TYPE_INTEGER, "int" },
>> +	{ V4L2_CTRL_TYPE_MENU, "menu" },
>> +	{ V4L2_CTRL_TYPE_INTEGER_MENU, "intmenu" },
>> +	{ V4L2_CTRL_TYPE_BITMASK, "bitmask" },
>> +	{ V4L2_CTRL_TYPE_INTEGER64, "int64" },
>> +};
>> +
>> +static int parse_v4l2_ctrl_id(struct media_device *media, const struct parser *p,
>> +			      char *string, char **endp, __u32 *ctrl_id)
>> +{
>> +	int rval;
>> +
>> +	for (; isspace(*string); string++);
>> +	rval = sscanf(string, "0x%" PRIx32, ctrl_id);
>> +	if (rval <= 0)
>> +		return -EINVAL;
>> +
>> +	for (; !isspace(*string) && *string; string++);
>> +	for (; isspace(*string); string++);
>> +
>> +	*endp = string;
>> +
>> +	return 0;
>> +}
>> +
>> +/* adapted from yavta.c */
>> +static int parse_v4l2_ctrl(struct media_device *media, const struct parser *p,
>> +			   char *string)
>> +{
>> +	struct v4l2_ext_control ctrl = { 0 };
>> +	struct v4l2_ext_controls ctrls = { .count = 1,
>> +					   .controls = &ctrl };
>> +	int64_t val;
>> +	int rval;
>> +	struct media_entity *entity;
>> +	struct ctrl_type *ctype;
>> +	unsigned int i;
>> +
>> +	entity = media_parse_entity(media, string, &string);
>> +	if (!entity)
>> +		return -ENOENT;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(ctrltypes); i++)
>> +		if (!strncmp(string, ctrltypes[i].str,
>> +			     strlen(ctrltypes[i].str)))
>> +			break;
>> +
>> +	if (i == ARRAY_SIZE(ctrltypes))
>> +		return -ENOENT;
>> +
>> +	ctype = &ctrltypes[i];
>> +
>> +	string += strlen(ctrltypes[i].str);
>> +
>> +	rval = parse_v4l2_ctrl_id(media, p, string, &string, &ctrl.id);
>> +	if (rval < 0)
>> +		return -EINVAL;
>> +
>> +	ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(ctrl.id);
>> +
>> +	switch (ctype->type) {
>> +	case V4L2_CTRL_TYPE_BITMASK:
>> +		if (*string++ != 'b')
>> +			return -EINVAL;
>> +		while (*string == '1' || *string == '0') {
>> +			val <<= 1;
>> +			if (*string == '1')
>> +				val++;
>> +			string++;
>> +		}
>> +		break;
>> +	default:
>> +		rval = sscanf(string, "%" PRId64, &val);
>> +		break;
>> +	}
>> +	if (rval <= 0)
>> +		return -EINVAL;
>> +
>> +	media_dbg(media, "Setting control 0x%8.8x (type %s), value %" PRId64 "\n",
>> +		  ctrl.id, ctype->str, val);
>> +
>> +	if (ctype->type == V4L2_CTRL_TYPE_INTEGER64)
>> +		ctrl.value64 = val;
>> +	else
>> +		ctrl.value = val;
>> +
>> +	rval = v4l2_subdev_open(entity);
>> +	if (rval < 0)
>> +		return rval;
>> +
>> +	rval = ioctl(entity->sd->fd, VIDIOC_S_EXT_CTRLS, &ctrls);
>> +	if (ctype->type != V4L2_CTRL_TYPE_INTEGER64) {
>> +		if (rval != -1) {
>> +			ctrl.value64 = ctrl.value;
>> +		} else if (ctype->type != V4L2_CTRL_TYPE_STRING &&
>> +			   (errno == EINVAL || errno == ENOTTY)) {
>> +			struct v4l2_control old = { .id = ctrl.id,
>> +						    .value = val };
>> +
>> +			rval = ioctl(entity->sd->fd, VIDIOC_S_CTRL, &old);
>> +			if (rval != -1)
>> +				ctrl.value64 = old.value;
>> +		}
>> +	}
>> +	if (rval == -1) {
>> +		media_dbg(media,
>> +			  "Failed setting control 0x%8.8x: %s (%d) to value %"
>> +			  PRId64 "\n", ctrl.id, strerror(errno), errno, val);
>> +		return -errno;
>> +	}
>> +
>> +	if (val != ctrl.value64)
>> +		media_dbg(media, "Asking for %" PRId64 ", got %" PRId64 "\n",
>> +			  val, ctrl.value64);
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> +
>> +parse_name(string, end)
>> +
>> +*/
>> +
>> +int parse_v4l2_ctrl_binding(struct media_device *media, const struct parser *p,
>> +			    char *string)
>> +{
>> +	struct media_entity *entity;
>> +	struct v4l2_subdev *sd;
>> +	__u32 ctrl_id;
>> +	int rval;
>> +
>> +	rval = parse_v4l2_ctrl_id(media, p, string, &string, &ctrl_id);
>> +	if (rval < 0)
>> +		return -EINVAL;
>> +
>> +	for (; isspace(*string); ++string);
>> +
>> +	if (string[0] != '-' || string[1] != '>') {
>> +		media_dbg(media, "Expected '->'\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	string += 2;
>> +
>> +	entity = media_parse_entity(media, string, &string);
>> +	if (!entity)
>> +		return -ENOENT;
>> +
>> +	rval = v4l2_subdev_supports_v4l2_ctrl(media, entity, ctrl_id);
>> +	if (rval < 0) {
>> +		media_dbg(media,
>> +			  "Failed to check v4l2 control support for entity %s\n",
>> +			  entity->info.name);
>> +		return rval;
>> +	}
>> +
>> +	sd = entity->sd;
>> +
>> +	sd->v4l2_ctrl_bindings = realloc(sd->v4l2_ctrl_bindings,
>> +					 sizeof(*sd->v4l2_ctrl_bindings) *
>> +					 (sd->num_v4l2_ctrl_bindings + 1));
>> +	if (!sd->v4l2_ctrl_bindings)
>> +		return -ENOMEM;
>> +
>> +	sd->v4l2_ctrl_bindings[sd->num_v4l2_ctrl_bindings] = ctrl_id;
>> +	++sd->num_v4l2_ctrl_bindings;
>> +
>> +	return 0;
>> +}
>> +
>> +static int parse_v4l2_mbus(struct media_device *media, const struct parser *p,
>> +			   char *string)
>> +{
>> +	media_dbg(media, "Media bus format setup: %s\n", string);
>> +	return v4l2_subdev_parse_setup_formats(media, string);
>> +}
>> +
>> +static int parse_link_reset(struct media_device *media, const struct parser *p,
>> +			    char *string)
>> +{
>> +	media_dbg(media, "Resetting links\n");
>> +	return media_reset_links(media);
>> +}
>> +
>> +static int parse_link_conf(struct media_device *media, const struct parser *p,
>> +			   char *string)
>> +{
>> +	media_dbg(media, "Configuring links: %s\n", string);
>> +	return media_parse_setup_links(media, string);
>> +}
>> +
>> +static const struct parser parsers[] = {
>> +	{ "v4l2-ctrl", parse_v4l2_ctrl },
>> +	{ "v4l2-ctrl-binding", parse_v4l2_ctrl_binding },
>> +	{ "v4l2-mbus", parse_v4l2_mbus },
>> +	{ "link-reset", parse_link_reset, NULL, true },
>> +	{ "link-conf", parse_link_conf },
>> +	{ 0 }
>> +};
>> +
>> +int mediatext_parse(struct media_device *media, char *string)
>> +{
>> +	return parse(media, parsers, string);
>> +}
>> +
>> +int mediatext_parse_setup_config(struct media_device *device, const char *conf_path)
>> +{
>> +	char *line;
>> +	size_t n = 0;
>> +	FILE *f;
>> +	int ret;
>> +
>> +	if (conf_path == NULL)
>> +		return -EINVAL;
>> +
>> +	f = fopen(conf_path, "r");
>> +	if (!f)
>> +		return -EINVAL;
>> +
>> +	while (getline(&line, &n, f) != -1) {
>> +		ret = mediatext_parse(device, line);
>> +		if (ret < 0)
>> +			goto err_parse;
>> +		free(line);
>> +		line = NULL;
>> +		n = 0;
>> +	}
>> +
>> +err_parse:
>> +	fclose(f);
>> +	return ret;
>> +}
>> diff --git a/utils/media-ctl/mediatext.h b/utils/media-ctl/mediatext.h
>> new file mode 100644
>> index 0000000..7dfbaf6
>> --- /dev/null
>> +++ b/utils/media-ctl/mediatext.h
>> @@ -0,0 +1,52 @@
>> +/*
>> + * Media controller text-based configuration library
>> + *
>> + * Copyright (C) 2013 Intel Corporation
>> + *
>> + * Contact: Sakari Ailus <sakari.ailus@linux.intel.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.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public License
>> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#ifndef __MEDIATEXT_H__
>> +#define __MEDIATEXT_H__
>> +
>> +struct media_device;
>> +
>> +/**
>> + * @brief Parse and apply media device command
>> + * @param device - media device
>> + * @param string - string to parse
>> + *
>> + * Parse media device command and apply it to the media device
>> + * passed in the device argument.
>> + *
>> + * @return 0 on success, or a negative error code on failure.
>> + */
>> +int mediatext_parse(struct media_device *device, char *string);
>> +
>> +/**
>> + * @brief Parse and apply media device configuration
>> + * @param media - media device
>> + * @param conf_path - path to the configuration file
>> + *
>> + * Parse the media device commands listed in the file under
>> + * conf_path and apply them to the media device passed in the
>> + * device argument.
>> + *
>> + * @return 0 on success, or a negative error code on failure.
>> + */
>> +int mediatext_parse_setup_config(struct media_device *device, const char *conf_path);
>> +
>> +#endif /* __MEDIATEXT_H__ */
>


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 1/7] mediactl: Add support for v4l2-ctrl-binding config
  2016-10-12 14:35 ` [PATCH v4l-utils v7 1/7] mediactl: Add support for v4l2-ctrl-binding config Jacek Anaszewski
@ 2016-11-24 14:23   ` Sakari Ailus
  2016-11-28 21:32     ` Jacek Anaszewski
  0 siblings, 1 reply; 35+ messages in thread
From: Sakari Ailus @ 2016-11-24 14:23 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski,
	s.nawrocki, laurent.pinchart

Hi Jacek,

On Wed, Oct 12, 2016 at 04:35:16PM +0200, Jacek Anaszewski wrote:
> Make struct v4l2_subdev capable of aggregating v4l2-ctrl-bindings -
> media device configuration entries. Added are also functions for
> validating support for the control on given media entity and checking
> whether a v4l2-ctrl-binding has been defined for a media entity.

I still don't think this belongs here.

I think I told you about the generic pipeline configuration library I worked
on years ago; unfortunately it was left on prototype stage. Still, what I
realised was that something very similar is needed in that library ---
associating information to the representation of the media entities (or the
V4L2 sub-devices) in user space that has got nothing to do with the devices
themselves.

We could have e.g. a list of key--value pairs where the key is a pointer
provided by the user (i.e. application, another library) that could be
associated with the value. That would avoid having explicit information on
that in the struct media_entity itself.

An quicker alternative would be to manage a list of controls e.g. in the
plugin itself and store the media entity where they're implemented in that
list, with the control value.

Cc Laurent as well.

> 
> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  utils/media-ctl/libv4l2subdev.c | 32 ++++++++++++++++++++++++++++++++
>  utils/media-ctl/v4l2subdev.h    | 19 +++++++++++++++++++
>  2 files changed, 51 insertions(+)
> 
> diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c
> index c3439d7..4f8ee7f 100644
> --- a/utils/media-ctl/libv4l2subdev.c
> +++ b/utils/media-ctl/libv4l2subdev.c
> @@ -50,7 +50,15 @@ int v4l2_subdev_create(struct media_entity *entity)
>  
>  	entity->sd->fd = -1;
>  
> +	entity->sd->v4l2_ctrl_bindings = malloc(sizeof(__u32));
> +	if (entity->sd->v4l2_ctrl_bindings == NULL)
> +		goto err_v4l2_ctrl_bindings_alloc;
> +
>  	return 0;
> +
> +err_v4l2_ctrl_bindings_alloc:
> +	free(entity->sd);
> +	return -ENOMEM;
>  }
>  
>  int v4l2_subdev_create_opened(struct media_entity *entity, int fd)
> @@ -102,6 +110,7 @@ void v4l2_subdev_close(struct media_entity *entity)
>  	if (entity->sd->fd_owner)
>  		close(entity->sd->fd);
>  
> +	free(entity->sd->v4l2_ctrl_bindings);
>  	free(entity->sd);
>  }
>  
> @@ -884,3 +893,26 @@ const enum v4l2_mbus_pixelcode *v4l2_subdev_pixelcode_list(unsigned int *length)
>  
>  	return mbus_codes;
>  }
> +
> +int v4l2_subdev_supports_v4l2_ctrl(struct media_device *media,
> +				   struct media_entity *entity,
> +				   __u32 ctrl_id)
> +{
> +	struct v4l2_queryctrl queryctrl = {};
> +	int ret;
> +
> +	ret = v4l2_subdev_open(entity);
> +	if (ret < 0)
> +		return ret;
> +
> +	queryctrl.id = ctrl_id;
> +
> +	ret = ioctl(entity->sd->fd, VIDIOC_QUERYCTRL, &queryctrl);
> +	if (ret < 0)
> +		return ret;
> +
> +	media_dbg(media, "Validated control \"%s\" (0x%8.8x) on entity %s\n",
> +		  queryctrl.name, queryctrl.id, entity->info.name);
> +
> +	return 0;
> +}
> diff --git a/utils/media-ctl/v4l2subdev.h b/utils/media-ctl/v4l2subdev.h
> index 011fab1..4dee6b1 100644
> --- a/utils/media-ctl/v4l2subdev.h
> +++ b/utils/media-ctl/v4l2subdev.h
> @@ -26,10 +26,14 @@
>  
>  struct media_device;
>  struct media_entity;
> +struct media_device;
>  
>  struct v4l2_subdev {
>  	int fd;
>  	unsigned int fd_owner:1;
> +
> +	__u32 *v4l2_ctrl_bindings;
> +	unsigned int num_v4l2_ctrl_bindings;
>  };
>  
>  /**
> @@ -314,4 +318,19 @@ enum v4l2_field v4l2_subdev_string_to_field(const char *string);
>  const enum v4l2_mbus_pixelcode *v4l2_subdev_pixelcode_list(
>  	unsigned int *length);
>  
> +/**
> + * @brief Check if sub-device supports given v4l2 control
> + * @param media - media device.
> + * @param entity - media entity.
> + * @param ctrl_id - id of the v4l2 control to check.
> + *
> + * Verify if the sub-device associated with given media entity
> + * supports v4l2-control with given ctrl_id.
> + *
> + * @return 1 if the control is supported, 0 otherwise.
> + */
> +int v4l2_subdev_supports_v4l2_ctrl(struct media_device *device,
> +				   struct media_entity *entity,
> +				   __u32 ctrl_id);
> +
>  #endif

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 3/7] mediactl: Add media_entity_get_backlinks()
  2016-11-24 14:00     ` Jacek Anaszewski
@ 2016-11-24 14:26       ` Sakari Ailus
  0 siblings, 0 replies; 35+ messages in thread
From: Sakari Ailus @ 2016-11-24 14:26 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski,
	s.nawrocki, laurent.pinchart

Hi Jacek,

On Thu, Nov 24, 2016 at 03:00:46PM +0100, Jacek Anaszewski wrote:
> Hi Sakari,
> 
> Thanks for the review.

It's taken way too long. :-( My apologies for that.

> On 11/24/2016 01:40 PM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >On Wed, Oct 12, 2016 at 04:35:18PM +0200, Jacek Anaszewski wrote:
> >>Add a new graph helper useful for discovering video pipeline.
> >>
> >>Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> >>Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> >>---
> >> utils/media-ctl/libmediactl.c | 21 +++++++++++++++++++++
> >> utils/media-ctl/mediactl.h    | 15 +++++++++++++++
> >> 2 files changed, 36 insertions(+)
> >>
> >>diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c
> >>index 91ed003..155b65f 100644
> >>--- a/utils/media-ctl/libmediactl.c
> >>+++ b/utils/media-ctl/libmediactl.c
> >>@@ -36,6 +36,7 @@
> >> #include <unistd.h>
> >>
> >> #include <linux/media.h>
> >>+#include <linux/kdev_t.h>
> >
> >Is there something that needs this one in the patch?
> 
> MAJOR and MINOR macros.

Ok.

> 
> >
> >> #include <linux/videodev2.h>
> >>
> >> #include "mediactl.h"
> >>@@ -172,6 +173,26 @@ const struct media_entity_desc *media_entity_get_info(struct media_entity *entit
> >> 	return &entity->info;
> >> }
> >>
> >>+int media_entity_get_backlinks(struct media_entity *entity,
> >>+				struct media_link **backlinks,
> >>+				unsigned int *num_backlinks)
> >>+{
> >>+	unsigned int num_bklinks = 0;
> >>+	int i;
> >>+
> >>+	if (entity == NULL || backlinks == NULL || num_backlinks == NULL)
> >>+		return -EINVAL;
> >>+
> >
> >If you have an interface that accesses a memory buffer of unknown size, you
> >need to verify that the user has provided a buffer large enough.
> >
> >How about using the num_backlinks argument to provide the maximum size to
> >the function, and passing the actual number to the user, the latter of which
> >you already do?
> 
> Sounds reasonable.
> 
> >Alternatively, an iterator style API could be nice as well. Up to you.
> 
> It would probably need an addition of some generic infrastructure.
> I suppose that there is no such a feature in v4l-utils?

You basically need to store a value for the framework to tell which entity
is being worked on. So nothing too fancy.

I guess Laurent would prefer the current interface but I let him answer
that.

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers
  2016-11-24 13:50     ` Jacek Anaszewski
@ 2016-11-24 14:32       ` Sakari Ailus
  2016-11-24 15:39         ` Jacek Anaszewski
  0 siblings, 1 reply; 35+ messages in thread
From: Sakari Ailus @ 2016-11-24 14:32 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki

Hi Jacek,

On Thu, Nov 24, 2016 at 02:50:39PM +0100, Jacek Anaszewski wrote:
> Hi Sakari,
> 
> Thanks for the review.
> 
> On 11/24/2016 01:17 PM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >Thanks for the patchset.
> >
> >On Wed, Oct 12, 2016 at 04:35:19PM +0200, Jacek Anaszewski wrote:
> >>Add helper functions that allow for easy instantiation of media_device
> >>object basing on whether the media device contains v4l2 subdev with
> >>given file descriptor.
> >
> >Doesn't this work with video nodes as well? That's what you seem to be using
> >it for later on. And I think that's actually more useful.
> 
> Exactly, thanks for spotting this.
> 
> s/v4l2 subdev/video device opened/
> 
> >
> >The existing implementation uses udev to look up devices. Could you use
> >libudev device enumeration API to find the media devices, and fall back to
> >sysfs if udev doesn't work? There seems to be a reasonable-looking example
> >here:
> >
> ><URL:http://stackoverflow.com/questions/25361042/how-to-list-usb-mass-storage-devices-programatically-using-libudev-in-linux>
> 
> I'll check that, thanks.
> 
> >>
> >>Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> >>Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> >>---
> >> utils/media-ctl/libmediactl.c | 131 +++++++++++++++++++++++++++++++++++++++++-
> >> utils/media-ctl/mediactl.h    |  27 +++++++++
> >> 2 files changed, 156 insertions(+), 2 deletions(-)
> >>
> >>diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c
> >>index 155b65f..d347a40 100644
> >>--- a/utils/media-ctl/libmediactl.c
> >>+++ b/utils/media-ctl/libmediactl.c
> >>@@ -27,6 +27,7 @@
> >> #include <sys/sysmacros.h>
> >>
> >> #include <ctype.h>
> >>+#include <dirent.h>
> >> #include <errno.h>
> >> #include <fcntl.h>
> >> #include <stdbool.h>
> >>@@ -440,8 +441,9 @@ static int media_get_devname_udev(struct udev *udev,
> >> 		return -EINVAL;
> >>
> >> 	devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor);
> >>-	media_dbg(entity->media, "looking up device: %u:%u\n",
> >>-		  major(devnum), minor(devnum));
> >>+	if (entity->media)
> >>+		media_dbg(entity->media, "looking up device: %u:%u\n",
> >>+			  major(devnum), minor(devnum));
> >> 	device = udev_device_new_from_devnum(udev, 'c', devnum);
> >> 	if (device) {
> >> 		p = udev_device_get_devnode(device);
> >>@@ -523,6 +525,7 @@ static int media_get_devname_sysfs(struct media_entity *entity)
> >> 	return 0;
> >> }
> >>
> >>+
> >
> >Unrelated change.
> >
> >> static int media_enum_entities(struct media_device *media)
> >> {
> >> 	struct media_entity *entity;
> >>@@ -707,6 +710,92 @@ struct media_device *media_device_new(const char *devnode)
> >> 	return media;
> >> }
> >>
> >>+struct media_device *media_device_new_by_subdev_fd(int fd, struct media_entity **fd_entity)
> >>+{
> >>+	char video_devname[32], device_dir_path[256], media_dev_path[256], media_major_minor[10];
> >>+	struct media_device *media = NULL;
> >>+	struct dirent *entry;
> >>+	struct media_entity tmp_entity;
> >>+	DIR *device_dir;
> >>+	struct udev *udev;
> >>+	char *p;
> >>+	int ret, i;
> >>+
> >>+	if (fd_entity == NULL)
> >>+		return NULL;
> >>+
> >>+	ret = media_get_devname_by_fd(fd, video_devname);
> >>+	if (ret < 0)
> >>+		return NULL;
> >>+
> >>+	p = strrchr(video_devname, '/');
> >>+	if (p == NULL)
> >>+		return NULL;
> >>+
> >>+	ret = media_udev_open(&udev);
> >>+	if (ret < 0)
> >>+		return NULL;
> >>+
> >>+	sprintf(device_dir_path, "/sys/class/video4linux/%s/device/", p + 1);
> >>+
> >>+	device_dir = opendir(device_dir_path);
> >>+	if (device_dir == NULL)
> >>+		return NULL;
> >>+
> >>+	while ((entry = readdir(device_dir))) {
> >>+		if (strncmp(entry->d_name, "media", 4))
> >
> >Why 4? And isn't entry->d_name nul-terminated, so you could use strcmp()?
> 
> Media devices, as other devices, have numerical postfix, which is
> not of our interest.

Right. But still 5 would be the right number as we should also check the
last "a".

> 
> >>+			continue;
> >>+
> >>+		sprintf(media_dev_path, "%s%s/dev", device_dir_path, entry->d_name);
> >>+
> >>+		fd = open(media_dev_path, O_RDONLY);
> >>+		if (fd < 0)
> >>+			continue;
> >>+
> >>+		ret = read(fd, media_major_minor, sizeof(media_major_minor));
> >>+		if (ret < 0)
> >>+			continue;
> >>+
> >>+		sscanf(media_major_minor, "%d:%d", &tmp_entity.info.dev.major, &tmp_entity.info.dev.minor);
> >
> >This would be better split on two lines.
> 
> OK.
> 
> >>+
> >>+		/* Try to get the device name via udev */
> >>+		if (media_get_devname_udev(udev, &tmp_entity)) {
> >>+			/* Fall back to get the device name via sysfs */
> >>+			if (media_get_devname_sysfs(&tmp_entity))
> >>+				continue;
> >>+		}
> >>+
> >>+		media = media_device_new(tmp_entity.devname);
> >>+		if (media == NULL)
> >>+			continue;
> >>+
> >>+		ret = media_device_enumerate(media);
> >>+		if (ret < 0) {
> >>+			media_dbg(media, "Failed to enumerate %s (%d)\n",
> >>+				  tmp_entity.devname, ret);
> >>+			media_device_unref(media);
> >>+			media = NULL;
> >>+			continue;
> >>+		}
> >>+
> >>+		/* Get the entity associated with given fd */
> >>+		for (i = 0; i < media->entities_count; i++) {
> >>+			struct media_entity *entity = &media->entities[i];
> >>+
> >>+			if (!strcmp(entity->devname, video_devname)) {
> >>+				*fd_entity = &media->entities[i];
> >>+				break;
> >>+			}
> >>+		}
> >
> >What if you exit the loop without finding the entity you were looking for?
> 
> Ah, right, this case is unhandled.
> 
> Adding below condition should cover that:
> 
> if (i == media->entities_count)
>     media = NULL;

and media_device_unref()?

You could have a label for handling that at the end of the loop basic block
so you could implement handling of that just once to avoid such issues in
the future.

> 
> >>+
> >>+		break;
> 
> This break should be removed and the one in the inner for loop above
> should be replaced with goto here. Are you OK with that?

Um, yeah. There are indeed two loops. In Perl you could get out nicely but
in C we have to do something else. Two labels perhaps?

> 
> >>+	}
> >>+
> >>+	media_udev_close(udev);
> >>+
> >>+	return media;
> >>+}
> >>+
> >> struct media_device *media_device_new_emulated(struct media_device_info *info)
> >> {
> >> 	struct media_device *media;

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 6/7] mediactl: libv4l2subdev: add support for comparing mbus formats
  2016-10-12 14:35 ` [PATCH v4l-utils v7 6/7] mediactl: libv4l2subdev: add support for comparing mbus formats Jacek Anaszewski
@ 2016-11-24 14:36   ` Sakari Ailus
  2016-11-24 15:49     ` Jacek Anaszewski
  0 siblings, 1 reply; 35+ messages in thread
From: Sakari Ailus @ 2016-11-24 14:36 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki

Hi Jacek,

On Wed, Oct 12, 2016 at 04:35:21PM +0200, Jacek Anaszewski wrote:
> This patch adds a function for checking whether two mbus formats
> are compatible.

Compatible doesn't in general case mean the same as... the same.

On parallel busses a 10-bit source can be connected to an 8-bit sink-for
instance.

How about moving this to the plugin, and if someone else needs it, then we
move it out later?

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 7/7] Add a libv4l plugin for Exynos4 camera
  2016-10-12 14:35 ` [PATCH v4l-utils v7 7/7] Add a libv4l plugin for Exynos4 camera Jacek Anaszewski
@ 2016-11-24 15:11   ` Sakari Ailus
  2016-11-24 16:14     ` Jacek Anaszewski
  0 siblings, 1 reply; 35+ messages in thread
From: Sakari Ailus @ 2016-11-24 15:11 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski,
	s.nawrocki, gjasny

Hi Jacek,

Thank you for your continued work on the Exynos plugin patchset!

I think we're pretty close to being able to merge the set, if you could
still bear with me awhile. :-)

On Wed, Oct 12, 2016 at 04:35:22PM +0200, Jacek Anaszewski wrote:
...
> diff --git a/lib/libv4l-exynos4-camera/Makefile.am b/lib/libv4l-exynos4-camera/Makefile.am
> new file mode 100644
> index 0000000..c38b7f6
> --- /dev/null
> +++ b/lib/libv4l-exynos4-camera/Makefile.am
> @@ -0,0 +1,19 @@
> +if WITH_V4L_PLUGINS
> +libv4l2plugin_LTLIBRARIES = libv4l-exynos4-camera.la
> +endif
> +
> +media-bus-format-names.h: ../../include/linux/media-bus-format.h
> +	sed -e '/#define MEDIA_BUS_FMT/ ! d; s/.*FMT_//; /FIXED/ d; s/\t.*//; s/.*/{ \"&\", MEDIA_BUS_FMT_& },/;' \
> +	< $< > $@
> +
> +media-bus-format-codes.h: ../../include/linux/media-bus-format.h
> +	sed -e '/#define MEDIA_BUS_FMT/ ! d; s/.*#define //; /FIXED/ d; s/\t.*//; s/.*/ &,/;' \
> +	< $< > $@
> +
> +BUILT_SOURCES = media-bus-format-names.h media-bus-format-codes.h
> +CLEANFILES = $(BUILT_SOURCES)

It'd be nice to be able to use the same generated headers that now are under
utils/media-ctl, instead of copying the sed script here in verbatim. If the
script is changed or fixed in some way, the other location probably will
remain unchanged...

I wonder if there's a proper way to generate build time headers such as
these.

Another less good alternative would be to put these into a separate Makefile
and include that Makefile where the headers are needed. But I don't like
that much either, it's a hack.

> +
> +nodist_libv4l_exynos4_camera_la_SOURCES = $(BUILT_SOURCES)
> +libv4l_exynos4_camera_la_SOURCES = libv4l-exynos4-camera.c ../../utils/media-ctl/libmediactl.c ../../utils/media-ctl/libv4l2subdev.c ../../utils/media-ctl/mediatext.c
> +libv4l_exynos4_camera_la_CFLAGS = -fvisibility=hidden -std=gnu99
> +libv4l_exynos4_camera_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -lpthread
> 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..c219fe5
> --- /dev/null
> +++ b/lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
> @@ -0,0 +1,1325 @@
> +/*
> + * Copyright (c) 2016 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 <linux/types.h>
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/ioctl.h>
> +#include <sys/syscall.h>
> +#include <unistd.h>
> +
> +#include "../../utils/media-ctl/mediactl.h"
> +#include "../../utils/media-ctl/mediatext.h"
> +#include "../../utils/media-ctl/v4l2subdev.h"
> +#include "libv4l-plugin.h"
> +
> +#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)
> +
> +#define VIDIOC_CTRL(type)				\
> +	((type) == VIDIOC_S_CTRL ? "VIDIOC_S_CTRL" :	\
> +				   "VIDIOC_G_CTRL")
> +
> +#if HAVE_VISIBILITY
> +#define PLUGIN_PUBLIC __attribute__ ((visibility("default")))
> +#else
> +#define PLUGIN_PUBLIC
> +#endif
> +
> +#define SYS_IOCTL(fd, cmd, arg) \
> +	syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg))
> +
> +#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;                                          \
> +	})
> +
> +#ifndef min
> +#define min(a, b) (((a) < (b)) ? (a) : (b))
> +#endif
> +
> +#ifndef max
> +#define max(a, b) (((a) > (b)) ? (a) : (b))
> +#endif
> +
> +#define EXYNOS4_FIMC_DRV	"exynos4-fimc"
> +#define EXYNOS4_FIMC_LITE_DRV	"exynos-fimc-lit"
> +#define EXYNOS4_FIMC_IS_ISP_DRV	"exynos4-fimc-is"
> +#define EXYNOS4_CAPTURE_CONF	"/var/lib/libv4l/exynos4_capture_conf"

Is this something that should be configurable in ./configure?

> +#define EXYNOS4_FIMC_IS_ISP	"FIMC-IS-ISP"
> +#define EXYNOS4_S5K6A3		"S5K6A3"

Maybe this one as well. But it could come from a configuration file, too.

The problem at large is actually something else: how do you recognise the
hardware you're running on?

You could find the sensor by walking the media graph.

But if you need to know something very hardware specific it might not be
available through the generic interfaces, and you might need to know the
exact hardware you have.

How do you know that you should load a particular plugin? I wonder if we
could add a small snippet of code to detect different kinds of systems, I
presume this is the plugin you want to load if the user has opened a video
node of the Exynos4 ISP; you could find through the Media controller.

That's certainly out of scope of the patchset, I just thought of binging up
the topics for discussion. :-)

> +#define EXYNOS4_FIMC_LITE_PREFIX "FIMC-LITE."
> +#define EXYNOS4_FIMC_PREFIX	"FIMC."
> +#define EXYNOS4_MAX_FMT_NEGO_NUM 50
> +#define EXYNOS4_MAX_PIPELINE_LEN 7
> +
> +struct media_device;
> +struct media_entity;
> +
> +/*
> + * struct pipeline_entity - linked media device pipeline
> + * @entity:		linked entity
> + * @sink_pad:		inbound link pad of the entity
> + * @src_pad:		outbound link pad of the entity
> + */
> +struct pipeline_entity {
> +	struct media_entity *entity;
> +	struct media_pad *sink_pad;
> +	struct media_pad *src_pad;
> +};
> +
> +/*
> + * struct media_entity_to_cid - entity to control map
> + * @entity:		media entity
> + * @sink_pad:		inbound link pad of the entity
> + * @src_pad:		outbound link pad of the entity
> + */
> +struct media_entity_to_cid {
> +	struct media_entity *entity;
> +	union {
> +		struct v4l2_queryctrl queryctrl;
> +		struct v4l2_query_ext_ctrl query_ext_ctrl;
> +	};
> +};
> +
> +/*
> + * struct exynos4_camera_plugin - libv4l exynos4 camera plugin
> + * @media:		media device comprising the opened video device
> + * @pipeline:		video pipeline, element 0 is the source entity
> + * @pipeline_len:	length of the video pipeline
> + */
> +struct exynos4_camera_plugin {

static?

> +	struct media_device *media;
> +	struct pipeline_entity pipeline[EXYNOS4_MAX_PIPELINE_LEN];
> +	unsigned int pipeline_len;
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Pipeline operations
> + */
> +
> +/**
> + * @brief Discover video pipeline for given sink entity
> + * @param plugin - this plugin.
> + * @param entity - sink entity of the pipeline.
> + *
> + * Discover the video pipeline, by walking starting from the
> + * sink entity upwards until a source entity is encountered.
> + *
> + * @return 0 if the sensor entity was detected,
> + * 	   or negative error code on failure.
> + */
> +static int discover_pipeline_by_entity(struct exynos4_camera_plugin *plugin,
> +				       struct media_entity *entity)
> +{
> +	struct pipeline_entity reverse_pipeline[EXYNOS4_MAX_PIPELINE_LEN];
> +	struct media_pad *src_pad;
> +	struct media_link *link = NULL, *backlinks[2];
> +	unsigned int num_backlinks, cur_pipe_pos = 0;
> +	int i, j;
> +	int ret;
> +
> +	if (entity == NULL)
> +		return -EINVAL;
> +
> +	for (;;) {
> +		/* Cache the recently discovered entity. */
> +		reverse_pipeline[cur_pipe_pos].entity = entity;
> +
> +		/* Cache the source pad used for linking the entity. */
> +		if (link)
> +			reverse_pipeline[cur_pipe_pos].src_pad = link->source;
> +
> +		ret = media_entity_get_backlinks(entity, backlinks,
> +						 &num_backlinks);
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Check if pipeline source entity has been reached. */
> +		if (num_backlinks > 2) {
> +			V4L2_EXYNOS4_DBG("Unexpected number of busy sink pads (%d)",
> +					 num_backlinks);
> +			return -EINVAL;
> +		} else if (num_backlinks == 2) {
> +			/*
> +			 * Allow two active pads only in case of
> +			 * S5C73M3-OIF entity.
> +			 */
> +			if (strcmp(media_entity_get_info(entity)->name,
> +				   "S5C73M3-OIF")) {
> +				V4L2_EXYNOS4_DBG("Ambiguous media device topology: "
> +						 "two busy sink pads");
> +				return -EINVAL;
> +			}
> +			/*
> +			 * Two active links are allowed betwen S5C73M3-OIF and
> +			 * S5C73M3 entities. In such a case route through the
> +			 * pad with id == 0 has to be chosen.
> +			 */
> +			link = NULL;
> +			for (i = 0; i < num_backlinks; i++)
> +				if (backlinks[i]->sink->index == 0)
> +					link = backlinks[i];
> +			if (link == NULL)
> +				return -EINVAL;
> +		} else if (num_backlinks == 1) {
> +			link = backlinks[0];
> +		} else {
> +			reverse_pipeline[cur_pipe_pos].sink_pad = NULL;
> +			break;
> +		}
> +
> +		/* Cache the sink pad used for linking the entity. */
> +		reverse_pipeline[cur_pipe_pos].sink_pad = link->sink;
> +
> +		V4L2_EXYNOS4_DBG("Discovered sink pad %d for the %s entity",
> +				 reverse_pipeline[cur_pipe_pos].sink_pad->index,
> +				 media_entity_get_info(entity)->name);
> +
> +		src_pad = media_entity_remote_source(link->sink);
> +		if (!src_pad)
> +			return -EINVAL;
> +
> +		entity = src_pad->entity;
> +		if (++cur_pipe_pos == EXYNOS4_MAX_PIPELINE_LEN)
> +			return -EINVAL;
> +	}
> +
> +	/*
> +	 * Reorder discovered pipeline elements so that the sensor
> +	 * entity was the pipeline head.
> +	 */
> +	j = 0;
> +	for (i = cur_pipe_pos; i >= 0; i--)

How about:

	for (i = cur_pipe_pos, j = 0; i >= 0; i--, j++)

And you can avoid setting j to zero outside the loop plus incrementing j++
where it doesn't seem to fit too well.

> +		plugin->pipeline[j++] = reverse_pipeline[i];
> +
> +	plugin->pipeline_len = j;
> +
> +	return 0;
> +}
> +
> +/**
> + * @brief Check if the entity belongs to the plugin video pipeline
> + * @param plugin - this plugin.
> + * @param entity_name - name of the entity to look for.
> + *
> + * Check if the entity belongs to the pipeline whose sink element
> + * is the video device represented by the file descriptor passed
> + * to plugin_init().
> + *
> + * @return True if the entity belongs to the pipeline,
> + *         and false otherwise.
> + */
> +static bool has_pipeline_entity(struct exynos4_camera_plugin *plugin,
> +				char *entity_name)
> +{
> +	struct pipeline_entity *pipeline = plugin->pipeline;
> +	struct media_entity *entity;
> +	unsigned int i;
> +
> +	if (pipeline == NULL || entity_name == NULL)
> +		return -EINVAL;
> +
> +	for (i = 0; i < plugin->pipeline_len; i++) {
> +		entity = pipeline[i].entity;
> +		if (!strncmp(media_entity_get_info(entity)->name, entity_name,
> +			     strlen(entity_name)))
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +/**
> + * @brief Adjust mbus format to FIMC_IS_ISP limitations.
> + * @param mbus_fmt - format to adjust.
> + *
> + * FIMC_IS_ISP shears the video frame off, effectively the format set
> + * on the preceding entities has to be suitably greater to achieve
> + * the frame size requested by the user.
> + *
> + * Also the miminum frame size supported by FIMC_IS_ISP is 128x128.
> + */
> +static void adjust_format_to_fimc_is_isp(struct v4l2_mbus_framefmt *mbus_fmt)
> +{
> +	mbus_fmt->width = max(128, mbus_fmt->width);
> +	mbus_fmt->height = max(128, mbus_fmt->height);
> +
> +	mbus_fmt->width += 16;
> +	mbus_fmt->height += 12;
> +}
> +
> +/**
> + * @brief Negotiate format acceptable for all pipeline entities.
> + * @param plugin - this plugin.
> + * @param dev_fmt - requested video device format.
> + *
> + * Negotiate common format acceptable by all the pipeline entities,
> + * that is closest to the requested one.
> + *
> + * @return 0 if the negotiation succeeded,
> + * 	   or negative error code on failure.
> + */
> +static int negotiate_pipeline_fmt(struct exynos4_camera_plugin *plugin,
> +				  struct v4l2_format *dev_fmt)
> +{
> +	struct v4l2_mbus_framefmt mbus_fmt = { 0 }, common_fmt;
> +	int repeat_negotiation, cnt_negotiation = 0, ret, pad_id, i;
> +	struct pipeline_entity *pipeline = plugin->pipeline;
> +	enum v4l2_subdev_fmt_mismatch fmt_err;
> +	struct media_entity *entity;
> +
> +	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;
> +
> +	if (has_pipeline_entity(plugin, EXYNOS4_FIMC_IS_ISP))
> +		adjust_format_to_fimc_is_isp(&mbus_fmt);
> +
> +	V4L2_EXYNOS4_DBG("Begin pipeline format negotiation...");
> +
> +	for (;;) {
> +		repeat_negotiation = 0;
> +		entity = pipeline[0].entity;
> +
> +		pad_id = pipeline[0].src_pad->index;
> +
> +		V4L2_EXYNOS4_DBG("Setting format on the source entity pad %s:%d",
> +				 media_entity_get_info(entity)->name, pad_id);
> +
> +		ret = v4l2_subdev_set_format(entity, &mbus_fmt,
> +					     pad_id, V4L2_SUBDEV_FORMAT_TRY);
> +		if (ret < 0)
> +			return ret;
> +
> +		V4L2_EXYNOS4_DBG("Format set on the pad %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
> +				 media_entity_get_info(entity)->name, pad_id,
> +				 v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
> +				 v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
> +				 mbus_fmt.width, mbus_fmt.height);
> +
> +		common_fmt = mbus_fmt;
> +
> +		/* Stop iterating on last but one entity as it is not a sub-device. */
> +		for (i = 1; i < plugin->pipeline_len - 1; i++) {
> +			entity = pipeline[i].entity;
> +
> +			pad_id = pipeline[i].sink_pad->index;
> +
> +			V4L2_EXYNOS4_DBG("Setting format on the pad %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
> +					 media_entity_get_info(entity)->name, pad_id,
> +					 v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
> +					 v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
> +					 mbus_fmt.width, mbus_fmt.height);
> +
> +			/* Set format on the entity sink pad. */
> +			ret = v4l2_subdev_set_format(entity, &mbus_fmt, pad_id,
> +							V4L2_SUBDEV_FORMAT_TRY);
> +			if (ret < 0)
> +				return ret;
> +
> +			fmt_err = v4l2_subdev_format_compare(&mbus_fmt, &common_fmt);
> +			if (fmt_err) {
> +				if (fmt_err == FMT_MISMATCH_WIDTH &&
> +				     !strncmp(media_entity_get_info(entity)->name,
> +					      EXYNOS4_FIMC_LITE_PREFIX,
> +					      strlen(EXYNOS4_FIMC_LITE_PREFIX))) {
> +					/*
> +					 * Align width downwards, according to the FIMC-LITE
> +					 * width step. Otherwise pipeline format negotiation
> +					 * wouldn't succeed for widths excessing maximum sensor
> +					 * frame width, which is probed by GStreamer, no matter
> +					 * what actual frame size is to be set.
> +					 */
> +					mbus_fmt.width -= 8;
> +				}
> +				repeat_negotiation = 1;
> +				break;
> +			}
> +
> +			/*
> +			 * Do not check format on FIMC.[n] source pad
> +			 * and stop negotiation.
> +			 */
> +			if (!strncmp(media_entity_get_info(entity)->name,
> +				     EXYNOS4_FIMC_PREFIX,
> +				     strlen(EXYNOS4_FIMC_PREFIX)))
> +				break;
> +
> +			pad_id = pipeline[i].src_pad->index;
> +
> +			/* Get format on the entity src pad */
> +			ret = v4l2_subdev_get_format(entity, &mbus_fmt, pad_id,
> +							V4L2_SUBDEV_FORMAT_TRY);
> +			if (ret < 0)
> +				return -EINVAL;
> +
> +			V4L2_EXYNOS4_DBG("Format propagated to the pad %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
> +					 media_entity_get_info(entity)->name, pad_id,
> +					 v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
> +					 v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
> +					 mbus_fmt.width, mbus_fmt.height);
> +
> +			if (!strcmp(media_entity_get_info(entity)->name,
> +				    EXYNOS4_FIMC_IS_ISP)) {
> +				common_fmt.code = mbus_fmt.code;
> +				common_fmt.colorspace = mbus_fmt.colorspace;
> +				common_fmt.width -= 16;
> +				common_fmt.height -= 12;
> +			}
> +
> +			if (v4l2_subdev_format_compare(&mbus_fmt, &common_fmt)) {
> +				repeat_negotiation = 1;
> +				break;
> +			}
> +		}
> +
> +		if (!repeat_negotiation) {
> +			break;
> +		} else if (++cnt_negotiation > EXYNOS4_MAX_FMT_NEGO_NUM) {
> +			V4L2_EXYNOS4_DBG("Pipeline format negotiation failed!");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	dev_fmt->fmt.pix_mp.width = mbus_fmt.width;
> +	dev_fmt->fmt.pix_mp.height = mbus_fmt.height;
> +	dev_fmt->fmt.pix_mp.field = mbus_fmt.field;
> +	dev_fmt->fmt.pix_mp.colorspace = mbus_fmt.colorspace;
> +
> +	V4L2_EXYNOS4_DBG("Pipeline format successfuly negotiated");
> +
> +	return 0;
> +}
> +
> +/**
> + * @brief Apply previously negotiated pipeline format
> + * @param plugin - this plugin.
> + *
> + * Apply the format, previously negotiated with negotiate_pipeline_fmt(),
> + * on all the pipeline v4l2 sub-devices.
> + *
> + * @return 0 if the format was successfuly applied,
> + * 	   or negative error code on failure.
> + */
> +static int apply_pipeline_fmt(struct exynos4_camera_plugin *plugin,
> +			      struct v4l2_format *fmt)
> +{
> +	struct v4l2_mbus_framefmt mbus_fmt = { 0 };
> +	struct pipeline_entity *pipeline = plugin->pipeline;
> +	struct media_entity *entity;
> +	struct media_pad *pad;
> +	unsigned int i;
> +	int ret;
> +
> +	if (pipeline == NULL)
> +		return -EINVAL;
> +
> +	for (i = 0; i < plugin->pipeline_len - 1; i++) {
> +		entity = pipeline[i].entity;
> +		/*
> +		 * Source entity is linked only through a source pad
> +		 * and this pad should be used for setting the format.
> +		 * For other entities set the format on a sink pad.
> +		 */
> +		pad = pipeline[i].sink_pad ? pipeline[i].sink_pad :
> +					pipeline[i].src_pad;
> +		if (pad == NULL)
> +			return -EINVAL;
> +
> +		ret = v4l2_subdev_get_format(entity, &mbus_fmt, pad->index,
> +					     V4L2_SUBDEV_FORMAT_TRY);
> +
> +		if (ret < 0)
> +			return ret;
> +
> +		V4L2_EXYNOS4_DBG("VIDIOC_SUBDEV_G_FMT %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
> +			  media_entity_get_info(entity)->name, pad->index,
> +			  v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
> +			  v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
> +			  mbus_fmt.width, mbus_fmt.height);
> +
> +		ret = v4l2_subdev_set_format(entity, &mbus_fmt, pad->index,
> +					     V4L2_SUBDEV_FORMAT_ACTIVE);
> +		if (ret < 0)
> +			return ret;
> +
> +		V4L2_EXYNOS4_DBG("VIDIOC_SUBDEV_S_FMT %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
> +			  media_entity_get_info(entity)->name, pad->index,
> +			  v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
> +			  v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
> +			  mbus_fmt.width, mbus_fmt.height);
> +	}
> +
> +	/*
> +	 * Sink entity represents /dev/videoN node and is not
> +	 * a sub-device. Nonetheless because it has associated
> +	 * file descriptor and can expose v4l2-controls the
> +	 * v4l2-subdev structure is used for caching the
> +	 * related data.
> +	 */
> +	struct v4l2_subdev *sd =
> +		media_entity_get_v4l2_subdev(pipeline[i].entity);
> +	if (!sd)
> +		return -EINVAL;
> +
> +	ret = SYS_IOCTL(sd->fd, VIDIOC_S_FMT, fmt);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +/**
> + * @brief Open all v4l2 sub-devices associated with pipeline entities
> + * @param plugin - this plugin.
> + *
> + * Open all v4l2 sub-devices associated with the entities of the pipeline
> + * discovered with discover_pipeline_by_entity().
> + *
> + * @return 0 if all v4l2 sub-devices were opened successfuly,
> + * 	   or negative error code on failure.
> + */
> +static int open_pipeline(struct exynos4_camera_plugin *plugin)
> +{
> +	struct pipeline_entity *pipeline = plugin->pipeline;
> +	struct media_entity *entity;
> +	unsigned int i;
> +	int ret;
> +
> +	if (pipeline == NULL)
> +		return -EINVAL;
> +
> +	/*
> +	 * Stop walking the pipeline on the last but one entity, because
> +	 * the sink entity was already opened by libv4l2 core.
> +	 */
> +	for (i = 0; i < plugin->pipeline_len - 1; i++) {
> +		entity = pipeline[i].entity;
> +		V4L2_EXYNOS4_DBG("Opening sub-device: %s",
> +				 media_entity_get_info(entity)->name);
> +		ret = v4l2_subdev_open(entity);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * @brief Check if there was a v4l2_ctrl binding defined for the entity
> + * @param entity - media entity.
> + * @param ctrl_id - v4l2 control identifier.
> + *
> + * Check if there was a v4l2-ctrl-binding entry defined for the entity.
> + *
> + * @return true if the binding exists, false otherwise.
> + */
> +static bool has_v4l2_ctrl_binding(struct media_entity *entity,
> +				       int ctrl_id)
> +{
> +	struct v4l2_subdev *sd = media_entity_get_v4l2_subdev(entity);
> +	int i;
> +
> +	if (!sd)
> +		return false;
> +
> +	for (i = 0; i < sd->num_v4l2_ctrl_bindings; ++i)
> +		if (sd->v4l2_ctrl_bindings[i] == ctrl_id)
> +			return true;
> +
> +	return false;
> +}
> +
> +/**
> + * @brief Get the first pipeline entity with matching v4l2-ctrl-binding.
> + * @param plugin - this plugin.
> + * @param cid - v4l2-control identifier.
> + *
> + * Get the first pipeline entity for which v4l2-control-binding
> + * with given cid was defined.
> + *
> + * @return associated entity if defined, or NULL if no matching
> + *         v4l2-ctrl-binding was defined for any entity
> + *         in the pipeline.
> + */
> +static struct media_entity *get_pipeline_entity_by_cid(
> +				struct exynos4_camera_plugin *plugin,
> +				int cid)
> +{
> +	struct pipeline_entity *pipeline = plugin->pipeline;
> +	struct media_entity *entity;
> +	unsigned int i;
> +
> +	if (pipeline == NULL)
> +		return NULL;
> +
> +	for (i = 0; i < plugin->pipeline_len; i++) {
> +		entity = pipeline[i].entity;
> +		if (has_v4l2_ctrl_binding(entity, cid))
> +			return entity;
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * @brief Check if the entity is of capture type
> + * @param entity_name - name of the entity to check
> + *
> + * Check if the entity name ends with a "capture", which
> + * gives a hint that it is of capture type.
> + *
> + * @return True if the entity name ends with a "capture"
> + *         string, and false otherwise.
> + */
> +static bool is_capture_entity(const char *entity_name)
> +{
> +	const char capture_segment[] = "capture";
> +	int cap_segment_pos;
> +
> +	if (entity_name == NULL)
> +		return false;
> +
> +	cap_segment_pos = strlen(entity_name) - strlen(capture_segment);
> +
> +	if (strcmp(entity_name + cap_segment_pos, capture_segment) == 0)
> +		return true;
> +
> +	return false;
> +}
> +
> +static __u32 convert_type(__u32 type)
> +{
> +	switch (type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +		return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +	default:
> +		return type;
> +	}
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Ioctl handlers
> + */
> +
> +static int set_fmt_ioctl(struct exynos4_camera_plugin *plugin,
> +				unsigned long int cmd,
> +				struct v4l2_format *arg,
> +				enum v4l2_subdev_format_whence set_mode)
> +{
> +	struct v4l2_format fmt = { 0 };
> +	int ret;
> +
> +	fmt.type = convert_type(arg->type);
> +	if (fmt.type != arg->type) {
> +		fmt.fmt.pix_mp.width = arg->fmt.pix.width;
> +		fmt.fmt.pix_mp.height = arg->fmt.pix.height;
> +		fmt.fmt.pix_mp.pixelformat = arg->fmt.pix.pixelformat;
> +		fmt.fmt.pix_mp.field = arg->fmt.pix.field;
> +		fmt.fmt.pix_mp.colorspace = arg->fmt.pix.colorspace;
> +		fmt.fmt.pix_mp.num_planes = 1;
> +		fmt.fmt.pix_mp.flags = arg->fmt.pix.flags;
> +		fmt.fmt.pix_mp.plane_fmt[0].bytesperline = arg->fmt.pix.bytesperline;
> +		fmt.fmt.pix_mp.plane_fmt[0].sizeimage = arg->fmt.pix.sizeimage;
> +	} else {
> +		fmt = *arg;
> +	}
> +
> +	ret = negotiate_pipeline_fmt(plugin, &fmt);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (set_mode == V4L2_SUBDEV_FORMAT_ACTIVE) {
> +		ret = apply_pipeline_fmt(plugin, &fmt);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	if (fmt.type != arg->type) {
> +		arg->fmt.pix.width = fmt.fmt.pix_mp.width;
> +		arg->fmt.pix.height = fmt.fmt.pix_mp.height;
> +		arg->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
> +		arg->fmt.pix.field = fmt.fmt.pix_mp.field;
> +		arg->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
> +		arg->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
> +		arg->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
> +		arg->fmt.pix.flags = fmt.fmt.pix_mp.flags;
> +	} else {
> +		*arg = fmt;
> +	}
> +
> +	return 0;
> +}
> +
> +static int get_fmt_ioctl(int fd,
> +			 unsigned long int cmd,
> +			 struct v4l2_format *arg)
> +{
> +	struct v4l2_format fmt = { 0 };
> +	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(&arg->fmt.pix, 0, sizeof(arg->fmt.pix));
> +	arg->fmt.pix.width = fmt.fmt.pix_mp.width;
> +	arg->fmt.pix.height = fmt.fmt.pix_mp.height;
> +	arg->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
> +	arg->fmt.pix.field = fmt.fmt.pix_mp.field;
> +	arg->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
> +	arg->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
> +	arg->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
> +	arg->fmt.pix.flags = fmt.fmt.pix_mp.flags;
> +
> +	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(int fd, struct v4l2_capability *arg)
> +{
> +	int ret;
> +
> +	ret = SYS_IOCTL(fd, VIDIOC_QUERYCAP, arg);
> +	if (ret < 0)
> +		return ret;
> +
> +	arg->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
> +	arg->capabilities |= V4L2_CAP_VIDEO_CAPTURE;
> +
> +	return ret;
> +}
> +
> +int ctrl_ioctl(struct exynos4_camera_plugin *plugin,
> +	       unsigned long int request, struct v4l2_control *arg)
> +{
> +	struct pipeline_entity *pipeline = plugin->pipeline;
> +	struct media_entity *entity;
> +	struct v4l2_control ctrl = *arg;
> +	struct v4l2_queryctrl queryctrl = { 0 };
> +	bool ctrl_found = false;
> +	int i, ret;
> +
> +	if (pipeline == NULL)
> +		return -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) {
> +		for (i = 0; i < plugin->pipeline_len; i++) {
> +			struct v4l2_control ctrl_def = ctrl;
> +
> +			entity = pipeline[i].entity;
> +
> +			queryctrl.id = ctrl.id;
> +
> +			/* query default control value */
> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +					VIDIOC_QUERYCTRL, &queryctrl);
> +			if (ret < 0)
> +				continue;
> +
> +			ctrl_found = true;
> +
> +			if (queryctrl.type & V4L2_CTRL_TYPE_BUTTON)
> +				break;
> +
> +			ctrl_def.value = queryctrl.default_value;
> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +					VIDIOC_S_CTRL, &ctrl_def);
> +			if (ret < 0)
> +				return -EINVAL;
> +		}
> +
> +		if (!ctrl_found) {
> +			ret = -EINVAL;
> +			goto exit;
> +		}
> +	}
> +
> +	entity = get_pipeline_entity_by_cid(plugin, ctrl.id);
> +
> +	if (entity) {
> +		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +				request, &ctrl);
> +	} else {
> +		/* Walk the pipeline until the request succeeds */
> +		ret = -ENOENT;
> +
> +		for (i = 0; i < plugin->pipeline_len; i++) {
> +			entity = pipeline[i].entity;
> +
> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +					request, &ctrl);
> +			if (!ret)
> +				break;
> +		}
> +	}
> +
> +exit:
> +	*arg = ctrl;
> +
> +	V4L2_EXYNOS4_DBG("%s [id: 0x%8.8x, name: %s, entity: %s] (%d)",
> +			 VIDIOC_CTRL(request), ctrl.id,
> +			 ret ? NULL : queryctrl.name,
> +			 entity ? media_entity_get_info(entity)->name :
> +				  NULL, ret);
> +
> +
> +	return ret;
> +}
> +
> +static int single_ext_ctrl_ioctl(struct exynos4_camera_plugin *plugin,
> +				 unsigned long int request,
> +				 struct v4l2_ext_controls *arg)
> +{
> +	struct pipeline_entity *pipeline = plugin->pipeline;
> +	struct media_entity *entity;
> +	struct v4l2_ext_controls ctrls = *arg;
> +	struct v4l2_ext_control *ctrl = &ctrls.controls[0];
> +	struct v4l2_query_ext_ctrl queryctrl;
> +	bool ctrl_found = 0;
> +	int i, 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_EXT_CTRLS,
> +	 * to avoid the situation when a control is set on more
> +	 * than one sub-device.
> +	 */
> +	if (request == VIDIOC_S_EXT_CTRLS) {
> +		for (i = 0; i < plugin->pipeline_len; i++) {
> +			struct v4l2_ext_controls ctrls_def = ctrls;
> +			struct v4l2_ext_control *ctrl_def = &ctrls_def.controls[0];
> +
> +			entity = pipeline[i].entity;
> +
> +			queryctrl.id = ctrl->id;
> +
> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +					VIDIOC_QUERY_EXT_CTRL, &queryctrl);
> +			if (ret < 0)
> +				continue;
> +
> +			ctrl_found = true;
> +
> +			if (queryctrl.type & V4L2_CTRL_TYPE_BUTTON)
> +				break;
> +
> +			ctrl_def->value64 = queryctrl.default_value;
> +
> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +					VIDIOC_S_EXT_CTRLS, &ctrls_def);
> +			if (ret < 0)
> +				return -EINVAL;
> +		}
> +
> +		if (!ctrl_found) {
> +			ret = -EINVAL;
> +			goto exit;
> +		}
> +	}
> +
> +	entity = get_pipeline_entity_by_cid(plugin, ctrl->id);
> +
> +	if (entity) {
> +		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +				request, &ctrls);
> +	} else {
> +		/* Walk the pipeline until the request succeeds */
> +		for (i = 0; i < plugin->pipeline_len; i++) {
> +			entity = pipeline[i].entity;
> +
> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +					request, &ctrls);
> +			if (!ret)
> +				break;
> +		}
> +	}
> +
> +exit:
> +	*arg = ctrls;
> +
> +	V4L2_EXYNOS4_DBG("%s [id: 0x%8.8x, name: %s, entity: %s] (%d)",
> +			 VIDIOC_CTRL(request), ctrl->id,
> +			 ret ? NULL : queryctrl.name,
> +			 entity ? media_entity_get_info(entity)->name :
> +				  NULL, ret);
> +
> +	return ret;
> +}
> +
> +int ext_ctrl_ioctl(struct exynos4_camera_plugin *plugin,
> +		   unsigned long 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 = single_ext_ctrl_ioctl(plugin, 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;
> +			break;
> +		}
> +	}
> +
> +	*arg = out_ctrls;
> +	return ret;
> +}
> +
> +int sort_ctrls(const void * a, const void * b)
> +{
> +	const struct media_entity_to_cid *ctrl_a = a, *ctrl_b = b;
> +
> +	return ctrl_a->queryctrl.id - ctrl_b->queryctrl.id;
> +}
> +
> +int queryctrl_ioctl(struct exynos4_camera_plugin *plugin,
> +		    struct v4l2_queryctrl *arg)
> +{
> +	struct pipeline_entity *pipeline = plugin->pipeline;
> +	struct media_entity *entity, *target_entity;
> +	struct v4l2_queryctrl queryctrl = *arg;
> +	struct media_entity_to_cid *ctrls_found;
> +	int i, ret = -EINVAL, num_ctrls = 0;
> +
> +	/*
> +	 * If id is or'ed with V4L2_CTRL_FLAG_NEXT_CTRL then the control to
> +	 * be found is the one with the next lowest id among all entities
> +	 * in the pipeline.
> +	 */
> +	if (queryctrl.id & V4L2_CTRL_FLAG_NEXT_CTRL) {
> +		ctrls_found = malloc(sizeof(*ctrls_found));
> +
> +		for (i = 0; i < plugin->pipeline_len; i++) {
> +			entity = pipeline[i].entity;
> +
> +			queryctrl = *arg;
> +
> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +					VIDIOC_QUERYCTRL, &queryctrl);
> +			if (!ret) {
> +				ctrls_found = realloc(ctrls_found,
> +					sizeof(*ctrls_found) * (num_ctrls + 1));
> +				ctrls_found[num_ctrls].queryctrl = queryctrl;
> +				ctrls_found[num_ctrls].entity = entity;
> +				++num_ctrls;
> +			}
> +		}
> +
> +		if (num_ctrls == 0) {
> +			ret = -EINVAL;
> +			entity = NULL;
> +			goto done;
> +		}
> +
> +		qsort(ctrls_found, num_ctrls, sizeof(*ctrls_found), sort_ctrls);
> +
> +		queryctrl = ctrls_found[0].queryctrl;
> +		target_entity = ctrls_found[0].entity;
> +
> +		free(ctrls_found);
> +	}
> +
> +	entity = get_pipeline_entity_by_cid(plugin, queryctrl.id);
> +	if (entity)
> +		target_entity = entity;
> +
> +	ret = SYS_IOCTL(media_entity_get_v4l2_subdev(target_entity)->fd,
> +			VIDIOC_QUERYCTRL, &queryctrl);
> +
> +done:
> +	V4L2_EXYNOS4_DBG(
> +		"VIDIOC_QUERYCTRL [id: 0x%8.8x, name: %s, entity: %s] (%d)",
> +		ret ? arg->id : queryctrl.id, ret ? NULL : queryctrl.name,
> +		target_entity ? media_entity_get_info(target_entity)->name :
> +				NULL, ret);
> +
> +	*arg = queryctrl;
> +
> +	return ret;
> +}
> +
> +int query_ext_ctrl_ioctl(struct exynos4_camera_plugin *plugin,
> +			 struct v4l2_query_ext_ctrl *arg)
> +{
> +	struct pipeline_entity *pipeline = plugin->pipeline;
> +	struct media_entity *entity, *target_entity;
> +	struct v4l2_query_ext_ctrl query_ext_ctrl = *arg;
> +	struct media_entity_to_cid *ctrls_found;
> +	int i, ret = -EINVAL, num_ctrls = 0;
> +
> +	/*
> +	 * If id is or'ed with V4L2_CTRL_FLAG_NEXT_CTRL then the control to
> +	 * be found is the one with the next lowest id among all entities
> +	 * in the pipeline.
> +	 */
> +	if (query_ext_ctrl.id & V4L2_CTRL_FLAG_NEXT_CTRL) {
> +		ctrls_found = malloc(sizeof(*ctrls_found));
> +
> +		for (i = 0; i < plugin->pipeline_len; i++) {
> +			entity = pipeline[i].entity;
> +
> +			query_ext_ctrl = *arg;
> +
> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +					VIDIOC_QUERY_EXT_CTRL, &query_ext_ctrl.id);
> +			if (!ret) {
> +				ctrls_found = realloc(ctrls_found,
> +					sizeof(*ctrls_found) * (num_ctrls + 1));
> +				ctrls_found[num_ctrls].query_ext_ctrl =
> +								query_ext_ctrl;
> +				ctrls_found[num_ctrls].entity = entity;
> +				++num_ctrls;
> +			}
> +		}
> +
> +		if (num_ctrls == 0) {
> +			ret = -EINVAL;
> +			entity = NULL;
> +			goto done;
> +		}
> +
> +		qsort(ctrls_found, num_ctrls, sizeof(*ctrls_found), sort_ctrls);
> +
> +		query_ext_ctrl = ctrls_found[0].query_ext_ctrl;
> +		target_entity = ctrls_found[0].entity;
> +
> +		free(ctrls_found);
> +	}
> +
> +	entity = get_pipeline_entity_by_cid(plugin, query_ext_ctrl.id);
> +	if (entity)
> +		target_entity = entity;
> +
> +	ret = SYS_IOCTL(media_entity_get_v4l2_subdev(target_entity)->fd,
> +			VIDIOC_QUERYCTRL, &query_ext_ctrl);
> +
> +done:
> +	V4L2_EXYNOS4_DBG(
> +		"VIDIOC_QUERY_EXT_CTRL [id: 0x%8.8x, name: %s, entity: %s] (%d)",
> +		ret ? arg->id : query_ext_ctrl.id,
> +		ret ? NULL : query_ext_ctrl.name,
> +		target_entity ? media_entity_get_info(target_entity)->name :
> +				NULL, ret);
> +
> +	*arg = query_ext_ctrl;
> +
> +	return ret;
> +}
> +
> +int querymenu_ioctl(struct exynos4_camera_plugin *plugin,
> +		    struct v4l2_querymenu *arg)

static? I presume a lot of the other functions should be static as well.

> +{
> +	struct pipeline_entity *pipeline = plugin->pipeline;
> +	struct media_entity *entity;
> +	struct v4l2_querymenu querymenu = *arg;
> +	int i, ret = -EINVAL;
> +
> +	entity = get_pipeline_entity_by_cid(plugin, querymenu.id);
> +	if (entity) {
> +		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +			        VIDIOC_QUERYMENU, &querymenu);
> +		goto exit;
> +	}
> +
> +	for (i = 0; i < plugin->pipeline_len; i++) {
> +		entity = pipeline[i].entity;
> +
> +		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
> +				VIDIOC_QUERYMENU, &querymenu);
> +		if (!ret)
> +			break;
> +	}
> +
> +exit:
> +	*arg = querymenu;
> +
> +	V4L2_EXYNOS4_DBG(
> +		"VIDIOC_QUERYMENU [id: 0x%8.8x, name: %s, entity: %s] (%d)",
> +		querymenu.id, ret ? NULL : querymenu.name,
> +		entity ? media_entity_get_info(entity)->name : NULL, ret);
> +
> +	return ret;
> +}
> +
> +static void *plugin_init(int fd)
> +{
> +	struct v4l2_capability cap = { 0 };
> +	struct exynos4_camera_plugin *plugin = NULL;
> +	const char *sink_entity_name;
> +	struct media_device *media;
> +	struct media_entity *sink_entity;
> +	int ret;
> +
> +	ret = SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap);
> +	if (ret < 0) {
> +		V4L2_EXYNOS4_ERR("Failed to query video capabilities.");
> +		return NULL;
> +	}
> +
> +	/* Check if this is Exynos4 media device */
> +	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 media device.");
> +		return NULL;
> +	}
> +
> +	/*
> +	 * Create a representation of the media device
> +	 * containing the opened video device.
> +	 */
> +	media = media_device_new_by_subdev_fd(fd, &sink_entity);
> +	if (media == NULL) {
> +		V4L2_EXYNOS4_ERR("Failed to create media device.");
> +		return NULL;
> +	}
> +
> +#ifdef DEBUG
> +	media_debug_set_handler(media, (void (*)(void *, ...))fprintf, stdout);
> +#endif
> +
> +	sink_entity_name = media_entity_get_info(sink_entity)->name;
> +
> +	/* Check if video entity is of capture type, not m2m */
> +	if (!is_capture_entity(sink_entity_name)) {
> +		V4L2_EXYNOS4_ERR("Device not of capture type.");
> +		goto err_get_sink_entity;
> +	}
> +
> +	/*
> +	 * Initialize sink_entity subdev fd with the one opened
> +	 * by the libv4l core to avoid losing it on pipeline opening.
> +	 */
> +	v4l2_subdev_create_opened(sink_entity, fd);
> +
> +	/* Parse media configuration file and apply its settings */
> +	ret = mediatext_parse_setup_config(media, EXYNOS4_CAPTURE_CONF);
> +	if (ret < 0) {
> +		V4L2_EXYNOS4_ERR("Media config parser error.");
> +		goto err_get_sink_entity;
> +	}
> +
> +	/* Allocate private data */
> +	plugin = calloc(1, sizeof(*plugin));
> +	if (!plugin)
> +		goto err_get_sink_entity;
> +
> +	plugin->media = media;
> +
> +	/*
> +	 * Discover the pipeline of sub-devices from a camera sensor
> +	 * to the opened video device.
> +	 */
> +	ret = discover_pipeline_by_entity(plugin, sink_entity);
> +	if (ret < 0) {
> +		V4L2_EXYNOS4_ERR("Error discovering video pipeline.");
> +		goto err_discover_pipeline;
> +	}
> +
> +	/* Open all sub-devices in the discovered pipeline */
> +	ret = open_pipeline(plugin);
> +	if (ret < 0) {
> +		V4L2_EXYNOS4_ERR("Error opening video pipeline.");
> +		goto err_discover_pipeline;
> +	}
> +
> +	V4L2_EXYNOS4_LOG("Initialized exynos4-camera plugin.");
> +
> +	return plugin;
> +
> +err_discover_pipeline:
> +	free(plugin);
> +err_get_sink_entity:
> +	if (media)
> +		media_device_unref(media);
> +	return NULL;
> +}
> +
> +static void plugin_close(void *dev_ops_priv)
> +{
> +	struct exynos4_camera_plugin *plugin = dev_ops_priv;
> +
> +	if (plugin == NULL)
> +		return;
> +
> +	media_device_unref(plugin->media);
> +
> +	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;
> +
> +	if (plugin == NULL || arg == NULL)
> +		return -EINVAL;
> +
> +	switch (cmd) {
> +	case VIDIOC_S_CTRL:
> +	case VIDIOC_G_CTRL:
> +		return ctrl_ioctl(plugin, cmd, arg);
> +	case VIDIOC_S_EXT_CTRLS:
> +	case VIDIOC_G_EXT_CTRLS:
> +	case VIDIOC_TRY_EXT_CTRLS:
> +		return ext_ctrl_ioctl(plugin, cmd, arg);
> +	case VIDIOC_QUERYCTRL:
> +		return queryctrl_ioctl(plugin, arg);
> +	case VIDIOC_QUERY_EXT_CTRL:
> +		return query_ext_ctrl_ioctl(plugin, arg);
> +	case VIDIOC_QUERYMENU:
> +		return querymenu_ioctl(plugin, arg);
> +	case VIDIOC_TRY_FMT:
> +		return set_fmt_ioctl(plugin, cmd, arg,
> +				     V4L2_SUBDEV_FORMAT_TRY);
> +	case VIDIOC_S_FMT:
> +		return set_fmt_ioctl(plugin, cmd, arg,
> +				     V4L2_SUBDEV_FORMAT_ACTIVE);
> +	case VIDIOC_G_FMT:
> +		return get_fmt_ioctl(fd, cmd, arg);
> +	case VIDIOC_QUERYCAP:
> +		return querycap_ioctl(fd, arg);
> +	case VIDIOC_QBUF:
> +	case VIDIOC_DQBUF:
> +	case VIDIOC_QUERYBUF:
> +	case VIDIOC_PREPARE_BUF:
> +		return buf_ioctl(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;
> +			__u32 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,
> +};

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers
  2016-11-24 14:32       ` Sakari Ailus
@ 2016-11-24 15:39         ` Jacek Anaszewski
  0 siblings, 0 replies; 35+ messages in thread
From: Jacek Anaszewski @ 2016-11-24 15:39 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki

On 11/24/2016 03:32 PM, Sakari Ailus wrote:
[...]
>>>> +	sprintf(device_dir_path, "/sys/class/video4linux/%s/device/", p + 1);
>>>> +
>>>> +	device_dir = opendir(device_dir_path);
>>>> +	if (device_dir == NULL)
>>>> +		return NULL;
>>>> +
>>>> +	while ((entry = readdir(device_dir))) {
>>>> +		if (strncmp(entry->d_name, "media", 4))
>>>
>>> Why 4? And isn't entry->d_name nul-terminated, so you could use strcmp()?
>>
>> Media devices, as other devices, have numerical postfix, which is
>> not of our interest.
>
> Right. But still 5 would be the right number as we should also check the
> last "a".

Of course, this needs to be fixed, thanks.

-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 6/7] mediactl: libv4l2subdev: add support for comparing mbus formats
  2016-11-24 14:36   ` Sakari Ailus
@ 2016-11-24 15:49     ` Jacek Anaszewski
  0 siblings, 0 replies; 35+ messages in thread
From: Jacek Anaszewski @ 2016-11-24 15:49 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki

On 11/24/2016 03:36 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Wed, Oct 12, 2016 at 04:35:21PM +0200, Jacek Anaszewski wrote:
>> This patch adds a function for checking whether two mbus formats
>> are compatible.
>
> Compatible doesn't in general case mean the same as... the same.
>
> On parallel busses a 10-bit source can be connected to an 8-bit sink-for
> instance.
>
> How about moving this to the plugin, and if someone else needs it, then we
> move it out later?

This is a good idea, as I am checking not all fields of
v4l2_mbus_framefmt, but only those which matter during Exynos4 media
devuce pipeline format negotiation.

-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 7/7] Add a libv4l plugin for Exynos4 camera
  2016-11-24 15:11   ` Sakari Ailus
@ 2016-11-24 16:14     ` Jacek Anaszewski
  2016-11-24 23:55       ` Sakari Ailus
  0 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-11-24 16:14 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski,
	s.nawrocki, gjasny

Hi Sakari,

On 11/24/2016 04:11 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> Thank you for your continued work on the Exynos plugin patchset!
>
> I think we're pretty close to being able to merge the set, if you could
> still bear with me awhile. :-)

Your demanding reviewer I must admit. :-)
Of course, I appreciate all your remarks, they're highly valuable.

> On Wed, Oct 12, 2016 at 04:35:22PM +0200, Jacek Anaszewski wrote:
> ...
>> diff --git a/lib/libv4l-exynos4-camera/Makefile.am b/lib/libv4l-exynos4-camera/Makefile.am
>> new file mode 100644
>> index 0000000..c38b7f6
>> --- /dev/null
>> +++ b/lib/libv4l-exynos4-camera/Makefile.am
>> @@ -0,0 +1,19 @@
>> +if WITH_V4L_PLUGINS
>> +libv4l2plugin_LTLIBRARIES = libv4l-exynos4-camera.la
>> +endif
>> +
>> +media-bus-format-names.h: ../../include/linux/media-bus-format.h
>> +	sed -e '/#define MEDIA_BUS_FMT/ ! d; s/.*FMT_//; /FIXED/ d; s/\t.*//; s/.*/{ \"&\", MEDIA_BUS_FMT_& },/;' \
>> +	< $< > $@
>> +
>> +media-bus-format-codes.h: ../../include/linux/media-bus-format.h
>> +	sed -e '/#define MEDIA_BUS_FMT/ ! d; s/.*#define //; /FIXED/ d; s/\t.*//; s/.*/ &,/;' \
>> +	< $< > $@
>> +
>> +BUILT_SOURCES = media-bus-format-names.h media-bus-format-codes.h
>> +CLEANFILES = $(BUILT_SOURCES)
>
> It'd be nice to be able to use the same generated headers that now are under
> utils/media-ctl, instead of copying the sed script here in verbatim. If the
> script is changed or fixed in some way, the other location probably will
> remain unchanged...

The problem is that those headers are built after this plugin.

> I wonder if there's a proper way to generate build time headers such as
> these.
>
> Another less good alternative would be to put these into a separate Makefile
> and include that Makefile where the headers are needed. But I don't like
> that much either, it's a hack.

In this case it seems to be the only feasible optimization.

>> +
>> +nodist_libv4l_exynos4_camera_la_SOURCES = $(BUILT_SOURCES)
>> +libv4l_exynos4_camera_la_SOURCES = libv4l-exynos4-camera.c ../../utils/media-ctl/libmediactl.c ../../utils/media-ctl/libv4l2subdev.c ../../utils/media-ctl/mediatext.c
>> +libv4l_exynos4_camera_la_CFLAGS = -fvisibility=hidden -std=gnu99
>> +libv4l_exynos4_camera_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -lpthread
>> 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..c219fe5
>> --- /dev/null
>> +++ b/lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
>> @@ -0,0 +1,1325 @@
>> +/*
>> + * Copyright (c) 2016 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 <linux/types.h>
>> +#include <stdbool.h>
>> +#include <stdint.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +#include <sys/ioctl.h>
>> +#include <sys/syscall.h>
>> +#include <unistd.h>
>> +
>> +#include "../../utils/media-ctl/mediactl.h"
>> +#include "../../utils/media-ctl/mediatext.h"
>> +#include "../../utils/media-ctl/v4l2subdev.h"
>> +#include "libv4l-plugin.h"
>> +
>> +#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)
>> +
>> +#define VIDIOC_CTRL(type)				\
>> +	((type) == VIDIOC_S_CTRL ? "VIDIOC_S_CTRL" :	\
>> +				   "VIDIOC_G_CTRL")
>> +
>> +#if HAVE_VISIBILITY
>> +#define PLUGIN_PUBLIC __attribute__ ((visibility("default")))
>> +#else
>> +#define PLUGIN_PUBLIC
>> +#endif
>> +
>> +#define SYS_IOCTL(fd, cmd, arg) \
>> +	syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg))
>> +
>> +#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;                                          \
>> +	})
>> +
>> +#ifndef min
>> +#define min(a, b) (((a) < (b)) ? (a) : (b))
>> +#endif
>> +
>> +#ifndef max
>> +#define max(a, b) (((a) > (b)) ? (a) : (b))
>> +#endif
>> +
>> +#define EXYNOS4_FIMC_DRV	"exynos4-fimc"
>> +#define EXYNOS4_FIMC_LITE_DRV	"exynos-fimc-lit"
>> +#define EXYNOS4_FIMC_IS_ISP_DRV	"exynos4-fimc-is"
>> +#define EXYNOS4_CAPTURE_CONF	"/var/lib/libv4l/exynos4_capture_conf"
>
> Is this something that should be configurable in ./configure?

OK.

>
>> +#define EXYNOS4_FIMC_IS_ISP	"FIMC-IS-ISP"
>> +#define EXYNOS4_S5K6A3		"S5K6A3"
>
> Maybe this one as well. But it could come from a configuration file, too.

Right, the sensor is independent of SoC.

>
> The problem at large is actually something else: how do you recognise the
> hardware you're running on?
>
> You could find the sensor by walking the media graph.
>
> But if you need to know something very hardware specific it might not be
> available through the generic interfaces, and you might need to know the
> exact hardware you have.
>
> How do you know that you should load a particular plugin? I wonder if we
> could add a small snippet of code to detect different kinds of systems, I
> presume this is the plugin you want to load if the user has opened a video
> node of the Exynos4 ISP; you could find through the Media controller.

This code snippet in plugin_init() detects if we are on Exynos4 media
device:

         ret = SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap);
         if (ret < 0) {
                 V4L2_EXYNOS4_ERR("Failed to query video capabilities.");
                 return NULL;
         }

         /* Check if this is Exynos4 media device */
         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 media device.");
                 return NULL;
         }

Additionally I check if this is capture and not m2m device:

         /* Check if video entity is of capture type, not m2m */
         if (!is_capture_entity(sink_entity_name)) {
                 V4L2_EXYNOS4_ERR("Device not of capture type.");
                 goto err_get_sink_entity;
         }



>
> That's certainly out of scope of the patchset, I just thought of binging up
> the topics for discussion. :-)
>
>> +#define EXYNOS4_FIMC_LITE_PREFIX "FIMC-LITE."
>> +#define EXYNOS4_FIMC_PREFIX	"FIMC."
>> +#define EXYNOS4_MAX_FMT_NEGO_NUM 50
>> +#define EXYNOS4_MAX_PIPELINE_LEN 7
>> +
>> +struct media_device;
>> +struct media_entity;
>> +
>> +/*
>> + * struct pipeline_entity - linked media device pipeline
>> + * @entity:		linked entity
>> + * @sink_pad:		inbound link pad of the entity
>> + * @src_pad:		outbound link pad of the entity
>> + */
>> +struct pipeline_entity {
>> +	struct media_entity *entity;
>> +	struct media_pad *sink_pad;
>> +	struct media_pad *src_pad;
>> +};
>> +
>> +/*
>> + * struct media_entity_to_cid - entity to control map
>> + * @entity:		media entity
>> + * @sink_pad:		inbound link pad of the entity
>> + * @src_pad:		outbound link pad of the entity
>> + */
>> +struct media_entity_to_cid {
>> +	struct media_entity *entity;
>> +	union {
>> +		struct v4l2_queryctrl queryctrl;
>> +		struct v4l2_query_ext_ctrl query_ext_ctrl;
>> +	};
>> +};
>> +
>> +/*
>> + * struct exynos4_camera_plugin - libv4l exynos4 camera plugin
>> + * @media:		media device comprising the opened video device
>> + * @pipeline:		video pipeline, element 0 is the source entity
>> + * @pipeline_len:	length of the video pipeline
>> + */
>> +struct exynos4_camera_plugin {
>
> static?

Right.

>> +	struct media_device *media;
>> +	struct pipeline_entity pipeline[EXYNOS4_MAX_PIPELINE_LEN];
>> +	unsigned int pipeline_len;
>> +};
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Pipeline operations
>> + */
>> +
>> +/**
>> + * @brief Discover video pipeline for given sink entity
>> + * @param plugin - this plugin.
>> + * @param entity - sink entity of the pipeline.
>> + *
>> + * Discover the video pipeline, by walking starting from the
>> + * sink entity upwards until a source entity is encountered.
>> + *
>> + * @return 0 if the sensor entity was detected,
>> + * 	   or negative error code on failure.
>> + */
>> +static int discover_pipeline_by_entity(struct exynos4_camera_plugin *plugin,
>> +				       struct media_entity *entity)
>> +{
>> +	struct pipeline_entity reverse_pipeline[EXYNOS4_MAX_PIPELINE_LEN];
>> +	struct media_pad *src_pad;
>> +	struct media_link *link = NULL, *backlinks[2];
>> +	unsigned int num_backlinks, cur_pipe_pos = 0;
>> +	int i, j;
>> +	int ret;
>> +
>> +	if (entity == NULL)
>> +		return -EINVAL;
>> +
>> +	for (;;) {
>> +		/* Cache the recently discovered entity. */
>> +		reverse_pipeline[cur_pipe_pos].entity = entity;
>> +
>> +		/* Cache the source pad used for linking the entity. */
>> +		if (link)
>> +			reverse_pipeline[cur_pipe_pos].src_pad = link->source;
>> +
>> +		ret = media_entity_get_backlinks(entity, backlinks,
>> +						 &num_backlinks);
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		/* Check if pipeline source entity has been reached. */
>> +		if (num_backlinks > 2) {
>> +			V4L2_EXYNOS4_DBG("Unexpected number of busy sink pads (%d)",
>> +					 num_backlinks);
>> +			return -EINVAL;
>> +		} else if (num_backlinks == 2) {
>> +			/*
>> +			 * Allow two active pads only in case of
>> +			 * S5C73M3-OIF entity.
>> +			 */
>> +			if (strcmp(media_entity_get_info(entity)->name,
>> +				   "S5C73M3-OIF")) {
>> +				V4L2_EXYNOS4_DBG("Ambiguous media device topology: "
>> +						 "two busy sink pads");
>> +				return -EINVAL;
>> +			}
>> +			/*
>> +			 * Two active links are allowed betwen S5C73M3-OIF and
>> +			 * S5C73M3 entities. In such a case route through the
>> +			 * pad with id == 0 has to be chosen.
>> +			 */
>> +			link = NULL;
>> +			for (i = 0; i < num_backlinks; i++)
>> +				if (backlinks[i]->sink->index == 0)
>> +					link = backlinks[i];
>> +			if (link == NULL)
>> +				return -EINVAL;
>> +		} else if (num_backlinks == 1) {
>> +			link = backlinks[0];
>> +		} else {
>> +			reverse_pipeline[cur_pipe_pos].sink_pad = NULL;
>> +			break;
>> +		}
>> +
>> +		/* Cache the sink pad used for linking the entity. */
>> +		reverse_pipeline[cur_pipe_pos].sink_pad = link->sink;
>> +
>> +		V4L2_EXYNOS4_DBG("Discovered sink pad %d for the %s entity",
>> +				 reverse_pipeline[cur_pipe_pos].sink_pad->index,
>> +				 media_entity_get_info(entity)->name);
>> +
>> +		src_pad = media_entity_remote_source(link->sink);
>> +		if (!src_pad)
>> +			return -EINVAL;
>> +
>> +		entity = src_pad->entity;
>> +		if (++cur_pipe_pos == EXYNOS4_MAX_PIPELINE_LEN)
>> +			return -EINVAL;
>> +	}
>> +
>> +	/*
>> +	 * Reorder discovered pipeline elements so that the sensor
>> +	 * entity was the pipeline head.
>> +	 */
>> +	j = 0;
>> +	for (i = cur_pipe_pos; i >= 0; i--)
>
> How about:
>
> 	for (i = cur_pipe_pos, j = 0; i >= 0; i--, j++)
>
> And you can avoid setting j to zero outside the loop plus incrementing j++
> where it doesn't seem to fit too well.

I was thinking about it, but finally decided that this arrangement
will improve readability. Nothing prevents us to apply the
optimizations you've just suggested though.

>> +		plugin->pipeline[j++] = reverse_pipeline[i];
>> +
>> +	plugin->pipeline_len = j;
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * @brief Check if the entity belongs to the plugin video pipeline
>> + * @param plugin - this plugin.
>> + * @param entity_name - name of the entity to look for.
>> + *
>> + * Check if the entity belongs to the pipeline whose sink element
>> + * is the video device represented by the file descriptor passed
>> + * to plugin_init().
>> + *
>> + * @return True if the entity belongs to the pipeline,
>> + *         and false otherwise.
>> + */
>> +static bool has_pipeline_entity(struct exynos4_camera_plugin *plugin,
>> +				char *entity_name)
>> +{
>> +	struct pipeline_entity *pipeline = plugin->pipeline;
>> +	struct media_entity *entity;
>> +	unsigned int i;
>> +
>> +	if (pipeline == NULL || entity_name == NULL)
>> +		return -EINVAL;
>> +
>> +	for (i = 0; i < plugin->pipeline_len; i++) {
>> +		entity = pipeline[i].entity;
>> +		if (!strncmp(media_entity_get_info(entity)->name, entity_name,
>> +			     strlen(entity_name)))
>> +			return true;
>> +	}
>> +
>> +	return false;
>> +}
>> +
>> +/**
>> + * @brief Adjust mbus format to FIMC_IS_ISP limitations.
>> + * @param mbus_fmt - format to adjust.
>> + *
>> + * FIMC_IS_ISP shears the video frame off, effectively the format set
>> + * on the preceding entities has to be suitably greater to achieve
>> + * the frame size requested by the user.
>> + *
>> + * Also the miminum frame size supported by FIMC_IS_ISP is 128x128.
>> + */
>> +static void adjust_format_to_fimc_is_isp(struct v4l2_mbus_framefmt *mbus_fmt)
>> +{
>> +	mbus_fmt->width = max(128, mbus_fmt->width);
>> +	mbus_fmt->height = max(128, mbus_fmt->height);
>> +
>> +	mbus_fmt->width += 16;
>> +	mbus_fmt->height += 12;
>> +}
>> +
>> +/**
>> + * @brief Negotiate format acceptable for all pipeline entities.
>> + * @param plugin - this plugin.
>> + * @param dev_fmt - requested video device format.
>> + *
>> + * Negotiate common format acceptable by all the pipeline entities,
>> + * that is closest to the requested one.
>> + *
>> + * @return 0 if the negotiation succeeded,
>> + * 	   or negative error code on failure.
>> + */
>> +static int negotiate_pipeline_fmt(struct exynos4_camera_plugin *plugin,
>> +				  struct v4l2_format *dev_fmt)
>> +{
>> +	struct v4l2_mbus_framefmt mbus_fmt = { 0 }, common_fmt;
>> +	int repeat_negotiation, cnt_negotiation = 0, ret, pad_id, i;
>> +	struct pipeline_entity *pipeline = plugin->pipeline;
>> +	enum v4l2_subdev_fmt_mismatch fmt_err;
>> +	struct media_entity *entity;
>> +
>> +	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;
>> +
>> +	if (has_pipeline_entity(plugin, EXYNOS4_FIMC_IS_ISP))
>> +		adjust_format_to_fimc_is_isp(&mbus_fmt);
>> +
>> +	V4L2_EXYNOS4_DBG("Begin pipeline format negotiation...");
>> +
>> +	for (;;) {
>> +		repeat_negotiation = 0;
>> +		entity = pipeline[0].entity;
>> +
>> +		pad_id = pipeline[0].src_pad->index;
>> +
>> +		V4L2_EXYNOS4_DBG("Setting format on the source entity pad %s:%d",
>> +				 media_entity_get_info(entity)->name, pad_id);
>> +
>> +		ret = v4l2_subdev_set_format(entity, &mbus_fmt,
>> +					     pad_id, V4L2_SUBDEV_FORMAT_TRY);
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		V4L2_EXYNOS4_DBG("Format set on the pad %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
>> +				 media_entity_get_info(entity)->name, pad_id,
>> +				 v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
>> +				 v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
>> +				 mbus_fmt.width, mbus_fmt.height);
>> +
>> +		common_fmt = mbus_fmt;
>> +
>> +		/* Stop iterating on last but one entity as it is not a sub-device. */
>> +		for (i = 1; i < plugin->pipeline_len - 1; i++) {
>> +			entity = pipeline[i].entity;
>> +
>> +			pad_id = pipeline[i].sink_pad->index;
>> +
>> +			V4L2_EXYNOS4_DBG("Setting format on the pad %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
>> +					 media_entity_get_info(entity)->name, pad_id,
>> +					 v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
>> +					 v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
>> +					 mbus_fmt.width, mbus_fmt.height);
>> +
>> +			/* Set format on the entity sink pad. */
>> +			ret = v4l2_subdev_set_format(entity, &mbus_fmt, pad_id,
>> +							V4L2_SUBDEV_FORMAT_TRY);
>> +			if (ret < 0)
>> +				return ret;
>> +
>> +			fmt_err = v4l2_subdev_format_compare(&mbus_fmt, &common_fmt);
>> +			if (fmt_err) {
>> +				if (fmt_err == FMT_MISMATCH_WIDTH &&
>> +				     !strncmp(media_entity_get_info(entity)->name,
>> +					      EXYNOS4_FIMC_LITE_PREFIX,
>> +					      strlen(EXYNOS4_FIMC_LITE_PREFIX))) {
>> +					/*
>> +					 * Align width downwards, according to the FIMC-LITE
>> +					 * width step. Otherwise pipeline format negotiation
>> +					 * wouldn't succeed for widths excessing maximum sensor
>> +					 * frame width, which is probed by GStreamer, no matter
>> +					 * what actual frame size is to be set.
>> +					 */
>> +					mbus_fmt.width -= 8;
>> +				}
>> +				repeat_negotiation = 1;
>> +				break;
>> +			}
>> +
>> +			/*
>> +			 * Do not check format on FIMC.[n] source pad
>> +			 * and stop negotiation.
>> +			 */
>> +			if (!strncmp(media_entity_get_info(entity)->name,
>> +				     EXYNOS4_FIMC_PREFIX,
>> +				     strlen(EXYNOS4_FIMC_PREFIX)))
>> +				break;
>> +
>> +			pad_id = pipeline[i].src_pad->index;
>> +
>> +			/* Get format on the entity src pad */
>> +			ret = v4l2_subdev_get_format(entity, &mbus_fmt, pad_id,
>> +							V4L2_SUBDEV_FORMAT_TRY);
>> +			if (ret < 0)
>> +				return -EINVAL;
>> +
>> +			V4L2_EXYNOS4_DBG("Format propagated to the pad %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
>> +					 media_entity_get_info(entity)->name, pad_id,
>> +					 v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
>> +					 v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
>> +					 mbus_fmt.width, mbus_fmt.height);
>> +
>> +			if (!strcmp(media_entity_get_info(entity)->name,
>> +				    EXYNOS4_FIMC_IS_ISP)) {
>> +				common_fmt.code = mbus_fmt.code;
>> +				common_fmt.colorspace = mbus_fmt.colorspace;
>> +				common_fmt.width -= 16;
>> +				common_fmt.height -= 12;
>> +			}
>> +
>> +			if (v4l2_subdev_format_compare(&mbus_fmt, &common_fmt)) {
>> +				repeat_negotiation = 1;
>> +				break;
>> +			}
>> +		}
>> +
>> +		if (!repeat_negotiation) {
>> +			break;
>> +		} else if (++cnt_negotiation > EXYNOS4_MAX_FMT_NEGO_NUM) {
>> +			V4L2_EXYNOS4_DBG("Pipeline format negotiation failed!");
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	dev_fmt->fmt.pix_mp.width = mbus_fmt.width;
>> +	dev_fmt->fmt.pix_mp.height = mbus_fmt.height;
>> +	dev_fmt->fmt.pix_mp.field = mbus_fmt.field;
>> +	dev_fmt->fmt.pix_mp.colorspace = mbus_fmt.colorspace;
>> +
>> +	V4L2_EXYNOS4_DBG("Pipeline format successfuly negotiated");
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * @brief Apply previously negotiated pipeline format
>> + * @param plugin - this plugin.
>> + *
>> + * Apply the format, previously negotiated with negotiate_pipeline_fmt(),
>> + * on all the pipeline v4l2 sub-devices.
>> + *
>> + * @return 0 if the format was successfuly applied,
>> + * 	   or negative error code on failure.
>> + */
>> +static int apply_pipeline_fmt(struct exynos4_camera_plugin *plugin,
>> +			      struct v4l2_format *fmt)
>> +{
>> +	struct v4l2_mbus_framefmt mbus_fmt = { 0 };
>> +	struct pipeline_entity *pipeline = plugin->pipeline;
>> +	struct media_entity *entity;
>> +	struct media_pad *pad;
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	if (pipeline == NULL)
>> +		return -EINVAL;
>> +
>> +	for (i = 0; i < plugin->pipeline_len - 1; i++) {
>> +		entity = pipeline[i].entity;
>> +		/*
>> +		 * Source entity is linked only through a source pad
>> +		 * and this pad should be used for setting the format.
>> +		 * For other entities set the format on a sink pad.
>> +		 */
>> +		pad = pipeline[i].sink_pad ? pipeline[i].sink_pad :
>> +					pipeline[i].src_pad;
>> +		if (pad == NULL)
>> +			return -EINVAL;
>> +
>> +		ret = v4l2_subdev_get_format(entity, &mbus_fmt, pad->index,
>> +					     V4L2_SUBDEV_FORMAT_TRY);
>> +
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		V4L2_EXYNOS4_DBG("VIDIOC_SUBDEV_G_FMT %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
>> +			  media_entity_get_info(entity)->name, pad->index,
>> +			  v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
>> +			  v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
>> +			  mbus_fmt.width, mbus_fmt.height);
>> +
>> +		ret = v4l2_subdev_set_format(entity, &mbus_fmt, pad->index,
>> +					     V4L2_SUBDEV_FORMAT_ACTIVE);
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		V4L2_EXYNOS4_DBG("VIDIOC_SUBDEV_S_FMT %s:%d: mcode: %s, cs: %s, w: %d, h: %d",
>> +			  media_entity_get_info(entity)->name, pad->index,
>> +			  v4l2_subdev_pixelcode_to_string(mbus_fmt.code),
>> +			  v4l2_subdev_colorspace_to_string(mbus_fmt.colorspace),
>> +			  mbus_fmt.width, mbus_fmt.height);
>> +	}
>> +
>> +	/*
>> +	 * Sink entity represents /dev/videoN node and is not
>> +	 * a sub-device. Nonetheless because it has associated
>> +	 * file descriptor and can expose v4l2-controls the
>> +	 * v4l2-subdev structure is used for caching the
>> +	 * related data.
>> +	 */
>> +	struct v4l2_subdev *sd =
>> +		media_entity_get_v4l2_subdev(pipeline[i].entity);
>> +	if (!sd)
>> +		return -EINVAL;
>> +
>> +	ret = SYS_IOCTL(sd->fd, VIDIOC_S_FMT, fmt);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * @brief Open all v4l2 sub-devices associated with pipeline entities
>> + * @param plugin - this plugin.
>> + *
>> + * Open all v4l2 sub-devices associated with the entities of the pipeline
>> + * discovered with discover_pipeline_by_entity().
>> + *
>> + * @return 0 if all v4l2 sub-devices were opened successfuly,
>> + * 	   or negative error code on failure.
>> + */
>> +static int open_pipeline(struct exynos4_camera_plugin *plugin)
>> +{
>> +	struct pipeline_entity *pipeline = plugin->pipeline;
>> +	struct media_entity *entity;
>> +	unsigned int i;
>> +	int ret;
>> +
>> +	if (pipeline == NULL)
>> +		return -EINVAL;
>> +
>> +	/*
>> +	 * Stop walking the pipeline on the last but one entity, because
>> +	 * the sink entity was already opened by libv4l2 core.
>> +	 */
>> +	for (i = 0; i < plugin->pipeline_len - 1; i++) {
>> +		entity = pipeline[i].entity;
>> +		V4L2_EXYNOS4_DBG("Opening sub-device: %s",
>> +				 media_entity_get_info(entity)->name);
>> +		ret = v4l2_subdev_open(entity);
>> +		if (ret < 0)
>> +			return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * @brief Check if there was a v4l2_ctrl binding defined for the entity
>> + * @param entity - media entity.
>> + * @param ctrl_id - v4l2 control identifier.
>> + *
>> + * Check if there was a v4l2-ctrl-binding entry defined for the entity.
>> + *
>> + * @return true if the binding exists, false otherwise.
>> + */
>> +static bool has_v4l2_ctrl_binding(struct media_entity *entity,
>> +				       int ctrl_id)
>> +{
>> +	struct v4l2_subdev *sd = media_entity_get_v4l2_subdev(entity);
>> +	int i;
>> +
>> +	if (!sd)
>> +		return false;
>> +
>> +	for (i = 0; i < sd->num_v4l2_ctrl_bindings; ++i)
>> +		if (sd->v4l2_ctrl_bindings[i] == ctrl_id)
>> +			return true;
>> +
>> +	return false;
>> +}
>> +
>> +/**
>> + * @brief Get the first pipeline entity with matching v4l2-ctrl-binding.
>> + * @param plugin - this plugin.
>> + * @param cid - v4l2-control identifier.
>> + *
>> + * Get the first pipeline entity for which v4l2-control-binding
>> + * with given cid was defined.
>> + *
>> + * @return associated entity if defined, or NULL if no matching
>> + *         v4l2-ctrl-binding was defined for any entity
>> + *         in the pipeline.
>> + */
>> +static struct media_entity *get_pipeline_entity_by_cid(
>> +				struct exynos4_camera_plugin *plugin,
>> +				int cid)
>> +{
>> +	struct pipeline_entity *pipeline = plugin->pipeline;
>> +	struct media_entity *entity;
>> +	unsigned int i;
>> +
>> +	if (pipeline == NULL)
>> +		return NULL;
>> +
>> +	for (i = 0; i < plugin->pipeline_len; i++) {
>> +		entity = pipeline[i].entity;
>> +		if (has_v4l2_ctrl_binding(entity, cid))
>> +			return entity;
>> +	}
>> +
>> +	return NULL;
>> +}
>> +
>> +/**
>> + * @brief Check if the entity is of capture type
>> + * @param entity_name - name of the entity to check
>> + *
>> + * Check if the entity name ends with a "capture", which
>> + * gives a hint that it is of capture type.
>> + *
>> + * @return True if the entity name ends with a "capture"
>> + *         string, and false otherwise.
>> + */
>> +static bool is_capture_entity(const char *entity_name)
>> +{
>> +	const char capture_segment[] = "capture";
>> +	int cap_segment_pos;
>> +
>> +	if (entity_name == NULL)
>> +		return false;
>> +
>> +	cap_segment_pos = strlen(entity_name) - strlen(capture_segment);
>> +
>> +	if (strcmp(entity_name + cap_segment_pos, capture_segment) == 0)
>> +		return true;
>> +
>> +	return false;
>> +}
>> +
>> +static __u32 convert_type(__u32 type)
>> +{
>> +	switch (type) {
>> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> +		return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>> +	default:
>> +		return type;
>> +	}
>> +}
>> +
>> +/* -----------------------------------------------------------------------------
>> + * Ioctl handlers
>> + */
>> +
>> +static int set_fmt_ioctl(struct exynos4_camera_plugin *plugin,
>> +				unsigned long int cmd,
>> +				struct v4l2_format *arg,
>> +				enum v4l2_subdev_format_whence set_mode)
>> +{
>> +	struct v4l2_format fmt = { 0 };
>> +	int ret;
>> +
>> +	fmt.type = convert_type(arg->type);
>> +	if (fmt.type != arg->type) {
>> +		fmt.fmt.pix_mp.width = arg->fmt.pix.width;
>> +		fmt.fmt.pix_mp.height = arg->fmt.pix.height;
>> +		fmt.fmt.pix_mp.pixelformat = arg->fmt.pix.pixelformat;
>> +		fmt.fmt.pix_mp.field = arg->fmt.pix.field;
>> +		fmt.fmt.pix_mp.colorspace = arg->fmt.pix.colorspace;
>> +		fmt.fmt.pix_mp.num_planes = 1;
>> +		fmt.fmt.pix_mp.flags = arg->fmt.pix.flags;
>> +		fmt.fmt.pix_mp.plane_fmt[0].bytesperline = arg->fmt.pix.bytesperline;
>> +		fmt.fmt.pix_mp.plane_fmt[0].sizeimage = arg->fmt.pix.sizeimage;
>> +	} else {
>> +		fmt = *arg;
>> +	}
>> +
>> +	ret = negotiate_pipeline_fmt(plugin, &fmt);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	if (set_mode == V4L2_SUBDEV_FORMAT_ACTIVE) {
>> +		ret = apply_pipeline_fmt(plugin, &fmt);
>> +		if (ret < 0)
>> +			return ret;
>> +	}
>> +
>> +	if (fmt.type != arg->type) {
>> +		arg->fmt.pix.width = fmt.fmt.pix_mp.width;
>> +		arg->fmt.pix.height = fmt.fmt.pix_mp.height;
>> +		arg->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
>> +		arg->fmt.pix.field = fmt.fmt.pix_mp.field;
>> +		arg->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
>> +		arg->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
>> +		arg->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
>> +		arg->fmt.pix.flags = fmt.fmt.pix_mp.flags;
>> +	} else {
>> +		*arg = fmt;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int get_fmt_ioctl(int fd,
>> +			 unsigned long int cmd,
>> +			 struct v4l2_format *arg)
>> +{
>> +	struct v4l2_format fmt = { 0 };
>> +	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(&arg->fmt.pix, 0, sizeof(arg->fmt.pix));
>> +	arg->fmt.pix.width = fmt.fmt.pix_mp.width;
>> +	arg->fmt.pix.height = fmt.fmt.pix_mp.height;
>> +	arg->fmt.pix.pixelformat = fmt.fmt.pix_mp.pixelformat;
>> +	arg->fmt.pix.field = fmt.fmt.pix_mp.field;
>> +	arg->fmt.pix.colorspace = fmt.fmt.pix_mp.colorspace;
>> +	arg->fmt.pix.bytesperline = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
>> +	arg->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
>> +	arg->fmt.pix.flags = fmt.fmt.pix_mp.flags;
>> +
>> +	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(int fd, struct v4l2_capability *arg)
>> +{
>> +	int ret;
>> +
>> +	ret = SYS_IOCTL(fd, VIDIOC_QUERYCAP, arg);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	arg->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
>> +	arg->capabilities |= V4L2_CAP_VIDEO_CAPTURE;
>> +
>> +	return ret;
>> +}
>> +
>> +int ctrl_ioctl(struct exynos4_camera_plugin *plugin,
>> +	       unsigned long int request, struct v4l2_control *arg)
>> +{
>> +	struct pipeline_entity *pipeline = plugin->pipeline;
>> +	struct media_entity *entity;
>> +	struct v4l2_control ctrl = *arg;
>> +	struct v4l2_queryctrl queryctrl = { 0 };
>> +	bool ctrl_found = false;
>> +	int i, ret;
>> +
>> +	if (pipeline == NULL)
>> +		return -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) {
>> +		for (i = 0; i < plugin->pipeline_len; i++) {
>> +			struct v4l2_control ctrl_def = ctrl;
>> +
>> +			entity = pipeline[i].entity;
>> +
>> +			queryctrl.id = ctrl.id;
>> +
>> +			/* query default control value */
>> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +					VIDIOC_QUERYCTRL, &queryctrl);
>> +			if (ret < 0)
>> +				continue;
>> +
>> +			ctrl_found = true;
>> +
>> +			if (queryctrl.type & V4L2_CTRL_TYPE_BUTTON)
>> +				break;
>> +
>> +			ctrl_def.value = queryctrl.default_value;
>> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +					VIDIOC_S_CTRL, &ctrl_def);
>> +			if (ret < 0)
>> +				return -EINVAL;
>> +		}
>> +
>> +		if (!ctrl_found) {
>> +			ret = -EINVAL;
>> +			goto exit;
>> +		}
>> +	}
>> +
>> +	entity = get_pipeline_entity_by_cid(plugin, ctrl.id);
>> +
>> +	if (entity) {
>> +		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +				request, &ctrl);
>> +	} else {
>> +		/* Walk the pipeline until the request succeeds */
>> +		ret = -ENOENT;
>> +
>> +		for (i = 0; i < plugin->pipeline_len; i++) {
>> +			entity = pipeline[i].entity;
>> +
>> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +					request, &ctrl);
>> +			if (!ret)
>> +				break;
>> +		}
>> +	}
>> +
>> +exit:
>> +	*arg = ctrl;
>> +
>> +	V4L2_EXYNOS4_DBG("%s [id: 0x%8.8x, name: %s, entity: %s] (%d)",
>> +			 VIDIOC_CTRL(request), ctrl.id,
>> +			 ret ? NULL : queryctrl.name,
>> +			 entity ? media_entity_get_info(entity)->name :
>> +				  NULL, ret);
>> +
>> +
>> +	return ret;
>> +}
>> +
>> +static int single_ext_ctrl_ioctl(struct exynos4_camera_plugin *plugin,
>> +				 unsigned long int request,
>> +				 struct v4l2_ext_controls *arg)
>> +{
>> +	struct pipeline_entity *pipeline = plugin->pipeline;
>> +	struct media_entity *entity;
>> +	struct v4l2_ext_controls ctrls = *arg;
>> +	struct v4l2_ext_control *ctrl = &ctrls.controls[0];
>> +	struct v4l2_query_ext_ctrl queryctrl;
>> +	bool ctrl_found = 0;
>> +	int i, 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_EXT_CTRLS,
>> +	 * to avoid the situation when a control is set on more
>> +	 * than one sub-device.
>> +	 */
>> +	if (request == VIDIOC_S_EXT_CTRLS) {
>> +		for (i = 0; i < plugin->pipeline_len; i++) {
>> +			struct v4l2_ext_controls ctrls_def = ctrls;
>> +			struct v4l2_ext_control *ctrl_def = &ctrls_def.controls[0];
>> +
>> +			entity = pipeline[i].entity;
>> +
>> +			queryctrl.id = ctrl->id;
>> +
>> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +					VIDIOC_QUERY_EXT_CTRL, &queryctrl);
>> +			if (ret < 0)
>> +				continue;
>> +
>> +			ctrl_found = true;
>> +
>> +			if (queryctrl.type & V4L2_CTRL_TYPE_BUTTON)
>> +				break;
>> +
>> +			ctrl_def->value64 = queryctrl.default_value;
>> +
>> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +					VIDIOC_S_EXT_CTRLS, &ctrls_def);
>> +			if (ret < 0)
>> +				return -EINVAL;
>> +		}
>> +
>> +		if (!ctrl_found) {
>> +			ret = -EINVAL;
>> +			goto exit;
>> +		}
>> +	}
>> +
>> +	entity = get_pipeline_entity_by_cid(plugin, ctrl->id);
>> +
>> +	if (entity) {
>> +		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +				request, &ctrls);
>> +	} else {
>> +		/* Walk the pipeline until the request succeeds */
>> +		for (i = 0; i < plugin->pipeline_len; i++) {
>> +			entity = pipeline[i].entity;
>> +
>> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +					request, &ctrls);
>> +			if (!ret)
>> +				break;
>> +		}
>> +	}
>> +
>> +exit:
>> +	*arg = ctrls;
>> +
>> +	V4L2_EXYNOS4_DBG("%s [id: 0x%8.8x, name: %s, entity: %s] (%d)",
>> +			 VIDIOC_CTRL(request), ctrl->id,
>> +			 ret ? NULL : queryctrl.name,
>> +			 entity ? media_entity_get_info(entity)->name :
>> +				  NULL, ret);
>> +
>> +	return ret;
>> +}
>> +
>> +int ext_ctrl_ioctl(struct exynos4_camera_plugin *plugin,
>> +		   unsigned long 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 = single_ext_ctrl_ioctl(plugin, 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;
>> +			break;
>> +		}
>> +	}
>> +
>> +	*arg = out_ctrls;
>> +	return ret;
>> +}
>> +
>> +int sort_ctrls(const void * a, const void * b)
>> +{
>> +	const struct media_entity_to_cid *ctrl_a = a, *ctrl_b = b;
>> +
>> +	return ctrl_a->queryctrl.id - ctrl_b->queryctrl.id;
>> +}
>> +
>> +int queryctrl_ioctl(struct exynos4_camera_plugin *plugin,
>> +		    struct v4l2_queryctrl *arg)
>> +{
>> +	struct pipeline_entity *pipeline = plugin->pipeline;
>> +	struct media_entity *entity, *target_entity;
>> +	struct v4l2_queryctrl queryctrl = *arg;
>> +	struct media_entity_to_cid *ctrls_found;
>> +	int i, ret = -EINVAL, num_ctrls = 0;
>> +
>> +	/*
>> +	 * If id is or'ed with V4L2_CTRL_FLAG_NEXT_CTRL then the control to
>> +	 * be found is the one with the next lowest id among all entities
>> +	 * in the pipeline.
>> +	 */
>> +	if (queryctrl.id & V4L2_CTRL_FLAG_NEXT_CTRL) {
>> +		ctrls_found = malloc(sizeof(*ctrls_found));
>> +
>> +		for (i = 0; i < plugin->pipeline_len; i++) {
>> +			entity = pipeline[i].entity;
>> +
>> +			queryctrl = *arg;
>> +
>> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +					VIDIOC_QUERYCTRL, &queryctrl);
>> +			if (!ret) {
>> +				ctrls_found = realloc(ctrls_found,
>> +					sizeof(*ctrls_found) * (num_ctrls + 1));
>> +				ctrls_found[num_ctrls].queryctrl = queryctrl;
>> +				ctrls_found[num_ctrls].entity = entity;
>> +				++num_ctrls;
>> +			}
>> +		}
>> +
>> +		if (num_ctrls == 0) {
>> +			ret = -EINVAL;
>> +			entity = NULL;
>> +			goto done;
>> +		}
>> +
>> +		qsort(ctrls_found, num_ctrls, sizeof(*ctrls_found), sort_ctrls);
>> +
>> +		queryctrl = ctrls_found[0].queryctrl;
>> +		target_entity = ctrls_found[0].entity;
>> +
>> +		free(ctrls_found);
>> +	}
>> +
>> +	entity = get_pipeline_entity_by_cid(plugin, queryctrl.id);
>> +	if (entity)
>> +		target_entity = entity;
>> +
>> +	ret = SYS_IOCTL(media_entity_get_v4l2_subdev(target_entity)->fd,
>> +			VIDIOC_QUERYCTRL, &queryctrl);
>> +
>> +done:
>> +	V4L2_EXYNOS4_DBG(
>> +		"VIDIOC_QUERYCTRL [id: 0x%8.8x, name: %s, entity: %s] (%d)",
>> +		ret ? arg->id : queryctrl.id, ret ? NULL : queryctrl.name,
>> +		target_entity ? media_entity_get_info(target_entity)->name :
>> +				NULL, ret);
>> +
>> +	*arg = queryctrl;
>> +
>> +	return ret;
>> +}
>> +
>> +int query_ext_ctrl_ioctl(struct exynos4_camera_plugin *plugin,
>> +			 struct v4l2_query_ext_ctrl *arg)
>> +{
>> +	struct pipeline_entity *pipeline = plugin->pipeline;
>> +	struct media_entity *entity, *target_entity;
>> +	struct v4l2_query_ext_ctrl query_ext_ctrl = *arg;
>> +	struct media_entity_to_cid *ctrls_found;
>> +	int i, ret = -EINVAL, num_ctrls = 0;
>> +
>> +	/*
>> +	 * If id is or'ed with V4L2_CTRL_FLAG_NEXT_CTRL then the control to
>> +	 * be found is the one with the next lowest id among all entities
>> +	 * in the pipeline.
>> +	 */
>> +	if (query_ext_ctrl.id & V4L2_CTRL_FLAG_NEXT_CTRL) {
>> +		ctrls_found = malloc(sizeof(*ctrls_found));
>> +
>> +		for (i = 0; i < plugin->pipeline_len; i++) {
>> +			entity = pipeline[i].entity;
>> +
>> +			query_ext_ctrl = *arg;
>> +
>> +			ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +					VIDIOC_QUERY_EXT_CTRL, &query_ext_ctrl.id);
>> +			if (!ret) {
>> +				ctrls_found = realloc(ctrls_found,
>> +					sizeof(*ctrls_found) * (num_ctrls + 1));
>> +				ctrls_found[num_ctrls].query_ext_ctrl =
>> +								query_ext_ctrl;
>> +				ctrls_found[num_ctrls].entity = entity;
>> +				++num_ctrls;
>> +			}
>> +		}
>> +
>> +		if (num_ctrls == 0) {
>> +			ret = -EINVAL;
>> +			entity = NULL;
>> +			goto done;
>> +		}
>> +
>> +		qsort(ctrls_found, num_ctrls, sizeof(*ctrls_found), sort_ctrls);
>> +
>> +		query_ext_ctrl = ctrls_found[0].query_ext_ctrl;
>> +		target_entity = ctrls_found[0].entity;
>> +
>> +		free(ctrls_found);
>> +	}
>> +
>> +	entity = get_pipeline_entity_by_cid(plugin, query_ext_ctrl.id);
>> +	if (entity)
>> +		target_entity = entity;
>> +
>> +	ret = SYS_IOCTL(media_entity_get_v4l2_subdev(target_entity)->fd,
>> +			VIDIOC_QUERYCTRL, &query_ext_ctrl);
>> +
>> +done:
>> +	V4L2_EXYNOS4_DBG(
>> +		"VIDIOC_QUERY_EXT_CTRL [id: 0x%8.8x, name: %s, entity: %s] (%d)",
>> +		ret ? arg->id : query_ext_ctrl.id,
>> +		ret ? NULL : query_ext_ctrl.name,
>> +		target_entity ? media_entity_get_info(target_entity)->name :
>> +				NULL, ret);
>> +
>> +	*arg = query_ext_ctrl;
>> +
>> +	return ret;
>> +}
>> +
>> +int querymenu_ioctl(struct exynos4_camera_plugin *plugin,
>> +		    struct v4l2_querymenu *arg)
>
> static? I presume a lot of the other functions should be static as well.

Ah right, I missed these ones.

>> +{
>> +	struct pipeline_entity *pipeline = plugin->pipeline;
>> +	struct media_entity *entity;
>> +	struct v4l2_querymenu querymenu = *arg;
>> +	int i, ret = -EINVAL;
>> +
>> +	entity = get_pipeline_entity_by_cid(plugin, querymenu.id);
>> +	if (entity) {
>> +		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +			        VIDIOC_QUERYMENU, &querymenu);
>> +		goto exit;
>> +	}
>> +
>> +	for (i = 0; i < plugin->pipeline_len; i++) {
>> +		entity = pipeline[i].entity;
>> +
>> +		ret = SYS_IOCTL(media_entity_get_v4l2_subdev(entity)->fd,
>> +				VIDIOC_QUERYMENU, &querymenu);
>> +		if (!ret)
>> +			break;
>> +	}
>> +
>> +exit:
>> +	*arg = querymenu;
>> +
>> +	V4L2_EXYNOS4_DBG(
>> +		"VIDIOC_QUERYMENU [id: 0x%8.8x, name: %s, entity: %s] (%d)",
>> +		querymenu.id, ret ? NULL : querymenu.name,
>> +		entity ? media_entity_get_info(entity)->name : NULL, ret);
>> +
>> +	return ret;
>> +}
>> +
>> +static void *plugin_init(int fd)
>> +{
>> +	struct v4l2_capability cap = { 0 };
>> +	struct exynos4_camera_plugin *plugin = NULL;
>> +	const char *sink_entity_name;
>> +	struct media_device *media;
>> +	struct media_entity *sink_entity;
>> +	int ret;
>> +
>> +	ret = SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap);
>> +	if (ret < 0) {
>> +		V4L2_EXYNOS4_ERR("Failed to query video capabilities.");
>> +		return NULL;
>> +	}
>> +
>> +	/* Check if this is Exynos4 media device */
>> +	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 media device.");
>> +		return NULL;
>> +	}
>> +
>> +	/*
>> +	 * Create a representation of the media device
>> +	 * containing the opened video device.
>> +	 */
>> +	media = media_device_new_by_subdev_fd(fd, &sink_entity);
>> +	if (media == NULL) {
>> +		V4L2_EXYNOS4_ERR("Failed to create media device.");
>> +		return NULL;
>> +	}
>> +
>> +#ifdef DEBUG
>> +	media_debug_set_handler(media, (void (*)(void *, ...))fprintf, stdout);
>> +#endif
>> +
>> +	sink_entity_name = media_entity_get_info(sink_entity)->name;
>> +
>> +	/* Check if video entity is of capture type, not m2m */
>> +	if (!is_capture_entity(sink_entity_name)) {
>> +		V4L2_EXYNOS4_ERR("Device not of capture type.");
>> +		goto err_get_sink_entity;
>> +	}
>> +
>> +	/*
>> +	 * Initialize sink_entity subdev fd with the one opened
>> +	 * by the libv4l core to avoid losing it on pipeline opening.
>> +	 */
>> +	v4l2_subdev_create_opened(sink_entity, fd);
>> +
>> +	/* Parse media configuration file and apply its settings */
>> +	ret = mediatext_parse_setup_config(media, EXYNOS4_CAPTURE_CONF);
>> +	if (ret < 0) {
>> +		V4L2_EXYNOS4_ERR("Media config parser error.");
>> +		goto err_get_sink_entity;
>> +	}
>> +
>> +	/* Allocate private data */
>> +	plugin = calloc(1, sizeof(*plugin));
>> +	if (!plugin)
>> +		goto err_get_sink_entity;
>> +
>> +	plugin->media = media;
>> +
>> +	/*
>> +	 * Discover the pipeline of sub-devices from a camera sensor
>> +	 * to the opened video device.
>> +	 */
>> +	ret = discover_pipeline_by_entity(plugin, sink_entity);
>> +	if (ret < 0) {
>> +		V4L2_EXYNOS4_ERR("Error discovering video pipeline.");
>> +		goto err_discover_pipeline;
>> +	}
>> +
>> +	/* Open all sub-devices in the discovered pipeline */
>> +	ret = open_pipeline(plugin);
>> +	if (ret < 0) {
>> +		V4L2_EXYNOS4_ERR("Error opening video pipeline.");
>> +		goto err_discover_pipeline;
>> +	}
>> +
>> +	V4L2_EXYNOS4_LOG("Initialized exynos4-camera plugin.");
>> +
>> +	return plugin;
>> +
>> +err_discover_pipeline:
>> +	free(plugin);
>> +err_get_sink_entity:
>> +	if (media)
>> +		media_device_unref(media);
>> +	return NULL;
>> +}
>> +
>> +static void plugin_close(void *dev_ops_priv)
>> +{
>> +	struct exynos4_camera_plugin *plugin = dev_ops_priv;
>> +
>> +	if (plugin == NULL)
>> +		return;
>> +
>> +	media_device_unref(plugin->media);
>> +
>> +	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;
>> +
>> +	if (plugin == NULL || arg == NULL)
>> +		return -EINVAL;
>> +
>> +	switch (cmd) {
>> +	case VIDIOC_S_CTRL:
>> +	case VIDIOC_G_CTRL:
>> +		return ctrl_ioctl(plugin, cmd, arg);
>> +	case VIDIOC_S_EXT_CTRLS:
>> +	case VIDIOC_G_EXT_CTRLS:
>> +	case VIDIOC_TRY_EXT_CTRLS:
>> +		return ext_ctrl_ioctl(plugin, cmd, arg);
>> +	case VIDIOC_QUERYCTRL:
>> +		return queryctrl_ioctl(plugin, arg);
>> +	case VIDIOC_QUERY_EXT_CTRL:
>> +		return query_ext_ctrl_ioctl(plugin, arg);
>> +	case VIDIOC_QUERYMENU:
>> +		return querymenu_ioctl(plugin, arg);
>> +	case VIDIOC_TRY_FMT:
>> +		return set_fmt_ioctl(plugin, cmd, arg,
>> +				     V4L2_SUBDEV_FORMAT_TRY);
>> +	case VIDIOC_S_FMT:
>> +		return set_fmt_ioctl(plugin, cmd, arg,
>> +				     V4L2_SUBDEV_FORMAT_ACTIVE);
>> +	case VIDIOC_G_FMT:
>> +		return get_fmt_ioctl(fd, cmd, arg);
>> +	case VIDIOC_QUERYCAP:
>> +		return querycap_ioctl(fd, arg);
>> +	case VIDIOC_QBUF:
>> +	case VIDIOC_DQBUF:
>> +	case VIDIOC_QUERYBUF:
>> +	case VIDIOC_PREPARE_BUF:
>> +		return buf_ioctl(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;
>> +			__u32 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,
>> +};
>


-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 7/7] Add a libv4l plugin for Exynos4 camera
  2016-11-24 16:14     ` Jacek Anaszewski
@ 2016-11-24 23:55       ` Sakari Ailus
  0 siblings, 0 replies; 35+ messages in thread
From: Sakari Ailus @ 2016-11-24 23:55 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski,
	s.nawrocki, gjasny

Hi Jacek,

On Thu, Nov 24, 2016 at 05:14:40PM +0100, Jacek Anaszewski wrote:
> Hi Sakari,
> 
> On 11/24/2016 04:11 PM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >Thank you for your continued work on the Exynos plugin patchset!
> >
> >I think we're pretty close to being able to merge the set, if you could
> >still bear with me awhile. :-)
> 
> Your demanding reviewer I must admit. :-)
> Of course, I appreciate all your remarks, they're highly valuable.
> 
> >On Wed, Oct 12, 2016 at 04:35:22PM +0200, Jacek Anaszewski wrote:
> >...
> >>diff --git a/lib/libv4l-exynos4-camera/Makefile.am b/lib/libv4l-exynos4-camera/Makefile.am
> >>new file mode 100644
> >>index 0000000..c38b7f6
> >>--- /dev/null
> >>+++ b/lib/libv4l-exynos4-camera/Makefile.am
> >>@@ -0,0 +1,19 @@
> >>+if WITH_V4L_PLUGINS
> >>+libv4l2plugin_LTLIBRARIES = libv4l-exynos4-camera.la
> >>+endif
> >>+
> >>+media-bus-format-names.h: ../../include/linux/media-bus-format.h
> >>+	sed -e '/#define MEDIA_BUS_FMT/ ! d; s/.*FMT_//; /FIXED/ d; s/\t.*//; s/.*/{ \"&\", MEDIA_BUS_FMT_& },/;' \
> >>+	< $< > $@
> >>+
> >>+media-bus-format-codes.h: ../../include/linux/media-bus-format.h
> >>+	sed -e '/#define MEDIA_BUS_FMT/ ! d; s/.*#define //; /FIXED/ d; s/\t.*//; s/.*/ &,/;' \
> >>+	< $< > $@
> >>+
> >>+BUILT_SOURCES = media-bus-format-names.h media-bus-format-codes.h
> >>+CLEANFILES = $(BUILT_SOURCES)
> >
> >It'd be nice to be able to use the same generated headers that now are under
> >utils/media-ctl, instead of copying the sed script here in verbatim. If the
> >script is changed or fixed in some way, the other location probably will
> >remain unchanged...
> 
> The problem is that those headers are built after this plugin.

They could be moved to another directory from where they are now, and
explicitly built as a dependency. I don't know how to properly do that with
automake though.

I cc'd Gregor to the thread.

> 
> >I wonder if there's a proper way to generate build time headers such as
> >these.
> >
> >Another less good alternative would be to put these into a separate Makefile
> >and include that Makefile where the headers are needed. But I don't like
> >that much either, it's a hack.
> 
> In this case it seems to be the only feasible optimization.

I'm ok with that but still hope we could have something better. :-)

> 
> >>+
> >>+nodist_libv4l_exynos4_camera_la_SOURCES = $(BUILT_SOURCES)
> >>+libv4l_exynos4_camera_la_SOURCES = libv4l-exynos4-camera.c ../../utils/media-ctl/libmediactl.c ../../utils/media-ctl/libv4l2subdev.c ../../utils/media-ctl/mediatext.c
> >>+libv4l_exynos4_camera_la_CFLAGS = -fvisibility=hidden -std=gnu99
> >>+libv4l_exynos4_camera_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -lpthread
> >>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..c219fe5
> >>--- /dev/null
> >>+++ b/lib/libv4l-exynos4-camera/libv4l-exynos4-camera.c
> >>@@ -0,0 +1,1325 @@
> >>+/*
> >>+ * Copyright (c) 2016 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 <linux/types.h>
> >>+#include <stdbool.h>
> >>+#include <stdint.h>
> >>+#include <stdio.h>
> >>+#include <stdlib.h>
> >>+#include <string.h>
> >>+#include <sys/ioctl.h>
> >>+#include <sys/syscall.h>
> >>+#include <unistd.h>
> >>+
> >>+#include "../../utils/media-ctl/mediactl.h"
> >>+#include "../../utils/media-ctl/mediatext.h"
> >>+#include "../../utils/media-ctl/v4l2subdev.h"
> >>+#include "libv4l-plugin.h"
> >>+
> >>+#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)
> >>+
> >>+#define VIDIOC_CTRL(type)				\
> >>+	((type) == VIDIOC_S_CTRL ? "VIDIOC_S_CTRL" :	\
> >>+				   "VIDIOC_G_CTRL")
> >>+
> >>+#if HAVE_VISIBILITY
> >>+#define PLUGIN_PUBLIC __attribute__ ((visibility("default")))
> >>+#else
> >>+#define PLUGIN_PUBLIC
> >>+#endif
> >>+
> >>+#define SYS_IOCTL(fd, cmd, arg) \
> >>+	syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg))
> >>+
> >>+#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;                                          \
> >>+	})
> >>+
> >>+#ifndef min
> >>+#define min(a, b) (((a) < (b)) ? (a) : (b))
> >>+#endif
> >>+
> >>+#ifndef max
> >>+#define max(a, b) (((a) > (b)) ? (a) : (b))
> >>+#endif
> >>+
> >>+#define EXYNOS4_FIMC_DRV	"exynos4-fimc"
> >>+#define EXYNOS4_FIMC_LITE_DRV	"exynos-fimc-lit"
> >>+#define EXYNOS4_FIMC_IS_ISP_DRV	"exynos4-fimc-is"
> >>+#define EXYNOS4_CAPTURE_CONF	"/var/lib/libv4l/exynos4_capture_conf"
> >
> >Is this something that should be configurable in ./configure?
> 
> OK.
> 
> >
> >>+#define EXYNOS4_FIMC_IS_ISP	"FIMC-IS-ISP"
> >>+#define EXYNOS4_S5K6A3		"S5K6A3"
> >
> >Maybe this one as well. But it could come from a configuration file, too.
> 
> Right, the sensor is independent of SoC.
> 
> >
> >The problem at large is actually something else: how do you recognise the
> >hardware you're running on?
> >
> >You could find the sensor by walking the media graph.
> >
> >But if you need to know something very hardware specific it might not be
> >available through the generic interfaces, and you might need to know the
> >exact hardware you have.
> >
> >How do you know that you should load a particular plugin? I wonder if we
> >could add a small snippet of code to detect different kinds of systems, I
> >presume this is the plugin you want to load if the user has opened a video
> >node of the Exynos4 ISP; you could find through the Media controller.
> 
> This code snippet in plugin_init() detects if we are on Exynos4 media
> device:
> 
>         ret = SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap);
>         if (ret < 0) {
>                 V4L2_EXYNOS4_ERR("Failed to query video capabilities.");
>                 return NULL;
>         }
> 
>         /* Check if this is Exynos4 media device */
>         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 media device.");
>                 return NULL;
>         }
> 
> Additionally I check if this is capture and not m2m device:
> 
>         /* Check if video entity is of capture type, not m2m */
>         if (!is_capture_entity(sink_entity_name)) {
>                 V4L2_EXYNOS4_ERR("Device not of capture type.");
>                 goto err_get_sink_entity;
>         }

Ah, indeed. I presume it should work just fine then. Good.

The approach doesn't look too scalable still, but I don't think there's
a reason to worry about that now.

> 
> 
> 
> >
> >That's certainly out of scope of the patchset, I just thought of binging up
> >the topics for discussion. :-)
> >
> >>+#define EXYNOS4_FIMC_LITE_PREFIX "FIMC-LITE."
> >>+#define EXYNOS4_FIMC_PREFIX	"FIMC."
> >>+#define EXYNOS4_MAX_FMT_NEGO_NUM 50
> >>+#define EXYNOS4_MAX_PIPELINE_LEN 7
> >>+
> >>+struct media_device;
> >>+struct media_entity;
> >>+
> >>+/*
> >>+ * struct pipeline_entity - linked media device pipeline
> >>+ * @entity:		linked entity
> >>+ * @sink_pad:		inbound link pad of the entity
> >>+ * @src_pad:		outbound link pad of the entity
> >>+ */
> >>+struct pipeline_entity {
> >>+	struct media_entity *entity;
> >>+	struct media_pad *sink_pad;
> >>+	struct media_pad *src_pad;
> >>+};
> >>+
> >>+/*
> >>+ * struct media_entity_to_cid - entity to control map
> >>+ * @entity:		media entity
> >>+ * @sink_pad:		inbound link pad of the entity
> >>+ * @src_pad:		outbound link pad of the entity
> >>+ */
> >>+struct media_entity_to_cid {
> >>+	struct media_entity *entity;
> >>+	union {
> >>+		struct v4l2_queryctrl queryctrl;
> >>+		struct v4l2_query_ext_ctrl query_ext_ctrl;
> >>+	};
> >>+};
> >>+
> >>+/*
> >>+ * struct exynos4_camera_plugin - libv4l exynos4 camera plugin
> >>+ * @media:		media device comprising the opened video device
> >>+ * @pipeline:		video pipeline, element 0 is the source entity
> >>+ * @pipeline_len:	length of the video pipeline
> >>+ */
> >>+struct exynos4_camera_plugin {
> >
> >static?
> 
> Right.
> 
> >>+	struct media_device *media;
> >>+	struct pipeline_entity pipeline[EXYNOS4_MAX_PIPELINE_LEN];
> >>+	unsigned int pipeline_len;
> >>+};
> >>+
> >>+/* -----------------------------------------------------------------------------
> >>+ * Pipeline operations
> >>+ */
> >>+
> >>+/**
> >>+ * @brief Discover video pipeline for given sink entity
> >>+ * @param plugin - this plugin.
> >>+ * @param entity - sink entity of the pipeline.
> >>+ *
> >>+ * Discover the video pipeline, by walking starting from the
> >>+ * sink entity upwards until a source entity is encountered.
> >>+ *
> >>+ * @return 0 if the sensor entity was detected,
> >>+ * 	   or negative error code on failure.
> >>+ */
> >>+static int discover_pipeline_by_entity(struct exynos4_camera_plugin *plugin,
> >>+				       struct media_entity *entity)
> >>+{
> >>+	struct pipeline_entity reverse_pipeline[EXYNOS4_MAX_PIPELINE_LEN];
> >>+	struct media_pad *src_pad;
> >>+	struct media_link *link = NULL, *backlinks[2];
> >>+	unsigned int num_backlinks, cur_pipe_pos = 0;
> >>+	int i, j;
> >>+	int ret;
> >>+
> >>+	if (entity == NULL)
> >>+		return -EINVAL;
> >>+
> >>+	for (;;) {
> >>+		/* Cache the recently discovered entity. */
> >>+		reverse_pipeline[cur_pipe_pos].entity = entity;
> >>+
> >>+		/* Cache the source pad used for linking the entity. */
> >>+		if (link)
> >>+			reverse_pipeline[cur_pipe_pos].src_pad = link->source;
> >>+
> >>+		ret = media_entity_get_backlinks(entity, backlinks,
> >>+						 &num_backlinks);
> >>+		if (ret < 0)
> >>+			return ret;
> >>+
> >>+		/* Check if pipeline source entity has been reached. */
> >>+		if (num_backlinks > 2) {
> >>+			V4L2_EXYNOS4_DBG("Unexpected number of busy sink pads (%d)",
> >>+					 num_backlinks);
> >>+			return -EINVAL;
> >>+		} else if (num_backlinks == 2) {
> >>+			/*
> >>+			 * Allow two active pads only in case of
> >>+			 * S5C73M3-OIF entity.
> >>+			 */
> >>+			if (strcmp(media_entity_get_info(entity)->name,
> >>+				   "S5C73M3-OIF")) {
> >>+				V4L2_EXYNOS4_DBG("Ambiguous media device topology: "
> >>+						 "two busy sink pads");
> >>+				return -EINVAL;
> >>+			}
> >>+			/*
> >>+			 * Two active links are allowed betwen S5C73M3-OIF and
> >>+			 * S5C73M3 entities. In such a case route through the
> >>+			 * pad with id == 0 has to be chosen.
> >>+			 */
> >>+			link = NULL;
> >>+			for (i = 0; i < num_backlinks; i++)
> >>+				if (backlinks[i]->sink->index == 0)
> >>+					link = backlinks[i];
> >>+			if (link == NULL)
> >>+				return -EINVAL;
> >>+		} else if (num_backlinks == 1) {
> >>+			link = backlinks[0];
> >>+		} else {
> >>+			reverse_pipeline[cur_pipe_pos].sink_pad = NULL;
> >>+			break;
> >>+		}
> >>+
> >>+		/* Cache the sink pad used for linking the entity. */
> >>+		reverse_pipeline[cur_pipe_pos].sink_pad = link->sink;
> >>+
> >>+		V4L2_EXYNOS4_DBG("Discovered sink pad %d for the %s entity",
> >>+				 reverse_pipeline[cur_pipe_pos].sink_pad->index,
> >>+				 media_entity_get_info(entity)->name);
> >>+
> >>+		src_pad = media_entity_remote_source(link->sink);
> >>+		if (!src_pad)
> >>+			return -EINVAL;
> >>+
> >>+		entity = src_pad->entity;
> >>+		if (++cur_pipe_pos == EXYNOS4_MAX_PIPELINE_LEN)
> >>+			return -EINVAL;
> >>+	}
> >>+
> >>+	/*
> >>+	 * Reorder discovered pipeline elements so that the sensor
> >>+	 * entity was the pipeline head.
> >>+	 */
> >>+	j = 0;
> >>+	for (i = cur_pipe_pos; i >= 0; i--)
> >
> >How about:
> >
> >	for (i = cur_pipe_pos, j = 0; i >= 0; i--, j++)
> >
> >And you can avoid setting j to zero outside the loop plus incrementing j++
> >where it doesn't seem to fit too well.
> 
> I was thinking about it, but finally decided that this arrangement
> will improve readability. Nothing prevents us to apply the
> optimizations you've just suggested though.

You could do that using a single loop variable, replacing j by cur_pipe_pos
- i. Up to you. :-)

> 
> >>+		plugin->pipeline[j++] = reverse_pipeline[i];
> >>+
> >>+	plugin->pipeline_len = j;
> >>+
> >>+	return 0;
> >>+}
> >>+

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 1/7] mediactl: Add support for v4l2-ctrl-binding config
  2016-11-24 14:23   ` Sakari Ailus
@ 2016-11-28 21:32     ` Jacek Anaszewski
  2016-11-29  6:37       ` Sakari Ailus
  0 siblings, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-11-28 21:32 UTC (permalink / raw)
  To: Sakari Ailus, Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski,
	s.nawrocki, laurent.pinchart

Hi Sakari,

On 11/24/2016 03:23 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Wed, Oct 12, 2016 at 04:35:16PM +0200, Jacek Anaszewski wrote:
>> Make struct v4l2_subdev capable of aggregating v4l2-ctrl-bindings -
>> media device configuration entries. Added are also functions for
>> validating support for the control on given media entity and checking
>> whether a v4l2-ctrl-binding has been defined for a media entity.
>
> I still don't think this belongs here.
>
> I think I told you about the generic pipeline configuration library I worked
> on years ago; unfortunately it was left on prototype stage. Still, what I
> realised was that something very similar is needed in that library ---
> associating information to the representation of the media entities (or the
> V4L2 sub-devices) in user space that has got nothing to do with the devices
> themselves.
>
> We could have e.g. a list of key--value pairs where the key is a pointer
> provided by the user (i.e. application, another library) that could be
> associated with the value. That would avoid having explicit information on
> that in the struct media_entity itself.
>
> An quicker alternative would be to manage a list of controls e.g. in the
> plugin itself and store the media entity where they're implemented in that
> list, with the control value.

We are not interested in media entity -> control value relation but
but media entity -> control id. The value is an arbitrary choice of
userspace. Binding's task is to route the ctrl ioctl to a desired
pipeline entity if more than one supports same control.

Effectively we'd need a list of controls as a keys and entities
as values. The list should be allocated dynamically as it would
make no sense to keep keys for all v4l2 controls if only few bindings
are defined.

Best regards,
Jacek Anaszewski

> Cc Laurent as well.
>
>>
>> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
>> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>  utils/media-ctl/libv4l2subdev.c | 32 ++++++++++++++++++++++++++++++++
>>  utils/media-ctl/v4l2subdev.h    | 19 +++++++++++++++++++
>>  2 files changed, 51 insertions(+)
>>
>> diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c
>> index c3439d7..4f8ee7f 100644
>> --- a/utils/media-ctl/libv4l2subdev.c
>> +++ b/utils/media-ctl/libv4l2subdev.c
>> @@ -50,7 +50,15 @@ int v4l2_subdev_create(struct media_entity *entity)
>>
>>  	entity->sd->fd = -1;
>>
>> +	entity->sd->v4l2_ctrl_bindings = malloc(sizeof(__u32));
>> +	if (entity->sd->v4l2_ctrl_bindings == NULL)
>> +		goto err_v4l2_ctrl_bindings_alloc;
>> +
>>  	return 0;
>> +
>> +err_v4l2_ctrl_bindings_alloc:
>> +	free(entity->sd);
>> +	return -ENOMEM;
>>  }
>>
>>  int v4l2_subdev_create_opened(struct media_entity *entity, int fd)
>> @@ -102,6 +110,7 @@ void v4l2_subdev_close(struct media_entity *entity)
>>  	if (entity->sd->fd_owner)
>>  		close(entity->sd->fd);
>>
>> +	free(entity->sd->v4l2_ctrl_bindings);
>>  	free(entity->sd);
>>  }
>>
>> @@ -884,3 +893,26 @@ const enum v4l2_mbus_pixelcode *v4l2_subdev_pixelcode_list(unsigned int *length)
>>
>>  	return mbus_codes;
>>  }
>> +
>> +int v4l2_subdev_supports_v4l2_ctrl(struct media_device *media,
>> +				   struct media_entity *entity,
>> +				   __u32 ctrl_id)
>> +{
>> +	struct v4l2_queryctrl queryctrl = {};
>> +	int ret;
>> +
>> +	ret = v4l2_subdev_open(entity);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	queryctrl.id = ctrl_id;
>> +
>> +	ret = ioctl(entity->sd->fd, VIDIOC_QUERYCTRL, &queryctrl);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	media_dbg(media, "Validated control \"%s\" (0x%8.8x) on entity %s\n",
>> +		  queryctrl.name, queryctrl.id, entity->info.name);
>> +
>> +	return 0;
>> +}
>> diff --git a/utils/media-ctl/v4l2subdev.h b/utils/media-ctl/v4l2subdev.h
>> index 011fab1..4dee6b1 100644
>> --- a/utils/media-ctl/v4l2subdev.h
>> +++ b/utils/media-ctl/v4l2subdev.h
>> @@ -26,10 +26,14 @@
>>
>>  struct media_device;
>>  struct media_entity;
>> +struct media_device;
>>
>>  struct v4l2_subdev {
>>  	int fd;
>>  	unsigned int fd_owner:1;
>> +
>> +	__u32 *v4l2_ctrl_bindings;
>> +	unsigned int num_v4l2_ctrl_bindings;
>>  };
>>
>>  /**
>> @@ -314,4 +318,19 @@ enum v4l2_field v4l2_subdev_string_to_field(const char *string);
>>  const enum v4l2_mbus_pixelcode *v4l2_subdev_pixelcode_list(
>>  	unsigned int *length);
>>
>> +/**
>> + * @brief Check if sub-device supports given v4l2 control
>> + * @param media - media device.
>> + * @param entity - media entity.
>> + * @param ctrl_id - id of the v4l2 control to check.
>> + *
>> + * Verify if the sub-device associated with given media entity
>> + * supports v4l2-control with given ctrl_id.
>> + *
>> + * @return 1 if the control is supported, 0 otherwise.
>> + */
>> +int v4l2_subdev_supports_v4l2_ctrl(struct media_device *device,
>> +				   struct media_entity *entity,
>> +				   __u32 ctrl_id);
>> +
>>  #endif
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 1/7] mediactl: Add support for v4l2-ctrl-binding config
  2016-11-28 21:32     ` Jacek Anaszewski
@ 2016-11-29  6:37       ` Sakari Ailus
  0 siblings, 0 replies; 35+ messages in thread
From: Sakari Ailus @ 2016-11-29  6:37 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Jacek Anaszewski, linux-media, sakari.ailus, hverkuil, mchehab,
	m.szyprowski, s.nawrocki, laurent.pinchart

Hi Jacek,

On Mon, Nov 28, 2016 at 10:32:43PM +0100, Jacek Anaszewski wrote:
> Hi Sakari,
> 
> On 11/24/2016 03:23 PM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >On Wed, Oct 12, 2016 at 04:35:16PM +0200, Jacek Anaszewski wrote:
> >>Make struct v4l2_subdev capable of aggregating v4l2-ctrl-bindings -
> >>media device configuration entries. Added are also functions for
> >>validating support for the control on given media entity and checking
> >>whether a v4l2-ctrl-binding has been defined for a media entity.
> >
> >I still don't think this belongs here.
> >
> >I think I told you about the generic pipeline configuration library I worked
> >on years ago; unfortunately it was left on prototype stage. Still, what I
> >realised was that something very similar is needed in that library ---
> >associating information to the representation of the media entities (or the
> >V4L2 sub-devices) in user space that has got nothing to do with the devices
> >themselves.
> >
> >We could have e.g. a list of key--value pairs where the key is a pointer
> >provided by the user (i.e. application, another library) that could be
> >associated with the value. That would avoid having explicit information on
> >that in the struct media_entity itself.
> >
> >An quicker alternative would be to manage a list of controls e.g. in the
> >plugin itself and store the media entity where they're implemented in that
> >list, with the control value.

s/value/id/

> 
> We are not interested in media entity -> control value relation but
> but media entity -> control id. The value is an arbitrary choice of
> userspace. Binding's task is to route the ctrl ioctl to a desired
> pipeline entity if more than one supports same control.

Correct. But even that's more efficient if you don't need to iterate over
all the entities.

> 
> Effectively we'd need a list of controls as a keys and entities
> as values. The list should be allocated dynamically as it would
> make no sense to keep keys for all v4l2 controls if only few bindings
> are defined.
> 
> Best regards,
> Jacek Anaszewski
> 
> >Cc Laurent as well.
> >
> >>
> >>Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> >>Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> >>---
> >> utils/media-ctl/libv4l2subdev.c | 32 ++++++++++++++++++++++++++++++++
> >> utils/media-ctl/v4l2subdev.h    | 19 +++++++++++++++++++
> >> 2 files changed, 51 insertions(+)
> >>
> >>diff --git a/utils/media-ctl/libv4l2subdev.c b/utils/media-ctl/libv4l2subdev.c
> >>index c3439d7..4f8ee7f 100644
> >>--- a/utils/media-ctl/libv4l2subdev.c
> >>+++ b/utils/media-ctl/libv4l2subdev.c
> >>@@ -50,7 +50,15 @@ int v4l2_subdev_create(struct media_entity *entity)
> >>
> >> 	entity->sd->fd = -1;
> >>
> >>+	entity->sd->v4l2_ctrl_bindings = malloc(sizeof(__u32));
> >>+	if (entity->sd->v4l2_ctrl_bindings == NULL)
> >>+		goto err_v4l2_ctrl_bindings_alloc;
> >>+
> >> 	return 0;
> >>+
> >>+err_v4l2_ctrl_bindings_alloc:
> >>+	free(entity->sd);
> >>+	return -ENOMEM;
> >> }
> >>
> >> int v4l2_subdev_create_opened(struct media_entity *entity, int fd)
> >>@@ -102,6 +110,7 @@ void v4l2_subdev_close(struct media_entity *entity)
> >> 	if (entity->sd->fd_owner)
> >> 		close(entity->sd->fd);
> >>
> >>+	free(entity->sd->v4l2_ctrl_bindings);
> >> 	free(entity->sd);
> >> }
> >>
> >>@@ -884,3 +893,26 @@ const enum v4l2_mbus_pixelcode *v4l2_subdev_pixelcode_list(unsigned int *length)
> >>
> >> 	return mbus_codes;
> >> }
> >>+
> >>+int v4l2_subdev_supports_v4l2_ctrl(struct media_device *media,
> >>+				   struct media_entity *entity,
> >>+				   __u32 ctrl_id)
> >>+{
> >>+	struct v4l2_queryctrl queryctrl = {};
> >>+	int ret;
> >>+
> >>+	ret = v4l2_subdev_open(entity);
> >>+	if (ret < 0)
> >>+		return ret;
> >>+
> >>+	queryctrl.id = ctrl_id;
> >>+
> >>+	ret = ioctl(entity->sd->fd, VIDIOC_QUERYCTRL, &queryctrl);
> >>+	if (ret < 0)
> >>+		return ret;
> >>+
> >>+	media_dbg(media, "Validated control \"%s\" (0x%8.8x) on entity %s\n",
> >>+		  queryctrl.name, queryctrl.id, entity->info.name);
> >>+
> >>+	return 0;
> >>+}
> >>diff --git a/utils/media-ctl/v4l2subdev.h b/utils/media-ctl/v4l2subdev.h
> >>index 011fab1..4dee6b1 100644
> >>--- a/utils/media-ctl/v4l2subdev.h
> >>+++ b/utils/media-ctl/v4l2subdev.h
> >>@@ -26,10 +26,14 @@
> >>
> >> struct media_device;
> >> struct media_entity;
> >>+struct media_device;
> >>
> >> struct v4l2_subdev {
> >> 	int fd;
> >> 	unsigned int fd_owner:1;
> >>+
> >>+	__u32 *v4l2_ctrl_bindings;
> >>+	unsigned int num_v4l2_ctrl_bindings;
> >> };
> >>
> >> /**
> >>@@ -314,4 +318,19 @@ enum v4l2_field v4l2_subdev_string_to_field(const char *string);
> >> const enum v4l2_mbus_pixelcode *v4l2_subdev_pixelcode_list(
> >> 	unsigned int *length);
> >>
> >>+/**
> >>+ * @brief Check if sub-device supports given v4l2 control
> >>+ * @param media - media device.
> >>+ * @param entity - media entity.
> >>+ * @param ctrl_id - id of the v4l2 control to check.
> >>+ *
> >>+ * Verify if the sub-device associated with given media entity
> >>+ * supports v4l2-control with given ctrl_id.
> >>+ *
> >>+ * @return 1 if the control is supported, 0 otherwise.
> >>+ */
> >>+int v4l2_subdev_supports_v4l2_ctrl(struct media_device *device,
> >>+				   struct media_entity *entity,
> >>+				   __u32 ctrl_id);
> >>+
> >> #endif
> >

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers
  2016-11-24 12:17   ` Sakari Ailus
  2016-11-24 13:50     ` Jacek Anaszewski
@ 2016-12-08 22:04     ` Jacek Anaszewski
  2016-12-08 23:05       ` Sakari Ailus
  1 sibling, 1 reply; 35+ messages in thread
From: Jacek Anaszewski @ 2016-12-08 22:04 UTC (permalink / raw)
  To: Sakari Ailus, Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki

Hi Sakari,

On 11/24/2016 01:17 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> Thanks for the patchset.
>
> On Wed, Oct 12, 2016 at 04:35:19PM +0200, Jacek Anaszewski wrote:
>> Add helper functions that allow for easy instantiation of media_device
>> object basing on whether the media device contains v4l2 subdev with
>> given file descriptor.
>
> Doesn't this work with video nodes as well? That's what you seem to be using
> it for later on. And I think that's actually more useful.
>
> The existing implementation uses udev to look up devices. Could you use
> libudev device enumeration API to find the media devices, and fall back to
> sysfs if udev doesn't work? There seems to be a reasonable-looking example
> here:
>
> <URL:http://stackoverflow.com/questions/25361042/how-to-list-usb-mass-storage-devices-programatically-using-libudev-in-linux>

Actually I am calling media_get_devname_udev() at first and falling back
to sysfs similarly as it is accomplished in media_enum_entities().
Is there any specific reason for which I should use libudev device
enumeration API in media_device_new_by_subdev_fd()?

Best regards,
Jacek Anaszewski

>>
>> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
>> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>  utils/media-ctl/libmediactl.c | 131 +++++++++++++++++++++++++++++++++++++++++-
>>  utils/media-ctl/mediactl.h    |  27 +++++++++
>>  2 files changed, 156 insertions(+), 2 deletions(-)
>>
>> diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c
>> index 155b65f..d347a40 100644
>> --- a/utils/media-ctl/libmediactl.c
>> +++ b/utils/media-ctl/libmediactl.c
>> @@ -27,6 +27,7 @@
>>  #include <sys/sysmacros.h>
>>
>>  #include <ctype.h>
>> +#include <dirent.h>
>>  #include <errno.h>
>>  #include <fcntl.h>
>>  #include <stdbool.h>
>> @@ -440,8 +441,9 @@ static int media_get_devname_udev(struct udev *udev,
>>  		return -EINVAL;
>>
>>  	devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor);
>> -	media_dbg(entity->media, "looking up device: %u:%u\n",
>> -		  major(devnum), minor(devnum));
>> +	if (entity->media)
>> +		media_dbg(entity->media, "looking up device: %u:%u\n",
>> +			  major(devnum), minor(devnum));
>>  	device = udev_device_new_from_devnum(udev, 'c', devnum);
>>  	if (device) {
>>  		p = udev_device_get_devnode(device);
>> @@ -523,6 +525,7 @@ static int media_get_devname_sysfs(struct media_entity *entity)
>>  	return 0;
>>  }
>>
>> +
>
> Unrelated change.
>
>>  static int media_enum_entities(struct media_device *media)
>>  {
>>  	struct media_entity *entity;
>> @@ -707,6 +710,92 @@ struct media_device *media_device_new(const char *devnode)
>>  	return media;
>>  }
>>
>> +struct media_device *media_device_new_by_subdev_fd(int fd, struct media_entity **fd_entity)
>> +{
>> +	char video_devname[32], device_dir_path[256], media_dev_path[256], media_major_minor[10];
>> +	struct media_device *media = NULL;
>> +	struct dirent *entry;
>> +	struct media_entity tmp_entity;
>> +	DIR *device_dir;
>> +	struct udev *udev;
>> +	char *p;
>> +	int ret, i;
>> +
>> +	if (fd_entity == NULL)
>> +		return NULL;
>> +
>> +	ret = media_get_devname_by_fd(fd, video_devname);
>> +	if (ret < 0)
>> +		return NULL;
>> +
>> +	p = strrchr(video_devname, '/');
>> +	if (p == NULL)
>> +		return NULL;
>> +
>> +	ret = media_udev_open(&udev);
>> +	if (ret < 0)
>> +		return NULL;
>> +
>> +	sprintf(device_dir_path, "/sys/class/video4linux/%s/device/", p + 1);
>> +
>> +	device_dir = opendir(device_dir_path);
>> +	if (device_dir == NULL)
>> +		return NULL;
>> +
>> +	while ((entry = readdir(device_dir))) {
>> +		if (strncmp(entry->d_name, "media", 4))
>
> Why 4? And isn't entry->d_name nul-terminated, so you could use strcmp()?
>
>> +			continue;
>> +
>> +		sprintf(media_dev_path, "%s%s/dev", device_dir_path, entry->d_name);
>> +
>> +		fd = open(media_dev_path, O_RDONLY);
>> +		if (fd < 0)
>> +			continue;
>> +
>> +		ret = read(fd, media_major_minor, sizeof(media_major_minor));
>> +		if (ret < 0)
>> +			continue;
>> +
>> +		sscanf(media_major_minor, "%d:%d", &tmp_entity.info.dev.major, &tmp_entity.info.dev.minor);
>
> This would be better split on two lines.
>
>> +
>> +		/* Try to get the device name via udev */
>> +		if (media_get_devname_udev(udev, &tmp_entity)) {
>> +			/* Fall back to get the device name via sysfs */
>> +			if (media_get_devname_sysfs(&tmp_entity))
>> +				continue;
>> +		}
>> +
>> +		media = media_device_new(tmp_entity.devname);
>> +		if (media == NULL)
>> +			continue;
>> +
>> +		ret = media_device_enumerate(media);
>> +		if (ret < 0) {
>> +			media_dbg(media, "Failed to enumerate %s (%d)\n",
>> +				  tmp_entity.devname, ret);
>> +			media_device_unref(media);
>> +			media = NULL;
>> +			continue;
>> +		}
>> +
>> +		/* Get the entity associated with given fd */
>> +		for (i = 0; i < media->entities_count; i++) {
>> +			struct media_entity *entity = &media->entities[i];
>> +
>> +			if (!strcmp(entity->devname, video_devname)) {
>> +				*fd_entity = &media->entities[i];
>> +				break;
>> +			}
>> +		}
>
> What if you exit the loop without finding the entity you were looking for?
>
>> +
>> +		break;
>> +	}
>> +
>> +	media_udev_close(udev);
>> +
>> +	return media;
>> +}
>> +
>>  struct media_device *media_device_new_emulated(struct media_device_info *info)
>>  {
>>  	struct media_device *media;
>> @@ -748,6 +837,44 @@ void media_device_unref(struct media_device *media)
>>  	free(media);
>>  }
>>
>> +int media_get_devname_by_fd(int fd, char *node_name)
>> +{
>> +	struct udev *udev;
>> +	struct media_entity tmp_entity;
>> +	struct stat stat;
>> +	int ret, ret_udev;
>> +
>> +	if (node_name == NULL)
>> +		return -EINVAL;
>> +
>> +	ret = fstat(fd, &stat);
>> +	if (ret < 0)
>> +		return -errno;
>> +
>> +	tmp_entity.info.v4l.major = MAJOR(stat.st_rdev);
>> +	tmp_entity.info.v4l.minor = MINOR(stat.st_rdev);
>> +
>> +	ret_udev = media_udev_open(&udev);
>> +	if (ret_udev < 0)
>> +		printf("Can't get udev context\n");
>> +
>> +	/* Try to get the device name via udev */
>> +	ret = media_get_devname_udev(udev, &tmp_entity);
>> +	if (!ret)
>> +		goto out;
>> +
>> +	ret = media_get_devname_sysfs(&tmp_entity);
>> +	if (ret < 0)
>> +		goto err_get_devname;
>> +
>> +out:
>> +	strncpy(node_name, tmp_entity.devname, sizeof(tmp_entity.devname));
>
> This seems risky. How does the caller know the maximum size? I'd simply
> allocate the string, and document the caller is responsible for releasing
> it.
>
>> +err_get_devname:
>> +	if (!ret_udev)
>> +		media_udev_close(udev);
>> +	return ret;
>> +}
>> +
>>  int media_device_add_entity(struct media_device *media,
>>  			    const struct media_entity_desc *desc,
>>  			    const char *devnode)
>> diff --git a/utils/media-ctl/mediactl.h b/utils/media-ctl/mediactl.h
>> index b1f33cd..580a25a 100644
>> --- a/utils/media-ctl/mediactl.h
>> +++ b/utils/media-ctl/mediactl.h
>> @@ -76,6 +76,21 @@ struct media_device *media_device_new(const char *devnode);
>>  struct media_device *media_device_new_emulated(struct media_device_info *info);
>>
>>  /**
>> + * @brief Create a new media device contatning entity associated with v4l2 subdev fd.
>> + * @param fd - file descriptor of a v4l2 subdev.
>> + * @param fd_entity - media entity associated with the v4l2 subdev.
>> + *
>> + * Create a representation of the media device referenced by the v4l2-subdev.
>> + * The media device instance is initialized with enumerated entities and links.
>> + *
>> + * Media devices are reference-counted, see media_device_ref() and
>> + * media_device_unref() for more information.
>> + *
>> + * @return A pointer to the new media device or NULL if error occurred.
>> + */
>> +struct media_device *media_device_new_by_subdev_fd(int fd, struct media_entity **fd_entity);
>
> I'd drop the "subdev_" part as both V4L2 device nodes and sub-devices work.
>
> If you wish to keep this V4L2 specific, I suggest ...by_v4l2_fd().
>
>> +
>> +/**
>>   * @brief Take a reference to the device.
>>   * @param media - device instance.
>>   *
>> @@ -231,6 +246,18 @@ const struct media_link *media_entity_get_link(struct media_entity *entity,
>>  const char *media_entity_get_devname(struct media_entity *entity);
>>
>>  /**
>> + * @brief Get the device node name by its file descriptor
>> + * @param fd - file descriptor of a device.
>> + * @param node_name - output device node name string.
>> + *
>> + * This function returns the full path and name to the device node corresponding
>> + * to the given file descriptor.
>> + *
>> + * @return 0 on success, or a negative error code on failure.
>> + */
>> +int media_get_devname_by_fd(int fd, char *node_name);
>> +
>> +/**
>>   * @brief Get the type of an entity.
>>   * @param entity - the entity.
>>   *
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers
  2016-12-08 22:04     ` Jacek Anaszewski
@ 2016-12-08 23:05       ` Sakari Ailus
  2016-12-09  7:43         ` Jacek Anaszewski
  0 siblings, 1 reply; 35+ messages in thread
From: Sakari Ailus @ 2016-12-08 23:05 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Jacek Anaszewski, linux-media, sakari.ailus, hverkuil, mchehab,
	m.szyprowski, s.nawrocki

Hi Jacek,

On Thu, Dec 08, 2016 at 11:04:20PM +0100, Jacek Anaszewski wrote:
> Hi Sakari,
> 
> On 11/24/2016 01:17 PM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >Thanks for the patchset.
> >
> >On Wed, Oct 12, 2016 at 04:35:19PM +0200, Jacek Anaszewski wrote:
> >>Add helper functions that allow for easy instantiation of media_device
> >>object basing on whether the media device contains v4l2 subdev with
> >>given file descriptor.
> >
> >Doesn't this work with video nodes as well? That's what you seem to be using
> >it for later on. And I think that's actually more useful.
> >
> >The existing implementation uses udev to look up devices. Could you use
> >libudev device enumeration API to find the media devices, and fall back to
> >sysfs if udev doesn't work? There seems to be a reasonable-looking example
> >here:
> >
> ><URL:http://stackoverflow.com/questions/25361042/how-to-list-usb-mass-storage-devices-programatically-using-libudev-in-linux>
> 
> Actually I am calling media_get_devname_udev() at first and falling back
> to sysfs similarly as it is accomplished in media_enum_entities().
> Is there any specific reason for which I should use libudev device
> enumeration API in media_device_new_by_subdev_fd()?

Yes. You rely on the API udev provides; the sysfs implementation is just a
fallback in case udev isn't available in the system. I guess it'd mostly
work but, for instance, you assume sysfs is found under /sys. The sysfs
itself isn't one of the most stable APIs either. Udev is a simply better
option when it's there.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers
  2016-12-08 23:05       ` Sakari Ailus
@ 2016-12-09  7:43         ` Jacek Anaszewski
  0 siblings, 0 replies; 35+ messages in thread
From: Jacek Anaszewski @ 2016-12-09  7:43 UTC (permalink / raw)
  To: Sakari Ailus, Jacek Anaszewski
  Cc: linux-media, sakari.ailus, hverkuil, mchehab, m.szyprowski, s.nawrocki

Hhi Sakari,

On 12/09/2016 12:05 AM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Thu, Dec 08, 2016 at 11:04:20PM +0100, Jacek Anaszewski wrote:
>> Hi Sakari,
>>
>> On 11/24/2016 01:17 PM, Sakari Ailus wrote:
>>> Hi Jacek,
>>>
>>> Thanks for the patchset.
>>>
>>> On Wed, Oct 12, 2016 at 04:35:19PM +0200, Jacek Anaszewski wrote:
>>>> Add helper functions that allow for easy instantiation of media_device
>>>> object basing on whether the media device contains v4l2 subdev with
>>>> given file descriptor.
>>>
>>> Doesn't this work with video nodes as well? That's what you seem to be using
>>> it for later on. And I think that's actually more useful.
>>>
>>> The existing implementation uses udev to look up devices. Could you use
>>> libudev device enumeration API to find the media devices, and fall back to
>>> sysfs if udev doesn't work? There seems to be a reasonable-looking example
>>> here:
>>>
>>> <URL:http://stackoverflow.com/questions/25361042/how-to-list-usb-mass-storage-devices-programatically-using-libudev-in-linux>
>>
>> Actually I am calling media_get_devname_udev() at first and falling back
>> to sysfs similarly as it is accomplished in media_enum_entities().
>> Is there any specific reason for which I should use libudev device
>> enumeration API in media_device_new_by_subdev_fd()?
>
> Yes. You rely on the API udev provides; the sysfs implementation is just a
> fallback in case udev isn't available in the system. I guess it'd mostly
> work but, for instance, you assume sysfs is found under /sys. The sysfs
> itself isn't one of the most stable APIs either. Udev is a simply better
> option when it's there.

Thanks for clarifying that. I'll check the libudev device enumeration
API then.

-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2016-12-09  7:44 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-12 14:35 [PATCH v4l-utils v7 0/7] Add a plugin for Exynos4 camera Jacek Anaszewski
2016-10-12 14:35 ` [PATCH v4l-utils v7 1/7] mediactl: Add support for v4l2-ctrl-binding config Jacek Anaszewski
2016-11-24 14:23   ` Sakari Ailus
2016-11-28 21:32     ` Jacek Anaszewski
2016-11-29  6:37       ` Sakari Ailus
2016-10-12 14:35 ` [PATCH v4l-utils v7 2/7] mediatext: Add library Jacek Anaszewski
2016-11-24 13:01   ` Sakari Ailus
2016-11-24 14:04     ` Jacek Anaszewski
2016-10-12 14:35 ` [PATCH v4l-utils v7 3/7] mediactl: Add media_entity_get_backlinks() Jacek Anaszewski
2016-11-24 12:40   ` Sakari Ailus
2016-11-24 14:00     ` Jacek Anaszewski
2016-11-24 14:26       ` Sakari Ailus
2016-10-12 14:35 ` [PATCH v4l-utils v7 4/7] mediactl: Add media_device creation helpers Jacek Anaszewski
2016-11-24 12:17   ` Sakari Ailus
2016-11-24 13:50     ` Jacek Anaszewski
2016-11-24 14:32       ` Sakari Ailus
2016-11-24 15:39         ` Jacek Anaszewski
2016-12-08 22:04     ` Jacek Anaszewski
2016-12-08 23:05       ` Sakari Ailus
2016-12-09  7:43         ` Jacek Anaszewski
2016-10-12 14:35 ` [PATCH v4l-utils v7 5/7] mediactl: libv4l2subdev: Add colorspace logging Jacek Anaszewski
2016-11-24 12:28   ` Sakari Ailus
2016-10-12 14:35 ` [PATCH v4l-utils v7 6/7] mediactl: libv4l2subdev: add support for comparing mbus formats Jacek Anaszewski
2016-11-24 14:36   ` Sakari Ailus
2016-11-24 15:49     ` Jacek Anaszewski
2016-10-12 14:35 ` [PATCH v4l-utils v7 7/7] Add a libv4l plugin for Exynos4 camera Jacek Anaszewski
2016-11-24 15:11   ` Sakari Ailus
2016-11-24 16:14     ` Jacek Anaszewski
2016-11-24 23:55       ` Sakari Ailus
2016-11-03 11:51 ` [PATCH v4l-utils v7 0/7] Add a " Hans Verkuil
2016-11-03 12:13   ` Jacek Anaszewski
2016-11-23 21:51     ` Mauro Carvalho Chehab
2016-11-24  8:10       ` Jacek Anaszewski
2016-11-24  9:10         ` Mauro Carvalho Chehab
2016-11-24 12:20           ` 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.