linux的 bus、device、driver介绍

  linux 通过device和driver分别管理系统中的设备和驱动,用bus将设备和驱动关联起来,bus可以看成是设备和驱动的媒介,可以匹配设备和驱动。这样设备和驱动可以独立加载,互不影响。sysfs是一个基于内存的文件系统,它的作用是将内核信息以文件的方式提供给用户程序使用。我们都知道设备和对应的驱动都是由内核管理的,这些对于用户空间是不可见的。现在通过sysfs,可以在用户空间直观的了解设备驱动的层次结构。

一、bus注册过程

bus_type结构体代表一条总线,如下所示:

struct bus_type {
    const char        *name;    //名称
    const char        *dev_name;
    struct device        *dev_root;
    struct device_attribute    *dev_attrs;    /* use dev_groups instead */
    const struct attribute_group **bus_groups;
    const struct attribute_group **dev_groups;
    const struct attribute_group **drv_groups;

    int (*match)(struct device *dev, struct device_driver *drv);    //device和driver的匹配函数
    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 (*online)(struct device *dev);
    int (*offline)(struct device *dev);

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

    const struct dev_pm_ops *pm;

    const struct iommu_ops *iommu_ops;

    struct subsys_private *p;    
    struct lock_class_key lock_key;
};
struct subsys_private {
    struct kset subsys;        //对应/sys/bus/xxx目录,xxx为自己取的bus名称
    struct kset *devices_kset;    //对应/sys/bus/xxx/devices目录
    struct list_head interfaces;
    struct mutex mutex;

    struct kset *drivers_kset;    //对应/sys/bus/xxx/drivers目录
    struct klist klist_devices;   //该bus下的所有device
    struct klist klist_drivers;    //该bus下的所有driver
    struct blocking_notifier_head bus_notifier;
    unsigned int drivers_autoprobe:1;
    struct bus_type *bus;

    struct kset glue_dirs;
    struct class *class;
};

 

向系统添加一条bus_type总线时,改总线会自动添加到/sys/bus目录下,bus目录是系统自动创建的,这个bus目录为static struct kset *bus_kset,定义在kernel/drivers/base/bus.c中。创建过程如下所示:

/sys/bus目录的建立过程:kernel/drivers/base/bus.c

               -->buses_init

                -->kset_create_and_add  //这行代码建立/sys下面的bus目录,其中bus_kset就代表bus目录

向系统注册一个yy_bus总线代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

static int yy_bus_match(struct device *dev, struct device_driver *drv)
{
    printk("dev->kobj.name = %s, drv->name = %s", dev->kobj.name, drv->name);
    if (!strncmp(dev->kobj.name, drv->name, strlen(drv->name)))
    {
        printk("device and driver is match!\n");
        return 1;
    }
    else
    {
        printk("device and driver is match error ttt!\n");
        return 0;
    }
}

struct bus_type yy_bus = {
    .name = "yy_bus",      //总线名称,注册成功后,将在/sys/bus/ 目录下生成yy_bus目录
    .match = yy_bus_match,    //设备和驱动的匹配方法
};

EXPORT_SYMBOL(yy_bus); 


static int __init yy_bus_init(void)
{
    int ret = 0;
    printk ("yy_bus_init is run\n");

    ret = bus_register(&yy_bus);
    if(ret < 0)
    {
        printk("bus register error!\n");
        return ret;        
    }
    return ret;
}

static void __exit yy_bus_exit(void)
{
    printk ("yy_bus_exit is run\n");
    bus_unregister(&yy_bus);
}

module_init(yy_bus_init);
module_exit(yy_bus_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yyfage");

编译完成后,执行 insmod yy_bus.ko命令会在/sys/bus/目录下生成yy_bus目录,如下所示:

进入bus目录,里面还会有几个文件,如下所示:

devices: 挂在该总线上的设备,目前没有设备挂载,里面是空的

drivers: 挂在该总线上的驱动,目前没哟驱动挂载,里面是空的

yy_bus总线的注册过程及注释如下所示:

/*bus总线注册部分关键代码*/

int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;


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


priv->bus = bus;
bus->p = priv;


BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);


retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;

//bus_kset就是上面的/sys/bus,这样指定后,用户自己创建的bus就会在/sys/bus目录下面

priv->subsys.kobj.kset = bus_kset;          
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;

//在/sys/bus/目录下创建xxx目录,xxx目录名字为bus->name

retval = kset_register(&priv->subsys);          


if (retval)
goto out;

//在/sys/bus/xxx/目录下创建uevent文件

retval = bus_create_file(bus, &bus_attr_uevent);    


if (retval)
goto bus_uevent_fail;

//在/sys/bus/xxx/目录下创建devices目录

priv->devices_kset = kset_create_and_add("devices", NULL,    
&priv->subsys.kobj);    
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}

//在/sys/bus/xxx/目录下创建drivers目录

priv->drivers_kset = kset_create_and_add("drivers", NULL,  
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}


INIT_LIST_HEAD(&priv->interfaces);
__mutex_init(&priv->mutex, "subsys mutex", key);
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);

/*创建属性,在bus/XXX/建立文件drivers_autoprobe和drivers_probe*/ 

retval = add_probe_files(bus);     
if (retval)
goto bus_probe_files_fail;


retval = bus_add_groups(bus, bus->bus_groups);
if (retval)
goto bus_groups_fail;


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

}

 

二、device注册过程

struct device 结构体部分成员变量

struct device {
    struct device        *parent;  //该device的父类

    struct device_private    *p;  //私有数据

    struct kobject kobj;      //device的目录,/sys/devices/xxx,xxx就是device的目录
    const char        *init_name; /* initial name of the device */
    const struct device_type *type;

    struct mutex        mutex;    /* mutex to synchronize calls to* its driver.*/

    struct bus_type    *bus;        /* type of bus device is on */
    struct device_driver *driver;    /* which driver has allocated this device */

  dev_t devt;            /*设备号,如果有设备号,则会在/dev目录下创建设备*/ };
struct device_private结构体
struct device_private {
    struct klist klist_children;
    struct klist_node knode_parent;  //
    struct klist_node knode_driver;  
    struct klist_node knode_bus;    //这个会加入到bus_type->subsys_private->klist_devices中
struct list_head deferred_probe; struct device *device; };

 

向系统添加一个device设备时,该设备会自动添加到/sys/devies目录中。devies目录是系统自动创建的,这个devices目录为struct kset *devices_kset;,定义在kernel/drivers/base/core.c中注册过程如下所示:

/sys/devices/目录注册过程:kernel/drivers/base/core.c

              -->devices_init

                -->kset_create_and_add  //在/sys/目录下创建devices目录

向系统注册一个yy_dev设备的代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

extern struct bus_type yy_bus;

static struct device yy_dev = {
    .kobj.name     = "yy_dev",
    .bus         = &yy_bus,

};


static int __init init_yy_dev(void)
{
    int ret = 0;

    printk("yy init_yy_dev is run\n");
    
    ret = device_register(&yy_dev);
    if(ret < 0)
    {
        printk("device register error!\n");
        return ret;        
    }
    
    return ret;
}

static void __exit exit_yy_dev(void)
{
    printk("yy exit_yy_dev is run\n");
    device_unregister(&yy_dev);
}

module_init(init_yy_dev);
module_exit(exit_yy_dev);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("yyfage");

编译完后执行 insmod yy_devices.ko命令,会在sys/devices/目录下生成yy_dev目录,如下所示:

 device注册过程部分关键源码分析

kernel/drivers/base/core.c

  -->device_register

    -->device_initialize

    -->device_add

device_register源码如下:

