videobuf-core.c

来自「linux 内核源代码」· C语言 代码 · 共 1,088 行 · 第 1/2 页

C
1,088
字号
/* * 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)	if (unlikely((is) != (should))) \	{ printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); }static int debug = 0;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...)	if (debug >= level) \	printk(KERN_DEBUG "vbuf: " fmt , ## arg)/* --------------------------------------------------------------------- */#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 == STATE_ACTIVE || vb->state == STATE_QUEUED) {		if (non_blocking) {			retval = -EAGAIN;			break;		}		set_current_state(intr  ? TASK_INTERRUPTIBLE					: TASK_UNINTERRUPTIBLE);		if (vb->state == STATE_ACTIVE || vb->state == STATE_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);	/* FIXME: 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.	   However, this way is just too dirty! Better to wait for some event.	 */	schedule_timeout(HZ);	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->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 == STATE_QUEUED) {			dprintk(1,"busy: buffer #%d queued\n",i);			return 1;		}		if (q->bufs[i]->state == STATE_ACTIVE) {			dprintk(1,"busy: buffer #%d avtive\n",i);			return 1;		}	}	return 0;}/* Locking: Caller holds q->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 == STATE_QUEUED) {			list_del(&q->bufs[i]->queue);			q->bufs[i]->state = STATE_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->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->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 STATE_PREPARED:	case STATE_QUEUED:	case STATE_ACTIVE:		b->flags |= V4L2_BUF_FLAG_QUEUED;		break;	case STATE_DONE:	case STATE_ERROR:		b->flags |= V4L2_BUF_FLAG_DONE;		break;	case STATE_NEEDS_INIT:	case STATE_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->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);	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->lock);	ret = __videobuf_mmap_free(q);	mutex_unlock(&q->lock);	return ret;}/* Locking: Caller holds q->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->lock);	ret = __videobuf_mmap_setup(q, bcount, bsize, memory);	mutex_unlock(&q->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->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->lock);	return retval;}int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b){	int ret = -EINVAL;	mutex_lock(&q->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->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->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 != STATE_NEEDS_INIT && buf->state != STATE_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 (STATE_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);

⌨️ 快捷键说明

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