videobuf-core.c

来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 1,097 行 · 第 1/2 页

C
1,097
字号
/* * 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/slab.h>#include <linux/interrupt.h>#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)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;}int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr){	int retval = 0;	DECLARE_WAITQUEUE(wait, current);	MAGIC_CHECK(vb->magic, MAGIC_BUFFER);	add_wait_queue(&vb->done, &wait);	while (vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) {		if (non_blocking) {			retval = -EAGAIN;			break;		}		set_current_state(intr  ? TASK_INTERRUPTIBLE					: TASK_UNINTERRUPTIBLE);		if (vb->state == VIDEOBUF_ACTIVE ||		    vb->state == VIDEOBUF_QUEUED)			schedule();		set_current_state(TASK_RUNNING);		if (intr && signal_pending(current)) {			dprintk(1, "buffer waiton: -EINTR\n");			retval = -EINTR;			break;		}	}	remove_wait_queue(&vb->done, &wait);	return retval;}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);	/* 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_core_init(struct videobuf_queue *q,			 struct videobuf_queue_ops *ops,			 void *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);	/* Having implementations for abstract methods are mandatory */	BUG_ON(!q->int_ops);	mutex_init(&q->vb_lock);	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;	/* remove queued buffers from list */	if (q->irqlock)		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;		}	}	if (q->irqlock)		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 */static 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;

⌨️ 快捷键说明

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