前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux设备驱动模型-Bus

Linux设备驱动模型-Bus

作者头像
DragonKingZhu
发布2020-03-24 14:36:16
3.8K0
发布2020-03-24 14:36:16
举报

前言

在linux设备驱动模型中,总线可以看作是linux设备模型的核心,系统中的其他设备以及驱动都是以总线为核心围绕。不过驱动程序员在系统中创建一条总线的机会并不多。驱动模型中的总线可以是真是存在的物理总线(USB总线,I2C总线,PCI总线),也可以是为了驱动模型架构设计出的虚拟总线(Platform总线)。为此linux设备驱动模型都将围绕"总线--设备--驱动"来展开,因为符合linux设备驱动模型的设备与驱动都是必须挂载在一个总线上的,无论是实际存在的或者虚拟的。

数据结构

在详细说明bus开始的时候,先需要说明其数据结构,因为一个好的数据结构,就可以很大程度上了解其功能,内核中使用bus_type代表一个总线。

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: 总线的名称,比如i2c, spi

.dev_name: 通常用户枚举设备的名称,比如常见的i2c-0, i2c-1....

.dev_root: 该bus默认的父设备。

.dev_attrs: 此bus设备上默认的属性。

.bus_groups, dev_groups, drv_groups: 分别是总线, 设备,驱动的属性。

.match: 当一个设备或者驱动添加到此总线上的时候,bus就会调用match对设备和驱动一一匹配的。

.uevent: 当该bus下的任何设备,驱动发生增加,删除的操作时,就会调用到uevnet函数。

.probe,remove: 当一个driver添加到系统的时候,是先会调用bus的probe的,如果bus的probe都没有初始化,则bus下的任何驱动或者设备都无法使用。但是不是所有的bus都会实现probe函数的,虚拟的bus是不存在初始化的。

.supend,resume: bus电源管理回调函数。

.pm: 一组电源管理的回调函数。

p: 一个用来管理总线上设备与驱动的数据结构。

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: 用来表示bus所在的子系统,系统中所有注册的bus都将指向bus_kset。

.devices_kset: 代表该bus下所有设备的集合。

.drivers_kset: 代表该bus下所有驱动的集合。

.interfaces: 用于保存该bus下所有的interface。

.klist_devices: 代表该bus下所有设备的链表。

.klist_drivers: 代表该bus下所有驱动的链表。

.bus_notifier: 该bus创建的通知链。

.drivers_autoprobe: 代表在该bus下注册某一个驱动或者设备的时候,是否需要自动匹配

.bus: 指向所属的bus。

.class: 指向所属的class。

BUS相关的函数

  • buses_init(创建bus/system集合)
int __init buses_init(void)
{
	bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
	if (!bus_kset)
		return -ENOMEM;

	system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
	if (!system_kset)
		return -ENOMEM;

	return 0;
}

通过上述的操作将在sysfs下创建了一个名字为"bus"的目录,同时也会在/sys/devices/下创建一个名字为"system"的目录。

在前面kset学习中知道,当这个名为bus的kset下有状态变化的时候,就会发送uevent消息的,在发送过程中就会调用到bus_uevent_ops中实现的函数。

static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
{
	struct kobj_type *ktype = get_ktype(kobj);

	if (ktype == &bus_ktype)
		return 1;
	return 0;
}

static const struct kset_uevent_ops bus_uevent_ops = {
	.filter = bus_uevent_filter,
};

在bus_uevent_ops中只实现了filter函数。此函数只要是判断发生状态的kobj对象类型是不是总线类型,不是就不会上报该事件的。

  • bus_register(注册一个总线到系统中)
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);         //分配subsys_private结构体
	if (!priv)
		return -ENOMEM;

	priv->bus = bus;                                        //设置bus指针
	bus->p = priv;                                          //设置bus->p指针
 
	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);       //初始化bus通知链

	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);  //设置该bus的名字,体现在/sys/bus/下
	if (retval)
		goto out;

	priv->subsys.kobj.kset = bus_kset;         //设置该bus所属的kset, 以及ktype
	priv->subsys.kobj.ktype = &bus_ktype;
	priv->drivers_autoprobe = 1;

	retval = kset_register(&priv->subsys);     //注册该kset到系统中,表现在/sys/bus下
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_uevent); //创建该bus的uevent属性
	if (retval)
		goto bus_uevent_fail;

	priv->devices_kset = kset_create_and_add("devices", NULL,        
						 &priv->subsys.kobj);  //在该bus下创建devices目录
	if (!priv->devices_kset) {
		retval = -ENOMEM;
		goto bus_devices_fail;
	}

	priv->drivers_kset = kset_create_and_add("drivers", NULL,
						 &priv->subsys.kobj);  //在该bus下创建drivers目录
	if (!priv->drivers_kset) {
		retval = -ENOMEM;
		goto bus_drivers_fail;
	}

	INIT_LIST_HEAD(&priv->interfaces);          //初始化interface, mutex, klist_devices, klist_drivers
	__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);

	retval = add_probe_files(bus);      //创建bus的probe属性
	if (retval)
		goto bus_probe_files_fail;

	retval = bus_add_groups(bus, bus->bus_groups);  //创建bus的属性
	if (retval)
		goto bus_groups_fail;

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

