Re: [PATCHv13 03/28] media-request: implement media requests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 04/05/18 14:27, Sakari Ailus wrote:
> Hi Hans,
> 
> I've read this patch a large number of times and I think also the details
> begin to seem sound. A few comments below.
> 
> On Thu, May 03, 2018 at 04:52:53PM +0200, Hans Verkuil wrote:
>> From: Hans Verkuil <hans.verkuil@xxxxxxxxx>
>>
>> Add initial media request support:
>>
>> 1) Add MEDIA_IOC_REQUEST_ALLOC ioctl support to media-device.c
>> 2) Add struct media_request to store request objects.
>> 3) Add struct media_request_object to represent a request object.
>> 4) Add MEDIA_REQUEST_IOC_QUEUE/REINIT ioctl support.
>>
>> Basic lifecycle: the application allocates a request, adds
>> objects to it, queues the request, polls until it is completed
>> and can then read the final values of the objects at the time
>> of completion. When it closes the file descriptor the request
>> memory will be freed (actually, when the last user of that request
>> releases the request).
>>
>> Drivers will bind an object to a request (the 'adds objects to it'
>> phase), when MEDIA_REQUEST_IOC_QUEUE is called the request is
>> validated (req_validate op), then queued (the req_queue op).
>>
>> When done with an object it can either be unbound from the request
>> (e.g. when the driver has finished with a vb2 buffer) or marked as
>> completed (e.g. for controls associated with a buffer). When all
>> objects in the request are completed (or unbound), then the request
>> fd will signal an exception (poll).
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@xxxxxxxxx>
>> ---
>>  drivers/media/Makefile        |   3 +-
>>  drivers/media/media-device.c  |  14 ++
>>  drivers/media/media-request.c | 407 ++++++++++++++++++++++++++++++++++
>>  include/media/media-device.h  |  16 ++
>>  include/media/media-request.h | 244 ++++++++++++++++++++
>>  5 files changed, 683 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/media/media-request.c
>>  create mode 100644 include/media/media-request.h
>>
>> diff --git a/drivers/media/Makefile b/drivers/media/Makefile
>> index 594b462ddf0e..985d35ec6b29 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
>>  
>>  #
>>  # 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 35e81f7c0d2f..bb6a64acd3f0 100644
>> --- a/drivers/media/media-device.c
>> +++ b/drivers/media/media-device.c
>> @@ -32,6 +32,7 @@
>>  #include <media/media-device.h>
>>  #include <media/media-devnode.h>
>>  #include <media/media-entity.h>
>> +#include <media/media-request.h>
>>  
>>  #ifdef CONFIG_MEDIA_CONTROLLER
>>  
>> @@ -366,6 +367,15 @@ static long media_device_get_topology(struct media_device *mdev,
>>  	return ret;
>>  }
>>  
>> +static long media_device_request_alloc(struct media_device *mdev,
>> +				       struct media_request_alloc *alloc)
>> +{
>> +	if (!mdev->ops || !mdev->ops->req_validate || !mdev->ops->req_queue)
>> +		return -ENOTTY;
>> +
>> +	return media_request_alloc(mdev, alloc);
>> +}
>> +
>>  static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd)
>>  {
>>  	/* All media IOCTLs are _IOWR() */
>> @@ -414,6 +424,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_ALLOC, media_device_request_alloc, 0),
>>  };
>>  
>>  static long media_device_ioctl(struct file *filp, unsigned int cmd,
>> @@ -686,6 +697,8 @@ void media_device_init(struct media_device *mdev)
>>  	INIT_LIST_HEAD(&mdev->pads);
>>  	INIT_LIST_HEAD(&mdev->links);
>>  	INIT_LIST_HEAD(&mdev->entity_notify);
>> +
>> +	mutex_init(&mdev->req_queue_mutex);
>>  	mutex_init(&mdev->graph_mutex);
>>  	ida_init(&mdev->entity_internal_idx);
>>  
>> @@ -699,6 +712,7 @@ void media_device_cleanup(struct media_device *mdev)
>>  	mdev->entity_internal_idx_max = 0;
>>  	media_graph_walk_cleanup(&mdev->pm_count_walk);
>>  	mutex_destroy(&mdev->graph_mutex);
>> +	mutex_destroy(&mdev->req_queue_mutex);
>>  }
>>  EXPORT_SYMBOL_GPL(media_device_cleanup);
>>  
>> diff --git a/drivers/media/media-request.c b/drivers/media/media-request.c
>> new file mode 100644
>> index 000000000000..c216c4ab628b
>> --- /dev/null
>> +++ b/drivers/media/media-request.c
>> @@ -0,0 +1,407 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Media device request objects
>> + *
>> + * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
>> + * Copyright (C) 2018 Intel Corporation
>> + * Copyright (C) 2018 Google, Inc.
>> + *
>> + * Author: Hans Verkuil <hans.verkuil@xxxxxxxxx>
>> + * Author: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
>> + */
>> +
>> +#include <linux/anon_inodes.h>
>> +#include <linux/file.h>
>> +
>> +#include <media/media-device.h>
>> +#include <media/media-request.h>
>> +
>> +static const char * const request_state[] = {
>> +	[MEDIA_REQUEST_STATE_IDLE]	 = "idle",
>> +	[MEDIA_REQUEST_STATE_VALIDATING] = "validating",
>> +	[MEDIA_REQUEST_STATE_QUEUED]	 = "queued",
>> +	[MEDIA_REQUEST_STATE_COMPLETE]	 = "complete",
>> +	[MEDIA_REQUEST_STATE_CLEANING]	 = "cleaning",
>> +};
>> +
>> +static const char *
>> +media_request_state_str(enum media_request_state state)
>> +{
>> +	if (WARN_ON(state >= ARRAY_SIZE(request_state)))
>> +		return "invalid";
>> +	return request_state[state];
>> +}
>> +
>> +static void media_request_clean(struct media_request *req)
>> +{
>> +	struct media_request_object *obj, *obj_safe;
>> +
>> +	WARN_ON(atomic_read(&req->state) != MEDIA_REQUEST_STATE_CLEANING);
>> +
>> +	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
>> +		media_request_object_unbind(obj);
>> +		media_request_object_put(obj);
>> +	}
>> +
>> +	req->num_incomplete_objects = 0;
> 
> The number of incomplete objects should be already zero here. I'd think
> that a different number would suggest that something has gone very wrong
> and should be complained about. How about adding
> WARN_ON(req->num_incomplete_objects) above this line? 

