Chinaunix首页 | 论坛 | 博客
  • 博客访问: 942328
  • 博文数量: 146
  • 博客积分: 3321
  • 博客等级: 中校
  • 技术积分: 1523
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-29 10:32
文章分类

全部博文(146)

文章存档

2014年(2)

2013年(5)

2012年(4)

2011年(6)

2010年(30)

2009年(75)

2008年(24)

分类: LINUX

2010-07-22 21:11:03

在学习字符设备驱动的时候,对于设备的注册我们经常会用到register_chrdev_region等函数。今天就来看看这些函数在内核中是如何实现的。在内核中这些函数的实现主要是调用__register_chrdev_region函数。

下面是一些简单的分析(2.6.33.3):
/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)))

阅读(1486) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~