在学习字符设备驱动的时候,对于设备的注册我们经常会用到register_chrdev_region等函数。今天就来看看这些函数在内核中是如何实现的。在内核中这些函数的实现主要是调用__register_chrdev_region函数。
/fs/chr_dev.c 184
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;
197 dev_t n, next;
198
199 for (n = from; n < to; n = next) {
200 next = MKDEV(MAJOR(n)+1, 0); //根据主设备号MAJOR(n)+1和次设备号0生成设备号next
201 if (next > to) //说明申请的设备号已经足够
202 next = to;
203 cd = __register_chrdev_region(MAJOR(n), MINOR(n),
204 next - n, name); //将设备号n添加到散列表中
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); //由此说明分配设备号的时候是从from逐个递增上去的
213 kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
214 }
215 return PTR_ERR(cd);
216 }
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 */
91 static struct char_device_struct *
92 __register_chrdev_region(unsigned int major, unsigned int baseminor,
93 int minorct, const char *name)
94 {
95 struct char_device_struct *cd, **cp;
96 int ret = 0;
97 int i;
98
99 cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
100 if (cd == NULL)
101 return ERR_PTR(-ENOMEM);
102
103 mutex_lock(&chrdevs_lock);
104
105 /* temporary */
106 if (major == 0) { //major=0说明需要动态申请主设备号
107 for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) { //chrdevs是在/fs/char_dev.c文件的49行定义的全局变量。
//一组对应同一个字符设备驱动的设备编号范围定义一个 char_device_struct 结构
108 if (chrdevs[i] == NULL)
109 break;
110 }
111
112 if (i == 0) { //i=0说明没有可以分配的设备号了
113 ret = -EBUSY;
114 goto out;
115 }
116 major = i; //i=!0说明散列表中能够给动态分配一个字符设备,分配成功之后主设备号major为i
117 ret = major; //返回值设定为major
118 }
119
120 cd->major = major; //主设备号
121 cd->baseminor = baseminor; //次设备号
122 cd->minorct = minorct; //设备编号的范围
123 strlcpy(cd->name, name, sizeof(cd->name));//设备名称
124
//返回主设备号对应在散列表chardevs中的位置
125 i = major_to_index(major);
126
//若当前的major在chrdevs中存在对应的项目则找出来存放在cp中,或者不存在((*cp)->major>major)
//根据major主设备号计算出她在散列表中的位置
127 for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
128 if ((*cp)->major > major ||
129 ((*cp)->major == major &&
130 (((*cp)->baseminor >= 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
//正确的时候将相应的结构char_device_struct 插入对应的散列表中。
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 }
163
/fs/chr_dev.c 218行
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 {
232 struct char_device_struct *cd;
//动态分配设备编号,传递给__register_chrdev_region的第一个参数等于0
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
关于chardevs的定义:
49 static struct char_device_struct {
50 struct char_device_struct *next;
51 unsigned int major;
52 unsigned int baseminor;
53 int minorct;
54 char name[64];
55 struct cdev *cdev; /* will die */
56 } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
关于major_to_index的定义:
59 static inline int major_to_index(int major)
60 {
61 return major % CHRDEV_MAJOR_HASH_SIZE;
62 }
//结果是主设备号对255取余得到的数值。
在/include/linux/fs.h中定义CHRDEV_MAJOR_HASH_SIZE的大小是255:
1991 #define CHRDEV_MAJOR_HASH_SIZE 255
在/arch/x86/boot/boot.h中定义:
35 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))