zoukankan      html  css  js  c++  java
  • linux下的总线设备驱动模型分析

    =============================================================================================================
    sysfs文件系统结构分析:
    =============================================================================================================
    这里仅对一个指定bus及其驱动进行结构分析:(pci)
    总线及驱动:
    sys-bus-pci
    pci
    ├── devices        PCI上注册的device列表
    │   ├── 0000:00:00.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:00.0
    │   ├── 0000:00:02.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:02.0
    │   ├── 0000:00:16.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:16.0
    │   ├── 0000:00:1a.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1a.0
    │   ├── 0000:00:1b.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1b.0
    │   ├── 0000:00:1c.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1c.0
    │   ├── 0000:00:1c.5 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1c.5
    │   ├── 0000:00:1d.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1d.0
    │   ├── 0000:00:1f.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1f.0
    │   ├── 0000:00:1f.2 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1f.2
    │   ├── 0000:00:1f.3 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1f.3
    │   └── 0000:02:00.0 -> http://www.cnblogs.com/../devices/pci0000:00/0000:00:1c.5/0000:02:00.0
    ├── drivers        PCI上注册的driver列表
    │   ├── ehci_hcd    一个特定的设备驱动(能同时支持两个设备)
    │       ├── 0000:00:1a.0 -> http://www.cnblogs.com/http://www.cnblogs.com/devices/pci0000:00/0000:00:1a.0       支持的设备列表
    │       ├── 0000:00:1d.0 -> http://www.cnblogs.com/http://www.cnblogs.com/devices/pci0000:00/0000:00:1d.0
    │       ├── bind
    │       ├── module -> http://www.cnblogs.com/http://www.cnblogs.com/module/ehci_hcd                                所属的模块(THIS_MODULE)
    │       ├── new_id                                                                其余均为此驱动相应的ATTR
    │       ├── remove_id
    │       ├── uevent
    │       └── unbind
    │────────── drivers_autoprobe                                                    PCI相应的ATTR
    │────────── drivers_probe
    │────────── rescan
    │────────── resource_alignment
    │────────── uevent

    真实的设备:
    sys-device-pci0000:00-0000:00:00.0
    00-0000:00:00.0
    ├── broken_parity_status
    ├── class
    ├── config
    ├── consistent_dma_mask_bits
    ├── device
    ├── dma_mask_bits
    ├── enable
    ├── irq
    ├── local_cpulist
    ├── local_cpus
    ├── modalias
    ├── msi_bus
    ├── numa_node
    ├── power
    │   ├── autosuspend_delay_ms
    │   ├── control
    │   ├── runtime_active_time
    │   ├── runtime_status
    │   ├── runtime_suspended_time
    │   ├── wakeup
    │   ├── wakeup_active
    │   ├── wakeup_active_count
    │   ├── wakeup_count
    │   ├── wakeup_hit_count
    │   ├── wakeup_last_time_ms
    │   ├── wakeup_max_time_ms
    │   └── wakeup_total_time_ms
    ├── remove
    ├── rescan
    ├── resource
    ├── subsystem -> http://www.cnblogs.com/../bus/pci
    ├── subsystem_device
    ├── subsystem_vendor
    ├── uevent
    └── vendor


    除了上面bus/driver/device的链接关系外,注意到还有一个driver与module的依赖关系:
    system/module/ehci_hcd
    module
    ├── drivers
    │   └── pci:ehci_hcd -> http://www.cnblogs.com/../bus/pci/drivers/ehci_hcd
    └── parameters
        ├── hird
        ├── ignore_oc
        ├── log2_irq_thresh
        └── park

    上面的结构对于理解设备驱动模型具有提纲挈领的作用.整个总线/驱动/设备的注册过程就是一个建立对应sysfs的过程

    =============================================================================================================
    设备驱动模型属性回调流程分析:
    =============================================================================================================
    kobject: 在sysfs中代表一个独立的目录

    struct kobject {
        const char        *name;                    对象名
        struct list_head    entry;                挂接到kset列表上的钩子
        struct kobject        *parent;            父对象
        struct kset        *kset;                    所属的kset
        struct kobj_type    *ktype;                见下
        struct sysfs_dirent    *sd;                见下
        struct kref        kref;                    引用计数
        unsigned int state_initialized:1;        是否初始化
        unsigned int state_in_sysfs:1;            是否在sysfs中
        unsigned int state_add_uevent_sent:1;    是否发送相关设备增加/移除事件
        unsigned int state_remove_uevent_sent:1;
        unsigned int uevent_suppress:1;            是否丢弃事件(1为丢弃)
    };
    kobj_type: 代表一个kobject的相关操作
    struct kobj_type {
        void (*release)(struct kobject *kobj);    释放相关kobject
        const struct sysfs_ops *sysfs_ops;        文件系统接口
        struct attribute **default_attrs;        默认attrs
        const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
        const void *(*namespace)(struct kobject *kobj);
    };

    如:
    总线kobject类型
    static struct kobj_type bus_ktype = {
        .sysfs_ops    = &bus_sysfs_ops,
    };
    定义:
    static const struct sysfs_ops bus_sysfs_ops = {
        .show    = bus_attr_show,
        .store    = bus_attr_store,
    };
    具体的函数
    static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
                     char *buf)
    {
        struct bus_attribute *bus_attr = to_bus_attr(attr);
        struct subsys_private *subsys_priv = to_subsys_private(kobj);
        ssize_t ret = 0;

        if (bus_attr->show)
            ret = bus_attr->show(subsys_priv->bus, buf);
        return ret;
    }


    驱动kobject类型:
    static struct kobj_type driver_ktype = {
        .sysfs_ops    = &driver_sysfs_ops,
        .release    = driver_release,
    };
    定义:
    static const struct sysfs_ops driver_sysfs_ops = {
        .show    = drv_attr_show,
        .store    = drv_attr_store,
    };
    具体的函数:
    static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,
                     char *buf)
    {
        struct driver_attribute *drv_attr = to_drv_attr(attr);
        struct driver_private *drv_priv = to_driver(kobj);
        ssize_t ret = -EIO;

        if (drv_attr->show)
            ret = drv_attr->show(drv_priv->driver, buf);
        return ret;
    }

    设备的kobject类型
    static struct kobj_type device_ktype = {
        .release    = device_release,
        .sysfs_ops    = &dev_sysfs_ops,
        .namespace    = device_namespace,
    };
    定义:
    static const struct sysfs_ops dev_sysfs_ops = {
        .show    = dev_attr_show,
        .store    = dev_attr_store,
    };
    具体的函数:
    static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
                     char *buf)
    {
        struct device_attribute *dev_attr = to_dev_attr(attr);
        struct device *dev = kobj_to_dev(kobj);
        ssize_t ret = -EIO;

        if (dev_attr->show)
            ret = dev_attr->show(dev, dev_attr, buf);
        if (ret >= (ssize_t)PAGE_SIZE) {
            print_symbol("dev_attr_show: %s returned bad count\n",
                    (unsigned long)dev_attr->show);
        }
        return ret;
    }

    struct sysfs_ops {                桥接文件系统的接口(namespace)
        ssize_t    (*show)(struct kobject *, struct attribute *,char *);
        ssize_t    (*store)(struct kobject *,struct attribute *,const char *, size_t);
        const void *(*namespace)(struct kobject *, const struct attribute *);
    };

    到这里为止,总结一下关于attribute, sysfs_ops, kobj_type的关系:
    拿bus作为示例,
    1.首先声明一个总线属性:
    static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);   (bus.c)
    这个属性作为bus的uevent属性,并提供的一个写接口(未提供读接口)
    2.在创建对应文件的时候,附属attr于sysfs系统的sysfs_dirent的结构中:
    sd->s_attr.attr = (void *)attr;   (file.c)
    同时这里bus下产生一个uevent文件
    3.在用户空间产生对对应的属性文件的读写操作,like: echo add > uevent/echo 1 > autoprobe等等
    4.由文件系统发起调用,从sysfs_ops中进入,主要根据其所在对应目录的kobj的kobj_type的类型来寻找设备模型层的接口
        kobj    =>    driver_ktype      =》 driver_sysfs_ops        => driver_attribute        =>    实际的函数
        kobj    =>    bus_ktype        =》 bus_attr_show        => bus_attribute        =>  实际的函数
    5.回调成功.


    =============================================================================================================
    总线注册流程:(结合PCI总线进行分析)
    =============================================================================================================
    在整个设备驱动流程中,总线必定是第一个注册到系统中去的
    总线对象
    struct bus_type {
        const char        *name;                                总线名称
        const char        *dev_name;                            用于设备名称前缀
        struct device        *dev_root;                        总线设备的根设备
        struct bus_attribute    *bus_attrs;                    默认的总线/设备/驱动属性(强制性加入sysfs)
        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 iommu_ops *iommu_ops;

        struct subsys_private *p;
    };

    总线对象的一些私有数据
    struct subsys_private {
        struct kset subsys;            
        //核心数据:描述这个节点的子系统                    
        //subsys->kobj.kset 为本总线所属于的总线集合,由系统来统计的一个全局变量(bus_kset,这个数组域由系统在init.c中初始化)
        //subsys->kobj.ktype 为bus_ktype, 代表我是一条总线
        struct kset *devices_kset;                        devices链
        struct list_head interfaces;                    
        struct mutex mutex;

        struct kset *drivers_kset;                        driver链
        struct klist klist_devices;                        从devices链中增加或移除device结构的方法
        struct klist klist_drivers;                        从drivers链中增加或移除driver结构的方法
        struct blocking_notifier_head bus_notifier;        通知事件链
        unsigned int drivers_autoprobe:1;                自动匹配
        struct bus_type *bus;                            指向包含自己的总线类型

        struct kset glue_dirs;                            防止命名空间冲突?
        struct class *class;
    };

    详细注册流程如下:(关于kset的创建注册等稍后讨论)
    int __bus_register(struct bus_type *bus, struct lock_class_key *key)
    {
        int retval;
        struct subsys_private *priv;
        //Alloc a new subsys_private struct.
        priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
        if (!priv)
            return -ENOMEM;

        //Connect each other
        priv->bus = bus;
        bus->p = priv;
        
        //Init its rw semaphore and set notifier head to null.
        BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

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

        //For all buses store, created by inic.c
        priv->subsys.kobj.kset = bus_kset;
        //For all buses kobj type (bus_ktype). some common ops for bus. For Attr operation.
        priv->subsys.kobj.ktype = &bus_ktype;
        //Probe auto.
        priv->drivers_autoprobe = 1;

        //For kset and its kobj init and register.[Discuss later]
        retval = kset_register(&priv->subsys);
        if (retval)
            goto out;

        //Add attr file of this bus. uevent, User can be operate it from cmd like echo add > uevent and so on.
        retval = bus_create_file(bus, &bus_attr_uevent);
        if (retval)
            goto bus_uevent_fail;

        //Create kset to store devices for bus and add to the sysfs.(like the dir devices in each bus)   /sys/i2c/devices/xx
        priv->devices_kset = kset_create_and_add("devices", NULL,
                             &priv->subsys.kobj);
        if (!priv->devices_kset) {
            retval = -ENOMEM;
            goto bus_devices_fail;
        }

        //Create kset to store drivers for bus and add to the sysfs.(like the dir drivers in each bus)
        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);
        //Init mutex of it.
        __mutex_init(&priv->mutex, "subsys mutex", key);
        //Init the method how to get and release node(device); device_private is set in parent/bus/driver
        klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
        klist_init(&priv->klist_drivers, NULL, NULL);

        //Create probe and autoprobe action.
        retval = add_probe_files(bus);
        if (retval)
            goto bus_probe_files_fail;

        //Create other own files for special control.Default ATTR for bus.
        retval = bus_add_attrs(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);
    out:
        kfree(bus->p);
        bus->p = NULL;
        return retval;
    }
    ---------------------------------------------------------------------------------------------------------------------
    pci 总线注册流程 (pci.h/pci-driver.c)
    总线类型声明:
    struct bus_type pci_bus_type = {
        .name        = "pci",
        .match        = pci_bus_match,
        .uevent        = pci_uevent,
        .probe        = pci_device_probe,
        .remove        = pci_device_remove,
        .shutdown    = pci_device_shutdown,
        .dev_attrs    = pci_dev_attrs,
        .bus_attrs    = pci_bus_attrs,                
        .drv_attrs    = pci_drv_attrs,
        .pm        = PCI_PM_OPS_PTR,
    };

    struct device_attribute pci_dev_attrs[] = {
        __ATTR_RO(resource),
        __ATTR_RO(vendor),
        __ATTR_RO(device),
        __ATTR_RO(subsystem_vendor),
        __ATTR_RO(subsystem_device),
        __ATTR_RO(class),
        ...
        
    struct bus_attribute pci_bus_attrs[] = {
        __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store),
        __ATTR_NULL
    };

    static struct driver_attribute pci_drv_attrs[] = {
        __ATTR(new_id, S_IWUSR, NULL, store_new_id),
        __ATTR(remove_id, S_IWUSR, NULL, store_remove_id),
        __ATTR_NULL,
    };
    这里三个属性分别对应sysfs里的相关属性文件, 下面不再描述属性相关的东西.

    static int __init pci_driver_init(void)
    {
        return bus_register(&pci_bus_type);
    }
    注册到系统中就可以完事了.

    下面是相关注册函数的作用及规则说明:
    在写某总线下的驱动时,必须熟悉相关总线的match流程,等等。

    pci:
    static int pci_bus_match(struct device *dev, struct device_driver *drv)
    {
        struct pci_dev *pci_dev = to_pci_dev(dev);
        struct pci_driver *pci_drv = to_pci_driver(drv);
        const struct pci_device_id *found_id;

        found_id = pci_match_device(pci_drv, pci_dev);
        if (found_id)
            return 1;

        return 0;
    }
    spi:
    static int spi_match_device(struct device *dev, struct device_driver *drv)
    {
        const struct spi_device    *spi = to_spi_device(dev);
        const struct spi_driver    *sdrv = to_spi_driver(drv);

        /* Attempt an OF style match */
        if (of_driver_match_device(dev, drv))
            return 1;

        if (sdrv->id_table)
            return !!spi_match_id(sdrv->id_table, spi);

        return strcmp(spi->modalias, drv->name) == 0;
    }
    基本的原理是相同的,获取对应的设备和驱动的结构,并进行匹配(iptable/name).
    后面会详细描述SPI总线的匹配规则.

    =============================================================================================================
    驱动注册流程:(spi)
    =============================================================================================================
    struct device_driver {
        const char        *name;                                                    驱动名称
        struct bus_type        *bus;                                                挂接的总线类型(spi/pci...)
        struct module        *owner;                                                THIS_MODULE
        //The modules' name.
        const char        *mod_name;    /* used for built-in modules */                对于内建模块的名称
        bool suppress_bind_attrs;    /* disables bind/unbind via sysfs */        是否启用属性控制bind/unbind操作
        const struct of_device_id    *of_match_table;                            匹配的设备列表

        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;                                            挂接到目标总线上的连接件
        //The module kobj whitch handle this driver.
        struct module_kobject *mkobj;                                            module handle
        struct device_driver *driver;                                            驱动
    };

    驱动注册:
    int driver_register(struct device_driver *drv)
    {
        int ret;
        struct device_driver *other;

        //三个接口函数,bus与driver都要有
        if ((drv->bus->probe && drv->probe) ||
            (drv->bus->remove && drv->remove) ||
            (drv->bus->shutdown && drv->shutdown))
            printk(KERN_WARNING "Driver '%s' needs updating - please use "
                "bus_type methods\n", drv->name);

        //Driver name can not be used for twice.
        //查找bus是否存在同名driver(在总线的驱动列表中查找kobj名称相同的驱动)
        other = driver_find(drv->name, drv->bus);
        if (other) {
            printk(KERN_ERR "Error: Driver '%s' is already registered, "
                "aborting...\n", drv->name);
            return -EBUSY;
        }

        //Add the driver to the bus.
        ret = bus_add_driver(drv);
        if (ret)
            return ret;
        ret = driver_add_groups(drv, drv->groups);
        if (ret) {
            bus_remove_driver(drv);
            return ret;
        }
        //热插拔事件
        kobject_uevent(&drv->p->kobj, KOBJ_ADD);

        return ret;
    }

    增加driver到bus上去(核心):
    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;

        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv) {
            error = -ENOMEM;
            goto out_put_bus;
        }

        //Create klist for devices.
        klist_init(&priv->klist_devices, NULL, NULL);
        priv->driver = drv;
        drv->p = priv;
        //Belong to bus driver kset.
        //反向挂接
        priv->kobj.kset = bus->p->drivers_kset;

        //Init its kobj and add it to sysfs.
        error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
                         "%s", drv->name);
        if (error)
            goto out_unregister;

        //Auto probe, driver to find device.
        //匹配设备
        if (drv->bus->p->drivers_autoprobe) {
            error = driver_attach(drv);
            if (error)
                goto out_unregister;
        }

        //Add driver to the list of bus.
        //正向挂接
        klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
        //Connect driver to the module.
        //驱动与模块挂接
        module_add_driver(drv->owner, drv);

        //Create event attr file for driver
        //创建uevent属性
        error = driver_create_file(drv, &driver_attr_uevent);
        if (error) {
            printk(KERN_ERR "%s: uevent attr (%s) failed\n",
                __func__, drv->name);
        }
        //创建bus上的默认属性
        //Common drv attrs for bus add to the driver.
        error = driver_add_attrs(bus, drv);
        if (error) {
            /* How the hell do we get out of this pickle? Give up */
            printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
                __func__, drv->name);
        }

        //Disable bind/unbind via sysfs. Wheather create bind or unbind files.
        //创建bind/unbind文件
        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;
    }

    核心在这一行:【取决于bus是否autoprobe】
    error = driver_attach(drv);
    跟踪进去:
    int driver_attach(struct device_driver *drv)
    {
        return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
    }
    int bus_for_each_dev(struct bus_type *bus, struct device *start,
                 void *data, int (*fn)(struct device *, void *))
    {
        struct klist_iter i;
        struct device *dev;
        int error = 0;

        if (!bus)
            return -EINVAL;

        klist_iter_init_node(&bus->p->klist_devices, &i,
                     (start ? &start->p->knode_bus : NULL));
        while ((dev = next_device(&i)) && !error)
            error = fn(dev, data);
        klist_iter_exit(&i);
        return error;
    }
    真正的匹配在这一行
    error = fn(dev, data);
    真正的核心匹配函数:
    static int __driver_attach(struct device *dev, void *data)
    {
        struct device_driver *drv = data;

        //总线优先调用match函数进行匹配,有提前否决权
        if (!driver_match_device(drv, dev))
            return 0;

        //Get matched.  to check the matched device must have no driver to driver it.
        if (dev->parent)    /* Needed for USB */
            device_lock(dev->parent);
        device_lock(dev);
        if (!dev->driver)
            driver_probe_device(drv, dev);        //此处注意如果当前设备已经有驱动配对,则直接返回,此设备不会与驱动挂接,也就是说设备的驱动不会被替代。
        device_unlock(dev);
        if (dev->parent)
            device_unlock(dev->parent);

        return 0;
    }


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

        //Connectted 设备区驱动挂接
        dev->driver = drv;
        //Add the device name in driver, like /sys/bus/i2c/drivers/xxx
        if (driver_sysfs_add(dev)) {
            printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
                __func__, dev_name(dev));
            goto probe_failed;
        }

        //Bus probe first.
        //Bus的probe函数进行匹配
        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;
        dev_set_drvdata(dev, NULL);

        if (ret == -EPROBE_DEFER) {
            /* Driver requested deferred probing */
            dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
            driver_deferred_probe_add(dev);
        } else if (ret != -ENODEV && ret != -ENXIO) {
            /* driver matched but the probe failed */
            printk(KERN_WARNING
                   "%s: probe of %s failed with error %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;
    }

    至此,整个驱动的注册过程总结如下:
    1. 检测总线上没有同名驱动
    2. 添加驱动到总线
        初始化驱动私有数据域(设备链表等,并与driver挂接)
        添加到sysfs系统中
        进行匹配
            遍历总线上的所有设备(不管是否在中途匹配到设备)
            首先调用bus的match函数,进行匹配(两个参数:dev/driver)
            匹配成功后,确定设备已经在sysfs中正常注册(存在)
            挂接dev的driver,添加dev到driver的sysfs下
            调用bus的probe函数,检查设备与bus的匹配性(一个参数:dev)
            否则调用driver的probe函数(一般走此条路)进行匹配
                    匹配成功:添加dev到driver的dev链表中
                    匹配失败:dev->driver = NULL;转失败处理流程
        添加到bus的driver列表
        驱动与所属模块挂接
        创建uevent/bus默认driver属性/bind(unbind)属性等
    3. 触发热插拔事件


    =============================================================================================================
    设备注册流程
    =============================================================================================================
    类的相关知识:class
    -------------------------------------------------------------------------------------------------------------
    sys
    class
    vc
    ├── vcs -> http://www.cnblogs.com/devices/virtual/vc/vcs
    ├── vcs1 -> http://www.cnblogs.com/devices/virtual/vc/vcs1
    ├── vcs2 -> http://www.cnblogs.com/devices/virtual/vc/vcs2
    ├── vcs3 -> http://www.cnblogs.com/devices/virtual/vc/vcs3
    ├── vcs4 -> http://www.cnblogs.com/devices/virtual/vc/vcs4

    在注册字符设备的时候通常使用的两句代码:
    wch_class = class_create(THIS_MODULE, "wch_class");
    wch_device = device_create(wch_class, NULL, dev, NULL, "wch");
    这样就会为为指定dev在/dev/目录下创建对应的设备节点以及在/sys/class下创建对应的类
    深入类的创建相关代码:
    #define class_create(owner, name)        \
    ({                        \
        static struct lock_class_key __key;    \
        __class_create(owner, name, &__key);    \
    })

    核心在类的注册函数:__class_register
    int __class_register(struct class *cls, struct lock_class_key *key)
    {
        struct subsys_private *cp;
        int error;
        //初始化一个子系统私有数据(注:这里只初始化了它的设备列表,没有驱动列表,变相说明只有设备属于类,见上图)
        cp = kzalloc(sizeof(*cp), GFP_KERNEL);
        klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
        INIT_LIST_HEAD(&cp->interfaces);
        kset_init(&cp->glue_dirs);        //初始化绿名目录(用于无主设备)
        __mutex_init(&cp->mutex, "subsys mutex", key);
        //初始化类名到目录kobj
        error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);

        //设置kobj:默认使用/sys/dev这个目录的kobj[后面在此类下建device,就相当于在/sys/dev下建立对应的device]
        /* set the default /sys/dev directory for devices of this class */
        if (!cls->dev_kobj)
            cls->dev_kobj = sysfs_dev_char_kobj;

    #if defined(CONFIG_BLOCK)
        /* let the block class directory show up in the root of sysfs */
        if (!sysfs_deprecated || cls != &block_class)
            cp->subsys.kobj.kset = class_kset;
    #else
        cp->subsys.kobj.kset = class_kset;
    #endif
        cp->subsys.kobj.ktype = &class_ktype;
        //挂接
        cp->class = cls;
        cls->p = cp;
        //注册kset到sysfs中
        error = kset_register(&cp->subsys);
        if (error) {
            kfree(cp);
            return error;
        }
        error = add_class_attrs(class_get(cls));
        class_put(cls);
        return error;
    }

    device_create函数:
    @设备类
    @父设备
    @设备号
    @回调数据
    @设备名称/dev/xxname
    dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);

    struct device *device_create_vargs(struct class *class, struct device *parent,
                       dev_t devt, void *drvdata, const char *fmt,
                       va_list args)
    {
        struct device *dev = NULL;
        int retval = -ENODEV;

        if (class == NULL || IS_ERR(class))
            goto error;

        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (!dev) {
            retval = -ENOMEM;
            goto error;
        }

        dev->devt = devt;        //设备号
        dev->class = class;        //设备类
        dev->parent = parent;    //父设备
        dev->release = device_create_release;//释放函数
        dev_set_drvdata(dev, drvdata);//创建并初始化设备的私有数据  dev->p->driver_data = data;

        //设置设备名称
        retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
        if (retval)
            goto error;
        //初始化dev
        //增加设备:回到了device_add函数中去。
        retval = device_register(dev);
        if (retval)
            goto error;

        return dev;

    error:
        put_device(dev);
        return ERR_PTR(retval);
    }
    -------------------------------------------------------------------------------------------------------------
    设备添加:device_add




    =============================================================================================================
    Kobject创建步骤
    =============================================================================================================
    1.分配内存
    kzalloc(sizeof(*kobj), GFP_KERNEL);
    2.初始化所属kobj_type
    kobject_init(kobj, &dynamic_kobj_ktype);
    3.设置父亲kobj及名称,以及加入kobj所属的kset的列表
    kobject_add(kobj, parent, "%s", name);

    三者之间的一些优先级:
    kobj->kset->kobj_type > kobj->kobj_type

    默认属性:
    kobj_type->default_attrs
    添加属性:在指定的kobj目录下创建属性attr.
    sysfs_create_file(kobj, attr)

    软链接:
    sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);

    设备的层次关系:
    device->kobj->parent 等同于 device->parent->kobj



    =============================================================================================================
    Kset的创建与初始化及相关热插拔事件的传递原理
    =============================================================================================================
    首先ket需要kobject的内嵌,任何kset的初始化都包含了kobj的初始化,加上kobj_type,这里的内核驱动模型三要素就全了。
    这个函数是bus用来创建drivers kset 和 devices kset.

    @name: 将作为kobj的名称(也就是sysfs中的目录名称)
    @uevent_ops:NULL
    @parent_kobj:父亲kobj
    struct kset *kset_create_and_add(const char *name,
                     const struct kset_uevent_ops *uevent_ops,
                     struct kobject *parent_kobj)
    {
        struct kset *kset;
        int error;

        kset = kset_create(name, uevent_ops, parent_kobj);
        if (!kset)
            return NULL;
        error = kset_register(kset);
        if (error) {
            kfree(kset);
            return NULL;
        }
        return kset;
    }

    Kset的创建过程:
    static struct kset *kset_create(const char *name,
                    const struct kset_uevent_ops *uevent_ops,
                    struct kobject *parent_kobj)
    {
        struct kset *kset;
        int retval;
        //Alloc kset struct.
        kset = kzalloc(sizeof(*kset), GFP_KERNEL);
        if (!kset)
            return NULL;
        //Set name to kobj.name.
        retval = kobject_set_name(&kset->kobj, name);
        if (retval) {
            kfree(kset);
            return NULL;
        }
        //Handle uevent.[Discuss later]
        kset->uevent_ops = uevent_ops;
        //Set parent kobj.
        kset->kobj.parent = parent_kobj;

        /*
         * The kobject of this kset will have a type of kset_ktype and belong to
         * no kset itself.  That way we can properly free it when it is
         * finished being used.
         */
        //It init to belong no kset and no kset_type.
        kset->kobj.ktype = &kset_ktype;
        kset->kobj.kset = NULL;

        return kset;
    }

    Kset 注册过程:
    int kset_register(struct kset *k)
    {
        int err;

        if (!k)
            return -EINVAL;
        //Init kobj of kset and init ket.
        kset_init(k);
        //Add kobj to sysfs.
        err = kobject_add_internal(&k->kobj);
        if (err)
            return err;
        //Triggle a kobj add event.
        kobject_uevent(&k->kobj, KOBJ_ADD);
        return 0;
    }
    -----------------------------------------------------------------------------------------------------------------
    这里整个过程就结束了,但是里面暗含了一个事件传递链.hotplug,热插拔事件
    int kobject_uevent(struct kobject *kobj, enum kobject_action action)
    {
        return kobject_uevent_env(kobj, action, NULL);
    }

    这里,在驱动模型中device_add方法同样会调用此函数,来进行热插拔的消息通知.
    相关的结构体介绍:
    @kset:顶级kset
    @kobj:发生事件的kobj
    struct kset_uevent_ops {
        int (* const filter)(struct kset *kset, struct kobject *kobj);            过滤相关事件
        const char *(* const name)(struct kset *kset, struct kobject *kobj);    获取子系统名称(bus级别的),如果没有则使用kset中的kobj.name
        int (* const uevent)(struct kset *kset, struct kobject *kobj,            通知事件
                  struct kobj_uevent_env *env);
    };
    这个接口是附在kset上的,用于处理相关热插拔的事件
    struct kset {
        struct list_head list;
        spinlock_t list_lock;
        struct kobject kobj;
        const struct kset_uevent_ops *uevent_ops;
    };
    kset_create(name, uevent_ops, parent_kobj)

    struct kobj_uevent_env {
        char *envp[UEVENT_NUM_ENVP];            事件号
        int envp_idx;
        char buf[UEVENT_BUFFER_SIZE];           事件变量
        int buflen;
    };
    这个相当与一个事件传递的载体.

    具体的传递过程:
    int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                   char *envp_ext[])
    {
        struct kobj_uevent_env *env;
        const char *action_string = kobject_actions[action];
        const char *devpath = NULL;
        const char *subsystem;
        struct kobject *top_kobj;
        struct kset *kset;
        const struct kset_uevent_ops *uevent_ops;
        int i = 0;
        int retval = 0;
    ...
        top_kobj = kobj;
        //Get top kobj.
        while (!top_kobj->kset && top_kobj->parent)
            top_kobj = top_kobj->parent;

        if (!top_kobj->kset) {
            return -EINVAL;
        }

        //Get the top kset.(such as bus)
        kset = top_kobj->kset;
        uevent_ops = kset->uevent_ops;

        //Drop it if the kobj set.
        if (kobj->uevent_suppress) {
            return 0;
        }

        //Filter it, the top kset don't care the event about this kobj.
        if (uevent_ops && uevent_ops->filter)
            if (!uevent_ops->filter(kset, kobj)) {
                return 0;
            }

        //Get subsystem.
        if (uevent_ops && uevent_ops->name)
            subsystem = uevent_ops->name(kset, kobj);
        else    //Use kset's kobj.name.
            subsystem = kobject_name(&kset->kobj);
        if (!subsystem) {
            return 0;
        }

        /* environment buffer */
        env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
        if (!env)
            return -ENOMEM;

        //Get path of cur kobj.    
        devpath = kobject_get_path(kobj, GFP_KERNEL);
        if (!devpath) {
            retval = -ENOENT;
            goto exit;
        }
        增加环境变量ACTION=<action name>
        增加环境变量DEVPATH=<kobj’s path>    
        增加环境变量SUBSYSTEM=<subsystem name>
        增加环境变量kobject_uevent_env中参数envp_ext指定的环境变量。
        /* default keys */
        retval = add_uevent_var(env, "ACTION=%s", action_string);        //Action
        if (retval)
            goto exit;
        retval = add_uevent_var(env, "DEVPATH=%s", devpath);            //Kobj path
        if (retval)
            goto exit;
        retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);        //subsystem name(Top)
        if (retval)
            goto exit;

        /* keys passed in from the caller */
        if (envp_ext) {
            for (i = 0; envp_ext[i]; i++) {
                retval = add_uevent_var(env, "%s", envp_ext[i]);
                if (retval)
                    goto exit;
            }
        }

        //Handled by top set first
        if (uevent_ops && uevent_ops->uevent) {
            retval = uevent_ops->uevent(kset, kobj, env);
            if (retval) {
                goto exit;
            }
        }

        /*
         * Mark "add" and "remove" events in the object to ensure proper
         * events to userspace during automatic cleanup. If the object did
         * send an "add" event, "remove" will automatically generated by
         * the core, if not already done by the caller.
         */
        //Set the current kobj uevent state.
        if (action == KOBJ_ADD)
            kobj->state_add_uevent_sent = 1;
        else if (action == KOBJ_REMOVE)
            kobj->state_remove_uevent_sent = 1;

        mutex_lock(&uevent_sock_mutex);
        /* we will send an event, so request a new sequence number */
        retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);
        if (retval) {
            mutex_unlock(&uevent_sock_mutex);
            goto exit;
        }

        mutex_unlock(&uevent_sock_mutex);

        //
        if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
            char *argv [3];

            argv [0] = uevent_helper;
            argv [1] = (char *)subsystem;
            argv [2] = NULL;
            retval = add_uevent_var(env, "HOME=/");
            if (retval)
                goto exit;
            retval = add_uevent_var(env,
                        "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
            if (retval)
                goto exit;

            retval = call_usermodehelper(argv[0], argv,
                             env->envp, UMH_WAIT_EXEC);
        }

    }

    通过call_usermodehelper一个用户程序uevent_helper(path: a executable file), 并传递相关的参数,事件类型,这样通知就到达了用户空间.




















  • 相关阅读:
    struct tm->time() localtime() gmtime()
    解决VS2013中“This function or variable may be unsafe”的问题
    vm虚拟机三种网络模式
    vulnhub靶机-Raven解题思路
    vulnhub靶机-VulnOs:v2解题思路
    信息收集
    vulnhub靶机-SickOs1.2解题思路
    vulnhub靶机-SickOs解题思路
    vulnhub靶机-SkyTower解题思路
    vulnhub靶场-Stapler解题思路
  • 原文地址:https://www.cnblogs.com/wenhuisun/p/3050022.html
Copyright © 2011-2022 走看看