11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25bc3cb74SMauro Carvalho Chehab /*
35bc3cb74SMauro Carvalho Chehab  * v4l2-event.c
45bc3cb74SMauro Carvalho Chehab  *
55bc3cb74SMauro Carvalho Chehab  * V4L2 events.
65bc3cb74SMauro Carvalho Chehab  *
75bc3cb74SMauro Carvalho Chehab  * Copyright (C) 2009--2010 Nokia Corporation.
85bc3cb74SMauro Carvalho Chehab  *
98c5dff90SSakari Ailus  * Contact: Sakari Ailus <sakari.ailus@iki.fi>
105bc3cb74SMauro Carvalho Chehab  */
115bc3cb74SMauro Carvalho Chehab 
125bc3cb74SMauro Carvalho Chehab #include <media/v4l2-dev.h>
135bc3cb74SMauro Carvalho Chehab #include <media/v4l2-fh.h>
145bc3cb74SMauro Carvalho Chehab #include <media/v4l2-event.h>
155bc3cb74SMauro Carvalho Chehab 
16758d90e1STomasz Figa #include <linux/mm.h>
175bc3cb74SMauro Carvalho Chehab #include <linux/sched.h>
185bc3cb74SMauro Carvalho Chehab #include <linux/slab.h>
195bc3cb74SMauro Carvalho Chehab #include <linux/export.h>
205bc3cb74SMauro Carvalho Chehab 
sev_pos(const struct v4l2_subscribed_event * sev,unsigned int idx)21*40d62da2Slijian static unsigned int sev_pos(const struct v4l2_subscribed_event *sev, unsigned int idx)
225bc3cb74SMauro Carvalho Chehab {
235bc3cb74SMauro Carvalho Chehab 	idx += sev->first;
245bc3cb74SMauro Carvalho Chehab 	return idx >= sev->elems ? idx - sev->elems : idx;
255bc3cb74SMauro Carvalho Chehab }
265bc3cb74SMauro Carvalho Chehab 
__v4l2_event_dequeue(struct v4l2_fh * fh,struct v4l2_event * event)275bc3cb74SMauro Carvalho Chehab static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
285bc3cb74SMauro Carvalho Chehab {
295bc3cb74SMauro Carvalho Chehab 	struct v4l2_kevent *kev;
301a6c0b36SArnd Bergmann 	struct timespec64 ts;
315bc3cb74SMauro Carvalho Chehab 	unsigned long flags;
325bc3cb74SMauro Carvalho Chehab 
335bc3cb74SMauro Carvalho Chehab 	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
345bc3cb74SMauro Carvalho Chehab 
355bc3cb74SMauro Carvalho Chehab 	if (list_empty(&fh->available)) {
365bc3cb74SMauro Carvalho Chehab 		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
375bc3cb74SMauro Carvalho Chehab 		return -ENOENT;
385bc3cb74SMauro Carvalho Chehab 	}
395bc3cb74SMauro Carvalho Chehab 
405bc3cb74SMauro Carvalho Chehab 	WARN_ON(fh->navailable == 0);
415bc3cb74SMauro Carvalho Chehab 
425bc3cb74SMauro Carvalho Chehab 	kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
435bc3cb74SMauro Carvalho Chehab 	list_del(&kev->list);
445bc3cb74SMauro Carvalho Chehab 	fh->navailable--;
455bc3cb74SMauro Carvalho Chehab 
465bc3cb74SMauro Carvalho Chehab 	kev->event.pending = fh->navailable;
475bc3cb74SMauro Carvalho Chehab 	*event = kev->event;
481a6c0b36SArnd Bergmann 	ts = ns_to_timespec64(kev->ts);
491a6c0b36SArnd Bergmann 	event->timestamp.tv_sec = ts.tv_sec;
501a6c0b36SArnd Bergmann 	event->timestamp.tv_nsec = ts.tv_nsec;
515bc3cb74SMauro Carvalho Chehab 	kev->sev->first = sev_pos(kev->sev, 1);
525bc3cb74SMauro Carvalho Chehab 	kev->sev->in_use--;
535bc3cb74SMauro Carvalho Chehab 
545bc3cb74SMauro Carvalho Chehab 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
555bc3cb74SMauro Carvalho Chehab 
565bc3cb74SMauro Carvalho Chehab 	return 0;
575bc3cb74SMauro Carvalho Chehab }
585bc3cb74SMauro Carvalho Chehab 
v4l2_event_dequeue(struct v4l2_fh * fh,struct v4l2_event * event,int nonblocking)595bc3cb74SMauro Carvalho Chehab int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
605bc3cb74SMauro Carvalho Chehab 		       int nonblocking)
615bc3cb74SMauro Carvalho Chehab {
625bc3cb74SMauro Carvalho Chehab 	int ret;
635bc3cb74SMauro Carvalho Chehab 
645bc3cb74SMauro Carvalho Chehab 	if (nonblocking)
655bc3cb74SMauro Carvalho Chehab 		return __v4l2_event_dequeue(fh, event);
665bc3cb74SMauro Carvalho Chehab 
675bc3cb74SMauro Carvalho Chehab 	/* Release the vdev lock while waiting */
685bc3cb74SMauro Carvalho Chehab 	if (fh->vdev->lock)
695bc3cb74SMauro Carvalho Chehab 		mutex_unlock(fh->vdev->lock);
705bc3cb74SMauro Carvalho Chehab 
715bc3cb74SMauro Carvalho Chehab 	do {
725bc3cb74SMauro Carvalho Chehab 		ret = wait_event_interruptible(fh->wait,
735bc3cb74SMauro Carvalho Chehab 					       fh->navailable != 0);
745bc3cb74SMauro Carvalho Chehab 		if (ret < 0)
755bc3cb74SMauro Carvalho Chehab 			break;
765bc3cb74SMauro Carvalho Chehab 
775bc3cb74SMauro Carvalho Chehab 		ret = __v4l2_event_dequeue(fh, event);
785bc3cb74SMauro Carvalho Chehab 	} while (ret == -ENOENT);
795bc3cb74SMauro Carvalho Chehab 
805bc3cb74SMauro Carvalho Chehab 	if (fh->vdev->lock)
815bc3cb74SMauro Carvalho Chehab 		mutex_lock(fh->vdev->lock);
825bc3cb74SMauro Carvalho Chehab 
835bc3cb74SMauro Carvalho Chehab 	return ret;
845bc3cb74SMauro Carvalho Chehab }
855bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
865bc3cb74SMauro Carvalho Chehab 
875bc3cb74SMauro Carvalho Chehab /* Caller must hold fh->vdev->fh_lock! */
v4l2_event_subscribed(struct v4l2_fh * fh,u32 type,u32 id)885bc3cb74SMauro Carvalho Chehab static struct v4l2_subscribed_event *v4l2_event_subscribed(
895bc3cb74SMauro Carvalho Chehab 		struct v4l2_fh *fh, u32 type, u32 id)
905bc3cb74SMauro Carvalho Chehab {
915bc3cb74SMauro Carvalho Chehab 	struct v4l2_subscribed_event *sev;
925bc3cb74SMauro Carvalho Chehab 
935bc3cb74SMauro Carvalho Chehab 	assert_spin_locked(&fh->vdev->fh_lock);
945bc3cb74SMauro Carvalho Chehab 
955bc3cb74SMauro Carvalho Chehab 	list_for_each_entry(sev, &fh->subscribed, list)
965bc3cb74SMauro Carvalho Chehab 		if (sev->type == type && sev->id == id)
975bc3cb74SMauro Carvalho Chehab 			return sev;
985bc3cb74SMauro Carvalho Chehab 
995bc3cb74SMauro Carvalho Chehab 	return NULL;
1005bc3cb74SMauro Carvalho Chehab }
1015bc3cb74SMauro Carvalho Chehab 
__v4l2_event_queue_fh(struct v4l2_fh * fh,const struct v4l2_event * ev,u64 ts)10263635b54SHans Verkuil static void __v4l2_event_queue_fh(struct v4l2_fh *fh,
10363635b54SHans Verkuil 				  const struct v4l2_event *ev, u64 ts)
1045bc3cb74SMauro Carvalho Chehab {
1055bc3cb74SMauro Carvalho Chehab 	struct v4l2_subscribed_event *sev;
1065bc3cb74SMauro Carvalho Chehab 	struct v4l2_kevent *kev;
1075bc3cb74SMauro Carvalho Chehab 	bool copy_payload = true;
1085bc3cb74SMauro Carvalho Chehab 
1095bc3cb74SMauro Carvalho Chehab 	/* Are we subscribed? */
1105bc3cb74SMauro Carvalho Chehab 	sev = v4l2_event_subscribed(fh, ev->type, ev->id);
1115bc3cb74SMauro Carvalho Chehab 	if (sev == NULL)
1125bc3cb74SMauro Carvalho Chehab 		return;
1135bc3cb74SMauro Carvalho Chehab 
1145bc3cb74SMauro Carvalho Chehab 	/* Increase event sequence number on fh. */
1155bc3cb74SMauro Carvalho Chehab 	fh->sequence++;
1165bc3cb74SMauro Carvalho Chehab 
1175bc3cb74SMauro Carvalho Chehab 	/* Do we have any free events? */
1185bc3cb74SMauro Carvalho Chehab 	if (sev->in_use == sev->elems) {
1195bc3cb74SMauro Carvalho Chehab 		/* no, remove the oldest one */
1205bc3cb74SMauro Carvalho Chehab 		kev = sev->events + sev_pos(sev, 0);
1215bc3cb74SMauro Carvalho Chehab 		list_del(&kev->list);
1225bc3cb74SMauro Carvalho Chehab 		sev->in_use--;
1235bc3cb74SMauro Carvalho Chehab 		sev->first = sev_pos(sev, 1);
1245bc3cb74SMauro Carvalho Chehab 		fh->navailable--;
1255bc3cb74SMauro Carvalho Chehab 		if (sev->elems == 1) {
1265bc3cb74SMauro Carvalho Chehab 			if (sev->ops && sev->ops->replace) {
1275bc3cb74SMauro Carvalho Chehab 				sev->ops->replace(&kev->event, ev);
1285bc3cb74SMauro Carvalho Chehab 				copy_payload = false;
1295bc3cb74SMauro Carvalho Chehab 			}
1305bc3cb74SMauro Carvalho Chehab 		} else if (sev->ops && sev->ops->merge) {
1315bc3cb74SMauro Carvalho Chehab 			struct v4l2_kevent *second_oldest =
1325bc3cb74SMauro Carvalho Chehab 				sev->events + sev_pos(sev, 0);
1335bc3cb74SMauro Carvalho Chehab 			sev->ops->merge(&kev->event, &second_oldest->event);
1345bc3cb74SMauro Carvalho Chehab 		}
1355bc3cb74SMauro Carvalho Chehab 	}
1365bc3cb74SMauro Carvalho Chehab 
1375bc3cb74SMauro Carvalho Chehab 	/* Take one and fill it. */
1385bc3cb74SMauro Carvalho Chehab 	kev = sev->events + sev_pos(sev, sev->in_use);
1395bc3cb74SMauro Carvalho Chehab 	kev->event.type = ev->type;
1405bc3cb74SMauro Carvalho Chehab 	if (copy_payload)
1415bc3cb74SMauro Carvalho Chehab 		kev->event.u = ev->u;
1425bc3cb74SMauro Carvalho Chehab 	kev->event.id = ev->id;
14363635b54SHans Verkuil 	kev->ts = ts;
1445bc3cb74SMauro Carvalho Chehab 	kev->event.sequence = fh->sequence;
1455bc3cb74SMauro Carvalho Chehab 	sev->in_use++;
1465bc3cb74SMauro Carvalho Chehab 	list_add_tail(&kev->list, &fh->available);
1475bc3cb74SMauro Carvalho Chehab 
1485bc3cb74SMauro Carvalho Chehab 	fh->navailable++;
1495bc3cb74SMauro Carvalho Chehab 
1505bc3cb74SMauro Carvalho Chehab 	wake_up_all(&fh->wait);
1515bc3cb74SMauro Carvalho Chehab }
1525bc3cb74SMauro Carvalho Chehab 
v4l2_event_queue(struct video_device * vdev,const struct v4l2_event * ev)1535bc3cb74SMauro Carvalho Chehab void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
1545bc3cb74SMauro Carvalho Chehab {
1555bc3cb74SMauro Carvalho Chehab 	struct v4l2_fh *fh;
1565bc3cb74SMauro Carvalho Chehab 	unsigned long flags;
15763635b54SHans Verkuil 	u64 ts;
1585bc3cb74SMauro Carvalho Chehab 
159fb8dfda9SHans Verkuil 	if (vdev == NULL)
160fb8dfda9SHans Verkuil 		return;
161fb8dfda9SHans Verkuil 
16263635b54SHans Verkuil 	ts = ktime_get_ns();
1635bc3cb74SMauro Carvalho Chehab 
1645bc3cb74SMauro Carvalho Chehab 	spin_lock_irqsave(&vdev->fh_lock, flags);
1655bc3cb74SMauro Carvalho Chehab 
1665bc3cb74SMauro Carvalho Chehab 	list_for_each_entry(fh, &vdev->fh_list, list)
16763635b54SHans Verkuil 		__v4l2_event_queue_fh(fh, ev, ts);
1685bc3cb74SMauro Carvalho Chehab 
1695bc3cb74SMauro Carvalho Chehab 	spin_unlock_irqrestore(&vdev->fh_lock, flags);
1705bc3cb74SMauro Carvalho Chehab }
1715bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_event_queue);
1725bc3cb74SMauro Carvalho Chehab 
v4l2_event_queue_fh(struct v4l2_fh * fh,const struct v4l2_event * ev)1735bc3cb74SMauro Carvalho Chehab void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
1745bc3cb74SMauro Carvalho Chehab {
1755bc3cb74SMauro Carvalho Chehab 	unsigned long flags;
17663635b54SHans Verkuil 	u64 ts = ktime_get_ns();
1775bc3cb74SMauro Carvalho Chehab 
1785bc3cb74SMauro Carvalho Chehab 	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
17963635b54SHans Verkuil 	__v4l2_event_queue_fh(fh, ev, ts);
1805bc3cb74SMauro Carvalho Chehab 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
1815bc3cb74SMauro Carvalho Chehab }
1825bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
1835bc3cb74SMauro Carvalho Chehab 
v4l2_event_pending(struct v4l2_fh * fh)1845bc3cb74SMauro Carvalho Chehab int v4l2_event_pending(struct v4l2_fh *fh)
1855bc3cb74SMauro Carvalho Chehab {
1865bc3cb74SMauro Carvalho Chehab 	return fh->navailable;
1875bc3cb74SMauro Carvalho Chehab }
1885bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_event_pending);
1895bc3cb74SMauro Carvalho Chehab 
v4l2_event_wake_all(struct video_device * vdev)19028955a61SHans Verkuil void v4l2_event_wake_all(struct video_device *vdev)
19128955a61SHans Verkuil {
19228955a61SHans Verkuil 	struct v4l2_fh *fh;
19328955a61SHans Verkuil 	unsigned long flags;
19428955a61SHans Verkuil 
19528955a61SHans Verkuil 	if (!vdev)
19628955a61SHans Verkuil 		return;
19728955a61SHans Verkuil 
19828955a61SHans Verkuil 	spin_lock_irqsave(&vdev->fh_lock, flags);
19928955a61SHans Verkuil 
20028955a61SHans Verkuil 	list_for_each_entry(fh, &vdev->fh_list, list)
20128955a61SHans Verkuil 		wake_up_all(&fh->wait);
20228955a61SHans Verkuil 
20328955a61SHans Verkuil 	spin_unlock_irqrestore(&vdev->fh_lock, flags);
20428955a61SHans Verkuil }
20528955a61SHans Verkuil EXPORT_SYMBOL_GPL(v4l2_event_wake_all);
20628955a61SHans Verkuil 
__v4l2_event_unsubscribe(struct v4l2_subscribed_event * sev)20792539d3eSSakari Ailus static void __v4l2_event_unsubscribe(struct v4l2_subscribed_event *sev)
20892539d3eSSakari Ailus {
20992539d3eSSakari Ailus 	struct v4l2_fh *fh = sev->fh;
21092539d3eSSakari Ailus 	unsigned int i;
21192539d3eSSakari Ailus 
21292539d3eSSakari Ailus 	lockdep_assert_held(&fh->subscribe_lock);
21392539d3eSSakari Ailus 	assert_spin_locked(&fh->vdev->fh_lock);
21492539d3eSSakari Ailus 
21592539d3eSSakari Ailus 	/* Remove any pending events for this subscription */
21692539d3eSSakari Ailus 	for (i = 0; i < sev->in_use; i++) {
21792539d3eSSakari Ailus 		list_del(&sev->events[sev_pos(sev, i)].list);
21892539d3eSSakari Ailus 		fh->navailable--;
21992539d3eSSakari Ailus 	}
22092539d3eSSakari Ailus 	list_del(&sev->list);
22192539d3eSSakari Ailus }
22292539d3eSSakari Ailus 
v4l2_event_subscribe(struct v4l2_fh * fh,const struct v4l2_event_subscription * sub,unsigned int elems,const struct v4l2_subscribed_event_ops * ops)2235bc3cb74SMauro Carvalho Chehab int v4l2_event_subscribe(struct v4l2_fh *fh,
224*40d62da2Slijian 			 const struct v4l2_event_subscription *sub, unsigned int elems,
2255bc3cb74SMauro Carvalho Chehab 			 const struct v4l2_subscribed_event_ops *ops)
2265bc3cb74SMauro Carvalho Chehab {
2275bc3cb74SMauro Carvalho Chehab 	struct v4l2_subscribed_event *sev, *found_ev;
2285bc3cb74SMauro Carvalho Chehab 	unsigned long flags;
229*40d62da2Slijian 	unsigned int i;
230ad608fbcSSakari Ailus 	int ret = 0;
2315bc3cb74SMauro Carvalho Chehab 
2325bc3cb74SMauro Carvalho Chehab 	if (sub->type == V4L2_EVENT_ALL)
2335bc3cb74SMauro Carvalho Chehab 		return -EINVAL;
2345bc3cb74SMauro Carvalho Chehab 
2355bc3cb74SMauro Carvalho Chehab 	if (elems < 1)
2365bc3cb74SMauro Carvalho Chehab 		elems = 1;
2375bc3cb74SMauro Carvalho Chehab 
238c817e6ccSMatthew Wilcox 	sev = kvzalloc(struct_size(sev, events, elems), GFP_KERNEL);
2395bc3cb74SMauro Carvalho Chehab 	if (!sev)
2405bc3cb74SMauro Carvalho Chehab 		return -ENOMEM;
2415bc3cb74SMauro Carvalho Chehab 	for (i = 0; i < elems; i++)
2425bc3cb74SMauro Carvalho Chehab 		sev->events[i].sev = sev;
2435bc3cb74SMauro Carvalho Chehab 	sev->type = sub->type;
2445bc3cb74SMauro Carvalho Chehab 	sev->id = sub->id;
2455bc3cb74SMauro Carvalho Chehab 	sev->flags = sub->flags;
2465bc3cb74SMauro Carvalho Chehab 	sev->fh = fh;
2475bc3cb74SMauro Carvalho Chehab 	sev->ops = ops;
248ad608fbcSSakari Ailus 	sev->elems = elems;
249ad608fbcSSakari Ailus 
250ad608fbcSSakari Ailus 	mutex_lock(&fh->subscribe_lock);
2515bc3cb74SMauro Carvalho Chehab 
2525bc3cb74SMauro Carvalho Chehab 	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
2535bc3cb74SMauro Carvalho Chehab 	found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
25492539d3eSSakari Ailus 	if (!found_ev)
25592539d3eSSakari Ailus 		list_add(&sev->list, &fh->subscribed);
2565bc3cb74SMauro Carvalho Chehab 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
2575bc3cb74SMauro Carvalho Chehab 
2585bc3cb74SMauro Carvalho Chehab 	if (found_ev) {
259ad608fbcSSakari Ailus 		/* Already listening */
260758d90e1STomasz Figa 		kvfree(sev);
26192539d3eSSakari Ailus 	} else if (sev->ops && sev->ops->add) {
262ad608fbcSSakari Ailus 		ret = sev->ops->add(sev, elems);
2635bc3cb74SMauro Carvalho Chehab 		if (ret) {
264ad608fbcSSakari Ailus 			spin_lock_irqsave(&fh->vdev->fh_lock, flags);
26592539d3eSSakari Ailus 			__v4l2_event_unsubscribe(sev);
266ad608fbcSSakari Ailus 			spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
26792539d3eSSakari Ailus 			kvfree(sev);
26892539d3eSSakari Ailus 		}
26992539d3eSSakari Ailus 	}
270ad608fbcSSakari Ailus 
271ad608fbcSSakari Ailus 	mutex_unlock(&fh->subscribe_lock);
272ad608fbcSSakari Ailus 
2735bc3cb74SMauro Carvalho Chehab 	return ret;
2745bc3cb74SMauro Carvalho Chehab }
2755bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
2765bc3cb74SMauro Carvalho Chehab 
v4l2_event_unsubscribe_all(struct v4l2_fh * fh)2775bc3cb74SMauro Carvalho Chehab void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
2785bc3cb74SMauro Carvalho Chehab {
2795bc3cb74SMauro Carvalho Chehab 	struct v4l2_event_subscription sub;
2805bc3cb74SMauro Carvalho Chehab 	struct v4l2_subscribed_event *sev;
2815bc3cb74SMauro Carvalho Chehab 	unsigned long flags;
2825bc3cb74SMauro Carvalho Chehab 
2835bc3cb74SMauro Carvalho Chehab 	do {
2845bc3cb74SMauro Carvalho Chehab 		sev = NULL;
2855bc3cb74SMauro Carvalho Chehab 
2865bc3cb74SMauro Carvalho Chehab 		spin_lock_irqsave(&fh->vdev->fh_lock, flags);
2875bc3cb74SMauro Carvalho Chehab 		if (!list_empty(&fh->subscribed)) {
2885bc3cb74SMauro Carvalho Chehab 			sev = list_first_entry(&fh->subscribed,
2895bc3cb74SMauro Carvalho Chehab 					struct v4l2_subscribed_event, list);
2905bc3cb74SMauro Carvalho Chehab 			sub.type = sev->type;
2915bc3cb74SMauro Carvalho Chehab 			sub.id = sev->id;
2925bc3cb74SMauro Carvalho Chehab 		}
2935bc3cb74SMauro Carvalho Chehab 		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
2945bc3cb74SMauro Carvalho Chehab 		if (sev)
2955bc3cb74SMauro Carvalho Chehab 			v4l2_event_unsubscribe(fh, &sub);
2965bc3cb74SMauro Carvalho Chehab 	} while (sev);
2975bc3cb74SMauro Carvalho Chehab }
2985bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
2995bc3cb74SMauro Carvalho Chehab 
v4l2_event_unsubscribe(struct v4l2_fh * fh,const struct v4l2_event_subscription * sub)3005bc3cb74SMauro Carvalho Chehab int v4l2_event_unsubscribe(struct v4l2_fh *fh,
30185f5fe39SHans Verkuil 			   const struct v4l2_event_subscription *sub)
3025bc3cb74SMauro Carvalho Chehab {
3035bc3cb74SMauro Carvalho Chehab 	struct v4l2_subscribed_event *sev;
3045bc3cb74SMauro Carvalho Chehab 	unsigned long flags;
3055bc3cb74SMauro Carvalho Chehab 
3065bc3cb74SMauro Carvalho Chehab 	if (sub->type == V4L2_EVENT_ALL) {
3075bc3cb74SMauro Carvalho Chehab 		v4l2_event_unsubscribe_all(fh);
3085bc3cb74SMauro Carvalho Chehab 		return 0;
3095bc3cb74SMauro Carvalho Chehab 	}
3105bc3cb74SMauro Carvalho Chehab 
311ad608fbcSSakari Ailus 	mutex_lock(&fh->subscribe_lock);
312ad608fbcSSakari Ailus 
3135bc3cb74SMauro Carvalho Chehab 	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
3145bc3cb74SMauro Carvalho Chehab 
3155bc3cb74SMauro Carvalho Chehab 	sev = v4l2_event_subscribed(fh, sub->type, sub->id);
31692539d3eSSakari Ailus 	if (sev != NULL)
31792539d3eSSakari Ailus 		__v4l2_event_unsubscribe(sev);
3185bc3cb74SMauro Carvalho Chehab 
3195bc3cb74SMauro Carvalho Chehab 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
3205bc3cb74SMauro Carvalho Chehab 
3215bc3cb74SMauro Carvalho Chehab 	if (sev && sev->ops && sev->ops->del)
3225bc3cb74SMauro Carvalho Chehab 		sev->ops->del(sev);
3235bc3cb74SMauro Carvalho Chehab 
324ad608fbcSSakari Ailus 	mutex_unlock(&fh->subscribe_lock);
325ad608fbcSSakari Ailus 
326758d90e1STomasz Figa 	kvfree(sev);
3275bc3cb74SMauro Carvalho Chehab 
3285bc3cb74SMauro Carvalho Chehab 	return 0;
3295bc3cb74SMauro Carvalho Chehab }
3305bc3cb74SMauro Carvalho Chehab EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
3314f4d14b7SSylwester Nawrocki 
v4l2_event_subdev_unsubscribe(struct v4l2_subdev * sd,struct v4l2_fh * fh,struct v4l2_event_subscription * sub)3324f4d14b7SSylwester Nawrocki int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh,
3334f4d14b7SSylwester Nawrocki 				  struct v4l2_event_subscription *sub)
3344f4d14b7SSylwester Nawrocki {
3354f4d14b7SSylwester Nawrocki 	return v4l2_event_unsubscribe(fh, sub);
3364f4d14b7SSylwester Nawrocki }
3374f4d14b7SSylwester Nawrocki EXPORT_SYMBOL_GPL(v4l2_event_subdev_unsubscribe);
3383cbe6e5bSArun Kumar K 
v4l2_event_src_replace(struct v4l2_event * old,const struct v4l2_event * new)3393cbe6e5bSArun Kumar K static void v4l2_event_src_replace(struct v4l2_event *old,
3403cbe6e5bSArun Kumar K 				const struct v4l2_event *new)
3413cbe6e5bSArun Kumar K {
3423cbe6e5bSArun Kumar K 	u32 old_changes = old->u.src_change.changes;
3433cbe6e5bSArun Kumar K 
3443cbe6e5bSArun Kumar K 	old->u.src_change = new->u.src_change;
3453cbe6e5bSArun Kumar K 	old->u.src_change.changes |= old_changes;
3463cbe6e5bSArun Kumar K }
3473cbe6e5bSArun Kumar K 
v4l2_event_src_merge(const struct v4l2_event * old,struct v4l2_event * new)3483cbe6e5bSArun Kumar K static void v4l2_event_src_merge(const struct v4l2_event *old,
3493cbe6e5bSArun Kumar K 				struct v4l2_event *new)
3503cbe6e5bSArun Kumar K {
3513cbe6e5bSArun Kumar K 	new->u.src_change.changes |= old->u.src_change.changes;
3523cbe6e5bSArun Kumar K }
3533cbe6e5bSArun Kumar K 
3543cbe6e5bSArun Kumar K static const struct v4l2_subscribed_event_ops v4l2_event_src_ch_ops = {
3553cbe6e5bSArun Kumar K 	.replace = v4l2_event_src_replace,
3563cbe6e5bSArun Kumar K 	.merge = v4l2_event_src_merge,
3573cbe6e5bSArun Kumar K };
3583cbe6e5bSArun Kumar K 
v4l2_src_change_event_subscribe(struct v4l2_fh * fh,const struct v4l2_event_subscription * sub)3593cbe6e5bSArun Kumar K int v4l2_src_change_event_subscribe(struct v4l2_fh *fh,
3603cbe6e5bSArun Kumar K 				const struct v4l2_event_subscription *sub)
3613cbe6e5bSArun Kumar K {
3623cbe6e5bSArun Kumar K 	if (sub->type == V4L2_EVENT_SOURCE_CHANGE)
3633cbe6e5bSArun Kumar K 		return v4l2_event_subscribe(fh, sub, 0, &v4l2_event_src_ch_ops);
3643cbe6e5bSArun Kumar K 	return -EINVAL;
3653cbe6e5bSArun Kumar K }
3663cbe6e5bSArun Kumar K EXPORT_SYMBOL_GPL(v4l2_src_change_event_subscribe);
3673cbe6e5bSArun Kumar K 
v4l2_src_change_event_subdev_subscribe(struct v4l2_subdev * sd,struct v4l2_fh * fh,struct v4l2_event_subscription * sub)3683cbe6e5bSArun Kumar K int v4l2_src_change_event_subdev_subscribe(struct v4l2_subdev *sd,
3693cbe6e5bSArun Kumar K 		struct v4l2_fh *fh, struct v4l2_event_subscription *sub)
3703cbe6e5bSArun Kumar K {
3713cbe6e5bSArun Kumar K 	return v4l2_src_change_event_subscribe(fh, sub);
3723cbe6e5bSArun Kumar K }
3733cbe6e5bSArun Kumar K EXPORT_SYMBOL_GPL(v4l2_src_change_event_subdev_subscribe);
374