您的位置:首页 > 其它

编写了个videobuf以获得物理上连续的大的内存(DMA)

2013-03-09 07:28 543 查看
编写了个videobuf-contig.c以获得物理上连续的大的内存(DMA)

转载:http://zhiqiang0071.cublog.cn
--------------------------------------------

撰写了符合v4L2的s3c2440的camera驱动,采用videobuf模块管理视频内存,需要申请四个物理上连续的内存,但是linux内核中现有的videobuf-dma-contig在我的系统中只能申请三个(其实是因为默认定义可以申请的buffer大小只有2M,
如果修改大点也可以直接使用videobuf-dma-contig的,修改的地方是arch/arm/include/asm/memory.h, #define CONSISTENT_DMA_SIZE SZ_2M),故以videobuf-dma-contig为蓝本,撰写了videobuf-contig.c,videobuf-contig采用获取物理上连续的页并设置为保留的方式来获得物理上连续的内存,在s3c2440平台中这些物理内存的物理地址等于总线地址,可以进行DMA传输。把videobuf-contig.c加入到内核的drivers/media/video文件夹下
,把videobuf-contig.h加入到内核的include/media文件夹下,

修改drivers/media/video/kconfig,加入

config VIDEOBUF_CONTIG

select VIDEOBUF_GEN

tristate

修改drivers/media/video/Makefile,加入

obj-$(CONFIG_VIDEOBUF_CONTIG) += videobuf-contig.o


代码如下:

videobuf-contig.c:

/*

* helper functions for physically contiguous 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) 2009 modified from videobuf-dma-contig by Seen Yang, <yzq.seen@gmail.com>

*

* 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/pci.h>

#include<linux/vmalloc.h>

#include<linux/pagemap.h>

#include<asm/page.h>

#include<asm/pgtable.h>

#include<media/videobuf-contig.h>

#define MAGIC_PC_MEM 0x18666666

#define MAGIC_CHECK(is,should) if(unlikely((is)!=(should)))
\

{ printk("magic mismatch: %x (expected %x)\n",is,should);
BUG();}

static int debug;

module_param(debug,int, 0644);

MODULE_DESCRIPTION("helper module to manage video4linux physic continue buffers");

MODULE_AUTHOR("Seen Yang, <yzq.seen@gmail.com> ");

MODULE_LICENSE("GPL");

#define dprintk(level, fmt,arg...) if(debug
>= level) \

printk(KERN_DEBUG
"vbuf-contig: " fmt ,
##
arg)

struct videobuf_contig_memory

{

u32 magic;

dma_addr_t paddr;

void *vaddr;

u32 size;

};

/* Allocate physic continue memory for buffers */

static void*malloc_contig(u32 buf_size)

{

void
*mem = 0;

u32 size = PAGE_SIZE
<<
(get_order(buf_size));

mem =
(void
*)__get_dma_pages(GFP_KERNEL,

get_order(buf_size));

if (mem){

unsigned
long adr =
(unsigned
long)mem;

while
(size > 0){

SetPageReserved(virt_to_page(adr));

adr += PAGE_SIZE;

size -= PAGE_SIZE;

}

} else

printk(
"dma memory can't be alloced!!\n");

return mem;

}

/* Free memory for buffers */

static void free_contig(void*addr,
u32 buf_size)

{

u32 size, adr;

if (!addr)

return;

adr =
(u32)addr;

size = PAGE_SIZE
<<
(get_order(buf_size));

while
(size > 0){

ClearPageReserved(virt_to_page(adr));

adr += PAGE_SIZE;

size -= PAGE_SIZE;

}

free_pages((u32)addr, get_order(buf_size));

}

/***************************************************************************/

static void

videobuf_vm_open(struct vm_area_struct*vma)

{

struct videobuf_mapping
*map
= vma->vm_private_data;

dprintk(2,"vm_open %p [count=%u,vma=%08lx-%08lx]\n",map,

map->count,vma->vm_start,vma->vm_end);

map->count++;

}

static void videobuf_vm_close(struct vm_area_struct*vma)

{

struct videobuf_mapping
*map
= vma->vm_private_data;

struct videobuf_queue
*q =
map->q;

int i;

dprintk(2,"vm_close %p [count=%u,vma=%08lx-%08lx]\n",map,

map->count, vma->vm_start,
vma->vm_end);

map->count--;

if (0==map->count){

struct videobuf_contig_memory
*mem;

dprintk(1,"munmap %p q=%p\n",map,
q);

mutex_lock(&q->vb_lock);

/* We need first to cancel streams, before unmapping */

if
(q->streaming)

videobuf_queue_cancel(q);

for
(i = 0; i< VIDEO_MAX_FRAME; i++){

if
(NULL
== q->bufs[i])

continue;

if
(q->bufs[i]->map!=map)

continue;

mem = q->bufs[i]->priv;

if
(mem)
{

/* This callback is called only if kernel has

allocated memory and this memory is mmapped.

In this case, memory should be freed,

in order to do memory unmap.

*/

MAGIC_CHECK(mem->magic, MAGIC_PC_MEM);

dprintk(1,"%s: buf[%d] freeing (%d)\n",

__func__, i, mem->paddr);

free_contig(mem->vaddr, mem->size);

mem->paddr= 0;

mem->vaddr=
NULL;

}

q->bufs[i]->map=
NULL;

q->bufs[i]->baddr=
0;

}

kfree(map);

mutex_unlock(&q->vb_lock);

}

return;

}

static struct vm_operations_struct videobuf_vm_ops=

{

.open= videobuf_vm_open,

.close= videobuf_vm_close,

};

/* ---------------------------------------------------------------------

* contig handlers for the generic methods

*/

/* Allocated area consists on 3 parts:

struct video_buffer

struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)

struct videobuf_dma_sg_memory

*/

static void*__videobuf_alloc(size_t size)

{

struct videobuf_contig_memory
*mem;

struct videobuf_buffer
*vb;

vb = kzalloc(size+sizeof(*mem),GFP_KERNEL);

mem = vb->priv=
((char*)vb)+size;

mem->magic=MAGIC_PC_MEM;

dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n",

__func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb),

mem,(long)sizeof(*mem));

