android misc 设备 用户空间,CMA连续物理内存用户空间映射---(一)(示例代码)

背景:

在多媒体和图像处理等应用中,经经常使用到大块内存,尤其是硬件编解码。须要内核分配大块的物理连续内存。

这里希望通过把从内核分配的连续物理内存映射到用户空间。在用户空间经过处理,又能够入队到驱动中。

前提:

Kernel Config中 依据需求配置和调整CMA的大小。

方法:

(一)

1、驱动注冊misc设备。

2、驱动实现IOCTL的内存分配,使用dma_alloc_writecombine从CMA中拿出一个内存。

3、驱动实现mmap,通过remap_pfn_range,把上面第二步dma分配到的物理内存映射到用户空间;

(二)

1、用户打开设备节点/dev/cma_mem;

2、通过ioctl命令,设置须要分配的大小。

3、通过mmap映射;

測试环境:

Linux-3.9.7

arm-linux-gcc 4.5.1

s5pv210

源代码:

驱动

cma_mem.c

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "cma_mem.h"

#define DEVICE_NAME "cma_mem"

enum cma_status{

UNKNOW_STATUS = 0,

HAVE_ALLOCED = 1,

HAVE_MMAPED =2,

};

struct cmamem_dev {

unsigned int count;

struct miscdevice dev;

struct mutex cmamem_lock;

struct list_head info_list;

};

struct current_status{

int status;

int id_count;

dma_addr_t phy_base;

};

static struct current_status cmamem_status;

static struct cmamem_dev cmamem_dev;

static struct cmamem_info cma_info[32];

static long cmamem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

{

int ret = 0;

int size = 0;

dma_addr_t map_dma;

switch(cmd){

case CMEM_ALLOCATE:

{

printk(KERN_ERR"cmamem_ioctl:CMEM_ALLOCATE\n");

cmamem_status.id_count = cmamem_dev.count++;

cma_info[cmamem_status.id_count].id = cmamem_status.id_count;

if ((ret = copy_from_user(&cma_info[cmamem_status.id_count], (void __user *)arg,

sizeof(struct cmamem_info))))

{

printk(KERN_ERR"cmamem_ioctl:CMEM_ALLOCATE:copy_from_user error:%d\n", ret);

ret = -EFAULT;

goto err;

}

size = cma_info[cmamem_status.id_count].len;

size = PAGE_ALIGN(size);

if(size == 0)

{

printk(KERN_ERR"size is 0\n");

ret = -ENOMEM;

goto err;

}

printk(KERN_ERR"cmamem_ioctl:CMEM_ALLOCATE:start alloc:%d,size:%d\n", cmamem_status.id_count, cma_info[cmamem_status.id_count].len);

cma_info[cmamem_status.id_count].mem_base = (unsigned int)dma_alloc_writecombine(NULL, size, &map_dma, GFP_KERNEL);

if (!cma_info[cmamem_status.id_count].mem_base){

printk(KERN_ERR "dma alloc fail:%d!\n", __LINE__);

ret = -ENOMEM;

goto err;

}

printk(KERN_ERR"map_dma:0x%08x,size:%d\n", map_dma, size);

cma_info[cmamem_status.id_count].phy_base = map_dma;

cmamem_status.phy_base = map_dma;

mutex_lock(&cmamem_dev.cmamem_lock);

cmamem_status.status = HAVE_ALLOCED;

mutex_unlock(&cmamem_dev.cmamem_lock);

break;

}

default:

{

printk(KERN_INFO "cma mem not support command\n");

break;

}

}

err:

return ret;

}

static int cmamem_mmap(struct file *filp, struct vm_area_struct *vma)

{

unsigned long start = vma->vm_start;

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

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

unsigned long page, pos;

//dump_stack();

if(cmamem_status.status != HAVE_ALLOCED)

{

printk(KERN_ERR"%s, you should allocted memory firstly\n", __func__);

return -EINVAL;

}

printk( "start=0x%08x offset=0x%08x\n", (unsigned int)start, (unsigned int)offset );

pos = (unsigned long)cmamem_status.phy_base + offset;

page = pos >> PAGE_SHIFT ;

if( remap_pfn_range( vma, start, page, size, PAGE_SHARED )) {

return -EAGAIN;

}

else{

printk( "remap_pfn_range %u\n success\n", (unsigned int)page );

}

vma->vm_flags &= ~VM_IO;

vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP);

return 0;

}

static struct file_operations dev_fops = {

.owner = THIS_MODULE,

.unlocked_ioctl = cmamem_ioctl,

.mmap = cmamem_mmap,

};

static int __init cmamem_init(void)

