这只是看代码时的记录,完后会统一整理,希望会这样吧。
Linux kernel v3.6.7
先看kobj_map相关的代码
涉及到的文件
<linux/kobj_map.h>
<drivers/base/map.c>
- typedef struct kobject
*kobj_probe_t(dev_t,
int *, void
*);
- struct kobj_map;
- int kobj_map(struct kobj_map *, dev_t, unsigned long, struct module *, kobj_probe_t *, int (*)(dev_t, void *), void *);
- void kobj_unmap(struct kobj_map
*, dev_t, unsigned long);
- struct kobject *kobj_lookup(struct kobj_map
*, dev_t,
int *);
- struct kobj_map *kobj_map_init(kobj_probe_t *, struct mutex *);
先看kobj_map结构体
- struct kobj_map {
- struct probe {
- struct probe *next; /* 这样形成了链表结构 */
- dev_t dev; /* 设备号 */
- unsigned long range; /* 设备号的范围 */
- struct module
*owner;
- kobj_probe_t *get;
- int
(*lock)
(dev_t, void *);
- void *data; /* 指向struct cdev对象 */
- }
*probes[255];
- struct mutex *lock;
- }
根据下面的函数作用来看,kobj_map结构体是用来管理设备号及其对应的设备的。
kobj_map函数就是将指定的设备号加入到该数组,kobj_lookup则查找该结构体,然后返回对应设备号的kobject对象,利用
利用该kobject对象,我们可以得到包含它的对象如cdev。
struct probe结构体中的get函数指针就是用来获得kobject对象的,可能不同类型的设备获取的方式不同,我现在就看过cdev的exact_match函数。
kobj_map函数
- 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)
- {
- unsigned n = MAJOR(dev+range-1)
- MAJOR(dev)
+ 1;
- unsigned index = MAJOR(dev);
- unsigned i;
- struct probe *p;
- if
(n > 255) /* 若n > 255,则超出了kobj_map中probes数组的大小 */
- n = 255;
- p = kmalloc(sizeof(struct probe)
* n, GFP_KERNEL); /* 分配n个struct probe */
- if(p
==
NULL)
- return -ENOMEM;
- for(i
= 0; i
< n; i++, p++)
{ /* 用函数的参数初始化probe */
- p->owner
= module;
- p->get
= probe;
- p->lock
= lock;
- p->dev
= dev;
- p->range
= range;
- p->data
= data;
- }
- mutex_lock(domain->lock);
- for(i
= 0, p-=n; i
< n; i++, p++,
index++)
{
- struct probe **s
= &domain->probes[index
% 255];
- while(*s
&&
(*s)->range
< range)
- s =
&(*s)->next;
- p->next
= *s;
- *s
= p;
- }
- mutex_unlock(domain->lock);
- return 0;
- }
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函数
- void kobj_unmap(struct kobj_map
*domain, dev_t dev, unsigned long range)
- {
- unsigned n = MAJOR(dev
+ range - 1)
- MAJOR(dev)
+ 1;
- unsigned index = MAJOR(dev);
- unsigned i;
- struct probe *found
= NULL;
- if
(n > 255)
- n = 255;
- mutex_lock(domain->lock);
- for
(i = 0; i
< n; i++, index++)
{
- struct probe **s;
- for
(s = &domain->probes[index
% 255];
*s; s
= &(*s)->next)
{
- struct probe
*p = *s;
- if
(p->dev
== dev && p->range
== range)
{
- *s
= p->next;
- if
(!found)
- found
= p;
- break;
- }
- }
- }
- mutex_unlock(domain->lock);
- kfree(found);
- }
kobj_lookup函数
- struct kobject *kobj_lookup(struct kobj_map
*domain, dev_t dev,
int *index)
- {
- struct kobject *kobj;
- struct probe *p;
- unsigned long best
= ~0UL;
- retry:
- mutex_lock(domain->lock);
- for
(p = domain->probes[MAJOR(dev)
% 255]; p; p
= p->next)
{
- struct kobject
*(*probe)(dev_t,
int *, void
*);
- struct module
*owner;
- void *data;
- if
(p->dev
> dev || p->dev
+ p->range
- 1 < dev)
- continue;
- if
(p->range
- 1 >= best)
- break;
- if
(!try_module_get(p->owner))
- continue;
- owner = p->owner;
- data = p->data;
- probe = p->get;
- best = p->range
- 1;
- *index
= dev - p->dev; /* 这个是用来干嘛的? */
- if
(p->lock
&& p->lock(dev, data)
< 0)
{
- module_put(owner);
- continue;
- }
- mutex_unlock(domain->lock);
- kobj = probe(dev, index, data);
- /* Currently
->owner protects _only_
->probe() itself.
*/
- module_put(owner);
- if
(kobj)
- return kobj;
- goto retry;
- }
- mutex_unlock(domain->lock);
- return NULL;
- }
kobj_map_init函数
- struct kobj_map *kobj_map_init(kobj_probe_t
*base_probe, struct mutex
*lock)
- {
- struct kobj_map *p
= kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
- struct probe *base
= kzalloc(sizeof(*base), GFP_KERNEL);
- int i;
- if
((p ==
NULL)
|| (base
==
NULL))
{
- kfree(p);
- kfree(base);
- return NULL;
- }
- base->dev
= 1;
- base->range
= ~0;
- base->get
= base_probe;
- for
(i = 0; i
< 255; i++)
- p->probes[i]
= base;
- p->lock
= lock;
- return p;
- }
下面是cdev部分。
文件:
<linux/cdev.h>
<fs/char_dev.c>
cdev.h
- struct cdev {
- struct kobject kobj;
- struct module *owner;
- const struct file_operations
*ops;
- struct list_head list;
- dev_t dev;
- unsigned int count;
- }
- void cdev_init(struct cdev
*,
const struct file_operations *);
- struct cdev *cdev_alloc(void);
- void cdev_put(struct cdev
*p);
- int cdev_add(struct cdev
*, dev_t, unsigned);
- void cdev_del(struct cdev *);
cdev_init函数
此函数首先调用kobject_init初始化cdev中的kobj,然后将cdev中的ops赋值。
cdev_alloc函数
先kzalloc分配一个cdev,然后用kobject_init初始化kobj
cdev_put函数
- void cdev_put(struct cdev
*p)
- {
- if
(p) {
- struct module
*owner = p->owner;
- kobject_put(&p->kobj);
- module_put(owner);
- }
- }
cdev_add函数
- int cdev_add(struct cdev
*p, dev_t dev, unsigned count)
- {
- p->dev
= dev;
- p->count
= count;
- return kobj_map(cdev_map, dev, count,
NULL, exact_match, exact_lock, p);
- }
cdev_del函数
- static void cdev_unmap(dev_t dev, unsigned count)
- {
- kobj_unmap(cdev_map, dev, count);
- }
- void cdev_del(struct cdev
*p)
- {
- cdev_unmap(p->dev, p->count);
- kobject_put(&p->kobj);
- }
LDD3上说“只要cdev_add返回了,我们的设备就‘活’了,它的操作就会被内核调用",那么这句奇妙的话到底是个什么意思?
下面是我目前了解的情况
据说在open一个字符设备文件时,最终总会调用chrdev_open。
下面是该函数的源码
注意inode->i_rdev中保存了设备编号,inode->icdev指向了cdev结构。
- static int chrdev_open(struct inode
*inode, struct file
*filp)
- {
- struct cdev *p;
- struct cdev *new
= NULL;
- int ret
= 0;
- spin_lock(&cdev_lock);
- p = inode->i_cdev;
- if
(!p)
{
- struct kobject
*kobj;
- int idx;
- spin_unlock(&cdev_lock);
- kobj = kobj_lookup(cdev_map, inode->i_rdev,
&idx);
- if
(!kobj)
- return -ENXIO;
- new = container_of(kobj, struct cdev, kobj);
/* 找到字符设备的cdev */
- spin_lock(&cdev_lock);
- /* Check i_cdev again
in case somebody beat us
to it while
- we dropped the lock.
*/
- p = inode->i_cdev;
- if
(!p)
{
- inode->i_cdev
= p = new;
- list_add(&inode->i_devices,
&p->list);/* ZXG: 这是啥? */
- new =
NULL;
- }
else if (!cdev_get(p))
- ret =
-ENXIO;
- }
else if (!cdev_get(p))
- ret =
-ENXIO;
- spin_unlock(&cdev_lock);
- cdev_put(new);
- if
(ret)
- return ret;
- ret =
-ENXIO;
- filp->f_op
= fops_get(p->ops);
- if
(!filp->f_op)
- goto out_cdev_put;
- if
(filp->f_op->open)
{
- ret = filp->f_op->open(inode,
filp); /* 调用cdev->ops中的open函数 */
- if
(ret)
- goto out_cdev_put;
- }
- return 0;
- out_cdev_put:
- cdev_put(p);
- return ret;
- }