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

📄 video-buf.c

📁 V4l driver for DVB HD
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * * generic helper functions for video4linux capture buffers, to handle * memory management and PCI DMA. * Right now, bttv, saa7134, saa7146 and cx88 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. * * device specific map/unmap/sync stuff now are mapped as operations * to allow its usage by USB and virtual devices. * * (c) 2001-2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs] * (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 of the License, or * (at your option) any later version. */#include "compat.h"#include <linux/init.h>#include <linux/module.h>#include <linux/moduleparam.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>#include <media/video-buf.h>#define MAGIC_DMABUF 0x19721112#define MAGIC_BUFFER 0x20040302#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 pci dma buffers");MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");MODULE_LICENSE("GPL");#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 = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);	if (NULL == sglist)		return NULL;	for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {		pg = vmalloc_to_page(virt);		if (NULL == pg)			goto err;		BUG_ON(PageHighMem(pg));		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 = kcalloc(nr_pages, sizeof(*sglist), GFP_KERNEL);	if (NULL == sglist)		return NULL;	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;}/* --------------------------------------------------------------------- */void videobuf_dma_init(struct videobuf_dmabuf *dma){	memset(dma,0,sizeof(*dma));	dma->magic = MAGIC_DMABUF;}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;	}	dprintk(1,"vmalloc is at addr 0x%08lx, size=%d\n",				(unsigned long)dma->vmalloc,				nr_pages << PAGE_SHIFT);	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_map(struct videobuf_queue* q,struct videobuf_dmabuf *dma){	void                   *dev=q->dev;	MAGIC_CHECK(dma->magic,MAGIC_DMABUF);	BUG_ON(0 == dma->nr_pages);	if (dma->pages) {		dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages,						   dma->offset);	}	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) {		if (q->ops->vb_map_sg) {			dma->sglen = q->ops->vb_map_sg(dev,dma->sglist,					dma->nr_pages, dma->direction);		}		if (0 == dma->sglen) {			printk(KERN_WARNING			       "%s: videobuf_map_sg failed\n",__FUNCTION__);			kfree(dma->sglist);			dma->sglist = NULL;			dma->sglen = 0;			return -EIO;		}	}	return 0;}int videobuf_dma_sync(struct videobuf_queue* q,struct videobuf_dmabuf *dma){	void                   *dev=q->dev;	MAGIC_CHECK(dma->magic,MAGIC_DMABUF);	BUG_ON(!dma->sglen);	if (!dma->bus_addr && q->ops->vb_dma_sync_sg)		q->ops->vb_dma_sync_sg(dev,dma->sglist,dma->nr_pages,							dma->direction);	return 0;}int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma){	void                   *dev=q->dev;	MAGIC_CHECK(dma->magic,MAGIC_DMABUF);	if (!dma->sglen)		return 0;	if (!dma->bus_addr && q->ops->vb_unmap_sg)			q->ops->vb_unmap_sg(dev,dma->sglist,dma->nr_pages,							dma->direction);	kfree(dma->sglist);	dma->sglist = NULL;	dma->sglen = 0;	return 0;}int videobuf_dma_free(struct videobuf_dmabuf *dma){	MAGIC_CHECK(dma->magic,MAGIC_DMABUF);	BUG_ON(dma->sglen);	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;	}	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 = kzalloc(size,GFP_KERNEL);	if (NULL != vb) {		videobuf_dma_init(&vb->dma);		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;}intvideobuf_iolock(struct videobuf_queue* q, struct videobuf_buffer *vb,		struct v4l2_framebuffer *fbuf){	int err,pages;	dma_addr_t bus;	MAGIC_CHECK(vb->magic,MAGIC_BUFFER);	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_map(q,&vb->dma);	if (0 != err)		return err;	return 0;}/* --------------------------------------------------------------------- */void videobuf_queue_pci(struct videobuf_queue* q){	/* If not specified, defaults to PCI map sg */	if (!q->ops->vb_map_sg)		q->ops->vb_map_sg=(vb_map_sg_t *)pci_map_sg;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5)	if (!q->ops->vb_dma_sync_sg)		q->ops->vb_dma_sync_sg=(vb_map_sg_t *)pci_dma_sync_sg;#else	if (!q->ops->vb_dma_sync_sg)		q->ops->vb_dma_sync_sg=(vb_map_sg_t *)pci_dma_sync_sg_for_cpu;#endif	if (!q->ops->vb_unmap_sg)		q->ops->vb_unmap_sg=(vb_map_sg_t *)pci_unmap_sg;}int videobuf_pci_dma_map(struct pci_dev *pci,struct videobuf_dmabuf *dma){	struct videobuf_queue q;	struct videobuf_queue_ops qops;	q.dev=pci;	qops.vb_map_sg=(vb_map_sg_t *)pci_map_sg;	qops.vb_unmap_sg=(vb_map_sg_t *)pci_unmap_sg;	q.ops = &qops;	return (videobuf_dma_map(&q,dma));}int videobuf_pci_dma_unmap(struct pci_dev *pci,struct videobuf_dmabuf *dma){	struct videobuf_queue q;	struct videobuf_queue_ops qops;	q.dev=pci;	qops.vb_map_sg=(vb_map_sg_t *)pci_map_sg;	qops.vb_unmap_sg=(vb_map_sg_t *)pci_unmap_sg;	q.ops = &qops;	return (videobuf_dma_unmap(&q,dma));}void videobuf_queue_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){	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;	videobuf_queue_pci(q);	mutex_init(&q->lock);	INIT_LIST_HEAD(&q->stream);}intvideobuf_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) {

⌨️ 快捷键说明

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