1. 概述

在Linux设备模型中,bus(总线)是一类特殊的设备,它是连接处理器和其它设备之间的通道(channel)。为了方便设备模型的实现,内核规定,系统中的每个设备都要连接在一个Bus上,这个Bus可以是一个内部Bus、虚拟Bus或者Platform Bus。

内核通过struct bus_type结构,抽象bus,它是在include/linux/device.h中定义的。本文会围绕该结构,描述Linux内核中bus的功能,以及相关的实现逻辑。最后,会简单的介绍一些标准的bus(如platform),介绍它们的用途、它们的使用场景。

2. 功能说明

描述功能前,先介绍一下该模块的一些核心数据结构,对bus模块而言,核心数据结构就是struct bus_type,另外,还有一个sub system相关的结构,会一并说明。


 
/**
 * struct bus_type - The bus type of the device
 *
 * @name:	The name of the bus.
 * @dev_name:	Used for subsystems to enumerate devices like ("foo%u", dev->id).
 * @dev_root:	Default device to use as the parent.
 * @dev_attrs:	Default attributes of the devices on the bus.
 * @bus_groups:	Default attributes of the bus.
 * @dev_groups:	Default attributes of the devices on the bus.
 * @drv_groups: Default attributes of the device drivers on the bus.
 * @match:	Called, perhaps multiple times, whenever a new device or driver
 *		is added for this bus. It should return a positive value if the
 *		given device can be handled by the given driver and zero
 *		otherwise. It may also return error code if determining that
 *		the driver supports the device is not possible. In case of
 *		-EPROBE_DEFER it will queue the device for deferred probing.
 * @uevent:	Called when a device is added, removed, or a few other things
 *		that generate uevents to add the environment variables.
 * @probe:	Called when a new device or driver add to this bus, and callback
 *		the specific driver's probe to initial the matched device.
 * @remove:	Called when a device removed from this bus.
 * @shutdown:	Called at shut-down time to quiesce the device.
 *
 * @online:	Called to put the device back online (after offlining it).
 * @offline:	Called to put the device offline for hot-removal. May fail.
 *
 * @suspend:	Called when a device on this bus wants to go to sleep mode.
 * @resume:	Called to bring a device on this bus out of sleep mode.
 * @pm:		Power management operations of this bus, callback the specific
 *		device driver's pm-ops.
 * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU
 *              driver implementations to a bus and allow the driver to do
 *              bus-specific setup
 * @p:		The private data of the driver core, only the driver core can
 *		touch this.
 * @lock_key:	Lock class key for use by the lock validator
 *
 * A bus is a channel between the processor and one or more devices. For the
 * purposes of the device model, all devices are connected via a bus, even if
 * it is an internal, virtual, "platform" bus. Buses can plug into each other.
 * A USB controller is usually a PCI device, for example. The device model
 * represents the actual connections between buses and the devices they control.
 * A bus is represented by the bus_type structure. It contains the name, the
 * default attributes, the bus' methods, PM operations, and the driver core's
 * private data.
 */
struct bus_type {
	const char		*name;
	const char		*dev_name;
	struct device		*dev_root;
	struct device_attribute	*dev_attrs;	/* use dev_groups instead */
	const struct attribute_group **bus_groups;
	const struct attribute_group **dev_groups;
	const struct attribute_group **drv_groups;
 
	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 (*online)(struct device *dev);
	int (*offline)(struct device *dev);
 
	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);
 
	const struct dev_pm_ops *pm;
 
	const struct iommu_ops *iommu_ops;
 
	struct subsys_private *p;
	struct lock_class_key lock_key;
};

name,该bus的名称,会在sysfs中以目录的形式存在,如platform bus在sysfs中表现为"/sys/bus/platform”。

