今天我们通过内核中的源码共同分析一下Linux设备驱动中申请设备号的过程,首先在Linux内核为我们提供了两种申请设备号的方式,一是指定设备号注册,二是动态分配设备号;分别用到一下两个函数:
l register_chrdev_region();
l alloc_chrdev_region();
设备号的数据类型是dev_t类型,是一个无符号长整型,在32位操作系统中,它的大小是4个字节,32位,高12位用来存放主设备号,低20位用来存放次设备号。
通过主设备号和次设备号合成设备号的宏如下:
MKDEV(major,minor);
从设备号中提取主设备号和次设备号的宏如下:
MAJOR(dev);
MINOR(dev);
接下来我们就来分析设备号注册过程:
一、自动分配设备号:
1. 调用内核提供的自动分配设备号函数,分配设备号 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name) 2. alloc_chrdev_region函数中继续调用如下函数,进行进一步申请 struct char_device_struct *cd = __register_chrdev_region(0, baseminor, count, name); /*在该函数中进行判断,如果major==0,执行if内的语句*/ if (major == 0) { //自动分配时major == 0 /* *chrdevs是一个结构体指针数组,见附录1-1 *作用是从struct char_device_struct类型的结构体指针数组中找到一个空的结构体指针 */ for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) { // for(i=254;i>0;i--) if (chrdevs[i] == NULL) break; } if (i == 0) { //i == 0 ,说明,存放设备号的结构体指针数组已经用完 ret = -EBUSY; goto out; } major = i; //否则,主设备号 = i ret = major; cd->major = major; // 将主设备号赋值给cd->major cd->baseminor = baseminor; // 将起始次设备号赋值cd->baseminor cd->minorct = minorct; // 将传入的第三个参数赋值给程cd->minorct, // 表示申请设备号的个数 strlcpy(cd->name, name, sizeof(cd->name)); //设备名拷贝 /* *struct char_device_struct *cd, **cp; *cp存放的是结构体指针的地址,基于当前的条件,*cp == NULL ,所以for循环条件不成立 */ --------------------------------------不执行------------------------------------- for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major > major ||((*cp)->major == major &&(((*cp)->baseminor >= baseminor) ||((*cp)->baseminor + (*cp)->minorct > baseminor)))) break; --------------------------------------------------------------------------------------- /* **cp == NULL ,所以条件不成立*/ ---------------------------------------不执行----------------------------------------- if (*cp && (*cp)->major == major) { int old_min = (*cp)->baseminor; int old_max = (*cp)->baseminor + (*cp)->minorct - 1; int new_min = baseminor; int new_max = baseminor + minorct - 1; /* New driver overlaps from the left. */ if (new_max >= old_min && new_max <= old_max) { ret = -EBUSY; goto out; } /* New driver overlaps from the right. */ if (new_min <= old_max && new_min >= old_min) { ret = -EBUSY; goto out; } } --------------------------------------------------------------------------------------- cd->next = *cp; //cd ->next = NULL *cp = cd; //*cp[chrdevs[i]] , cd 结构体在上面赋值的 mutex_unlock(&chrdevs_lock); return cd; 回到alloc_chrdev_region函数的*dev = MKDEV(cd->major, cd->baseminor); 实际上就申请了一个主设备号,次设备号 二、用户指定设备号注册(eg:major = 250;count=3) 1. int register_chrdev_region(dev_t from, unsigned count, const char *name); 2. 在该函数中进行如下操作: dev_t to = from + count; // dev_t to = MKDEV(dev_major,dev_minor) +3; dev_t n, next; /*如果申请的设备编号范围跨越了主设备号, *它会把分配范围内的编号按主设备号分割 *成较小的子范围,并在每个子范围上调用 *__register_chrdev_region() 。如果其中 *有一次分配失败的话,那会把之前成功分配的都全部退回*/ // for(n = MKDEV(dev_major,dev_minor); n<to;n=next)循环一次(next = to) for (n = from; n < to; n = next) { next = MKDEV(MAJOR(n)+1, 0); // next = MKDEV(250+1,0); if (next > to) // if(MKDEV(251,0)>MKDEV(250,0)+3) [成立] next = to; // next = MKDEV(250,0)+3 cd = __register_chrdev_region(MAJOR(n), MINOR(n),next - n, name); //__register_chrdev_region(250, 0,3, "xxx_demo"); { cd->major = major; //cd->major = 250; cd->baseminor = baseminor; //cd->baseminor = 0; cd->minorct = minorct; //cd->minorct = 3; strlcpy(cd->name, name, sizeof(cd->name)); i = major_to_index(major); //i = 250; /*for (cp = &chrdevs[250]; *cp; cp = &(*cp)->next)*/ for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if((*cp)->major > major || ((*cp)->major == major &&(((*cp)->baseminor >= baseminor) ||((*cp)->baseminor + (*cp)->minorct > baseminor)))) //正常情况下不会成立,即次设备号范围不会重复 break; /*判断次设备号范围是否重复,如果重复就错误返回*/ if (*cp && (*cp)->major == major) { int old_min = (*cp)->baseminor; int old_max = (*cp)->baseminor + (*cp)->minorct - 1; int new_min = baseminor; int new_max = baseminor + minorct - 1; /* New driver overlaps from the left. */ if (new_max >= old_min && new_max <= old_max) { ret = -EBUSY; goto out; } /* New driver overlaps from the right. */ if (new_min <= old_max && new_min >= old_min) { ret = -EBUSY; goto out; } } /*将设备号的结构体插入到散列表中*/ cd->next = *cp *cp = cd; } 附录1-1: #define CHRDEV_MAJOR_HASH_SIZE 255 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]; |
附录1-2
热点新闻
讲师博文
今天我们通过内核中的源码共同分析一下Linux设备驱动中申请设备号的过程,首先在Linux内核为我们提供了两种申请设备号的方式,一是指定设备号注册,二是动态分配设备号;分别用到一下两个函数: l register_chrdev_r...
今天我们通过内核中的源码共同分析一下Linux设备驱动中申请设备号的过程,首先在Linux内核为我们提供了两种申请设备号的方式,一是指定设备号注册,二是动态分配设备号;分别用到一下两个函数:
l register_chrdev_region();
l alloc_chrdev_region();
设备号的数据类型是dev_t类型,是一个无符号长整型,在32位操作系统中,它的大小是4个字节,32位,高12位用来存放主设备号,低20位用来存放次设备号。
通过主设备号和次设备号合成设备号的宏如下:
MKDEV(major,minor);
从设备号中提取主设备号和次设备号的宏如下:
MAJOR(dev);
MINOR(dev);
接下来我们就来分析设备号注册过程:
一、自动分配设备号:
1. 调用内核提供的自动分配设备号函数,分配设备号 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name) 2. alloc_chrdev_region函数中继续调用如下函数,进行进一步申请 struct char_device_struct *cd = __register_chrdev_region(0, baseminor, count, name); /*在该函数中进行判断,如果major==0,执行if内的语句*/ if (major == 0) { //自动分配时major == 0 /* *chrdevs是一个结构体指针数组,见附录1-1 *作用是从struct char_device_struct类型的结构体指针数组中找到一个空的结构体指针 */ for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) { // for(i=254;i>0;i--) if (chrdevs[i] == NULL) break; } if (i == 0) { //i == 0 ,说明,存放设备号的结构体指针数组已经用完 ret = -EBUSY; goto out; } major = i; //否则,主设备号 = i ret = major; cd->major = major; // 将主设备号赋值给cd->major cd->baseminor = baseminor; // 将起始次设备号赋值cd->baseminor cd->minorct = minorct; // 将传入的第三个参数赋值给程cd->minorct, // 表示申请设备号的个数 strlcpy(cd->name, name, sizeof(cd->name)); //设备名拷贝 /* *struct char_device_struct *cd, **cp; *cp存放的是结构体指针的地址,基于当前的条件,*cp == NULL ,所以for循环条件不成立 */ --------------------------------------不执行------------------------------------- for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major > major ||((*cp)->major == major &&(((*cp)->baseminor >= baseminor) ||((*cp)->baseminor + (*cp)->minorct > baseminor)))) break; --------------------------------------------------------------------------------------- /* **cp == NULL ,所以条件不成立*/ ---------------------------------------不执行----------------------------------------- if (*cp && (*cp)->major == major) { int old_min = (*cp)->baseminor; int old_max = (*cp)->baseminor + (*cp)->minorct - 1; int new_min = baseminor; int new_max = baseminor + minorct - 1; /* New driver overlaps from the left. */ if (new_max >= old_min && new_max <= old_max) { ret = -EBUSY; goto out; } /* New driver overlaps from the right. */ if (new_min <= old_max && new_min >= old_min) { ret = -EBUSY; goto out; } } --------------------------------------------------------------------------------------- cd->next = *cp; //cd ->next = NULL *cp = cd; //*cp[chrdevs[i]] , cd 结构体在上面赋值的 mutex_unlock(&chrdevs_lock); return cd; 回到alloc_chrdev_region函数的*dev = MKDEV(cd->major, cd->baseminor); 实际上就申请了一个主设备号,次设备号 二、用户指定设备号注册(eg:major = 250;count=3) 1. int register_chrdev_region(dev_t from, unsigned count, const char *name); 2. 在该函数中进行如下操作: dev_t to = from + count; // dev_t to = MKDEV(dev_major,dev_minor) +3; dev_t n, next; /*如果申请的设备编号范围跨越了主设备号, *它会把分配范围内的编号按主设备号分割 *成较小的子范围,并在每个子范围上调用 *__register_chrdev_region() 。如果其中 *有一次分配失败的话,那会把之前成功分配的都全部退回*/ // for(n = MKDEV(dev_major,dev_minor); n<to;n=next)循环一次(next = to) for (n = from; n < to; n = next) { next = MKDEV(MAJOR(n)+1, 0); // next = MKDEV(250+1,0); if (next > to) // if(MKDEV(251,0)>MKDEV(250,0)+3) [成立] next = to; // next = MKDEV(250,0)+3 cd = __register_chrdev_region(MAJOR(n), MINOR(n),next - n, name); //__register_chrdev_region(250, 0,3, "xxx_demo"); { cd->major = major; //cd->major = 250; cd->baseminor = baseminor; //cd->baseminor = 0; cd->minorct = minorct; //cd->minorct = 3; strlcpy(cd->name, name, sizeof(cd->name)); i = major_to_index(major); //i = 250; /*for (cp = &chrdevs[250]; *cp; cp = &(*cp)->next)*/ for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if((*cp)->major > major || ((*cp)->major == major &&(((*cp)->baseminor >= baseminor) ||((*cp)->baseminor + (*cp)->minorct > baseminor)))) //正常情况下不会成立,即次设备号范围不会重复 break; /*判断次设备号范围是否重复,如果重复就错误返回*/ if (*cp && (*cp)->major == major) { int old_min = (*cp)->baseminor; int old_max = (*cp)->baseminor + (*cp)->minorct - 1; int new_min = baseminor; int new_max = baseminor + minorct - 1; /* New driver overlaps from the left. */ if (new_max >= old_min && new_max <= old_max) { ret = -EBUSY; goto out; } /* New driver overlaps from the right. */ if (new_min <= old_max && new_min >= old_min) { ret = -EBUSY; goto out; } } /*将设备号的结构体插入到散列表中*/ cd->next = *cp *cp = cd; } 附录1-1: #define CHRDEV_MAJOR_HASH_SIZE 255 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]; |
附录1-2
相关推荐
全国咨询热线:400-611-6270
?2004-2018华清远见教育科技集团 版权所有 京ICP备16055225号 京公海网安备11010802025203号