实验探究 ioremap

    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 得到的虚拟地址不相等

    1、那么,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 的物理地址。


4、重复映射了,如何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时只是把各地的虚拟地址释放了而已,相互不会产生影响。

你可能感兴趣的:(Hardware,Linux,Kernel)