void device_initialize(struct device *dev)
{
    dev->kobj.kset = devices_kset;          //指定注册的设备添加到/sys/devices目录下,device_kset就是上面提到的那个
    kobject_init(&dev->kobj, &device_ktype);
    INIT_LIST_HEAD(&dev->dma_pools);
    mutex_init(&dev->mutex);
    lockdep_set_novalidate_class(&dev->mutex);
    spin_lock_init(&dev->devres_lock);
    INIT_LIST_HEAD(&dev->devres_head);
    device_pm_init(dev);
    set_dev_node(dev, -1);
#ifdef CONFIG_GENERIC_MSI_IRQ
    INIT_LIST_HEAD(&dev->msi_list);
#endif
}
int device_add(struct device *dev)
{
    struct device *parent = NULL;
    struct kobject *kobj;
    struct class_interface *class_intf;
    int error = -EINVAL;
    struct kobject *glue_dir = NULL;

    dev = get_device(dev);            /*增加引用计数*/
    if (!dev)
        goto done;

    if (!dev->p) {
        error = device_private_init(dev);
        if (error)
            goto done;
    }

    /*
     * for statically allocated devices, which should all be converted
     * some day, we need to initialize the name. We prevent reading back
     * the name, and force the use of dev_name()
     */
    if (dev->init_name) {
        dev_set_name(dev, "%s", dev->init_name);
        dev->init_name = NULL;
    }

    /* subsystems can specify simple device enumeration */
    if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
        dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);

    if (!dev_name(dev)) {
        error = -EINVAL;
        goto name_error;
    }

    pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

    parent = get_device(dev->parent);
    kobj = get_device_parent(dev, parent);
    if (kobj)
        dev->kobj.parent = kobj;

    /* use parent numa_node */
    if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
        set_dev_node(dev, dev_to_node(parent));

    /* first, register with generic layer. */
    /* we require the name to be set before, and pass NULL */
    /* 执行完以后,将在/sys/devices/下建立目录XXX,目录名XXX为dev->kobj->name*/  
    error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
    if (error) {
        glue_dir = get_glue_dir(dev);
        goto Error;
    }

    /* notify platform of device entry */
    if (platform_notify)
        platform_notify(dev);
    /*在XXX下建立文件uevent*/ 
    error = device_create_file(dev, &dev_attr_uevent);
    if (error)
        goto attrError;

    error = device_add_class_symlinks(dev);
    if (error)
        goto SymlinkError;
    error = device_add_attrs(dev);    /*添加类设备属型文件和属性组*/
    if (error)
        goto AttrsError;
    error = bus_add_device(dev);    /*添加3个symlink*/ 
    if (error)
        goto BusError;
    error = dpm_sysfs_add(dev);        /*创建power子目录,并在其下添加电源管理的属性组文件*/  
    if (error)
        goto DPMError;    
    device_pm_add(dev);                /*将该device添加到电源管理链表中*/  

    if (MAJOR(dev->devt)) {      /*如果有设备号,这在/dev 目录下创建设备*/
        error = device_create_file(dev, &dev_attr_dev);
        if (error)
            goto DevAttrError;

        error = device_create_sys_dev_entry(dev);
        if (error)
            goto SysEntryError;

        devtmpfs_create_node(dev);
    }

    /* Notify clients of device addition.  This call must come
     * after dpm_sysfs_add() and before kobject_uevent().
     */
    if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                         BUS_NOTIFY_ADD_DEVICE, dev);

    kobject_uevent(&dev->kobj, KOBJ_ADD);
    bus_probe_device(dev);        //查找总线下的所有驱动,为匹配的驱动执行prob函数
    if (parent)            /*有父设备,则将该设备添加到父设备的儿子链表中*/  
        klist_add_tail(&dev->p->knode_parent,
                   &parent->p->klist_children);

    if (dev->class) {        /*将device添加到class的类设备链表中*/  
        mutex_lock(&dev->class->p->mutex);
        /* tie the class to the device */
        klist_add_tail(&dev->knode_class,
                   &dev->class->p->klist_devices);

        /* notify any interfaces that the device is here */
        list_for_each_entry(class_intf,
                    &dev->class->p->interfaces, node)
            if (class_intf->add_dev)
                class_intf->add_dev(dev, class_intf);
        mutex_unlock(&dev->class->p->mutex);
    }
}
bus_add_device函数分析
int bus_add_device(struct device *dev)
{
    struct bus_type *bus = bus_get(dev->bus);
    int error = 0;

    if (bus) {
        pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
        error = device_add_attrs(bus, dev);
        if (error)
            goto out_put;
        error = device_add_groups(dev, bus->dev_groups);
        if (error)
            goto out_id;
        error = sysfs_create_link(&bus->p->devices_kset->kobj,
                        &dev->kobj, dev_name(dev));      //将/sys/devices/dev_name 链接到/sys/bus/bus_name/devices中
        if (error)
            goto out_groups;
        error = sysfs_create_link(&dev->kobj,
                &dev->bus->p->subsys.kobj, "subsystem");
        if (error)
            goto out_subsys;
        klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);  //将device节点添加到bus链表中
    }
    return 0;
}

 



