videobuf-core.c

来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,138 行 · 第 1/2 页

C
1,138
字号
/* * generic helper functions for handling video4linux capture buffers * * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org> * * Highly based on video-buf written originally by: * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> * (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org> * (c) 2006 Ted Walther and John Sokol * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 */#include <linux/init.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <media/videobuf-core.h>#include "compat.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)static int debug;module_param(debug, int, 0644);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 CALL(q, f, arg...)						\	((q->int_ops->f) ? q->int_ops->f(arg) : 0)void *videobuf_alloc(struct videobuf_queue *q){	struct videobuf_buffer *vb;	BUG_ON(q->msize < sizeof(*vb));	if (!q->int_ops || !q->int_ops->alloc) {		printk(KERN_ERR "No specific ops defined!\n");		BUG();	}	vb = q->int_ops->alloc(q->msize);	if (NULL != vb) {		init_waitqueue_head(&vb->done);		vb->magic     = MAGIC_BUFFER;	}	return vb;}#define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE &&\				vb->state != VIDEOBUF_QUEUED)int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr){	MAGIC_CHECK(vb->magic, MAGIC_BUFFER);	if (non_blocking) {		if (WAITON_CONDITION)			return 0;		else			return -EAGAIN;	}	if (intr)		return wait_event_interruptible(vb->done, WAITON_CONDITION);	else		wait_event(vb->done, WAITON_CONDITION);	return 0;}int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,		    struct v4l2_framebuffer *fbuf){	MAGIC_CHECK(vb->magic, MAGIC_BUFFER);	MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);	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);/* --------------------------------------------------------------------- */void videobuf_queue_core_init(struct videobuf_queue *q,			 struct videobuf_queue_ops *ops,			 struct device *dev,			 spinlock_t *irqlock,			 enum v4l2_buf_type type,			 enum v4l2_field field,			 unsigned int msize,			 void *priv,			 struct videobuf_qtype_ops *int_ops){	memset(q, 0, sizeof(*q));	q->irqlock   = irqlock;	q->dev       = dev;	q->type      = type;	q->field     = field;	q->msize     = msize;	q->ops       = ops;	q->priv_data = priv;	q->int_ops   = int_ops;	/* All buffer operations are mandatory */	BUG_ON(!q->ops->buf_setup);	BUG_ON(!q->ops->buf_prepare);	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);}/* Locking: Only usage in bttv unsafe find way to remove */int videobuf_queue_is_busy(struct videobuf_queue *q){	int i;	MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);	if (q->streaming) {		dprintk(1, "busy: streaming active\n");		return 1;	}	if (q->reading) {		dprintk(1, "busy: pending read #1\n");		return 1;	}	if (q->read_buf) {		dprintk(1, "busy: pending read #2\n");		return 1;	}	for (i = 0; i < VIDEO_MAX_FRAME; i++) {		if (NULL == q->bufs[i])			continue;		if (q->bufs[i]->map) {			dprintk(1, "busy: buffer #%d mapped\n", i);			return 1;		}		if (q->bufs[i]->state == VIDEOBUF_QUEUED) {			dprintk(1, "busy: buffer #%d queued\n", i);			return 1;		}		if (q->bufs[i]->state == VIDEOBUF_ACTIVE) {			dprintk(1, "busy: buffer #%d avtive\n", i);			return 1;		}	}	return 0;}/* Locking: Caller holds q->vb_lock */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 */	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);		}	}	spin_unlock_irqrestore(q->irqlock, flags);	/* free all buffers + clear queue */	for (i = 0; i < VIDEO_MAX_FRAME; i++) {		if (NULL == q->bufs[i])			continue;		q->ops->buf_release(q, q->bufs[i]);	}	INIT_LIST_HEAD(&q->stream);}/* --------------------------------------------------------------------- *//* Locking: Caller holds q->vb_lock */enum v4l2_field videobuf_next_field(struct videobuf_queue *q){	enum v4l2_field field = q->field;	BUG_ON(V4L2_FIELD_ANY == field);	if (V4L2_FIELD_ALTERNATE == field) {		if (V4L2_FIELD_TOP == q->last) {			field   = V4L2_FIELD_BOTTOM;			q->last = V4L2_FIELD_BOTTOM;		} else {			field   = V4L2_FIELD_TOP;			q->last = V4L2_FIELD_TOP;		}	}	return field;}/* Locking: Caller holds q->vb_lock */static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,			    struct videobuf_buffer *vb, enum v4l2_buf_type type){	MAGIC_CHECK(vb->magic, MAGIC_BUFFER);	MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);	b->index    = vb->i;	b->type     = type;	b->memory   = vb->memory;	switch (b->memory) {	case V4L2_MEMORY_MMAP:		b->m.offset  = vb->boff;		b->length    = vb->bsize;		break;	case V4L2_MEMORY_USERPTR:		b->m.userptr = vb->baddr;		b->length    = vb->bsize;		break;	case V4L2_MEMORY_OVERLAY:		b->m.offset  = vb->boff;		break;	}	b->flags    = 0;	if (vb->map)		b->flags |= V4L2_BUF_FLAG_MAPPED;	switch (vb->state) {	case VIDEOBUF_PREPARED:	case VIDEOBUF_QUEUED:	case VIDEOBUF_ACTIVE:		b->flags |= V4L2_BUF_FLAG_QUEUED;		break;	case VIDEOBUF_DONE:	case VIDEOBUF_ERROR:		b->flags |= V4L2_BUF_FLAG_DONE;		break;	case VIDEOBUF_NEEDS_INIT:	case VIDEOBUF_IDLE:		/* nothing */		break;	}	if (vb->input != UNSET) {		b->flags |= V4L2_BUF_FLAG_INPUT;		b->input  = vb->input;	}	b->field     = vb->field;	b->timestamp = vb->ts;	b->bytesused = vb->size;	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);	mutex_unlock(&q->vb_lock);	return ret;}/* Locking: Caller holds q->vb_lock */int __videobuf_mmap_setup(struct videobuf_queue *q,			unsigned int bcount, unsigned int bsize,			enum v4l2_memory memory){	unsigned int i;	int err;	MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);	err = __videobuf_mmap_free(q);	if (0 != err)		return err;	/* Allocate and initialize buffers */	for (i = 0; i < bcount; i++) {		q->bufs[i] = videobuf_alloc(q);		if (q->bufs[i] == NULL)			break;		q->bufs[i]->i      = i;		q->bufs[i]->input  = UNSET;		q->bufs[i]->memory = memory;		q->bufs[i]->bsize  = bsize;		switch (memory) {		case V4L2_MEMORY_MMAP:			q->bufs[i]->boff  = bsize * i;			break;		case V4L2_MEMORY_USERPTR:		case V4L2_MEMORY_OVERLAY:			/* nothing */			break;		}	}	if (!i)		return -ENOMEM;	dprintk(1, "mmap setup: %d buffers, %d bytes each\n",		i, bsize);	return i;}int videobuf_mmap_setup(struct videobuf_queue *q,			unsigned int bcount, unsigned int bsize,			enum v4l2_memory memory){	int ret;	mutex_lock(&q->vb_lock);	ret = __videobuf_mmap_setup(q, bcount, bsize, memory);	mutex_unlock(&q->vb_lock);	return ret;}int videobuf_reqbufs(struct videobuf_queue *q,		 struct v4l2_requestbuffers *req){	unsigned int size, count;	int retval;	if (req->count < 1) {		dprintk(1, "reqbufs: count invalid (%d)\n", req->count);		return -EINVAL;	}	if (req->memory != V4L2_MEMORY_MMAP     &&	    req->memory != V4L2_MEMORY_USERPTR  &&	    req->memory != V4L2_MEMORY_OVERLAY) {		dprintk(1, "reqbufs: memory type invalid\n");		return -EINVAL;	}	mutex_lock(&q->vb_lock);	if (req->type != q->type) {		dprintk(1, "reqbufs: queue type invalid\n");		retval = -EINVAL;		goto done;	}	if (q->streaming) {		dprintk(1, "reqbufs: streaming already exists\n");		retval = -EBUSY;		goto done;	}	if (!list_empty(&q->stream)) {		dprintk(1, "reqbufs: stream running\n");		retval = -EBUSY;		goto done;	}	count = req->count;	if (count > VIDEO_MAX_FRAME)		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);	retval = __videobuf_mmap_setup(q, count, size, req->memory);	if (retval < 0) {		dprintk(1, "reqbufs: mmap setup returned %d\n", retval);		goto done;	}	req->count = retval; done:	mutex_unlock(&q->vb_lock);	return retval;}int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b){	int ret = -EINVAL;	mutex_lock(&q->vb_lock);	if (unlikely(b->type != q->type)) {		dprintk(1, "querybuf: Wrong type.\n");		goto done;	}	if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) {		dprintk(1, "querybuf: index out of range.\n");		goto done;	}	if (unlikely(NULL == q->bufs[b->index])) {		dprintk(1, "querybuf: buffer is null.\n");		goto done;	}	videobuf_status(q, b, q->bufs[b->index], q->type);	ret = 0;done:	mutex_unlock(&q->vb_lock);	return ret;}int videobuf_qbuf(struct videobuf_queue *q,	      struct v4l2_buffer *b){	struct videobuf_buffer *buf;	enum v4l2_field field;	unsigned long flags = 0;	int retval;	MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);	if (b->memory == V4L2_MEMORY_MMAP)		down_read(&current->mm->mmap_sem);	mutex_lock(&q->vb_lock);	retval = -EBUSY;	if (q->reading) {		dprintk(1, "qbuf: Reading running...\n");		goto done;	}	retval = -EINVAL;	if (b->type != q->type) {		dprintk(1, "qbuf: Wrong type.\n");		goto done;	}	if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) {		dprintk(1, "qbuf: index out of range.\n");		goto done;	}	buf = q->bufs[b->index];	if (NULL == buf) {		dprintk(1, "qbuf: buffer is null.\n");		goto done;	}	MAGIC_CHECK(buf->magic, MAGIC_BUFFER);	if (buf->memory != b->memory) {		dprintk(1, "qbuf: memory type is wrong.\n");		goto done;	}	if (buf->state != VIDEOBUF_NEEDS_INIT && buf->state != VIDEOBUF_IDLE) {		dprintk(1, "qbuf: buffer is already queued or active.\n");		goto done;	}	if (b->flags & V4L2_BUF_FLAG_INPUT) {		if (b->input >= q->inputs) {			dprintk(1, "qbuf: wrong input.\n");			goto done;		}		buf->input = b->input;	} else {		buf->input = UNSET;	}	switch (b->memory) {	case V4L2_MEMORY_MMAP:		if (0 == buf->baddr) {			dprintk(1, "qbuf: mmap requested "				   "but buffer addr is zero!\n");			goto done;		}		break;	case V4L2_MEMORY_USERPTR:		if (b->length < buf->bsize) {			dprintk(1, "qbuf: buffer length is not enough\n");			goto done;		}		if (VIDEOBUF_NEEDS_INIT != buf->state &&		    buf->baddr != b->m.userptr)			q->ops->buf_release(q, buf);		buf->baddr = b->m.userptr;		break;	case V4L2_MEMORY_OVERLAY:		buf->boff = b->m.offset;		break;	default:		dprintk(1, "qbuf: wrong memory type\n");		goto done;	}	dprintk(1, "qbuf: requesting next field\n");	field = videobuf_next_field(q);	retval = q->ops->buf_prepare(q, buf, field);	if (0 != retval) {		dprintk(1, "qbuf: buffer_prepare returned %d\n", retval);		goto done;	}	list_add_tail(&buf->stream, &q->stream);	if (q->streaming) {		spin_lock_irqsave(q->irqlock, flags);		q->ops->buf_queue(q, buf);		spin_unlock_irqrestore(q->irqlock, flags);	}	dprintk(1, "qbuf: succeded\n");	retval = 0;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?