Chinaunix首页 | 论坛 | 博客
  • 博客访问: 19413
  • 博文数量: 4
  • 博客积分: 45
  • 博客等级: 民兵
  • 技术积分: 35
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-27 16:49
文章分类
文章存档

2013年(1)

2012年(3)

我的朋友
最近访客

分类:

2012-11-20 23:17:08

原文地址:linux设备:cdev和kobj_map 作者:zengxg14

这只是看代码时的记录,完后会统一整理,希望会这样吧。

Linux kernel v3.6.7

先看kobj_map相关的代码
涉及到的文件


  1. typedef struct kobject *kobj_probe_t(dev_t, int *, void *);
  2. struct kobj_map;
  3. int kobj_map(struct kobj_map *, dev_t, unsigned long, struct module *, kobj_probe_t *, int (*)(dev_t, void *), void *);
  4. void kobj_unmap(struct kobj_map *, dev_t, unsigned long);
  5. struct kobject *kobj_lookup(struct kobj_map *, dev_t, int *);
  6. struct kobj_map *kobj_map_init(kobj_probe_t *, struct mutex *);

先看kobj_map结构体
  1. struct kobj_map {
  2.     struct probe {
  3.         struct probe *next;      /* 这样形成了链表结构 */
  4.         dev_t dev;               /* 设备号 */
  5.         unsigned long range;     /* 设备号的范围 */
  6.         struct module *owner;
  7.         kobj_probe_t *get;
  8.         int (*lock) (dev_t, void *);
  9.         void *data;              /* 指向struct cdev对象 */
  10.     } *probes[255];
  11.     struct mutex *lock;
  12. }
结构体中有一个互斥锁lock,一个probes[255]数组,数组元素为struct probe的指针。
根据下面的函数作用来看,kobj_map结构体是用来管理设备号及其对应的设备的。
kobj_map函数就是将指定的设备号加入到该数组,kobj_lookup则查找该结构体,然后返回对应设备号的kobject对象,利用
利用该kobject对象,我们可以得到包含它的对象如cdev。
struct probe结构体中的get函数指针就是用来获得kobject对象的,可能不同类型的设备获取的方式不同,我现在就看过cdev的exact_match函数。

kobj_map函数
  1. int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range, struct module *module, kobj_probe_t *probe, int (*lock)(dev_t, void *), void *data)
  2. {
  3.     unsigned n = MAJOR(dev+range-1) - MAJOR(dev) + 1;
  4.     unsigned index = MAJOR(dev);
  5.     unsigned i;
  6.     struct probe *p;

  7.     if (n > 255)     /* 若n > 255,则超出了kobj_map中probes数组的大小 */
  8.         n = 255;
  9.     p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);  /* 分配n个struct probe */
  10.     if(p == NULL)
  11.         return -ENOMEM;
  12.     for(i = 0; i < n; i++, p++) {     /* 用函数的参数初始化probe */
  13.         p->owner = module;
  14.         p->get = probe;
  15.         p->lock = lock;
  16.         p->dev = dev;
  17.         p->range = range;
  18.         p->data = data;
  19.     }
  20.     mutex_lock(domain->lock);
  21.     for(i = 0, p-=n; i < n; i++, p++, index++) {
  22.         struct probe **s = &domain->probes[index % 255];
  23.         while(*s && (*s)->range < range)
  24.             s = &(*s)->next;
  25.         p->next = *s;
  26.         *s = p;
  27.     }
  28.     mutex_unlock(domain->lock);
  29.     return 0;
  30. }
dev_t的前12位为主设备号,后20位为次设备号。
n = MAJOR(dev + range - 1) - MAJOR(dev) + 1 表示设备号范围(dev, dev+range)中不同的主设备号的个数。
通常n的值为1。
从代码中的第二个for循环可以看出kobj_map中的probes数组中每个元素为一个struct probe链表的头指针。
每个链表中的probe对象有(MAJOR(probe.dev) % 255)值相同的关系。若主设备号小于255, 则每个链表中的probe都有相同的主设备号。
链表中的元素是按照range值从小到大排列的。
while循环即是找出该将p插入的位置。



kobj_unmap函数

  1. void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range)
  2. {
  3.     unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
  4.     unsigned index = MAJOR(dev);
  5.     unsigned i;
  6.     struct probe *found = NULL;

  7.     if (n > 255)
  8.         n = 255;

  9.     mutex_lock(domain->lock);
  10.     for (i = 0; i < n; i++, index++) {
  11.         struct probe **s;
  12.         for (s = &domain->probes[index % 255]; *s; s = &(*s)->next) {
  13.             struct probe *p = *s;
  14.             if (p->dev == dev && p->range == range) {
  15.                 *s = p->next;
  16.                 if (!found)
  17.                     found = p;
  18.                 break;
  19.             }
  20.         }
  21.     }
  22.     mutex_unlock(domain->lock);
  23.     kfree(found);
  24. }
在16行,找到对应设备号dev和range指定的probe对象后,退出,然后kfree释放空间。


kobj_lookup函数

  1. struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
  2. {
  3.     struct kobject *kobj;
  4.     struct probe *p;
  5.     unsigned long best = ~0UL;

  6. retry:
  7.     mutex_lock(domain->lock);
  8.     for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
  9.         struct kobject *(*probe)(dev_t, int *, void *);
  10.         struct module *owner;
  11.         void *data;

  12.         if (p->dev > dev || p->dev + p->range - 1 < dev)
  13.             continue;
  14.         if (p->range - 1 >= best)
  15.             break;
  16.         if (!try_module_get(p->owner))
  17.             continue;
  18.         owner = p->owner;
  19.         data = p->data;
  20.         probe = p->get;
  21.         best = p->range - 1;
  22.         *index = dev - p->dev;   /* 这个是用来干嘛的? */
  23.         if (p->lock && p->lock(dev, data) < 0) {
  24.             module_put(owner);
  25.             continue;
  26.         }
  27.         mutex_unlock(domain->lock);
  28.         kobj = probe(dev, index, data);
  29.         /* Currently ->owner protects _only_ ->probe() itself. */
  30.         module_put(owner);
  31.         if (kobj)
  32.             return kobj;
  33.         goto retry;
  34.     }
  35.     mutex_unlock(domain->lock);
  36.     return NULL;
  37. }
对cdev_add函数,这里的p->probe函数即是exact_match, p->lock为exact_lock函数。


kobj_map_init函数

  1. struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
  2. {
  3.     struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
  4.     struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL);
  5.     int i;

  6.     if ((p == NULL) || (base == NULL)) {
  7.         kfree(p);
  8.         kfree(base);
  9.         return NULL;
  10.     }

  11.     base->dev = 1;
  12.     base->range = ~0;
  13.     base->get = base_probe;
  14.     for (i = 0; i < 255; i++)
  15.         p->probes[i] = base;
  16.     p->lock = lock;
  17.     return p;
  18. }
在初始化一个kobj_map对象时,将probes指针全部指向同一个base。



下面是cdev部分。
文件:

cdev.h

  1. struct cdev {
  2.     struct kobject kobj;
  3.     struct module *owner;
  4.     const struct file_operations *ops;
  5.     struct list_head list;
  6.     dev_t dev;
  7.     unsigned int count;
  8. }
  9. void cdev_init(struct cdev *, const struct file_operations *);
  10. struct cdev *cdev_alloc(void);
  11. void cdev_put(struct cdev *p);
  12. int cdev_add(struct cdev *, dev_t, unsigned);
  13. void cdev_del(struct cdev *);

cdev_init函数
此函数首先调用kobject_init初始化cdev中的kobj,然后将cdev中的ops赋值。

cdev_alloc函数
先kzalloc分配一个cdev,然后用kobject_init初始化kobj

cdev_put函数

  1. void cdev_put(struct cdev *p)
  2. {
  3.     if (p) {
  4.         struct module *owner = p->owner;
  5.         kobject_put(&p->kobj);
  6.         module_put(owner);
  7.     }
  8. }
此函数调用kobject_put和module_put,好像它们的作用就是减少引用计数

cdev_add函数

  1. int cdev_add(struct cdev *p, dev_t dev, unsigned count)
  2. {
  3.     p->dev = dev;
  4.     p->count = count;
  5.     return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
  6. }
主要是调用kobj_map将cdev放入cdev_map中。

cdev_del函数
  1. static void cdev_unmap(dev_t dev, unsigned count)
  2. {
  3.     kobj_unmap(cdev_map, dev, count);
  4. }

  5. void cdev_del(struct cdev *p)
  6. {
  7.     cdev_unmap(p->dev, p->count);
  8.     kobject_put(&p->kobj);
  9. }
这就不用说啥了。

LDD3上说“只要cdev_add返回了,我们的设备就‘活’了,它的操作就会被内核调用",那么这句奇妙的话到底是个什么意思?

下面是我目前了解的情况

据说在open一个字符设备文件时,最终总会调用chrdev_open。
下面是该函数的源码
注意inode->i_rdev中保存了设备编号,inode->icdev指向了cdev结构。
  1. static int chrdev_open(struct inode *inode, struct file *filp)
  2. {
  3.     struct cdev *p;
  4.     struct cdev *new = NULL;
  5.     int ret = 0;

  6.     spin_lock(&cdev_lock);
  7.     p = inode->i_cdev;
  8.     if (!p) {
  9.         struct kobject *kobj;
  10.         int idx;
  11.         spin_unlock(&cdev_lock);
  12.         kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);   
  13.         if (!kobj)
  14.             return -ENXIO;
  15.         new = container_of(kobj, struct cdev, kobj);   /* 找到字符设备的cdev */
  16.         spin_lock(&cdev_lock);
  17.         /* Check i_cdev again in case somebody beat us to it while
  18.          we dropped the lock. */
  19.         p = inode->i_cdev;
  20.         if (!p) {
  21.             inode->i_cdev = p = new;
  22.             list_add(&inode->i_devices, &p->list);/* ZXG: 这是啥? */
  23.             new = NULL;
  24.         } else if (!cdev_get(p))
  25.             ret = -ENXIO;
  26.     } else if (!cdev_get(p))
  27.         ret = -ENXIO;
  28.     spin_unlock(&cdev_lock);
  29.     cdev_put(new);
  30.     if (ret)
  31.         return ret;

  32.     ret = -ENXIO;
  33.     filp->f_op = fops_get(p->ops);
  34.     if (!filp->f_op)
  35.         goto out_cdev_put;

  36.     if (filp->f_op->open) {
  37.         ret = filp->f_op->open(inode, filp); /* 调用cdev->ops中的open函数 */
  38.         if (ret)
  39.             goto out_cdev_put;
  40.     }

  41.     return 0;

  42.  out_cdev_put:
  43.     cdev_put(p);
  44.     return ret;
  45. }






























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