| // 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 | |
| * @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); | |
| /** | |
| * 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 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 |