Background:
In multimedia and image processing applications, large blocks of memory are often used, especially hardware codec. The kernel needs to allocate large blocks of physical continuous memory.
We hope to map the continuous physical memory allocated from the kernel to the user space. After processing the user space, we can also join the driver.
Prerequisites:
Configure and adjust the CMA size as needed in kernel config.
Method:
(1)
1. The driver registers the MISC device;
2. The driver allocates IOCTL memory and uses dma_alloc_writecombine to extract a memory from CMA;
3. The driver implements MMAP and maps the physical memory allocated by the DMA in the second step to the user space through remap_pfn_range;
(2)
1. the user opens the device node/dev/cma_mem;
2. Use the ioctl command to set the size to be allocated;
3. MMAP ing;
Test environment:
Linux-3.9.7
Arm-Linux-GCC 4.5.1
S5pv210
Source code:
Driver
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.7all:make -C $(KERN_DIR) M=`pwd` modules clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderobj-m+= cma_mem.o
User Testing Program
# 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, unsigne D INT) # define cmem_map_iow (cmem_ioctl_magic, 2, unsigned INT) # define evaluate (cmem_ioctl_magic, 3, unsigned INT) # define evaluate (cmem_ioctl_magic, 4, unsigned INT) # define evaluate (Measures, 5, unsigned INT) # define cmem_connect_iow (cmem_ioctl_magic, 6, unsigned INT) # define evaluate (cmem_ioctl_magic, 7, unsigned INT) # define evaluate Tl_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); // open the device. To operate the hardware engine, 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) // obtain all space {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 operation printf ("cmem_base: 0x % 08x, region. len: 0x % 08x offset: 0x % 08x \ n ", (unsigned INT) cmem_base, region. len, region. offs Et); 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 ;}