linux驱动开发 主设备号与次设备号

一个字符设备或者块设备都有一个主设备号和次设备号。主设备号和次设备号统称为设
备号。主设备号用来表示一个特定的驱动程序。次设备号用来表示使用该驱动程序的各
设备。例如一个嵌入式系统,有两个LED指示灯,LED灯需要独立的打开或者关闭。那么
,可以写一个LED灯的字符设备驱动程序,可以将其主设备号注册成5号设备,次设备号
分别为1和2。这里,次设备号就分别表示两个LED灯。
1.主设备号和次设备号的表示
在Linux内核中,dev_t类型用来表示设备号。在Linux 2.6.29.4中,dev_t定义为一个
无符号长整型变量,如下:typedef u_long dev_t;
u_long在32位机中是4个字节,在64位机中是8字节。以32位机为例,其中高12表示主设备号,低20为表示次设备号,如图6.1所示。
2.主设备号和次设备号的获取
为了写出可移植的驱动程序,不能假定主设备号和次设备号的位数。不同的机型中,主
设备号和次设备号的位数可能是不同的。应该使用MAJOR宏得到主设备号,使用MINOR宏来得到次设备号。下面是两个宏的定义:
#define MINORBITS   20                                  /*次设备号位数*/
#define MINORMASK   ((1U << MINORBITS) - 1)             /*次设备号掩码*/
#define MAJOR(dev)  ((unsigned int) ((dev) >> MINORBITS))
/*dev右移20位得到主设备号*/ 
#define MINOR(dev)((unsigned int) ((dev) & MINORMASK))
   /*与次设备掩码与,得到次设备号*/
MAJOR宏将dev_t向右移动20位,得到主设备号;MINOR宏将dev_t的高12位清零,得到次
设备号。相反,可以将主设备号和次设备号转换为设备号类型(dev_t),使用宏
MKDEV可以完成这个功能。#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))  
MKDEV宏将主设备号(ma)左移20位,然后与次设备号(mi)相与,得到设备号。
3.静态分配设备号
静态分配设备号,就是驱动程序开发者,静态地指定一个设备号。对于一部分常用的设
备,内核开发者已经为其分配了设备号。这些设备号可以在内核源码documentation/
devices.txt文件中找到。如果只有开发者自己使用这些设备驱动程序,那么其可以选
择一个尚未使用的设备号。在不添加新硬件的时候,这种方式不会产生设备号冲突。但
是当添加新硬件时,则很可能造成设备号冲突,影响设备的使用。
4.动态分配设备号
由于静态分配设备号存在冲突的问题,所以内核社区建议开发者使用动态分配设备号的
方法。动态分配设备号的函数是alloc_chrdev_region()。
5.查看设备号
当静态分配设备号时,需要查看系统中已经存在的设备号,从而决定使用哪个新设备号
。可以读取/proc/devices文件获得设备的设备号。/proc/devices文件包含字符设备和
块设备的设备号,如下所示。[root@tom /]# cat /proc/devices /*cat命令查看
/proc/devices文件的内容*/  Character devices:                  /*字符设备*/
1 mem    4 /dev/vc/0    7 vcs    13 input    14 sound    21 sg  Block
devices:  /*块设备*/    1 ramdisk    2 fd    8 sd    253  device-mapper    254 mdp   
3. 6.1.3 申请和释放设备号
 内核维护着一个特殊的数据结构,用来存放设备号与设备的关系。在安装设备时,应该
给设备申请一个设备号,使系统可以明确设备对应的设备号。设备驱动程序中的很多功
能,是通过设备号来操作设备的。下面,首先对申请设备号进行简述。
1.申请设备号
在构建字符设备之前,首先要向系统申请一个或者多个设备号。完成该工作的函数是
register_chrdev_region(),该函数在<fs/char_dev.c>中定义:
int register_chrdev_region(dev_t from, unsigned count, const char *name);
其中,from是要分配的设备号范围的起始值。一般只提供from的主设备号,from的次设
备号通常被设置成0。count是需要申请的连续设备号的个数。最后name是和该范围编号
关联的设备名称,该名称不能超过64字节。
和大多数内核函数一样,register_chrdev_region()函数成功时返回0。错误时,返回
一个负的错误码,并且不能为字符设备分配设备号。下面是一个例子代码,其申请了
CS5535_GPIO_COUNT个设备号。retval = register_chrdev_region(dev_id,
CS5535_GPIO_COUNT,NAME);
在Linux中有非常多的字符设备,在人为的为字符设备分配设备号时,很可能发生冲突
。Linux内核开发者一直在努力将设备号变为动态的。可以使用
alloc_chrdev_region()函数达到这个目的。
int alloc_chrdev_region(dev_t *dev, unsigned baseminor,unsigned count, const char *name)
在上面的函数中,dev作为输出参数,在函数成功返回后将保存已经分配的设备号。函
数有可能申请一段连续的设备号,这是dev返回第一个设备号。baseminor表示要申请的
第一个次设备号,其通常设为0。count和name与register_chrdev_region()函数的对应
参数一样。count表示要申请的连续设备号个数,name表示设备的名字。下面是一个例
子代码,其申请了CS5535_GPIO_COUNT个设备号。retval =
alloc_chrdev_region(&dev_id, 0, CS5535_GPIO_COUNT, NAME);
2.释放设备号
使用上面两种方式申请的设备号,都应该在不使用设备时,释放设备号。设备号的释放
统一使用下面的函数:void unregister_chrdev_region(dev_t from, unsignedcount);
在上面这个函数中,from表示要释放的设备号,count表示从from开始要释放的设备号

个数。通常,在模块的卸载函数中调用unregister_chrdev_region()函数。

你可能感兴趣的:(linux驱动开发 主设备号与次设备号)