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(®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连续物理内存用户空间映射---(一)的更多相关文章
- CMA连续物理内存用户空间映射---(二)
摘要: 相对于上一篇測试程序CMA连续物理内存用户空间映射---(一) 添加功能: 1.分配和映射统一放在IOCTL,一次完毕,能够连续多次分配并映射到用户空间,提高操作性: 2.驱动添加链表,使分配 ...
- Linux用户空间与内核空间(理解高端内存)
Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...
- Linux用户空间与内核空间
源:http://blog.csdn.net/f22jay/article/details/7925531 Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针 ...
- linux 用户空间与内核空间——高端内存详解
摘要:Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对 ...
- linux内存管理-内核用户空间 【转】
转自:http://blog.chinaunix.net/uid-25909619-id-4491362.html 1,linux内存管理中几个重要的结构体和数组 page unsigned long ...
- linux用户空间和内核空间(内核高端内存)_转
转自:Linux用户空间与内核空间(理解高端内存) 参考: 1. 进程内核栈.用户栈 2. 解惑-Linux内核空间 3. linux kernel学习笔记-5 内存管理 Linux 操作系统和驱 ...
- Linux用户空间与内核空间(理解高端内存)【转】
转自:http://www.cnblogs.com/wuchanming/p/4360277.html Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递 ...
- User space(用户空间) 与 Kernel space(内核空间)
出处: User space 与 Kernel space (整理)用户空间_内核空间以及内存映射 学习 Linux 时,经常可以看到两个词:User space(用户空间)和 Kernel spac ...
- Linux用户空间与内核地址空间
Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...
随机推荐
- C#关于使用枚举遇到的问题----Parse()方法使用注意
声明了一个枚举 public enum ceshimeiju { 跃动,光子} ceshimeiju ce = Enum.Parse(typeof(ceshimeiju ), "跃动&quo ...
- ASP.NET协作应用集成到trsids身份验证服务器的开发流程
开发Actor协同模块: (参考TRSIDS4.0 协作应用集成手册[asp.net]) ASP.Net协作应用集成到IDS之前,需要开发Actor类实现协作应用回调接口中定义的本地登录.退出.用户信 ...
- (转)SVN教程总结
文章原地址:http://www.cnblogs.com/armyfai/p/3985660.html SVN简介: 为什么要使用SVN? 程序员在编写程序的过程中,每个程序员都会生成很多不同的版本, ...
- ASP.NET 页面间数据传递的方法
在做WEB开发时,很多地方会涉及到页面间的数据传递.这几天在完善教务基础系统,遇到了这个问题,上网查了一些资料,现总结如下: 说到页面间数据传递,很多人都会想到通过像Session这样的全局变量,但是 ...
- Visual Studio 2013如何破解(密钥激活)
其实有个方法最简单,就是点击“帮助”,选择注册产品,点击打开页面右下边的“使用秘钥注册产品”,输入上述秘钥即可. 在输入密钥界面,输入密钥“BWG7X-J98B3-W34RT-33B3R-JVYW ...
- CUICatalog: Invalid asset name supplied: (null)
出现这个问题的根本原因是你调用了[UIImage imageNamed:name]这个方法 但是name = nil;所以报出该错误. 解决方法,在项目中搜索[UIImage imageNamed ...
- 数据库SQLite在Qt5+VS2012使用规则总结---中文乱码
VS2012默认格式为 "GB2312-80",而有时我们用到字符串需要显示中文时,就会出现乱码.下面仅就Qt5和VS2012中使用数据库SQLite时,做一个简单的备忘录 #in ...
- QT5-控件-QTimeEdit和QTime
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QTimeEdit> #i ...
- 你好,C++(22) 排排坐,吃果果——4.3.3 for循环:某个范围内…每个都…
4.3.3 for循环:某个范围内…每个都… 既然while语句和do…while…语句都已经可以满足我们表达循环现象的需要,那为什么C++还要专门提供for语句来表达循环现象呢?在现实世界中,常常 ...
- mahout学习-1
一. 安装软件 需要安装如下文件: java, Eclipse, Maven,Hadoop,mahout 二. 推荐系统简介 每天,我们都会对一些事物表达自己的看法,喜欢,或不喜欢,或不在乎.这些都在 ...