標籤:
背景:
在多媒體和影像處理等應用中,經經常使用到大塊記憶體,尤其是硬體編解碼。須要核心分配大塊的物理連續記憶體。
這裡希望通過把從核心分配的連續實體記憶體映射到使用者空間。在使用者空間經過處理,又能夠入隊到驅動中。
前提:
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.7all:make -C $(KERN_DIR) M=`pwd` modules clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderobj-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;}
CMA連續實體記憶體使用者空間映射---(一)