headers: remove sched.h from interrupt.h
[safe/jmp/linux-2.6] / drivers / media / video / videobuf-core.c
index 1ba3aaa..8e93c6f 100644 (file)
@@ -16,6 +16,8 @@
 #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>
 
@@ -91,23 +93,19 @@ 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);
 }
 
+void *videobuf_queue_to_vmalloc (struct videobuf_queue *q,
+                          struct videobuf_buffer *buf)
+{
+       if (q->int_ops->vmalloc)
+               return q->int_ops->vmalloc(buf);
+       else
+               return NULL;
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc);
+
 /* --------------------------------------------------------------------- */
 
 
@@ -121,6 +119,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;
@@ -137,10 +136,14 @@ 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);
 }
 
@@ -188,9 +191,12 @@ 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;
@@ -200,8 +206,7 @@ void videobuf_queue_cancel(struct videobuf_queue *q)
                        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++) {
@@ -329,7 +334,7 @@ int videobuf_mmap_free(struct videobuf_queue *q)
 }
 
 /* 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)
 {
@@ -436,6 +441,7 @@ int videobuf_reqbufs(struct videobuf_queue *q,
        }
 
        req->count = retval;
+       retval = 0;
 
  done:
        mutex_unlock(&q->vb_lock);
@@ -451,7 +457,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;
        }
@@ -492,7 +498,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;
        }
@@ -557,14 +563,13 @@ 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");
        retval = 0;
+       wake_up_interruptible_sync(&q->wait);
 
  done:
        mutex_unlock(&q->vb_lock);
@@ -575,37 +580,88 @@ int videobuf_qbuf(struct videobuf_queue *q,
        return retval;
 }
 
-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);
+
        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");
@@ -646,14 +702,13 @@ 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);
 
+       wake_up_interruptible_sync(&q->wait);
  done:
        mutex_unlock(&q->vb_lock);
        return retval;
@@ -666,7 +721,6 @@ static int __videobuf_streamoff(struct videobuf_queue *q)
                return -EINVAL;
 
        videobuf_queue_cancel(q);
-       q->streaming = 0;
 
        return 0;
 }
@@ -708,11 +762,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);
@@ -773,12 +825,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;
        }
 
@@ -844,12 +895,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;
 }
@@ -858,7 +907,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);
@@ -869,7 +917,6 @@ static void __videobuf_read_stop(struct videobuf_queue *q)
                q->bufs[i] = NULL;
        }
        q->read_buf = NULL;
-       q->reading  = 0;
 
 }
 
@@ -914,7 +961,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)
@@ -963,11 +1010,9 @@ 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)
@@ -1088,6 +1133,7 @@ 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_setup);
 EXPORT_SYMBOL_GPL(videobuf_mmap_free);
 EXPORT_SYMBOL_GPL(videobuf_mmap_mapper);