BUG_ON(!q->int_ops);
 
        mutex_init(&q->vb_lock);
+       init_waitqueue_head(&q->wait);
        INIT_LIST_HEAD(&q->stream);
 }
 
        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);
        }
        dprintk(1, "qbuf: succeded\n");
        retval = 0;
+       wake_up_interruptible_sync(&q->wait);
 
  done:
        mutex_unlock(&q->vb_lock);
        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");
        if (q->irqlock)
                spin_unlock_irqrestore(q->irqlock, flags);
 
+       wake_up_interruptible_sync(&q->wait);
  done:
        mutex_unlock(&q->vb_lock);
        return retval;
                return -EINVAL;
 
        videobuf_queue_cancel(q);
-       q->streaming = 0;
 
        return 0;
 }
                q->bufs[i] = NULL;
        }
        q->read_buf = NULL;
-       q->reading  = 0;
 
 }