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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM