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

📄 videobuf-dma-sg.c

📁 trident tm5600的linux驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * helper functions for SG DMA video4linux capture buffers * * The functions expect the hardware being able to scatter gather * (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. * * (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 <linux/dma-mapping.h>#include <linux/vmalloc.h>#include <linux/pagemap.h>#include <linux/scatterlist.h>#include <asm/page.h>#include <asm/pgtable.h>#include <media/videobuf-dma-sg.h>#include "compat.h"#define MAGIC_DMABUF 0x19721112#define MAGIC_SG_MEM 0x17890714#define MAGIC_CHECK(is,should)	if (unlikely((is) != (should))) \	{ printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); }static int debug;module_param(debug, int, 0644);MODULE_DESCRIPTION("helper module to manage video4linux dma sg buffers");MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");MODULE_LICENSE("GPL");#define dprintk(level, fmt, arg...)	if (debug >= level) \	printk(KERN_DEBUG "vbuf-sg: " 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;	sg_init_table(sglist, nr_pages);	for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {		pg = vmalloc_to_page(virt);		if (NULL == pg)			goto err;		BUG_ON(PageHighMem(pg));		sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);	}	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;	if (NULL == pages[0])		return NULL;	sglist = kmalloc(nr_pages * sizeof(*sglist), GFP_KERNEL);	if (NULL == sglist)		return NULL;	sg_init_table(sglist, nr_pages);	if (PageHighMem(pages[0]))		/* DMA to highmem pages might not work */		goto highmem;	sg_set_page(&sglist[0], pages[0], PAGE_SIZE - offset, offset);	for (i = 1; i < nr_pages; i++) {		if (NULL == pages[i])			goto nopage;		if (PageHighMem(pages[i]))			goto highmem;		sg_set_page(&sglist[i], pages[i], PAGE_SIZE, 0);	}	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;}/* --------------------------------------------------------------------- */struct videobuf_dmabuf *videobuf_to_dma (struct videobuf_buffer *buf){	struct videobuf_dma_sg_memory *mem = buf->priv;	BUG_ON(!mem);	MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);	return &mem->dma;}void videobuf_dma_init(struct videobuf_dmabuf *dma){	memset(dma,0,sizeof(*dma));	dma->magic = MAGIC_DMABUF;}static int videobuf_dma_init_user_locked(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 DMA_FROM_DEVICE:		rw = READ;		break;	case DMA_TO_DEVICE:		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);	err = get_user_pages(current,current->mm,			     data & PAGE_MASK, dma->nr_pages,			     rw == READ, 1, /* force */			     dma->pages, NULL);	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_user(struct videobuf_dmabuf *dma, int direction,			   unsigned long data, unsigned long size){	int ret;	down_read(&current->mm->mmap_sem);	ret = videobuf_dma_init_user_locked(dma, direction, data, size);	up_read(&current->mm->mmap_sem);	return ret;}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){	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) {		dma->sglen = dma_map_sg(q->dev, dma->sglist,					dma->nr_pages, dma->direction);		if (0 == dma->sglen) {			printk(KERN_WARNING			       "%s: videobuf_map_sg failed\n",__func__);			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){	MAGIC_CHECK(dma->magic, MAGIC_DMABUF);	BUG_ON(!dma->sglen);	dma_sync_sg_for_cpu(q->dev, dma->sglist, dma->nr_pages, dma->direction);	return 0;}int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma){	MAGIC_CHECK(dma->magic, MAGIC_DMABUF);	if (!dma->sglen)		return 0;	dma_unmap_sg(q->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 = DMA_NONE;	return 0;}/* --------------------------------------------------------------------- */int videobuf_sg_dma_map(struct device *dev, struct videobuf_dmabuf *dma){	struct videobuf_queue q;	q.dev = dev;	return videobuf_dma_map(&q, dma);}int videobuf_sg_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma){	struct videobuf_queue q;	q.dev = dev;	return videobuf_dma_unmap(&q, dma);}/* --------------------------------------------------------------------- */static voidvideobuf_vm_open(struct vm_area_struct *vma){	struct videobuf_mapping *map = vma->vm_private_data;	dprintk(2,"vm_open %p [count=%d,vma=%08lx-%08lx]\n",map,		map->count,vma->vm_start,vma->vm_end);	map->count++;}static voidvideobuf_vm_close(struct vm_area_struct *vma){	struct videobuf_mapping *map = vma->vm_private_data;	struct videobuf_queue *q = map->q;	struct videobuf_dma_sg_memory *mem;	int i;	dprintk(2,"vm_close %p [count=%d,vma=%08lx-%08lx]\n",map,		map->count,vma->vm_start,vma->vm_end);	map->count--;	if (0 == map->count) {		dprintk(1,"munmap %p q=%p\n",map,q);		mutex_lock(&q->vb_lock);		for (i = 0; i < VIDEO_MAX_FRAME; i++) {			if (NULL == q->bufs[i])				continue;			mem=q->bufs[i]->priv;#if 0			/* FIXME: this seems to be a hack!!!  */			if (q->bufs[i])				;#endif			if (!mem)				continue;			MAGIC_CHECK(mem->magic,MAGIC_SG_MEM);			if (q->bufs[i]->map != map)				continue;			q->bufs[i]->map   = NULL;			q->bufs[i]->baddr = 0;			q->ops->buf_release(q,q->bufs[i]);		}		mutex_unlock(&q->vb_lock);		kfree(map);	}	return;}/* * Get a anonymous page for the mapping.  Make sure we can DMA to that * memory location with 32bit PCI devices (i.e. don't use highmem for * now ...).  Bounce buffers don't work very well for the data rates * video capture has. */

⌨️ 快捷键说明

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