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), 并传递相关的参数,事件类型,这样通知就到达了用户空间.




















posted @ 2013-04-28 20:36  天地不仁  阅读(2505)  评论(0编辑  收藏  举报