Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 193045
b: refs/heads/master
c: c3b5b02
h: refs/heads/master
i:
  193043: 80ae97a
v: v3
  • Loading branch information
Sakari Ailus authored and Mauro Carvalho Chehab committed May 19, 2010
1 parent de14927 commit 7cdd101
Show file tree
Hide file tree
Showing 6 changed files with 367 additions and 3 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: fda1021477b390506ebed0225eaa6d31a903e2b7
refs/heads/master: c3b5b0241f620a356c97d8f43343e721c718806d
3 changes: 2 additions & 1 deletion trunk/drivers/media/video/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
290 changes: 290 additions & 0 deletions trunk/drivers/media/video/v4l2-event.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
/*
* 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>

int v4l2_event_init(struct v4l2_fh *fh)
{
fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL);
if (fh->events == NULL)
return -ENOMEM;

init_waitqueue_head(&fh->events->wait);

INIT_LIST_HEAD(&fh->events->free);
INIT_LIST_HEAD(&fh->events->available);
INIT_LIST_HEAD(&fh->events->subscribed);

fh->events->sequence = -1;

return 0;
}

int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n)
{
struct v4l2_events *events = fh->events;
unsigned long flags;

if (!events) {
WARN_ON(1);
return -ENOMEM;
}

while (events->nallocated < n) {
struct v4l2_kevent *kev;

kev = kzalloc(sizeof(*kev), GFP_KERNEL);
if (kev == NULL)
return -ENOMEM;

spin_lock_irqsave(&fh->vdev->fh_lock, flags);
list_add_tail(&kev->list, &events->free);
events->nallocated++;
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
}

return 0;
}
EXPORT_SYMBOL_GPL(v4l2_event_alloc);

#define list_kfree(list, type, member) \
while (!list_empty(list)) { \
type *hi; \
hi = list_first_entry(list, type, member); \
list_del(&hi->member); \
kfree(hi); \
}

void v4l2_event_free(struct v4l2_fh *fh)
{
struct v4l2_events *events = fh->events;

if (!events)
return;

list_kfree(&events->free, struct v4l2_kevent, list);
list_kfree(&events->available, struct v4l2_kevent, list);
list_kfree(&events->subscribed, struct v4l2_subscribed_event, list);

kfree(events);
fh->events = NULL;
}
EXPORT_SYMBOL_GPL(v4l2_event_free);

static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
{
struct v4l2_events *events = fh->events;
struct v4l2_kevent *kev;
unsigned long flags;

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

if (list_empty(&events->available)) {
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
return -ENOENT;
}

WARN_ON(events->navailable == 0);

kev = list_first_entry(&events->available, struct v4l2_kevent, list);
list_move(&kev->list, &events->free);
events->navailable--;

kev->event.pending = events->navailable;
*event = kev->event;

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

return 0;
}

int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
int nonblocking)
{
struct v4l2_events *events = fh->events;
int ret;

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

do {
ret = wait_event_interruptible(events->wait,
events->navailable != 0);
if (ret < 0)
return ret;

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

return ret;
}

/* Caller must hold fh->event->lock! */
static struct v4l2_subscribed_event *v4l2_event_subscribed(
struct v4l2_fh *fh, u32 type)
{
struct v4l2_events *events = fh->events;
struct v4l2_subscribed_event *sev;

WARN_ON(!spin_is_locked(&fh->vdev->fh_lock));

list_for_each_entry(sev, &events->subscribed, list) {
if (sev->type == type)
return sev;
}

return NULL;
}

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);

list_for_each_entry(fh, &vdev->fh_list, list) {
struct v4l2_events *events = fh->events;
struct v4l2_kevent *kev;

/* Are we subscribed? */
if (!v4l2_event_subscribed(fh, ev->type))
continue;

/* Increase event sequence number on fh. */
events->sequence++;

/* Do we have any free events? */
if (list_empty(&events->free))
continue;

/* Take one and fill it. */
kev = list_first_entry(&events->free, struct v4l2_kevent, list);
kev->event.type = ev->type;
kev->event.u = ev->u;
kev->event.timestamp = timestamp;
kev->event.sequence = events->sequence;
list_move_tail(&kev->list, &events->available);

events->navailable++;

wake_up_all(&events->wait);
}

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

int v4l2_event_pending(struct v4l2_fh *fh)
{
return fh->events->navailable;
}
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 *sev;
unsigned long flags;

if (fh->events == NULL) {
WARN_ON(1);
return -ENOMEM;
}

sev = kmalloc(sizeof(*sev), GFP_KERNEL);
if (!sev)
return -ENOMEM;

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

if (v4l2_event_subscribed(fh, sub->type) == NULL) {
INIT_LIST_HEAD(&sev->list);
sev->type = sub->type;

list_add(&sev->list, &events->subscribed);
sev = NULL;
}

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

kfree(sev);

return 0;
}
EXPORT_SYMBOL_GPL(v4l2_event_subscribe);

static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
{
struct v4l2_events *events = fh->events;
struct v4l2_subscribed_event *sev;
unsigned long flags;

do {
sev = NULL;

spin_lock_irqsave(&fh->vdev->fh_lock, flags);
if (!list_empty(&events->subscribed)) {
sev = list_first_entry(&events->subscribed,
struct v4l2_subscribed_event, list);
list_del(&sev->list);
}
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
kfree(sev);
} while (sev);
}

int v4l2_event_unsubscribe(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
struct v4l2_subscribed_event *sev;
unsigned long flags;

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

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

sev = v4l2_event_subscribed(fh, sub->type);
if (sev != NULL)
list_del(&sev->list);

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

kfree(sev);

return 0;
}
EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
6 changes: 5 additions & 1 deletion trunk/drivers/media/video/v4l2-fh.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@
#include <linux/bitops.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>

int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
{
fh->vdev = vdev;
INIT_LIST_HEAD(&fh->list);
set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags);

return 0;
return v4l2_event_init(fh);
}
EXPORT_SYMBOL_GPL(v4l2_fh_init);

Expand Down Expand Up @@ -62,5 +64,7 @@ void v4l2_fh_exit(struct v4l2_fh *fh)
return;

fh->vdev = NULL;

v4l2_event_free(fh);
}
EXPORT_SYMBOL_GPL(v4l2_fh_exit);
67 changes: 67 additions & 0 deletions trunk/include/media/v4l2-event.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* v4l2-event.h
*
* 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
*/

#ifndef V4L2_EVENT_H
#define V4L2_EVENT_H

#include <linux/types.h>
#include <linux/videodev2.h>
#include <linux/wait.h>

struct v4l2_fh;
struct video_device;

struct v4l2_kevent {
struct list_head list;
struct v4l2_event event;
};

struct v4l2_subscribed_event {
struct list_head list;
u32 type;
};

struct v4l2_events {
wait_queue_head_t wait;
struct list_head subscribed; /* Subscribed events */
struct list_head free; /* Events ready for use */
struct list_head available; /* Dequeueable event */
unsigned int navailable;
unsigned int nallocated; /* Number of allocated events */
u32 sequence;
};

int v4l2_event_init(struct v4l2_fh *fh);
int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n);
void v4l2_event_free(struct v4l2_fh *fh);
int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
int nonblocking);
void v4l2_event_queue(struct video_device *vdev, const 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 */
Loading

0 comments on commit 7cdd101

Please sign in to comment.