162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Media device request objects
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
662306a36Sopenharmony_ci * Copyright (C) 2018 Intel Corporation
762306a36Sopenharmony_ci * Copyright (C) 2018 Google, Inc.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Author: Hans Verkuil <hans.verkuil@cisco.com>
1062306a36Sopenharmony_ci * Author: Sakari Ailus <sakari.ailus@linux.intel.com>
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/anon_inodes.h>
1462306a36Sopenharmony_ci#include <linux/file.h>
1562306a36Sopenharmony_ci#include <linux/refcount.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <media/media-device.h>
1862306a36Sopenharmony_ci#include <media/media-request.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic const char * const request_state[] = {
2162306a36Sopenharmony_ci	[MEDIA_REQUEST_STATE_IDLE]	 = "idle",
2262306a36Sopenharmony_ci	[MEDIA_REQUEST_STATE_VALIDATING] = "validating",
2362306a36Sopenharmony_ci	[MEDIA_REQUEST_STATE_QUEUED]	 = "queued",
2462306a36Sopenharmony_ci	[MEDIA_REQUEST_STATE_COMPLETE]	 = "complete",
2562306a36Sopenharmony_ci	[MEDIA_REQUEST_STATE_CLEANING]	 = "cleaning",
2662306a36Sopenharmony_ci	[MEDIA_REQUEST_STATE_UPDATING]	 = "updating",
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic const char *
3062306a36Sopenharmony_cimedia_request_state_str(enum media_request_state state)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(request_state) != NR_OF_MEDIA_REQUEST_STATE);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (WARN_ON(state >= ARRAY_SIZE(request_state)))
3562306a36Sopenharmony_ci		return "invalid";
3662306a36Sopenharmony_ci	return request_state[state];
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void media_request_clean(struct media_request *req)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct media_request_object *obj, *obj_safe;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* Just a sanity check. No other code path is allowed to change this. */
4462306a36Sopenharmony_ci	WARN_ON(req->state != MEDIA_REQUEST_STATE_CLEANING);
4562306a36Sopenharmony_ci	WARN_ON(req->updating_count);
4662306a36Sopenharmony_ci	WARN_ON(req->access_count);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
4962306a36Sopenharmony_ci		media_request_object_unbind(obj);
5062306a36Sopenharmony_ci		media_request_object_put(obj);
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	req->updating_count = 0;
5462306a36Sopenharmony_ci	req->access_count = 0;
5562306a36Sopenharmony_ci	WARN_ON(req->num_incomplete_objects);
5662306a36Sopenharmony_ci	req->num_incomplete_objects = 0;
5762306a36Sopenharmony_ci	wake_up_interruptible_all(&req->poll_wait);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic void media_request_release(struct kref *kref)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct media_request *req =
6362306a36Sopenharmony_ci		container_of(kref, struct media_request, kref);
6462306a36Sopenharmony_ci	struct media_device *mdev = req->mdev;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	dev_dbg(mdev->dev, "request: release %s\n", req->debug_str);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* No other users, no need for a spinlock */
6962306a36Sopenharmony_ci	req->state = MEDIA_REQUEST_STATE_CLEANING;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	media_request_clean(req);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (mdev->ops->req_free)
7462306a36Sopenharmony_ci		mdev->ops->req_free(req);
7562306a36Sopenharmony_ci	else
7662306a36Sopenharmony_ci		kfree(req);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_civoid media_request_put(struct media_request *req)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	kref_put(&req->kref, media_request_release);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_put);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int media_request_close(struct inode *inode, struct file *filp)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct media_request *req = filp->private_data;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	media_request_put(req);
9062306a36Sopenharmony_ci	return 0;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic __poll_t media_request_poll(struct file *filp,
9462306a36Sopenharmony_ci				   struct poll_table_struct *wait)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct media_request *req = filp->private_data;
9762306a36Sopenharmony_ci	unsigned long flags;
9862306a36Sopenharmony_ci	__poll_t ret = 0;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (!(poll_requested_events(wait) & EPOLLPRI))
10162306a36Sopenharmony_ci		return 0;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	poll_wait(filp, &req->poll_wait, wait);
10462306a36Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
10562306a36Sopenharmony_ci	if (req->state == MEDIA_REQUEST_STATE_COMPLETE) {
10662306a36Sopenharmony_ci		ret = EPOLLPRI;
10762306a36Sopenharmony_ci		goto unlock;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci	if (req->state != MEDIA_REQUEST_STATE_QUEUED) {
11062306a36Sopenharmony_ci		ret = EPOLLERR;
11162306a36Sopenharmony_ci		goto unlock;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciunlock:
11562306a36Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
11662306a36Sopenharmony_ci	return ret;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic long media_request_ioctl_queue(struct media_request *req)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct media_device *mdev = req->mdev;
12262306a36Sopenharmony_ci	enum media_request_state state;
12362306a36Sopenharmony_ci	unsigned long flags;
12462306a36Sopenharmony_ci	int ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	dev_dbg(mdev->dev, "request: queue %s\n", req->debug_str);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/*
12962306a36Sopenharmony_ci	 * Ensure the request that is validated will be the one that gets queued
13062306a36Sopenharmony_ci	 * next by serialising the queueing process. This mutex is also used
13162306a36Sopenharmony_ci	 * to serialize with canceling a vb2 queue and with setting values such
13262306a36Sopenharmony_ci	 * as controls in a request.
13362306a36Sopenharmony_ci	 */
13462306a36Sopenharmony_ci	mutex_lock(&mdev->req_queue_mutex);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	media_request_get(req);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
13962306a36Sopenharmony_ci	if (req->state == MEDIA_REQUEST_STATE_IDLE)
14062306a36Sopenharmony_ci		req->state = MEDIA_REQUEST_STATE_VALIDATING;
14162306a36Sopenharmony_ci	state = req->state;
14262306a36Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
14362306a36Sopenharmony_ci	if (state != MEDIA_REQUEST_STATE_VALIDATING) {
14462306a36Sopenharmony_ci		dev_dbg(mdev->dev,
14562306a36Sopenharmony_ci			"request: unable to queue %s, request in state %s\n",
14662306a36Sopenharmony_ci			req->debug_str, media_request_state_str(state));
14762306a36Sopenharmony_ci		media_request_put(req);
14862306a36Sopenharmony_ci		mutex_unlock(&mdev->req_queue_mutex);
14962306a36Sopenharmony_ci		return -EBUSY;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	ret = mdev->ops->req_validate(req);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/*
15562306a36Sopenharmony_ci	 * If the req_validate was successful, then we mark the state as QUEUED
15662306a36Sopenharmony_ci	 * and call req_queue. The reason we set the state first is that this
15762306a36Sopenharmony_ci	 * allows req_queue to unbind or complete the queued objects in case
15862306a36Sopenharmony_ci	 * they are immediately 'consumed'. State changes from QUEUED to another
15962306a36Sopenharmony_ci	 * state can only happen if either the driver changes the state or if
16062306a36Sopenharmony_ci	 * the user cancels the vb2 queue. The driver can only change the state
16162306a36Sopenharmony_ci	 * after each object is queued through the req_queue op (and note that
16262306a36Sopenharmony_ci	 * that op cannot fail), so setting the state to QUEUED up front is
16362306a36Sopenharmony_ci	 * safe.
16462306a36Sopenharmony_ci	 *
16562306a36Sopenharmony_ci	 * The other reason for changing the state is if the vb2 queue is
16662306a36Sopenharmony_ci	 * canceled, and that uses the req_queue_mutex which is still locked
16762306a36Sopenharmony_ci	 * while req_queue is called, so that's safe as well.
16862306a36Sopenharmony_ci	 */
16962306a36Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
17062306a36Sopenharmony_ci	req->state = ret ? MEDIA_REQUEST_STATE_IDLE
17162306a36Sopenharmony_ci			 : MEDIA_REQUEST_STATE_QUEUED;
17262306a36Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (!ret)
17562306a36Sopenharmony_ci		mdev->ops->req_queue(req);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	mutex_unlock(&mdev->req_queue_mutex);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (ret) {
18062306a36Sopenharmony_ci		dev_dbg(mdev->dev, "request: can't queue %s (%d)\n",
18162306a36Sopenharmony_ci			req->debug_str, ret);
18262306a36Sopenharmony_ci		media_request_put(req);
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return ret;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic long media_request_ioctl_reinit(struct media_request *req)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct media_device *mdev = req->mdev;
19162306a36Sopenharmony_ci	unsigned long flags;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
19462306a36Sopenharmony_ci	if (req->state != MEDIA_REQUEST_STATE_IDLE &&
19562306a36Sopenharmony_ci	    req->state != MEDIA_REQUEST_STATE_COMPLETE) {
19662306a36Sopenharmony_ci		dev_dbg(mdev->dev,
19762306a36Sopenharmony_ci			"request: %s not in idle or complete state, cannot reinit\n",
19862306a36Sopenharmony_ci			req->debug_str);
19962306a36Sopenharmony_ci		spin_unlock_irqrestore(&req->lock, flags);
20062306a36Sopenharmony_ci		return -EBUSY;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	if (req->access_count) {
20362306a36Sopenharmony_ci		dev_dbg(mdev->dev,
20462306a36Sopenharmony_ci			"request: %s is being accessed, cannot reinit\n",
20562306a36Sopenharmony_ci			req->debug_str);
20662306a36Sopenharmony_ci		spin_unlock_irqrestore(&req->lock, flags);
20762306a36Sopenharmony_ci		return -EBUSY;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	req->state = MEDIA_REQUEST_STATE_CLEANING;
21062306a36Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	media_request_clean(req);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
21562306a36Sopenharmony_ci	req->state = MEDIA_REQUEST_STATE_IDLE;
21662306a36Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return 0;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic long media_request_ioctl(struct file *filp, unsigned int cmd,
22262306a36Sopenharmony_ci				unsigned long arg)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct media_request *req = filp->private_data;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	switch (cmd) {
22762306a36Sopenharmony_ci	case MEDIA_REQUEST_IOC_QUEUE:
22862306a36Sopenharmony_ci		return media_request_ioctl_queue(req);
22962306a36Sopenharmony_ci	case MEDIA_REQUEST_IOC_REINIT:
23062306a36Sopenharmony_ci		return media_request_ioctl_reinit(req);
23162306a36Sopenharmony_ci	default:
23262306a36Sopenharmony_ci		return -ENOIOCTLCMD;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic const struct file_operations request_fops = {
23762306a36Sopenharmony_ci	.owner = THIS_MODULE,
23862306a36Sopenharmony_ci	.poll = media_request_poll,
23962306a36Sopenharmony_ci	.unlocked_ioctl = media_request_ioctl,
24062306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
24162306a36Sopenharmony_ci	.compat_ioctl = media_request_ioctl,
24262306a36Sopenharmony_ci#endif /* CONFIG_COMPAT */
24362306a36Sopenharmony_ci	.release = media_request_close,
24462306a36Sopenharmony_ci};
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistruct media_request *
24762306a36Sopenharmony_cimedia_request_get_by_fd(struct media_device *mdev, int request_fd)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct fd f;
25062306a36Sopenharmony_ci	struct media_request *req;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (!mdev || !mdev->ops ||
25362306a36Sopenharmony_ci	    !mdev->ops->req_validate || !mdev->ops->req_queue)
25462306a36Sopenharmony_ci		return ERR_PTR(-EBADR);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	f = fdget(request_fd);
25762306a36Sopenharmony_ci	if (!f.file)
25862306a36Sopenharmony_ci		goto err_no_req_fd;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (f.file->f_op != &request_fops)
26162306a36Sopenharmony_ci		goto err_fput;
26262306a36Sopenharmony_ci	req = f.file->private_data;
26362306a36Sopenharmony_ci	if (req->mdev != mdev)
26462306a36Sopenharmony_ci		goto err_fput;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/*
26762306a36Sopenharmony_ci	 * Note: as long as someone has an open filehandle of the request,
26862306a36Sopenharmony_ci	 * the request can never be released. The fdget() above ensures that
26962306a36Sopenharmony_ci	 * even if userspace closes the request filehandle, the release()
27062306a36Sopenharmony_ci	 * fop won't be called, so the media_request_get() always succeeds
27162306a36Sopenharmony_ci	 * and there is no race condition where the request was released
27262306a36Sopenharmony_ci	 * before media_request_get() is called.
27362306a36Sopenharmony_ci	 */
27462306a36Sopenharmony_ci	media_request_get(req);
27562306a36Sopenharmony_ci	fdput(f);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return req;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cierr_fput:
28062306a36Sopenharmony_ci	fdput(f);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cierr_no_req_fd:
28362306a36Sopenharmony_ci	dev_dbg(mdev->dev, "cannot find request_fd %d\n", request_fd);
28462306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_get_by_fd);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ciint media_request_alloc(struct media_device *mdev, int *alloc_fd)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct media_request *req;
29162306a36Sopenharmony_ci	struct file *filp;
29262306a36Sopenharmony_ci	int fd;
29362306a36Sopenharmony_ci	int ret;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* Either both are NULL or both are non-NULL */
29662306a36Sopenharmony_ci	if (WARN_ON(!mdev->ops->req_alloc ^ !mdev->ops->req_free))
29762306a36Sopenharmony_ci		return -ENOMEM;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (mdev->ops->req_alloc)
30062306a36Sopenharmony_ci		req = mdev->ops->req_alloc(mdev);
30162306a36Sopenharmony_ci	else
30262306a36Sopenharmony_ci		req = kzalloc(sizeof(*req), GFP_KERNEL);
30362306a36Sopenharmony_ci	if (!req)
30462306a36Sopenharmony_ci		return -ENOMEM;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	fd = get_unused_fd_flags(O_CLOEXEC);
30762306a36Sopenharmony_ci	if (fd < 0) {
30862306a36Sopenharmony_ci		ret = fd;
30962306a36Sopenharmony_ci		goto err_free_req;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC);
31362306a36Sopenharmony_ci	if (IS_ERR(filp)) {
31462306a36Sopenharmony_ci		ret = PTR_ERR(filp);
31562306a36Sopenharmony_ci		goto err_put_fd;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	filp->private_data = req;
31962306a36Sopenharmony_ci	req->mdev = mdev;
32062306a36Sopenharmony_ci	req->state = MEDIA_REQUEST_STATE_IDLE;
32162306a36Sopenharmony_ci	req->num_incomplete_objects = 0;
32262306a36Sopenharmony_ci	kref_init(&req->kref);
32362306a36Sopenharmony_ci	INIT_LIST_HEAD(&req->objects);
32462306a36Sopenharmony_ci	spin_lock_init(&req->lock);
32562306a36Sopenharmony_ci	init_waitqueue_head(&req->poll_wait);
32662306a36Sopenharmony_ci	req->updating_count = 0;
32762306a36Sopenharmony_ci	req->access_count = 0;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	*alloc_fd = fd;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	snprintf(req->debug_str, sizeof(req->debug_str), "%u:%d",
33262306a36Sopenharmony_ci		 atomic_inc_return(&mdev->request_id), fd);
33362306a36Sopenharmony_ci	dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	fd_install(fd, filp);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return 0;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cierr_put_fd:
34062306a36Sopenharmony_ci	put_unused_fd(fd);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cierr_free_req:
34362306a36Sopenharmony_ci	if (mdev->ops->req_free)
34462306a36Sopenharmony_ci		mdev->ops->req_free(req);
34562306a36Sopenharmony_ci	else
34662306a36Sopenharmony_ci		kfree(req);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return ret;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic void media_request_object_release(struct kref *kref)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct media_request_object *obj =
35462306a36Sopenharmony_ci		container_of(kref, struct media_request_object, kref);
35562306a36Sopenharmony_ci	struct media_request *req = obj->req;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (WARN_ON(req))
35862306a36Sopenharmony_ci		media_request_object_unbind(obj);
35962306a36Sopenharmony_ci	obj->ops->release(obj);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistruct media_request_object *
36362306a36Sopenharmony_cimedia_request_object_find(struct media_request *req,
36462306a36Sopenharmony_ci			  const struct media_request_object_ops *ops,
36562306a36Sopenharmony_ci			  void *priv)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct media_request_object *obj;
36862306a36Sopenharmony_ci	struct media_request_object *found = NULL;
36962306a36Sopenharmony_ci	unsigned long flags;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (WARN_ON(!ops || !priv))
37262306a36Sopenharmony_ci		return NULL;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
37562306a36Sopenharmony_ci	list_for_each_entry(obj, &req->objects, list) {
37662306a36Sopenharmony_ci		if (obj->ops == ops && obj->priv == priv) {
37762306a36Sopenharmony_ci			media_request_object_get(obj);
37862306a36Sopenharmony_ci			found = obj;
37962306a36Sopenharmony_ci			break;
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
38362306a36Sopenharmony_ci	return found;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_find);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_civoid media_request_object_put(struct media_request_object *obj)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	kref_put(&obj->kref, media_request_object_release);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_put);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_civoid media_request_object_init(struct media_request_object *obj)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	obj->ops = NULL;
39662306a36Sopenharmony_ci	obj->req = NULL;
39762306a36Sopenharmony_ci	obj->priv = NULL;
39862306a36Sopenharmony_ci	obj->completed = false;
39962306a36Sopenharmony_ci	INIT_LIST_HEAD(&obj->list);
40062306a36Sopenharmony_ci	kref_init(&obj->kref);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_init);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ciint media_request_object_bind(struct media_request *req,
40562306a36Sopenharmony_ci			      const struct media_request_object_ops *ops,
40662306a36Sopenharmony_ci			      void *priv, bool is_buffer,
40762306a36Sopenharmony_ci			      struct media_request_object *obj)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	unsigned long flags;
41062306a36Sopenharmony_ci	int ret = -EBUSY;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (WARN_ON(!ops->release))
41362306a36Sopenharmony_ci		return -EBADR;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (WARN_ON(req->state != MEDIA_REQUEST_STATE_UPDATING &&
41862306a36Sopenharmony_ci		    req->state != MEDIA_REQUEST_STATE_QUEUED))
41962306a36Sopenharmony_ci		goto unlock;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	obj->req = req;
42262306a36Sopenharmony_ci	obj->ops = ops;
42362306a36Sopenharmony_ci	obj->priv = priv;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (is_buffer)
42662306a36Sopenharmony_ci		list_add_tail(&obj->list, &req->objects);
42762306a36Sopenharmony_ci	else
42862306a36Sopenharmony_ci		list_add(&obj->list, &req->objects);
42962306a36Sopenharmony_ci	req->num_incomplete_objects++;
43062306a36Sopenharmony_ci	ret = 0;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ciunlock:
43362306a36Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
43462306a36Sopenharmony_ci	return ret;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_bind);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_civoid media_request_object_unbind(struct media_request_object *obj)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct media_request *req = obj->req;
44162306a36Sopenharmony_ci	unsigned long flags;
44262306a36Sopenharmony_ci	bool completed = false;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	if (WARN_ON(!req))
44562306a36Sopenharmony_ci		return;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
44862306a36Sopenharmony_ci	list_del(&obj->list);
44962306a36Sopenharmony_ci	obj->req = NULL;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (req->state == MEDIA_REQUEST_STATE_COMPLETE)
45262306a36Sopenharmony_ci		goto unlock;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (WARN_ON(req->state == MEDIA_REQUEST_STATE_VALIDATING))
45562306a36Sopenharmony_ci		goto unlock;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (req->state == MEDIA_REQUEST_STATE_CLEANING) {
45862306a36Sopenharmony_ci		if (!obj->completed)
45962306a36Sopenharmony_ci			req->num_incomplete_objects--;
46062306a36Sopenharmony_ci		goto unlock;
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (WARN_ON(!req->num_incomplete_objects))
46462306a36Sopenharmony_ci		goto unlock;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	req->num_incomplete_objects--;
46762306a36Sopenharmony_ci	if (req->state == MEDIA_REQUEST_STATE_QUEUED &&
46862306a36Sopenharmony_ci	    !req->num_incomplete_objects) {
46962306a36Sopenharmony_ci		req->state = MEDIA_REQUEST_STATE_COMPLETE;
47062306a36Sopenharmony_ci		completed = true;
47162306a36Sopenharmony_ci		wake_up_interruptible_all(&req->poll_wait);
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ciunlock:
47562306a36Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
47662306a36Sopenharmony_ci	if (obj->ops->unbind)
47762306a36Sopenharmony_ci		obj->ops->unbind(obj);
47862306a36Sopenharmony_ci	if (completed)
47962306a36Sopenharmony_ci		media_request_put(req);
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_unbind);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_civoid media_request_object_complete(struct media_request_object *obj)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct media_request *req = obj->req;
48662306a36Sopenharmony_ci	unsigned long flags;
48762306a36Sopenharmony_ci	bool completed = false;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	spin_lock_irqsave(&req->lock, flags);
49062306a36Sopenharmony_ci	if (obj->completed)
49162306a36Sopenharmony_ci		goto unlock;
49262306a36Sopenharmony_ci	obj->completed = true;
49362306a36Sopenharmony_ci	if (WARN_ON(!req->num_incomplete_objects) ||
49462306a36Sopenharmony_ci	    WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED))
49562306a36Sopenharmony_ci		goto unlock;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (!--req->num_incomplete_objects) {
49862306a36Sopenharmony_ci		req->state = MEDIA_REQUEST_STATE_COMPLETE;
49962306a36Sopenharmony_ci		wake_up_interruptible_all(&req->poll_wait);
50062306a36Sopenharmony_ci		completed = true;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ciunlock:
50362306a36Sopenharmony_ci	spin_unlock_irqrestore(&req->lock, flags);
50462306a36Sopenharmony_ci	if (completed)
50562306a36Sopenharmony_ci		media_request_put(req);
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(media_request_object_complete);
508