您的位置:首页 > 其它

设备模型之 bus 、devices、drivers、class 分析

2014-03-25 19:08 225 查看
 在分析设备模型的bus之前,我们看一下初始化的过程,很有意思:

     start_kernel-->rest_init-->kernel_init-->do_basic_setup

     在do_basic_setup中有个usermodehelper_init有意思,凭感觉,这个函数与kobject_uevent有联系,kobject_uevent做什么的,就是内核空间来通知用户空间的,比如udev就是利用这个来在/dev创建设备文件的,再比如在嵌入式中,插拔个硬盘或者USB,gui总会提示插入或者拨出之类的提示,这个一般是内核中驱动模型根据kobject的uevent来告诉用户态的,如果通信的,是通过netlink这种特殊的socket来像用户空间广播的,这样gui中可以用socket来接受信息,并解析,然后显示出来。先提到这里,usermodehelper_init是不是这么回事,还需要更多的时间去研究。

      好,继续看do_basic_setup中的其它函数:driver_init

      /**
* driver_init - initialize driver model.
*
* Call the driver model init functions to initialize their
* subsystems. Called early from init/main.c.
*/
void __init driver_init(void)
{
/* These are the core pieces */
devtmpfs_init();
devices_init();
buses_init();
classes_init();
firmware_init();
hypervisor_init();

/* These are also core pieces, but must come after the
* core core pieces.
*/
platform_bus_init();
system_bus_init();
cpu_dev_init();
memory_dev_init();
}

      看下这个函数的注释,就清楚它就是接下来要分析的设备模型 的初始化。

      devtmpfs_init是什么?是在/dev下面挂载的一个内存文件系统,具体可以看《linux设备模型之字符设备》一文,与驱动是有一定联系,应该是2.6.32之后加入的。

      今天我们只分析core core pieces中的buses_init:

int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return 0;
}

       很简单,新增一个kset,在/sys下有了bus目录。

       我们的重心在后面,如何在bus目录下添加总线类型:

       描述总线的结构体:bus_type

struct bus_type {
const char *name; //总线的名称
struct bus_attribute *bus_attrs; //总线的属性
struct device_attribute *dev_attrs; //设备的属性
struct driver_attribute *drv_attrs; //驱动的属性

int (*match)(struct device *dev, struct device_driver *drv); //设备与驱动的匹配函数
int (*uevent)(struct device *dev, struct kobj_uevent_env *env); //添加或移除设备时,该函数向用户空间产生环境变量
int (*probe)(struct device *dev); //当设备与驱动匹配上时调用,初始化设备
int (*remove)(struct device *dev); //当设备移除时调用
void (*shutdown)(struct device *dev);

int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev); //总线的电源管理操作

const struct dev_pm_ops *pm;

struct bus_type_private *p; //总线的私有变量指针
};

         与bus_type紧密相关的结构体:struct bus_type_private

struct bus_type_private {
struct kset subsys; //具体总线的kset
struct kset *drivers_kset; //总线上所有驱动的集合
struct kset *devices_kset; //总线上所有设备的集合
struct klist klist_devices; //链表,上面是总线上的设备
struct klist klist_drivers; //链表,上面是总线上的驱动
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus; //指向总线类型结构体
};

        总线的注册:bus_register

        /**
* bus_register - register a bus with the system.
* @bus: bus.
*
* Once we have that, we registered the bus with the kobject
* infrastructure, then register the children subsystems it has:
* the devices and drivers that belong to the bus.
*/
int bus_register(struct bus_type *bus)
{
int retval;
struct bus_type_private *priv;

priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;

priv->bus = bus;
bus->p = priv; //bus_type和bus_type_private紧密联系起来

BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //设置具体总线kset内嵌kobject的名称
if (retval)
goto out;

priv->subsys.kobj.kset = bus_kset; //总线kset内嵌kobject的kset指向bus_kset,也就是说上级目录是/sys/bus
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;

retval = kset_register(&priv->subsys); //注册总线的kset,生成/sys/bus/xxbus 目录
if (retval)
goto out;

retval = bus_create_file(bus, &bus_attr_uevent); //创建uevent目录,只写的文件,写文件会调用kobject_uevent函数
if (retval)
goto bus_uevent_fail;

priv->devices_kset = kset_create_and_add("devices", NULL, //在xxbus目录下建立devices目录
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}

priv->drivers_kset = kset_create_and_add("drivers", NULL, //在xxbus目录下建立drivers目录
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}

klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); //初始化klist_devices链表
klist_init(&priv->klist_drivers, NULL, NULL); //初始化klist_drivers链表

