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