#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);
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_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;
+ vb->magic = MAGIC_BUFFER;
}
return vb;
}
+EXPORT_SYMBOL_GPL(videobuf_alloc_vb);
#define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE &&\
vb->state != VIDEOBUF_QUEUED)
return 0;
}
+EXPORT_SYMBOL_GPL(videobuf_waiton);
int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
struct v4l2_framebuffer *fbuf)
return CALL(q, iolock, q, vb, fbuf);
}
+EXPORT_SYMBOL_GPL(videobuf_iolock);
-void *videobuf_queue_to_vmalloc (struct videobuf_queue *q,
- struct videobuf_buffer *buf)
+void *videobuf_queue_to_vaddr(struct videobuf_queue *q,
+ struct videobuf_buffer *buf)
{
- if (q->int_ops->vmalloc)
- return q->int_ops->vmalloc(buf);
- else
- return NULL;
+ if (q->int_ops->vaddr)
+ return q->int_ops->vaddr(buf);
+ return NULL;
}
-EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc);
+EXPORT_SYMBOL_GPL(videobuf_queue_to_vaddr);
/* --------------------------------------------------------------------- */
void videobuf_queue_core_init(struct videobuf_queue *q,
- struct videobuf_queue_ops *ops,
+ const struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
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)
}
return 0;
}
+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)
}
INIT_LIST_HEAD(&q->stream);
}
+EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
/* --------------------------------------------------------------------- */
}
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,
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:
b->sequence = vb->field_count >> 1;
}
-/* Locking: Caller holds q->vb_lock */
-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 (NULL == q->bufs[i])
- continue;
- q->ops->buf_release(q, q->bufs[i]);
- kfree(q->bufs[i]);
- q->bufs[i] = NULL;
- }
-
- return rc;
-}
-
int videobuf_mmap_free(struct videobuf_queue *q)
{
int ret;
mutex_lock(&q->vb_lock);
- ret = __videobuf_mmap_free(q);
+ ret = __videobuf_free(q);
mutex_unlock(&q->vb_lock);
return ret;
}
+EXPORT_SYMBOL_GPL(videobuf_mmap_free);
/* Locking: Caller holds q->vb_lock */
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 (q->bufs[i] == NULL)
+ if (NULL == q->bufs[i])
break;
q->bufs[i]->i = i;
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:
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,
mutex_unlock(&q->vb_lock);
return ret;
}
+EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
int videobuf_reqbufs(struct videobuf_queue *q,
struct v4l2_requestbuffers *req)
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) {
mutex_unlock(&q->vb_lock);
return retval;
}
+EXPORT_SYMBOL_GPL(videobuf_reqbufs);
int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
{
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;
"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) {
q->ops->buf_queue(q, buf);
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)
return retval;
}
-
+EXPORT_SYMBOL_GPL(videobuf_qbuf);
/* Locking: Caller holds q->vb_lock */
static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock)
return retval;
}
-
/* Locking: Caller holds q->vb_lock */
static int stream_next_buffer(struct videobuf_queue *q,
struct videobuf_buffer **vb, int nonblocking)
}
int videobuf_dqbuf(struct videobuf_queue *q,
- struct v4l2_buffer *b, int nonblocking)
+ 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);
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)
{
spin_unlock_irqrestore(q->irqlock, flags);
wake_up_interruptible_sync(&q->wait);
- done:
+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)
return retval;
}
+EXPORT_SYMBOL_GPL(videobuf_streamoff);
/* Locking: Caller holds q->vb_lock */
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;
retval = q->read_buf->size;
}
- done:
+done:
/* cleanup */
q->ops->buf_release(q, q->read_buf);
kfree(q->read_buf);
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)
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)
}
/* 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;
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)
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])
q->bufs[i] = NULL;
}
q->read_buf = NULL;
-
}
int videobuf_read_start(struct videobuf_queue *q)
return rc;
}
+EXPORT_SYMBOL_GPL(videobuf_read_start);
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)
{
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,
}
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;
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,
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,
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;
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_setup);
-EXPORT_SYMBOL_GPL(videobuf_mmap_free);
-EXPORT_SYMBOL_GPL(videobuf_mmap_mapper);