diff options
Diffstat (limited to 'drivers/media/video/videobuf-core.c')
-rw-r--r-- | drivers/media/video/videobuf-core.c | 199 |
1 files changed, 118 insertions, 81 deletions
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index 7d3378437ded..8979f91fa8e5 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -52,18 +52,18 @@ MODULE_LICENSE("GPL"); #define CALL(q, f, arg...) \ ((q->int_ops->f) ? q->int_ops->f(arg) : 0) -struct videobuf_buffer *videobuf_alloc(struct videobuf_queue *q) +struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q) { struct videobuf_buffer *vb; BUG_ON(q->msize < sizeof(*vb)); - if (!q->int_ops || !q->int_ops->alloc) { + if (!q->int_ops || !q->int_ops->alloc_vb) { printk(KERN_ERR "No specific ops defined!\n"); BUG(); } - vb = q->int_ops->alloc(q->msize); + vb = q->int_ops->alloc_vb(q->msize); if (NULL != vb) { init_waitqueue_head(&vb->done); vb->magic = MAGIC_BUFFER; @@ -71,27 +71,48 @@ struct videobuf_buffer *videobuf_alloc(struct videobuf_queue *q) return vb; } -EXPORT_SYMBOL_GPL(videobuf_alloc); +EXPORT_SYMBOL_GPL(videobuf_alloc_vb); -#define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE &&\ - vb->state != VIDEOBUF_QUEUED) -int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr) +static int is_state_active_or_queued(struct videobuf_queue *q, struct videobuf_buffer *vb) { + unsigned long flags; + bool rc; + + spin_lock_irqsave(q->irqlock, flags); + rc = vb->state != VIDEOBUF_ACTIVE && vb->state != VIDEOBUF_QUEUED; + spin_unlock_irqrestore(q->irqlock, flags); + return rc; +}; + +int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb, + int non_blocking, int intr) +{ + bool is_ext_locked; + int ret = 0; + MAGIC_CHECK(vb->magic, MAGIC_BUFFER); if (non_blocking) { - if (WAITON_CONDITION) + if (is_state_active_or_queued(q, vb)) return 0; - else - return -EAGAIN; + return -EAGAIN; } + is_ext_locked = q->ext_lock && mutex_is_locked(q->ext_lock); + + /* Release vdev lock to prevent this wait from blocking outside access to + the device. */ + if (is_ext_locked) + mutex_unlock(q->ext_lock); if (intr) - return wait_event_interruptible(vb->done, WAITON_CONDITION); + ret = wait_event_interruptible(vb->done, is_state_active_or_queued(q, vb)); else - wait_event(vb->done, WAITON_CONDITION); + wait_event(vb->done, is_state_active_or_queued(q, vb)); + /* Relock */ + if (is_ext_locked) + mutex_lock(q->ext_lock); - return 0; + return ret; } EXPORT_SYMBOL_GPL(videobuf_waiton); @@ -125,11 +146,13 @@ void videobuf_queue_core_init(struct videobuf_queue *q, enum v4l2_field field, unsigned int msize, void *priv, - struct videobuf_qtype_ops *int_ops) + struct videobuf_qtype_ops *int_ops, + struct mutex *ext_lock) { BUG_ON(!q); memset(q, 0, sizeof(*q)); q->irqlock = irqlock; + q->ext_lock = ext_lock; q->dev = dev; q->type = type; q->field = field; @@ -195,6 +218,45 @@ int videobuf_queue_is_busy(struct videobuf_queue *q) } EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); +/** + * __videobuf_free() - free all the buffers and their control structures + * + * This function can only be called if streaming/reading is off, i.e. no buffers + * are under control of the driver. + */ +/* Locking: Caller holds q->vb_lock */ +static int __videobuf_free(struct videobuf_queue *q) +{ + int i; + + dprintk(1, "%s\n", __func__); + if (!q) + return 0; + + if (q->streaming || q->reading) { + dprintk(1, "Cannot free buffers when streaming or reading\n"); + return -EBUSY; + } + + MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); + + for (i = 0; i < VIDEO_MAX_FRAME; i++) + if (q->bufs[i] && q->bufs[i]->map) { + dprintk(1, "Cannot free mmapped buffers\n"); + return -EBUSY; + } + + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == q->bufs[i]) + continue; + q->ops->buf_release(q, q->bufs[i]); + kfree(q->bufs[i]); + q->bufs[i] = NULL; + } + + return 0; +} + /* Locking: Caller holds q->vb_lock */ void videobuf_queue_cancel(struct videobuf_queue *q) { @@ -308,37 +370,12 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, b->sequence = vb->field_count >> 1; } -/* Locking: Caller holds q->vb_lock */ -static int __videobuf_mmap_free(struct videobuf_queue *q) -{ - int i; - - if (!q) - return 0; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - 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]) - continue; - q->ops->buf_release(q, q->bufs[i]); - kfree(q->bufs[i]); - q->bufs[i] = NULL; - } - - return 0; -} - int videobuf_mmap_free(struct videobuf_queue *q) { int ret; - mutex_lock(&q->vb_lock); - ret = __videobuf_mmap_free(q); - mutex_unlock(&q->vb_lock); + videobuf_queue_lock(q); + ret = __videobuf_free(q); + videobuf_queue_unlock(q); return ret; } EXPORT_SYMBOL_GPL(videobuf_mmap_free); @@ -353,13 +390,13 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - err = __videobuf_mmap_free(q); + err = __videobuf_free(q); if (0 != err) return err; /* Allocate and initialize buffers */ for (i = 0; i < bcount; i++) { - q->bufs[i] = videobuf_alloc(q); + q->bufs[i] = videobuf_alloc_vb(q); if (NULL == q->bufs[i]) break; @@ -393,9 +430,9 @@ int videobuf_mmap_setup(struct videobuf_queue *q, enum v4l2_memory memory) { int ret; - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); ret = __videobuf_mmap_setup(q, bcount, bsize, memory); - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); return ret; } EXPORT_SYMBOL_GPL(videobuf_mmap_setup); @@ -418,7 +455,7 @@ int videobuf_reqbufs(struct videobuf_queue *q, return -EINVAL; } - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); if (req->type != q->type) { dprintk(1, "reqbufs: queue type invalid\n"); retval = -EINVAL; @@ -455,7 +492,7 @@ int videobuf_reqbufs(struct videobuf_queue *q, retval = 0; done: - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); return retval; } EXPORT_SYMBOL_GPL(videobuf_reqbufs); @@ -464,7 +501,7 @@ int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) { int ret = -EINVAL; - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); if (unlikely(b->type != q->type)) { dprintk(1, "querybuf: Wrong type.\n"); goto done; @@ -482,7 +519,7 @@ int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) ret = 0; done: - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); return ret; } EXPORT_SYMBOL_GPL(videobuf_querybuf); @@ -499,7 +536,7 @@ int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) if (b->memory == V4L2_MEMORY_MMAP) down_read(¤t->mm->mmap_sem); - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); retval = -EBUSY; if (q->reading) { dprintk(1, "qbuf: Reading running...\n"); @@ -591,7 +628,7 @@ int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) wake_up_interruptible_sync(&q->wait); done: - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); if (b->memory == V4L2_MEMORY_MMAP) up_read(¤t->mm->mmap_sem); @@ -621,14 +658,14 @@ checks: dprintk(2, "next_buffer: waiting on buffer\n"); /* Drop lock to avoid deadlock with qbuf */ - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); /* 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); + videobuf_queue_lock(q); if (retval) goto done; @@ -655,7 +692,7 @@ static int stream_next_buffer(struct videobuf_queue *q, goto done; buf = list_entry(q->stream.next, struct videobuf_buffer, stream); - retval = videobuf_waiton(buf, nonblocking, 1); + retval = videobuf_waiton(q, buf, nonblocking, 1); if (retval < 0) goto done; @@ -673,7 +710,7 @@ int videobuf_dqbuf(struct videobuf_queue *q, MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); memset(b, 0, sizeof(*b)); - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); retval = stream_next_buffer(q, &buf, nonblocking); if (retval < 0) { @@ -699,7 +736,7 @@ int videobuf_dqbuf(struct videobuf_queue *q, buf->state = VIDEOBUF_IDLE; b->flags &= ~V4L2_BUF_FLAG_DONE; done: - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); return retval; } EXPORT_SYMBOL_GPL(videobuf_dqbuf); @@ -710,7 +747,7 @@ int videobuf_streamon(struct videobuf_queue *q) unsigned long flags = 0; int retval; - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); retval = -EBUSY; if (q->reading) goto done; @@ -726,7 +763,7 @@ int videobuf_streamon(struct videobuf_queue *q) wake_up_interruptible_sync(&q->wait); done: - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); return retval; } EXPORT_SYMBOL_GPL(videobuf_streamon); @@ -746,9 +783,9 @@ int videobuf_streamoff(struct videobuf_queue *q) { int retval; - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); retval = __videobuf_streamoff(q); - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); return retval; } @@ -766,7 +803,7 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); /* setup stuff */ - q->read_buf = videobuf_alloc(q); + q->read_buf = videobuf_alloc_vb(q); if (NULL == q->read_buf) return -ENOMEM; @@ -783,7 +820,7 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, spin_lock_irqsave(q->irqlock, flags); q->ops->buf_queue(q, q->read_buf); spin_unlock_irqrestore(q->irqlock, flags); - retval = videobuf_waiton(q->read_buf, 0, 0); + retval = videobuf_waiton(q, q->read_buf, 0, 0); if (0 == retval) { CALL(q, sync, q, q->read_buf); if (VIDEOBUF_ERROR == q->read_buf->state) @@ -854,7 +891,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); q->ops->buf_setup(q, &nbufs, &size); @@ -871,7 +908,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, if (NULL == q->read_buf) { /* need to capture a new frame */ retval = -ENOMEM; - q->read_buf = videobuf_alloc(q); + q->read_buf = videobuf_alloc_vb(q); dprintk(1, "video alloc=0x%p\n", q->read_buf); if (NULL == q->read_buf) @@ -895,7 +932,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, } /* wait until capture is done */ - retval = videobuf_waiton(q->read_buf, nonblocking, 1); + retval = videobuf_waiton(q, q->read_buf, nonblocking, 1); if (0 != retval) goto done; @@ -924,7 +961,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, } done: - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); return retval; } EXPORT_SYMBOL_GPL(videobuf_read_one); @@ -970,7 +1007,7 @@ static void __videobuf_read_stop(struct videobuf_queue *q) int i; videobuf_queue_cancel(q); - __videobuf_mmap_free(q); + __videobuf_free(q); INIT_LIST_HEAD(&q->stream); for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) @@ -985,9 +1022,9 @@ int videobuf_read_start(struct videobuf_queue *q) { int rc; - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); rc = __videobuf_read_start(q); - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); return rc; } @@ -995,15 +1032,15 @@ EXPORT_SYMBOL_GPL(videobuf_read_start); void videobuf_read_stop(struct videobuf_queue *q) { - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); __videobuf_read_stop(q); - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); } EXPORT_SYMBOL_GPL(videobuf_read_stop); void videobuf_stop(struct videobuf_queue *q) { - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); if (q->streaming) __videobuf_streamoff(q); @@ -1011,7 +1048,7 @@ void videobuf_stop(struct videobuf_queue *q) if (q->reading) __videobuf_read_stop(q); - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); } EXPORT_SYMBOL_GPL(videobuf_stop); @@ -1025,7 +1062,7 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q, MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); dprintk(2, "%s\n", __func__); - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); retval = -EBUSY; if (q->streaming) goto done; @@ -1045,7 +1082,7 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q, list_del(&q->read_buf->stream); q->read_off = 0; } - rc = videobuf_waiton(q->read_buf, nonblocking, 1); + rc = videobuf_waiton(q, q->read_buf, nonblocking, 1); if (rc < 0) { if (0 == retval) retval = rc; @@ -1083,7 +1120,7 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q, } done: - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); return retval; } EXPORT_SYMBOL_GPL(videobuf_read_stream); @@ -1095,7 +1132,7 @@ unsigned int videobuf_poll_stream(struct file *file, struct videobuf_buffer *buf = NULL; unsigned int rc = 0; - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); if (q->streaming) { if (!list_empty(&q->stream)) buf = list_entry(q->stream.next, @@ -1133,7 +1170,7 @@ unsigned int videobuf_poll_stream(struct file *file, } } } - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); return rc; } EXPORT_SYMBOL_GPL(videobuf_poll_stream); @@ -1150,7 +1187,7 @@ int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma) return -EINVAL; } - mutex_lock(&q->vb_lock); + videobuf_queue_lock(q); for (i = 0; i < VIDEO_MAX_FRAME; i++) { struct videobuf_buffer *buf = q->bufs[i]; @@ -1160,7 +1197,7 @@ int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma) break; } } - mutex_unlock(&q->vb_lock); + videobuf_queue_unlock(q); return rc; } |