cdev_add
初始化 cdev 后,需要把它添加到系统中去。为此可以调用 cdev_add()函数。传入cdev 结构的指针,起始设备编号,以及设备编号范围。
函数首先将分配的设备号与设备数目保存进cdev结构体中。然后再讲cdev结构体记录在一个 kobj_map 结构的 cdev_map 变量中。
1 /** 2 * cdev_add() - add a char device to the system 3 * @p: the cdev structure for the device 4 * @dev: the first device number for which this device is responsible 5 * @count: the number of consecutive minor numbers corresponding to this 6 * device 7 * 8 * cdev_add() adds the device represented by @p to the system, making it 9 * live immediately. A negative error code is returned on failure. 10 */ 11 int cdev_add(struct cdev *p, dev_t dev, unsigned count) 12 { 13 p->dev = dev; 14 p->count = count; 15 return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); 16 }
内核中所有都字符设备都会记录在一个 kobj_map 结构的 cdev_map 变量中。这个结构的变量中包含一个散列表用来快速存取所有的对象。kobj_map() 函数就是用来把字符设备编号和 cdev 结构变量一起保存到 cdev_map 这个散列表里。当后续要打开一个字符设备文件时,通过调用 kobj_lookup() 函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops 字段。
kobj_map函数中哈希表的实现原理和前面注册分配设备号中的几乎完全一样,通过要加入系统的设备的主设备号major(major=MAJOR(dev))来获得probes数组的索引值i(i = major % 255),然后把一个类型为struct probe的节点对象加入到probes[i]所管理的链表中,如图2-6所示。其中struct probe所在的矩形块中的深色部分是我们重点关注的内容,记录了当前正在加入系统的字符设备对象的有关信息。其中,dev是它的设备号,range是从次设备号开始连续的设备数量,data是一void *变量,指向当前正要加入系统的设备对象指针p。图2-6展示了两个满足主设备号major % 255 = 2的字符设备通过调用cdev_add之后,cdev_map所展现出来的数据结构状态。
所以,简单地说,设备驱动程序通过调用cdev_add把它所管理的设备对象的指针嵌入到一个类型为struct probe的节点之中,然后再把该节点加入到cdev_map所实现的哈希链表中。对系统而言,当设备驱动程序成功调用了cdev_add之后,就意味着一个字符设备对象已经加入到了系统,在需要的时候,系统就可以找到它。对用户态的程序而言,cdev_add调用之后,就已经可以通过文件系统的接口呼叫到我们的驱动程序。
1 static struct kobj_map *cdev_map; 2 typedef struct kobject *kobj_probe_t(dev_t, int *, void *); 3 struct kobj_map { 4 struct probe { 5 struct probe *next; 6 dev_t dev; 7 unsigned long range; 8 struct module *owner; 9 kobj_probe_t *get; 10 int (*lock)(dev_t, void *); 11 void *data; 12 } *probes[255]; 13 struct mutex *lock; 14 };
1 //cdev_add(struct cdev *p, dev_t dev, unsigned count) 2 // 设备号, 设备数目, 匹配函数, 锁定函数, cdev指针 3 //kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); 4 int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range, 5 struct module *module, kobj_probe_t *probe, 6 int (*lock)(dev_t, void *), void *data) 7 { 8 unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;//判断占用几个主设备号 9 unsigned index = MAJOR(dev); 10 unsigned i; 11 struct probe *p; 12 13 if (n > 255) 14 n = 255; 15 16 p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL); 17 18 if (p == NULL) 19 return -ENOMEM; 20 21 for (i = 0; i < n; i++, p++) { 22 p->owner = module; 23 p->get = probe; 24 p->lock = lock; 25 p->dev = dev; 26 p->range = range; 27 p->data = data; 28 } 29 mutex_lock(domain->lock); 30 for (i = 0, p -= n; i < n; i++, p++, index++) 31 { 32 struct probe **s = &domain->probes[index % 255]; 33 while (*s && (*s)->range < range) 34 s = &(*s)->next; 35 p->next = *s; 36 *s = p; 37 } 38 mutex_unlock(domain->lock); 39 return 0; 40 }
当一个字符设备驱动不再需要的时候(比如模块卸载),就可以用 cdev_del() 函数来释放 cdev 占用的内存。
1 /** 2 * cdev_del() - remove a cdev from the system 3 * @p: the cdev structure to be removed 4 * 5 * cdev_del() removes @p from the system, possibly freeing the structure 6 * itself. 7 */ 8 void cdev_del(struct cdev *p) 9 { 10 cdev_unmap(p->dev, p->count); 11 kobject_put(&p->kobj); 12 }
kobj_unmap() 释放 cdev_map 散列表中的对象。
1 //void cdev_del(struct cdev *p) 2 //cdev_unmap(p->dev, p->count); 3 static void cdev_unmap(dev_t dev, unsigned count) 4 { 5 kobj_unmap(cdev_map, dev, count); 6 } 7 void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range) 8 { 9 unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1; 10 unsigned index = MAJOR(dev); 11 unsigned i; 12 struct probe *found = NULL; 13 14 if (n > 255) 15 n = 255; 16 17 mutex_lock(domain->lock); 18 for (i = 0; i < n; i++, index++) { 19 struct probe **s; 20 for (s = &domain->probes[index % 255]; *s; s = &(*s)->next) 21 { 22 struct probe *p = *s; 23 if (p->dev == dev && p->range == range) 24 { 25 *s = p->next; 26 if (!found) 27 found = p; 28 break; 29 } 30 } 31 } 32 mutex_unlock(domain->lock); 33 kfree(found); 34 }
kobject_put() 释放 cdev 结构本身。
1 /** 2 * kobject_put - decrement refcount for object. 3 * @kobj: object. 4 * 5 * Decrement the refcount, and if 0, call kobject_cleanup(). 6 */ 7 void kobject_put(struct kobject *kobj) 8 { 9 if (kobj) { 10 if (!kobj->state_initialized) 11 WARN(1, KERN_WARNING "kobject: '%s' (%p): is not " 12 "initialized, yet kobject_put() is being " 13 "called.\n", kobject_name(kobj), kobj); 14 kref_put(&kobj->kref, kobject_release); 15 } 16 }