一、相关知识点(涉及接口、结构体、调用关系等)
一个软件系统可以分为以下四层:应用程序、库、内核、驱动,借用韦老师一副图,如下图:
即:应用层开发的功能,各个接口会通过库、通过内核调用到底层驱动程序的对应接口,从而执行对应的功能。
1.1 裸机开发步骤与驱动开发过程对比
1.1.1 裸机开发步骤
裸机开发时,我们需要按照如下过程进行开发。
步骤:
1、看电路图,查清楚led在哪个管脚,对应哪个GPIO口
2、看芯片手册,查看需要配置的相关寄存器地址——(主要查看需要配置哪些寄存器可以将GPIO配置成输出模式,地址可以在map地址映射章节找到)
3、看芯片手册,查清楚怎么样能将GPIO配置成输出高电平或低电平。
4、编译代码,烧写到板卡中,重新上电
1.1.2 Linux系统下LED驱动开发步骤
备注:裸机开发与带操作系统的驱动开发对寄存器的操作不同,裸机开发直接操作寄存器地址即可,而带linux操作系统时,必须要进行地址映射(有相关接口可以直接使用),映射完成后,再进行操作即可。
步骤:
1、撰写基本字符驱动框架代码(字符驱动代码框架实现步骤详见第2节:基本驱动框架实现流程)
2、看电路图,查清楚led在哪个管脚,对应哪个GPIO口
3、看芯片手册,查看需要配置的相关寄存器地址——(主要查看需要配置哪些寄存器可以将GPIO配置成输出模式,地址可以在map地址映射章节找到)
4、在驱动入口函数与出口函数中完成寄存器地址映射与取消映射
5、将led操作功能代码填充到驱动框架中
1.2 预备知识:写驱动时涉及的接口、结构体、宏等
name 含义
struct file_operations 结构体:该结构体中告诉每一个接口成员指向某个函数指针,指向的函数指针则为要实现的功能
struct class 类
module_init(lxxx_init); 注册驱动入口接口
module_exit(xxx_exit); 注册驱动出口接口
MODULE_LICENSE(“Dual BSD/GPL”); 声明
xxx_init() 驱动入口函数
xxx_exit() 驱动出口函数
register_chrdev() 注册模块,告诉内核让内核知道有这个模块,并且返回值为自动分配的主设备号
unregister_chrdev() 卸载驱动程序,告诉内核
class_create() 创建一个类
device_create(major, …); 创建一个设备,使用该接口创建设备以后,在板卡中insmod xxx之后,会自动生成/dev/设备,并且会将major主设备号分配给该设备
device_destroy() 删除设备,删除设备号
class_destroy 删除类
ioremap(); 映射寄存器地址
iounmap(); 删除映射关系
二、基本驱动框架
本小结,作为预备知识,先对字符驱动基本框架做一个简要描述,具体从两个方面进行描述,分别是:
2.1 基本驱动框架实现流程(思路)
2.2 基本驱动框架结构
2.1 基本驱动框架实现流程(思路)
撰写驱动框架时,按照以下驱动思路来写即可,后续代码实现,即可对比参照此流程来分析代码。
步骤如下:
1、实现结构体:【xxx_fops】。该结构体内部制定了相关的功能代码接口,从而应用能够知道哪个对应功能在哪个接口中实现。
2、实现步骤1中的相关函数(读写函数)框架,此时不需要实现函数具体细节,函数内部为空即可。
3、驱动入口函数:【xxx_init()】
xxx_init() {
(a)在函数前面定义全局变量【int major】,下一步用该变量接收系统自动分配的主设备号。
(b)驱动入口函数中实现注册函数,返回值为系统自动分配的主设备号:major = register_chrdev(0,设备名称,结构体);
}
4、驱动入口修饰函数:【modules_init();】
5、驱动出口函数:【xxx_exit();】
xxx_exit() {
(a)驱动出口函数中实现卸载函数:【unregister_chrdev(主设备号,设备名称)】
}
6、驱动出口修饰函数:【modules_exit();】
7、给sysfs文件系统提供更多驱动信息,这样上层应用因为【udev机制】便可自动创建设备节点,跟底层驱动代码中自动生成的主设备号保持一致,从而关联起来。(备注:udev机制个mdev机制基本可以认为是同一个东西,mdev是udev的简化版本)
(a)定义结构体class:【static struct class *xxx_class;】
(b)驱动入口函数xxx_init()中增加两个接口:【创建类class_create()】和【创建设备device_create()】
(c&#x