目录
简介:
Linux内核主要包括三种驱动模型,字符设备驱动、块设备驱动以及网络设备驱动。本文主要介绍字符设备驱动的框架和机制。
一、字符设备驱动框架
Linux底层驱动一般都从入口函数开始分析。
1、字符设备驱动入口
驱动首先实现的就是加载和卸载函数,也是驱动程序的入口函数。
static int __init xxx_init(void) { } static void __exit xxx_exit(void) { } module_init(xxx_init); module_exit(xxx_exit);
这段代码实现了通用驱动的 module_init 加载与 module_exit 卸载框架。
2、字符设备驱动加载过程
- 字符设备驱动加载过程
- 字符设备驱动卸载过程
2.1 申请设备号
每一类字符设备都有唯一的设备号,其中设备号又分为主设备号和次设备号。
-
主设备号:用于标识设备的类型,如:区分是
IIC
设备还是SPI
设备; -
次设备号:用于区分同类型的不同设备,如:区分
IIC
设备下,具体哪一个设备,是MPU6050
还是EEPROM
;
2.1.1 分配设备号函数
设备号的分配方式有两种,一种是静态分配,另一种是动态分配。
(1) 静态分配函数
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:以from设备号开始,连续分配count个同类型的设备号
参数:
-
from:表示已知的一个设备号
-
count:表示连续设备编号的个数,(同类型的设备有多少个)
-
name:表示设备或者驱动的名称
(2) 动态分配函数
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
功能:从baseminor次设备号开始,连续分配count个同类型的设备号,并自动分配一个主设备号,将主、次组成的设备号信息赋值给*dev
参数:
-
dev:设备号的指针,用于存放分配的设备号,可使用MAJOR和MINOR宏,将主设备号和次设备号分别提取出来;
-
baseminor:次设备号从第几开始分配;
-
count:表示连续次设备号的个数,(同类型的设备有多少个);
-
name:表示设备或者驱动的名称;
两个函数的区别:
-
register_chrdev_region:已给定主设备号和次设备号,调用该函数将设备号注册到内核;
-
alloc_chrdev_region:未给定设备的主设备号和次设备号,调用后,主设备号以
0
来表示,以自动分配,并且将自动分配的设备号,同样加入到子系统中,方便系统追踪系统设备号的使用情况;
(3) 注销设备号
设备号作为一种系统资源,使用完后需要将占用的设备号归还给系统,调用 unregister_chrdev_region 注销
void unregister_chrdev_region(dev_t from, unsigned count)
功能:要注销from主设备号下的连续count个设备
参数:
-
from:表示已知的一个设备号
-
count:表示连续设备编号的个数,(同类型的设备有多少个)
2.1.2 设备号中的主/次设备号
设备号的类型是dev_t ,内核 include/linux/types.h 可以看到dev_t 表示u32类型的数值。
typedef u32 __kernel_dev_t;
typedef __kernel_dev_t dev_t;
dev_t 包括主设备号和次设备号,由 MINORBITS 宏定义指定分界线,主设备号占用高12bit
,次设备号占用低20bit。
头文件 include/linux/kdev_t.h 中定义
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) //从dev_t中获取主设备号
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) //从dev_t中获取次设备号
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) //主/次设备号组成dev_t