首页 > 代码库 > CMA连续物理内存用户空间映射---(一)

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\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;
};




#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\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;
}