/*
* generic helper functions for handling video4linux capture buffers
*
* (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
*
* Highly based on video-buf written originally by:
* (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
* (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org>
* (c) 2006 Ted Walther and John Sokol
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <media/videobuf-core.h>
#define MAGIC_BUFFER 0x20070728
#define MAGIC_CHECK(is, should) do { \
if (unlikely((is) != (should))) { \
printk(KERN_ERR "magic mismatch: %x (expected %x)\n", is, should); \
BUG(); } } while (0)
static int debug;
module_param(debug, int, 0644);
MODULE_DESCRIPTION("helper module to manage video4linux buffers");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
MODULE_LICENSE("GPL");
#define dprintk(level, fmt, arg...) do { \
if (debug >= level) \
printk(KERN_DEBUG "vbuf: " fmt , ## arg); } while (0)
/* --------------------------------------------------------------------- */
#define CALL(q, f, arg...) \
((q->int_ops->f) ? q->int_ops->f(arg) : 0)
void *videobuf_alloc(struct videobuf_queue *q)
{
struct videobuf_buffer *vb;
BUG_ON(q->msize < sizeof(*vb));
if (!q->int_ops || !q->int_ops->alloc) {
printk(KERN_ERR "No specific ops defined!\n");
BUG();
}
vb = q->int_ops->alloc(q->msize);
if (NULL != vb) {
init_waitqueue_head(&vb->done);
vb->magic = MAGIC_BUFFER;
}
return vb;
}
int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr)
{
int retval = 0;
DECLARE_WAITQUEUE(wait, current);
MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
add_wait_queue(&vb->done, &wait);
while (vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) {
if (non_blocking) {
retval = -EAGAIN;
break;
}
set_current_state(intr ? TASK_INTERRUPTIBLE
: TASK_UNINTERRUPTIBLE);
if (vb->state == VIDEOBUF_ACTIVE ||
vb->state == VIDEOBUF_QUEUED)
schedule();
set_current_state(TASK_RUNNING);
if (intr && signal_pending(current)) {
dprintk(1, "buffer waiton: -EINTR\n");
retval = -EINTR;
break;
}
}
remove_wait_queue(&vb->done, &wait);
return retval;
}
int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
struct v4l2_framebuffer *fbuf)
{
MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
/* This is required to avoid OOPS on some cases,
since mmap_mapper() method should be called before _iolock.
On some cases, the mmap_mapper() is called only after scheduling.
*/
if (vb->memory == V4L2_MEMORY_MMAP) {
wait_event_timeout(vb->done, q->is_mmapped,
msecs_to_jiffies(100));
if (!q->is_mmapped) {
printk(KERN_ERR
"Error: mmap_mapper() never called!\n");
return -EINVAL;
}
}
return CALL(q, iolock, q, vb, fbuf);
}
/* --------------------------------------------------------------------- */
void videobuf_queue_core_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
void *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
void *priv,
struct videobuf_qtype_ops *int_ops)
{
memset(q, 0, sizeof(*q));
q->irqlock = irqlock;
q->dev = dev;
q->type = type;
q->field = field;
q->msize = msize;
q->ops