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