Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。主设备号用来区分不同类型的设备,而次设备号用来区分同一类型的多个设备。
1 设备号的组成
一个字符设备或者块设备都有一个主设备号和次设备号。主设备号和次设备号统称为设备号。主设备号用来表示一个特定的驱动程序。次设备号用来表示使用该驱动程序的各个设备。Linux提供了一个名为dev_t的数据类型表示设备号,dev_t定义在文件include/linux/types.h 里面,定义如下:
typedef __u32 __kernel_dev_t;
typedef __kernel_dev_t dev_t;
dev_t是个32位的变量,其中12位用来表示主设备号,20位用来表示次设备号。因此Linux系统中主设备号范围为0~4095,所以大家在选择主设备号的时候一定不要超过这个范围。在文件include/linux/kdev_t.h中提供了几个操作设备号的宏定义,如下所示:
6 #define MINORBITS 20
7 #define MINORMASK ((1U << MINORBITS) - 1)
8
9 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
10 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
11 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
第 6 行,宏MINORBITS 表示次设备号位数,一共20位。
第 7 行,宏MINORMASK表示次设备号掩码。
第 9 行,宏MAJOR用于从dev_t中获取主设备号,将dev_t右移20位即可。
第 10 行,宏MINOR用于从dev_t中获取次设备号,取dev_t的低20位的值即可。
第 11 行,宏MKDEV用于将给定的主设备号和次设备号的值组合成dev_t类型的设备号。
2 设备号的分配
1、静态分配设备号
静态分配设备号,就是驱动程序开发者通过静态指定一个设备号。对于一部分常用的设备,内核开发者已经为其分配了设备号。这些设备号可以在内核源码documentation/ devices.txt文件中找到。如果只有开发者自己使用这些设备驱动程序,那么其可以选择一个尚未使用的设备号。在不添加新硬件的时候,这种方式不会产生设备号冲突。但是当添加新硬件时,则很可能造成设备号冲突,影响设备的使用。使用“cat /proc/devices”命令即可查看当前系统中所有已经使用了的主设备号,如图 2.1所示:
上图中第一列就是每种设备的主设备号(我这里直接截图了其中的一部分,大家有兴趣,可以在开发板的调试串口输入相应的命令查看所有设备的设备号)。
2、动态分配设备号
由于静态分配设备号可能存在冲突问题,因此建议使用动态分配设备号。在注册字符设备之前先申请一个设备号,系统会自动给你一个没有被使用的设备号,这样就避免了冲突。卸载驱动的时候释放掉这个设备号即可,设备号的申请函数如下:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
dev:保存申请到的设备号。
baseminor:次设备号起始地址,alloc_chrdev_region 可以申请一段连续的多个设备号,这些设备号的主设备号一样,但是次设备号不同,次设备号以baseminor为起始地址地址开始递增。一般baseminor为0,也就是说次设备号从0开始。
count:要申请的设备号数量。
name:设备名字。
注销字符设备之后要释放掉设备号,设备号释放函数如下:
void unregister_chrdev_region(dev_t from, unsigned count)
from:要释放的设备号。
count:表示从from开始,要释放的设备号数量。