v4l2-event.c 7.3 KB
Newer Older
S
Sakari Ailus 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*
 * v4l2-event.c
 *
 * V4L2 events.
 *
 * Copyright (C) 2009--2010 Nokia Corporation.
 *
 * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include <media/v4l2-dev.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>

#include <linux/sched.h>
#include <linux/slab.h>
31
#include <linux/export.h>
S
Sakari Ailus 已提交
32

33
static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx)
S
Sakari Ailus 已提交
34
{
35 36
	idx += sev->first;
	return idx >= sev->elems ? idx - sev->elems : idx;
S
Sakari Ailus 已提交
37 38 39 40 41 42 43 44 45
}

static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
{
	struct v4l2_kevent *kev;
	unsigned long flags;

	spin_lock_irqsave(&fh->vdev->fh_lock, flags);

46
	if (list_empty(&fh->available)) {
S
Sakari Ailus 已提交
47 48 49 50
		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
		return -ENOENT;
	}

51
	WARN_ON(fh->navailable == 0);
S
Sakari Ailus 已提交
52

53
	kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
54
	list_del(&kev->list);
55
	fh->navailable--;
S
Sakari Ailus 已提交
56

57
	kev->event.pending = fh->navailable;
S
Sakari Ailus 已提交
58
	*event = kev->event;
59 60
	kev->sev->first = sev_pos(kev->sev, 1);
	kev->sev->in_use--;
S
Sakari Ailus 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74

	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);

	return 0;
}

int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
		       int nonblocking)
{
	int ret;

	if (nonblocking)
		return __v4l2_event_dequeue(fh, event);

75 76 77 78
	/* Release the vdev lock while waiting */
	if (fh->vdev->lock)
		mutex_unlock(fh->vdev->lock);

S
Sakari Ailus 已提交
79
	do {
80 81
		ret = wait_event_interruptible(fh->wait,
					       fh->navailable != 0);
S
Sakari Ailus 已提交
82
		if (ret < 0)
83
			break;
S
Sakari Ailus 已提交
84 85 86 87

		ret = __v4l2_event_dequeue(fh, event);
	} while (ret == -ENOENT);

88 89 90
	if (fh->vdev->lock)
		mutex_lock(fh->vdev->lock);

S
Sakari Ailus 已提交
91 92
	return ret;
}
93
EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
S
Sakari Ailus 已提交
94

95
/* Caller must hold fh->vdev->fh_lock! */
S
Sakari Ailus 已提交
96
static struct v4l2_subscribed_event *v4l2_event_subscribed(
97
		struct v4l2_fh *fh, u32 type, u32 id)
S
Sakari Ailus 已提交
98 99 100
{
	struct v4l2_subscribed_event *sev;

101
	assert_spin_locked(&fh->vdev->fh_lock);
S
Sakari Ailus 已提交
102

103
	list_for_each_entry(sev, &fh->subscribed, list)
104
		if (sev->type == type && sev->id == id)
S
Sakari Ailus 已提交
105 106 107 108 109
			return sev;

	return NULL;
}

110 111 112 113 114
static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
		const struct timespec *ts)
{
	struct v4l2_subscribed_event *sev;
	struct v4l2_kevent *kev;
115
	bool copy_payload = true;
116 117 118 119 120 121

	/* Are we subscribed? */
	sev = v4l2_event_subscribed(fh, ev->type, ev->id);
	if (sev == NULL)
		return;

122 123 124 125 126 127 128 129
	/*
	 * If the event has been added to the fh->subscribed list, but its
	 * add op has not completed yet elems will be 0, treat this as
	 * not being subscribed.
	 */
	if (!sev->elems)
		return;

130
	/* Increase event sequence number on fh. */
131
	fh->sequence++;
132 133

	/* Do we have any free events? */
134 135 136 137 138 139 140
	if (sev->in_use == sev->elems) {
		/* no, remove the oldest one */
		kev = sev->events + sev_pos(sev, 0);
		list_del(&kev->list);
		sev->in_use--;
		sev->first = sev_pos(sev, 1);
		fh->navailable--;
141
		if (sev->elems == 1) {
142 143
			if (sev->ops && sev->ops->replace) {
				sev->ops->replace(&kev->event, ev);
144 145
				copy_payload = false;
			}
146
		} else if (sev->ops && sev->ops->merge) {
147 148
			struct v4l2_kevent *second_oldest =
				sev->events + sev_pos(sev, 0);
149
			sev->ops->merge(&kev->event, &second_oldest->event);
150
		}
151
	}
152 153

	/* Take one and fill it. */
154
	kev = sev->events + sev_pos(sev, sev->in_use);
155
	kev->event.type = ev->type;
156 157
	if (copy_payload)
		kev->event.u = ev->u;
158 159
	kev->event.id = ev->id;
	kev->event.timestamp = *ts;
160
	kev->event.sequence = fh->sequence;
161 162
	sev->in_use++;
	list_add_tail(&kev->list, &fh->available);
163

164
	fh->navailable++;
165

166
	wake_up_all(&fh->wait);
167 168
}

