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 }
kobj_unmap()

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.
", kobject_name(kobj), kobj);
14         kref_put(&kobj->kref, kobject_release);
15     }
16 }
kobject_put()
原文地址:https://www.cnblogs.com/yangjiguang/p/6034776.html