Commit 10905d70 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

media: media-request: implement media requests

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).
Co-developed-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Co-developed-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Co-developed-by: default avatarAlexandre Courbot <acourbot@chromium.org>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent 66431c0b
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
# Makefile for the kernel multimedia device drivers. # 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 # I2C drivers should come before other drivers, otherwise they'll fail
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <media/media-device.h> #include <media/media-device.h>
#include <media/media-devnode.h> #include <media/media-devnode.h>
#include <media/media-entity.h> #include <media/media-entity.h>
#include <media/media-request.h>
#ifdef CONFIG_MEDIA_CONTROLLER #ifdef CONFIG_MEDIA_CONTROLLER
...@@ -377,10 +378,19 @@ static long media_device_get_topology(struct media_device *mdev, void *arg) ...@@ -377,10 +378,19 @@ static long media_device_get_topology(struct media_device *mdev, void *arg)
return ret; return ret;
} }
static long media_device_request_alloc(struct media_device *mdev,
int *alloc_fd)
{
if (!mdev->ops || !mdev->ops->req_validate || !mdev->ops->req_queue)
return -ENOTTY;
return media_request_alloc(mdev, alloc_fd);
}
static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd) static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd)
{ {
/* All media IOCTLs are _IOWR() */ if ((_IOC_DIR(cmd) & _IOC_WRITE) &&
if (copy_from_user(karg, uarg, _IOC_SIZE(cmd))) copy_from_user(karg, uarg, _IOC_SIZE(cmd)))
return -EFAULT; return -EFAULT;
return 0; return 0;
...@@ -388,8 +398,8 @@ static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd) ...@@ -388,8 +398,8 @@ static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd)
static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd) static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd)
{ {
/* All media IOCTLs are _IOWR() */ if ((_IOC_DIR(cmd) & _IOC_READ) &&
if (copy_to_user(uarg, karg, _IOC_SIZE(cmd))) copy_to_user(uarg, karg, _IOC_SIZE(cmd)))
return -EFAULT; return -EFAULT;
return 0; return 0;
...@@ -425,6 +435,7 @@ static const struct media_ioctl_info ioctl_info[] = { ...@@ -425,6 +435,7 @@ static const struct media_ioctl_info ioctl_info[] = {
MEDIA_IOC(ENUM_LINKS, media_device_enum_links, MEDIA_IOC_FL_GRAPH_MUTEX), 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(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(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, static long media_device_ioctl(struct file *filp, unsigned int cmd,
...@@ -691,9 +702,13 @@ void media_device_init(struct media_device *mdev) ...@@ -691,9 +702,13 @@ void media_device_init(struct media_device *mdev)
INIT_LIST_HEAD(&mdev->pads); INIT_LIST_HEAD(&mdev->pads);
INIT_LIST_HEAD(&mdev->links); INIT_LIST_HEAD(&mdev->links);
INIT_LIST_HEAD(&mdev->entity_notify); INIT_LIST_HEAD(&mdev->entity_notify);
mutex_init(&mdev->req_queue_mutex);
mutex_init(&mdev->graph_mutex); mutex_init(&mdev->graph_mutex);
ida_init(&mdev->entity_internal_idx); ida_init(&mdev->entity_internal_idx);
atomic_set(&mdev->request_id, 0);
dev_dbg(mdev->dev, "Media device initialized\n"); dev_dbg(mdev->dev, "Media device initialized\n");
} }
EXPORT_SYMBOL_GPL(media_device_init); EXPORT_SYMBOL_GPL(media_device_init);
...@@ -704,6 +719,7 @@ void media_device_cleanup(struct media_device *mdev) ...@@ -704,6 +719,7 @@ void media_device_cleanup(struct media_device *mdev)
mdev->entity_internal_idx_max = 0; mdev->entity_internal_idx_max = 0;
media_graph_walk_cleanup(&mdev->pm_count_walk); media_graph_walk_cleanup(&mdev->pm_count_walk);
mutex_destroy(&mdev->graph_mutex); mutex_destroy(&mdev->graph_mutex);
mutex_destroy(&mdev->req_queue_mutex);
} }
EXPORT_SYMBOL_GPL(media_device_cleanup); EXPORT_SYMBOL_GPL(media_device_cleanup);
......
This diff is collapsed.
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
struct ida; struct ida;
struct device; struct device;
struct media_device;
/** /**
* struct media_entity_notify - Media Entity Notify * struct media_entity_notify - Media Entity Notify
...@@ -50,10 +51,32 @@ struct media_entity_notify { ...@@ -50,10 +51,32 @@ struct media_entity_notify {
* struct media_device_ops - Media device operations * struct media_device_ops - Media device operations
* @link_notify: Link state change notification callback. This callback is * @link_notify: Link state change notification callback. This callback is
* called with the graph_mutex held. * called with the graph_mutex held.
* @req_alloc: Allocate a request. Set this if you need to allocate a struct
* larger then struct media_request. @req_alloc and @req_free must
* either both be set or both be NULL.
* @req_free: Free a request. Set this if @req_alloc was set as well, leave
* to NULL otherwise.
* @req_validate: Validate a request, but do not queue yet. The req_queue_mutex
* lock is held when this op is called.
* @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.
* The req_queue_mutex lock is held when this op is called.
* It is important that vb2 buffer objects are queued last after
* all other object types are queued: queueing a buffer kickstarts
* the request processing, so all other objects related to the
* request (and thus the buffer) must be available to the driver.
* And once a buffer is queued, then the driver can complete
* or delete objects from the request before req_queue exits.
*/ */
struct media_device_ops { struct media_device_ops {
int (*link_notify)(struct media_link *link, u32 flags, int (*link_notify)(struct media_link *link, u32 flags,
unsigned int notification); 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 +111,9 @@ struct media_device_ops { ...@@ -88,6 +111,9 @@ struct media_device_ops {
* @disable_source: Disable Source Handler function pointer * @disable_source: Disable Source Handler function pointer
* *
* @ops: Operation handler callbacks * @ops: Operation handler callbacks
* @req_queue_mutex: Serialise the MEDIA_REQUEST_IOC_QUEUE ioctl w.r.t.
* other operations that stop or start streaming.
* @request_id: Used to generate unique request IDs
* *
* This structure represents an abstract high-level media device. It allows easy * This structure represents an abstract high-level media device. It allows easy
* access to entities and provides basic media device-level support. The * access to entities and provides basic media device-level support. The
...@@ -158,6 +184,9 @@ struct media_device { ...@@ -158,6 +184,9 @@ struct media_device {
void (*disable_source)(struct media_entity *entity); void (*disable_source)(struct media_entity *entity);
const struct media_device_ops *ops; const struct media_device_ops *ops;
struct mutex req_queue_mutex;
atomic_t request_id;
}; };
/* We don't need to include pci.h or usb.h here */ /* We don't need to include pci.h or usb.h here */
......
// SPDX-License-Identifier: GPL-2.0
/*
* 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@cisco.com>
* Author: Sakari Ailus <sakari.ailus@linux.intel.com>
*/
#ifndef MEDIA_REQUEST_H
#define MEDIA_REQUEST_H
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/refcount.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
* @MEDIA_REQUEST_STATE_UPDATING: The request is being updated, i.e.
* request objects are being added,
* modified or removed
* @NR_OF_MEDIA_REQUEST_STATE: The number of media request states, used
* internally for sanity check purposes
*/
enum media_request_state {
MEDIA_REQUEST_STATE_IDLE,
MEDIA_REQUEST_STATE_VALIDATING,
MEDIA_REQUEST_STATE_QUEUED,
MEDIA_REQUEST_STATE_COMPLETE,
MEDIA_REQUEST_STATE_CLEANING,
MEDIA_REQUEST_STATE_UPDATING,
NR_OF_MEDIA_REQUEST_STATE,
};
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
* @updating_count: count the number of request updates that are in progress
* @objects: List of @struct media_request_object request objects
* @num_incomplete_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];
enum media_request_state state;
unsigned int updating_count;
struct list_head objects;
unsigned int num_incomplete_objects;
struct wait_queue_head poll_wait;
spinlock_t lock;
};
#ifdef CONFIG_MEDIA_CONTROLLER
/**
* media_request_lock_for_update - Lock the request for updating its objects
*
* @req: The media request
*
* Use before updating a request, i.e. adding, modifying or removing a request
* object in it. A reference to the request must be held during the update. This
* usually takes place automatically through a file handle. Use
* @media_request_unlock_for_update when done.
*/
static inline int __must_check
media_request_lock_for_update(struct media_request *req)
{
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&req->lock, flags);
if (req->state == MEDIA_REQUEST_STATE_IDLE ||
req->state == MEDIA_REQUEST_STATE_UPDATING) {
req->state = MEDIA_REQUEST_STATE_UPDATING;
req->updating_count++;
} else {
ret = -EBUSY;
}
spin_unlock_irqrestore(&req->lock, flags);
return ret;
}
/**
* media_request_unlock_for_update - Unlock a request previously locked for
* update
*
* @req: The media request
*
* Unlock a request that has previously been locked using
* @media_request_lock_for_update.
*/
static inline void media_request_unlock_for_update(struct media_request *req)
{
unsigned long flags;
spin_lock_irqsave(&req->lock, flags);
WARN_ON(req->updating_count <= 0);
if (!--req->updating_count)
req->state = MEDIA_REQUEST_STATE_IDLE;
spin_unlock_irqrestore(&req->lock, flags);
}
/**
* media_request_get - Get the media request
*
* @req: The media 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 media 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_fd: Store the request's file descriptor in this int
*
* Allocated the media request and put the fd in @alloc_fd.
*/
int media_request_alloc(struct media_device *mdev,
int *alloc_fd);
#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 always embedded in
* another struct that contains the actual data for this request object.
*/
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
*
* @obj: The 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
*
* @req: The media request
* @ops: The object ops for this object
* @priv: A driver-specific priv pointer associated with this object
* @is_buffer: Set to true if the object a buffer object.
* @obj: The object
*
* Bind this object to the request and set the ops and priv values of
* the object so it can be found later with media_request_object_find().
*
* Every bound object must be unbound or completed by the kernel at some
* point in time, otherwise the request will never complete. When the
* request is released all completed objects will be unbound by the
* request core code.
*
* Buffer objects will be added to the end of the request's object
* list, non-buffer objects will be added to the front of the list.
* This ensures that all buffer objects are at the end of the list
* and that all non-buffer objects that they depend on are processed
* first.
*/
int media_request_object_bind(struct media_request *req,
const struct media_request_object_ops *ops,
void *priv, bool is_buffer,
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. Only bound objects can
* be completed.
*/
void media_request_object_complete(struct media_request_object *obj);
#else
static inline int __must_check
media_request_lock_for_update(struct media_request *req)
{
return -EINVAL;
}
static inline void media_request_unlock_for_update(struct media_request *req)
{
}
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, bool is_buffer,
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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment