Merge branch 'linus' into cont_syslog
[safe/jmp/linux-2.6] / drivers / media / video / videobuf-core.c
index 5ea635f..7d33784 100644 (file)
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/mm.h>
+#include <linux/sched.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)
+#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);
@@ -34,16 +41,18 @@ 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 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 *videobuf_alloc(struct videobuf_queue *q)
 {
        struct videobuf_buffer *vb;
 
@@ -55,42 +64,36 @@ void *videobuf_alloc(struct videobuf_queue *q)
        }
 
        vb = q->int_ops->alloc(q->msize);
-
        if (NULL != vb) {
                init_waitqueue_head(&vb->done);
-               vb->magic     = MAGIC_BUFFER;
+               vb->magic = MAGIC_BUFFER;
        }
 
        return vb;
 }
+EXPORT_SYMBOL_GPL(videobuf_alloc);
 
+#define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE &&\
+                               vb->state != VIDEOBUF_QUEUED)
 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;
-               }
+
+       if (non_blocking) {
+               if (WAITON_CONDITION)
+                       return 0;
+               else
+                       return -EAGAIN;
        }
-       remove_wait_queue(&vb->done, &wait);
-       return retval;
+
+       if (intr)
+               return wait_event_interruptible(vb->done, WAITON_CONDITION);
+       else
+               wait_event(vb->done, WAITON_CONDITION);
+
+       return 0;
 }
+EXPORT_SYMBOL_GPL(videobuf_waiton);
 
 int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
                    struct v4l2_framebuffer *fbuf)
@@ -98,29 +101,25 @@ int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
        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);
 }
+EXPORT_SYMBOL_GPL(videobuf_iolock);
+
+void *videobuf_queue_to_vaddr(struct videobuf_queue *q,
+                             struct videobuf_buffer *buf)
+{
+       if (q->int_ops->vaddr)
+               return q->int_ops->vaddr(buf);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_to_vaddr);
 
 /* --------------------------------------------------------------------- */
 
 
 void videobuf_queue_core_init(struct videobuf_queue *q,
-                        struct videobuf_queue_ops *ops,
-                        void *dev,
+                        const struct videobuf_queue_ops *ops,
+                        struct device *dev,
                         spinlock_t *irqlock,
                         enum v4l2_buf_type type,
                         enum v4l2_field field,
@@ -128,6 +127,7 @@ void videobuf_queue_core_init(struct videobuf_queue *q,
                         void *priv,
                         struct videobuf_qtype_ops *int_ops)
 {
+       BUG_ON(!q);
        memset(q, 0, sizeof(*q));
        q->irqlock   = irqlock;
        q->dev       = dev;
@@ -144,12 +144,17 @@ void videobuf_queue_core_init(struct videobuf_queue *q,
        BUG_ON(!q->ops->buf_queue);
        BUG_ON(!q->ops->buf_release);
 
+       /* Lock is mandatory for queue_cancel to work */
+       BUG_ON(!irqlock);
+
        /* Having implementations for abstract methods are mandatory */
        BUG_ON(!q->int_ops);
 
        mutex_init(&q->vb_lock);
+       init_waitqueue_head(&q->wait);
        INIT_LIST_HEAD(&q->stream);
 }
+EXPORT_SYMBOL_GPL(videobuf_queue_core_init);
 
 /* Locking: Only usage in bttv unsafe find way to remove */
 int videobuf_queue_is_busy(struct videobuf_queue *q)
@@ -188,6 +193,7 @@ int videobuf_queue_is_busy(struct videobuf_queue *q)
        }
        return 0;
 }
+EXPORT_SYMBOL_GPL(videobuf_queue_is_busy);
 
 /* Locking: Caller holds q->vb_lock */
 void videobuf_queue_cancel(struct videobuf_queue *q)
@@ -195,19 +201,22 @@ void videobuf_queue_cancel(struct videobuf_queue *q)
        unsigned long flags = 0;
        int i;
 
+       q->streaming = 0;
+       q->reading  = 0;
+       wake_up_interruptible_sync(&q->wait);
+
        /* remove queued buffers from list */
-       if (q->irqlock)
-               spin_lock_irqsave(q->irqlock, flags);
+       spin_lock_irqsave(q->irqlock, flags);
        for (i = 0; i < VIDEO_MAX_FRAME; i++) {
                if (NULL == q->bufs[i])
                        continue;
                if (q->bufs[i]->state == VIDEOBUF_QUEUED) {
                        list_del(&q->bufs[i]->queue);
                        q->bufs[i]->state = VIDEOBUF_ERROR;
+                       wake_up_all(&q->bufs[i]->done);
                }
        }
-       if (q->irqlock)
-               spin_unlock_irqrestore(q->irqlock, flags);
+       spin_unlock_irqrestore(q->irqlock, flags);
 
        /* free all buffers + clear queue */
        for (i = 0; i < VIDEO_MAX_FRAME; i++) {
@@ -217,6 +226,7 @@ void videobuf_queue_cancel(struct videobuf_queue *q)
        }
        INIT_LIST_HEAD(&q->stream);
 }
+EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
 
 /* --------------------------------------------------------------------- */
 
@@ -238,6 +248,7 @@ enum v4l2_field videobuf_next_field(struct videobuf_queue *q)
        }
        return field;
 }
+EXPORT_SYMBOL_GPL(videobuf_next_field);
 
 /* Locking: Caller holds q->vb_lock */
 static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
@@ -274,8 +285,10 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
        case VIDEOBUF_ACTIVE:
                b->flags |= V4L2_BUF_FLAG_QUEUED;
                break;
-       case VIDEOBUF_DONE:
        case VIDEOBUF_ERROR:
+               b->flags |= V4L2_BUF_FLAG_ERROR;
+               /* fall through */
+       case VIDEOBUF_DONE:
                b->flags |= V4L2_BUF_FLAG_DONE;
                break;
        case VIDEOBUF_NEEDS_INIT:
@@ -299,20 +312,15 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
 static int __videobuf_mmap_free(struct videobuf_queue *q)
 {
        int i;
-       int rc;
 
        if (!q)
                return 0;
 
        MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
 
-
-       rc  = CALL(q, mmap_free, q);
-
-       q->is_mmapped = 0;
-
-       if (rc < 0)
-               return rc;
+       for (i = 0; i < VIDEO_MAX_FRAME; i++)
+               if (q->bufs[i] && q->bufs[i]->map)
+                       return -EBUSY;
 
        for (i = 0; i < VIDEO_MAX_FRAME; i++) {
                if (NULL == q->bufs[i])
@@ -322,7 +330,7 @@ static int __videobuf_mmap_free(struct videobuf_queue *q)
                q->bufs[i] = NULL;
        }
 
-       return rc;
+       return 0;
 }
 
 int videobuf_mmap_free(struct videobuf_queue *q)
@@ -333,9 +341,10 @@ int videobuf_mmap_free(struct videobuf_queue *q)
        mutex_unlock(&q->vb_lock);
        return ret;
 }
+EXPORT_SYMBOL_GPL(videobuf_mmap_free);
 
 /* Locking: Caller holds q->vb_lock */
-static int __videobuf_mmap_setup(struct videobuf_queue *q,
+int __videobuf_mmap_setup(struct videobuf_queue *q,
                        unsigned int bcount, unsigned int bsize,
                        enum v4l2_memory memory)
 {
@@ -352,7 +361,7 @@ static int __videobuf_mmap_setup(struct videobuf_queue *q,
        for (i = 0; i < bcount; i++) {
                q->bufs[i] = videobuf_alloc(q);
 
-               if (q->bufs[i] == NULL)
+               if (NULL == q->bufs[i])
                        break;
 
                q->bufs[i]->i      = i;
@@ -361,7 +370,7 @@ static int __videobuf_mmap_setup(struct videobuf_queue *q,
                q->bufs[i]->bsize  = bsize;
                switch (memory) {
                case V4L2_MEMORY_MMAP:
-                       q->bufs[i]->boff  = bsize * i;
+                       q->bufs[i]->boff = PAGE_ALIGN(bsize) * i;
                        break;
                case V4L2_MEMORY_USERPTR:
                case V4L2_MEMORY_OVERLAY:
@@ -373,11 +382,11 @@ static int __videobuf_mmap_setup(struct videobuf_queue *q,
        if (!i)
                return -ENOMEM;
 
-       dprintk(1, "mmap setup: %d buffers, %d bytes each\n",
-               i, bsize);
+       dprintk(1, "mmap setup: %d buffers, %d bytes each\n", i, bsize);
 
        return i;
 }
+EXPORT_SYMBOL_GPL(__videobuf_mmap_setup);
 
 int videobuf_mmap_setup(struct videobuf_queue *q,
                        unsigned int bcount, unsigned int bsize,
@@ -389,6 +398,7 @@ int videobuf_mmap_setup(struct videobuf_queue *q,
        mutex_unlock(&q->vb_lock);
        return ret;
 }
+EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
 
 int videobuf_reqbufs(struct videobuf_queue *q,
                 struct v4l2_requestbuffers *req)
@@ -431,9 +441,9 @@ int videobuf_reqbufs(struct videobuf_queue *q,
                count = VIDEO_MAX_FRAME;
        size = 0;
        q->ops->buf_setup(q, &count, &size);
-       size = PAGE_ALIGN(size);
-       dprintk(1, "reqbufs: bufs=%d, size=0x%x [%d pages total]\n",
-               count, size, (count*size)>>PAGE_SHIFT);
+       dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n",
+               count, size,
+               (unsigned int)((count * PAGE_ALIGN(size)) >> PAGE_SHIFT));
 
        retval = __videobuf_mmap_setup(q, count, size, req->memory);
        if (retval < 0) {
@@ -442,11 +452,13 @@ int videobuf_reqbufs(struct videobuf_queue *q,
        }
 
        req->count = retval;
+       retval = 0;
 
  done:
        mutex_unlock(&q->vb_lock);
        return retval;
 }
+EXPORT_SYMBOL_GPL(videobuf_reqbufs);
 
 int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
 {
@@ -457,7 +469,7 @@ int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
                dprintk(1, "querybuf: Wrong type.\n");
                goto done;
        }
-       if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) {
+       if (unlikely(b->index >= VIDEO_MAX_FRAME)) {
                dprintk(1, "querybuf: index out of range.\n");
                goto done;
        }
@@ -473,9 +485,9 @@ done:
        mutex_unlock(&q->vb_lock);
        return ret;
 }
+EXPORT_SYMBOL_GPL(videobuf_querybuf);
 
-int videobuf_qbuf(struct videobuf_queue *q,
-             struct v4l2_buffer *b)
+int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b)
 {
        struct videobuf_buffer *buf;
        enum v4l2_field field;
@@ -498,7 +510,7 @@ int videobuf_qbuf(struct videobuf_queue *q,
                dprintk(1, "qbuf: Wrong type.\n");
                goto done;
        }
-       if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) {
+       if (b->index >= VIDEO_MAX_FRAME) {
                dprintk(1, "qbuf: index out of range.\n");
                goto done;
        }
@@ -534,6 +546,13 @@ int videobuf_qbuf(struct videobuf_queue *q,
                                   "but buffer addr is zero!\n");
                        goto done;
                }
+               if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT
+                   || q->type == V4L2_BUF_TYPE_VBI_OUTPUT
+                   || q->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+                       buf->size = b->bytesused;
+                       buf->field = b->field;
+                       buf->ts = b->timestamp;
+               }
                break;
        case V4L2_MEMORY_USERPTR:
                if (b->length < buf->bsize) {
@@ -563,16 +582,15 @@ int videobuf_qbuf(struct videobuf_queue *q,
 
        list_add_tail(&buf->stream, &q->stream);
        if (q->streaming) {
-               if (q->irqlock)
-                       spin_lock_irqsave(q->irqlock, flags);
+               spin_lock_irqsave(q->irqlock, flags);
                q->ops->buf_queue(q, buf);
-               if (q->irqlock)
-                       spin_unlock_irqrestore(q->irqlock, flags);
+               spin_unlock_irqrestore(q->irqlock, flags);
        }
-       dprintk(1, "qbuf: succeded\n");
+       dprintk(1, "qbuf: succeeded\n");
        retval = 0;
+       wake_up_interruptible_sync(&q->wait);
 
- done:
+done:
        mutex_unlock(&q->vb_lock);
 
        if (b->memory == V4L2_MEMORY_MMAP)
@@ -580,63 +598,111 @@ int videobuf_qbuf(struct videobuf_queue *q,
 
        return retval;
 }
+EXPORT_SYMBOL_GPL(videobuf_qbuf);
 
-int videobuf_dqbuf(struct videobuf_queue *q,
-              struct v4l2_buffer *b, int nonblocking)
+/* Locking: Caller holds q->vb_lock */
+static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock)
 {
-       struct videobuf_buffer *buf;
        int retval;
 
-       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
-
-       mutex_lock(&q->vb_lock);
-       retval = -EBUSY;
-       if (q->reading) {
-               dprintk(1, "dqbuf: Reading running...\n");
-               goto done;
-       }
-       retval = -EINVAL;
-       if (b->type != q->type) {
-               dprintk(1, "dqbuf: Wrong type.\n");
+checks:
+       if (!q->streaming) {
+               dprintk(1, "next_buffer: Not streaming\n");
+               retval = -EINVAL;
                goto done;
        }
+
        if (list_empty(&q->stream)) {
-               dprintk(1, "dqbuf: stream running\n");
-               goto done;
+               if (noblock) {
+                       retval = -EAGAIN;
+                       dprintk(2, "next_buffer: no buffers to dequeue\n");
+                       goto done;
+               } else {
+                       dprintk(2, "next_buffer: waiting on buffer\n");
+
+                       /* Drop lock to avoid deadlock with qbuf */
+                       mutex_unlock(&q->vb_lock);
+
+                       /* Checking list_empty and streaming is safe without
+                        * locks because we goto checks to validate while
+                        * holding locks before proceeding */
+                       retval = wait_event_interruptible(q->wait,
+                               !list_empty(&q->stream) || !q->streaming);
+                       mutex_lock(&q->vb_lock);
+
+                       if (retval)
+                               goto done;
+
+                       goto checks;
+               }
        }
+
+       retval = 0;
+
+done:
+       return retval;
+}
+
+/* Locking: Caller holds q->vb_lock */
+static int stream_next_buffer(struct videobuf_queue *q,
+                       struct videobuf_buffer **vb, int nonblocking)
+{
+       int retval;
+       struct videobuf_buffer *buf = NULL;
+
+       retval = stream_next_buffer_check_queue(q, nonblocking);
+       if (retval)
+               goto done;
+
        buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
-       mutex_unlock(&q->vb_lock);
        retval = videobuf_waiton(buf, nonblocking, 1);
+       if (retval < 0)
+               goto done;
+
+       *vb = buf;
+done:
+       return retval;
+}
+
+int videobuf_dqbuf(struct videobuf_queue *q,
+                  struct v4l2_buffer *b, int nonblocking)
+{
+       struct videobuf_buffer *buf = NULL;
+       int retval;
+
+       MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+       memset(b, 0, sizeof(*b));
        mutex_lock(&q->vb_lock);
+
+       retval = stream_next_buffer(q, &buf, nonblocking);
        if (retval < 0) {
-               dprintk(1, "dqbuf: waiton returned %d\n", retval);
+               dprintk(1, "dqbuf: next_buffer error: %i\n", retval);
                goto done;
        }
+
        switch (buf->state) {
        case VIDEOBUF_ERROR:
                dprintk(1, "dqbuf: state is error\n");
-               retval = -EIO;
-               CALL(q, sync, q, buf);
-               buf->state = VIDEOBUF_IDLE;
                break;
        case VIDEOBUF_DONE:
                dprintk(1, "dqbuf: state is done\n");
-               CALL(q, sync, q, buf);
-               buf->state = VIDEOBUF_IDLE;
                break;
        default:
                dprintk(1, "dqbuf: state invalid\n");
                retval = -EINVAL;
                goto done;
        }
-       list_del(&buf->stream);
-       memset(b, 0, sizeof(*b));
+       CALL(q, sync, q, buf);
        videobuf_status(q, b, buf, q->type);
-
- done:
+       list_del(&buf->stream);
+       buf->state = VIDEOBUF_IDLE;
+       b->flags &= ~V4L2_BUF_FLAG_DONE;
+done:
        mutex_unlock(&q->vb_lock);
        return retval;
 }
+EXPORT_SYMBOL_GPL(videobuf_dqbuf);
 
 int videobuf_streamon(struct videobuf_queue *q)
 {
@@ -652,18 +718,18 @@ int videobuf_streamon(struct videobuf_queue *q)
        if (q->streaming)
                goto done;
        q->streaming = 1;
-       if (q->irqlock)
-               spin_lock_irqsave(q->irqlock, flags);
+       spin_lock_irqsave(q->irqlock, flags);
        list_for_each_entry(buf, &q->stream, stream)
                if (buf->state == VIDEOBUF_PREPARED)
                        q->ops->buf_queue(q, buf);
-       if (q->irqlock)
-               spin_unlock_irqrestore(q->irqlock, flags);
+       spin_unlock_irqrestore(q->irqlock, flags);
 
- done:
+       wake_up_interruptible_sync(&q->wait);
+done:
        mutex_unlock(&q->vb_lock);
        return retval;
 }
+EXPORT_SYMBOL_GPL(videobuf_streamon);
 
 /* Locking: Caller holds q->vb_lock */
 static int __videobuf_streamoff(struct videobuf_queue *q)
@@ -672,7 +738,6 @@ static int __videobuf_streamoff(struct videobuf_queue *q)
                return -EINVAL;
 
        videobuf_queue_cancel(q);
-       q->streaming = 0;
 
        return 0;
 }
@@ -687,6 +752,7 @@ int videobuf_streamoff(struct videobuf_queue *q)
 
        return retval;
 }
+EXPORT_SYMBOL_GPL(videobuf_streamoff);
 
 /* Locking: Caller holds q->vb_lock */
 static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
@@ -714,11 +780,9 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
                goto done;
 
        /* start capture & wait */
-       if (q->irqlock)
-               spin_lock_irqsave(q->irqlock, flags);
+       spin_lock_irqsave(q->irqlock, flags);
        q->ops->buf_queue(q, q->read_buf);
-       if (q->irqlock)
-               spin_unlock_irqrestore(q->irqlock, flags);
+       spin_unlock_irqrestore(q->irqlock, flags);
        retval = videobuf_waiton(q->read_buf, 0, 0);
        if (0 == retval) {
                CALL(q, sync, q, q->read_buf);
@@ -728,7 +792,7 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
                        retval = q->read_buf->size;
        }
 
- done:
+done:
        /* cleanup */
        q->ops->buf_release(q, q->read_buf);
        kfree(q->read_buf);
@@ -736,20 +800,62 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
        return retval;
 }
 
+static int __videobuf_copy_to_user(struct videobuf_queue *q,
+                                  struct videobuf_buffer *buf,
+                                  char __user *data, size_t count,
+                                  int nonblocking)
+{
+       void *vaddr = CALL(q, vaddr, buf);
+
+       /* copy to userspace */
+       if (count > buf->size - q->read_off)
+               count = buf->size - q->read_off;
+
+       if (copy_to_user(data, vaddr + q->read_off, count))
+               return -EFAULT;
+
+       return count;
+}
+
+static int __videobuf_copy_stream(struct videobuf_queue *q,
+                                 struct videobuf_buffer *buf,
+                                 char __user *data, size_t count, size_t pos,
+                                 int vbihack, int nonblocking)
+{
+       unsigned int *fc = CALL(q, vaddr, buf);
+
+       if (vbihack) {
+               /* dirty, undocumented hack -- pass the frame counter
+                       * within the last four bytes of each vbi data block.
+                       * We need that one to maintain backward compatibility
+                       * to all vbi decoding software out there ... */
+               fc += (buf->size >> 2) - 1;
+               *fc = buf->field_count >> 1;
+               dprintk(1, "vbihack: %d\n", *fc);
+       }
+
+       /* copy stuff using the common method */
+       count = __videobuf_copy_to_user(q, buf, data, count, nonblocking);
+
+       if ((count == -EFAULT) && (pos == 0))
+               return -EFAULT;
+
+       return count;
+}
+
 ssize_t videobuf_read_one(struct videobuf_queue *q,
                          char __user *data, size_t count, loff_t *ppos,
                          int nonblocking)
 {
        enum v4l2_field field;
        unsigned long flags = 0;
-       unsigned size, nbufs;
+       unsigned size = 0, nbufs = 1;
        int retval;
 
        MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
 
        mutex_lock(&q->vb_lock);
 
-       nbufs = 1; size = 0;
        q->ops->buf_setup(q, &nbufs, &size);
 
        if (NULL == q->read_buf  &&
@@ -780,12 +886,11 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
                        q->read_buf = NULL;
                        goto done;
                }
-               if (q->irqlock)
-                       spin_lock_irqsave(q->irqlock, flags);
 
+               spin_lock_irqsave(q->irqlock, flags);
                q->ops->buf_queue(q, q->read_buf);
-               if (q->irqlock)
-                       spin_unlock_irqrestore(q->irqlock, flags);
+               spin_unlock_irqrestore(q->irqlock, flags);
+
                q->read_off = 0;
        }
 
@@ -806,7 +911,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
        }
 
        /* Copy to userspace */
-       retval = CALL(q, video_copy_to_user, q, data, count, nonblocking);
+       retval = __videobuf_copy_to_user(q, q->read_buf, data, count, nonblocking);
        if (retval < 0)
                goto done;
 
@@ -818,10 +923,11 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
                q->read_buf = NULL;
        }
 
- done:
+done:
        mutex_unlock(&q->vb_lock);
        return retval;
 }
+EXPORT_SYMBOL_GPL(videobuf_read_one);
 
 /* Locking: Caller holds q->vb_lock */
 static int __videobuf_read_start(struct videobuf_queue *q)
@@ -851,12 +957,10 @@ static int __videobuf_read_start(struct videobuf_queue *q)
                        return err;
                list_add_tail(&q->bufs[i]->stream, &q->stream);
        }
-       if (q->irqlock)
-               spin_lock_irqsave(q->irqlock, flags);
+       spin_lock_irqsave(q->irqlock, flags);
        for (i = 0; i < count; i++)
                q->ops->buf_queue(q, q->bufs[i]);
-       if (q->irqlock)
-               spin_unlock_irqrestore(q->irqlock, flags);
+       spin_unlock_irqrestore(q->irqlock, flags);
        q->reading = 1;
        return 0;
 }
@@ -865,7 +969,6 @@ static void __videobuf_read_stop(struct videobuf_queue *q)
 {
        int i;
 
-
        videobuf_queue_cancel(q);
        __videobuf_mmap_free(q);
        INIT_LIST_HEAD(&q->stream);
@@ -876,8 +979,6 @@ static void __videobuf_read_stop(struct videobuf_queue *q)
                q->bufs[i] = NULL;
        }
        q->read_buf = NULL;
-       q->reading  = 0;
-
 }
 
 int videobuf_read_start(struct videobuf_queue *q)
@@ -890,6 +991,7 @@ int videobuf_read_start(struct videobuf_queue *q)
 
        return rc;
 }
+EXPORT_SYMBOL_GPL(videobuf_read_start);
 
 void videobuf_read_stop(struct videobuf_queue *q)
 {
@@ -897,6 +999,7 @@ void videobuf_read_stop(struct videobuf_queue *q)
        __videobuf_read_stop(q);
        mutex_unlock(&q->vb_lock);
 }
+EXPORT_SYMBOL_GPL(videobuf_read_stop);
 
 void videobuf_stop(struct videobuf_queue *q)
 {
@@ -910,7 +1013,7 @@ void videobuf_stop(struct videobuf_queue *q)
 
        mutex_unlock(&q->vb_lock);
 }
-
+EXPORT_SYMBOL_GPL(videobuf_stop);
 
 ssize_t videobuf_read_stream(struct videobuf_queue *q,
                             char __user *data, size_t count, loff_t *ppos,
@@ -921,7 +1024,7 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q,
 
        MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
 
-       dprintk(2, "%s\n", __FUNCTION__);
+       dprintk(2, "%s\n", __func__);
        mutex_lock(&q->vb_lock);
        retval = -EBUSY;
        if (q->streaming)
@@ -950,7 +1053,7 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q,
                }
 
                if (q->read_buf->state == VIDEOBUF_DONE) {
-                       rc = CALL(q, copy_stream, q, data + retval, count,
+                       rc = __videobuf_copy_stream(q, q->read_buf, data + retval, count,
                                        retval, vbihack, nonblocking);
                        if (rc < 0) {
                                retval = rc;
@@ -970,21 +1073,20 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q,
                if (q->read_off == q->read_buf->size) {
                        list_add_tail(&q->read_buf->stream,
                                      &q->stream);
-                       if (q->irqlock)
-                               spin_lock_irqsave(q->irqlock, flags);
+                       spin_lock_irqsave(q->irqlock, flags);
                        q->ops->buf_queue(q, q->read_buf);
-                       if (q->irqlock)
-                               spin_unlock_irqrestore(q->irqlock, flags);
+                       spin_unlock_irqrestore(q->irqlock, flags);
                        q->read_buf = NULL;
                }
                if (retval < 0)
                        break;
        }
 
- done:
+done:
        mutex_unlock(&q->vb_lock);
        return retval;
 }
+EXPORT_SYMBOL_GPL(videobuf_read_stream);
 
 unsigned int videobuf_poll_stream(struct file *file,
                                  struct videobuf_queue *q,
@@ -1018,27 +1120,51 @@ unsigned int videobuf_poll_stream(struct file *file,
        if (0 == rc) {
                poll_wait(file, &buf->done, wait);
                if (buf->state == VIDEOBUF_DONE ||
-                   buf->state == VIDEOBUF_ERROR)
-                       rc = POLLIN|POLLRDNORM;
+                   buf->state == VIDEOBUF_ERROR) {
+                       switch (q->type) {
+                       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+                       case V4L2_BUF_TYPE_VBI_OUTPUT:
+                       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+                               rc = POLLOUT | POLLWRNORM;
+                               break;
+                       default:
+                               rc = POLLIN | POLLRDNORM;
+                               break;
+                       }
+               }
        }
        mutex_unlock(&q->vb_lock);
        return rc;
 }
+EXPORT_SYMBOL_GPL(videobuf_poll_stream);
 
-int videobuf_mmap_mapper(struct videobuf_queue *q,
-                        struct vm_area_struct *vma)
+int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma)
 {
-       int retval;
+       int rc = -EINVAL;
+       int i;
 
        MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
 
+       if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) {
+               dprintk(1, "mmap appl bug: PROT_WRITE and MAP_SHARED are required\n");
+               return -EINVAL;
+       }
+
        mutex_lock(&q->vb_lock);
