微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > arm驱动linux设备地址映射到用户空间

arm驱动linux设备地址映射到用户空间

时间:11-19 来源:互联网 点击:

运行结果:

mypid is 28529
********打印映射区域内容;和mapChar*******
mapChar = this is a just test temp file
********通过mapChar将数据写入映射区域*******
mapChar = writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,
********checkChar验证*******
mapCharaddress = 0x7f356eaaa000
checkCharaddress = 0x7f356eaa9000
checkChar = writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,

Tip:一个进程的内存映象由下面几部分组成:程序代码、数据、BSS和栈区域,以及内存映射的区域。一个进程的内存区域可以通过查看/proc/pid/maps

三、给驱动设备添加mmap虚拟内存映射
内核函数一)1、 驱动中的mmap(内核空间):

int(*mmap)(struct file *,struct vm_area_struct *);

2、在struct file_operations中与mmap接口函数关联

static struct file_operations module_drv_fops = {
//............
.mmap = memdev_mmap,
//...............
}

结构体一)3、struct vm_area_struct(VMA)结构体如下

struct vm_area_struct {
struct mm_struct * vm_mm; /* The address space we belong to. */
unsigned long vm_start; /* Our start address within vm_mm. */
unsigned long vm_end; /* The first byte after our end address within vm_mm. */
/* linked list of VM areas per task, sorted by address */
struct vm_area_struct *vm_next;
pgprot_t vm_page_prot; /* Access permissions of this VMA. */
unsigned long vm_flags; /* Flags, listed below. */
struct rb_node vm_rb;
struct vm_operations_struct * vm_ops;
/* Information about our backing store: */
unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */
struct file * vm_file; /* File we map to (can be NULL). */
void * vm_private_data; /* was vm_pte (shared mem) */
unsigned long vm_truncate_count;/* truncate_count or restart_addr */
//..................
};

4、struct vm_area_struct(VMA)结构体flag参数
VM_IO将该VMA标记为内存映射的IO区域,VM_IO会阻止系统将该区域包含在进程的存放转存(core dump )中
VM_RESERVED标志内存区域不能被换出

内核函数二)5、内核mmap中创建页表函数:remap_pfn_range();

作用用“addr ~ addr + size之间的虚拟地址”构造页表,参考《虚拟内存共享原理图b》和《虚拟内存共享原理图c》

int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
unsigned long pfn, unsigned long size, pgprot_t prot)

a)参数
1) vma: 虚拟内存区域指针(默认使用vma)
2)addr: 虚拟地址的起始值(默认使用vma->vm_start)
3)pfn:总的来说(pfn = virt_to_phys(void *mem))>>PAGE_SHIFT(常用);或使用默认方式vma->vm_pgoff)

推理:pfn是虚拟地址应该映射到的物理地址所在的物理页帧号就是物理地址右移PAGE_SHIFT位,若PAGE_SIZE为4k则PAGE_SHIFT为12(2的12次方为4k),因此PAGE_SIZE为1>PAGE_SHIFT"得到(虚拟地址 = 物理地址>>PAGE_SHIFT)。如何得到物理地址:将驱动设备中某个内存变量用函数virt_to_phys(void *mem)转换成物理地址;(物理地址 = virt_to_phys(void *mem));
4)size: 要映射的区域的大小。(默认使用vma->vm_end - vma->vm_start)
5)prot: VMA的保护属性。(默认使用vma->vm_page_prot)

模板一)6、mmap驱动模板

static int memdev_mmap(struct file*file, struct vm_area_struct *vma){
struct VirtualDisk *devp = file->private_data; /*鑾峰緱璁惧缁撴瀯浣撴寚閽?/
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED;
if (remap_pfn_range(vma,vma->vm_start,virt_to_phys(devp->mem)>>PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
}

file_oprations中添加或修改.mmap

static struct file_operations module_drv_fops = {
//............
.mmap = memdev_mmap,
//...............
}

实例二)7、完整实例

a)驱动部分

//“module_drv”,"module_","module"
#include //模块所需的大量符号和函数定义
#include
#include //文件系统相关的函数和头文件
#include //指定初始化和清除函数
#include
#include //cdev结构的头文件包含
#include
#include
//#include //包含驱动程序使用的大部分内核API的定义,包括睡眠函数以及各种变量声明
#include //在内核和用户空间中移动数据的函数
#include
#include
#include
#include
#define VIRTUALDISK_SIZE 0x1000//4k
#define MEM_CLEAR 0x1
#define VIRTUALDISK_MAJOR 250
int VirtualDisk_major = VIRTUALDISK_MAJOR;
struct VirtualDisk{
struct cdev cdev;//详细看cdev机制
unsigned char mem[VIRTUALDISK_SIZE ];
long count; /*记录设备目前被多少设备打开*/
};
static struct class *module_class;
static struct class_device *module_class_dev;
struct VirtualDisk *VirtualDiskp;
static int module_drv_open(struct inode *inode, struct file *file)
{
printk("module_dev read\n");
file->private_data = VirtualDiskp;
VirtualDiskp->count++; /*增加设备打开次数*/
return 0;
}
static int module_drv_release(struct inode *inode, struct file *file)
{
printk("module_dev release\n");
VirtualDiskp->count--; /*减少设备打开次数*/
return 0;
}
/*seek文件定位函数:seek()函数对文件定位的起始地址可以是文件开头(SEEK_SET,0)、当前位置(SEEK_CUR,1)、文件尾(SEEK_END,2)*/
static loff_t module_drv_llseek(struct file *file, loff_t offset, int origin){
loff_t ret = 0;/*返回的位置偏移*/

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top