Linux 字符设备驱动基本框架(二)
一、地址映射
我们知道 STM32 无法跑 Linux 系统,是由于它内部没有 MMU(内存管理单元),MMU 主要完成的功能如下:
(1)完成虚拟空间到物理空间的映射。
(2)内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。
Linux 内核启动的时候会初始化 MMU,设置好内存映射,设置好以后 CPU 访问的都是虚拟地址 。
如:I.MX6ULL 的 GPIO1_IO03 引脚的复用寄存器 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 地址为 0x020E0068。如果没有开启 MMU,直接向 0x020E0068 地址处写入数据即可配置复用功能。但现在开启了 MMU 并设置了内存映射,因此就不能直接向 0X020E0068 这个地址写入数据了。我们必须得到 0x020E0068 这个物理地址在 Linux 系统里面对应的虚拟地址,这里就涉及到了物理内存和虚拟内存之间的转换,需要用到两个函数:ioremap 和 iounmap。
1、ioremap 函数
用于获取指定物理地址空间对应的虚拟地址空间。
函数原型:void __iomem * ioremap(cookie,size) // cookie-物理起始地址 size-映射字节数
返回值:__iomem 类型的指针,指向映射后的虚拟空间首地址
我们要获取 I.MX6ULL 的 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器对应的虚拟地址,使用如下代码即可:
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
static void __iomem* SW_MUX_GPIO1_IO03;
SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
2、iounmap 函数
卸载驱动时需要使用 iounmap 函数释放掉 ioremap 函数所做的映射。
函数原型:void iounmap (volatile void __iomem *addr)
__iomem 是什么意思?
【答】:用来表示指针是指向一个 I/O 的内存空间,检查地址空间的有效性。
#define __iomem __attribute((noderef,address_space(2)))
attribute 用来修饰变量,这个变量必须是非解除参考(noderef),即这个变量地址必须是有效的,而且所在的地址空间必须是 2,即 I/O 存储空间。
二、 I/O 内存访问函数
可直接通过指针访问这些地址,但是 Linux 内核不建议这么做,而是推荐使用操作函数对映射后的内存进行读写操作。
1、读操作函数
u32 readl(const volatile void __iomem *addr) // 32bit 读操作,返回值是读到的值
2、写操作函数
void writel(u32 value, volatile void __iomem *addr) // 32bit 写操作