mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	The portable type for a wait queue is wait_queue_head_t. Signed-off-by: Jasmin Jessich <jasmin@anw.at> Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
		
			
				
	
	
		
			442 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			442 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// 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 -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
 |