linux platform总线初始化流程分析

内核在初始化的过程中调用platform_bus_init()函数来初始化平台总线,调用流程如下
kernel_init_freeable()->do_basic_setup() -> driver_init()->platform_bus_init()

我们直接分析platform_bus_init函数
drivers/base/platform.c , linux代码还是很简洁的,在内核3.18这个文件代码1300多行

int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();

	error = device_register(&platform_bus);
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);
	if (error)
		device_unregister(&platform_bus);
	return error;
}

void __init early_platform_cleanup(void)
{
	struct platform_device *pd, *pd2;

	/* clean up the devres list used to chain devices */
	list_for_each_entry_safe(pd, pd2, &early_platform_device_list,
				 dev.devres_head) {
		list_del(&pd->dev.devres_head);
		memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head));
	}
}

struct device platform_bus = {
	.init_name	= "platform",
};
struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};

1 函数首先清空总线early_platform_device_list上的所有节点
2 调用device_register(platform_bus_type) 注册设备 , 这个设备为platform_bus
3 注册总线  bus_register(&platform_bus_type) , bus_type为platform_bus_type
我们就先围绕这三步分析platform bus device是如何注册的,第一步只是简单的清空链表,就不再说明了,后面我们再回来说明early_platform_device_list链表是做什么的

首先分析device_register函数, 在drivers/base/core.c文件下面

int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}

device_register明显是设备注册的含义,注册设备分为两步1 初始化,2 添加到设备系统

device_initialize 函数如下

void device_initialize(struct device *dev)
{
	dev->kobj.kset = devices_kset;
	kobject_init(&dev->kobj, &device_ktype);
	INIT_LIST_HEAD(&dev->dma_pools);
	mutex_init(&dev->mutex);
	lockdep_set_novalidate_class(&dev->mutex);
	spin_lock_init(&dev->devres_lock);
	INIT_LIST_HEAD(&dev->devres_head);
	device_pm_init(dev);
	set_dev_node(dev, -1);
}

static struct kobj_type device_ktype = {
	.release	= device_release,
	.sysfs_ops	= &dev_sysfs_ops,
	.namespace	= device_namespace,
};

主要做的工作 
1把设备挂在到sys/device节点的kset下面,对应的kobj_type为device_ktype, device_ktype的device_release函数会在设备注销的时候做一些清理工作,这会关联到driver的操作,比较有意思。我们后面分析,现在主要分析注册流程
2 初始化锁和链表
3初始化电源管理
4 numa node

这些都不是我们的主要目的,主要目的是分析platform bus,所以就不深入分析了,初始化操作完成之后就是调用device_add函数将设备添加进系统,我们看看这会触发什么操作

int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;

	dev = get_device(dev);
	if (!dev)
		goto done;

	if (!dev->p) {
		error = device_private_init(dev);
		if (error)
			goto done;
	}

	/*
	 * for statically allocated devices, which should all be converted
	 * some day, we need to initialize the name. We prevent reading back
	 * the name, and force the use of dev_name()
	 */
	if (dev->init_name) {
		dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}

	/* subsystems can specify simple device enumeration */
	if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
		dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);

	if (!dev_name(dev)) {
		error = -EINVAL;
		goto name_error;
	}

	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

	parent = get_device(dev->parent);
	kobj = get_device_parent(dev, parent);
	if (kobj)
		dev->kobj.parent = kobj;

	/* use parent numa_node */
	if (parent)
		set_dev_node(dev, dev_to_node(parent));

	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error)
		goto Error;

	/* notify platform of device entry */
	if (platform_notify)
		platform_notify(dev);

	error = device_create_file(dev, &dev_attr_uevent);
	if (error)
		goto attrError;

	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &dev_attr_dev);
		if (error)
			goto ueventattrError;

		error = device_create_sys_dev_entry(dev);
		if (error)
			goto devtattrError;

		devtmpfs_create_node(dev);
	}

	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev);
	if (error)
		goto DPMError;
	device_pm_add(dev);

	/* Notify clients of device addition.  This call must come
	 * after dpm_sysfs_add() and before kobject_uevent().
	 */
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_ADD_DEVICE, dev);

	kobject_uevent(&dev->kobj, KOBJ_ADD);
	bus_probe_device(dev);
	if (parent)
		klist_add_tail(&dev->p->knode_parent,
			       &parent->p->klist_children);

	if (dev->class) {
		mutex_lock(&dev->class->p->mutex);
		/* tie the class to the device */
		klist_add_tail(&dev->knode_class,
			       &dev->class->p->klist_devices);

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf,
				    &dev->class->p->interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->mutex);
	}
done:
	put_device(dev);
	return error;
 DPMError:
	bus_remove_device(dev);
 BusError:
	device_remove_attrs(dev);
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	if (MAJOR(dev->devt))
		devtmpfs_delete_node(dev);
	if (MAJOR(dev->devt))
		device_remove_sys_dev_entry(dev);
 devtattrError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &dev_attr_dev);
 ueventattrError:
	device_remove_file(dev, &dev_attr_uevent);
 attrError:
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	kobject_del(&dev->kobj);
 Error:
	cleanup_device_parent(dev);
	if (parent)
		put_device(parent);
