@@ -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
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;
+
+static void v4l2_event_kmem_ctor(void *ptr)
+{
+ struct _v4l2_event *ev = ptr;
+
+ INIT_LIST_HEAD(&ev->list);
+}
+
+int v4l2_event_init(struct v4l2_events *events)
+{
+ if (!event_kmem)
+ event_kmem = kmem_cache_create("event_kmem",
+ sizeof(struct _v4l2_event), 0,
+ SLAB_HWCACHE_ALIGN,
+ &v4l2_event_kmem_ctor);
+
+ if (!event_kmem)
+ return -ENOMEM;
+
+ init_waitqueue_head(&events->wait);
+ spin_lock_init(&events->lock);
+
+ INIT_LIST_HEAD(&events->available);
+ INIT_LIST_HEAD(&events->subscribed);
+
+ return 0;
+};
+EXPORT_SYMBOL_GPL(v4l2_event_init);
+
+void v4l2_event_exit(struct v4l2_events *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);
+
+int v4l2_event_dequeue(struct v4l2_events *events, struct v4l2_event *event)
+{
+ struct _v4l2_event *ev;
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&events->lock, flags);
+
+ if (list_empty(&events->available)) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ev = list_first_entry(&events->available, struct _v4l2_event, list);
+ list_del(&ev->list);
+
+ ev->event.count = !list_empty(&events->available);
+
+ memcpy(event, &ev->event, sizeof(ev->event));
+
+ kmem_cache_free(event_kmem, ev);
+
+out:
+
+ spin_unlock_irqrestore(&events->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
+
+static struct _v4l2_event *v4l2_event_queue_get(struct v4l2_events *events)
+{
+ return kmem_cache_alloc(event_kmem, GFP_KERNEL | GFP_ATOMIC);
+}
+
+static void v4l2_event_queue_put(struct v4l2_events *events,
+ struct _v4l2_event *ev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&events->lock, flags);
+
+ list_add(&ev->list, &events->available);
+
+ spin_unlock_irqrestore(&events->lock, flags);
+
+ wake_up_all(&events->wait);
+}
+
+static struct v4l2_subscribed_event *__v4l2_event_subscribed(
+ struct v4l2_events *events, u32 type)
+{
+ 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_events *events, u32 type)
+{
+ struct v4l2_subscribed_event *ev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&events->lock, flags);
+
+ ev = __v4l2_event_subscribed(events, 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_file_handle *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->events, ev->type))
+ continue;
+
+ _ev = v4l2_event_queue_get(&fh->events);
+ if (!_ev)
+ continue;
+
+ _ev->event = *ev;
+ v4l2_event_queue_put(&fh->events, _ev);
+
+ printk(KERN_ALERT "deliver to some fh\n");
+ }
+
+ spin_unlock_irqrestore(&vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue);
+
+int v4l2_event_pending(struct v4l2_events *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_events *events,
+ struct v4l2_event_subscription *sub)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct v4l2_subscribed_event *ev;
+
+ ev = kmalloc(sizeof(*ev), GFP_KERNEL);
+ if (!ev)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&events->lock, flags);
+
+ if (__v4l2_event_subscribed(events, 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_events *events,
+ struct v4l2_event_subscription *sub)
+{
+ struct v4l2_subscribed_event *ev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&events->lock, flags);
+
+ ev = __v4l2_event_subscribed(events, sub->type);
+
+ if (ev != NULL)
+ list_del(&ev->list);
+
+ spin_unlock_irqrestore(&events->lock, flags);
+
+ return ev == NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
@@ -59,6 +59,7 @@ int v4l2_file_handle_add(struct video_device *vdev, struct file *filp)
{
struct v4l2_file_handle *fh;
unsigned long flags;
+ int ret;
fh = kmalloc(sizeof(*fh), GFP_KERNEL);
if (!fh)
@@ -66,6 +67,12 @@ int v4l2_file_handle_add(struct video_device *vdev, struct file *filp)
fh->filp = filp;
+ ret = v4l2_event_init(&fh->events);
+ if (ret) {
+ kfree(fh);
+ return ret;
+ }
+
spin_lock_irqsave(&vdev->fh_lock, flags);
list_add(&fh->list, &vdev->fh);
spin_unlock_irqrestore(&vdev->fh_lock, flags);
@@ -79,6 +86,8 @@ void v4l2_file_handle_del(struct video_device *vdev, struct file *filp)
struct v4l2_file_handle *fh = v4l2_file_handle_get(vdev, filp);
unsigned long flags;
+ v4l2_event_exit(&fh->events);
+
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,73 @@
+/*
+ * 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/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/videodev2.h>
+
+#define V4L2_MAX_EVENTS 1024 /* Ought to be enough for everyone. */
+
+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(struct v4l2_events *events);
+void v4l2_event_exit(struct v4l2_events *events);
+
+int v4l2_event_dequeue(struct v4l2_events *events, struct v4l2_event *event);
+
+struct v4l2_subscribed_event *v4l2_event_subscribed(struct v4l2_events *sub,
+ u32 type);
+
+void v4l2_event_queue(struct video_device *vdev, struct v4l2_event *ev);
+int v4l2_event_pending(struct v4l2_events *events);
+
+int v4l2_event_subscribe(struct v4l2_events *sub,
+ struct v4l2_event_subscription *s);
+int v4l2_event_unsubscribe(struct v4l2_events *sub,
+ struct v4l2_event_subscription *s);
+void v4l2_event_sub_init(struct v4l2_events *sub);
+void v4l2_event_sub_exit(struct v4l2_events *sub);
+
+#endif /* V4L2_EVENT_H */
@@ -28,9 +28,12 @@
#include <linux/types.h>
#include <linux/list.h>
+#include <media/v4l2-event.h>
+
struct v4l2_file_handle {
struct list_head list;
struct file *filp;
+ struct v4l2_events events; /* events, pending and subscribed */
};
struct video_device;