| // 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; | 
 | 	struct wait_queue_head 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 -EACCES 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(-EACCES); | 
 | } | 
 |  | 
 | #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 |