学习《Linux设备模型浅析之设备篇》笔记(三)

来源:互联网 发布:linux如何查看ftp密码 编辑:程序博客网 时间:2024/05/11 00:06

文件/drivers/base/core.c

static struct kobject *get_device_parent(struct device *dev,

                                         struct device *parent)
{
        if (dev->class) {
                static DEFINE_MUTEX(gdp_mutex);
                struct kobject *kobj = NULL;
                struct kobject *parent_kobj;
                struct kobject *k;

#ifdef CONFIG_BLOCK
                /* block disks show up in /sys/block */
                if (sysfs_deprecated && dev->class == &block_class) {
                        if (parent && parent->class == &block_class)
                                return &parent->kobj;
                        return &block_class.p->subsys.kobj;
                }
#endif

                /*
                 * 如果没有parent, 就放在"/sys/devices/virtual"下.
                 * Class-devices会把一个非class-device作为parent, 放在
                 * 一个"glue"目录下避免命名空间冲突.
                 */
                if (parent == NULL)
                        parent_kobj = virtual_device_parent(dev);
                else if (parent->class && !dev->class->ns_type)
                        return &parent->kobj;
                else
                        parent_kobj = &parent->kobj;

                mutex_lock(&gdp_mutex);

                /* 在parent下找到我们的class目录,并引用它*/
                spin_lock(&dev->class->p->glue_dirs.list_lock);
                list_for_each_entry(k, &dev->class->p->glue_dirs.list, entry)
                        if (k->parent == parent_kobj) {
                                kobj = kobject_get(k);
                                break;
                        }
                spin_unlock(&dev->class->p->glue_dirs.list_lock);
                if (kobj) {
                        mutex_unlock(&gdp_mutex);
                        return kobj;
                }

                /* 或在父设备下创建一个新的class目录 */
                k = class_dir_create_and_add(dev->class, parent_kobj);
                /* 不要为这个简单的“glue”目录发出uevent*/
                mutex_unlock(&gdp_mutex);
                return k;
        }

        /* 子系统可以为他们的设备指定一个默认的root目录 */
        if (!parent && dev->bus && dev->bus->dev_root)
                return &dev->bus->dev_root->kobj;

        if (parent)
                return &parent->kobj;
        return NULL;

}


文件/drivers/base/core.c

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 and does not need symlinks */
        if (sysfs_deprecated && dev->class == &block_class)
                return 0;
#endif

        /* link in the class directory pointing 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;
}

文件/drivers/base/bus.c

/**
 * bus_probe_device - 为一个新设备探测驱动
 * @dev: 要探测的设备
 *
 * - 当总线允许的时候,自动探测驱动
 */
void bus_probe_device(struct device *dev)
{
        struct bus_type *bus = dev->bus;
        struct subsys_interface *sif;
        int ret;

        if (!bus)
                return;

        if (bus->p->drivers_autoprobe) {
                ret = device_attach(dev);
                WARN_ON(ret < 0);
        }

        mutex_lock(&bus->p->mutex);
        list_for_each_entry(sif, &bus->p->interfaces, node)
                if (sif->add_dev)
                        sif->add_dev(dev, sif);
        mutex_unlock(&bus->p->mutex);
}


文件/drivers/base/dd.c

/**
 * device_attach - 尝试把一个设备粘附上一个驱动
 * @dev: device.
 *
 * 步进遍历该总线拥有的驱动list,并为每一对调用driver_probe_device()。
 *  如果找到了一个兼容对,就break并return.
 *
 * 如果一个设备绑定到了一个驱动上就返回1;
 * 没有没有匹配的驱动找到,就返回0;
 * 如果设备未注册返回-ENODEV
 *
 * 当其实为一个USB接口调用,必须要有一个@dev->parent的lock。
 */
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 {
                ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
                pm_request_idle(dev);
        }
out_unlock:
        device_unlock(dev);
        return ret;
}


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


文件/drivers/base/dd.c

/**
 * driver_probe_device - 尝试把设备和驱动绑定在一起
 * @drv: 要绑定到设备的驱动
 * @dev: 尝试绑定到驱动上的设备
 *
 * 如果设备未注册,该方法返回-ENODEV,
 * 如果设备绑定成功返回1,否则返回0
 *
 * 该方法必须在@dev被锁的情况下调用。 如果是为USB接口调用,
 * @dev->parent也必须被锁。
 */
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
        int ret = 0;

        if (!device_is_registered(dev))
                return -ENODEV;

        pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
                 drv->bus->name, __func__, dev_name(dev), drv->name);

        pm_runtime_barrier(dev);
        ret = really_probe(dev, drv);
        pm_request_idle(dev);

        return ret;
}


文件/drivers/base/platform.c

/**
 * platform_match - 绑定platform设备到platform驱动
 * @dev: device.
 * @drv: driver.
 *
 * Platform 设备ID被认定为这样编码:
 * "<name><instance>", <name>是一个设备类型的短描述符,
 *  如"pci"或"floppy",而<instance>是该设备的枚举实例,如'0'
 * 或'42'.  驱动ID是简单的"<name>"。 所以提取platform_device
 * 结构的<name>并拿他和驱动的名称比较。返回它们是否匹配。
 */
static int platform_match(struct device *dev, struct device_driver *drv)
{
        struct platform_device *pdev = to_platform_device(dev);
        struct platform_driver *pdrv = to_platform_driver(drv);

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

        /* Then try ACPI style match */
        if (acpi_driver_match_device(dev, drv))
                return 1;

        /* Then try to match against the id table */
        if (pdrv->id_table)
                return platform_match_id(pdrv->id_table, pdev) != NULL;

        /* fall-back to driver name match */
        return (strcmp(pdev->name, drv->name) == 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));
        WARN_ON(!list_empty(&dev->devres_head));

        dev->driver = drv;

        /* 如果使用pinctrl, 在探测前绑定pins */
        ret = pinctrl_bind_pins(dev);
        if (ret)
                goto probe_failed;

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

0 0
原创粉丝点击