clear-pkgs-linux-iot-lts2018/0045-media-Add-request-API....

898 lines
25 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Meng Wei <wei.meng@intel.com>
Date: Fri, 26 Oct 2018 09:51:54 +0800
Subject: [PATCH] media: Add request API
The request API allows bundling media device parameters with request
objects and applying them atomically, either synchronously or
asynchronously.
Signed-off-by: Chang Ying <ying.chang@intel.com>
Signed-off-by: Meng Wei <wei.meng@intel.com>
---
drivers/media/media-device.c | 502 +++++++++++++++++++++++++++++++++-
drivers/media/media-devnode.c | 4 +
include/media/media-device.h | 67 +++++
include/media/media-devnode.h | 18 +-
include/uapi/linux/media.h | 38 +++
5 files changed, 619 insertions(+), 10 deletions(-)
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index d04ed438a45d..126873f1b0b0 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -26,6 +26,8 @@
#include <linux/pci.h>
#include <linux/usb.h>
#include <linux/version.h>
+#include <linux/atomic.h>
+#include <linux/wait.h>
#include <media/media-device.h>
#include <media/media-devnode.h>
@@ -43,6 +45,356 @@
#define MEDIA_ENT_T_DEVNODE_UNKNOWN (MEDIA_ENT_F_OLD_BASE | \
MEDIA_ENT_SUBTYPE_MASK)
+static char *__request_state[] = {
+ "IDLE",
+ "QUEUED",
+ "DELETED",
+ "COMPLETED",
+};
+
+#define request_state(i) \
+ ((i) < ARRAY_SIZE(__request_state) ? __request_state[i] : "UNKNOWN")
+
+
+struct media_device_fh {
+ struct media_devnode_fh fh;
+ struct list_head requests;
+ struct {
+ struct list_head head;
+ wait_queue_head_t wait;
+ atomic_t sequence;
+ } kevents;
+};
+
+static inline struct media_device_fh *media_device_fh(struct file *filp)
+{
+ return container_of(filp->private_data, struct media_device_fh, fh);
+}
+
+/* -----------------------------------------------------------------------------
+ * Requests
+ */
+
+/**
+ * media_device_request_find - Find a request based from its ID
+ * @mdev: The media device
+ * @reqid: The request ID
+ *
+ * Find and return the request associated with the given ID, or NULL if no such
+ * request exists.
+ *
+ * When the function returns a non-NULL request it increases its reference
+ * count. The caller is responsible for releasing the reference by calling
+ * media_device_request_put() on the request.
+ */
+struct media_device_request *
+media_device_request_find(struct media_device *mdev, u16 reqid)
+{
+ struct media_device_request *req;
+ unsigned long flags;
+ bool found = false;
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ list_for_each_entry(req, &mdev->requests, list) {
+ if (req->id == reqid) {
+ kref_get(&req->kref);
+ found = true;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ if (!found) {
+ dev_dbg(mdev->dev,
+ "request: can't find %u\n", reqid);
+ return NULL;
+ }
+
+ return req;
+}
+EXPORT_SYMBOL_GPL(media_device_request_find);
+
+void media_device_request_get(struct media_device_request *req)
+{
+ kref_get(&req->kref);
+}
+EXPORT_SYMBOL_GPL(media_device_request_get);
+
+static void media_device_request_queue_event(struct media_device *mdev,
+ struct media_device_request *req,
+ struct media_device_fh *fh)
+{
+ struct media_kevent *kev = req->kev;
+ struct media_event *ev = &kev->ev;
+
+ lockdep_assert_held(&mdev->req_lock);
+
+ ev->sequence = atomic_inc_return(&fh->kevents.sequence);
+ ev->type = MEDIA_EVENT_TYPE_REQUEST_COMPLETE;
+ ev->req_complete.id = req->id;
+
+ list_add(&kev->list, &fh->kevents.head);
+ req->kev = NULL;
+ req->state = MEDIA_DEVICE_REQUEST_STATE_COMPLETE;
+ wake_up(&fh->kevents.wait);
+}
+
+static void media_device_request_release(struct kref *kref)
+{
+ struct media_device_request *req =
+ container_of(kref, struct media_device_request, kref);
+ struct media_device *mdev = req->mdev;
+
+ dev_dbg(mdev->dev, "release request %u\n", req->id);
+
+ ida_simple_remove(&mdev->req_ids, req->id);
+
+ kfree(req->kev);
+ req->kev = NULL;
+
+ mdev->ops->req_free(mdev, req);
+}
+
+void media_device_request_put(struct media_device_request *req)
+{
+ kref_put(&req->kref, media_device_request_release);
+}
+EXPORT_SYMBOL_GPL(media_device_request_put);
+
+static int media_device_request_alloc(struct media_device *mdev,
+ struct file *filp,
+ struct media_request_cmd *cmd)
+{
+ struct media_device_fh *fh = media_device_fh(filp);
+ struct media_device_request *req;
+ struct media_kevent *kev;
+ unsigned long flags;
+ int id = ida_simple_get(&mdev->req_ids, 1, 0, GFP_KERNEL);
+ int ret;
+
+ if (id < 0) {
+ dev_dbg(mdev->dev, "request: unable to obtain new id\n");
+ return id;
+ }
+
+ kev = kzalloc(sizeof(*kev), GFP_KERNEL);
+ if (!kev) {
+ ret = -ENOMEM;
+ goto out_ida_simple_remove;
+ }
+
+ req = mdev->ops->req_alloc(mdev);
+ if (!req) {
+ ret = -ENOMEM;
+ goto out_kev_free;
+ }
+
+ req->mdev = mdev;
+ req->id = id;
+ req->filp = filp;
+ req->state = MEDIA_DEVICE_REQUEST_STATE_IDLE;
+ req->kev = kev;
+ kref_init(&req->kref);
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ list_add_tail(&req->list, &mdev->requests);
+ list_add_tail(&req->fh_list, &fh->requests);
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ cmd->request = req->id;
+
+ dev_dbg(mdev->dev, "request: allocated id %u\n", req->id);
+
+ return 0;
+
+out_kev_free:
+ kfree(kev);
+
+out_ida_simple_remove:
+ ida_simple_remove(&mdev->req_ids, id);
+
+ return ret;
+}
+
+static int media_device_request_delete(struct media_device *mdev,
+ struct media_device_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+
+ if (req->state != MEDIA_DEVICE_REQUEST_STATE_IDLE) {
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+ dev_dbg(mdev->dev, "request: can't delete %u, state %s\n",
+ req->id, request_state(req->state));
+ return -EINVAL;
+ }
+
+ req->state = MEDIA_DEVICE_REQUEST_STATE_DELETED;
+
+ if (req->filp) {
+ /*
+ * If the file handle is gone by now the
+ * request has already been deleted from the
+ * two lists.
+ */
+ list_del(&req->list);
+ list_del(&req->fh_list);
+ req->filp = NULL;
+ }
+
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ media_device_request_put(req);
+
+ return 0;
+}
+
+void media_device_request_complete(struct media_device *mdev,
+ struct media_device_request *req)
+{
+ struct file *filp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+
+ if (req->state == MEDIA_DEVICE_REQUEST_STATE_IDLE) {
+ dev_dbg(mdev->dev,
+ "request: not completing an idle request %u\n",
+ req->id);
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+ return;
+ }
+
+ if (WARN_ON(req->state != MEDIA_DEVICE_REQUEST_STATE_QUEUED)) {
+ dev_dbg(mdev->dev, "request: can't delete %u, state %s\n",
+ req->id, request_state(req->state));
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+ return;
+ }
+
+ req->state = MEDIA_DEVICE_REQUEST_STATE_COMPLETE;
+ filp = req->filp;
+ if (filp) {
+ /*
+ * If the file handle is still around we remove if
+ * from the lists here. Otherwise it has been removed
+ * when the file handle closed.
+ */
+ list_del(&req->list);
+ list_del(&req->fh_list);
+ /* If the user asked for an event, let's queue one. */
+ if (req->flags & MEDIA_REQ_FL_COMPLETE_EVENT)
+ media_device_request_queue_event(
+ mdev, req, media_device_fh(filp));
+ req->filp = NULL;
+ }
+
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ /*
+ * The driver holds a reference to a request if the filp
+ * pointer is non-NULL: the file handle associated to the
+ * request may have been released by now, i.e. filp is NULL.
+ */
+ if (filp)
+ media_device_request_put(req);
+}
+EXPORT_SYMBOL_GPL(media_device_request_complete);
+
+static int media_device_request_queue_apply(
+ struct media_device *mdev, struct media_device_request *req,
+ u32 req_flags, int (*fn)(struct media_device *mdev,
+ struct media_device_request *req), bool queue)
+{
+ char *str = queue ? "queue" : "apply";
+ unsigned long flags;
+ int rval = 0;
+
+ if (!fn)
+ return -ENOSYS;
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ if (req->state != MEDIA_DEVICE_REQUEST_STATE_IDLE) {
+ rval = -EINVAL;
+ dev_dbg(mdev->dev,
+ "request: unable to %s %u, request in state %s\n",
+ str, req->id, request_state(req->state));
+ } else {
+ req->state = MEDIA_DEVICE_REQUEST_STATE_QUEUED;
+ req->flags = req_flags;
+ }
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ if (rval)
+ return rval;
+
+ rval = fn(mdev, req);
+ if (rval) {
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ req->state = MEDIA_DEVICE_REQUEST_STATE_IDLE;
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+ dev_dbg(mdev->dev,
+ "request: can't %s %u\n", str, req->id);
+ } else {
+ dev_dbg(mdev->dev,
+ "request: %s %u\n", str, req->id);
+ }
+
+ return rval;
+}
+
+static long media_device_request_cmd(struct media_device *mdev,
+ struct file *filp,
+ struct media_request_cmd *cmd)
+{
+ struct media_device_request *req = NULL;
+ int ret;
+
+ if (!mdev->ops || !mdev->ops->req_alloc || !mdev->ops->req_free)
+ return -ENOTTY;
+
+ if (cmd->cmd != MEDIA_REQ_CMD_ALLOC) {
+ req = media_device_request_find(mdev, cmd->request);
+ if (!req)
+ return -EINVAL;
+ }
+
+ switch (cmd->cmd) {
+ case MEDIA_REQ_CMD_ALLOC:
+ ret = media_device_request_alloc(mdev, filp, cmd);
+ break;
+
+ case MEDIA_REQ_CMD_DELETE:
+ ret = media_device_request_delete(mdev, req);
+ break;
+
+ case MEDIA_REQ_CMD_APPLY:
+ ret = media_device_request_queue_apply(mdev, req, cmd->flags,
+ mdev->ops->req_apply,
+ false);
+ break;
+
+ case MEDIA_REQ_CMD_QUEUE:
+ ret = media_device_request_queue_apply(mdev, req, cmd->flags,
+ mdev->ops->req_queue,
+ true);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (req)
+ media_device_request_put(req);
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
/* -----------------------------------------------------------------------------
* Userspace API
*/
@@ -54,15 +406,58 @@ static inline void __user *media_get_uptr(__u64 arg)
static int media_device_open(struct file *filp)
{
+ struct media_device_fh *fh;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (!fh)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&fh->requests);
+ INIT_LIST_HEAD(&fh->kevents.head);
+ init_waitqueue_head(&fh->kevents.wait);
+ atomic_set(&fh->kevents.sequence, -1);
+ filp->private_data = &fh->fh;
+
return 0;
}
static int media_device_close(struct file *filp)
{
+ struct media_device_fh *fh = media_device_fh(filp);
+ struct media_device *mdev = fh->fh.devnode->media_dev;
+
+ spin_lock_irq(&mdev->req_lock);
+ while (!list_empty(&fh->requests)) {
+ struct media_device_request *req =
+ list_first_entry(&fh->requests, typeof(*req), fh_list);
+
+ list_del(&req->list);
+ list_del(&req->fh_list);
+ req->filp = NULL;
+ spin_unlock_irq(&mdev->req_lock);
+ media_device_request_put(req);
+ spin_lock_irq(&mdev->req_lock);
+ }
+
+ while (!list_empty(&fh->kevents.head)) {
+ struct media_kevent *kev =
+ list_first_entry(&fh->kevents.head, typeof(*kev), list);
+
+ list_del(&kev->list);
+ spin_unlock_irq(&mdev->req_lock);
+ kfree(kev);
+ spin_lock_irq(&mdev->req_lock);
+ }
+ spin_unlock_irq(&mdev->req_lock);
+
+ kfree(fh);
+
return 0;
}
-static long media_device_get_info(struct media_device *dev, void *arg)
+static long media_device_get_info(struct media_device *dev,
+ struct file *filp,
+ void *arg)
{
struct media_device_info *info = arg;
@@ -102,7 +497,9 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id)
return NULL;
}
-static long media_device_enum_entities(struct media_device *mdev, void *arg)
+static long media_device_enum_entities(struct media_device *mdev,
+ struct file *filp,
+ void *arg)
{
struct media_entity_desc *entd = arg;
struct media_entity *ent;
@@ -155,7 +552,9 @@ static void media_device_kpad_to_upad(const struct media_pad *kpad,
upad->flags = kpad->flags;
}
-static long media_device_enum_links(struct media_device *mdev, void *arg)
+static long media_device_enum_links(struct media_device *mdev,
+ struct file *filp,
+ void *arg)
{
struct media_links_enum *links = arg;
struct media_entity *entity;
@@ -204,7 +603,9 @@ static long media_device_enum_links(struct media_device *mdev, void *arg)
return 0;
}
-static long media_device_setup_link(struct media_device *mdev, void *arg)
+static long media_device_setup_link(struct media_device *mdev,
+ struct file *filp,
+ void *arg)
{
struct media_link_desc *linkd = arg;
struct media_link *link = NULL;
@@ -377,6 +778,49 @@ static long media_device_get_topology(struct media_device *mdev, void *arg)
return ret;
}
+static struct media_kevent *opportunistic_dqevent(struct media_device *mdev,
+ struct file *filp)
+{
+ struct media_device_fh *fh = media_device_fh(filp);
+ struct media_kevent *kev = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ if (!list_empty(&fh->kevents.head)) {
+ kev = list_last_entry(&fh->kevents.head,
+ struct media_kevent, list);
+ list_del(&kev->list);
+ }
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ return kev;
+}
+
+static int media_device_dqevent(struct media_device *mdev,
+ struct file *filp,
+ struct media_event *ev)
+{
+ struct media_device_fh *fh = media_device_fh(filp);
+ struct media_kevent *kev;
+
+ if (filp->f_flags & O_NONBLOCK) {
+ kev = opportunistic_dqevent(mdev, filp);
+ if (!kev)
+ return -ENODATA;
+ } else {
+ int ret = wait_event_interruptible(
+ fh->kevents.wait,
+ (kev = opportunistic_dqevent(mdev, filp)));
+ if (ret == -ERESTARTSYS)
+ return ret;
+ }
+
+ *ev = kev->ev;
+ kfree(kev);
+
+ return 0;
+}
+
static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd)
{
/* All media IOCTLs are _IOWR() */
@@ -401,7 +845,8 @@ static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd)
#define MEDIA_IOC_ARG(__cmd, func, fl, from_user, to_user) \
[_IOC_NR(MEDIA_IOC_##__cmd)] = { \
.cmd = MEDIA_IOC_##__cmd, \
- .fn = (long (*)(struct media_device *, void *))func, \
+ .fn = (long (*)(struct media_device *, \
+ struct file *, void *))func, \
.flags = fl, \
.arg_from_user = from_user, \
.arg_to_user = to_user, \
@@ -414,7 +859,7 @@ static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd)
struct media_ioctl_info {
unsigned int cmd;
unsigned short flags;
- long (*fn)(struct media_device *dev, void *arg);
+ long (*fn)(struct media_device *dev, struct file *file, void *arg);
long (*arg_from_user)(void *karg, void __user *uarg, unsigned int cmd);
long (*arg_to_user)(void __user *uarg, void *karg, unsigned int cmd);
};
@@ -425,6 +870,8 @@ 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),
+ MEDIA_IOC(DQEVENT, media_device_dqevent, 0),
};
static long media_device_ioctl(struct file *filp, unsigned int cmd,
@@ -458,7 +905,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
if (info->flags & MEDIA_IOC_FL_GRAPH_MUTEX)
mutex_lock(&dev->graph_mutex);
- ret = info->fn(dev, karg);
+ ret = info->fn(dev, filp, karg);
if (info->flags & MEDIA_IOC_FL_GRAPH_MUTEX)
mutex_unlock(&dev->graph_mutex);
@@ -473,6 +920,34 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
return ret;
}
+static unsigned int media_device_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct media_device_fh *fh = media_device_fh(filp);
+ struct media_device *mdev = fh->fh.devnode->media_dev;
+ unsigned int poll_events = poll_requested_events(wait);
+ int ret = 0;
+
+ if (poll_events & (POLLIN | POLLOUT))
+ return POLLERR;
+
+ if (poll_events & POLLPRI) {
+ unsigned long flags;
+ bool empty;
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ empty = list_empty(&fh->kevents.head);
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ if (empty)
+ poll_wait(filp, &fh->kevents.wait, wait);
+ else
+ ret |= POLLPRI;
+ }
+
+ return ret;
+}
+
#ifdef CONFIG_COMPAT
struct media_links_enum32 {
@@ -483,7 +958,8 @@ struct media_links_enum32 {
};
static long media_device_enum_links32(struct media_device *mdev,
- struct media_links_enum32 __user *ulinks)
+ struct file *filp,
+ struct media_links_enum32 __user *ulinks)
{
struct media_links_enum links;
compat_uptr_t pads_ptr, links_ptr;
@@ -499,7 +975,7 @@ static long media_device_enum_links32(struct media_device *mdev,
links.pads = compat_ptr(pads_ptr);
links.links = compat_ptr(links_ptr);
- ret = media_device_enum_links(mdev, &links);
+ ret = media_device_enum_links(mdev, filp, &links);
if (ret)
return ret;
@@ -522,6 +998,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd,
case MEDIA_IOC_ENUM_LINKS32:
mutex_lock(&dev->graph_mutex);
ret = media_device_enum_links32(dev,
+ filp,
(struct media_links_enum32 __user *)arg);
mutex_unlock(&dev->graph_mutex);
break;
@@ -538,6 +1015,7 @@ static const struct media_file_operations media_device_fops = {
.owner = THIS_MODULE,
.open = media_device_open,
.ioctl = media_device_ioctl,
+ .poll = media_device_poll,
#ifdef CONFIG_COMPAT
.compat_ioctl = media_device_compat_ioctl,
#endif /* CONFIG_COMPAT */
@@ -726,6 +1204,10 @@ int __must_check __media_device_register(struct media_device *mdev,
if (!devnode)
return -ENOMEM;
+ ida_init(&mdev->req_ids);
+ spin_lock_init(&mdev->req_lock);
+ INIT_LIST_HEAD(&mdev->requests);
+
/* Register the device node. */
mdev->devnode = devnode;
devnode->fops = &media_device_fops;
@@ -748,6 +1230,7 @@ int __must_check __media_device_register(struct media_device *mdev,
mdev->devnode = NULL;
media_devnode_unregister_prepare(devnode);
media_devnode_unregister(devnode);
+ ida_destroy(&mdev->req_ids);
return ret;
}
@@ -832,6 +1315,7 @@ void media_device_unregister(struct media_device *mdev)
device_remove_file(&mdev->devnode->dev, &dev_attr_model);
media_devnode_unregister(mdev->devnode);
+ ida_destroy(&mdev->req_ids);
/* devnode free is handled in media_devnode_*() */
mdev->devnode = NULL;
}
diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
index 6b87a721dc49..86e92cb4289c 100644
--- a/drivers/media/media-devnode.c
+++ b/drivers/media/media-devnode.c
@@ -149,6 +149,7 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd,
/* Override for the open function */
static int media_open(struct inode *inode, struct file *filp)
{
+ struct media_devnode_fh *fh;
struct media_devnode *devnode;
int ret;
@@ -181,6 +182,9 @@ static int media_open(struct inode *inode, struct file *filp)
}
}
+ fh = filp->private_data;
+ fh->devnode = devnode;
+
return 0;
}
diff --git a/include/media/media-device.h b/include/media/media-device.h
index bcc6ec434f1f..6f398006b54c 100644
--- a/include/media/media-device.h
+++ b/include/media/media-device.h
@@ -19,6 +19,8 @@
#ifndef _MEDIA_DEVICE_H
#define _MEDIA_DEVICE_H
+#include <linux/kref.h>
+#include <linux/idr.h>
#include <linux/list.h>
#include <linux/mutex.h>
@@ -26,7 +28,42 @@
#include <media/media-entity.h>
struct ida;
+#include <uapi/linux/media.h>
+
struct device;
+struct file;
+struct media_device;
+struct media_device_fh;
+
+enum media_device_request_state {
+ MEDIA_DEVICE_REQUEST_STATE_IDLE,
+ MEDIA_DEVICE_REQUEST_STATE_QUEUED,
+ MEDIA_DEVICE_REQUEST_STATE_DELETED,
+ MEDIA_DEVICE_REQUEST_STATE_COMPLETE,
+};
+
+/**
+ * struct media_device_request - Media device request
+ * @id: Request ID
+ * @mdev: Media device this request belongs to
+ * @kref: Reference count
+ * @list: List entry in the media device requests list
+ * @fh_list: List entry in the media file handle requests list
+ * @state: The state of the request, MEDIA_DEVICE_REQUEST_STATE_*,
+ * access to state serialised by mdev->req_lock
+ * @flags: Request specific flags, MEDIA_REQ_FL_*
+ */
+struct media_device_request {
+ u32 id;
+ struct media_device *mdev;
+ struct file *filp;
+ struct media_kevent *kev;
+ struct kref kref;
+ struct list_head list;
+ struct list_head fh_list;
+ enum media_device_request_state state;
+ u32 flags;
+};
/**
* struct media_entity_notify - Media Entity Notify
@@ -50,10 +87,26 @@ struct media_entity_notify {
* struct media_device_ops - Media device operations
* @link_notify: Link state change notification callback. This callback is
* called with the graph_mutex held.
+ * @req_alloc: Allocate a request
+ * @req_free: Free a request
+ * @req_apply: Apply a request
+ * @req_queue: Queue a request
*/
struct media_device_ops {
int (*link_notify)(struct media_link *link, u32 flags,
unsigned int notification);
+ struct media_device_request *(*req_alloc)(struct media_device *mdev);
+ void (*req_free)(struct media_device *mdev,
+ struct media_device_request *req);
+ int (*req_apply)(struct media_device *mdev,
+ struct media_device_request *req);
+ int (*req_queue)(struct media_device *mdev,
+ struct media_device_request *req);
+};
+
+struct media_kevent {
+ struct list_head list;
+ struct media_event ev;
};
/**
@@ -88,6 +141,9 @@ struct media_device_ops {
* @disable_source: Disable Source Handler function pointer
*
* @ops: Operation handler callbacks
+ * @req_ids: Allocated request IDs
+ * @req_lock: Serialise access to requests list
+ * @requests: List of allocated requests
*
* This structure represents an abstract high-level media device. It allows easy
* access to entities and provides basic media device-level support. The
@@ -158,6 +214,10 @@ struct media_device {
void (*disable_source)(struct media_entity *entity);
const struct media_device_ops *ops;
+
+ struct ida req_ids;
+ spinlock_t req_lock;
+ struct list_head requests;
};
/* We don't need to include pci.h or usb.h here */
@@ -475,4 +535,11 @@ static inline void __media_device_usb_init(struct media_device *mdev,
#define media_device_usb_init(mdev, udev, name) \
__media_device_usb_init(mdev, udev, name, KBUILD_MODNAME)
+struct media_device_request *
+media_device_request_find(struct media_device *mdev, u16 reqid);
+void media_device_request_get(struct media_device_request *req);
+void media_device_request_put(struct media_device_request *req);
+void media_device_request_complete(struct media_device *mdev,
+ struct media_device_request *req);
+
#endif
diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
index dc2f64e1b08f..2ece17356b33 100644
--- a/include/media/media-devnode.h
+++ b/include/media/media-devnode.h
@@ -63,6 +63,20 @@ struct media_file_operations {
int (*release) (struct file *);
};
+/**
+ * struct media_devnode_fh - Media device node file handle
+ * @devnode: pointer to the media device node
+ *
+ * This structure serves as a base for per-file-handle data storage. Media
+ * device node users embed media_devnode_fh in their custom file handle data
+ * structures and store the media_devnode_fh in the file private_data in order
+ * to let the media device node core locate the media_devnode corresponding to a
+ * file handle.
+ */
+struct media_devnode_fh {
+ struct media_devnode *devnode;
+};
+
/**
* struct media_devnode - Media device node
* @media_dev: pointer to struct &media_device
@@ -154,7 +168,9 @@ void media_devnode_unregister(struct media_devnode *devnode);
*/
static inline struct media_devnode *media_devnode_data(struct file *filp)
{
- return filp->private_data;
+ struct media_devnode_fh *fh = filp->private_data;
+
+ return fh->devnode;
}
/**
diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
index 36f76e777ef9..4506519c7b38 100644
--- a/include/uapi/linux/media.h
+++ b/include/uapi/linux/media.h
@@ -364,11 +364,49 @@ struct media_v2_topology {
/* ioctls */
+#define MEDIA_REQ_CMD_ALLOC 0
+#define MEDIA_REQ_CMD_DELETE 1
+#define MEDIA_REQ_CMD_APPLY 2
+#define MEDIA_REQ_CMD_QUEUE 3
+
+#define MEDIA_REQ_FL_COMPLETE_EVENT (1 << 0)
+
+#ifdef __KERNEL__
+struct __attribute__ ((packed)) media_request_cmd_0 {
+ __u32 cmd;
+ __u32 request;
+};
+#endif
+
+struct __attribute__ ((packed)) media_request_cmd {
+ __u32 cmd;
+ __u32 request;
+ __u32 flags;
+};
+
+struct __attribute__ ((packed)) media_event_request_complete {
+ __u32 id;
+};
+
+#define MEDIA_EVENT_TYPE_REQUEST_COMPLETE 1
+
+struct __attribute__ ((packed)) media_event {
+ __u32 type;
+ __u32 sequence;
+ __u32 reserved[4];
+
+ union {
+ struct media_event_request_complete req_complete;
+ };
+};
+
#define MEDIA_IOC_DEVICE_INFO _IOWR('|', 0x00, struct media_device_info)
#define MEDIA_IOC_ENUM_ENTITIES _IOWR('|', 0x01, struct media_entity_desc)
#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)
+#define MEDIA_IOC_DQEVENT _IOWR('|', 0x06, struct media_event)
#ifndef __KERNEL__
--
https://clearlinux.org