在内核源码中,涉及设备号分配和管理的函数有两个
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 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 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 插入链表。否则返回失败。 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插入到 */ cd=_register_chrdev_region(0,baseminor,count,name); if(IS_ERR(cd)) return PTR_ERR(cd); *dev=MKDEV(cd->major,cd->baseminor); return 0; } 现在以一个具体事例来说明
2.alloc_chrdev_region函数
该函数由系统协助分配设备号,分配的主设备号在1-254间
chrdevs[i]中。
设备号做为一种系统资源,当所对应的设备驱动程序被卸载时,很显然要把其所占用的设备号归还给系统,以便分配给其他内核模块使用。
其释放函数为
void unregister_chrdev_region(dev_t from , unsigned count);