zoukankan      html  css  js  c++  java
  • 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 <linux/miscdevice.h>
    #include <linux/platform_device.h>
    #include <linux/fs.h>
    #include <linux/file.h>
    #include <linux/mm.h>
    #include <linux/list.h>
    #include <linux/mutex.h>
    #include <linux/debugfs.h>
    #include <linux/mempolicy.h>
    #include <linux/sched.h>
    #include <linux/module.h>
    #include <asm/io.h>
    #include <asm/uaccess.h>
    #include <asm/cacheflush.h>
    #include <linux/dma-mapping.h>
    #include <linux/export.h>
    
    #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
    ");
    			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
    ", 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
    ");
    				ret = -ENOMEM;
    				goto err;
    			}
    			printk(KERN_ERR"cmamem_ioctl:CMEM_ALLOCATE:start alloc:%d,size:%d
    ", 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!
    ", __LINE__);
    				ret = -ENOMEM;
    				goto err;
    			}
    			
    			printk(KERN_ERR"map_dma:0x%08x,size:%d
    ", 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
    ");
    			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
    ", __func__);
    		return -EINVAL; 
    	}
    	
    	
    	printk( "start=0x%08x offset=0x%08x
    ", (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
     success
    ", (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
    ", __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
    ", __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;
    };
    
    
    
    
    #endif
    Makefile

    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 <stdio.h>
    #include <stdarg.h>
    #include <string.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <time.h>
    #include <sys/mman.h>
    #include <assert.h>
    #include <linux/videodev2.h>
    #include <linux/fb.h>
    #include <pthread.h>
    #include <poll.h>
    #include <semaphore.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 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
    ", 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
    ");
    			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
    ",(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!
    ");
    		}
    		for(i = 0; i < 10; i++)
    		((unsigned int *)cmem_base)[i] = i;
    		printf("pmem_base:0x%08x
    ", cmem_base);
    		for(i = 0; i < 10; i++)
    		printf("%d
    ", ((unsigned int *)cmem_base)[i]);
    		
        }
    	close(cmem_fd);
    	return 0;
    }




  • 相关阅读:
    功能测试
    数据库
    loadrunner
    笔记
    基础知识
    类方法, 实例方法, 静态方法
    统计英文单词次数
    合并文件内容
    字典排序
    排序算法
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5239633.html
Copyright © 2011-2022 走看看