2.在 中的一套宏定义. 为获得一个 dev_t 的主或者次编号, 使用: 2.1设备编号的内部表示 MAJOR(dev_t dev); MINOR(dev_t dev); 2.在有主次编号时, 需要将其转换为一个 dev_t, 可使用: MKDEV(int major, int minor); 在linux/kdev_t.h中有下了内容 ... 4 #define MINORBITS 20 5 #define MINORMASK ((1U << MINORBITS) - 1) 6 7 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) 8 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) 9 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))//高12为表示主设备号,低20位表示次设备号 ... 3.分配和释放设备编号register_chrdev_region函数 下面摘自文件fs/char_dev.c内核源代码 184 /** 185 * register_chrdev_region() - register a range of device numbers 186 * @from: the first in the desired range of device numbers; must include 187 * the major number. 188 * @count: the number of consecutive device numbers required 189 * @name: the name of the device or driver. 190 * 191 * Return value is zero on success, a negative error code on failure. 192 */ 193 int register_chrdev_region(dev_t from, unsigned count, const char *name) 194 {
195 struct char_device_struct *cd; 196 dev_t to = from + count; //计算分配号范围中的最大值1280+400=1680 197 dev_t n, next; 198 199 for (n = from; n < to; n = next) {/*每次申请256个设备号*/ 200 next = MKDEV(MAJOR(n)+1, 0);/*主设备号加一得到的设备号,次设备号为0*/ 201 if (next > to) 202 next = to; 203 cd = __register_chrdev_region(MAJOR(n), MINOR(n), 204 next - n, name); 205 if (IS_ERR(cd)) 206 goto fail; 207 } 208 return 0; 209 fail:/*当一次分配失败的时候,释放所有已经分配到地设备号*/ 210 to = n; 211 for (n = from; n < to; n = next) { 212 next = MKDEV(MAJOR(n)+1, 0); 213 kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); 214 } 215 return PTR_ERR(cd); 216 } 这里, from是要分配的起始设备编号. from 的次编号部分常常是 0, 但是没有要求是那个效果. count是你请求的连续设备编号的总数. 注意, 如果count 太大, 要求的范围可能溢出到下一个次编号;但是只要要求的编号范围可用, 一切都仍然会正确工作. 最后, name 是应当连接到这个编号范围的设备的名子; 它会出现在 /proc/devices 和 sysfs 中.如同大部分内核函数, 如果分配成功进行, register_chrdev_region 的返回值是 0. 出错的情况下, 返回一个负的错误码, 不能存取请求的区域. 4.下面是char_device_struct结构体的信息 fs/char_dev.c static struct char_device_struct { struct char_device_struct *next; // 指向散列冲突链表中的下一个元素的指针 unsigned int major; // 主设备号 unsigned int baseminor; // 起始次设备号 int minorct; // 设备编号的范围大小 const char *name; // 处理该设备编号范围内的设备驱动的名称 struct file_operations *fops; // 没有使用 struct cdev *cdev; /* will die指向字符设备驱动程序描述符的指针*/ } *chrdevs[MAX_PROBE_HASH];
80 /* 81 * Register a single major with a specified minor range. 82 * 83 * If major == 0 this functions will dynamically allocate a major and return 84 * its number. 85 * 86 * If major > 0 this function will attempt to reserve the passed range of 87 * minors and will return zero on success. 88 * 89 * Returns a -ve errno on failure. 90 */ /** * 该函数主要是注册注册注册主设备号和次设备号 * major == 0此函数动态分配主设备号 * major > 0 则是申请分配指定的主设备号 * 返回0表示申请成功,返 回负数说明申请失败 */
91 static struct char_device_struct * 92 __register_chrdev_region(unsigned int major, unsigned int baseminor, 93 int minorct, const char *name) 94 {/*以下处理char_device_struct变量的初始化和注册*/ 95 struct char_device_struct *cd, **cp; 96 int ret = 0; 97 int i; 98 //kzalloc()分配内存并且全部初始化为0, 99 cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL); 100 if (cd == NULL) //ENOMEM定义在include/asm-generic/error-base.h中, //15 #define ENOMEM 12 /* Out of memory */
101 return ERR_PTR(-ENOMEM); 102 103 mutex_lock(&chrdevs_lock); 104 105 /* temporary */ 106 if (major == 0) {//下面动态申请主设备号 107 for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i—) { //ARRAY_SIZE是定义为ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) //#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 108if (chrdevs[i] == NULL)
//chrdevs是内核中已经注册了的设备好设备的一个数组 109 break; 110 } 111 112 if (i == 0) { 113 ret = -EBUSY; 114 goto out; 115 } 116 major = i; 117 ret = major;//这里得到一个位使用的设备号 118 } 119 //下面四句是对已经申请到的设备数据结构进行填充 120 cd->major = major; 121 cd->baseminor = baseminor; 122 cd->minorct = minorct;/*申请设备号的个数*/ 123 strlcpy(cd->name, name, sizeof(cd->name)); 124/*以下部分将char_device_struct变量注册到内核*/ 125 i = major_to_index(major); 126 127 for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) 128 if ((*cp)->major > major || //chardevs[i]设备号大于主设备号 129 ((*cp)->major == major && 130 (((*cp)->baseminor >= baseminor) || //chardevs[i]主设备号等于主设备号,并且此设备号大于baseminor 131 ((*cp)->baseminor + (*cp)->minorct > baseminor)))) 132 break; 133 //在字符设备数组中找到现在注册的设备 134 /* Check for overlapping minor ranges. */ 135 if (*cp && (*cp)->major == major) { 136 int old_min = (*cp)->baseminor; 137 int old_max = (*cp)->baseminor + (*cp)->minorct - 1; 138 int new_min = baseminor; 139 int new_max = baseminor + minorct - 1; 140 141 /* New driver overlaps from the left. */ 142 if (new_max >= old_min && new_max <= old_max) { 143 ret = -EBUSY; 144 goto out; 145 } 146 147 /* New driver overlaps from the right. */ 148 if (new_min <= old_max && new_min >= old_min) { 149 ret = -EBUSY; 150 goto out; 151 } 152 } 153 /*所申请的设备好号能够满足*/ 154 cd->next = *cp;/*按照主设备号从小到大顺序排列*/ 155 *cp = cd; 156 mutex_unlock(&chrdevs_lock); 157 return cd; 158 out: 159 mutex_unlock(&chrdevs_lock); 160 kfree(cd); 161 return ERR_PTR(ret); 162 }
下面程序出自fs/char_dev.c 动态申请设备号 ... 218 /** 219 * alloc_chrdev_region() - register a range of char device numbers 220 * @dev: output parameter for first assigned number 221 * @baseminor: first of the requested range of minor numbers 222 * @count: the number of minor numbers required 223 * @name: the name of the associated device or driver 224 * 225 * Allocates a range of char device numbers. The major number will be 226 * chosen dynamically, and returned (along with the first minor number) 227 * in @dev. Returns zero or a negative error code. 228 */ 229 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, 230 const char *name) 231 { /* dev: 仅仅作为输出参数,成功分配后将保存已分配的第一个设备编号。 baseminor: 被请求的第一个次设备号,通常是0。 count: 所要分配的设备号的个数。 name: 和所分配的设备号范围相对应的设备名称。 b.返回值: 成功返回0,失败返回负的错误编码 */ 232 struct char_device_struct *cd; 233 cd = __register_chrdev_region(0, baseminor, count, name); 234 if (IS_ERR(cd)) 235 return PTR_ERR(cd); 236 *dev = MKDEV(cd->major, cd->baseminor); 237 return 0; 238 } 239 ...