ioremap 写驱动最常用的函数之一,但是对它始终一知半解,看了内核关于这部分的代码,功力不够也是一头雾水。本文通过实验的方法,了解 ioremap 到底干了些啥,本文献给那些看不懂内核源代码,还想知道 ioremap 能干些什么,干了些什么的同学。
实验方法:
点灯实验,LED接在GPB5~8,因此需要使用 gpbcon 配置,gpbdat 输出高低。
gpbcon 0x56000010
gpbdat 0x56000014
下面,尝试了各种 ioremap 的方法测试,点灯 关灯 是否正常,点灯正常代表寄存器访问没问题。
1、字节映射还是页映射?
gpbcon = (volatile unsigned long *)ioremap(0x56000010, 1);
gpbdat = gpbcon + 1;
测试结果,点灯正常,虽然只映射一个字节,但是 gpbcon、gpbdat 使用虚拟地址时可以访问到物理地址,因此是页映射。
2、同一页的 映射的物理地址之前的地址是否也映射了?
gpbdat = (volatile unsigned long *)ioremap(0x56000014, 1);
gpbcon = gpbdat - 1;
测试结果,点灯正常,虽然只映射了1个字节,但是之前的地址也可以访问到对应的物理地址,因此,该物理地址所在的页,全都都进行了映射。
3、同一页内,重复映射会怎样?
gpbcon = (volatile unsigned long *)ioremap(0x56000010, 1);
gpbdat = (volatile unsigned long *)ioremap(0x56000014, 1);
temp = (volatile unsigned long *)ioremap(0x56000014, 1);
printk("gpbcon: %x\n", gpbcon);
printk("gpbdat: %x\n", gpbdat);
printk(" temp: %x\n", temp);
输出:
gpbcon: c483c010
gpbdat: c4840014
temp: c4844014
测试结果:
程序依旧正常运行,但是输出的 gpbcon、gpbdat 虚拟地址不连续!即使是相同的物理地址 ioremap 得到的虚拟地址不相等。
4、那么,gpbcon + 1 也就是 c483c014 是不是也对应于 gpbdat的物理地址呢?
gpbcon = (volatile unsigned long *)ioremap(0x56000010, 1);
gpbdat = (volatile unsigned long *)ioremap(0x56000014, 1);
temp = (volatile unsigned long *)ioremap(0x56000014, 1);
printk("gpbcon: %x\n", gpbcon);
printk("gpbdat: %x\n", gpbdat);
printk(" temp: %x\n", temp);
gpbdat_temp = gpbcon + 1;
测试 虚拟地址gpbdat_temp 、gpbdat 是不是都能访问到 gpbdat 的物理地址。
输出:
gpbcon: c484a010
gpbdat: c484e014
temp: c4852014
测试结果:
1、神奇,程序正常运行,也就是说通过 虚拟地址 c484e014 或者(c484a010 + 4) 都能访问到 gpbdat 的物理地址!
因此,如果两个物理地址位于同一页,我们是没必要去两次ioremap的,1次就够了,而且两次会浪费掉一页虚拟空间。
2、 推理,我通过ioremap temp 得到的虚拟地址也可以访问到 gpbdat 的物理地址。
我通过ioremap temp 得到的虚拟地址 -4 也可以访问到 gpbcon 的物理地址。
5、重复映射了,如何iounmap ?
gpbcon = (volatile unsigned long *)ioremap(0x56000010, 1);
gpbdat = (volatile unsigned long *)ioremap(0x56000014, 1);
temp = (volatile unsigned long *)ioremap(0x56000014, 1);
printk("gpbcon: %x\n", gpbcon);
printk("gpbdat: %x\n", gpbdat);
printk(" temp: %x\n", temp);
iounmap(gpbcon);
iounmap(gpbdat);
iounmap(temp);
测试:程序正常,没有崩溃。
ioremap 可以有多个虚拟地址对应于一个物理地址,iounmap时相互不受影响。
猜测:iounmap(gpbcon + n);保证(gpbcon + n 与 gpbcon在同一页)应该也可以释放掉gpbcon,没有测试。
结论:
1、ioremap 按照页大小进行映射,而且是 整页 。
2、ioremap 允许对一个物理地址进行多次映射,而且分配的虚拟空间地址各不相同(多个虚拟地址对应于同一个物理地址)。而且,ioumap相互不影响。
曾经疑惑的那些问题:
1、我要是用一个寄存器,4个字节
ioremap(0x56000010, 1);
ioremap(0x56000010, 4);
都能用?显然,都能用页映射,该物理地址所在的页,已经全部被映射了。
2、两个驱动程序,都要用到同一个寄存器,两次ioremap/iounmap会不会冲突?
显然经过上边的实验,它们各自ioremap得到的虚拟地址不同,iounmap时只是把各地的虚拟地址释放了而已,相互不会产生影响。