18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * v4l2-fh.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * V4L2 file handles.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2009--2010 Nokia Corporation.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Contact: Sakari Ailus <sakari.ailus@iki.fi>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/bitops.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/export.h>
158c2ecf20Sopenharmony_ci#include <media/v4l2-dev.h>
168c2ecf20Sopenharmony_ci#include <media/v4l2-fh.h>
178c2ecf20Sopenharmony_ci#include <media/v4l2-event.h>
188c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
198c2ecf20Sopenharmony_ci#include <media/v4l2-mc.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_civoid v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	fh->vdev = vdev;
248c2ecf20Sopenharmony_ci	/* Inherit from video_device. May be overridden by the driver. */
258c2ecf20Sopenharmony_ci	fh->ctrl_handler = vdev->ctrl_handler;
268c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fh->list);
278c2ecf20Sopenharmony_ci	set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags);
288c2ecf20Sopenharmony_ci	/*
298c2ecf20Sopenharmony_ci	 * determine_valid_ioctls() does not know if struct v4l2_fh
308c2ecf20Sopenharmony_ci	 * is used by this driver, but here we do. So enable the
318c2ecf20Sopenharmony_ci	 * prio ioctls here.
328c2ecf20Sopenharmony_ci	 */
338c2ecf20Sopenharmony_ci	set_bit(_IOC_NR(VIDIOC_G_PRIORITY), vdev->valid_ioctls);
348c2ecf20Sopenharmony_ci	set_bit(_IOC_NR(VIDIOC_S_PRIORITY), vdev->valid_ioctls);
358c2ecf20Sopenharmony_ci	fh->prio = V4L2_PRIORITY_UNSET;
368c2ecf20Sopenharmony_ci	init_waitqueue_head(&fh->wait);
378c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fh->available);
388c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fh->subscribed);
398c2ecf20Sopenharmony_ci	fh->sequence = -1;
408c2ecf20Sopenharmony_ci	mutex_init(&fh->subscribe_lock);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fh_init);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_civoid v4l2_fh_add(struct v4l2_fh *fh)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	unsigned long flags;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	v4l2_prio_open(fh->vdev->prio, &fh->prio);
498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
508c2ecf20Sopenharmony_ci	list_add(&fh->list, &fh->vdev->fh_list);
518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fh_add);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ciint v4l2_fh_open(struct file *filp)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(filp);
588c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	filp->private_data = fh;
618c2ecf20Sopenharmony_ci	if (fh == NULL)
628c2ecf20Sopenharmony_ci		return -ENOMEM;
638c2ecf20Sopenharmony_ci	v4l2_fh_init(fh, vdev);
648c2ecf20Sopenharmony_ci	v4l2_fh_add(fh);
658c2ecf20Sopenharmony_ci	return 0;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fh_open);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_civoid v4l2_fh_del(struct v4l2_fh *fh)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	unsigned long flags;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
748c2ecf20Sopenharmony_ci	list_del_init(&fh->list);
758c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
768c2ecf20Sopenharmony_ci	v4l2_prio_close(fh->vdev->prio, fh->prio);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fh_del);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_civoid v4l2_fh_exit(struct v4l2_fh *fh)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	if (fh->vdev == NULL)
838c2ecf20Sopenharmony_ci		return;
848c2ecf20Sopenharmony_ci	v4l_disable_media_source(fh->vdev);
858c2ecf20Sopenharmony_ci	v4l2_event_unsubscribe_all(fh);
868c2ecf20Sopenharmony_ci	mutex_destroy(&fh->subscribe_lock);
878c2ecf20Sopenharmony_ci	fh->vdev = NULL;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fh_exit);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ciint v4l2_fh_release(struct file *filp)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = filp->private_data;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (fh) {
968c2ecf20Sopenharmony_ci		v4l2_fh_del(fh);
978c2ecf20Sopenharmony_ci		v4l2_fh_exit(fh);
988c2ecf20Sopenharmony_ci		kfree(fh);
998c2ecf20Sopenharmony_ci		filp->private_data = NULL;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fh_release);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ciint v4l2_fh_is_singular(struct v4l2_fh *fh)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	unsigned long flags;
1088c2ecf20Sopenharmony_ci	int is_singular;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (fh == NULL || fh->vdev == NULL)
1118c2ecf20Sopenharmony_ci		return 0;
1128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
1138c2ecf20Sopenharmony_ci	is_singular = list_is_singular(&fh->list);
1148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
1158c2ecf20Sopenharmony_ci	return is_singular;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_fh_is_singular);
118