首先列一张Linux下应用程序对驱动的调用图
本次讨论内核是如何操作具体硬件的。
从图中可知,内核是通过驱动程序中的open(),close()等函数去实现硬件操作的,单从这一点来看,似乎跟单片机通过库函数操作外设差不多。
但是Linux内核不是为某一款芯片去设计的,它可以在许多芯片上运行,很明显,它不可能将每款芯片的具体操作函数都写进去,这样的话内核得爆炸。从软件设计层面上来说,分层是必不可少的,为此Linux有着自己的一套驱动框架,不同芯片只需要按它的驱动框架编写驱动就可以了。
#include <xxx.h>
static int __init driver_init(void)
{
}
static void __exit driver_exit(void)
{
}
module_init(driver_init);
module_exit(driver_exit);
MODULE_LICENSE("GPL");
正常情况下的driver_init()会进行设备注册,最基本需要实现的功能包括:
- 分配设备号和设备名
- 完成设备打开关闭的操作函数
- 完成设备读写操作函数
- 完成设备的注册
完成驱动函数后编译生成驱动文件,然后进行安装就可以测试使用了,安装指令使用modprobe,如果需要卸载使用rmmod,内核驱动安装卸载-CSDN博客
驱动安装完毕后,应用程序就可以按照驱动提供的操作函数去操作硬件了。
接下来针对过程中的一些细节展开
设备号
设备号分为主设备号和次设备号,高12位为主设备号,低20位为次设备号。通过register_chrdev_region和alloc_chrdev_region实现动态分配。
硬件地址
芯片本身是有各个外设的物理地址的,直接按物理地址操作外设是可行的,但不推荐。理由是一样的,直接对外设地址操作不符合软件分层的理念。一般能跑Linux的芯片都带有MMU,支持虚拟内存映射,可以将物理地址转换为虚拟地址,通过ioremap实现。这个虚拟地址也不建议直接操作,Linux提供了函数可用。
读函数
写函数