1:静态映射方法的特点:
内核移植时以代码的形式硬编码,如果要更改必须改源代码后重新编译内核在内核启动时建立静态映射表,到内核关机时销毁,中间一直有效对于移植好的内核,你用不用他都在那里
2:动态映射方法的特点:
驱动程序根据需要随时动态的建立映射、使用、销毁映射映射是短期临时的
3:如何选择虚拟地址映射方法
(1)2种映射并不排他,可以同时使用
(2)静态映射类似于C语言中全局变量,动态方式类似于C语言中malloc堆内存
(3)静态映射的好处是执行效率高,坏处是始终占用虚拟地址空间;动态映射的 好处是按需使用虚拟地址空间,坏处是每次使用前后都需要代码去建立映射&销毁映射(还得学会使用那些内核函数的使用)
4:三星版本内核(内核版本2.6.35.7)中的静态映射表
不同版本的内核,其静态映射表的文件名以及文件路径不一定相同,但是一般都在arch/arm/xxx/map_xx.h文件中
(1)主映射表位于:arch/arm/plat-s5p/include/plat/map-s5p.h
CPU在安排寄存器地址时不是随意乱序分布的,而是按照模块去区分的。每一个模块内部的很多个寄存器的地址是连续的。所以内核在定义寄存器地址时都是先找到基地址,然后再用基地址+偏移量来寻找具体的一个寄存器。这个文件夹下面的虚拟地址基地址是不全的,原因是三星在移植的时候只是移植了自己需要的部分,将来我们需要添加其他基地址的时候直接添加就行了。
map-s5p.h中定义的就是要用到的几个模块的寄存器基地址(虚拟地址)。
(2)虚拟地址基地址定义在:arch/arm/plat-samsung/include/plat/map-base.h
#define S3C_ADDR_BASE (0xFD000000) // 三星移植时确定的静态映射表的基地址,表中的所有虚拟地址都是以这个地址+偏移量来指定的
(3)GPIO相关的主映射表位于:arch/arm/machs5pv210/include/mach/regs-gpio.h
表中是GPIO的各个端口的基地址的定义
(4)GPIO的具体寄存器定义位于:arch/arm/mach-s5pv210/include/mach/gpio-bank.h
5:实驱动LED灯
通过驱动代码去操作LED灯和裸机操作LED灯的方法是一样的,都是操作相应的寄存器地址,不同之处在于驱动代码操作的是虚拟地址,而裸机操作的LED灯对应的物理地址。一个物理地址可以对应多个虚拟地址,原因在于虚拟地址最终指向的也是物理地址,(相当于一个房间(物理地址)有多个入口(虚拟地址)一样)但是一个虚拟地址只能对应一个物理地址。
虚拟地址寄存器(查看mach/gpio-bank.h)
#define GPJ0CONS5PV210_GPJ0CON //0xFD050240 #define GPJ0DATS5PV210_GPJ0DAT //0xFD050244 #define rGPJ0CON*((volatile unsigned int *)GPJ0CON) #define rGPJ0DAT*((volatile unsigned int *)GPJ0DAT) rGPJ0CON = 0x11111111; //设置寄存器的相应位 rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); // 亮 rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5)); // 灭
6:动态映射操作LED
6.1:如何建立动态映射
(1)request_mem_region:向内核申请(报告)需要映射的内存资源。
request_mem_region(GPJ0CON_PA, 8, "GPJ0CON"))
参数说明:
GPJ0CON_PA:需要进行虚拟映射的物理地址
8:映射的长度为8字节
"GPJ0CON":描述这个动态映射的名字
(2)ioremap:真正用来实现映射,传给他物理地址他返回给你一个虚拟地址
pGPJ0CON = ioremap(GPJ0CON_PA, 8);
参数:
GPJ0CON_PA:需要进行虚拟映射的物理地址
8:映射的长度为8字节
返回值:成功返回一个虚拟地址(或者是一段虚拟地址的首地址,具体取决于我们需要映射的字节长度),失败返回0
6.2:如何销毁动态映射
(1)iounmap:解除映射
(2)release_mem_region:释放映射
//使用动态映射来操作LED //发出虚拟地址申请请求 if (!request_mem_region(GPJ0CON_PA, 8, "GPJ0CON")) return -EINVAL; //申请虚拟地址 pGPJ0CON = ioremap(GPJ0CON_PA, 8); //if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0DAT")) //return -EINVAL; //申请虚拟地址 //pGPJ0DAT = ioremap(GPJ0DAT_PA, 4); //使用申请得到的虚拟地址 *pGPJ0CON = 0x11111111; *(pGPJ0CON+1) = ((0<<3) | (0<<4) | (0<<5)); //pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); *(pGPJ0CON+1) = ((1<<3) | (1<<4) | (1<<5)); //释放申请的虚拟地址 //解除映射 iounmap(pGPJ0CON); //iounmap(pGPJ0DAT); //释放申请的虚拟地址 //release_mem_region(GPJ0CON_PA, 8); release_mem_region(GPJ0DAT_PA, 4);
注:因为这里GPJ0CON(0xe0200240) 和GPJ0DAT(0xe0200244)是两个连续的物理地址,所以我们可以只映射0xe0200240这个物理地址,映射的字节数为8,那么(GPJ0CON+1)就是我们GPJ0DAT对应的虚拟地址。