retval = add_probe_files(bus); //在xxbus目录下建立drivers_probe和drivers_autoprobe文件
if (retval)
goto bus_probe_files_fail;

retval = bus_add_attrs(bus); //建立bus的总线属性文件
if (retval)
goto bus_attrs_fail;

pr_debug("bus: '%s': registered\n", bus->name);
return 0;

bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
kfree(bus->p);
out:
bus->p = NULL;
return retval;
}

        总线的注册方法就是上面这么多,比如platform总线的注册,就是在/sys/bus目录下多了个platform目录,而在platform目录下:

        devices  drivers  drivers_autoprobe  drivers_probe  uevent

        总线远没有目前看到的这么简单,因为它的方法我们还没用到。在分析完设备和驱动后,我们再分析一个完整的过程。

 

 

 

分析完了bus,接下来分析device:
      同样在driver_init-->devices_init

[cpp]
view plaincopyprint?

<SPAN style="FONT-FAMILY: 'Liberation Serif', serif">int __init devices_init(void)  
{  
    devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);  
    if (!devices_kset)  
        return -ENOMEM;  
    dev_kobj = kobject_create_and_add("dev", NULL);  
    if (!dev_kobj)  
        goto dev_kobj_err;  
    sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);  
    if (!sysfs_dev_block_kobj)  
        goto block_kobj_err;  
    sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);  
    if (!sysfs_dev_char_kobj)  
        goto char_kobj_err;  
  
    return 0;  
  
 char_kobj_err:  
    kobject_put(sysfs_dev_block_kobj);  
 block_kobj_err:  
    kobject_put(dev_kobj);  
 dev_kobj_err:  
    kset_unregister(devices_kset);  
    return -ENOMEM;  
}</SPAN>  

int __init devices_init(void)
{
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
if (!devices_kset)
return -ENOMEM;
dev_kobj = kobject_create_and_add("dev", NULL);
if (!dev_kobj)
goto dev_kobj_err;
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
if (!sysfs_dev_block_kobj)
goto block_kobj_err;
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
if (!sysfs_dev_char_kobj)
goto char_kobj_err;

return 0;

char_kobj_err:
kobject_put(sysfs_dev_block_kobj);
block_kobj_err:
kobject_put(dev_kobj);
dev_kobj_err:
kset_unregister(devices_kset);
return -ENOMEM;
}

       /sys目录下有了devices目录和dev目录,dev下面有block和char两个目录

       设备的注册:

[cpp]
view plaincopyprint?

<SPAN style="FONT-FAMILY: 'Liberation Serif', serif">int device_register(struct device *dev)  
{  
    device_initialize(dev);  
    return device_add(dev);  
}  
</SPAN>  

int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}


设备的初始化device_initialize

void device_initialize(structdevice *dev)
{
dev->kobj.kset =devices_kset; //这个device结构体内嵌的kobject代表的目录在/sys/devices下
kobject_init(&dev->kobj,&device_ktype); //初始化内嵌的kobject,kobj_type为device_ktype
INIT_LIST_HEAD(&dev->dma_pools);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
device_pm_init(dev);
set_dev_node(dev, -1);
}

添加设备device_add

2.1、初始化device的structdevice_private *p;指针
int device_private_init(structdevice *dev)
{
dev->p =kzalloc(sizeof(*dev->p), GFP_KERNEL);
if (!dev->p)
return -ENOMEM;
dev->p->device = dev; //指向dev
klist_init(&dev->p->klist_children,klist_children_get,
klist_children_put); //初始化klist_children链表
return 0;
}
2.2、设置device结构体内嵌的kobject的名称
2.3、setup_parent(dev,parent);
根据device结构体的父子层次关系设置内嵌kobject对象的父子层次关系
2.4、在kobject层注册device内嵌的kobject对象
2.5、创建设备属性文件uevent,uevent文件的读写与kobject_uevent相关
2.6、如果device结构体的devt设备号存在,那么创建devt属性文件文件,从这个属性文件中可以读出设备号;sysfs中会创建链接文件;devtmpfs会创建结点node;
2.7、设备添加class的链接文件
2.8、添加设备属性文件
2.9、bus_add_device(dev); 添加设备到总线
A、添加设备的总线属性:bus->dev_attrs
B、创建链接文件
C、添加设备到总线的设备链表中:
klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);
2.10、bus_probe_device(dev);为device探测driver
bus_probe_device-->device_attach
如果dev的driver指针已经指向了driver结构体,那么调用device_bind_driver将device和driver绑定,否则调用
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);对于bus总线klist_drivers链表上所有挂着的driver,都

