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; }; #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 <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(®ion, 0x00, sizeof(struct cmamem_info)); region.len = 800 * 480 * 4; if (ioctl(cmem_fd, CMEM_ALLOCATE, ®ion) < 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; }