Background:
In applications such as multimedia and image processing, large chunks of memory, especially hardware codecs, are often used. Requires the kernel to allocate large chunks of physical contiguous memory.
It is desirable to map the contiguous physical memory allocated from the kernel to the user space. The user space is processed and can be queued to the driver.
Premise:
The size of the CMA is configured and adjusted according to requirements in Kernel Config.
Method:
A
1, the Drive register misc equipment.
2, the driver realizes the IOCTL memory allocation, uses the dma_alloc_writecombine to take out a memory from the CMA.
3, the drive realizes Mmap, through the Remap_pfn_range, the above second step DMA allocates to the physical memory to map to the user space;
Two
1, the user opens the device node/dev/cma_mem;
2, through the IOCTL command, set the size required to allocate.
3, through the mmap mapping;
Test environment:
Linux-3.9.7
ARM-LINUX-GCC 4.5.1
s5pv210
Source:
Driven
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;st Ruct 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"); 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);p OS = (unsigned long) cmamem_ Status.phy_base + offset;page = pos >> page_shift, if (Remap_pfn_range (VMA, start, page, size, page_shared)) {Retu Rn-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 li St_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 clean RM-RF modules.orderobj-m+= CMA_MEM.O
User test procedure
#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);//Turn on the device. In order to operate the hardware engine, go to Noncache printf ("cmem_fd:%d\n", CMEM_FD), if (cmem_fd >= 0) {memset (®ion, 0x00, sizeof (struct C Mamem_info)) Region.len = * 480 * 4;IF (IOCTL (CMEM_FD, Cmem_allocate, ®ion) < 0)//Get 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.offset); if (cmem_base = = map_failed) {cmem_base = 0; Close (CMEM_FD); CMEM_FD = -1;perror ("mmap pmem error!\n");} for (i = 0; i < 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 Continuous physical memory User Space mapping---(i)