Re: [RFCv3 01/17] media: add request API core and UAPI

From: Sakari Ailus
Date: Wed Feb 07 2018 - 06:25:42 EST


Hi Alexandre,

On Wed, Feb 07, 2018 at 10:48:05AM +0900, Alexandre Courbot wrote:
> The request API provides a way to group buffers and device parameters
> into units of work to be queued and executed. This patch introduces the
> UAPI and core framework.
>
> This patch is based on the previous work by Laurent Pinchart. The core
> has changed considerably, but the UAPI is mostly untouched.

Thanks for the rebase.

What's the purpose of the split between media-request.c and
media-request-mgr.c? The use of ops for the request manager suggests it
could be replaced by an alternative implementation but then again the media
request is almost entirely initialised in the media-request-mgr. Either
could make some sense but not both --- I'd simply move the code from the
media-request-mgr to the functions calling them in media-request.c. That
should make this easier to read, too.

A few more comments below.

>
> Signed-off-by: Alexandre Courbot <acourbot@xxxxxxxxxxxx>
> ---
> drivers/media/Makefile | 3 +-
> drivers/media/media-device.c | 7 +
> drivers/media/media-request-mgr.c | 105 ++++++++++++
> drivers/media/media-request.c | 311 +++++++++++++++++++++++++++++++++++
> drivers/media/v4l2-core/v4l2-ioctl.c | 2 +-
> include/media/media-device.h | 3 +
> include/media/media-entity.h | 9 +
> include/media/media-request-mgr.h | 73 ++++++++
> include/media/media-request.h | 186 +++++++++++++++++++++
> include/uapi/linux/media.h | 10 ++
> 10 files changed, 707 insertions(+), 2 deletions(-)
> create mode 100644 drivers/media/media-request-mgr.c
> create mode 100644 drivers/media/media-request.c
> create mode 100644 include/media/media-request-mgr.h
> create mode 100644 include/media/media-request.h
>
> diff --git a/drivers/media/Makefile b/drivers/media/Makefile
> index 594b462ddf0e..06c43ddb52ea 100644
> --- a/drivers/media/Makefile
> +++ b/drivers/media/Makefile
> @@ -3,7 +3,8 @@
> # Makefile for the kernel multimedia device drivers.
> #
>
> -media-objs := media-device.o media-devnode.o media-entity.o
> +media-objs := media-device.o media-devnode.o media-entity.o \
> + media-request.o media-request-mgr.o
>
> #
> # I2C drivers should come before other drivers, otherwise they'll fail
> diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
> index e79f72b8b858..024ee81a8334 100644
> --- a/drivers/media/media-device.c
> +++ b/drivers/media/media-device.c
> @@ -32,6 +32,8 @@
> #include <media/media-device.h>
> #include <media/media-devnode.h>
> #include <media/media-entity.h>
> +#include <media/media-request.h>
> +#include <media/media-request-mgr.h>
>
> #ifdef CONFIG_MEDIA_CONTROLLER
>
> @@ -407,6 +409,7 @@ static const struct media_ioctl_info ioctl_info[] = {
> MEDIA_IOC(ENUM_LINKS, media_device_enum_links, MEDIA_IOC_FL_GRAPH_MUTEX),
> MEDIA_IOC(SETUP_LINK, media_device_setup_link, MEDIA_IOC_FL_GRAPH_MUTEX),
> MEDIA_IOC(G_TOPOLOGY, media_device_get_topology, MEDIA_IOC_FL_GRAPH_MUTEX),
> + MEDIA_IOC(REQUEST_CMD, media_device_request_cmd, 0),
> };
>
> static long media_device_ioctl(struct file *filp, unsigned int cmd,
> @@ -688,6 +691,10 @@ EXPORT_SYMBOL_GPL(media_device_init);
>
> void media_device_cleanup(struct media_device *mdev)
> {
> + if (mdev->req_mgr) {
> + media_request_mgr_free(mdev->req_mgr);
> + mdev->req_mgr = NULL;
> + }
> ida_destroy(&mdev->entity_internal_idx);
> mdev->entity_internal_idx_max = 0;
> media_graph_walk_cleanup(&mdev->pm_count_walk);
> diff --git a/drivers/media/media-request-mgr.c b/drivers/media/media-request-mgr.c
> new file mode 100644
> index 000000000000..686e877a884b
> --- /dev/null
> +++ b/drivers/media/media-request-mgr.c
> @@ -0,0 +1,105 @@
> +/*
> + * Generic request manager implementation.
> + *
> + * Copyright (C) 2018, The Chromium OS Authors. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/slab.h>
> +
> +#include <media/media-device.h>
> +#include <media/media-request.h>
> +#include <media/media-request-mgr.h>
> +
> +static struct media_request *
> +media_request_alloc(struct media_request_mgr *mgr)
> +{
> + struct media_request *req;
> +
> + req = kzalloc(sizeof(*req), GFP_KERNEL);
> + if (!req)
> + return ERR_PTR(-ENOMEM);
> +
> + req->mgr = mgr;
> + req->state = MEDIA_REQUEST_STATE_IDLE;
> + kref_init(&req->kref);
> + INIT_LIST_HEAD(&req->data);
> + init_waitqueue_head(&req->complete_wait);
> + ATOMIC_INIT_NOTIFIER_HEAD(&req->submit_notif);
> + mutex_init(&req->lock);
> +
> + mutex_lock(&mgr->mutex);
> + req->id = ++mgr->req_id;
> + list_add_tail(&req->list, &mgr->requests);
> + mutex_unlock(&mgr->mutex);
> +
> + return req;
> +}
> +
> +static void media_request_free(struct media_request *req)
> +{
> + struct media_request_mgr *mgr = req->mgr;
> + struct media_request_entity_data *data, *next;
> +
> + mutex_lock(&mgr->mutex);
> + list_del(&req->list);
> + mutex_unlock(&mgr->mutex);
> +
> + list_for_each_entry_safe(data, next, &req->data, list) {
> + list_del(&data->list);
> + data->entity->req_ops->data_free(data);
> + }
> +
> + kfree(req);
> +}
> +
> +void media_request_mgr_free(struct media_request_mgr *mgr)
> +{
> + struct media_device *mdev = mgr->mdev;
> +
> + /* Just a sanity check - we should have no remaining requests */
> + while (!list_empty(&mgr->requests)) {
> + struct media_request *req;
> +
> + req = list_first_entry(&mgr->requests, typeof(*req), list);
> + dev_warn(mdev->dev,
> + "%s: request %u still referenced, deleting forcibly!\n",
> + __func__, req->id);
> + mgr->ops->req_free(req);
> + }
> +
> + kfree(mgr);
> +}
> +EXPORT_SYMBOL_GPL(media_request_mgr_free);
> +
> +static const struct media_request_mgr_ops request_mgr_generic_ops = {
> + .req_alloc = media_request_alloc,
> + .req_free = media_request_free,
> +};
> +
> +struct media_request_mgr *
> +media_request_mgr_alloc(struct media_device *mdev)
> +{
> + struct media_request_mgr *mgr;
> +
> + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
> + if (!mgr)
> + return ERR_PTR(-ENOMEM);
> +
> + mgr->mdev = mdev;
> + mutex_init(&mgr->mutex);
> + INIT_LIST_HEAD(&mgr->requests);
> + mgr->ops = &request_mgr_generic_ops;
> +
> + return mgr;
> +}
> +EXPORT_SYMBOL_GPL(media_request_mgr_alloc);
> diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
> new file mode 100644
> index 000000000000..30a23235b019
> --- /dev/null
> +++ b/drivers/media/media-request.c
> @@ -0,0 +1,311 @@
> +/*
> + * Request base implementation
> + *
> + * Copyright (C) 2018, The Chromium OS Authors. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/anon_inodes.h>
> +#include <linux/media.h>
> +#include <linux/fs.h>
> +#include <linux/file.h>
> +#include <linux/slab.h>
> +#include <linux/list.h>
> +
> +#include <media/media-device.h>
> +#include <media/media-request.h>
> +#include <media/media-request-mgr.h>
> +
> +const struct file_operations request_fops;
> +
> +struct media_request *media_request_get(struct media_request *req)
> +{
> + kref_get(&req->kref);
> + return req;
> +}
> +EXPORT_SYMBOL_GPL(media_request_get);
> +
> +struct media_request *
> +media_request_get_from_fd(int fd)
> +{
> + struct file *f;
> + struct media_request *req;
> +
> + f = fget(fd);
> + if (!f)
> + return NULL;
> +
> + /* Not a request FD? */
> + if (f->f_op != &request_fops) {
> + fput(f);
> + return NULL;
> + }
> +
> + req = f->private_data;
> + media_request_get(req);
> + fput(f);
> +
> + return req;
> +}
> +EXPORT_SYMBOL_GPL(media_request_get_from_fd);
> +
> +static void media_request_release(struct kref *kref)
> +{
> + struct media_request *req =
> + container_of(kref, typeof(*req), kref);
> +
> + req->mgr->ops->req_free(req);
> +}
> +
> +void media_request_put(struct media_request *req)
> +{
> + if (WARN_ON(req == NULL))
> + return;
> +
> + kref_put(&req->kref, media_request_release);
> +}
> +EXPORT_SYMBOL_GPL(media_request_put);
> +
> +struct media_request_entity_data *
> +media_request_get_entity_data(struct media_request *req,
> + struct media_entity *entity, void *fh)
> +{
> + struct media_request_entity_data *data;
> +
> + mutex_lock(&req->lock);
> +
> + /* First look whether we already have entity data */
> + list_for_each_entry(data, &req->data, list) {
> + if (data->entity == entity) {
> + /*
> + * If so, then the fh must match otherwise this means
> + * we are trying to set the same entity through
> + * different handles
> + */
> + if (data->fh != fh)
> + data = ERR_PTR(-EINVAL);
> + goto done;
> + }
> + }
> +
> + /* No entity data found, let's create it */
> + data = entity->req_ops->data_alloc(entity, fh);
> + if (IS_ERR(data))
> + goto done;
> + data->fh = fh;
> + data->entity = entity;
> + list_add_tail(&data->list, &req->data);
> +
> +done:
> + mutex_unlock(&req->lock);
> + return data;
> +}
> +EXPORT_SYMBOL_GPL(media_request_get_entity_data);
> +
> +static const char * const media_request_states[] __maybe_unused = {
> + "IDLE",
> + "SUBMITTED",
> + "COMPLETED",
> + "DELETED",
> +};
> +
> +static const char *media_request_state(enum media_request_state state)
> +{
> + return state < ARRAY_SIZE(media_request_states) ?
> + media_request_states[state] : "INVALID";
> +}
> +
> +static int media_device_request_close(struct inode *inode, struct file *filp)
> +{
> + struct media_request *req = filp->private_data;
> +
> + if (req == NULL)
> + return 0;
> +
> + media_request_put(req);
> +
> + return 0;
> +}
> +
> +static unsigned int media_request_poll(struct file *file, poll_table *wait)
> +{
> + struct media_request *req = file->private_data;
> +
> + poll_wait(file, &req->complete_wait, wait);
> +
> + if (req->state == MEDIA_REQUEST_STATE_COMPLETED)
> + return POLLIN | POLLRDNORM;
> +
> + return 0;
> +}
> +
> +const struct file_operations request_fops = {
> + .owner = THIS_MODULE,
> + .poll = media_request_poll,
> + .release = media_device_request_close,
> +};
> +
> +void media_request_complete(struct media_request *req)
> +{
> + struct media_request_mgr *mgr = req->mgr;
> + struct media_device *mdev = mgr->mdev;
> +
> + mutex_lock(&req->lock);
> +
> + if (WARN_ON(req->state != MEDIA_REQUEST_STATE_SUBMITTED)) {
> + dev_dbg(mdev->dev, "%s: can't complete %u, state %s\n",
> + __func__, req->id, media_request_state(req->state));
> + mutex_unlock(&req->lock);
> + return;
> + }
> +
> + req->state = MEDIA_REQUEST_STATE_COMPLETED;
> +
> + if (mgr->ops->req_complete)
> + mgr->ops->req_complete(req);
> +
> + wake_up_interruptible(&req->complete_wait);
> +
> + mutex_unlock(&req->lock);
> +
> + /* Release the reference acquired when we submitted the request */
> + media_request_put(req);
> +}
> +EXPORT_SYMBOL_GPL(media_request_complete);
> +
> +/*
> + * Process the MEDIA_REQ_CMD_ALLOC command
> + */
> +static int media_request_cmd_alloc(struct media_request_mgr *mgr,
> + struct media_request_cmd *cmd)
> +{
> + struct media_request *req;
> + int fd;
> +
> + req = mgr->ops->req_alloc(mgr);
> + if (!req)
> + return -ENOMEM;
> +
> + fd = anon_inode_getfd("media_request", &request_fops, req, O_CLOEXEC);
> + if (fd < 0)
> + return fd;
> +
> + cmd->fd = fd;
> +
> + return 0;
> +}
> +
> +/*
> + * Process the MEDIA_REQ_CMD_SUBMIT command
> + */
> +static int media_request_cmd_submit(struct media_request *req)
> +{
> + mutex_lock(&req->lock);
> +
> + if (req->state != MEDIA_REQUEST_STATE_IDLE) {
> + dev_dbg(req->mgr->mdev->dev,
> + "%s: unable to submit request in state %s\n",
> + __func__, media_request_state(req->state));
> + mutex_unlock(&req->lock);
> + return -EINVAL;
> + }
> +
> + if (atomic_read(&req->buf_cpt) == 0) {
> + dev_dbg(req->mgr->mdev->dev,
> + "%s: request has no buffers!\n", __func__);
> + mutex_unlock(&req->lock);
> + return -EINVAL;
> + }
> +
> + req->state = MEDIA_REQUEST_STATE_SUBMITTED;
> +
> + /* Hold a reference to the request until it is completed */
> + media_request_get(req);
> +
> + mutex_unlock(&req->lock);
> +
> + atomic_notifier_call_chain(&req->submit_notif, req->state, req);
> +
> + return 0;
> +}
> +
> +static int media_request_cmd_reinit(struct media_request *req)
> +{
> + struct media_request_entity_data *data, *next;
> +
> + mutex_lock(&req->lock);
> +
> + if (req->state == MEDIA_REQUEST_STATE_SUBMITTED) {
> + dev_dbg(req->mgr->mdev->dev,
> + "%s: unable to reinit submitted request\n", __func__);
> + mutex_unlock(&req->lock);
> + return -EINVAL;
> + }
> +
> + /* delete all entity data */
> + list_for_each_entry_safe(data, next, &req->data, list) {
> + list_del(&data->list);
> + data->entity->req_ops->data_free(data);
> + }
> +
> + /* reinitialize request to idle state */
> + req->state = MEDIA_REQUEST_STATE_IDLE;
> + atomic_set(&req->buf_cpt, 0);
> +
> + mutex_unlock(&req->lock);
> +
> + return 0;
> +}
> +
> +long media_device_request_cmd(struct media_device *mdev,
> + struct media_request_cmd *cmd)
> +{
> + struct media_request *req = NULL;
> + int ret;
> +
> + if (!mdev->req_mgr)
> + return -ENOTTY;
> +
> + if (cmd->cmd != MEDIA_REQ_CMD_ALLOC) {
> + req = media_request_get_from_fd(cmd->fd);
> + if (IS_ERR(req))
> + return PTR_ERR(req);
> +
> + /* requests must belong to this media device's manager */
> + if (req->mgr != mdev->req_mgr) {
> + media_request_put(req);
> + return -EINVAL;
> + }
> + }
> +
> + switch (cmd->cmd) {
> + case MEDIA_REQ_CMD_ALLOC:
> + ret = media_request_cmd_alloc(mdev->req_mgr, cmd);
> + break;
> +
> + case MEDIA_REQ_CMD_SUBMIT:
> + ret = media_request_cmd_submit(req);
> + break;
> +
> + case MEDIA_REQ_CMD_REINIT:
> + ret = media_request_cmd_reinit(req);
> + break;
> +
> + default:
> + ret = -EINVAL;
> + break;
> + }
> +
> + if (req)
> + media_request_put(req);
> +
> + return ret;
> +}
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 260288ca4f55..e5109e5b8bf5 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -870,7 +870,7 @@ static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
> __u32 i;
>
> /* zero the reserved fields */
> - c->reserved[0] = c->reserved[1] = 0;
> + c->reserved[0] = 0;
> for (i = 0; i < c->count; i++)
> c->controls[i].reserved2[0] = 0;
>
> diff --git a/include/media/media-device.h b/include/media/media-device.h
> index bcc6ec434f1f..f2d471b8a53f 100644
> --- a/include/media/media-device.h
> +++ b/include/media/media-device.h
> @@ -27,6 +27,7 @@
>
> struct ida;
> struct device;
> +struct media_request_mgr;
>
> /**
> * struct media_entity_notify - Media Entity Notify
> @@ -158,6 +159,8 @@ struct media_device {
> void (*disable_source)(struct media_entity *entity);
>
> const struct media_device_ops *ops;
> +
> + struct media_request_mgr *req_mgr;
> };
>
> /* We don't need to include pci.h or usb.h here */
> diff --git a/include/media/media-entity.h b/include/media/media-entity.h
> index a732af1dbba0..39f5e88e2b23 100644
> --- a/include/media/media-entity.h
> +++ b/include/media/media-entity.h
> @@ -172,6 +172,8 @@ struct media_pad {
> unsigned long flags;
> };
>
> +struct media_request;
> +struct v4l2_ext_controls;
> /**
> * struct media_entity_operations - Media entity operations
> * @get_fwnode_pad: Return the pad number based on a fwnode endpoint or
> @@ -197,6 +199,11 @@ struct media_entity_operations {
> int (*link_validate)(struct media_link *link);
> };
>
> +struct media_entity_request_ops {
> + struct media_request_entity_data *(*data_alloc)(struct media_entity *entity, void *fh);
> + void (*data_free)(struct media_request_entity_data *data);
> +};
> +
> /**
> * enum media_entity_type - Media entity type
> *
> @@ -281,6 +288,8 @@ struct media_entity {
>
> const struct media_entity_operations *ops;
>
> + const struct media_entity_request_ops *req_ops;
> +
> int stream_count;
> int use_count;
>
> diff --git a/include/media/media-request-mgr.h b/include/media/media-request-mgr.h
> new file mode 100644
> index 000000000000..a3161fa2add0
> --- /dev/null
> +++ b/include/media/media-request-mgr.h
> @@ -0,0 +1,73 @@
> +/*
> + * Generic request manager.
> + *
> + * Copyright (C) 2018, The Chromium OS Authors. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _MEDIA_REQUEST_MGR_H
> +#define _MEDIA_REQUEST_MGR_H
> +
> +#include <linux/mutex.h>
> +
> +struct media_request_mgr;
> +
> +/**
> + * struct media_request_mgr_ops - request manager operations
> + *
> + * @req_alloc: allocate a request
> + * @req_free: free a previously allocated request
> + * @req_complete: callback invoked when a request has completed
> + *
> + */
> +struct media_request_mgr_ops {
> + struct media_request *(*req_alloc)(struct media_request_mgr *mgr);
> + void (*req_free)(struct media_request *req);
> + void (*req_complete)(struct media_request *req);
> +};
> +
> +/**
> + * struct media_request_mgr - requests manager
> + *
> + * @mdev: media_device owning this manager
> + * @ops: implementation of the manager
> + * @mutex: protects requests, active_request, req_id, and all members of
> + * struct media_request

The media request has a mutex as well. Is this outdated?

> + * @requests: list of alive requests produced by this manager
> + * @req_id: counter used to identify requests for debugging purposes
> + */
> +struct media_request_mgr {
> + struct media_device *mdev;
> + const struct media_request_mgr_ops *ops;
> +
> + struct mutex mutex;
> + struct list_head requests;
> + u32 req_id;
> +};
> +
> +/**
> + * media_request_mgr_alloc() - return an instance of the generic manager
> + *
> + * @mdev: owning media device
> + */
> +struct media_request_mgr *
> +media_request_mgr_alloc(struct media_device *mdev);
> +
> +/**
> + * media_request_mgr_free() - free a media manager
> + *
> + * This should only be called when all requests produced by this manager
> + * has been destroyed. Will warn if that is not the case.
> + *
> + */
> +void media_request_mgr_free(struct media_request_mgr *mgr);
> +
> +#endif
> diff --git a/include/media/media-request.h b/include/media/media-request.h
> new file mode 100644
> index 000000000000..817df13ef6e3
> --- /dev/null
> +++ b/include/media/media-request.h
> @@ -0,0 +1,186 @@
> +/*
> + * Media requests.
> + *
> + * Copyright (C) 2018, The Chromium OS Authors. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _MEDIA_REQUEST_H
> +#define _MEDIA_REQUEST_H
> +
> +#include <linux/kref.h>
> +#include <linux/list.h>
> +#include <linux/notifier.h>
> +#include <linux/wait.h>
> +
> +struct media_device;
> +struct media_entity;
> +struct media_request_cmd;
> +struct media_request_entity_data;
> +struct media_request_mgr;
> +
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +
> +enum media_request_state {
> + MEDIA_REQUEST_STATE_IDLE,
> + MEDIA_REQUEST_STATE_SUBMITTED,
> + MEDIA_REQUEST_STATE_COMPLETED,
> + MEDIA_REQUEST_STATE_DELETED,
> +};
> +
> +/**
> + * struct media_request - Media request base structure
> + * @id: request id, used internally for debugging
> + * @mgr: manager this request belongs to
> + * @kref: reference count
> + * @list: list entry in the media device requests list
> + * @lock: protects internal state against concurrent accesses
> + * @state: current state of the request
> + * @data: per-entity data list
> + * @complete_wait: wait queue that signals once the request has completed
> + * @submit_notif: notification list to call when the request is submitted
> + * @buf_cpt: counter of queued/completed buffers used to decide when the
> + * request is completed
> + */
> +struct media_request {
> + u32 id;
> + struct media_request_mgr *mgr;
> + struct kref kref;
> + struct list_head list;
> +
> + struct mutex lock;
> + enum media_request_state state;
> + struct list_head data;
> + wait_queue_head_t complete_wait;
> + struct atomic_notifier_head submit_notif;
> + atomic_t buf_cpt;
> +};
> +
> +/**
> + * media_request_get() - increment the reference counter of a request
> + *
> + * The calling context must call media_request_put() once it does not need
> + * the reference to the request anymore.
> + *
> + * Returns the request that has been passed as argument.
> + *
> + * @req: request to acquire a reference of
> + */
> +struct media_request *media_request_get(struct media_request *req);
> +
> +/**
> + * media_request_get_from_fd() - lookup request by fd and acquire a reference.
> + *
> + * Look a request up from its fd, acquire a reference and return a pointer to
> + * the request. As for media_request_get(), media_request_put() must be called
> + * once the reference is not used anymore.
> + *
> + * @req: request to lookup and acquire.
> + *
> + */
> +struct media_request *media_request_get_from_fd(int fd);
> +
> +/**
> + * media_request_put() - decrement the reference counter of a request
> + *
> + * Mirror function of media_request_get() and media_request_get_from_fd(). Will
> + * free the request if this was the last valid reference.
> + *
> + * @req: request to release.
> + */
> +void media_request_put(struct media_request *req);
> +
> +/**
> + * media_request_get_entity_data() - get per-entity data for a request
> + * @req: request to get entity data from
> + * @entity: entity to get data of
> + * @fh: cookie identifying the handle from which the entity is accessed
> + *
> + * Search and return the entity data associated associated to the request. If no
> + * such data exists, it is allocated as per the entity operations.
> + *
> + * The fh arguments serves as a cookie to make sure the same entity is not
> + * accessed through different opened file handles. The same handle must be
> + * passed to all calls of this function for the same entity. Failure to do so
> + * will return an error.
> + *
> + * Returns the per-entity data, or an error code if a problem happened. -EINVAL
> + * means that data for the entity already existed, but has been allocated under
> + * a different cookie.
> + */
> +struct media_request_entity_data *
> +media_request_get_entity_data(struct media_request *req,
> + struct media_entity *entity, void *fh);
> +
> +
> +/**
> + * media_request_complete() - to be invoked when the request is complete
> + *
> + * @req: request which has completed
> + */
> +void media_request_complete(struct media_request *req);
> +
> +/**
> + * struct media_request_entity_data - per-entity request data
> + *
> + * Base structure used to store request state data. To be extended by actual
> + * implementation.
> + *
> + * @entity: entity this data belongs to
> + * @fh: subsystem-dependent. For V4L2, the v4l2_fh of the opened device
> + * @list: entry in media_request::data
> + * @applied: whether this request has already been applied for this entity
> + */
> +struct media_request_entity_data {
> + struct media_entity *entity;
> + void *fh;
> + struct list_head list;
> + bool applied;
> +};
> +
> +/**
> + * media_device_request_cmd() - perform the REQUEST_CMD ioctl
> + *
> + * @mdev: media device the ioctl has been called on
> + * @cmd: user-space request command structure
> + */
> +long media_device_request_cmd(struct media_device *mdev,
> + struct media_request_cmd *cmd);
> +
> +#else /* CONFIG_MEDIA_CONTROLLER */
> +
> +static inline media_request_get(struct media_request *req)
> +{
> +}
> +
> +static inline struct media_request *media_request_get_from_fd(int fd)
> +{
> + return ERR_PTR(-ENOSYS);
> +}
> +
> +static inline void media_request_put(struct media_request *req)
> +{
> +}
> +
> +static inline struct media_request *media_request_get_entity_data(
> + struct media_request *req,
> + struct media_entity *entity, void *fh)
> +{
> + return ERR_PTR(-ENOSYS);
> +}
> +
> +static inline void media_request_entity_complete(struct media_request *req)
> +{
> +}
> +
> +#endif /* CONFIG_MEDIA_CONTROLLER */
> +
> +#endif
> diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
> index b9b9446095e9..6e20ac9848b5 100644
> --- a/include/uapi/linux/media.h
> +++ b/include/uapi/linux/media.h
> @@ -406,6 +406,15 @@ struct media_v2_topology {
> __u64 ptr_links;
> } __attribute__ ((packed));
>
> +#define MEDIA_REQ_CMD_ALLOC 0
> +#define MEDIA_REQ_CMD_SUBMIT 1
> +#define MEDIA_REQ_CMD_REINIT 2
> +
> +struct media_request_cmd {
> + __u32 cmd;
> + __s32 fd;
> +} __attribute__ ((packed));
> +
> /* ioctls */
>
> #define MEDIA_IOC_DEVICE_INFO _IOWR('|', 0x00, struct media_device_info)
> @@ -413,5 +422,6 @@ struct media_v2_topology {
> #define MEDIA_IOC_ENUM_LINKS _IOWR('|', 0x02, struct media_links_enum)
> #define MEDIA_IOC_SETUP_LINK _IOWR('|', 0x03, struct media_link_desc)
> #define MEDIA_IOC_G_TOPOLOGY _IOWR('|', 0x04, struct media_v2_topology)
> +#define MEDIA_IOC_REQUEST_CMD _IOWR('|', 0x05, struct media_request_cmd)
>
> #endif /* __LINUX_MEDIA_H */
> --
> 2.16.0.rc1.238.g530d649a79-goog
>

--
Sakari Ailus
sakari.ailus@xxxxxxxxxxxxxxx