aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/media/video/Makefile3
-rw-r--r--drivers/media/video/v4l2-event.c290
-rw-r--r--drivers/media/video/v4l2-fh.c6
-rw-r--r--include/media/v4l2-event.h67
-rw-r--r--include/media/v4l2-fh.h2
5 files changed, 366 insertions, 2 deletions
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index c84175c55268..c96b2b1e2fdf 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -10,7 +10,8 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o
10 10
11omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o 11omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
12 12
13videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o 13videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
14 v4l2-event.o
14 15
15# V4L2 core modules 16# V4L2 core modules
16 17
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
new file mode 100644
index 000000000000..170e40f56cf7
--- /dev/null
+++ b/drivers/media/video/v4l2-event.c
@@ -0,0 +1,290 @@
1/*
2 * v4l2-event.c
3 *
4 * V4L2 events.
5 *
6 * Copyright (C) 2009--2010 Nokia Corporation.
7 *
8 * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * version 2 as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA
23 */
24
25#include <media/v4l2-dev.h>
26#include <media/v4l2-fh.h>
27#include <media/v4l2-event.h>
28
29#include <linux/sched.h>
30#include <linux/slab.h>
31
32int v4l2_event_init(struct v4l2_fh *fh)
33{
34 fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL);
35 if (fh->events == NULL)
36 return -ENOMEM;
37
38 init_waitqueue_head(&fh->events->wait);
39
40 INIT_LIST_HEAD(&fh->events->free);
41 INIT_LIST_HEAD(&fh->events->available);
42 INIT_LIST_HEAD(&fh->events->subscribed);
43
44 fh->events->sequence = -1;
45
46 return 0;
47}
48
49int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n)
50{
51 struct v4l2_events *events = fh->events;
52 unsigned long flags;
53
54 if (!events) {
55 WARN_ON(1);
56 return -ENOMEM;
57 }
58
59 while (events->nallocated < n) {
60 struct v4l2_kevent *kev;
61
62 kev = kzalloc(sizeof(*kev), GFP_KERNEL);
63 if (kev == NULL)
64 return -ENOMEM;
65
66 spin_lock_irqsave(&fh->vdev->fh_lock, flags);
67 list_add_tail(&kev->list, &events->free);
68 events->nallocated++;
69 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
70 }
71
72 return 0;
73}
74EXPORT_SYMBOL_GPL(v4l2_event_alloc);
75
76#define list_kfree(list, type, member) \
77 while (!list_empty(list)) { \
78 type *hi; \
79 hi = list_first_entry(list, type, member); \
80 list_del(&hi->member); \
81 kfree(hi); \
82 }
83
84void v4l2_event_free(struct v4l2_fh *fh)
85{
86 struct v4l2_events *events = fh->events;
87
88 if (!events)
89 return;
90
91 list_kfree(&events->free, struct v4l2_kevent, list);
92 list_kfree(&events->available, struct v4l2_kevent, list);
93 list_kfree(&events->subscribed, struct v4l2_subscribed_event, list);
94
95 kfree(events);
96 fh->events = NULL;
97}
98EXPORT_SYMBOL_GPL(v4l2_event_free);
99
100static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
101{
102 struct v4l2_events *events = fh->events;
103 struct v4l2_kevent *kev;
104 unsigned long flags;
105
106 spin_lock_irqsave(&fh->vdev->fh_lock, flags);
107
108 if (list_empty(&events->available)) {
109 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
110 return -ENOENT;
111 }
112
113 WARN_ON(events->navailable == 0);
114
115 kev = list_first_entry(&events->available, struct v4l2_kevent, list);
116 list_move(&kev->list, &events->free);
117 events->navailable--;
118
119 kev->event.pending = events->navailable;
120 *event = kev->event;
121
122 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
123
124 return 0;
125}
126
127int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
128 int nonblocking)
129{
130 struct v4l2_events *events = fh->events;
131 int ret;
132
133 if (nonblocking)
134 return __v4l2_event_dequeue(fh, event);
135
136 do {
137 ret = wait_event_interruptible(events->wait,
138 events->navailable != 0);
139 if (ret < 0)
140 return ret;
141
142 ret = __v4l2_event_dequeue(fh, event);
143 } while (ret == -ENOENT);
144
145 return ret;
146}
147
148/* Caller must hold fh->event->lock! */
149static struct v4l2_subscribed_event *v4l2_event_subscribed(
150 struct v4l2_fh *fh, u32 type)
151{
152 struct v4l2_events *events = fh->events;
153 struct v4l2_subscribed_event *sev;
154
155 WARN_ON(!spin_is_locked(&fh->vdev->fh_lock));
156
157 list_for_each_entry(sev, &events->subscribed, list) {
158 if (sev->type == type)
159 return sev;
160 }
161
162 return NULL;
163}
164
165void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
166{
167 struct v4l2_fh *fh;
168 unsigned long flags;
169 struct timespec timestamp;
170
171 ktime_get_ts(&timestamp);
172
173 spin_lock_irqsave(&vdev->fh_lock, flags);
174
175 list_for_each_entry(fh, &vdev->fh_list, list) {
176 struct v4l2_events *events = fh->events;
177 struct v4l2_kevent *kev;
178
179 /* Are we subscribed? */
180 if (!v4l2_event_subscribed(fh, ev->type))
181 continue;
182
183 /* Increase event sequence number on fh. */
184 events->sequence++;
185
186 /* Do we have any free events? */
187 if (list_empty(&events->free))
188 continue;
189
190 /* Take one and fill it. */
191 kev = list_first_entry(&events->free, struct v4l2_kevent, list);
192 kev->event.type = ev->type;
193 kev->event.u = ev->u;
194 kev->event.timestamp = timestamp;
195 kev->event.sequence = events->sequence;
196 list_move_tail(&kev->list, &events->available);
197
198 events->navailable++;
199
200 wake_up_all(&events->wait);
201 }
202
203 spin_unlock_irqrestore(&vdev->fh_lock, flags);
204}
205EXPORT_SYMBOL_GPL(v4l2_event_queue);
206
207int v4l2_event_pending(struct v4l2_fh *fh)
208{
209 return fh->events->navailable;
210}
211EXPORT_SYMBOL_GPL(v4l2_event_pending);
212
213int v4l2_event_subscribe(struct v4l2_fh *fh,
214 struct v4l2_event_subscription *sub)
215{
216 struct v4l2_events *events = fh->events;
217 struct v4l2_subscribed_event *sev;
218 unsigned long flags;
219
220 if (fh->events == NULL) {
221 WARN_ON(1);
222 return -ENOMEM;
223 }
224
225 sev = kmalloc(sizeof(*sev), GFP_KERNEL);
226 if (!sev)
227 return -ENOMEM;
228
229 spin_lock_irqsave(&fh->vdev->fh_lock, flags);
230
231 if (v4l2_event_subscribed(fh, sub->type) == NULL) {
232 INIT_LIST_HEAD(&sev->list);
233 sev->type = sub->type;
234
235 list_add(&sev->list, &events->subscribed);
236 sev = NULL;
237 }
238
239 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
240
241 kfree(sev);
242
243 return 0;
244}
245EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
246
247static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
248{
249 struct v4l2_events *events = fh->events;
250 struct v4l2_subscribed_event *sev;
251 unsigned long flags;
252
253 do {
254 sev = NULL;
255
256 spin_lock_irqsave(&fh->vdev->fh_lock, flags);
257 if (!list_empty(&events->subscribed)) {
258 sev = list_first_entry(&events->subscribed,
259 struct v4l2_subscribed_event, list);
260 list_del(&sev->list);
261 }
262 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
263 kfree(sev);
264 } while (sev);
265}
266
267int v4l2_event_unsubscribe(struct v4l2_fh *fh,
268 struct v4l2_event_subscription *sub)
269{
270 struct v4l2_subscribed_event *sev;
271 unsigned long flags;
272
273 if (sub->type == V4L2_EVENT_ALL) {
274 v4l2_event_unsubscribe_all(fh);
275 return 0;
276 }
277
278 spin_lock_irqsave(&fh->vdev->fh_lock, flags);
279
280 sev = v4l2_event_subscribed(fh, sub->type);
281 if (sev != NULL)
282 list_del(&sev->list);
283
284 spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
285
286 kfree(sev);
287
288 return 0;
289}
290EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c
index 93ea0af5fac1..aab2fb639ef1 100644
--- a/drivers/media/video/v4l2-fh.c
+++ b/drivers/media/video/v4l2-fh.c
@@ -25,6 +25,8 @@
25#include <linux/bitops.h> 25#include <linux/bitops.h>
26#include <media/v4l2-dev.h> 26#include <media/v4l2-dev.h>
27#include <media/v4l2-fh.h> 27#include <media/v4l2-fh.h>
28#include <media/v4l2-event.h>
29#include <media/v4l2-ioctl.h>
28 30
29int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) 31int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
30{ 32{
@@ -32,7 +34,7 @@ int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
32 INIT_LIST_HEAD(&fh->list); 34 INIT_LIST_HEAD(&fh->list);
33 set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags); 35 set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags);
34 36
35 return 0; 37 return v4l2_event_init(fh);
36} 38}
37EXPORT_SYMBOL_GPL(v4l2_fh_init); 39EXPORT_SYMBOL_GPL(v4l2_fh_init);
38 40
@@ -62,5 +64,7 @@ void v4l2_fh_exit(struct v4l2_fh *fh)
62 return; 64 return;
63 65
64 fh->vdev = NULL; 66 fh->vdev = NULL;
67
68 v4l2_event_free(fh);
65} 69}
66EXPORT_SYMBOL_GPL(v4l2_fh_exit); 70EXPORT_SYMBOL_GPL(v4l2_fh_exit);
diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h
new file mode 100644
index 000000000000..3b86177c8cd2
--- /dev/null
+++ b/include/media/v4l2-event.h
@@ -0,0 +1,67 @@
1/*
2 * v4l2-event.h
3 *
4 * V4L2 events.
5 *
6 * Copyright (C) 2009--2010 Nokia Corporation.
7 *
8 * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * version 2 as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA
23 */
24
25#ifndef V4L2_EVENT_H
26#define V4L2_EVENT_H
27
28#include <linux/types.h>
29#include <linux/videodev2.h>
30#include <linux/wait.h>
31
32struct v4l2_fh;
33struct video_device;
34
35struct v4l2_kevent {
36 struct list_head list;
37 struct v4l2_event event;
38};
39
40struct v4l2_subscribed_event {
41 struct list_head list;
42 u32 type;
43};
44
45struct v4l2_events {
46 wait_queue_head_t wait;
47 struct list_head subscribed; /* Subscribed events */
48 struct list_head free; /* Events ready for use */
49 struct list_head available; /* Dequeueable event */
50 unsigned int navailable;
51 unsigned int nallocated; /* Number of allocated events */
52 u32 sequence;
53};
54
55int v4l2_event_init(struct v4l2_fh *fh);
56int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n);
57void v4l2_event_free(struct v4l2_fh *fh);
58int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
59 int nonblocking);
60void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev);
61int v4l2_event_pending(struct v4l2_fh *fh);
62int v4l2_event_subscribe(struct v4l2_fh *fh,
63 struct v4l2_event_subscription *sub);
64int v4l2_event_unsubscribe(struct v4l2_fh *fh,
65 struct v4l2_event_subscription *sub);
66
67#endif /* V4L2_EVENT_H */
diff --git a/include/media/v4l2-fh.h b/include/media/v4l2-fh.h
index 4aaa1508c8b5..1d72dde320bf 100644
--- a/include/media/v4l2-fh.h
+++ b/include/media/v4l2-fh.h
@@ -29,10 +29,12 @@
29#include <linux/list.h> 29#include <linux/list.h>
30 30
31struct video_device; 31struct video_device;
32struct v4l2_events;
32 33
33struct v4l2_fh { 34struct v4l2_fh {
34 struct list_head list; 35 struct list_head list;
35 struct video_device *vdev; 36 struct video_device *vdev;
37 struct v4l2_events *events; /* events, pending and subscribed */
36}; 38};
37 39
38/* 40/*