⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 video-buf.c

📁 saa7134-0.2.9.tar.gz 电视卡驱动程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * generic helper functions for video4linux capture buffers, to handle * memory management and PCI DMA.  Right now bttv + saa7134 use it. * * The functions expect the hardware being able to scatter gatter * (i.e. the buffers are not linear in physical memory, but fragmented * into PAGE_SIZE chunks).  They also assume the driver does not need * to touch the video data (thus it is probably not useful for USB as * data often must be uncompressed by the drivers). *  * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> * * 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 of the License, or * (at your option) any later version. */#include <linux/version.h>#include <linux/init.h>#include <linux/module.h>#include <linux/vmalloc.h>#include <linux/pagemap.h>#include <linux/slab.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <asm/page.h>#include <asm/pgtable.h>#ifndef TryLockPage# include "linux/page-flags.h"# define TryLockPage TestSetPageLocked#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,64)#include "video-buf.h"#else#include <media/video-buf.h>#endifstatic int debug = 0;MODULE_DESCRIPTION("helper module to manage video4linux pci dma buffers");MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");MODULE_LICENSE("GPL");MODULE_PARM(debug,"i");#define dprintk(level, fmt, arg...)	if (debug >= level) \	printk(KERN_DEBUG "vbuf: " fmt , ## arg)struct scatterlist*videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages){	struct scatterlist *sglist;	struct page *pg;	int i;	sglist = kmalloc(sizeof(struct scatterlist)*nr_pages, GFP_KERNEL);	if (NULL == sglist)		return NULL;	memset(sglist,0,sizeof(struct scatterlist)*nr_pages);	for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {		pg = vmalloc_to_page(virt);		if (NULL == pg)			goto err;		if (PageHighMem(pg))			BUG();		sglist[i].page   = pg;		sglist[i].length = PAGE_SIZE;	}	return sglist;	 err:	kfree(sglist);	return NULL;}struct scatterlist*videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset){	struct scatterlist *sglist;	int i = 0;	if (NULL == pages[0])		return NULL;	sglist = kmalloc(sizeof(*sglist) * nr_pages, GFP_KERNEL);	if (NULL == sglist)		return NULL;	memset(sglist, 0, sizeof(*sglist) * nr_pages);	if (NULL == pages[0])		goto nopage;	if (PageHighMem(pages[0]))		/* DMA to highmem pages might not work */		goto highmem;	sglist[0].page   = pages[0];	sglist[0].offset = offset;	sglist[0].length = PAGE_SIZE - offset;	for (i = 1; i < nr_pages; i++) {		if (NULL == pages[i])			goto nopage;		if (PageHighMem(pages[i]))			goto highmem;		sglist[i].page   = pages[i];		sglist[i].length = PAGE_SIZE;	}	return sglist; nopage:	dprintk(2,"sgl: oops - no page\n");	kfree(sglist);	return NULL; highmem:	dprintk(2,"sgl: oops - highmem page\n");	kfree(sglist);	return NULL;}int videobuf_lock(struct page **pages, int nr_pages){	int i;	dprintk(2,"lock start ...\n");	for (i = 0; i < nr_pages; i++)		if (TryLockPage(pages[i]))			goto err;	dprintk(2,"lock ok [%d pages]\n",nr_pages);	return 0; err:	dprintk(2,"lock failed, unlock ...\n");	while (i > 0)		unlock_page(pages[--i]);	dprintk(2,"lock quit\n");	return -EINVAL;}int videobuf_unlock(struct page **pages, int nr_pages){	int i;	dprintk(2,"unlock start ...\n");	for (i = 0; i < nr_pages; i++)		unlock_page(pages[i]);	dprintk(2,"unlock ok [%d pages]\n",nr_pages);	return 0;}/* --------------------------------------------------------------------- */int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,			   unsigned long data, unsigned long size){	unsigned long first,last;	int err, rw = 0;	dma->direction = direction;	switch (dma->direction) {	case PCI_DMA_FROMDEVICE: rw = READ;  break;	case PCI_DMA_TODEVICE:   rw = WRITE; break;	default:                 BUG();	}	first = (data          & PAGE_MASK) >> PAGE_SHIFT;	last  = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT;	dma->offset   = data & ~PAGE_MASK;	dma->nr_pages = last-first+1;	dma->pages = kmalloc(dma->nr_pages * sizeof(struct page*),			     GFP_KERNEL);	if (NULL == dma->pages)		return -ENOMEM;	dprintk(1,"init user [0x%lx+0x%lx => %d pages]\n",		data,size,dma->nr_pages);	down_read(&current->mm->mmap_sem);        err = get_user_pages(current,current->mm,			     data & PAGE_MASK, dma->nr_pages,			     rw == READ, 1, /* force */			     dma->pages, NULL);	up_read(&current->mm->mmap_sem);	if (err != dma->nr_pages) {		dma->nr_pages = (err >= 0) ? err : 0;		dprintk(1,"get_user_pages: err=%d [%d]\n",err,dma->nr_pages);		return err < 0 ? err : -EINVAL;	}	return 0;}int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,			     int nr_pages){	dprintk(1,"init kernel [%d pages]\n",nr_pages);	dma->direction = direction;	dma->vmalloc = vmalloc_32(nr_pages << PAGE_SHIFT);	if (NULL == dma->vmalloc) {		dprintk(1,"vmalloc_32(%d pages) failed\n",nr_pages);		return -ENOMEM;	}	memset(dma->vmalloc,0,nr_pages << PAGE_SHIFT);	dma->nr_pages = nr_pages;	return 0;}int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction,			      dma_addr_t addr, int nr_pages){	dprintk(1,"init overlay [%d pages @ bus 0x%lx]\n",		nr_pages,(unsigned long)addr);	dma->direction = direction;	if (0 == addr)		return -EINVAL;	dma->bus_addr = addr;	dma->nr_pages = nr_pages;	return 0;}int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma){	int err;	if (0 == dma->nr_pages)		BUG();		if (dma->pages) {		if (0 != (err = videobuf_lock(dma->pages, dma->nr_pages))) {			dprintk(1,"videobuf_lock: %d\n",err);			return err;		}		dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages,						   dma->offset);		if (NULL == dma->sglist)			videobuf_unlock(dma->pages, dma->nr_pages);	}	if (dma->vmalloc) {		dma->sglist = videobuf_vmalloc_to_sg			(dma->vmalloc,dma->nr_pages);	}	if (dma->bus_addr) {		dma->sglist = kmalloc(sizeof(struct scatterlist), GFP_KERNEL);		if (NULL != dma->sglist) {			dma->sglen  = 1;			sg_dma_address(&dma->sglist[0]) = dma->bus_addr & PAGE_MASK;			dma->sglist[0].offset           = dma->bus_addr & ~PAGE_MASK;			sg_dma_len(&dma->sglist[0])     = dma->nr_pages * PAGE_SIZE;		}	}	if (NULL == dma->sglist) {		dprintk(1,"scatterlist is NULL\n");		return -ENOMEM;	}	if (!dma->bus_addr)		dma->sglen = pci_map_sg(dev,dma->sglist,dma->nr_pages,					dma->direction);	return 0;}int videobuf_dma_pci_sync(struct pci_dev *dev, struct videobuf_dmabuf *dma){	if (!dma->sglen)		BUG();	if (!dma->bus_addr)		pci_dma_sync_sg(dev,dma->sglist,dma->nr_pages,dma->direction);	return 0;}int videobuf_dma_pci_unmap(struct pci_dev *dev, struct videobuf_dmabuf *dma){	if (!dma->sglen)		return 0;	if (!dma->bus_addr)		pci_unmap_sg(dev,dma->sglist,dma->nr_pages,dma->direction);	kfree(dma->sglist);	dma->sglist = NULL;	dma->sglen = 0;	if (dma->pages)		videobuf_unlock(dma->pages, dma->nr_pages);	return 0;}int videobuf_dma_free(struct videobuf_dmabuf *dma){	if (dma->sglen)		BUG();	if (dma->pages) {		int i;		for (i=0; i < dma->nr_pages; i++)			page_cache_release(dma->pages[i]);		kfree(dma->pages);		dma->pages = NULL;	}	if (dma->vmalloc) {		vfree(dma->vmalloc);		dma->vmalloc = NULL;	}	if (dma->bus_addr) {		dma->bus_addr = 0;	}	dma->direction = PCI_DMA_NONE;	return 0;}/* --------------------------------------------------------------------- */void* videobuf_alloc(unsigned int size){	struct videobuf_buffer *vb;	vb = kmalloc(size,GFP_KERNEL);	if (NULL != vb) {		memset(vb,0,size);		init_waitqueue_head(&vb->done);	}	return vb;}int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr){	int retval = 0;	DECLARE_WAITQUEUE(wait, current);		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;}intvideobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb,		struct v4l2_framebuffer *fbuf){	int err,pages;	dma_addr_t bus;	switch (vb->memory) {	case V4L2_MEMORY_MMAP:	case V4L2_MEMORY_USERPTR:		if (0 == vb->baddr) {			/* no userspace addr -- kernel bounce buffer */			pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;			err = videobuf_dma_init_kernel(&vb->dma,PCI_DMA_FROMDEVICE,						       pages);			if (0 != err)				return err;		} else {			/* dma directly to userspace */			err = videobuf_dma_init_user(&vb->dma,PCI_DMA_FROMDEVICE,						     vb->baddr,vb->bsize);			if (0 != err)				return err;		}		break;	case V4L2_MEMORY_OVERLAY:		if (NULL == fbuf)			return -EINVAL;		/* FIXME: need sanity checks for vb->boff */		bus   = (dma_addr_t)fbuf->base + vb->boff;		pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;		err = videobuf_dma_init_overlay(&vb->dma,PCI_DMA_FROMDEVICE,						bus, pages);		if (0 != err)			return err;		break;	default:		BUG();	}	err = videobuf_dma_pci_map(pci,&vb->dma);	if (0 != err)		return err;			return 0;}/* --------------------------------------------------------------------- */voidvideobuf_queue_init(struct videobuf_queue *q,		    struct videobuf_queue_ops *ops,		    struct pci_dev *pci,		    spinlock_t *irqlock,		    enum v4l2_buf_type type,		    enum v4l2_field field,		    unsigned int msize){	memset(q,0,sizeof(*q));	q->irqlock = irqlock;	q->pci     = pci;	q->type    = type;	q->field   = field;	q->msize   = msize;	q->ops     = ops;	init_MUTEX(&q->lock);	INIT_LIST_HEAD(&q->stream);}int videobuf_queue_is_busy(struct videobuf_queue *q){	int i;		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;}voidvideobuf_queue_cancel(struct file *file, struct videobuf_queue *q){	unsigned long flags;	int i;	/* 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 == STATE_QUEUED) {			list_del(&q->bufs[i]->queue);			q->bufs[i]->state = STATE_ERROR;		}	}	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(file,q->bufs[i]);	}	INIT_LIST_HEAD(&q->stream);}/* --------------------------------------------------------------------- */enum v4l2_fieldvideobuf_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;}voidvideobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb,		enum v4l2_buf_type type){	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;	}	b->field     = vb->field;	b->timestamp = vb->ts;	b->bytesused = vb->size;	b->sequence  = vb->field_count >> 1;}intvideobuf_reqbufs(struct file *file, struct videobuf_queue *q,		 struct v4l2_requestbuffers *req){	unsigned int size,count;	int retval;	if (req->type != q->type)		return -EINVAL;	if (req->count < 1)		return -EINVAL;	if (req->memory != V4L2_MEMORY_MMAP     &&	    req->memory != V4L2_MEMORY_USERPTR  &&	    req->memory != V4L2_MEMORY_OVERLAY)		return -EINVAL;	down(&q->lock);	count = req->count;	if (count > VIDEO_MAX_FRAME)		count = VIDEO_MAX_FRAME;	size = 0;	q->ops->buf_setup(file,&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(file,q,count,size,req->memory);	if (retval < 0)		goto done;	req->count = count; done:	up(&q->lock);	return retval;}intvideobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b){	if (unlikely(b->type != q->type))		return -EINVAL;	if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME))		return -EINVAL;	if (unlikely(NULL == q->bufs[b->index]))		return -EINVAL;	videobuf_status(b,q->bufs[b->index],q->type);	return 0;}intvideobuf_qbuf(struct file *file, struct videobuf_queue *q,	      struct v4l2_buffer *b){	struct videobuf_buffer *buf;	enum v4l2_field field;	unsigned long flags;	int retval;	down(&q->lock);	retval = -EBUSY;	if (q->reading)		goto done;	retval = -EINVAL;	if (b->type != q->type)		goto done;	if (b->index < 0 || b->index >= VIDEO_MAX_FRAME)		goto done;	buf = q->bufs[b->index];	if (NULL == buf)		goto done;	if (buf->memory != b->memory)		goto done;	if (buf->state == STATE_QUEUED ||	    buf->state == STATE_ACTIVE)		goto done;	switch (b->memory) {	case V4L2_MEMORY_MMAP:		if (0 == buf->baddr)			goto done;		break;	case V4L2_MEMORY_USERPTR:		if (b->length < buf->bsize)			goto done;		buf->baddr = b->m.userptr;		break;	case V4L2_MEMORY_OVERLAY:		buf->boff = b->m.offset;		break;	default:		goto done;	}	field = videobuf_next_field(q);	retval = q->ops->buf_prepare(file,buf,field);

⌨️ 快捷键说明

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