Makes sense. Added.

> 
>> +	wake_up_interruptible_all(&req->poll_wait);
>> +}
>> +
>> +static void media_request_release(struct kref *kref)
>> +{
>> +	struct media_request *req =
>> +		container_of(kref, struct media_request, kref);
>> +	struct media_device *mdev = req->mdev;
>> +
>> +	dev_dbg(mdev->dev, "request: release %s\n", req->debug_str);
>> +
>> +	atomic_set(&req->state, MEDIA_REQUEST_STATE_CLEANING);
>> +
>> +	media_request_clean(req);
>> +
>> +	if (mdev->ops->req_free)
>> +		mdev->ops->req_free(req);
>> +	else
>> +		kfree(req);
>> +}
>> +
>> +void media_request_put(struct media_request *req)
>> +{
>> +	kref_put(&req->kref, media_request_release);
>> +}
>> +EXPORT_SYMBOL_GPL(media_request_put);
>> +
>> +static int media_request_close(struct inode *inode, struct file *filp)
>> +{
>> +	struct media_request *req = filp->private_data;
>> +
>> +	media_request_put(req);
>> +	return 0;
>> +}
>> +
>> +static unsigned int media_request_poll(struct file *filp,
>> +				       struct poll_table_struct *wait)
>> +{
>> +	struct media_request *req = filp->private_data;
>> +	unsigned long flags;
>> +	unsigned int ret = 0;
>> +	enum media_request_state state;
>> +
>> +	if (!(poll_requested_events(wait) & POLLPRI))
>> +		return 0;
>> +
>> +	spin_lock_irqsave(&req->lock, flags);
>> +	state = atomic_read(&req->state);
>> +
>> +	if (state == MEDIA_REQUEST_STATE_COMPLETE) {
>> +		ret = POLLPRI;
>> +		goto unlock;
>> +	}
>> +	if (state != MEDIA_REQUEST_STATE_QUEUED) {
>> +		ret = POLLERR;
>> +		goto unlock;
>> +	}
>> +
>> +	poll_wait(filp, &req->poll_wait, wait);
>> +
>> +unlock:
>> +	spin_unlock_irqrestore(&req->lock, flags);
>> +	return ret;
>> +}
>> +
>> +static long media_request_ioctl_queue(struct media_request *req)
>> +{
>> +	struct media_device *mdev = req->mdev;
>> +	enum media_request_state state;
>> +	unsigned long flags;
>> +	int ret = 0;
> 
> ret is unconditionally assigned below, no need to initialise here.

Fixed.

> 
>> +
>> +	dev_dbg(mdev->dev, "request: queue %s\n", req->debug_str);
>> +
>> +	/*
>> +	 * Ensure the request that is validated will be the one that gets queued
>> +	 * next by serialising the queueing process. This mutex is also used
>> +	 * to serialize with canceling a vb2 queue and with setting values such
>> +	 * as controls in a request.
>> +	 */
>> +	mutex_lock(&mdev->req_queue_mutex);
>> +
>> +	spin_lock_irqsave(&req->lock, flags);
>> +	state = atomic_cmpxchg(&req->state, MEDIA_REQUEST_STATE_IDLE,
>> +			       MEDIA_REQUEST_STATE_VALIDATING);
>> +	spin_unlock_irqrestore(&req->lock, flags);
>> +	if (state != MEDIA_REQUEST_STATE_IDLE) {
>> +		dev_dbg(mdev->dev,
>> +			"request: unable to queue %s, request in state %s\n",
>> +			req->debug_str, media_request_state_str(state));
>> +		mutex_unlock(&mdev->req_queue_mutex);
>> +		return -EBUSY;
>> +	}
>> +
>> +	ret = mdev->ops->req_validate(req);
>> +
>> +	/*
>> +	 * If the req_validate was successful, then we mark the state as QUEUED
>> +	 * and call req_queue. The reason we set the state first is that this
>> +	 * allows req_queue to unbind or complete the queued objects in case
>> +	 * they are immediately 'consumed'. State changes from QUEUED to another
>> +	 * state can only happen if either the driver changes the state or if
>> +	 * the user cancels the vb2 queue. The driver can only change the state
>> +	 * after each object is queued through the req_queue op (and note that
>> +	 * that op cannot fail), so setting the state to QUEUED up front is
>> +	 * safe.
>> +	 *
>> +	 * The other reason for changing the state is if the vb2 queue is
>> +	 * canceled, and that uses the req_queue_mutex which is still locked
>> +	 * while req_queue is called, so that's safe as well.
>> +	 */
>> +	atomic_set(&req->state,
>> +		   ret ? MEDIA_REQUEST_STATE_IDLE : MEDIA_REQUEST_STATE_QUEUED);
>> +
>> +	if (!ret)
>> +		mdev->ops->req_queue(req);
>> +
>> +	mutex_unlock(&mdev->req_queue_mutex);
>> +
>> +	if (ret)
>> +		dev_dbg(mdev->dev, "request: can't queue %s (%d)\n",
>> +			req->debug_str, ret);
>> +	else
>> +		media_request_get(req);
>> +
>> +	return ret;
>> +}
>> +
>> +static long media_request_ioctl_reinit(struct media_request *req)
>> +{
>> +	struct media_device *mdev = req->mdev;
>> +	unsigned long flags;
>> +
>> +	spin_lock_irqsave(&req->lock, flags);
>> +	if (atomic_read(&req->state) != MEDIA_REQUEST_STATE_IDLE &&
>> +	    atomic_read(&req->state) != MEDIA_REQUEST_STATE_COMPLETE) {
>> +		dev_dbg(mdev->dev,
>> +			"request: %s not in idle or complete state, cannot reinit\n",
>> +			req->debug_str);
>> +		spin_unlock_irqrestore(&req->lock, flags);
>> +		return -EBUSY;
>> +	}
>> +	atomic_set(&req->state, MEDIA_REQUEST_STATE_CLEANING);
>> +	spin_unlock_irqrestore(&req->lock, flags);
>> +
>> +	media_request_clean(req);
>> +
>> +	atomic_set(&req->state, MEDIA_REQUEST_STATE_IDLE);
>> +
>> +	return 0;
>> +}
>> +
>> +static long media_request_ioctl(struct file *filp, unsigned int cmd,
>> +				unsigned long arg)
>> +{
>> +	struct media_request *req = filp->private_data;
>> +
>> +	switch (cmd) {
>> +	case MEDIA_REQUEST_IOC_QUEUE:
>> +		return media_request_ioctl_queue(req);
>> +	case MEDIA_REQUEST_IOC_REINIT:
>> +		return media_request_ioctl_reinit(req);
>> +	default:
>> +		return -ENOIOCTLCMD;
>> +	}
>> +}
>> +
>> +static const struct file_operations request_fops = {
>> +	.owner = THIS_MODULE,
>> +	.poll = media_request_poll,
>> +	.unlocked_ioctl = media_request_ioctl,
>> +	.release = media_request_close,
>> +};
>> +
>> +int media_request_alloc(struct media_device *mdev,
>> +			struct media_request_alloc *alloc)
>> +{
>> +	struct media_request *req;
>> +	struct file *filp;
>> +	char comm[TASK_COMM_LEN];
>> +	int fd;
>> +	int ret;
>> +
>> +	/* Either both are NULL or both are non-NULL */
>> +	if (WARN_ON(!mdev->ops->req_alloc ^ !mdev->ops->req_free))
>> +		return -ENOMEM;
>> +
>> +	fd = get_unused_fd_flags(O_CLOEXEC);
>> +	if (fd < 0)
>> +		return fd;
>> +
>> +	filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC);
>> +	if (IS_ERR(filp)) {
>> +		ret = PTR_ERR(filp);
>> +		goto err_put_fd;
>> +	}
>> +
>> +	if (mdev->ops->req_alloc)
>> +		req = mdev->ops->req_alloc(mdev);
>> +	else
>> +		req = kzalloc(sizeof(*req), GFP_KERNEL);
>> +	if (!req) {
>> +		ret = -ENOMEM;
>> +		goto err_fput;
>> +	}
>> +
>> +	filp->private_data = req;
>> +	req->mdev = mdev;
>> +	atomic_set(&req->state, MEDIA_REQUEST_STATE_IDLE);
>> +	req->num_incomplete_objects = 0;
>> +	kref_init(&req->kref);
>> +	INIT_LIST_HEAD(&req->objects);
>> +	spin_lock_init(&req->lock);
>> +	init_waitqueue_head(&req->poll_wait);
>> +
>> +	alloc->fd = fd;
>> +
>> +	get_task_comm(comm, current);
>> +	snprintf(req->debug_str, sizeof(req->debug_str), "%s:%d",
>> +		 comm, fd);
>> +	dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
>> +
>> +	fd_install(fd, filp);
>> +
>> +	return 0;
>> +
>> +err_fput:
>> +	fput(filp);
>> +
>> +err_put_fd:
>> +	put_unused_fd(fd);
>> +
>> +	return ret;
>> +}
>> +
>> +static void media_request_object_release(struct kref *kref)
>> +{
>> +	struct media_request_object *obj =
>> +		container_of(kref, struct media_request_object, kref);
>> +	struct media_request *req = obj->req;
>> +
>> +	if (req)
>> +		media_request_object_unbind(obj);
>> +	obj->ops->release(obj);
>> +}
>> +
>> +void media_request_object_put(struct media_request_object *obj)
>> +{
>> +	kref_put(&obj->kref, media_request_object_release);
>> +}
>> +EXPORT_SYMBOL_GPL(media_request_object_put);
>> +
>> +void media_request_object_init(struct media_request_object *obj)
>> +{
>> +	obj->ops = NULL;
>> +	obj->req = NULL;
>> +	obj->priv = NULL;
>> +	obj->completed = false;
>> +	INIT_LIST_HEAD(&obj->list);
>> +	kref_init(&obj->kref);
>> +}
>> +EXPORT_SYMBOL_GPL(media_request_object_init);
>> +
>> +int media_request_object_bind(struct media_request *req,
>> +			      const struct media_request_object_ops *ops,
>> +			      void *priv,
>> +			      struct media_request_object *obj)
>> +{
>> +	unsigned long flags;
>> +	int ret = -EBUSY;
>> +
>> +	if (WARN_ON(!ops->release))
>> +		return -EPERM;
>> +
>> +	obj->req = req;
>> +	obj->ops = ops;
>> +	obj->priv = priv;
>> +
>> +	spin_lock_irqsave(&req->lock, flags);
>> +
>> +	if (WARN_ON(atomic_read(&req->state) != MEDIA_REQUEST_STATE_IDLE))
>> +		goto unlock;
> 
> Is this worth a kernel warning, or rather how the drivers / other framework
> bits (e.g. VB2) prevent user from binding objects to non-idle requests?
> Even if you added a similar check to the caller, the request state could
> well change in the meantime.
> 
> Perhaps add __must_check to the return value?

This has already been addressed in v14 with media_request_lock_for_update().

> 
>> +
>> +	list_add_tail(&obj->list, &req->objects);
>> +	req->num_incomplete_objects++;
>> +	ret = 0;
>> +
>> +unlock:
>> +	spin_unlock_irqrestore(&req->lock, flags);
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(media_request_object_bind);
>> +
>> +void media_request_object_unbind(struct media_request_object *obj)
>> +{
>> +	struct media_request *req = obj->req;
>> +	enum media_request_state state;
>> +	unsigned long flags;
>> +	bool completed = false;
>> +
>> +	if (WARN_ON(!req))
>> +		return;
>> +
>> +	spin_lock_irqsave(&req->lock, flags);
>> +	list_del(&obj->list);
>> +	obj->req = NULL;
>> +
>> +	state = atomic_read(&req->state);
>> +
>> +	if (state == MEDIA_REQUEST_STATE_COMPLETE ||
>> +	    state == MEDIA_REQUEST_STATE_CLEANING)
>> +		goto unlock;
>> +
>> +	if (WARN_ON(state == MEDIA_REQUEST_STATE_VALIDATING))
>> +		goto unlock;
>> +
>> +	if (WARN_ON(!req->num_incomplete_objects))
>> +		goto unlock;
>> +
>> +	req->num_incomplete_objects--;
>> +	if (state == MEDIA_REQUEST_STATE_QUEUED &&
>> +	    !req->num_incomplete_objects) {
>> +		atomic_set(&req->state, MEDIA_REQUEST_STATE_COMPLETE);
>> +		completed = true;
>> +		wake_up_interruptible_all(&req->poll_wait);
>> +	}
>> +
>> +unlock:
>> +	spin_unlock_irqrestore(&req->lock, flags);
>> +	if (obj->ops->unbind)
>> +		obj->ops->unbind(obj);
>> +	if (completed)
>> +		media_request_put(req);
>> +}
>> +EXPORT_SYMBOL_GPL(media_request_object_unbind);
>> +
>> +void media_request_object_complete(struct media_request_object *obj)
>> +{
>> +	struct media_request *req = obj->req;
>> +	unsigned long flags;
>> +	bool completed = false;
>> +
>> +	spin_lock_irqsave(&req->lock, flags);
>> +	if (obj->completed)
>> +		goto unlock;
>> +	obj->completed = true;
>> +	if (WARN_ON(!req->num_incomplete_objects) ||
>> +	    WARN_ON(atomic_read(&req->state) != MEDIA_REQUEST_STATE_QUEUED))
>> +		goto unlock;
>> +
>> +	if (!--req->num_incomplete_objects) {
>> +		atomic_set(&req->state, MEDIA_REQUEST_STATE_COMPLETE);
>> +		wake_up_interruptible_all(&req->poll_wait);
>> +		completed = true;
>> +	}
>> +unlock:
>> +	spin_unlock_irqrestore(&req->lock, flags);
>> +	if (completed)
>> +		media_request_put(req);
>> +}
>> +EXPORT_SYMBOL_GPL(media_request_object_complete);
>> diff --git a/include/media/media-device.h b/include/media/media-device.h
>> index bcc6ec434f1f..7d855823341c 100644
>> --- a/include/media/media-device.h
>> +++ b/include/media/media-device.h
>> @@ -27,6 +27,7 @@
>>  
>>  struct ida;
>>  struct device;
>> +struct media_device;
>>  
>>  /**
>>   * struct media_entity_notify - Media Entity Notify
>> @@ -50,10 +51,21 @@ 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_validate: Validate a request, but do not queue yet
>> + * @req_queue: Queue a validated request, cannot fail. If something goes
>> + *	       wrong when queueing this request then it should be marked
>> + *	       as such internally in the driver and any related buffers
>> + *	       must eventually return to vb2 with state VB2_BUF_STATE_ERROR.
>>   */
>>  struct media_device_ops {
>>  	int (*link_notify)(struct media_link *link, u32 flags,
>>  			   unsigned int notification);
>> +	struct media_request *(*req_alloc)(struct media_device *mdev);
>> +	void (*req_free)(struct media_request *req);
>> +	int (*req_validate)(struct media_request *req);
>> +	void (*req_queue)(struct media_request *req);
>>  };
>>  
>>  /**
>> @@ -88,6 +100,8 @@ struct media_device_ops {
>>   * @disable_source: Disable Source Handler function pointer
>>   *
>>   * @ops:	Operation handler callbacks
>> + * @req_queue_mutex: Serialise the MEDIA_REQUEST_IOC_QUEUE ioctl w.r.t. this
>> + *		     media device.
>>   *
>>   * 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 +172,8 @@ struct media_device {
>>  	void (*disable_source)(struct media_entity *entity);
>>  
>>  	const struct media_device_ops *ops;
>> +
>> +	struct mutex req_queue_mutex;
>>  };
>>  
>>  /* We don't need to include pci.h or usb.h here */
>> diff --git a/include/media/media-request.h b/include/media/media-request.h
>> new file mode 100644
>> index 000000000000..e39122dfd717
>> --- /dev/null
>> +++ b/include/media/media-request.h
>> @@ -0,0 +1,244 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Media device request objects
>> + *
>> + * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
>> + * Copyright (C) 2018 Intel Corporation
>> + *
>> + * Author: Hans Verkuil <hans.verkuil@xxxxxxxxx>
>> + * Author: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
>> + */
>> +
>> +#ifndef MEDIA_REQUEST_H
>> +#define MEDIA_REQUEST_H
>> +
>> +#include <linux/list.h>
>> +#include <linux/slab.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/atomic.h>
>> +
>> +#include <media/media-device.h>
>> +
>> +/**
>> + * enum media_request_state - media request state
>> + *
>> + * @MEDIA_REQUEST_STATE_IDLE:		Idle
>> + * @MEDIA_REQUEST_STATE_VALIDATING:	Validating the request, no state changes
>> + *					allowed
>> + * @MEDIA_REQUEST_STATE_QUEUED:		Queued
>> + * @MEDIA_REQUEST_STATE_COMPLETE:	Completed, the request is done
>> + * @MEDIA_REQUEST_STATE_CLEANING:	Cleaning, the request is being re-inited
>> + */
>> +enum media_request_state {
>> +	MEDIA_REQUEST_STATE_IDLE,
>> +	MEDIA_REQUEST_STATE_VALIDATING,
>> +	MEDIA_REQUEST_STATE_QUEUED,
>> +	MEDIA_REQUEST_STATE_COMPLETE,
>> +	MEDIA_REQUEST_STATE_CLEANING,
>> +};
>> +
>> +struct media_request_object;
>> +
>> +/**
>> + * struct media_request - Media device request
>> + * @mdev: Media device this request belongs to
>> + * @kref: Reference count
>> + * @debug_str: Prefix for debug messages (process name:fd)
>> + * @state: The state of the request
>> + * @objects: List of @struct media_request_object request objects
>> + * @num_objects: The number of objects in the request
>> + * @num_incompleted_objects: The number of incomplete objects in the request
>> + * @poll_wait: Wait queue for poll
>> + * @lock: Serializes access to this struct
>> + */
>> +struct media_request {
>> +	struct media_device *mdev;
>> +	struct kref kref;
>> +	char debug_str[TASK_COMM_LEN + 11];
>> +	atomic_t state;
>> +	struct list_head objects;
>> +	unsigned int num_incomplete_objects;
>> +	struct wait_queue_head poll_wait;
>> +	spinlock_t lock;
>> +};
>> +
>> +#ifdef CONFIG_MEDIA_CONTROLLER
>> +
>> +/**
>> + * media_request_get - Get the media request
>> + *
>> + * @req: The request
>> + *
>> + * Get the media request.
>> + */
>> +static inline void media_request_get(struct media_request *req)
>> +{
>> +	kref_get(&req->kref);
>> +}
>> +
>> +/**
>> + * media_request_put - Put the media request
>> + *
>> + * @req: The request
>> + *
>> + * Put the media request. The media request will be released
>> + * when the refcount reaches 0.
>> + */
>> +void media_request_put(struct media_request *req);
>> +
>> +/**
>> + * media_request_alloc - Allocate the media request
>> + *
>> + * @mdev: Media device this request belongs to
>> + * @alloc: Store the request's file descriptor in this struct
>> + *
>> + * Allocated the media request and put the fd in @alloc->fd.
>> + */
>> +int media_request_alloc(struct media_device *mdev,
>> +			struct media_request_alloc *alloc);
>> +
>> +#else
>> +
>> +static inline void media_request_get(struct media_request *req)
>> +{
>> +}
>> +
>> +static inline void media_request_put(struct media_request *req)
>> +{
>> +}
>> +
>> +#endif
>> +
>> +/**
>> + * struct media_request_object_ops - Media request object operations
>> + * @prepare: Validate and prepare the request object, optional.
>> + * @unprepare: Unprepare the request object, optional.
>> + * @queue: Queue the request object, optional.
>> + * @unbind: Unbind the request object, optional.
>> + * @release: Release the request object, required.
>> + */
>> +struct media_request_object_ops {
>> +	int (*prepare)(struct media_request_object *object);
>> +	void (*unprepare)(struct media_request_object *object);
>> +	void (*queue)(struct media_request_object *object);
>> +	void (*unbind)(struct media_request_object *object);
>> +	void (*release)(struct media_request_object *object);
>> +};
>> +
>> +/**
>> + * struct media_request_object - An opaque object that belongs to a media
>> + *				 request
>> + *
>> + * @ops: object's operations
>> + * @priv: object's priv pointer
>> + * @req: the request this object belongs to (can be NULL)
>> + * @list: List entry of the object for @struct media_request
>> + * @kref: Reference count of the object, acquire before releasing req->lock
>> + * @completed: If true, then this object was completed.
>> + *
>> + * An object related to the request. This struct is embedded in the
>> + * larger object data.
>> + */
>> +struct media_request_object {
>> +	const struct media_request_object_ops *ops;
>> +	void *priv;
>> +	struct media_request *req;
>> +	struct list_head list;
>> +	struct kref kref;
>> +	bool completed;
>> +};
>> +
>> +#ifdef CONFIG_MEDIA_CONTROLLER
>> +
>> +/**
>> + * media_request_object_get - Get a media request object
>> + *
>> + * @obj: The object
>> + *
>> + * Get a media request object.
>> + */
>> +static inline void media_request_object_get(struct media_request_object *obj)
>> +{
>> +	kref_get(&obj->kref);
>> +}
>> +
>> +/**
>> + * media_request_object_put - Put a media request object
>> + *
>> + * @obj: The object
>> + *
>> + * Put a media request object. Once all references are gone, the
>> + * object's memory is released.
>> + */
>> +void media_request_object_put(struct media_request_object *obj);
>> +
>> +/**
>> + * media_request_object_init - Initialise a media request object
>> + *
>> + * Initialise a media request object. The object will be released using the
>> + * release callback of the ops once it has no references (this function
>> + * initialises references to one).
>> + */
>> +void media_request_object_init(struct media_request_object *obj);
>> +
>> +/**
>> + * media_request_object_bind - Bind a media request object to a request
> 
> Argument documentation is missing.
> 
> I think you should also say that "every bound object must be unbound later
> on".

Done.

> 
>> + */
>> +int media_request_object_bind(struct media_request *req,
>> +			      const struct media_request_object_ops *ops,
>> +			      void *priv,
>> +			      struct media_request_object *obj);
>> +
>> +/**
>> + * media_request_object_unbind - Unbind a media request object
>> + *
>> + * @obj: The object
>> + *
>> + * Unbind the media request object from the request.
>> + */
>> +void media_request_object_unbind(struct media_request_object *obj);
>> +
>> +/**
>> + * media_request_object_complete - Mark the media request object as complete
>> + *
>> + * @obj: The object
>> + *
>> + * Mark the media request object as complete.
> 
> Add:
> 
> Only bound request objects may be completed.

Done.

> 
>> + */
>> +void media_request_object_complete(struct media_request_object *obj);
>> +
>> +#else
>> +
>> +static inline void media_request_object_get(struct media_request_object *obj)
>> +{
>> +}
>> +
>> +static inline void media_request_object_put(struct media_request_object *obj)
>> +{
>> +}
>> +
>> +static inline void media_request_object_init(struct media_request_object *obj)
>> +{
>> +	obj->ops = NULL;
>> +	obj->req = NULL;
>> +}
>> +
>> +static inline int media_request_object_bind(struct media_request *req,
>> +			       const struct media_request_object_ops *ops,
>> +			       void *priv,
>> +			       struct media_request_object *obj)
>> +{
>> +	return 0;
>> +}
>> +
>> +static inline void media_request_object_unbind(struct media_request_object *obj)
>> +{
>> +}
>> +
>> +static inline void media_request_object_complete(struct media_request_object *obj)
>> +{
>> +}
>> +
>> +#endif
>> +
>> +#endif
> 

Regards,

	Hans



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux