Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1315389
  • 博文数量: 213
  • 博客积分: 7590
  • 博客等级: 少将
  • 技术积分: 2185
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-31 17:31
个人简介

热爱开源,热爱linux

文章分类

全部博文(213)

文章存档

2018年(4)

2017年(1)

2015年(1)

2014年(5)

2013年(2)

2012年(2)

2011年(21)

2010年(82)

2009年(72)

2008年(23)

分类: LINUX

2010-07-30 15:54:26


1.在内核中, dev_t 类型(在 中定义)用来持有设备编号 — 主次部分都包括.其中dev_t 是 32 位的量, 12 位用作主编号, 20 位用作次编号
  1 #ifndef _LINUX_TYPES_H
  2 #define _LINUX_TYPES_H
  3
  4 #include
  5
  6 #ifndef __ASSEMBLY__
  7 #ifdef  __KERNEL__
  8
  9 #define DECLARE_BITMAP(name,bits) \
 10     unsigned long name[BITS_TO_LONGS(bits)]
 11
 12 #endif
 13
 14 #include
 15
 16 #ifdef __KERNEL__
 17
 18 typedef __u32 __kernel_dev_t;
 19
 20 typedef __kernel_fd_set     fd_set;
 21 typedef __kernel_dev_t      dev_t;  //用来持有设备编号的主次部分                                        
 22 typedef __kernel_ino_t      ino_t;
 23 typedef __kernel_mode_t     mode_t;
...

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 }

以上程序大体上分为两个步骤:
    1.char_device_struct类型变量的分配以及初始化94~123行
    2.将char_device_struct变量注册到内核,12行页到162行

1.char_device_struct类型变量的分配以及初始化
    (1)首先,调用 kmalloc 分配一个 char_device_struct 变量cd。
        检查返回值,进行错误处理。
    (2)将分配的char_device_struct变量的内存区清零memset。
    (3)获取chrdevs_lock读写锁,并且关闭中断,禁止内核抢占,write_lock_irq。
    (4)如果传入的主设备号major不为0,跳转到第(7)步。
    (5)这时,major为0,首先需要分配一个合适的主设备号。
        将 i 赋值成 ARRAY_SIZE(chrdevs)-1,其中的 chrdevs 是包含有256个char_device_struct *类型的数组,
        然后递减 i 的值,直到在chrdevs数组中出现 NULL。当chrdevs数组中不存在空值的时候,
        ret = -EBUSY;    goto out;
    (6)到达这里,就表明主设备号major已经有合法的值了,接着进行char_device_struct变量的初始化。
        设置major, baseminor, minorct以及name。
2.将char_device_struct变量注册到内核
    (7)将 i 赋值成 major_to_index(major)
        将major对256取余数,得到可以存放char_device_struct在chrdevs中的索引
    (8)进入循环,在chrdevs[i]的链表中找到一个合适位置。
        退出循环的条件:
         (1)chrdevs[i]为空。
         (2)chrdevs[i]的主设备号大于major。
         (3)chrdevs[i]的主设备号等于major,但是次设备号大于等于baseminor。
         注意:cp = &(*cp)->next,cp是char_device_struct **类型,(*cp)->next是一个char_device_struct *
             类型,所以&(*cp)->next,就得到一个char_device_struct **,并且这时候由于是指针,所以
             对cp赋值,就相当于对链表中的元素的next字段进行操作。
    (9)进行冲突检查,因为退出循环的情况可能造成设备号冲突(产生交集)。
        如果*cp不空,并且*cp的major与要申请的major相同,此时,如果(*cp)->baseminor < baseminor + minorct,
        就会发生冲突,因为和已经分配了的设备号冲突了。出错就跳转到ret = -EBUSY; goto out;
       
    (10)到这里,内核可以满足设备号的申请,将cd链接到链表中。
    (11)释放chrdevs_lock读写锁,开中断,开内核抢占。
    (12)返回加入链表的char_device_struct变量cd。
    (13)out出错退出
        a.释放chrdevs_lock读写锁,开中断,开内核抢占。
        b.释放char_device_struct变量cd,kfree。
        c.返回错误信息   

下面程序出自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
...



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