name_error:
	kfree(dev->p);
	dev->p = NULL;
	goto done;
}

这个函数还是比较复杂的 ,我们逐步分析
1 增加device 引用计数
2 为device设置名称,其实就是设置kobject名称,用于创建kobj目录的时候使用,如果没有指定名称,则设置名称为bus_name, id,对于设备总线的名称为platform
3 设置kobj的父子关系与device的父子关系一致,parent一般情况下为总线或者某些控制器,主要反应设备的挂在关系,将这些关系反应到sys目录就是通过kobj体系
4 设置numa节点和父设备节点相同, 每个cpu对内存的访问速度是不同的,对自己所属的node访问速度要快,这就是numa,对于设备cpu的访问速度也是不同的,所以可能需要设备numa node,我自己的猜想
5 添加kobject到系统, 注意这里平台总线的父设备是不存在的,所以kobject的目录会创建到kset对应的目录下面
6 发出通知 platform_notify??? 稍后分析
7 创建uevent文件
8 创建设备属性文件
9 创建sys_dev_entry
10 创建tmfs node
12 创建class符号链接
13 创建其他设备自身的属性文件
14 dpm
15 pm
16 总线通知
17 KOBJ_ADD uevent
18 bus_probe_device 调用总线的probe函数
19 添加到设备链表
20 添加到class 链表

下面就按照这20步流程分析

前面5步都比较好理解,我从第6步开始详细分析
 platform_notify主要用于电源管理,x86的实现在drivers/acpi/glue.c下的 acpi_platform_notify函数,这部分我们不分析了

7 创建uevent文件,这个attribute 的实现在kernel/module.c 文件下面,主要用于用户空间出发内核空间发送uevent事件,如向/dev/block/sda/uevent写入ADD,会出发uevent发出消息
8 error = device_create_file(dev, &dev_attr_dev); 
9 如果class不为空,需要创建sys文件系统的class视图
10 通知这部分对platform bus不存在 跳过
11 bus_probe_device 在drivers/base/bus.c文件中

/**
 * bus_probe_device - probe drivers for a new device
 * @dev: device to probe
 *
 * - Automatically probe for a driver if the bus allows it.
 */
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);
}
/**
 * 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 nonzero value if the
 *		given device can be handled by the given driver.
 * @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;
};

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

bus_type的数据结构如上面,其中 subsys_private *p是什么? 它维护了一下该bus特有的数据,如果p->drivers_autoprobe为真,则添加设备的时候自动探测对应的驱动,执行device_attach函数,最后调用bus->p->interfaces下面的方法,靠,这里分析平台总线都是不需要的
继续往下看添加到链表的部分很简单,就不分析了。

总结下设备注册主要做的工作
1 创先sys文件目录和uevent事件,以及电源管理相关注册
2 执行prope绑定device和driver

上面分析的device的注册,其实这些和bus关系不大主要是要强行把bus弄成一种设备方便管理

下面就分析bus_register函数了

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

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

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

注释中说bus_register 注册driver-core 子系统 
1 创建driver-core 也就是subsys_private结构,并设置与bus的关系
2 初始化bus_notifier链表,这个链表用于存放关系bus事件的通知函数
3 设置kobject名称
4 设置kset为bus_kset,也就是目录sys/bus,没有设置父目录,所以创建的sys目录为/sys/bus/${busname}
注意这里设置了priv->drivers_autoprobe = 1 所以会自动绑定设备

5 创建/sys/bus/ b u s n a m e / u e v e n t   u d e v 6 d e v i c e s d r i v e r s k s e t , / s y s / b u s / {busname}/uevent 用于udev系统 6 创建devices和drivers两个kset ,目录分别为/sys/bus/ {busname}/devices , /sys/bus/ b u s n a m e / d r i v e r s 7 p r i v &gt; k l i s t d e v i c e s p r i v &gt; k l i s t d r i v e r s   8 a d d p r o b e f i l e s , / s y s / b u s / {busname}/drivers 7 初始化priv-&gt;klist_devices 和priv-&gt;klist_drivers 列表 8 add_probe_files, 主要创建/sys/bus/ {busname}/drivers_probe和 /sys/bus/ b u s n a m e / d r i v e r s a u t o p r o b e / s y s / b u s / {busname}/drivers_autoprobe /sys/bus/ {busname}/drivers_probe支持的功能是你向这个文件写入设备名称后,会重新扫描bus上对应的驱动(执行attach_device函数)。/sys/bus/${busname}/drivers_autoprobe主要用于设置priv->drivers_autoprobe的值,来告诉bus是否自动执行attach_device函数
9 bus_add_groups 创建goups相关的文件 platform-bus不存在

这样总线的注册我们就分析完了 总结一下
主要就是初始化subsys_private数据结构,创建相应的sys目录和链表

这样我们就可以分析platform添加设备的过程了

发布了113 篇原创文章 · 获赞 22 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/woai110120130/article/details/90736975
今日推荐