来和我们的device进行匹配,负责的函数就是__device_attach

如果dev的driver指针已经
static int__device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;

if (!driver_match_device(drv,dev))
return 0;

return driver_probe_device(drv,dev);
}
首先调用总线级别的match函数,然后调用driver_probe_device(drv,dev);
driver_probe_device-->really_probe-->dev->bus->probe(dev);以及drv->probe(dev);
最后调用driver_bound:
klist_add_tail(&dev->p->knode_driver,&dev->driver->p->klist_devices);

                       devices的注册过程就如上所述,至于最后的bus_probe_device具体是怎么实现的?等到分析完busdevice
driver和class后,再举一个实例来具体分析。分析完了bus,接下来分析device:
      同样在driver_init-->devices_init

[cpp]
view plaincopyprint?

<SPAN style="FONT-FAMILY: 'Liberation Serif', serif">int __init devices_init(void)  
{  
    devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);  
    if (!devices_kset)  
        return -ENOMEM;  
    dev_kobj = kobject_create_and_add("dev", NULL);  
    if (!dev_kobj)  
        goto dev_kobj_err;  
    sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);  
    if (!sysfs_dev_block_kobj)  
        goto block_kobj_err;  
    sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);  
    if (!sysfs_dev_char_kobj)  
        goto char_kobj_err;  
  
    return 0;  
  
 char_kobj_err:  
    kobject_put(sysfs_dev_block_kobj);  
 block_kobj_err:  
    kobject_put(dev_kobj);  
 dev_kobj_err:  
    kset_unregister(devices_kset);  
    return -ENOMEM;  
}</SPAN>  

int __init devices_init(void)
{
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
if (!devices_kset)
return -ENOMEM;
dev_kobj = kobject_create_and_add("dev", NULL);
if (!dev_kobj)
goto dev_kobj_err;
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
if (!sysfs_dev_block_kobj)
goto block_kobj_err;
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
if (!sysfs_dev_char_kobj)
goto char_kobj_err;

return 0;

char_kobj_err:
kobject_put(sysfs_dev_block_kobj);
block_kobj_err:
kobject_put(dev_kobj);
dev_kobj_err:
kset_unregister(devices_kset);
return -ENOMEM;
}


       /sys目录下有了devices目录和dev目录,dev下面有block和char两个目录

       设备的注册:

[cpp]
view plaincopyprint?

<SPAN style="FONT-FAMILY: 'Liberation Serif', serif">int device_register(struct device *dev)  
{  
    device_initialize(dev);  
    return device_add(dev);  
}  
</SPAN>  

int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}


设备的初始化device_initialize

void device_initialize(structdevice *dev)
{
dev->kobj.kset =devices_kset; //这个device结构体内嵌的kobject代表的目录在/sys/devices下
kobject_init(&dev->kobj,&device_ktype); //初始化内嵌的kobject,kobj_type为device_ktype
INIT_LIST_HEAD(&dev->dma_pools);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
device_pm_init(dev);
set_dev_node(dev, -1);
}

添加设备device_add

2.1、初始化device的structdevice_private *p;指针
int device_private_init(structdevice *dev)
{
dev->p =kzalloc(sizeof(*dev->p), GFP_KERNEL);
if (!dev->p)
return -ENOMEM;
dev->p->device = dev; //指向dev
klist_init(&dev->p->klist_children,klist_children_get,
klist_children_put); //初始化klist_children链表
return 0;
}
2.2、设置device结构体内嵌的kobject的名称
2.3、setup_parent(dev,parent);
根据device结构体的父子层次关系设置内嵌kobject对象的父子层次关系
2.4、在kobject层注册device内嵌的kobject对象
2.5、创建设备属性文件uevent,uevent文件的读写与kobject_uevent相关
2.6、如果device结构体的devt设备号存在,那么创建devt属性文件文件,从这个属性文件中可以读出设备号;sysfs中会创建链接文件;devtmpfs会创建结点node;
2.7、设备添加class的链接文件
2.8、添加设备属性文件
2.9、bus_add_device(dev); 添加设备到总线
A、添加设备的总线属性:bus->dev_attrs
B、创建链接文件
C、添加设备到总线的设备链表中:
klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);
2.10、bus_probe_device(dev);为device探测driver
bus_probe_device-->device_attach
如果dev的driver指针已经指向了driver结构体,那么调用device_bind_driver将device和driver绑定,否则调用
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);对于bus总线klist_drivers链表上所有挂着的driver,都

来和我们的device进行匹配,负责的函数就是__device_attach

如果dev的driver指针已经
static int__device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;

if (!driver_match_device(drv,dev))
return 0;

return driver_probe_device(drv,dev);
}
首先调用总线级别的match函数,然后调用driver_probe_device(drv,dev);
driver_probe_device-->really_probe-->dev->bus->probe(dev);以及drv->probe(dev);
最后调用driver_bound:
klist_add_tail(&dev->p->knode_driver,&dev->driver->p->klist_devices);

                       devices的注册过程就如上所述,至于最后的bus_probe_device具体是怎么实现的?等到分析完busdevice
driver和class后,再举一个实例来具体分析。

 

 看完了device看driver,给出device_driver的结构体:

struct device_driver {
const char *name;
struct bus_type *bus;

struct module *owner;
const char *mod_name; /* used for built-in modules */

bool suppress_bind_attrs; /* disables bind/unbind via sysfs */

int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;

const struct dev_pm_ops *pm;

struct driver_private *p;
};


 

struct driver_private {
struct kobject kobj;
struct klist klist_devices;
struct klist_node knode_bus;
struct module_kobject *mkobj;
struct device_driver *driver;
};

      驱动的注册:

int driver_register(struct device_driver *drv)

主体部分分为三块看:

1、other = driver_find(drv->name, drv->bus);

      driver_find函数是利用驱动模型的基石kobject和kset结构体的双向循环链表,根据name查找。这里的kset是bus->p->drivers_kset,也

      就是bus上已经挂着的驱动的集合。如果找到了,就有问题了:已经注册了该驱动,不能再注册了,kernel会打印error并返回EBUSY。

      如果没找到,就到了第二步

2、bus_add_driver(drv);

       这个过程和bus_add_device比较类似
       2.1、建立driver_private和device_driver你中有我 我中有你的亲密关系
       2.2、priv->kobj.kset = bus->p->drivers_kset;   意味了/bus/xxbus/driver下面就是具体driver的目录
       2.3、
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}

        调用driver_attach-->bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
        对于bus的klist_devices链表上挂着的所有device,都来和我们的driver进行匹配,进行匹配的函数是__driver_attach
        __driver_attach首先调用driver_match_device,也就是总线级别的match函数进行匹配
        如果没匹配上,就调用driver_probe_device
        这里的函数都是与device中用到的一样
        2.4、klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
                将driver加入bus的klist_drivers链表中
        2.5、driver_create_file(drv, &driver_attr_uevent);
                创建uevent属性文件
        2.6、添加driver指向的bus的驱动属性文件bus->drv_attrs
        2.7、如果需要的话,添加bind和unbind属性文件
        2.8、kobject_uevent(&priv->kobj, KOBJ_ADD);  发送uevent

driver的注册就是这样了,没什么好说的了。。。

看完了bus device和driver,终于到了设备模型上层架构的最后一个单元:class.class的初始化同样是在driver_init下:

      driver_init-->classes_init

int __init classes_init(void)
{
class_kset = kset_create_and_add("class", NULL, NULL);
if (!class_kset)
return -ENOMEM;
return 0;
}


        新增一个名为class的kset,这样/sys下面就有了class

        class到底是什么,它是对同类型设备的抽象,是device classes,class结构体的定义如下:

struct class {
const char *name;
struct module *owner;

struct class_attribute *class_attrs;
struct device_attribute *dev_attrs;
struct kobject *dev_kobj;

int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(struct device *dev, mode_t *mode);

void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);

int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);

const struct dev_pm_ops *pm;

struct class_private *p;
};


 

        比如我们的rtc子系统,就会在子系统初始化时创建rtc的class:

        rtc_init-->rtc_class = class_create(THIS_MODULE, "rtc");

        class_create是个宏定义:

#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})

         __class_create首先做初始话操作,然后调用__class_register函数做class的注册操作:

        注册过程主要是建立class名称的目录,然后建立class的属性文件.

        class实在没什么好说的了,其实讲了这么多,这个设备模型到底是什么还没讲清楚,只有自己动手做一次才明白,后面以s3c2440的rtc为例来全面的体验一下设备模型.

        s3c2440的rtc是一个很好的例子,既有platform这样的虚拟总线,也有rtc的class,完全符合体验的需求.

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: