在Linux系统中,设备的类型非常多。比如:字符设备,块设备,网络设备接口设备,PCI设备,USB设备,平台设备,混杂设备。设备类型不同,对应的驱动模型也不同。Linux下开发设备驱动程序要遵循内核模块的编写规范,在编写字符设备驱动程序时,有一个统一的框架,也就是字符设备驱动模型。下面我们来看下整个字符设备驱动模型是怎样的。
从思维导图中我们可以看到整个设备驱动模型分为了三个部分,分别是驱动初始化,实现设备操作,还有驱动注销。下面重点分别介绍每个部分的具体实现。
Part1:驱动初始化
- 分配设备描述结构
- 初始化设备描述结构
- 注册设备描述结构
- 硬件初始化
1.分配设备描述结构
在任何一种驱动模型中,设备都会通过内核的一种结构来描述,通过source in sight阅读内核源码我们可以看到这个字符描述设备结构体的定义如下
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;//设备操作集
struct list_head list;
dev_t dev;//设备号
unsigned int count;//设备数量
};
我们重点关注其中的三个变量。
1.第一个是count,这个变量代表了有多少个相同的设备,比如开发板上可能对应有多个串口,这个count值就代表了串口的数量。
2.第二个是dev,这个变量代表了这个设备的设备号,通过dev_t来定义,dev_t实际上是unsigned int无符号的32位整形内核规定,每个设备都要对应一个设备号,设备号又分为主设备号和次设备号。不同设备对应的主设备号是不相同的,多个同种设备是通过次设备号来区分开的。也就是说我们定义的这样一个cdev,是可以同时支持多个同种设备的,我们通过设置不同次设备号来将它们区分开。在linux终端下输入ls -l /dev 来查看当前接入系统的设备。设备在linux文件系统中都是以设备文件来表示的。一个设备文件即代表了一个设备。
矩形框里的数值代表主设备号,椭圆内的数值代表次设备号。我们可以看到这是几个同类型的设备,所以它们的主设备号都是1,而次设备号是不同的。字符设备文件和字符设备驱动程序是通过主设备号来建立起联系的。一个设备号由高12位的主设备号和低20位的次设备号组成。内核中通过几个宏拓展的实现来完成设备号的分解和合成,代码写的非常有意思,我们可以来看一下。
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define