S
Sakari Ailus 已提交
169 170 171 172 173 174 175 176 177 178
void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
{
	struct v4l2_fh *fh;
	unsigned long flags;
	struct timespec timestamp;

	ktime_get_ts(&timestamp);

	spin_lock_irqsave(&vdev->fh_lock, flags);

179
	list_for_each_entry(fh, &vdev->fh_list, list)
180
		__v4l2_event_queue_fh(fh, ev, &timestamp);
S
Sakari Ailus 已提交
181 182 183 184 185

	spin_unlock_irqrestore(&vdev->fh_lock, flags);
}
EXPORT_SYMBOL_GPL(v4l2_event_queue);

186 187 188 189 190 191 192 193 194 195 196 197 198
void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
{
	unsigned long flags;
	struct timespec timestamp;

	ktime_get_ts(&timestamp);

	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
	__v4l2_event_queue_fh(fh, ev, &timestamp);
	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
}
EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);

S
Sakari Ailus 已提交
199 200
int v4l2_event_pending(struct v4l2_fh *fh)
{
201
	return fh->navailable;
S
Sakari Ailus 已提交
202 203 204 205
}
EXPORT_SYMBOL_GPL(v4l2_event_pending);

int v4l2_event_subscribe(struct v4l2_fh *fh,
206 207
			 struct v4l2_event_subscription *sub, unsigned elems,
			 const struct v4l2_subscribed_event_ops *ops)
S
Sakari Ailus 已提交
208
{
209
	struct v4l2_subscribed_event *sev, *found_ev;
S
Sakari Ailus 已提交
210
	unsigned long flags;
211
	unsigned i;
S
Sakari Ailus 已提交
212

213 214 215
	if (sub->type == V4L2_EVENT_ALL)
		return -EINVAL;

216 217
	if (elems < 1)
		elems = 1;
218

219
	sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
S
Sakari Ailus 已提交
220 221
	if (!sev)
		return -ENOMEM;
222 223 224 225 226 227
	for (i = 0; i < elems; i++)
		sev->events[i].sev = sev;
	sev->type = sub->type;
	sev->id = sub->id;
	sev->flags = sub->flags;
	sev->fh = fh;
228
	sev->ops = ops;
S
Sakari Ailus 已提交
229 230

	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
231
	found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
232
	if (!found_ev)
233
		list_add(&sev->list, &fh->subscribed);
S
Sakari Ailus 已提交
234 235
	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);

236
	if (found_ev) {
237
		kfree(sev);
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
		return 0; /* Already listening */
	}

	if (sev->ops && sev->ops->add) {
		int ret = sev->ops->add(sev);
		if (ret) {
			sev->ops = NULL;
			v4l2_event_unsubscribe(fh, sub);
			return ret;
		}
	}

	/* Mark as ready for use */
	sev->elems = elems;

S
Sakari Ailus 已提交
253 254 255 256
	return 0;
}
EXPORT_SYMBOL_GPL(v4l2_event_subscribe);

257
void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
S
Sakari Ailus 已提交
258
{
259
	struct v4l2_event_subscription sub;
S
Sakari Ailus 已提交
260 261 262 263 264 265 266
	struct v4l2_subscribed_event *sev;
	unsigned long flags;

	do {
		sev = NULL;

		spin_lock_irqsave(&fh->vdev->fh_lock, flags);
267 268
		if (!list_empty(&fh->subscribed)) {
			sev = list_first_entry(&fh->subscribed,
269 270 271
					struct v4l2_subscribed_event, list);
			sub.type = sev->type;
			sub.id = sev->id;
S
Sakari Ailus 已提交
272 273
		}
		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
274 275
		if (sev)
			v4l2_event_unsubscribe(fh, &sub);
S
Sakari Ailus 已提交
276 277
	} while (sev);
}
278
EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
S
Sakari Ailus 已提交
279 280 281 282 283 284

int v4l2_event_unsubscribe(struct v4l2_fh *fh,
			   struct v4l2_event_subscription *sub)
{
	struct v4l2_subscribed_event *sev;
	unsigned long flags;
285
	int i;
S
Sakari Ailus 已提交
286 287 288 289 290 291 292 293

	if (sub->type == V4L2_EVENT_ALL) {
		v4l2_event_unsubscribe_all(fh);
		return 0;
	}

	spin_lock_irqsave(&fh->vdev->fh_lock, flags);

294
	sev = v4l2_event_subscribed(fh, sub->type, sub->id);
295
	if (sev != NULL) {
296 297 298 299 300
		/* Remove any pending events for this subscription */
		for (i = 0; i < sev->in_use; i++) {
			list_del(&sev->events[sev_pos(sev, i)].list);
			fh->navailable--;
		}
S
Sakari Ailus 已提交
301
		list_del(&sev->list);
302
	}
S
Sakari Ailus 已提交
303 304

	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
305 306 307 308

	if (sev && sev->ops && sev->ops->del)
		sev->ops->del(sev);

S
Sakari Ailus 已提交
309 310 311 312 313
	kfree(sev);

	return 0;
}
EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);