{

printk(KERN_ERR"%s\n", __func__);

mutex_init(&cmamem_dev.cmamem_lock);

INIT_LIST_HEAD(&cmamem_dev.info_list);

cmamem_dev.count = 0;

cmamem_dev.dev.name = DEVICE_NAME;

cmamem_dev.dev.minor = MISC_DYNAMIC_MINOR;

cmamem_dev.dev.fops = &dev_fops;

cmamem_status.status = UNKNOW_STATUS;

cmamem_status.id_count = -1;

cmamem_status.phy_base = 0;

return misc_register(&cmamem_dev.dev);

}

static void __exit cmamem_exit(void)

{

printk(KERN_ERR"%s\n", __func__);

misc_deregister(&cmamem_dev.dev);

}

module_init(cmamem_init);

module_exit(cmamem_exit);

MODULE_LICENSE("GPL");

cma_mem.h

#ifndef _CMA_MEM_H_

#define _CMA_MEM_H_

#define CMEM_IOCTL_MAGIC 'm'

#define CMEM_GET_PHYS_IOW(CMEM_IOCTL_MAGIC, 1, unsigned int)

#define CMEM_MAP_IOW(CMEM_IOCTL_MAGIC, 2, unsigned int)

#define CMEM_GET_SIZE_IOW(CMEM_IOCTL_MAGIC, 3, unsigned int)

#define CMEM_UNMAP_IOW(CMEM_IOCTL_MAGIC, 4, unsigned int)

#define CMEM_ALLOCATE_IOW(CMEM_IOCTL_MAGIC, 5, unsigned int)

#define CMEM_CONNECT_IOW(CMEM_IOCTL_MAGIC, 6, unsigned int)

#define CMEM_GET_TOTAL_SIZE_IOW(CMEM_IOCTL_MAGIC, 7, unsigned int)

#define CMEM_CACHE_FLUSH_IOW(CMEM_IOCTL_MAGIC, 8, unsigned int)

struct cmamem_info {

char *name;

char is_cache;

unsigned int id;

unsigned int offset;

unsigned int len;

unsigned int phy_base;

unsigned int mem_base;

//struct list_head list;

};

#endifMakefile

KERN_DIR = /work/kernel/linux-3.9.7

all:

make -C $(KERN_DIR) M=`pwd` modules

clean:

make -C $(KERN_DIR) M=`pwd` modules clean

rm -rf modules.order

obj-m+= cma_mem.o

用户測试程序

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define CMEM_IOCTL_MAGIC 'm'

#define CMEM_GET_PHYS_IOW(CMEM_IOCTL_MAGIC, 1, unsigned int)

#define CMEM_MAP_IOW(CMEM_IOCTL_MAGIC, 2, unsigned int)

#define CMEM_GET_SIZE_IOW(CMEM_IOCTL_MAGIC, 3, unsigned int)

#define CMEM_UNMAP_IOW(CMEM_IOCTL_MAGIC, 4, unsigned int)

#define CMEM_ALLOCATE_IOW(CMEM_IOCTL_MAGIC, 5, unsigned int)

#define CMEM_CONNECT_IOW(CMEM_IOCTL_MAGIC, 6, unsigned int)

#define CMEM_GET_TOTAL_SIZE_IOW(CMEM_IOCTL_MAGIC, 7, unsigned int)

#define CMEM_CACHE_FLUSH_IOW(CMEM_IOCTL_MAGIC, 8, unsigned int)

struct cmamem_info {

char *name;

char is_cache;

unsigned long id;

unsigned long offset;

unsigned long len;

unsigned long phy_base;

unsigned long mem_base;

//struct list_head list;

};

int main()

{

int cmem_fd;

void *cmem_base;

unsigned int size;

struct cmamem_info region;

int i;

cmem_fd = open("/dev/cma_mem", O_RDWR, 0);//打开设备。为了操作硬件引擎,要noncache的

printf("cmem_fd:%d\n", cmem_fd);

if (cmem_fd >= 0)

{

memset(&region, 0x00, sizeof(struct cmamem_info));

region.len = 800 * 480 * 4;

if (ioctl(cmem_fd, CMEM_ALLOCATE, &region) < 0) //获取所有空间

{

perror("PMEM_GET_TOTAL_SIZE failed\n");

return -1;

}

size = region.len;

cmem_base = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, cmem_fd, 0);//mmap操作

printf("cmem_base:0x%08x,region.len:0x%08x offset:0x%08x\n",(unsigned int)cmem_base, region.len, region.offset);

if (cmem_base == MAP_FAILED)

{cmem_base = 0;

close(cmem_fd);

cmem_fd = -1;

perror("mmap pmem error!\n");

}

for(i = 0; i < 10; i++)

((unsigned int *)cmem_base)[i] = i;

printf("pmem_base:0x%08x\n", cmem_base);

for(i = 0; i < 10; i++)

printf("%d\n", ((unsigned int *)cmem_base)[i]);

}

close(cmem_fd);

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值