forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
454 lines
12 KiB
454 lines
12 KiB
// 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 <[email protected]> |
|
* Author: Sakari Ailus <[email protected]> |
|
*/ |
|
|
|
#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 |
|
* @access_count: count the number of request accesses 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; |
|
unsigned int access_count; |
|
struct list_head objects; |
|
unsigned int num_incomplete_objects; |
|
wait_queue_head_t poll_wait; |
|
spinlock_t lock; |
|
}; |
|
|
|
#ifdef CONFIG_MEDIA_CONTROLLER |
|
|
|
/** |
|
* media_request_lock_for_access - Lock the request to access its objects |
|
* |
|
* @req: The media request |
|
* |
|
* Use before accessing a completed request. A reference to the request must |
|
* be held during the access. This usually takes place automatically through |
|
* a file handle. Use @media_request_unlock_for_access when done. |
|
*/ |
|
static inline int __must_check |
|
media_request_lock_for_access(struct media_request *req) |
|
{ |
|
unsigned long flags; |
|
int ret = -EBUSY; |
|
|
|
spin_lock_irqsave(&req->lock, flags); |
|
if (req->state == MEDIA_REQUEST_STATE_COMPLETE) { |
|
req->access_count++; |
|
ret = 0; |
|
} |
|
spin_unlock_irqrestore(&req->lock, flags); |
|
|
|
return ret; |
|
} |
|
|
|
/** |
|
* media_request_unlock_for_access - Unlock a request previously locked for |
|
* access |
|
* |
|
* @req: The media request |
|
* |
|
* Unlock a request that has previously been locked using |
|
* @media_request_lock_for_access. |
|
*/ |
|
static inline void media_request_unlock_for_access(struct media_request *req) |
|
{ |
|
unsigned long flags; |
|
|
|
spin_lock_irqsave(&req->lock, flags); |
|
if (!WARN_ON(!req->access_count)) |
|
req->access_count--; |
|
spin_unlock_irqrestore(&req->lock, flags); |
|
} |
|
|
|
/** |
|
* 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); |
|
|
|
void media_request_pin(struct media_request *req); |
|
|
|
void media_request_unpin(struct media_request *req); |
|
|
|
/** |
|
* media_request_get_by_fd - Get a media request by fd |
|
* |
|
* @mdev: Media device this request belongs to |
|
* @request_fd: The file descriptor of the request |
|
* |
|
* Get the request represented by @request_fd that is owned |
|
* by the media device. |
|
* |
|
* Return a -EBADR error pointer if requests are not supported |
|
* by this driver. Return -EINVAL if the request was not found. |
|
* Return the pointer to the request if found: the caller will |
|
* have to call @media_request_put when it finished using the |
|
* request. |
|
*/ |
|
struct media_request * |
|
media_request_get_by_fd(struct media_device *mdev, int request_fd); |
|
|
|
/** |
|
* 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) |
|
{ |
|
} |
|
|
|
static inline void media_request_pin(struct media_request *req) |
|
{ |
|
} |
|
|
|
static inline void media_request_unpin(struct media_request *req) |
|
{ |
|
} |
|
|
|
static inline struct media_request * |
|
media_request_get_by_fd(struct media_device *mdev, int request_fd) |
|
{ |
|
return ERR_PTR(-EBADR); |
|
} |
|
|
|
#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_find - Find an object in a request |
|
* |
|
* @req: The media request |
|
* @ops: Find an object with this ops value |
|
* @priv: Find an object with this priv value |
|
* |
|
* Both @ops and @priv must be non-NULL. |
|
* |
|
* Returns the object pointer or NULL if not found. The caller must |
|
* call media_request_object_put() once it finished using the object. |
|
* |
|
* Since this function needs to walk the list of objects it takes |
|
* the @req->lock spin lock to make this safe. |
|
*/ |
|
struct media_request_object * |
|
media_request_object_find(struct media_request *req, |
|
const struct media_request_object_ops *ops, |
|
void *priv); |
|
|
|
/** |
|
* 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_access(struct media_request *req) |
|
{ |
|
return -EINVAL; |
|
} |
|
|
|
static inline void media_request_unlock_for_access(struct media_request *req) |
|
{ |
|
} |
|
|
|
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 struct media_request_object * |
|
media_request_object_find(struct media_request *req, |
|
const struct media_request_object_ops *ops, |
|
void *priv) |
|
{ |
|
return NULL; |
|
} |
|
|
|
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
|
|
|