device_add函数中有个重要的函数bus_probe_device,这个函数用来匹配deviec和driver,匹配成功后执行drive的prob函数,下面对这个函数进行分析
kernel/drivers/base/bus.c
  -->bus_probe_device
    -->device_initial_probe
      -->__device_attach
        -->bus_for_each_drv(dev->bus, NULL, &data,__device_attach_driver);  //查找该bus下的所有驱动,这个bus就是上面注册的yy_bus,并为改设备和所以驱动执行函数__device_attach_driver。
kernel/drivers/base/dd.c
  -->__device_attach_driver  //匹配device和driver,并执行drive的prob函数
    -->driver_match_device  //查看device和driver是否匹配,就是执行bus下面的math函数
    -->driver_probe_device  //如果device和drive匹配,则执行这个 函数
      -->really_probe
        -->drv->probe    //最终执行drive的probe函数
 

三、driver注册过程

下面程序是向系统注册一个名称为“yy_dev”的driver

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>

static int yy_drv_prob(struct device *dev)
{
    int ret = 0;

    printk("yy yy_drv_prob is run\n");

    return ret;
}

static int yy_drv_remove(struct device *dev)
{
    int ret = 0;

    printk("yy yy_drv_remove is run\n");

    return ret;
}


extern struct bus_type yy_bus;

static struct device_driver yy_drv = {
    .name = "yy_dev",            
    .bus = &yy_bus,
    .probe = yy_drv_prob,
    .remove = yy_drv_remove,
};

static int __init init_yy_drv(void)
{
    int ret = 0;

    printk("yy init_yy_drv is run\n");
    ret = driver_register(&yy_drv);
    
    if(ret < 0)
    {
        printk("driver register error!\n");
        return ret;        
    }
    
    return ret;
}

static void __exit exit_yy_drv(void)
{
    printk("yy exit_yy_drv is run\n");
    driver_unregister(&yy_drv);
}

module_init(init_yy_drv);
module_exit(exit_yy_drv);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("yyfage");

编码代码,执行insmod yy_driver.ko(先要加载yy_bus.ko),注册成功后,会在/sys/bus/yy_bus/drivers目录下生成yy_dev目录,如下所示

只要driver中的name和device中的.kobj.name一致,就会执行drive下的prob函数,因为yy_bus中的的math函数是根据device和driver的名称匹配的:

driver注册过程如下:

  kernel/drivers/base/driver.c

    -->driver_register

      -->bus_add_driver //将driver添加到bus中,并匹配device,这个函数分析如下所示

int bus_add_driver(struct device_driver *drv)
{
    struct bus_type *bus;
    struct driver_private *priv;
    int error = 0;

    bus = bus_get(drv->bus);
    if (!bus)
        return -EINVAL;

    pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

    priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    if (!priv) {
        error = -ENOMEM;
        goto out_put_bus;
    }
    klist_init(&priv->klist_devices, NULL, NULL);
    priv->driver = drv;
    drv->p = priv;
    priv->kobj.kset = bus->p->drivers_kset;    //指定添加的driver目录在/sys/bus/drivers目录下
    error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,  //在/sys/bus/drivers目录下创建xxx目录,这xxx就是drv->name
                     "%s", drv->name);
    if (error)
        goto out_unregister;

    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);  //将driver添加到bus的链表中
    if (drv->bus->p->drivers_autoprobe) {
        if (driver_allows_async_probing(drv)) {
            pr_debug("bus: '%s': probing driver %s asynchronously\n",
                drv->bus->name, drv->name);
            async_schedule(driver_attach_async, drv);
        } else {
            error = driver_attach(drv);              //匹配driver和device,如果匹配成功,则执行driver的prob函数,匹配过程见上面的device
            if (error)
                goto out_unregister;
        }
    }
    module_add_driver(drv->owner, drv);

    error = driver_create_file(drv, &driver_attr_uevent);
    if (error) {
        printk(KERN_ERR "%s: uevent attr (%s) failed\n",
            __func__, drv->name);
    }
    error = driver_add_groups(drv, bus->drv_groups);
    if (error) {
        /* How the hell do we get out of this pickle? Give up */
        printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
            __func__, drv->name);
    }

    if (!drv->suppress_bind_attrs) {
        error = add_bind_files(drv);
        if (error) {
            /* Ditto */
            printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
                __func__, drv->name);
        }
    }

    return 0;
}

 

posted @ 2022-03-08 11:18  YYFaGe  阅读(1766)  评论(0编辑  收藏  举报