转载自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6
在Linux设备中,每个设备都有一个设备号。每个设备号都由主设备号(major number)和次设备号(minor number)两部分组成,主设备号用来表示某一个具体的驱动,次设备号用来表示用同一驱动的各个设备。每个设备在/dev目录下都有一个对应的文件(节点)。可以通过以下命令查看当前已经加载的设备驱动程序的主设备号。
cat /proc/devices
Linux提供了一个名为dev_t的数据类型表示设备号。在内核文件include/linux/types.h里面有定义:
/***** include/linux/types.h *****/
typedef __u32 __kernel_dev_t;
......
typedef __kernel_dev_t dev_t;
而u32是unsigned int类型,是一个32位的数据类型。也就是说dev_t是一个32位的数据类型。这32位的数据构成了主设备号和次设备号两个部分,其中高12位为主设备号,低20位为次设备号。因此 Linux 系统中主设备号范围为 0-4095,次设备号范围为0-1048576,选择设备号是不能超过这个范围的。在头件include/linux/kdev_t.h 中提供了关于设备号的操作函数,如下所示:
/***** include/linux/kdev_t.h *****/
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
宏MINORBITS表示次设备号位数,一共是20位。
宏MINORMASK表示次设备号掩码。
宏MAJOR(dev)用于从dev_t中获取主设备号,将dev_t右移20位即可。
宏MINOR(dev)用于从dev_t中获取次设备号,取dev_t的低 20 位的值即可。
宏MKDEV(ma,mi)用于将给定的主设备号和次设备号的值组合成 dev_t 类型的设备号。
这里讲的设备号分配主要是主设备号的分配。在注册字符设备的时候需要给设备指定一个设备号,这个设备号可以是驱动开发者静态的指定一个设备号,比如选择 200 这个主设备号。有一些常用的设备号已经被 Linux 内核开发者给分配掉了,具体分配的内容可以查看文档 Documentation/devices.txt。并不是说内核开发者已经分配掉 的主设备号我们就不能用了,具体能不能用还得看我们的硬件平台运行过程中有没有使用这个主设备号,使用“cat /proc/devices”命令即可查看当前系统中所有已经使用了的设备号。
1.1注册设备号函数
如果给定了设备的主设备号和次设备号就使用如下所示函数来注册设备号:
/*
* from:需要注册的设备号
* count:需要申请的设备号数量
* name:设备名字
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);
静态分配设备号需要我们检查当前系统中所有被使用了的设备号,然后挑选一个没有使用的。而且静态分配设备号很容易带来冲突问题,Linux 社区推荐使用动态分配设备号,在注册字符设备之前先申请一个设备号,系统会自动给你一个没有被使用的设备号,这样就避免了冲突。卸载驱动的时候释放掉这个设备号即可。
2.1申请设备号函数
如果没有指定设备号的话就使用如下函数来申请设备号:
/*
* dev:向系统申请的设备号存放的地址
* baseminor:次设备号起始地址
* alloc_chrdev_region可以申请一段连续的多个设备号,这些设备号的主设备号一样,
* 但是次设备号不同,次设备号以 baseminor 为起始地址开始递增。
* 一般 baseminor 为 0,也就是说次设备号从 0 开始。
* count:需要申请的设备号数量
* name:设备名字
*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
注销字符设备之后要释放掉设备号。不管是通过 alloc_chrdev_region 函数还是 register_chrdev_region 函数申请的设备号,统一使用如下释放函数:
/*
* from:需要释放掉的设备号
* count:需要释放掉的设备号数量
*/
void unregister_chrdev_region(dev_t from, unsigned count);
unsigned int major; /* 主设备号 */
unsigned int minor; /* 次设备号 */
dev_t devid; /* 设备号 */
/* 申请设备号 */
if (major) /* 如果给定了主设备号,则直接注册即可 */
{
devid = MKDEV(major, minor); /* 大部分驱动次设备号都选择 0 */
register_chrdev_region(devid, 1, "dev_name");
}
else /* 向linux系统申请设备号 */
{
alloc_chrdev_region(&devid, 0, 1, "dev_name");
major = MAJOR(devid); /* 获取系统分配的设备号的主设备号 */
minor = MINOR(devid); /* 获取系统分配的设备号的次设备号 */
}
/* 注销设备号 */
unregister_chrdev_region(devid, 1);