总线

总线是处理器和一个或多个设备之间的通信。在设备模型中,所有的设备都通过总线相连。

总线可以相互插入,比如一个USB控制器通常是一个PCI设备。

设备模型展示了总线和他们所控制的设备时间的关系。

1. bus_type 结构用来表示总线:

    struct bus_type {
        const char        *name;                 //总线名称
        struct bus_attribute    *bus_attrs;      //总线属性
        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 bus_type_private *p;
    };

每个总线都会有自己的子系统,可以在sys/bus/*下查看。

2. 总线的注册和注销:bus_register()/ bus_unregister().

    /**
     * bus_register - register a bus with the system.
     * @bus: bus.
     *
     * Once we have that, we registered the bus with the kobject
     * infrastructure, then register the children subsystems it has:
     * the devices and drivers that belong to the bus.
     */
    int bus_register(struct bus_type *bus)
    {
        int retval;
        struct bus_type_private *priv;
        priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
        if (!priv)
            return -ENOMEM;
        priv->bus = bus;
        bus->p = priv;
        BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
        retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);   //赋值总线目录名字。
        if (retval)
            goto out;
        priv->subsys.kobj.kset = bus_kset;
        priv->subsys.kobj.ktype = &bus_ktype;
        priv->drivers_autoprobe = 1;
        retval = kset_register(&priv->subsys);  //设置子系统的名字,即总线目录名字
        if (retval)
            goto out;
        retval = bus_create_file(bus, &bus_attr_uevent); //创建总线文件
        if (retval)
            goto bus_uevent_fail;
        priv->devices_kset = kset_create_and_add("devices", NULL,   //创建设备目录
                             &priv->subsys.kobj);
        if (!priv->devices_kset) {
            retval = -ENOMEM;
            goto bus_devices_fail;
        }
        priv->drivers_kset = kset_create_and_add("drivers", NULL,   //创建驱动目录
                             &priv->subsys.kobj);
        if (!priv->drivers_kset) {
            retval = -ENOMEM;
            goto bus_drivers_fail;
        }
        klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); //加入列表,对个设备时使用列表。
        klist_init(&priv->klist_drivers, NULL, NULL);
        retval = add_probe_files(bus);   //增加probe文件
        if (retval)
            goto bus_probe_files_fail;
        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);
        kfree(bus->p);
    out:
        bus->p = NULL;
        return retval;
    }
    EXPORT_SYMBOL_GPL(bus_register);

    /**
     * bus_unregister - remove a bus from the system
     * @bus: bus.
     *
     * Unregister the child subsystems and the bus itself.
     * Finally, we call bus_put() to release the refcount
     */
    void bus_unregister(struct bus_type *bus)
    {
        pr_debug("bus: '%s': unregistering\n", bus->name);
        bus_remove_attrs(bus);
        remove_probe_files(bus);
        kset_unregister(bus->p->drivers_kset);
        kset_unregister(bus->p->devices_kset);
        bus_remove_file(bus, &bus_attr_uevent);
        kset_unregister(&bus->p->subsys);
        kfree(bus->p);
        bus->p = NULL;
    }
    EXPORT_SYMBOL_GPL(bus_unregister);

注册完后,就可以到sys/bus/下查看总线情况。

3. 对设备和驱动程序的操作。

如果要编写总线层代码,就会对注册到总线的所有设备和驱动程序执行某些操作。

可以使用 bus_for_each_dev() 和 bus_for_each_drv():

    /**
     * bus_for_each_dev - device iterator.
     * @bus: bus type.
     * @start: device to start iterating from.
     * @data: data for the callback.
     * @fn: function to be called for each device.
     *
     * Iterate over @bus's list of devices, and call @fn for each,
     * passing it @data. If @start is not NULL, we use that device to
     * begin iterating from.
     *
     * We check the return of @fn each time. If it returns anything
     * other than 0, we break out and return that value.
     *
     * NOTE: The device that returns a non-zero value is not retained
     * in any way, nor is its refcount incremented. If the caller needs
     * to retain this data, it should do so, and increment the reference
     * count in the supplied callback.
     */
    int bus_for_each_dev(struct bus_type *bus, struct device *start,   //若果start=null,则从总线的第一个设备开始操作
             void *data, int (*fn)(struct device *, void *))     //fn---将对总线上设备进行操作的函数。
    {
        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;
    }
    EXPORT_SYMBOL_GPL(bus_for_each_dev);

    /**
     * bus_for_each_drv - driver iterator
     * @bus: bus we're dealing with.
     * @start: driver to start iterating on.
     * @data: data to pass to the callback.
     * @fn: function to call for each driver.
     *
     * This is nearly identical to the device iterator above.
     * We iterate over each driver that belongs to @bus, and call
     * @fn for each. If @fn returns anything but 0, we break out
     * and return it. If @start is not NULL, we use it as the head
     * of the list.
     *
     * NOTE: we don't return the driver that returns a non-zero
     * value, nor do we leave the reference count incremented for that
     * driver. If the caller needs to know that info, it must set it
     * in the callback. It must also be sure to increment the refcount
     * so it doesn't disappear before returning to the caller.
     */
    int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, //从start处驱动开始。
             void *data, int (*fn)(struct device_driver *, void *))
    {
        struct klist_iter i;
        struct device_driver *drv;
        int error = 0;
        if (!bus)
            return -EINVAL;
        klist_iter_init_node(&bus->p->klist_drivers, &i,
                 start ? &start->p->knode_bus : NULL);
        while ((drv = next_driver(&i)) && !error)
            error = fn(drv, data);
        klist_iter_exit(&i);
        return error;
    }
    EXPORT_SYMBOL_GPL(bus_for_each_drv);



4. 总线属性:bus_attribute

    struct bus_attribute {
        struct attribute    attr;
        ssize_t (*show)(struct bus_type *bus, char *buf);
        ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
    };

linux设备模型在每一层都提供了添加属性的函数,总线层也不例外。

bus_attribute类型也有2个函数用来显示和设置总线属性值。

a. 有一个常用的宏,BUS_ATTR:

    #define BUS_ATTR(_name, _mode, _show, _store)    \
    struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)

b. 创建或删除总线的任何属性,都需要显示调用 bus_create_file()/ bus_remove_file():


    int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
    {
        int error;
        if (bus_get(bus)) {
            error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
            bus_put(bus);
        } else
            error = -EINVAL;
        return error;
    }
    EXPORT_SYMBOL_GPL(bus_create_file);
    void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
    {
        if (bus_get(bus)) {
            sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);
            bus_put(bus);
        }
    }
    EXPORT_SYMBOL_GPL(bus_remove_file);


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值