return vb;

}

static int __videobuf_iolock(struct videobuf_queue* q,

struct videobuf_buffer
*vb,

struct v4l2_framebuffer
*fbuf)

{

struct videobuf_contig_memory
*mem = vb->priv;

BUG_ON(!mem);

MAGIC_CHECK(mem->magic, MAGIC_PC_MEM);

switch
(vb->memory){

case V4L2_MEMORY_MMAP:

dprintk(1,"%s memory method MMAP\n",__func__);

/* All handling should be done by __videobuf_mmap_mapper() */

if
(!mem->paddr){

printk(KERN_ERR
"memory is not alloced/mmapped.\n");

return
-EINVAL;

}

break;

case V4L2_MEMORY_USERPTR:

dprintk(1,"%s memory method USERPTR\n",__func__);

if
(vb->baddr){

printk(KERN_ERR
"USERPTR is currently not supported\n");

return
-EINVAL;

}

/* The only USERPTR currently supported is the one needed for

read() method.

*/

mem->size= PAGE_ALIGN(vb->size);

mem->vaddr= malloc_contig(mem->size);

if
(!mem->vaddr){

printk(KERN_ERR
"malloc_contig (%d pages) failed\n", mem->size);

return
-ENOMEM;

}

mem->paddr= virt_to_phys(mem->vaddr);

dprintk(1,"malloc_contig is at addr %d (%d pages)\n",

mem->paddr, mem->size);

break;

case V4L2_MEMORY_OVERLAY:

default:

dprintk(1,"%s memory method OVERLAY/unknown\n",__func__);

/* Currently, doesn't support V4L2_MEMORY_OVERLAY */

printk(KERN_ERR
"Memory method currently unsupported.\n");

return
-EINVAL;

}

return 0;

}

