1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * v4l2-fh.c |
4 | * |
5 | * V4L2 file handles. |
6 | * |
7 | * Copyright (C) 2009--2010 Nokia Corporation. |
8 | * |
9 | * Contact: Sakari Ailus <sakari.ailus@iki.fi> |
10 | */ |
11 | |
12 | #include <linux/bitops.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/export.h> |
15 | #include <media/v4l2-dev.h> |
16 | #include <media/v4l2-fh.h> |
17 | #include <media/v4l2-event.h> |
18 | #include <media/v4l2-ioctl.h> |
19 | #include <media/v4l2-mc.h> |
20 | |
21 | void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) |
22 | { |
23 | fh->vdev = vdev; |
24 | /* Inherit from video_device. May be overridden by the driver. */ |
25 | fh->ctrl_handler = vdev->ctrl_handler; |
26 | INIT_LIST_HEAD(list: &fh->list); |
27 | set_bit(nr: V4L2_FL_USES_V4L2_FH, addr: &fh->vdev->flags); |
28 | /* |
29 | * determine_valid_ioctls() does not know if struct v4l2_fh |
30 | * is used by this driver, but here we do. So enable the |
31 | * prio ioctls here. |
32 | */ |
33 | set_bit(_IOC_NR(VIDIOC_G_PRIORITY), addr: vdev->valid_ioctls); |
34 | set_bit(_IOC_NR(VIDIOC_S_PRIORITY), addr: vdev->valid_ioctls); |
35 | fh->prio = V4L2_PRIORITY_UNSET; |
36 | init_waitqueue_head(&fh->wait); |
37 | INIT_LIST_HEAD(list: &fh->available); |
38 | INIT_LIST_HEAD(list: &fh->subscribed); |
39 | fh->sequence = -1; |
40 | mutex_init(&fh->subscribe_lock); |
41 | } |
42 | EXPORT_SYMBOL_GPL(v4l2_fh_init); |
43 | |
44 | void v4l2_fh_add(struct v4l2_fh *fh) |
45 | { |
46 | unsigned long flags; |
47 | |
48 | v4l2_prio_open(global: fh->vdev->prio, local: &fh->prio); |
49 | spin_lock_irqsave(&fh->vdev->fh_lock, flags); |
50 | list_add(new: &fh->list, head: &fh->vdev->fh_list); |
51 | spin_unlock_irqrestore(lock: &fh->vdev->fh_lock, flags); |
52 | } |
53 | EXPORT_SYMBOL_GPL(v4l2_fh_add); |
54 | |
55 | int v4l2_fh_open(struct file *filp) |
56 | { |
57 | struct video_device *vdev = video_devdata(file: filp); |
58 | struct v4l2_fh *fh = kzalloc(size: sizeof(*fh), GFP_KERNEL); |
59 | |
60 | filp->private_data = fh; |
61 | if (fh == NULL) |
62 | return -ENOMEM; |
63 | v4l2_fh_init(fh, vdev); |
64 | v4l2_fh_add(fh); |
65 | return 0; |
66 | } |
67 | EXPORT_SYMBOL_GPL(v4l2_fh_open); |
68 | |
69 | void v4l2_fh_del(struct v4l2_fh *fh) |
70 | { |
71 | unsigned long flags; |
72 | |
73 | spin_lock_irqsave(&fh->vdev->fh_lock, flags); |
74 | list_del_init(entry: &fh->list); |
75 | spin_unlock_irqrestore(lock: &fh->vdev->fh_lock, flags); |
76 | v4l2_prio_close(global: fh->vdev->prio, local: fh->prio); |
77 | } |
78 | EXPORT_SYMBOL_GPL(v4l2_fh_del); |
79 | |
80 | void v4l2_fh_exit(struct v4l2_fh *fh) |
81 | { |
82 | if (fh->vdev == NULL) |
83 | return; |
84 | v4l_disable_media_source(vdev: fh->vdev); |
85 | v4l2_event_unsubscribe_all(fh); |
86 | mutex_destroy(lock: &fh->subscribe_lock); |
87 | fh->vdev = NULL; |
88 | } |
89 | EXPORT_SYMBOL_GPL(v4l2_fh_exit); |
90 | |
91 | int v4l2_fh_release(struct file *filp) |
92 | { |
93 | struct v4l2_fh *fh = filp->private_data; |
94 | |
95 | if (fh) { |
96 | v4l2_fh_del(fh); |
97 | v4l2_fh_exit(fh); |
98 | kfree(objp: fh); |
99 | filp->private_data = NULL; |
100 | } |
101 | return 0; |
102 | } |
103 | EXPORT_SYMBOL_GPL(v4l2_fh_release); |
104 | |
105 | int v4l2_fh_is_singular(struct v4l2_fh *fh) |
106 | { |
107 | unsigned long flags; |
108 | int is_singular; |
109 | |
110 | if (fh == NULL || fh->vdev == NULL) |
111 | return 0; |
112 | spin_lock_irqsave(&fh->vdev->fh_lock, flags); |
113 | is_singular = list_is_singular(head: &fh->list); |
114 | spin_unlock_irqrestore(lock: &fh->vdev->fh_lock, flags); |
115 | return is_singular; |
116 | } |
117 | EXPORT_SYMBOL_GPL(v4l2_fh_is_singular); |
118 | |