cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

v4l2-event.c (8730B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * v4l2-event.c
      4 *
      5 * V4L2 events.
      6 *
      7 * Copyright (C) 2009--2010 Nokia Corporation.
      8 *
      9 * Contact: Sakari Ailus <sakari.ailus@iki.fi>
     10 */
     11
     12#include <media/v4l2-dev.h>
     13#include <media/v4l2-fh.h>
     14#include <media/v4l2-event.h>
     15
     16#include <linux/mm.h>
     17#include <linux/sched.h>
     18#include <linux/slab.h>
     19#include <linux/export.h>
     20
     21static unsigned int sev_pos(const struct v4l2_subscribed_event *sev, unsigned int idx)
     22{
     23	idx += sev->first;
     24	return idx >= sev->elems ? idx - sev->elems : idx;
     25}
     26
     27static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
     28{
     29	struct v4l2_kevent *kev;
     30	struct timespec64 ts;
     31	unsigned long flags;
     32
     33	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
     34
     35	if (list_empty(&fh->available)) {
     36		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
     37		return -ENOENT;
     38	}
     39
     40	WARN_ON(fh->navailable == 0);
     41
     42	kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
     43	list_del(&kev->list);
     44	fh->navailable--;
     45
     46	kev->event.pending = fh->navailable;
     47	*event = kev->event;
     48	ts = ns_to_timespec64(kev->ts);
     49	event->timestamp.tv_sec = ts.tv_sec;
     50	event->timestamp.tv_nsec = ts.tv_nsec;
     51	kev->sev->first = sev_pos(kev->sev, 1);
     52	kev->sev->in_use--;
     53
     54	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
     55
     56	return 0;
     57}
     58
     59int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
     60		       int nonblocking)
     61{
     62	int ret;
     63
     64	if (nonblocking)
     65		return __v4l2_event_dequeue(fh, event);
     66
     67	/* Release the vdev lock while waiting */
     68	if (fh->vdev->lock)
     69		mutex_unlock(fh->vdev->lock);
     70
     71	do {
     72		ret = wait_event_interruptible(fh->wait,
     73					       fh->navailable != 0);
     74		if (ret < 0)
     75			break;
     76
     77		ret = __v4l2_event_dequeue(fh, event);
     78	} while (ret == -ENOENT);
     79
     80	if (fh->vdev->lock)
     81		mutex_lock(fh->vdev->lock);
     82
     83	return ret;
     84}
     85EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
     86
     87/* Caller must hold fh->vdev->fh_lock! */
     88static struct v4l2_subscribed_event *v4l2_event_subscribed(
     89		struct v4l2_fh *fh, u32 type, u32 id)
     90{
     91	struct v4l2_subscribed_event *sev;
     92
     93	assert_spin_locked(&fh->vdev->fh_lock);
     94
     95	list_for_each_entry(sev, &fh->subscribed, list)
     96		if (sev->type == type && sev->id == id)
     97			return sev;
     98
     99	return NULL;
    100}
    101
    102static void __v4l2_event_queue_fh(struct v4l2_fh *fh,
    103				  const struct v4l2_event *ev, u64 ts)
    104{
    105	struct v4l2_subscribed_event *sev;
    106	struct v4l2_kevent *kev;
    107	bool copy_payload = true;
    108
    109	/* Are we subscribed? */
    110	sev = v4l2_event_subscribed(fh, ev->type, ev->id);
    111	if (sev == NULL)
    112		return;
    113
    114	/* Increase event sequence number on fh. */
    115	fh->sequence++;
    116
    117	/* Do we have any free events? */
    118	if (sev->in_use == sev->elems) {
    119		/* no, remove the oldest one */
    120		kev = sev->events + sev_pos(sev, 0);
    121		list_del(&kev->list);
    122		sev->in_use--;
    123		sev->first = sev_pos(sev, 1);
    124		fh->navailable--;
    125		if (sev->elems == 1) {
    126			if (sev->ops && sev->ops->replace) {
    127				sev->ops->replace(&kev->event, ev);
    128				copy_payload = false;
    129			}
    130		} else if (sev->ops && sev->ops->merge) {
    131			struct v4l2_kevent *second_oldest =
    132				sev->events + sev_pos(sev, 0);
    133			sev->ops->merge(&kev->event, &second_oldest->event);
    134		}
    135	}
    136
    137	/* Take one and fill it. */
    138	kev = sev->events + sev_pos(sev, sev->in_use);
    139	kev->event.type = ev->type;
    140	if (copy_payload)
    141		kev->event.u = ev->u;
    142	kev->event.id = ev->id;
    143	kev->ts = ts;
    144	kev->event.sequence = fh->sequence;
    145	sev->in_use++;
    146	list_add_tail(&kev->list, &fh->available);
    147
    148	fh->navailable++;
    149
    150	wake_up_all(&fh->wait);
    151}
    152
    153void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
    154{
    155	struct v4l2_fh *fh;
    156	unsigned long flags;
    157	u64 ts;
    158
    159	if (vdev == NULL)
    160		return;
    161
    162	ts = ktime_get_ns();
    163
    164	spin_lock_irqsave(&vdev->fh_lock, flags);
    165
    166	list_for_each_entry(fh, &vdev->fh_list, list)
    167		__v4l2_event_queue_fh(fh, ev, ts);
    168
    169	spin_unlock_irqrestore(&vdev->fh_lock, flags);
    170}
    171EXPORT_SYMBOL_GPL(v4l2_event_queue);
    172
    173void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
    174{
    175	unsigned long flags;
    176	u64 ts = ktime_get_ns();
    177
    178	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
    179	__v4l2_event_queue_fh(fh, ev, ts);
    180	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
    181}
    182EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
    183
    184int v4l2_event_pending(struct v4l2_fh *fh)
    185{
    186	return fh->navailable;
    187}
    188EXPORT_SYMBOL_GPL(v4l2_event_pending);
    189
    190void v4l2_event_wake_all(struct video_device *vdev)
    191{
    192	struct v4l2_fh *fh;
    193	unsigned long flags;
    194
    195	if (!vdev)
    196		return;
    197
    198	spin_lock_irqsave(&vdev->fh_lock, flags);
    199
    200	list_for_each_entry(fh, &vdev->fh_list, list)
    201		wake_up_all(&fh->wait);
    202
    203	spin_unlock_irqrestore(&vdev->fh_lock, flags);
    204}
    205EXPORT_SYMBOL_GPL(v4l2_event_wake_all);
    206
    207static void __v4l2_event_unsubscribe(struct v4l2_subscribed_event *sev)
    208{
    209	struct v4l2_fh *fh = sev->fh;
    210	unsigned int i;
    211
    212	lockdep_assert_held(&fh->subscribe_lock);
    213	assert_spin_locked(&fh->vdev->fh_lock);
    214
    215	/* Remove any pending events for this subscription */
    216	for (i = 0; i < sev->in_use; i++) {
    217		list_del(&sev->events[sev_pos(sev, i)].list);
    218		fh->navailable--;
    219	}
    220	list_del(&sev->list);
    221}
    222
    223int v4l2_event_subscribe(struct v4l2_fh *fh,
    224			 const struct v4l2_event_subscription *sub, unsigned int elems,
    225			 const struct v4l2_subscribed_event_ops *ops)
    226{
    227	struct v4l2_subscribed_event *sev, *found_ev;
    228	unsigned long flags;
    229	unsigned int i;
    230	int ret = 0;
    231
    232	if (sub->type == V4L2_EVENT_ALL)
    233		return -EINVAL;
    234
    235	if (elems < 1)
    236		elems = 1;
    237
    238	sev = kvzalloc(struct_size(sev, events, elems), GFP_KERNEL);
    239	if (!sev)
    240		return -ENOMEM;
    241	for (i = 0; i < elems; i++)
    242		sev->events[i].sev = sev;
    243	sev->type = sub->type;
    244	sev->id = sub->id;
    245	sev->flags = sub->flags;
    246	sev->fh = fh;
    247	sev->ops = ops;
    248	sev->elems = elems;
    249
    250	mutex_lock(&fh->subscribe_lock);
    251
    252	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
    253	found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
    254	if (!found_ev)
    255		list_add(&sev->list, &fh->subscribed);
    256	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
    257
    258	if (found_ev) {
    259		/* Already listening */
    260		kvfree(sev);
    261	} else if (sev->ops && sev->ops->add) {
    262		ret = sev->ops->add(sev, elems);
    263		if (ret) {
    264			spin_lock_irqsave(&fh->vdev->fh_lock, flags);
    265			__v4l2_event_unsubscribe(sev);
    266			spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
    267			kvfree(sev);
    268		}
    269	}
    270
    271	mutex_unlock(&fh->subscribe_lock);
    272
    273	return ret;
    274}
    275EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
    276
    277void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
    278{
    279	struct v4l2_event_subscription sub;
    280	struct v4l2_subscribed_event *sev;
    281	unsigned long flags;
    282
    283	do {
    284		sev = NULL;
    285
    286		spin_lock_irqsave(&fh->vdev->fh_lock, flags);
    287		if (!list_empty(&fh->subscribed)) {
    288			sev = list_first_entry(&fh->subscribed,
    289					struct v4l2_subscribed_event, list);
    290			sub.type = sev->type;
    291			sub.id = sev->id;
    292		}
    293		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
    294		if (sev)
    295			v4l2_event_unsubscribe(fh, &sub);
    296	} while (sev);
    297}
    298EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
    299
    300int v4l2_event_unsubscribe(struct v4l2_fh *fh,
    301			   const struct v4l2_event_subscription *sub)
    302{
    303	struct v4l2_subscribed_event *sev;
    304	unsigned long flags;
    305
    306	if (sub->type == V4L2_EVENT_ALL) {
    307		v4l2_event_unsubscribe_all(fh);
    308		return 0;
    309	}
    310
    311	mutex_lock(&fh->subscribe_lock);
    312
    313	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
    314
    315	sev = v4l2_event_subscribed(fh, sub->type, sub->id);
    316	if (sev != NULL)
    317		__v4l2_event_unsubscribe(sev);
    318
    319	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
    320
    321	if (sev && sev->ops && sev->ops->del)
    322		sev->ops->del(sev);
    323
    324	mutex_unlock(&fh->subscribe_lock);
    325
    326	kvfree(sev);
    327
    328	return 0;
    329}
    330EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
    331
    332int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh,
    333				  struct v4l2_event_subscription *sub)
    334{
    335	return v4l2_event_unsubscribe(fh, sub);
    336}
    337EXPORT_SYMBOL_GPL(v4l2_event_subdev_unsubscribe);
    338
    339static void v4l2_event_src_replace(struct v4l2_event *old,
    340				const struct v4l2_event *new)
    341{
    342	u32 old_changes = old->u.src_change.changes;
    343
    344	old->u.src_change = new->u.src_change;
    345	old->u.src_change.changes |= old_changes;
    346}
    347
    348static void v4l2_event_src_merge(const struct v4l2_event *old,
    349				struct v4l2_event *new)
    350{
    351	new->u.src_change.changes |= old->u.src_change.changes;
    352}
    353
    354static const struct v4l2_subscribed_event_ops v4l2_event_src_ch_ops = {
    355	.replace = v4l2_event_src_replace,
    356	.merge = v4l2_event_src_merge,
    357};
    358
    359int v4l2_src_change_event_subscribe(struct v4l2_fh *fh,
    360				const struct v4l2_event_subscription *sub)
    361{
    362	if (sub->type == V4L2_EVENT_SOURCE_CHANGE)
    363		return v4l2_event_subscribe(fh, sub, 0, &v4l2_event_src_ch_ops);
    364	return -EINVAL;
    365}
    366EXPORT_SYMBOL_GPL(v4l2_src_change_event_subscribe);
    367
    368int v4l2_src_change_event_subdev_subscribe(struct v4l2_subdev *sd,
    369		struct v4l2_fh *fh, struct v4l2_event_subscription *sub)
    370{
    371	return v4l2_src_change_event_subscribe(fh, sub);
    372}
    373EXPORT_SYMBOL_GPL(v4l2_src_change_event_subdev_subscribe);