static int __videobuf_sync(struct videobuf_queue*q,

struct videobuf_buffer
*buf)

{

return 0;

}

static int __videobuf_mmap_free(struct videobuf_queue*q)

{

unsigned
int i;

dprintk(1,"%s\n",__func__);

for
(i = 0; i< VIDEO_MAX_FRAME; i++){

if
(q->bufs[i]){

if
(q->bufs[i]->map)

return
-EBUSY;

}

}

return 0;

}

static int __videobuf_mmap_mapper(struct videobuf_queue*q,

struct vm_area_struct
*vma)

{

struct videobuf_contig_memory
*mem;

struct videobuf_mapping
*map;

unsigned
int first;

int retval, size;

unsigned
long offset = vma->vm_pgoff<< PAGE_SHIFT;

dprintk(1,"%s\n",__func__);

if (!(vma->vm_flags&
VM_WRITE)||!(vma->vm_flags&
VM_SHARED))

return
-EINVAL;

/* look for first buffer to map */

for
(first = 0; first< VIDEO_MAX_FRAME; first++){

if
(NULL
== q->bufs[first])

continue;

if
(V4L2_MEMORY_MMAP != q->bufs[first]->memory)

continue;

if
(q->bufs[first]->boff==
offset)

break;

}

if (VIDEO_MAX_FRAME== first){

dprintk(1,"mmap app bug: invalid user space offset [offset=0x%lx]\n",

(vma->vm_pgoff<< PAGE_SHIFT));

return
-EINVAL;

}

/* create mapping + update buffer list */

map
= kzalloc(sizeof(struct videobuf_mapping),
GFP_KERNEL);

if (NULL==map)

return
-ENOMEM;

q->bufs[first]->map=
map;

map->start= vma->vm_start;

map->end= vma->vm_end;

map->q= q;

q->bufs[first]->baddr=
vma->vm_start;

mem = q->bufs[first]->priv;

BUG_ON(!mem);

MAGIC_CHECK(mem->magic, MAGIC_PC_MEM);

mem->size= PAGE_ALIGN(q->bufs[first]->bsize);

mem->vaddr= malloc_contig(mem->size);

if (!mem->vaddr){

printk(KERN_ERR
"malloc_contig (%d size) failed\n", mem->size);

goto
error;

}

mem->paddr= virt_to_phys(mem->vaddr);

dprintk(1,"malloc_contig is at addr %i (%d size)\n",

mem->paddr, mem->size);

size = vma->vm_end- vma->vm_start;

size =
(size < mem->size)? size
: mem->size;

/* Try to remap memory */

vma->vm_page_prot= pgprot_writecombine(vma->vm_page_prot);

retval = remap_pfn_range(vma, vma->vm_start,

mem->paddr>> PAGE_SHIFT,

size, vma->vm_page_prot);

if (retval< 0){

printk(KERN_ERR
"mmap: remap failed with error %d. ", retval);

free_contig(mem->vaddr, mem->size);

goto
error;

}

vma->vm_ops=
&videobuf_vm_ops;

vma->vm_flags|= VM_DONTEXPAND| VM_RESERVED;

vma->vm_private_data=
map;

dprintk(1,"mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",

map, q, vma->vm_start,
vma->vm_end,

(longint) q->bufs[first]->bsize,

vma->vm_pgoff, first);

videobuf_vm_open(vma);

return 0;

error:

mem =
NULL;

kfree(map);

return
-ENOMEM;

}

static int __videobuf_copy_to_user(
struct videobuf_queue*q,

char __user
*data,
size_t count,

int nonblocking
)

{

struct videobuf_contig_memory
*mem = q->read_buf->priv;

void
*vaddr;

BUG_ON(!mem);

MAGIC_CHECK(mem->magic, MAGIC_PC_MEM);

BUG_ON(!mem->vaddr);

/* copy to userspace */

if (count> q->read_buf->size-
q->read_off)

count
= q->read_buf->size- q->read_off;

vaddr = mem->vaddr;

if (copy_to_user(data, vaddr+ q->read_off,count))

return
-EFAULT;

return
count;

}

