@@ -10,7 +10,8 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o
omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
-videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o
+videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
+ v4l2-event.o
# V4L2 core modules
@@ -613,22 +613,36 @@ static int __init videodev_init(void)
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
int ret;
+ ret = v4l2_event_init();
+ if (ret < 0) {
+ printk(KERN_WARNING "videodev: unable to initialise events\n");
+ return ret;
+ }
+
printk(KERN_INFO "Linux video capture interface: v2.00\n");
ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
if (ret < 0) {
printk(KERN_WARNING "videodev: unable to get major %d\n",
VIDEO_MAJOR);
- return ret;
+ goto out_register_chrdev_region;
}
ret = class_register(&video_class);
if (ret < 0) {
- unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
printk(KERN_WARNING "video_dev: class_register failed\n");
- return -EIO;
+ ret = -EIO;
+ goto out_class_register;
}
return 0;
+
+out_class_register:
+ unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
+
+out_register_chrdev_region:
+ v4l2_event_exit();
+
+ return ret;
}
static void __exit videodev_exit(void)
@@ -637,6 +651,7 @@ static void __exit videodev_exit(void)
class_unregister(&video_class);
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
+ v4l2_event_exit();
}
module_init(videodev_init)
new file mode 100644
@@ -0,0 +1,254 @@
+/*
+ * drivers/media/video/v4l2-event.c
+ *
+ * V4L2 events.
+ *
+ * Copyright (C) 2009 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-event.h>
+
+#include <linux/sched.h>
+
+static struct kmem_cache *event_kmem;
+
+int v4l2_event_init(void)
+{
+ event_kmem = kmem_cache_create("event_kmem",
+ sizeof(struct _v4l2_event), 0,
+ SLAB_HWCACHE_ALIGN,
+ NULL);
+
+ if (!event_kmem)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void v4l2_event_exit(void)
+{
+ kmem_cache_destroy(event_kmem);
+}
+
+void v4l2_event_init_fh(struct v4l2_fh *fh)
+{
+ struct v4l2_events *events = &fh->events;
+
+ init_waitqueue_head(&events->wait);
+ spin_lock_init(&events->lock);
+
+ INIT_LIST_HEAD(&events->available);
+ INIT_LIST_HEAD(&events->subscribed);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_init_fh);
+
+void v4l2_event_exit_fh(struct v4l2_fh *fh)
+{
+ struct v4l2_events *events = &fh->events;
+
+ while (!list_empty(&events->available)) {
+ struct _v4l2_event *ev;
+
+ ev = list_entry(events->available.next,
+ struct _v4l2_event, list);
+
+ list_del(&ev->list);
+
+ kmem_cache_free(event_kmem, ev);
+ }
+
+ while (!list_empty(&events->subscribed)) {
+ struct v4l2_subscribed_event *sub;
+
+ sub = list_entry(events->subscribed.next,
+ struct v4l2_subscribed_event, list);
+
+ list_del(&sub->list);
+
+ kfree(sub);
+ }
+}
+EXPORT_SYMBOL_GPL(v4l2_event_exit_fh);
+
+int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
+{
+ struct v4l2_events *events = &fh->events;
+ struct _v4l2_event *ev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&events->lock, flags);
+
+ if (list_empty(&events->available)) {
+ spin_unlock_irqrestore(&events->lock, flags);
+ return -ENOENT;
+ }
+
+ ev = list_first_entry(&events->available, struct _v4l2_event, list);
+ list_del(&ev->list);
+
+ ev->event.count = !list_empty(&events->available);
+
+ spin_unlock_irqrestore(&events->lock, flags);
+
+ memcpy(event, &ev->event, sizeof(ev->event));
+
+ kmem_cache_free(event_kmem, ev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
+
+static struct v4l2_subscribed_event *__v4l2_event_subscribed(
+ struct v4l2_fh *fh, u32 type)
+{
+ struct v4l2_events *events = &fh->events;
+ struct v4l2_subscribed_event *ev;
+
+ list_for_each_entry(ev, &events->subscribed, list) {
+ if (ev->type == type)
+ return ev;
+ }
+
+ return NULL;
+}
+
+struct v4l2_subscribed_event *v4l2_event_subscribed(
+ struct v4l2_fh *fh, u32 type)
+{
+ struct v4l2_events *events = &fh->events;
+ struct v4l2_subscribed_event *ev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&events->lock, flags);
+
+ ev = __v4l2_event_subscribed(fh, type);
+
+ spin_unlock_irqrestore(&events->lock, flags);
+
+ return ev;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_subscribed);
+
+void v4l2_event_queue(struct video_device *vdev, struct v4l2_event *ev)
+{
+ struct v4l2_fh *fh;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vdev->fh_lock, flags);
+
+ list_for_each_entry(fh, &vdev->fh, list) {
+ struct _v4l2_event *_ev;
+
+ if (!v4l2_event_subscribed(fh, ev->type))
+ continue;
+
+ _ev = kmem_cache_alloc(event_kmem, GFP_ATOMIC);
+ if (!_ev)
+ continue;
+
+ _ev->event = *ev;
+
+ spin_lock(&fh->events.lock);
+ list_add_tail(&_ev->list, &fh->events.available);
+ spin_unlock(&fh->events.lock);
+
+ wake_up_all(&fh->events.wait);
+ }
+
+ spin_unlock_irqrestore(&vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue);
+
+int v4l2_event_pending(struct v4l2_fh *fh)
+{
+ struct v4l2_events *events = &fh->events;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&events->lock, flags);
+ ret = !list_empty(&events->available);
+ spin_unlock_irqrestore(&events->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_pending);
+
+int v4l2_event_subscribe(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct v4l2_events *events = &fh->events;
+ struct v4l2_subscribed_event *ev;
+ unsigned long flags;
+ int ret = 0;
+
+ /* Allow subscribing to valid events only. */
+ if (sub->type < V4L2_EVENT_PRIVATE_START)
+ switch (sub->type) {
+ case V4L2_EVENT_ALL:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ev = kmalloc(sizeof(*ev), GFP_KERNEL);
+ if (!ev)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&events->lock, flags);
+
+ if (__v4l2_event_subscribed(fh, sub->type) != NULL) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ INIT_LIST_HEAD(&ev->list);
+ ev->type = sub->type;
+
+ list_add(&ev->list, &events->subscribed);
+
+out:
+ spin_unlock_irqrestore(&events->lock, flags);
+
+ if (ret)
+ kfree(ev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
+
+int v4l2_event_unsubscribe(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct v4l2_events *events = &fh->events;
+ struct v4l2_subscribed_event *ev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&events->lock, flags);
+
+ ev = __v4l2_event_subscribed(fh, sub->type);
+
+ if (ev != NULL)
+ list_del(&ev->list);
+
+ spin_unlock_irqrestore(&events->lock, flags);
+
+ return ev == NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
@@ -32,6 +32,8 @@ int v4l2_fh_add(struct video_device *vdev, struct v4l2_fh *fh)
{
unsigned long flags;
+ v4l2_event_init_fh(fh);
+
spin_lock_irqsave(&vdev->fh_lock, flags);
list_add(&fh->list, &vdev->fh);
spin_unlock_irqrestore(&vdev->fh_lock, flags);
@@ -44,6 +46,8 @@ void v4l2_fh_del(struct video_device *vdev, struct v4l2_fh *fh)
{
unsigned long flags;
+ v4l2_event_exit_fh(fh);
+
spin_lock_irqsave(&vdev->fh_lock, flags);
list_del(&fh->list);
spin_unlock_irqrestore(&vdev->fh_lock, flags);
new file mode 100644
@@ -0,0 +1,65 @@
+/*
+ * include/media/v4l2-event.h
+ *
+ * V4L2 events.
+ *
+ * Copyright (C) 2009 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
+ */
+
+#ifndef V4L2_EVENT_H
+#define V4L2_EVENT_H
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+struct v4l2_fh;
+struct video_device;
+
+struct _v4l2_event {
+ struct list_head list;
+ struct v4l2_event event;
+};
+
+struct v4l2_events {
+ spinlock_t lock; /* Protect everything here. */
+ struct list_head available;
+ wait_queue_head_t wait;
+ struct list_head subscribed; /* Subscribed events. */
+};
+
+struct v4l2_subscribed_event {
+ struct list_head list;
+ u32 type;
+};
+
+int v4l2_event_init(void);
+void v4l2_event_exit(void);
+void v4l2_event_init_fh(struct v4l2_fh *fh);
+void v4l2_event_exit_fh(struct v4l2_fh *fh);
+int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event);
+struct v4l2_subscribed_event *v4l2_event_subscribed(
+ struct v4l2_fh *fh, u32 type);
+void v4l2_event_queue(struct video_device *vdev, struct v4l2_event *ev);
+int v4l2_event_pending(struct v4l2_fh *fh);
+int v4l2_event_subscribe(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+int v4l2_event_unsubscribe(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+
+#endif /* V4L2_EVENT_H */
@@ -28,8 +28,11 @@
#include <linux/types.h>
#include <linux/list.h>
+#include <media/v4l2-event.h>
+
struct v4l2_fh {
struct list_head list;
+ struct v4l2_events events; /* events, pending and subscribed */
};
struct video_device;