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