1.设备号的分配和管理
在内核源码中,涉及设备号分配和管理的函数有两个
1.register_chrdev_region函数
将当前设备驱动程序要使用的设备号记录到chrdevs数组中,前提是驱动程序已经知道要分配的设备号是多少啦
int register_chrdev_region(dev_t from,unsigned int count,char*name) //from为第一个设备编号,count为连续的设备个数,即是驱动程序所管理的设备个数,name为驱动程序名
{
//全局性指针数组,是内核用于设备号分配和管理的核心要素
/*
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct cdev *cdev; //*will die*/
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
*/
//其初始化状态为
struct char_device_struct *cd;
dev_t to=from+count; //末设备号
dev_t n,next;
from(n=from;n<to;n=next) {
next=MKDEV(MAJOR(n)+1,0); //下一个主设备号的首个设备
if(next>to)
next=to;
cd=_register_chrdev_region(MAJOR(n),MINOR(n),next-n,name); //最终要调用此函数实现分配
if(IS_ERR(cd))
goto fail;
}
return 0;
fail:
to=n;
for(n=from;n<to;n=next) {
next=MKDEV(MAJOR(n)+1,0);
kfree(_unregister_chrdev_region(MAJOR(n),MINOR(n),next-n));
}
return PTR_ERR(cd);
}
static struct char_device_struct *
_register_chrdev_region(unsigned int major,unsigned int baseminor,int minorct,const char *name)
{
cd=kzalloc(sizeof(struct char_device_struct),GFP_KERNEL);
...
cd->major=major;
cd->baseminor=baseminor;
cd->minorct=minorct;
strlcpy(cd->name,name,sizeof(cd->name));
//分配一个struct char_device_struct对其初始化
i=major_to_index(major); //开始搜索chrdev数组,以哈希表形式进行,为此必须获取一个散列关键值i=major%255.
此后函数将对chrdevs[i]元素管理的链表扫描
现在以一个具体事例来说明
现假设有一个设备驱动程序使用的主设备号是257,次设备号分别是0,1,2,3
其调用
int ret=register_chrdev_region(MKDEV(257,0),4,"demodev");
调用完毕其数组为
现在又有一个设备驱动程序使用的主设备号为2,次设备号为0,调用
int ret=register_chrdev_region(MKDEV(2,0),1,"augdev");
2%255=2;
有趣的是另一个设备驱动程序调用register_chrdev_region向系统注册,主设备号也为257,只要其次设备号所在范围[baseminor,baseminor+minrct]不与demodev的次设备号重叠则也会分配成功,系统依然会生成一个新的struct char_device_struct 插入链表。否则返回失败。
2.alloc_chrdev_region函数
该函数由系统协助分配设备号,分配的主设备号在1-254间
int alloc_chrdev_region(dev_t *dev,unisgned baseminor,unsigned count,const char*name) //dev仅用于输出的参数,在成功完成调用后将保存以分配范围的第一个编号。baseminor所请求的第一个编号,count是连续的分配个数,char为驱动程序名
{
struct char_device_struct *cd;
/*此函数核心部分依然是_register_chrdev_region,其首参为0,将调用如下逻辑
static struct char_device_struct *
_register_chrdev_region(unsigned int major,unsigned int baseminor,int minorct,const char *name)
{
...
if(major==0) {
for(i=ARRAY_SIZE(chrdevs-1;i>0;i--){
if(chrdevs[i]==NULL
break;
}
if(i==0) {
ret=-EBUSY;
goto out;
}
major=i;
ret=major;
}
...
}*/
/*上述代码片段的实现原理很简单,在for循环中从chrdevs数组的末项(254)前向扫描,如发现该数组中某项,如i项对应为NULL,那就把该项对应的索引值i作为分配的主设备返回给驱动程序,同时生成一个struct char_device_struct插入到
chrdevs[i]中。
*/
cd=_register_chrdev_region(0,baseminor,count,name);
if(IS_ERR(cd))
return PTR_ERR(cd);
*dev=MKDEV(cd->major,cd->baseminor);
return 0;
}