ioremap----------内核空间物理地址到虚拟地址映射
mmap----------用户空间虚拟地址到物理地址映射
ioremap、 mmap
一、映射方式
几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器、状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址。根据CPU体系结构的不同,CPU对IO端口的编址方式有两种:
a – I/O 映射方式(I/O-mapped)
典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为"I/O地址空间"或者"I/O端口空间",CPU通过专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元。
b – 内存映射方式(Memory-mapped)
RISC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间,外设I/O端口成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。
Linux下内存空间有4G 分为用户空间(3G)和内核空间(1G)
ioremap() -----将IO口的物理地址映射到内核空间的虚拟地址的一种方式
mmap() -----将物理地址映射到用户空间 常用在显存映射到用户空间增加读写效率
二、ioremap()申请映射
void __iomem *ioremap (unsigned long phys_addr, unsigned long size)
phys_addr:物理地址
size: 申请映射从phys_addr开始后的size 个
void __iomem *:返回的数据类型 之后直接对此变量进行操作
void __iomem * mmio;
mmio = ioremap(HW_PINCTRL_DOUT0_SET,OFFSET_REG);
三、iounmap()释放映射
void iounmap(void *addr);
addr : 申请时返回的数据变量
iounmap(mmio);
四、led驱动
使用ioremap来进行对led的操作
1、io方向
static int mx1909_gpio_direction(int pin, unsigned int input)
{
int group,offset;
group = pin/32;
offset = pin%32;
if (input)
__raw_writel(1 << offset, mmio +(HW_PINCTRL_DOE0_CLR-HW_PINCTRL_DOUT0_SET)+(0x10*group));
else
__raw_writel(1 << offset, mmio +(HW_PINCTRL_DOE0_SET-HW_PINCTRL_DOUT0_SET)+(0x10*group));
return 0;
}
2、获取io状态
static int mx1909_gpio_get(int pin)
{
int group,offset;
unsigned int data;
group = pin/32;
offset = pin%32;
data = __raw_readl(mmio +(HW_PINCTRL_DIN0-HW_PINCTRL_DOUT0_SET)+(0x10*group));
return (data>>pin)&0x01;
}
3、对io进行赋值
static void mx1909_gpio_set(int pin, int data)
{
int group,offset;
group = pin/32;
offset = pin%32;
if (data)
__raw_writel(1 << offset, mmio +(HW_PINCTRL_DOUT0_SET-HW_PINCTRL_DOUT0_SET)+(0x10*group));
else
__raw_writel(1 << offset, mmio +(HW_PINCTRL_DOUT0_CLR-HW_PINCTRL_DOUT0_SET)+(0x10*group));
}
五、用户mmap
mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存,普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read和write等。
主要应用在用户空间与内核空间大数据的拷贝、比如:camera 预览 拍照录像 LCD显示
1、用户mmap申请
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
start:映射区的开始地址
length:映射区的长度
prot:期望的内存保护标志
—-PROT_EXEC //页内容可以被执行
—-PROT_READ //页内容可以被读取
—-PROT_WRITE //页可以被写入
—-PROT_NONE //页不可访问
flags:指定映射对象的类型
—-MAP_FIXED
—-MAP_SHARED 与其它所有映射这个对象的进程共享映射空间
—-MAP_PRIVATE 建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原
文件
—-MAP_ANONYMOUS 匿名映射,映射区不与任何文件关联
fd:如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1
offset:被映射对象内容的起点
2、用户释放 munmap
int munmap(void *start, size_t length);
3、内核里的mmap
内核空间的mmap函数原型为:
在file_operations结构体里有其成员,需要字符设备作为桥梁
int (*map)(struct file *filp, struct vm_area_struct *vma);
参数struct vm_area_struct就是内核为我们找到的用户空间的
进程虚拟内存区域,这就是我驱动程序需要映射到设备内存的地址。
作用是实现用户进程中的地址与内核中物理页面的映射
mmap函数原型内需要的操作remap_pfn_range():
使用remap_pfn_range一次建立所有页表;
int remap_pfn_range(struct vm_area_struct *vma,
unsigned long addr,
unsigned long pfn, //物理地址,如为虚地址应通过virt_to_phys()进行转换
unsigned long size,
pgprot_t prot)
vma:用户虚拟内存区域指针, 用户进程创建一个VMA区域
addr:用户虚拟地址的起始值----vma->vm_start
pfn:对应虚拟地址应当被映射的物理地址所在的物理页帧号,可将物理地址
>>PAGE_SHIFT得到。 LINUX 的页,一页占用4KB,所以PAGE_SHIFT等于12
size:要映射的区域的大小---- vma->vm_end-vma->vm_start
prot:VMA的保护属性---PAGE_SHARED