static int __videobuf_copy_stream(
struct videobuf_queue*q,

char __user
*data,
size_t count,size_t pos,

int vbihack,int nonblocking
)

{

unsigned
int *fc;

struct videobuf_contig_memory
*mem=q->read_buf->priv;

BUG_ON (!mem);

MAGIC_CHECK(mem->magic,MAGIC_PC_MEM);

if (vbihack){

/* dirty, undocumented hack -- pass the frame counter

* within the last four bytes of each vbi data block.

* We need that one to maintain backward compatibility

* to all vbi decoding software out there ... */

fc =
(unsigned
int*)mem->vaddr;

fc +=(q->read_buf->size>>2)-1;

*fc
= q->read_buf->field_count>>
1;

dprintk(1,"vbihack: %d\n",*fc);

}

/* copy stuff using the common method */

count
= __videobuf_copy_to_user (q, data,count, nonblocking);

if ((count==-EFAULT)&&(0
== pos))

return
-EFAULT;

return
count;

}

static void*__videobuf_to_vmalloc(struct videobuf_buffer*buf)

{

struct videobuf_contig_memory
*mem = buf->priv;

BUG_ON(!mem);

MAGIC_CHECK(mem->magic, MAGIC_PC_MEM);

return mem->vaddr;

}

static struct videobuf_qtype_ops qops=
{

.magic
= MAGIC_QTYPE_OPS,

.alloc
= __videobuf_alloc,

.iolock
= __videobuf_iolock,

.sync
= __videobuf_sync,

.mmap_free
= __videobuf_mmap_free,

.mmap_mapper
= __videobuf_mmap_mapper,

.video_copy_to_user
= __videobuf_copy_to_user,

.copy_stream
= __videobuf_copy_stream,

.vmalloc
= __videobuf_to_vmalloc,

};

void videobuf_queue_contig_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)

{

videobuf_queue_core_init(q, ops, dev, irqlock, type,
field, msize,

priv,
&qops);

}

EXPORT_SYMBOL_GPL(videobuf_queue_contig_init);

u32 videobuf_to_contig (struct videobuf_buffer*buf)

{

struct videobuf_contig_memory
*mem=buf->priv;

BUG_ON (!mem);

MAGIC_CHECK(mem->magic,MAGIC_PC_MEM);

return mem->paddr;

}

EXPORT_SYMBOL_GPL(videobuf_to_contig);

void videobuf_contig_free
(struct videobuf_buffer
*buf)

{

struct videobuf_contig_memory
*mem = buf->priv;

/* mmapped memory can't be freed here, otherwise mmapped region

would be released, while still needed. In this case, the memory

release should happen inside videobuf_vm_close().

So, it should free memory only if the memory were allocated for

read() operation.

*/

if ((buf->memory!=
V4L2_MEMORY_USERPTR)||!buf->baddr)

return;

if (!mem)

return;

MAGIC_CHECK(mem->magic, MAGIC_PC_MEM);

free_contig(mem->vaddr, mem->size);

mem->paddr= 0;

mem->vaddr=
NULL;

return;

}

EXPORT_SYMBOL_GPL(videobuf_contig_free);

videobuf-contig.h:

/*

* helper functions for physically contiguous 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) 2009 modified from videobuf-dma-contig by Seen Yang, <yzq.seen@gmail.com>

*

* 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

*/

#ifndef _VIDEOBUF_CONTIG_H

#define _VIDEOBUF_CONTIG_H

#include<media/videobuf-core.h>

/* --------------------------------------------------------------------- */

void videobuf_queue_contig_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);

u32 videobuf_to_contig (struct videobuf_buffer*buf);

void videobuf_contig_free
(struct videobuf_buffer
*buf);

#endif
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