编写步骤
- 确定主设备号,也可以设为0让内核自动分配
- 定义 file_operations 结构体
- 实现 file_operations 结构体中的 open、read、write等函数
- register_chrdev 注册字符设备驱动
- 完成入口函数、出口函数(安装驱动时调用入口函数、卸载驱动时调用出口函数)
- 自动创建设备节点 class_create、device_create
注意:
-
驱动程序操作硬件:
ioremap 映射寄存器的物理地址得到虚拟地址
-
驱动程序与应用程序之间传递信息:
copy_to_user、copy_from_user
class_create、device_create
内核中定义有 struct class 结构体,顾名思义,一个 struct class 结构体对应一个类,内核同时提供了 class_create 函数,用它创建 class 类放在 /sys/class 目录下,一旦创建好这个类,再调用 device_create 就可以在 /dev 目录下创建设备节点。
eg:
hello_class = class_create(THIS_MODULE, "hello_class");
device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello");
上述代码的 class_create 会在 /sys/class 下创建 hello_class 目录,这个目录下有一个链接文件 hello,在它所指向的文件中包含着更多的设备信息(比如:dev文件中有主设备号、次设备号信息)
/sys/devices:系统中所有设备的存放目录
/sys/class:系统中的所有设备按照其功能放置的目录结构,同时该目录下的文件也链接到了 /sys/devices 目录 (eg:/sys/class/leds 存放所有的led设备、/sys/class/input 存放所有的输入类设备)
驱动设计思想
面向对象
字符设备驱动程序抽象出一个 file_operations 结构体;
分层
最简单的驱动程序:以操作一盏 led 灯为例(./ledtest /dev/myled on ./ledtest /dev/myled off)
在驱动程序中只需要:(1)在open函数中初始化 led
(2)在write函数中根据传进来的值控制 led
这两步都是直接对相应的寄存器进行操作
缺点:将驱动程序和硬件资源绑定死,更换硬件资源时需要重新编写驱动程序
分层思想:上下分层,将 led 驱动程序分为两层,将驱动程序和硬件相分离
leddrv.c 中并没有具体的对于寄存器的操作,只负责注册 file_operations 结构体,它的 open/write 成员会调用 board_X.c 中提供的硬件 led_opr 中的对应函数
board_X.c 中实现对硬件的具体操作,构造各自的 led_operations 硬件操作结构体