-       retval = CALL(q, mmap_mapper, q, vma);
-       q->is_mmapped = 1;
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               struct videobuf_buffer *buf = q->bufs[i];
+
+               if (buf && buf->memory == V4L2_MEMORY_MMAP &&
+                               buf->boff == (vma->vm_pgoff << PAGE_SHIFT)) {
+                       rc = CALL(q, mmap_mapper, q, buf, vma);
+                       break;
+               }
+       }
        mutex_unlock(&q->vb_lock);
 
-       return retval;
+       return rc;
 }
+EXPORT_SYMBOL_GPL(videobuf_mmap_mapper);
 
 #ifdef CONFIG_VIDEO_V4L1_COMPAT
 int videobuf_cgmbuf(struct videobuf_queue *q,
@@ -1061,7 +1187,7 @@ int videobuf_cgmbuf(struct videobuf_queue *q,
        mbuf->size   = 0;
        for (i = 0; i < mbuf->frames; i++) {
                mbuf->offsets[i]  = q->bufs[i]->boff;
-               mbuf->size       += q->bufs[i]->bsize;
+               mbuf->size       += PAGE_ALIGN(q->bufs[i]->bsize);
        }
 
        return 0;
@@ -1069,32 +1195,3 @@ int videobuf_cgmbuf(struct videobuf_queue *q,
 EXPORT_SYMBOL_GPL(videobuf_cgmbuf);
 #endif
 
-/* --------------------------------------------------------------------- */
-
-EXPORT_SYMBOL_GPL(videobuf_waiton);
-EXPORT_SYMBOL_GPL(videobuf_iolock);
-
-EXPORT_SYMBOL_GPL(videobuf_alloc);
-
-EXPORT_SYMBOL_GPL(videobuf_queue_core_init);
-EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
-EXPORT_SYMBOL_GPL(videobuf_queue_is_busy);
-
-EXPORT_SYMBOL_GPL(videobuf_next_field);
-EXPORT_SYMBOL_GPL(videobuf_reqbufs);
-EXPORT_SYMBOL_GPL(videobuf_querybuf);
-EXPORT_SYMBOL_GPL(videobuf_qbuf);
-EXPORT_SYMBOL_GPL(videobuf_dqbuf);
-EXPORT_SYMBOL_GPL(videobuf_streamon);
-EXPORT_SYMBOL_GPL(videobuf_streamoff);
-
-EXPORT_SYMBOL_GPL(videobuf_read_start);
-EXPORT_SYMBOL_GPL(videobuf_read_stop);
-EXPORT_SYMBOL_GPL(videobuf_stop);
-EXPORT_SYMBOL_GPL(videobuf_read_stream);
-EXPORT_SYMBOL_GPL(videobuf_read_one);
-EXPORT_SYMBOL_GPL(videobuf_poll_stream);
-
-EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
-EXPORT_SYMBOL_GPL(videobuf_mmap_free);
-EXPORT_SYMBOL_GPL(videobuf_mmap_mapper);