总线设备驱动模型——设备篇

本文深入探讨了Linux设备管理的核心概念,包括设备的初始化、注册、属性文件创建、与总线交互的过程,以及如何通过驱动匹配设备。文章详细介绍了设备结构、属性文件、设备注册流程等关键组件,为读者提供了全面的设备管理知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

strcut device_type

struct device_type {

    const char *name;

    const struct attribute_group **groups;

    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

    char *(*devnode)(struct device *dev, mode_t *mode);

    void (*release)(struct device *dev);

 

    const struct dev_pm_ops *pm;

};

struct device

    首先是指向父节点的指针parent,kobj是内嵌在device中的kbojcet,还包括device匹配的驱动,属于哪条总线。其中比较重要的是release函数,当这个设备被删去时,内核调用该方法,所有注册到内核的设备结构必须有一个release方法。

struct device {

    struct device        *parent;      /*指向父节点的指针*/

 

    struct device_private    *p;

 

    struct kobject kobj;                 /*用它联系到sysfs*/

    const char        *init_name;      /*初始化设备的名字 */

    const struct device_type *type;      /*device属性文件*/

 

    struct mutex        mutex;   /* mutexto synchronize calls to

                     * its driver.

                     */

 

    struct bus_type    *bus;                /*属于哪条总线 */

    struct device_driver *driver;    /*哪个驱动能匹配该设备*/

    void        *platform_data;   

    struct dev_pm_info    power;

    struct dev_pm_domain    *pm_domain;

 

#ifdef CONFIG_NUMA

    int        numa_node;   /* NUMAnode this device is close to */

#endif

    u64        *dma_mask;          /* dma mask (if dma'able device) */

    u64        coherent_dma_mask/* Like dma_mask, but for

                     alloc_coherentmappings as

                     not allhardware supports

                     64 bitaddresses for consistent

                     allocationssuch descriptors. */

 

    struct device_dma_parameters *dma_parms;

 

    struct list_head    dma_pools;   /* dmapools (if dma'ble) */

 

    struct dma_coherent_mem    *dma_mem; /* internal for coherent mem

                     override */

    /* arch specific additions */

    struct dev_archdata    archdata;

 

    struct device_node    *of_node; /* associated device tree node */

 

    dev_t            devt;   /*dev_t, creates the sysfs "dev" */

 

    spinlock_t        devres_lock;

    struct list_head    devres_head;

 

    struct klist_node    knode_class;

    struct class        *class;

    const struct attribute_group **groups;    /* optional groups */

 

    void   (*release)(struct device *dev);

};

struct device_private

    和总线一样,device也有一个联系bus、device、driver的数据结构device_private。

struct device_private {

    struct klist klist_children;     //子设备的链表

    struct klist_node knode_parent;  //连接到父设备的klist_childern时所用的节点

    struct klist_node knode_driver;  //连接到驱动的设备时用到的节点

    struct klist_node knode_bus;     //连接到总线时所用的节点

    void *driver_data;                  //用于在设备结构中存放相关的驱动信息

    struct device *device;           //指向属于的device

};

#define to_device_private_parent(obj)   \

    container_of(obj, structdevice_private, knode_parent)//从父设备的klist_children上节点,获取相应的private

#define to_device_private_driver(obj)   \

    container_of(obj, struct device_private,knode_driver)//从驱动链表上获得private

#define to_device_private_bus(obj)   \

    container_of(obj, struct device_private,knode_bus) //从总线上获得private

struct device_attribute

    sysf中的设备入口的属性结构

/* interface for exporting device attributes */

struct device_attribute{

    struct attribute    attr;

    ssize_t (*show)(struct device *dev, struct device_attribute *attr,

            char *buf);

    ssize_t (*store)(struct device *dev, struct device_attribute *attr,

             const char *buf, size_t count);

};

 

#define DEVICE_ATTR(_name, _mode, _show, _store) \

struct device_attributedev_attr_##_name = __ATTR(_name, _mode, _show, _store)

设备初始化devices_init()

    同bus过程一样,device的初始化也是在driver_init完成初始化的。这个函数就是创建在sysfs中的devices目录和dev目录,还在dev目录下创建了block和char两个子目录。

int __init devices_init(void)

{

    devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);                                                 //创建devices目录

    if (!devices_kset)

        return -ENOMEM;

    dev_kobj =kobject_create_and_add("dev", NULL); //创建dev目录

    if (!dev_kobj)

        goto dev_kobj_err;

sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);

                                                 //创建dev/block目录

    if (!sysfs_dev_block_kobj)

        goto block_kobj_err;

sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);

                                                 //创建dev/char目录

    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;

}

    这部分用于向用户控件传递event事件。

static const struct kset_uevent_opsdevice_uevent_ops = {

    .filter =   dev_uevent_filter,

    .name =        dev_uevent_name,

    .uevent =   dev_uevent,

};

设备注册device_register()

    下面来分析下设备的注册过程。device_register是提供给外界注册设备的接口,主要调用device_initialize完成dev结构的初始化,然后调用device_add将其加入到系统中。

int device_register(struct device *dev)

{

    device_initialize(dev);

    return device_add(dev);

}

device_initialize()主要是完成device结构的初始化,把device中能初始化的部分全部初始化,主要是将device_ktype加入到kobject中。

voiddevice_initialize(struct device *dev)

{

    dev->kobj.kset = devices_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);

}

    设置dev名字,调用kobject_add()将dev加入sysfs,调用device_create_file(dev, &uevent_attr)创建uevent属性。如果dev没有被分配设备号,调用device_create_file(dev, &devt_attr)添加dev属性,之后调用device_create_sys_dev_entry在/sys/dev下添加相应的软链接,最后调用devtmpfs_create_node在/dev下创建相应的设备文件。device_add_class_symlinks()主要是添加dev与class间的软链接,bus_add_device()将dev挂入到bus的设备链表中,之后dpm_sysfs_add增加dev下的power属性, device_pm_add将dev加入到dpm_list的链表中。kobject_uevent(&dev->kobj,KOBJ_ADD)会做kobject_uevent发布KOBJ_ADD消息,最后会进入重要的函数bus_probe_device(dev),为dev寻找合适的驱动。寻找到合适的驱动后,下面做的主要是如果有parent节点,把knode_parent挂入klist_children链表。如果dev有属于的class,也挂入到链表中,对device的分析至此结束。

int device_add(struct device *dev)

{

    struct device *parent = NULL;

    struct class_interface *class_intf;

    int error = -EINVAL;

 

    dev = get_device(dev);               //增加dev的引用计数

    if (!dev)

        goto done;

 

    if (!dev->p) {

        error = device_private_init(dev);//分配和初始化dev->p

        if (error)

            goto done;

    }

 

    /*

     * for statically allocateddevices, which should all be converted

     * some day, we need toinitialize the name. We prevent reading back

     * the name, and force the useof dev_name()

     */

    if (dev->init_name) {

        dev_set_name(dev, "%s", dev->init_name);//设置dev名字

        dev->init_name = NULL;

    }

 

    if (!dev_name(dev)) {

        error = -EINVAL;

        goto name_error;

    }

 

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

 

    parent = get_device(dev->parent);

    setup_parent(dev, parent);

 

    /* use parent numa_node */

    if (parent)

        set_dev_node(dev, dev_to_node(parent));

 

    /* first, register with genericlayer. */

    /* we require the name to be setbefore, and pass NULL */

    error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); //dev加入sysfs

    if (error)

        goto Error;

 

    /* notify platform of device entry */

    if (platform_notify)

        platform_notify(dev);

 

    error = device_create_file(dev, &uevent_attr);//创建uevent属性文件

    if (error)

        goto attrError;

 

    if (MAJOR(dev->devt)) {

        error = device_create_file(dev, &devt_attr);//如果没有设备号,就创建dev属性

        if (error)

            goto ueventattrError;

 

        error =device_create_sys_dev_entry(dev); //创建软链接

        if (error)

            goto devtattrError;

 

        devtmpfs_create_node(dev);                //dev下创建设备文件

    }

 

    error =device_add_class_symlinks(dev);  //创建devclass之间软链接

    if (error)

        goto SymlinkError;

    error = device_add_attrs(dev); //添加device/type/class定义的属性集合

    if (error)

        goto AttrsError;

    error = bus_add_device(dev);             //dev挂入到bus的设备链表中

    if (error)

        goto BusError;

    error = dpm_sysfs_add(dev);                                    //增加dev下的power属性

    if (error)

        goto DPMError;

    device_pm_add(dev);                                                         //dev加入到dpm_list的链表中

 

    /* Notify clients of device addition.This call must come

     * after dpm_sysf_add() andbefore 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);        //发布KOBJ_ADD消息

    bus_probe_device(dev);                       //dev寻找合适的驱动

    if (parent)

        klist_add_tail(&dev->p->knode_parent,

             &parent->p->klist_children);               //knode_parent挂入klist_children链表

 

    if (dev->class) {

        mutex_lock(&dev->class->p->class_mutex);

        /* tie the class to the device */

        klist_add_tail(&dev->knode_class,

             &dev->class->p->klist_devices);