比如常见的通过bus_register创建一个i2c总线,就会在/sys/bus/下创建i2c目录, 以及devices目录, driver目录, 以及probe属性, uevent属性。

root@test:/sys/bus/i2c # ls -l
drwxr-xr-x root     root              2012-01-01 08:17 devices
drwxr-xr-x root     root              2012-01-01 08:17 drivers
-rw-r--r-- root     root         4096 2012-01-01 08:17 drivers_autoprobe
--w------- root     root         4096 2012-01-01 08:17 drivers_probe
--w------- root     root         4096 2012-01-01 08:17 uevent

而在devices下就是该i2c-bus下所有的设备,drivers下就是i2c-bus下所有去的驱动。

  • subsys_register(注册一个子系统)
static int subsys_register(struct bus_type *subsys,
			   const struct attribute_group **groups,
			   struct kobject *parent_of_root)
{
	struct device *dev;
	int err;

	err = bus_register(subsys);       //注册一个总线
	if (err < 0)
		return err;

	dev = kzalloc(sizeof(struct device), GFP_KERNEL);        //分配一个device结构,然后设置名字
	if (!dev) {
		err = -ENOMEM;
		goto err_dev;
	}

	err = dev_set_name(dev, "%s", subsys->name);
	if (err < 0)
		goto err_name;

	dev->kobj.parent = parent_of_root;                //设置该device的parent,通常都是在buses_init中的system_kset
	dev->groups = groups;                             //表现在sys中就是在/sys/devices/system下新建一个subsys->name的目录
	dev->release = system_root_device_release;

	err = device_register(dev);  //注册该设备
	if (err < 0) 
		goto err_dev_reg;

	subsys->dev_root = dev;
	return 0;
}
  • subsys_system_register(/sys/devices/system/下注册一个子系统)
int subsys_system_register(struct bus_type *subsys,
			   const struct attribute_group **groups)
{
	return subsys_register(subsys, groups, &system_kset->kobj);
}

该函数将会调用上面的subsys_register函数。举例:

static int __init clockevents_init_sysfs(void)
{
	int err = subsys_system_register(&clockevents_subsys, NULL);

	if (!err)
		err = tick_init_sysfs();
	return err;
}
struct bus_type clockevents_subsys = {
	.name		= "clockevents",
	.dev_name       = "clockevent",
};

这样的话,就会在/sys/devices/system下创建一个名字为"clockevent"设备,在/sys/bus/下创建一个名字为“clockevents”的总线

root@test:/sys/bus/clockevents # ls
devices
drivers
drivers_autoprobe
drivers_probe
uevent
root@test:/sys/devices/system/clockevents # ls -l                   
drwxr-xr-x root     root              2012-01-01 08:00 broadcast
drwxr-xr-x root     root              2012-01-01 08:00 clockevent0
drwxr-xr-x root     root              2012-01-01 08:00 clockevent1
drwxr-xr-x root     root              2012-01-01 08:00 clockevent2
drwxr-xr-x root     root              2012-01-01 08:00 clockevent3
drwxr-xr-x root     root              2012-01-01 08:00 clockevent4
drwxr-xr-x root     root              2012-01-01 08:00 clockevent5
drwxr-xr-x root     root              2012-01-01 08:00 clockevent6
drwxr-xr-x root     root              2012-01-01 08:00 clockevent7
drwxr-xr-x root     root              2012-01-01 08:00 power
-rw-r--r-- root     root         4096 2012-01-01 08:00 uevent

不过根据该函数的注释“Do not use this interface for anything new, it exists for compatibility with bad ideas only.” 意思是不在建议使用该函数了。

  • subsys_virtual_register(在/sys/devices/virtual/下创建一个子系统)
int subsys_virtual_register(struct bus_type *subsys,
			    const struct attribute_group **groups)
{
	struct kobject *virtual_dir;

	virtual_dir = virtual_device_parent(NULL);
	if (!virtual_dir)
		return -ENOMEM;

	return subsys_register(subsys, groups, virtual_dir);
}

中间会调用virtual_device_parent函数

struct kobject *virtual_device_parent(struct device *dev)
{
	static struct kobject *virtual_dir = NULL;

	if (!virtual_dir)
		virtual_dir = kobject_create_and_add("virtual",
						     &devices_kset->kobj);

	return virtual_dir;
}

也就是在/sys/devices/下创建一个名字为“virtual”的目录。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 数据结构
  • BUS相关的函数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档