dev_name,该名称和Linux设备模型五(device和device driver所讲述的struct device结构中的init_name有关。对有些设备而言(例如批量化的USB设备),设计者根本就懒得为它起名字的,而内核也支持这种懒惰,允许将设备的名字留空。这样当设备注册到内核后,设备模型的核心逻辑就会用"bus->dev_name+device ID”的形式,为这样的设备生成一个名称。用官方翻译说:用于子系统枚举像(“foo%u”,dev-> id)这样的设备

dev_attr,总线上设备的默认属性

bus_groups、bus_groups、bus_groups,一些默认的attribute,可以在bus、device或者device_driver添加到内核时,自动为它们添加相应的attribute。(老一些版本的内核是叫device_attribute,driver_attribute,bus_attribute作用是一样的)

dev_root,根据内核的注释,dev_root设备为bus的默认父设备(Default device to use as the parent),但在内核实际实现中,只和一个叫sub system的功能有关,随后会介绍。

match,一个由具体的bus driver实现的回调函数。当任何属于该Bus的device或者device_driver添加到内核时,内核都会调用该接口,如果新加的device或device_driver匹配上了自己的另一半的话,该接口要返回非零值,此时bus模块的核心逻辑就会执行后续的处理。

uevent,一个由具体的bus driver实现的回调函数。当任何属于该Bus的device,发生添加、移除或者其它动作时,bus模块的核心逻辑就会调用该接口,以便bus driver能够修改环境变量。

probe、remove,这两个回调函数,和device,driver中的非常类似,但它们的存在是非常有意义的。可以想象一下,如果需要probe(其实就是初始化)指定的device话,需要保证该device所在的bus是被初始化过、确保能正确工作的。这就要就在执行device_driver的probe前,先执行它的bus的probe。remove的过程相反。 
注1:并不是所有的bus都需要probe和remove接口的,因为对有些bus来说(例如platform bus),它本身就是一个虚拟的总线,无所谓初始化,直接就能使用,因此这些bus的driver就可以将这两个回调函数留空。

shutdown、suspend、resume,和probe、remove的原理类似,电源管理相关的实现,暂不说明。

online:被调用以使设备重新上线(在离线后)。
offline:被调用以使设备脱机以进行热移除。可能会失败。

pm,电源管理相关的逻辑,暂不说明。

iommu_ops,此总线的IOMMU特定操作,用于连接IOMMU驱动程序实现到总线并允许驱动程序执行总线专用设置。

p,一个struct subsys_private类型的指针,驱动核心的私有数据,只有驱动核心才可以触摸这个。后面我们会用一个小节说明。

2.2 struct subsys_private

该结构和device_driver中的struct driver_private类似,在前面的章节 "设备模型驱动七(device_driver细节)"中有提到它,但没有详细说明。

要说明subsys_private的功能,让我们先看一下该结构的定义:

/**
 * struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
 *
 * @subsys - the struct kset that defines this subsystem
 * @devices_kset - the subsystem's 'devices' directory
 * @interfaces - list of subsystem interfaces associated
 * @mutex - protect the devices, and interfaces lists.
 *
 * @drivers_kset - the list of drivers associated
 * @klist_devices - the klist to iterate over the @devices_kset
 * @klist_drivers - the klist to iterate over the @drivers_kset
 * @bus_notifier - the bus notifier list for anything that cares about things
 *                 on this bus.
 * @bus - pointer back to the struct bus_type that this structure is associated
 *        with.
 *
 * @glue_dirs - "glue" directory to put in-between the parent device to
 *              avoid namespace conflicts
 * @class - pointer back to the struct class that this structure is associated
 *          with.
 *
 * This structure is the one that is the actual kobject allowing struct
 * bus_type/class to be statically allocated safely.  Nothing outside of the
 * driver core should ever touch these fields.
 */
struct subsys_private {
	struct kset subsys;
	struct kset *devices_kset;
	struct list_head interfaces;
	struct mutex mutex;
 
	struct kset *drivers_kset;
	struct klist klist_devices;
	struct klist klist_drivers;
	struct blocking_notifier_head bus_notifier;
	unsigned int drivers_autoprobe:1;
	struct bus_type *bus;
 
	struct kset glue_dirs;
	struct class *class;
};

看到结构内部的字段,就清晰多了,没事不要乱起名字嘛!什么subsys啊,看的晕晕的!不过还是试着先理解一下为什么起名为subsys吧:

按理说,这个结构就是集合了一些bus模块需要使用的私有数据,例如kset啦、klist啦等等,命名为bus_private会好点(就像device、driver模块一样)【事实上早期版本确实是命名为bus_type_private】。不过为什么内核最终抛弃了呢呢?看看include/linux/device.h中的struct class结构(我们会在下一篇文章中介绍class)就知道了,因为class结构中也包含了一个一模一样的struct subsys_private指针,看来class和bus很相似啊,所以在内核的subsys_private在现在最新版本就是这样。

2.6.35.7   最开始bus和class的private是分开的,已经很相似了

struct class_private {
	struct kset class_subsys;
	struct klist class_devices;
	struct list_head class_interfaces;
	struct kset class_dirs;
	struct mutex class_mutex;
	struct class *class;
};
 
struct bus_type_private {
	struct kset subsys;
	struct kset *drivers_kset;
	struct kset *devices_kset;
	struct klist klist_devices;
	struct klist klist_drivers;
	struct blocking_notifier_head bus_notifier;
	unsigned int drivers_autoprobe:1;
	struct bus_type *bus;
};

3.xxx   bus有需求升级为subsys_private ,同时为后面去掉class_private 做基础


struct subsys_private {
    struct kset subsys;
    struct kset *devices_kset;
    struct list_head interfaces;
    struct mutex mutex;
 
    struct kset *drivers_kset;
    struct klist klist_devices;
    struct klist klist_drivers;
    struct blocking_notifier_head bus_notifier;
    unsigned int drivers_autoprobe:1;
    struct bus_type *bus;
  
    struct kset glue_dirs;
    struct class *class;
 };
 
struct class_private {
	struct kset class_subsys;
	struct klist class_devices;
	struct list_head class_interfaces;
	struct kset class_dirs;
	struct mutex class_mutex;
	struct class *class;
};

3.x后期,两者完全统一用这个,class_private 在这个版本已经完全看不到了

struct subsys_private {
	struct kset subsys;
	struct kset *devices_kset;
	struct list_head interfaces;
	struct mutex mutex;
 
	struct kset *drivers_kset;
	struct klist klist_devices;
	struct klist klist_drivers;
	struct blocking_notifier_head bus_notifier;
	unsigned int drivers_autoprobe:1;
	struct bus_type *bus;        //
 
	struct kset glue_dirs;
	struct class *class;         //
};

想到这里,就好理解了,无论是bus,还是class,还是我们会在后面看到的一些虚拟的子系统,它都构成了一个“子系统(sub-system)”,该子系统会包含形形色色的device或device_driver,就像一个独立的王国一样,存在于内核中。而这些子系统的表现形式,就是/sys/bus(或/sys/class,或其它)目录下面的子目录,每一个子目录,都是一个子系统(如/sys/bus/spi/)。

 

 


/**
 * bus_register - register a driver-core subsystem
 * @bus: bus to register
 *
 * Once we have that, we register the bus with the kobject
 * infrastructure, then register the children subsystems it has:
 * the devices and drivers that belong to the subsystem.
 */
int bus_register(struct bus_type *bus)
{
	int retval;
	struct subsys_private *priv;
	struct lock_class_key *key = &bus->lock_key;

    /* 分配子系统 */
	priv = kzalloc(sizeof(struct subsys_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;    /* 绑定内核再初始化时就位所有的bus统一申请的kset */
	priv->subsys.kobj.ktype = &bus_ktype;   /* 绑定ktype */
	priv->drivers_autoprobe = 1;

	retval = kset_register(&priv->subsys);    /* 注册kset,即在bus目录下就出现了对应目录 */
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_uevent);    /**  /
	if (retval)
		goto bus_uevent_fail;

    /* bus目录下创建devices和drivers目录,用来将来存放绑定该总线的驱动 */
	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;
	}

    /* 初始化 */
	INIT_LIST_HEAD(&priv->interfaces);
	__mutex_init(&priv->mutex, "subsys mutex", key);
	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
	klist_init(&priv->klist_drivers, NULL, NULL);

    /* 总线的最重要的目的是匹配device和driver,用的就是probe机制 */
	retval = add_probe_files(bus);
	if (retval)
		goto bus_probe_files_fail;

    /* 总线可以属于其它总线的组里面 */
	retval = bus_add_groups(bus, bus->bus_groups);
	if (retval)
		goto bus_groups_fail;

	pr_debug("bus: '%s': registered\n", bus->name);
	return 0;

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

总线最主要的功能是匹配绑定到它下面的device和driver,上面的其它都是创建sys文件系统的关联性的东西。

 

最后以一个小例子,来实现一条简单总线。(该例子来自国嵌的老视频,新内核有部分改动)

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>

MODULE_AUTHOR("David Xie");
MODULE_LICENSE("Dual BSD/GPL");

static char *Version = "$Revision: 1.0 $";

static int my_match(struct device *dev, struct device_driver *driver)
{
    /* 比较设备和驱动的名字 */
    return !strncmp(dev_name(dev), driver->name, strlen(driver->name));
}

struct bus_type my_bus_type = { 
    .name = "my_bus",
    .match = my_match,
};


EXPORT_SYMBOL(my_bus_type);

static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
    return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}


static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);


static int __init my_bus_init(void)
{
    int ret;

        /*注册总线*/
    ret = bus_register(&my_bus_type);
    if (ret)
        return ret;

    /*创建属性文件*/
    if (bus_create_file(&my_bus_type, &bus_attr_version))
        printk(KERN_NOTICE "Fail to create version attribute!\n");

    return ret;
}

static void my_bus_exit(void)
{
    bus_unregister(&my_bus_type);
}

module_init(my_bus_init);
module_exit(my_bus_exit);
                                      

上面继续复习了前面的attribute属性,以及基本的驱动模型的使用。总线的match函数,即比较设备和驱动的函数名。

 

 

参考博客

http://www.wowotech.net/device_model/bus.html

Logo

更多推荐