//如果dev有属于的class,也挂入到链表中

 

        /* notify any interfaces that thedevice is here */

        list_for_each_entry(class_intf,

                 &dev->class->p->class_interfaces, node)

            if (class_intf->add_dev)

                class_intf->add_dev(dev, class_intf);

        mutex_unlock(&dev->class->p->class_mutex);

    }

done:

    put_device(dev);

    return error;

 DPMError:

    bus_remove_device(dev);

 BusError:

    device_remove_attrs(dev);

 AttrsError:

    device_remove_class_symlinks(dev);

 SymlinkError:

    if (MAJOR(dev->devt))

        devtmpfs_delete_node(dev);

    if (MAJOR(dev->devt))

        device_remove_sys_dev_entry(dev);

 devtattrError:

    if (MAJOR(dev->devt))

        device_remove_file(dev, &devt_attr);

 ueventattrError:

    device_remove_file(dev, &uevent_attr);

 attrError:

    kobject_uevent(&dev->kobj, KOBJ_REMOVE);

    kobject_del(&dev->kobj);

 Error:

    cleanup_device_parent(dev);

    if (parent)

        put_device(parent);

name_error:

    kfree(dev->p);

    dev->p = NULL;

    goto done;

}

    device_create_file()创建属性文件。

int device_create_file(struct device *dev,

         const struct device_attribute *attr)

{

    int error = 0;

    if (dev)

        error = sysfs_create_file(&dev->kobj, &attr->attr);

    return error;

}

    device_add_class_symlinks()主要是添加dev与class间的软链接。

static int device_add_class_symlinks(struct device *dev)

{

    int error;

 

    if (!dev->class)

        return 0;

 

    error = sysfs_create_link(&dev->kobj,

                 &dev->class->p->subsys.kobj,

                 "subsystem");

    if (error)

        goto out;

 

    if (dev->parent &&device_is_not_partition(dev)) {

        error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,

                     "device");

        if (error)

            goto out_subsys;

    }

 

#ifdef CONFIG_BLOCK

    /* /sys/block has directories anddoes not need symlinks */

    if (sysfs_deprecated && dev->class == &block_class)

        return 0;

#endif

 

    /* link in the class directorypointing to the device */

    error = sysfs_create_link(&dev->class->p->subsys.kobj,

                 &dev->kobj, dev_name(dev));

    if (error)

        goto out_device;

 

    return 0;

 

out_device:

    sysfs_remove_link(&dev->kobj, "device");

 

out_subsys:

    sysfs_remove_link(&dev->kobj, "subsystem");

out:

    return error;

}

    下面来看看bus_add_device(),可以看出其只做了一个添加dev与bus间的软链接,并将dev挂到bus的设备链表中。

int bus_add_device(struct device *dev)

{

    struct bus_type *bus = bus_get(dev->bus);

    int error = 0;

 

    if (bus) {

        pr_debug("bus: '%s': adddevice %s\n", bus->name, dev_name(dev));

        error = device_add_attrs(bus, dev);

        if (error)

            goto out_put;

        error = sysfs_create_link(&bus->p->devices_kset->kobj,

                        &dev->kobj, dev_name(dev));

        if (error)

            goto out_id;

        error = sysfs_create_link(&dev->kobj,

                &dev->bus->p->subsys.kobj, "subsystem");//创建busdev的软链接

        if (error)

            goto out_subsys;

        klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); //挂入链表

    }

    return 0;

 

out_subsys:

    sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));

out_id:

    device_remove_attrs(bus, dev);

out_put:

    bus_put(dev->bus);

    return error;

}

    device_pm_add()将dev加入到dpm_list的链表中。

void device_pm_add(struct device *dev)

{

    pr_debug("PM: Addinginfo for %s:%s\n",

         dev->bus ? dev->bus->name : "No Bus", dev_name(dev));

    mutex_lock(&dpm_list_mtx);

    if (dev->parent && dev->parent->power.is_prepared)

        dev_warn(dev, "parent %sshould not be sleeping\n",

            dev_name(dev->parent));

    list_add_tail(&dev->power.entry, &dpm_list);

    dev_pm_qos_constraints_init(dev);

    mutex_unlock(&dpm_list_mtx);

}

    重要的函数bus_probe_device(dev)为dev寻找合适的驱动,调用device_attach()。

void bus_probe_device(struct device *dev)

{

    struct bus_type *bus = dev->bus;

    int ret;

 

    if (bus && bus->p->drivers_autoprobe) {

        ret = device_attach(dev);

        WARN_ON(ret < 0);

    }

}

    当device被注册时候,会匹配drv是否有匹配的项,其匹配过程主要是调用__device_attach。

int device_attach(struct device *dev)

{

    int ret = 0;

 

    device_lock(dev);

    if (dev->driver) {

        if (klist_node_attached(&dev->p->knode_driver)) {

            ret = 1;

            goto out_unlock;

        }

        ret = device_bind_driver(dev);

        if (ret == 0)

            ret = 1;

        else {

            dev->driver = NULL;

            ret = 0;

        }

    } else {

        pm_runtime_get_noresume(dev);

        ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);

        pm_runtime_put_sync(dev);

    }

out_unlock:

    device_unlock(dev);

    return ret;

}

    __device_attach()如果匹配成功,就会调用probe函数。

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

}

    先来看看匹配的过程,主要是调用bus的match函数。

static inline int driver_match_device(struct device_driver *drv,

                 struct device *dev)

{

    return drv->bus->match ? drv->bus->match(dev, drv) : 1;

}

    匹配成功后,调用probe函数,从下面可以看出如果bus的probe函数存在,调用bus的,如果不存在,则调用dev的probe函数。

static int really_probe(struct device *dev, struct device_driver *drv)

{

    int ret = 0;

 

    atomic_inc(&probe_count);

    pr_debug("bus: '%s': %s:probing driver %s with device %s\n",

         drv->bus->name, __func__, drv->name, dev_name(dev));

    WARN_ON(!list_empty(&dev->devres_head));

 

    dev->driver = drv;

    if (driver_sysfs_add(dev)) {

        printk(KERN_ERR "%s:driver_sysfs_add(%s) failed\n",

            __func__, dev_name(dev));

        goto probe_failed;

    }

 

    if (dev->bus->probe) {

        ret = dev->bus->probe(dev);

        if (ret)

            goto probe_failed;

    } else if (drv->probe) {

        ret = drv->probe(dev);

        if (ret)

            goto probe_failed;

    }

 

    driver_bound(dev);

    ret = 1;

    pr_debug("bus: '%s': %s:bound device %s to driver %s\n",

         drv->bus->name, __func__, dev_name(dev), drv->name);

    goto done;

 

probe_failed:

    devres_release_all(dev);

    driver_sysfs_remove(dev);

    dev->driver = NULL;

 

    if (ret != -ENODEV && ret != -ENXIO) {

        /* driver matched but the probefailed */

        printk(KERN_WARNING

         "%s: probe of %s failed witherror %d\n",

         drv->name, dev_name(dev), ret);

    } else {

        pr_debug("%s: probe of%s rejects match %d\n",

         drv->name, dev_name(dev), ret);

    }

    /*

     * Ignore errors returned by->probe so that the next driver can try

     * its luck.

     */

    ret = 0;

done:

    atomic_dec(&probe_count);

    wake_up(&probe_waitqueue);

    return ret;

}

module_init(my_device_init)

extern struct device my_bus;

extern struct bus_typemy_bus_type;

 

/* Why need this ?*/

static void my_dev_release(struct device *dev)

{

   

}

 

struct devicemy_dev = {

    .bus = &my_bus_type,

    .parent = &my_bus,

    .release = my_dev_release,

};

 

/*

 * Export a simple attribute.

 */

static ssize_tmydev_show(struct device *dev, char *buf)

{

    return sprintf(buf, "%s\n", "This is mydevice!");

}

 

staticDEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);

 

static int __initmy_device_init(void)

{

    int ret = 0;

       

        /* 初始化设备 */

    strncpy(my_dev.bus_id, "my_dev", BUS_ID_SIZE);

       

        /*注册设备*/

    device_register(&my_dev);

       

    /*创建属性文件*/

    device_create_file(&my_dev, &dev_attr_dev);

   

    return ret;   

 

}

 

static void my_device_exit(void)

{

    device_unregister(&my_dev);

}

 

module_init(my_device_init);

module_exit(my_device_exit);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值