【无标题】

本文介绍了Linux内核驱动如何使用mmap映射不带cache的内存供应用层使用,以解决嵌入式系统中寄存器IO映射的同步问题。通过示例代码展示了驱动的mmap接口实现,并提供了应用层测试程序的mmap函数使用方法。测试程序演示了如何映射并读写设备内存。
摘要由CSDN通过智能技术生成

内核驱动mmap

在写这个驱动之前先说一个概念,在申请内存的时候,如果不主动传入不带cache的内存申请一般都是带cache,对于嵌入式SOC中的某个模块的寄存器IO映射区域,如果申请了带cache的内存,在应用层使用mmap映射得到的虚拟地址,当我们直接读写的时候,会先从pagecache中读取数据,而不是直接读取寄存器的值,这样就会存在一个不同步问题,导致应用层的写入和读出不是实时的,导致出错,所以驱动底层需要mmap映射不带cache的内存。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/slab.h> //定义kmalloc接口
#include <asm/io.h>     //定义virt_to_phys接口
#include <linux/mm.h>   //remap_pfn_range
#include <linux/vmalloc.h>
#include <linux/delay.h>
#define MM_SIZE 4096

static int mmap_open(struct inode *my_indoe, struct file *my_file)
{
   printk("mmap_test open \n");
   return 0;
}

static int mmap_release(struct inode *my_indoe, struct file *my_file)
{
   printk("mmap_test release\n");
   return 0;
}

static int mmap_test(struct file *myfile, struct vm_area_struct *vma)
{
   vma->vm_flags |= VM_IO;//表示对设备IO空间的映射
   vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP); //表示内存不回收

   if(remap_pfn_range(vma,//虚拟内存区域,即设备地址将要映射到这里
                  vma->vm_start,//虚拟空间的起始地址
                  vma->vm_pgoff,//与物理内存对应的页帧号,物理地址右移12位
                  vma->vm_end - vma->vm_start,//映射区域大小,一般是页大小的整数倍
		  		  pgprot_noncached(vma->vm_page_prot)//映射不带cache的内存
		  ))
   {
      return -EAGAIN;
   }

   printk("In %s,pgoff = %lx, start= %lx,end = %lx\n",__func__,vma->vm_pgoff,vma->vm_start,vma->vm_end);
   return 0;
}

static struct file_operations mmap_fops=
{
   .open=mmap_open,
   .release=mmap_release,
   .mmap=mmap_test
};

static struct miscdevice misc={
   .minor=255,
   .name="mmap_test",  // /dev/下的名称
   .fops=&mmap_fops,
};

static int __init mmap_init(void)
{
   /*1. 注册杂项字符设备*/
   misc_register(&misc);
   printk("mmap test init 驱动安装成功!\n");
   return 0;
}

static void __exit mmap_exit(void)
{
    /*2. 注销*/
    misc_deregister(&misc);
   printk("mmap test exit驱动卸载成功!\n");
}

module_init(mamp_init);
module_exit(mmap_exit);

MODULE_AUTHOR("mmap");      //声明驱动的作者
MODULE_DESCRIPTION("mmap模块测试"); //描述当前驱动功能
MODULE_LICENSE("GPL");  //驱动许可证。支持的协议GPL。

测试应用程序

比如需要访问0x02000000开始的地址,长度为0x400,因为mmap需要以page来映射 ,所以需要映射一个page页的大小。

应用层的mmap函数原型介绍

mmap函数用于将一个文件或者其它对象映射进内存,通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用read,write等操作。​

头文件:<sys/mman.h>​

函数原型:​

void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);int munmap(void* start,size_t length);​

映射函数
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset); ​

addr: 指定映射的起始地址,通常设为NULL,由系统指定。​
length:映射到内存的文件长度。​
prot:映射的保护方式,可以是:​PROT_EXEC:映射区可被执行​
PROT_READ:映射区可被读取​
PROT_WRITE:映射区可被写入​
PROT_NONE:映射区不能存取​
Flags:映射区的特性,可以是:​MAP_SHARED:写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享。​
MAP_PRIVATE:对映射区的写入操作会产生一个映射区的复制(copy_on_write),对此区域所做的修改不会写回原文件。​
fd:由open返回的文件描述符,代表要映射的文件。​
offset:以文件开始处的偏移量,必须是分页大小的整数倍,通常为0,表示从文件头开始映射。​
解除映射
int munmap(void *start, size_t length); ​

功能:取消参数start所指向的映射内存,参数length表示欲取消的内存大小。​

测试应用

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <ctype.h>

#define PAGESIZE  4096

#define readl(addr)           (*((volatile unsigned long *)(addr)))
#define writel(v, addr)       (*((volatile unsigned long *)(addr)) = (unsigned int)(v))


static int mmap_test(unsigned int reg_value)
{
	unsigned char* mmap_io;
	int fd;

	fd = open("/dev/mmap_test", 2);
	if(fd<0)
	{
		printf("the drivers is error!!!\n");
		return -1;
	}

	mmap_io =(unsigned char*)mmap(NULL, 4096,PROT_READ | PROT_WRITE,
						          MAP_SHARED, fd, 0x02000000);
	if(mmap_io == NULL)
	{
		printf("projection error:%s...%d\n", __func__, __LINE__);
		return -1;
	}


	printf("mmap_io reg(0x02000000) value 0x%x\n", readl(sid_oem));
	writel(reg_value, mmap_io);
	printf("mmap_io reg(0x02000000) value 0x%x\n", readl(sid_oem));
	
	munmap(mmap_io, PAGESIZE);
	close(fd);
	return 0;
}


int main(int argc, char **argv)
{
	int ret;
	unsigned int reg_value;
	
	if (argc < 1) {
		return -1;
	}

	reg_value = strtol(argv[1], NULL, 16);
	mmap_test(reg_value);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值