18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Media device request objects
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
68c2ecf20Sopenharmony_ci * Copyright (C) 2018 Intel Corporation
78c2ecf20Sopenharmony_ci * Copyright (C) 2018 Google, Inc.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Author: Hans Verkuil <hans.verkuil@cisco.com>
108c2ecf20Sopenharmony_ci * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/anon_inodes.h>
148c2ecf20Sopenharmony_ci#include <linux/file.h>
158c2ecf20Sopenharmony_ci#include <linux/refcount.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <media/media-device.h>
188c2ecf20Sopenharmony_ci#include <media/media-request.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic const char * const request_state[] = {
218c2ecf20Sopenharmony_ci	[MEDIA_REQUEST_STATE_IDLE]	 = "idle",
228c2ecf20Sopenharmony_ci	[MEDIA_REQUEST_STATE_VALIDATING] = "validating",
238c2ecf20Sopenharmony_ci	[MEDIA_REQUEST_STATE_QUEUED]	 = "queued",
248c2ecf20Sopenharmony_ci	[MEDIA_REQUEST_STATE_COMPLETE]	 = "complete",
258c2ecf20Sopenharmony_ci	[MEDIA_REQUEST_STATE_CLEANING]	 = "cleaning",
268c2ecf20Sopenharmony_ci	[MEDIA_REQUEST_STATE_UPDATING]	 = "updating",
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic const char *
308c2ecf20Sopenharmony_cimedia_request_state_str(enum media_request_state state)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(request_state) != NR_OF_MEDIA_REQUEST_STATE);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	if (WARN_ON(state >= ARRAY_SIZE(request_state)))
358c2ecf20Sopenharmony_ci		return "invalid";
368c2ecf20Sopenharmony_ci	return request_state[state];
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void media_request_clean(struct media_request *req)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct media_request_object *obj, *obj_safe;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* Just a sanity check. No other code path is allowed to change this. */
448c2ecf20Sopenharmony_ci	WARN_ON(req->state != MEDIA_REQUEST_STATE_CLEANING);
458c2ecf20Sopenharmony_ci	WARN_ON(req->updating_count);
468c2ecf20Sopenharmony_ci	WARN_ON(req->access_count);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
498c2ecf20Sopenharmony_ci		media_request_object_unbind(obj);
508c2ecf20Sopenharmony_ci		media_request_object_put(obj);
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	req->updating_count = 0;
548c2ecf20Sopenharmony_ci	req->access_count = 0;
558c2ecf20Sopenharmony_ci	WARN_ON(req->num_incomplete_objects);
568c2ecf20Sopenharmony_ci	req->num_incomplete_objects = 0;
578c2ecf20Sopenharmony_ci	wake_up_interruptible_all(&req->poll_wait);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void media_request_release(struct kref *kref)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct media_request *req =
638c2ecf20Sopenharmony_ci		container_of(kref, struct media_request, kref);
648c2ecf20Sopenharmony_ci	struct media_device *mdev = req->mdev;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	dev_dbg(mdev->dev, "request: release %s\n", req->debug_str);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* No other users, no need for a spinlock */
698c2ecf20Sopenharmony_ci	req->state = MEDIA_REQUEST_STATE_CLEANING;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	media_request_clean(req);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (mdev->ops->req_free)
748c2ecf20Sopenharmony_ci		mdev->ops->req_free(req);
758c2ecf20Sopenharmony_ci	else
768c2ecf20Sopenharmony_ci		kfree(req);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_civoid media_request_put(struct media_request *req)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	kref_put(&req->kref, media_request_release);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_put);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int media_request_close(struct inode *inode, struct file *filp)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct media_request *req = filp->private_data;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	media_request_put(req);
908c2ecf20Sopenharmony_ci	return 0;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic __poll_t media_request_poll(struct file *filp,
948c2ecf20Sopenharmony_ci				   struct poll_table_struct *wait)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct media_request *req = filp->private_data;
978c2ecf20Sopenharmony_ci	unsigned long flags;
988c2ecf20Sopenharmony_ci	__poll_t ret = 0;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (!(poll_requested_events(wait) & EPOLLPRI))
1018c2ecf20Sopenharmony_ci		return 0;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	poll_wait(filp, &req->poll_wait, wait);
1048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
1058c2ecf20Sopenharmony_ci	if (req->state == MEDIA_REQUEST_STATE_COMPLETE) {
1068c2ecf20Sopenharmony_ci		ret = EPOLLPRI;
1078c2ecf20Sopenharmony_ci		goto unlock;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci	if (req->state != MEDIA_REQUEST_STATE_QUEUED) {
1108c2ecf20Sopenharmony_ci		ret = EPOLLERR;
1118c2ecf20Sopenharmony_ci		goto unlock;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ciunlock:
1158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
1168c2ecf20Sopenharmony_ci	return ret;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic long media_request_ioctl_queue(struct media_request *req)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct media_device *mdev = req->mdev;
1228c2ecf20Sopenharmony_ci	enum media_request_state state;
1238c2ecf20Sopenharmony_ci	unsigned long flags;
1248c2ecf20Sopenharmony_ci	int ret;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	dev_dbg(mdev->dev, "request: queue %s\n", req->debug_str);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/*
1298c2ecf20Sopenharmony_ci	 * Ensure the request that is validated will be the one that gets queued
1308c2ecf20Sopenharmony_ci	 * next by serialising the queueing process. This mutex is also used
1318c2ecf20Sopenharmony_ci	 * to serialize with canceling a vb2 queue and with setting values such
1328c2ecf20Sopenharmony_ci	 * as controls in a request.
1338c2ecf20Sopenharmony_ci	 */
1348c2ecf20Sopenharmony_ci	mutex_lock(&mdev->req_queue_mutex);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	media_request_get(req);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
1398c2ecf20Sopenharmony_ci	if (req->state == MEDIA_REQUEST_STATE_IDLE)
1408c2ecf20Sopenharmony_ci		req->state = MEDIA_REQUEST_STATE_VALIDATING;
1418c2ecf20Sopenharmony_ci	state = req->state;
1428c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
1438c2ecf20Sopenharmony_ci	if (state != MEDIA_REQUEST_STATE_VALIDATING) {
1448c2ecf20Sopenharmony_ci		dev_dbg(mdev->dev,
1458c2ecf20Sopenharmony_ci			"request: unable to queue %s, request in state %s\n",
1468c2ecf20Sopenharmony_ci			req->debug_str, media_request_state_str(state));
1478c2ecf20Sopenharmony_ci		media_request_put(req);
1488c2ecf20Sopenharmony_ci		mutex_unlock(&mdev->req_queue_mutex);
1498c2ecf20Sopenharmony_ci		return -EBUSY;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	ret = mdev->ops->req_validate(req);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/*
1558c2ecf20Sopenharmony_ci	 * If the req_validate was successful, then we mark the state as QUEUED
1568c2ecf20Sopenharmony_ci	 * and call req_queue. The reason we set the state first is that this
1578c2ecf20Sopenharmony_ci	 * allows req_queue to unbind or complete the queued objects in case
1588c2ecf20Sopenharmony_ci	 * they are immediately 'consumed'. State changes from QUEUED to another
1598c2ecf20Sopenharmony_ci	 * state can only happen if either the driver changes the state or if
1608c2ecf20Sopenharmony_ci	 * the user cancels the vb2 queue. The driver can only change the state
1618c2ecf20Sopenharmony_ci	 * after each object is queued through the req_queue op (and note that
1628c2ecf20Sopenharmony_ci	 * that op cannot fail), so setting the state to QUEUED up front is
1638c2ecf20Sopenharmony_ci	 * safe.
1648c2ecf20Sopenharmony_ci	 *
1658c2ecf20Sopenharmony_ci	 * The other reason for changing the state is if the vb2 queue is
1668c2ecf20Sopenharmony_ci	 * canceled, and that uses the req_queue_mutex which is still locked
1678c2ecf20Sopenharmony_ci	 * while req_queue is called, so that's safe as well.
1688c2ecf20Sopenharmony_ci	 */
1698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
1708c2ecf20Sopenharmony_ci	req->state = ret ? MEDIA_REQUEST_STATE_IDLE
1718c2ecf20Sopenharmony_ci			 : MEDIA_REQUEST_STATE_QUEUED;
1728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (!ret)
1758c2ecf20Sopenharmony_ci		mdev->ops->req_queue(req);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	mutex_unlock(&mdev->req_queue_mutex);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (ret) {
1808c2ecf20Sopenharmony_ci		dev_dbg(mdev->dev, "request: can't queue %s (%d)\n",
1818c2ecf20Sopenharmony_ci			req->debug_str, ret);
1828c2ecf20Sopenharmony_ci		media_request_put(req);
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return ret;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic long media_request_ioctl_reinit(struct media_request *req)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct media_device *mdev = req->mdev;
1918c2ecf20Sopenharmony_ci	unsigned long flags;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
1948c2ecf20Sopenharmony_ci	if (req->state != MEDIA_REQUEST_STATE_IDLE &&
1958c2ecf20Sopenharmony_ci	    req->state != MEDIA_REQUEST_STATE_COMPLETE) {
1968c2ecf20Sopenharmony_ci		dev_dbg(mdev->dev,
1978c2ecf20Sopenharmony_ci			"request: %s not in idle or complete state, cannot reinit\n",
1988c2ecf20Sopenharmony_ci			req->debug_str);
1998c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&req->lock, flags);
2008c2ecf20Sopenharmony_ci		return -EBUSY;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	if (req->access_count) {
2038c2ecf20Sopenharmony_ci		dev_dbg(mdev->dev,
2048c2ecf20Sopenharmony_ci			"request: %s is being accessed, cannot reinit\n",
2058c2ecf20Sopenharmony_ci			req->debug_str);
2068c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&req->lock, flags);
2078c2ecf20Sopenharmony_ci		return -EBUSY;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci	req->state = MEDIA_REQUEST_STATE_CLEANING;
2108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	media_request_clean(req);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
2158c2ecf20Sopenharmony_ci	req->state = MEDIA_REQUEST_STATE_IDLE;
2168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return 0;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic long media_request_ioctl(struct file *filp, unsigned int cmd,
2228c2ecf20Sopenharmony_ci				unsigned long arg)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct media_request *req = filp->private_data;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	switch (cmd) {
2278c2ecf20Sopenharmony_ci	case MEDIA_REQUEST_IOC_QUEUE:
2288c2ecf20Sopenharmony_ci		return media_request_ioctl_queue(req);
2298c2ecf20Sopenharmony_ci	case MEDIA_REQUEST_IOC_REINIT:
2308c2ecf20Sopenharmony_ci		return media_request_ioctl_reinit(req);
2318c2ecf20Sopenharmony_ci	default:
2328c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic const struct file_operations request_fops = {
2378c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
2388c2ecf20Sopenharmony_ci	.poll = media_request_poll,
2398c2ecf20Sopenharmony_ci	.unlocked_ioctl = media_request_ioctl,
2408c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
2418c2ecf20Sopenharmony_ci	.compat_ioctl = media_request_ioctl,
2428c2ecf20Sopenharmony_ci#endif /* CONFIG_COMPAT */
2438c2ecf20Sopenharmony_ci	.release = media_request_close,
2448c2ecf20Sopenharmony_ci};
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistruct media_request *
2478c2ecf20Sopenharmony_cimedia_request_get_by_fd(struct media_device *mdev, int request_fd)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct fd f;
2508c2ecf20Sopenharmony_ci	struct media_request *req;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (!mdev || !mdev->ops ||
2538c2ecf20Sopenharmony_ci	    !mdev->ops->req_validate || !mdev->ops->req_queue)
2548c2ecf20Sopenharmony_ci		return ERR_PTR(-EBADR);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	f = fdget(request_fd);
2578c2ecf20Sopenharmony_ci	if (!f.file)
2588c2ecf20Sopenharmony_ci		goto err_no_req_fd;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (f.file->f_op != &request_fops)
2618c2ecf20Sopenharmony_ci		goto err_fput;
2628c2ecf20Sopenharmony_ci	req = f.file->private_data;
2638c2ecf20Sopenharmony_ci	if (req->mdev != mdev)
2648c2ecf20Sopenharmony_ci		goto err_fput;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/*
2678c2ecf20Sopenharmony_ci	 * Note: as long as someone has an open filehandle of the request,
2688c2ecf20Sopenharmony_ci	 * the request can never be released. The fdget() above ensures that
2698c2ecf20Sopenharmony_ci	 * even if userspace closes the request filehandle, the release()
2708c2ecf20Sopenharmony_ci	 * fop won't be called, so the media_request_get() always succeeds
2718c2ecf20Sopenharmony_ci	 * and there is no race condition where the request was released
2728c2ecf20Sopenharmony_ci	 * before media_request_get() is called.
2738c2ecf20Sopenharmony_ci	 */
2748c2ecf20Sopenharmony_ci	media_request_get(req);
2758c2ecf20Sopenharmony_ci	fdput(f);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	return req;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cierr_fput:
2808c2ecf20Sopenharmony_ci	fdput(f);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cierr_no_req_fd:
2838c2ecf20Sopenharmony_ci	dev_dbg(mdev->dev, "cannot find request_fd %d\n", request_fd);
2848c2ecf20Sopenharmony_ci	return ERR_PTR(-EINVAL);
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_get_by_fd);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ciint media_request_alloc(struct media_device *mdev, int *alloc_fd)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct media_request *req;
2918c2ecf20Sopenharmony_ci	struct file *filp;
2928c2ecf20Sopenharmony_ci	int fd;
2938c2ecf20Sopenharmony_ci	int ret;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* Either both are NULL or both are non-NULL */
2968c2ecf20Sopenharmony_ci	if (WARN_ON(!mdev->ops->req_alloc ^ !mdev->ops->req_free))
2978c2ecf20Sopenharmony_ci		return -ENOMEM;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (mdev->ops->req_alloc)
3008c2ecf20Sopenharmony_ci		req = mdev->ops->req_alloc(mdev);
3018c2ecf20Sopenharmony_ci	else
3028c2ecf20Sopenharmony_ci		req = kzalloc(sizeof(*req), GFP_KERNEL);
3038c2ecf20Sopenharmony_ci	if (!req)
3048c2ecf20Sopenharmony_ci		return -ENOMEM;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	fd = get_unused_fd_flags(O_CLOEXEC);
3078c2ecf20Sopenharmony_ci	if (fd < 0) {
3088c2ecf20Sopenharmony_ci		ret = fd;
3098c2ecf20Sopenharmony_ci		goto err_free_req;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC);
3138c2ecf20Sopenharmony_ci	if (IS_ERR(filp)) {
3148c2ecf20Sopenharmony_ci		ret = PTR_ERR(filp);
3158c2ecf20Sopenharmony_ci		goto err_put_fd;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	filp->private_data = req;
3198c2ecf20Sopenharmony_ci	req->mdev = mdev;
3208c2ecf20Sopenharmony_ci	req->state = MEDIA_REQUEST_STATE_IDLE;
3218c2ecf20Sopenharmony_ci	req->num_incomplete_objects = 0;
3228c2ecf20Sopenharmony_ci	kref_init(&req->kref);
3238c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&req->objects);
3248c2ecf20Sopenharmony_ci	spin_lock_init(&req->lock);
3258c2ecf20Sopenharmony_ci	init_waitqueue_head(&req->poll_wait);
3268c2ecf20Sopenharmony_ci	req->updating_count = 0;
3278c2ecf20Sopenharmony_ci	req->access_count = 0;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	*alloc_fd = fd;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	snprintf(req->debug_str, sizeof(req->debug_str), "%u:%d",
3328c2ecf20Sopenharmony_ci		 atomic_inc_return(&mdev->request_id), fd);
3338c2ecf20Sopenharmony_ci	dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	fd_install(fd, filp);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	return 0;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cierr_put_fd:
3408c2ecf20Sopenharmony_ci	put_unused_fd(fd);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cierr_free_req:
3438c2ecf20Sopenharmony_ci	if (mdev->ops->req_free)
3448c2ecf20Sopenharmony_ci		mdev->ops->req_free(req);
3458c2ecf20Sopenharmony_ci	else
3468c2ecf20Sopenharmony_ci		kfree(req);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return ret;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic void media_request_object_release(struct kref *kref)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct media_request_object *obj =
3548c2ecf20Sopenharmony_ci		container_of(kref, struct media_request_object, kref);
3558c2ecf20Sopenharmony_ci	struct media_request *req = obj->req;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (WARN_ON(req))
3588c2ecf20Sopenharmony_ci		media_request_object_unbind(obj);
3598c2ecf20Sopenharmony_ci	obj->ops->release(obj);
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistruct media_request_object *
3638c2ecf20Sopenharmony_cimedia_request_object_find(struct media_request *req,
3648c2ecf20Sopenharmony_ci			  const struct media_request_object_ops *ops,
3658c2ecf20Sopenharmony_ci			  void *priv)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct media_request_object *obj;
3688c2ecf20Sopenharmony_ci	struct media_request_object *found = NULL;
3698c2ecf20Sopenharmony_ci	unsigned long flags;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (WARN_ON(!ops || !priv))
3728c2ecf20Sopenharmony_ci		return NULL;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
3758c2ecf20Sopenharmony_ci	list_for_each_entry(obj, &req->objects, list) {
3768c2ecf20Sopenharmony_ci		if (obj->ops == ops && obj->priv == priv) {
3778c2ecf20Sopenharmony_ci			media_request_object_get(obj);
3788c2ecf20Sopenharmony_ci			found = obj;
3798c2ecf20Sopenharmony_ci			break;
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
3838c2ecf20Sopenharmony_ci	return found;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_find);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_civoid media_request_object_put(struct media_request_object *obj)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	kref_put(&obj->kref, media_request_object_release);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_put);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_civoid media_request_object_init(struct media_request_object *obj)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	obj->ops = NULL;
3968c2ecf20Sopenharmony_ci	obj->req = NULL;
3978c2ecf20Sopenharmony_ci	obj->priv = NULL;
3988c2ecf20Sopenharmony_ci	obj->completed = false;
3998c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&obj->list);
4008c2ecf20Sopenharmony_ci	kref_init(&obj->kref);
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_init);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ciint media_request_object_bind(struct media_request *req,
4058c2ecf20Sopenharmony_ci			      const struct media_request_object_ops *ops,
4068c2ecf20Sopenharmony_ci			      void *priv, bool is_buffer,
4078c2ecf20Sopenharmony_ci			      struct media_request_object *obj)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	unsigned long flags;
4108c2ecf20Sopenharmony_ci	int ret = -EBUSY;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	if (WARN_ON(!ops->release))
4138c2ecf20Sopenharmony_ci		return -EBADR;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_UPDATING))
4188c2ecf20Sopenharmony_ci		goto unlock;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	obj->req = req;
4218c2ecf20Sopenharmony_ci	obj->ops = ops;
4228c2ecf20Sopenharmony_ci	obj->priv = priv;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (is_buffer)
4258c2ecf20Sopenharmony_ci		list_add_tail(&obj->list, &req->objects);
4268c2ecf20Sopenharmony_ci	else
4278c2ecf20Sopenharmony_ci		list_add(&obj->list, &req->objects);
4288c2ecf20Sopenharmony_ci	req->num_incomplete_objects++;
4298c2ecf20Sopenharmony_ci	ret = 0;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ciunlock:
4328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
4338c2ecf20Sopenharmony_ci	return ret;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_bind);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_civoid media_request_object_unbind(struct media_request_object *obj)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct media_request *req = obj->req;
4408c2ecf20Sopenharmony_ci	unsigned long flags;
4418c2ecf20Sopenharmony_ci	bool completed = false;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (WARN_ON(!req))
4448c2ecf20Sopenharmony_ci		return;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
4478c2ecf20Sopenharmony_ci	list_del(&obj->list);
4488c2ecf20Sopenharmony_ci	obj->req = NULL;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (req->state == MEDIA_REQUEST_STATE_COMPLETE)
4518c2ecf20Sopenharmony_ci		goto unlock;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (WARN_ON(req->state == MEDIA_REQUEST_STATE_VALIDATING))
4548c2ecf20Sopenharmony_ci		goto unlock;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	if (req->state == MEDIA_REQUEST_STATE_CLEANING) {
4578c2ecf20Sopenharmony_ci		if (!obj->completed)
4588c2ecf20Sopenharmony_ci			req->num_incomplete_objects--;
4598c2ecf20Sopenharmony_ci		goto unlock;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (WARN_ON(!req->num_incomplete_objects))
4638c2ecf20Sopenharmony_ci		goto unlock;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	req->num_incomplete_objects--;
4668c2ecf20Sopenharmony_ci	if (req->state == MEDIA_REQUEST_STATE_QUEUED &&
4678c2ecf20Sopenharmony_ci	    !req->num_incomplete_objects) {
4688c2ecf20Sopenharmony_ci		req->state = MEDIA_REQUEST_STATE_COMPLETE;
4698c2ecf20Sopenharmony_ci		completed = true;
4708c2ecf20Sopenharmony_ci		wake_up_interruptible_all(&req->poll_wait);
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ciunlock:
4748c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
4758c2ecf20Sopenharmony_ci	if (obj->ops->unbind)
4768c2ecf20Sopenharmony_ci		obj->ops->unbind(obj);
4778c2ecf20Sopenharmony_ci	if (completed)
4788c2ecf20Sopenharmony_ci		media_request_put(req);
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_unbind);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_civoid media_request_object_complete(struct media_request_object *obj)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct media_request *req = obj->req;
4858c2ecf20Sopenharmony_ci	unsigned long flags;
4868c2ecf20Sopenharmony_ci	bool completed = false;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
4898c2ecf20Sopenharmony_ci	if (obj->completed)
4908c2ecf20Sopenharmony_ci		goto unlock;
4918c2ecf20Sopenharmony_ci	obj->completed = true;
4928c2ecf20Sopenharmony_ci	if (WARN_ON(!req->num_incomplete_objects) ||
4938c2ecf20Sopenharmony_ci	    WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED))
4948c2ecf20Sopenharmony_ci		goto unlock;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	if (!--req->num_incomplete_objects) {
4978c2ecf20Sopenharmony_ci		req->state = MEDIA_REQUEST_STATE_COMPLETE;
4988c2ecf20Sopenharmony_ci		wake_up_interruptible_all(&req->poll_wait);
4998c2ecf20Sopenharmony_ci		completed = true;
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ciunlock:
5028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
5038c2ecf20Sopenharmony_ci	if (completed)
5048c2ecf20Sopenharmony_ci		media_request_put(req);
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_complete);
507