platform初始化

在linux设备驱动中,有一类设备被称为"平台设备",通常Soc系统中集成的独立外设单元都被当做平台设备处理。如I2C,SPI等都归纳为平台设备。

我正在用的开发板,就将SPI控制器作为平台设备进行注册,其注册时会调用platform_device_register()进行注册。

platform_device_register(struct platform_device *dev)
	->platform_device_add(struct platform_device *pdev)
		->pdev->dev.parent = &platform_bus;
			pdev->dev.bus = &platform_bus_type;
platform_bus和platform_bus_type是在platform初始化时建立的,分别表示platform设备和platform总线
倒数第二行将父设备指向&platform_bus,表示SPI控制器是一个platform设备,可以在
/sys/devices/platform目录下找到带spi字样的文件夹。
最后一行将所属的总线指向&platform_bus_type;表示SPI控制器连接在platform总线上,
可以在/sys/bus/platform/devices目录下找到带spi字样的文件


本文分析platform_bus和platform_bus_type的建立过程,也可以说成/sys/devices/platform和/sys/bus/platform目录的建立过程。
platform_bus和platform_bus_type是在platform初始化时建立的,platform初始化函数的调用流程如下:

start_kernel()  
    ->rest_init()  
        ->kernel_init()  
            ->do_basic_setup()  
            ->driver_init()  
                ->platform_bus_init()
platform初始化函数包含两个方面:platform设备的初始化和platform总线的初始化   
int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();

	/*1.向系统注册platform设备--platform设备的初始化*/
	error = device_register(&platform_bus);
	if (error)
		return error;
	/*2.向系统注册platform总线--platform总线的初始化*/
	error =  bus_register(&platform_bus_type);
	if (error)
		device_unregister(&platform_bus);
	return error;
}
1.注册platform设备

struct device platform_bus = {
	.init_name	= "platform",
};
device_register(&platform_bus);
int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}
1.1初始化platform设备

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

void device_initialize(struct device *dev)
{
	/*父容器(父kset)指向devices_kset
	 *devices_kset在上篇文章中分析过,代表/sys/devices目录
	 *表示platform对象(kobj)属于/sys/devices这个对象集合(kset)
	 */
	dev->kobj.kset = devices_kset;
	/*初始化本对象(kobj),并设置kobj->ktype*/
	kobject_init(&dev->kobj, &device_ktype);
	/*初始化其他元素*/	
	INIT_LIST_HEAD(&dev->dma_pools);
	init_MUTEX(&dev->sem);
	spin_lock_init(&dev->devres_lock);
	INIT_LIST_HEAD(&dev->devres_head);
	device_init_wakeup(dev, 0);
	device_pm_init(dev);
	set_dev_node(dev, -1);
}
1.2向系统添加一个platform设备

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

	/*增加kobj的引用计数*/
	dev = get_device(dev);
	if (!dev)
		goto done;

	/*初始化设备的私有数据,主要是klist_children的初始化,以后会用到*/
	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()
	 */
	/*将dev->konj->name设置为dev->init_name
	 *将dev->init_name设置为NULL
	 */
	if (dev->init_name) {
		dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}

	if (!dev_name(dev))
		goto name_error;

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

	/*platform设备没有父设备,所以parent为NULL
	 *以后注册SPI主控制器设备时,其父设备就不为NULL,
	 *而是现在的platform设备
	 */
	parent = get_device(dev->parent);
	setup_parent(dev, parent);

	/* 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 */
	/*1.2.1该函数会调用kobject_add_internal()中的create_dir()函数,创建目录*/
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error)
		goto Error;

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

	/*1.2.2在目录下创建uevent文件*/
	error = device_create_file(dev, &uevent_attr);
	if (error)
		goto attrError;
	/*为空*/
	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &devt_attr);
		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;
	/*和CONFIG_PM_XX有关,本例为空*/	
	error = dpm_sysfs_add(dev);
	if (error)
		goto DPMError;
	/*和CONFIG_PM_XX有关,本例为空*/	
	device_pm_add(dev);

	/* Notify clients of device addition.  This call must come
	 * after dpm_sysf_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->class_mutex);
		/* tie the class to the device */
		klist_add_tail(&dev->knode_class,
			       &dev->class->p->class_devices);

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf,
				    &dev->class->p->class_interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->class_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))
		device_remove_sys_dev_entry(dev);
 devtattrError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &devt_attr);
 ueventattrError:
	device_remove_file(dev, &uevent_attr);
 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;
}
该函数中,因为dev-bus、dev->class等都为空,所以很多子函数都不执行。当以后注册SPI设备时,会先设置dev->bus
、dev->class等,这些函数就会执行。

1.2.1 创建目录kobject_add()
上文提到kobject_add()会调用kobject_add_internal()创建目录,其实创建目录之前还会设置kobj->parent。
因为在1.1中初始化了kobj.kset,所以会执行下面的代码段

if (kobj->kset) {
	if (!parent)
		parent = kobject_get(&kobj->kset->kobj);
	/*将kobj->entry添加都到父容器kset->list中*/
	kobj_kset_join(kobj);
	/*将父对象设置为父容器对应的对象*/
	kobj->parent = parent;
}
这个过程会在本文最后的结构图中体现。

1.2.2创建文件device_create_file();

device_create_file(dev, &uevent_attr);
int device_create_file(struct device *dev, struct device_attribute *attr)
{
	int error = 0;
	if (dev)
		error = sysfs_create_file(&dev->kobj, &attr->attr);
	return error;
}
总线初始化那篇文章也遇到过sysfs_create_file()函数。该函数主要用来创建文件,
具体创建过程不再分析了,这里讲下uevent_attr这个变量。

static struct device_attribute uevent_attr =
	__ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);

#define __ATTR(_name,_mode,_show,_store) { \
	.attr = {.name = __stringify(_name), .mode = _mode },	\
	.show	= _show,					\
	.store	= _store,					\
}
展开之后,static struct device_attribute uevent_attr =
{
	.attr = 
		{
			.name = "uevent",
			.mode = S_IRUGO | S_IWUSR
		},
	.show = show_uevent,
	.store = store_uevent,
}
其中name代表文件名,mode表示文件权限,show,store函数会在读写该文件时调用。
可以按照下面的函数调用流程研究下(不一定正确,嘿嘿),
inode->i_fop = &sysfs_file_operations;
	-->sysfs_read_file()
		-->fill_read_buffer()
			-->ops->show()
			
inode->i_fop = &sysfs_file_operations;
	-->sysfs_write_file()
		-->flush_write_buffer()
			-->ops->store()
至此,我们创建了platform_bus,并在/sys/devices下创建了platform目录

2.注册platform总线

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};
bus_register(&platform_bus_type);	

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

	/*设置kobj的name,subsys是kset类型*/
	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
	if (retval)
		goto out;
	
	/*设置kobj的父容器,和1.1中paltform设备初始化时类似,
	 *bus_kset是在总线初始化时创建的,代表/sys/bus目录
	 */
	priv->subsys.kobj.kset = bus_kset;
	/*设置kobj->ktype*/
	priv->subsys.kobj.ktype = &bus_ktype;
	/*drivers_autoprobe自动检测是否有匹配的驱动*/
	priv->drivers_autoprobe = 1;

	/*注册kset,会创建/sys/bus/platform目录
	 *因为设置了父容器kobj.kset,所以会将kobj->entry添加到
	 *kobj->kset->list链表中,并设置kobj->parent为父容器里的kobj
	 */
	retval = kset_register(&priv->subsys);
	if (retval)
		goto out;

	/*创建uevent文件,可以在/sys/bus/platform中看到*/
	retval = bus_create_file(bus, &bus_attr_uevent);
	if (retval)
		goto bus_uevent_fail;

  /*创建devices目录,可以在/sys/bus/platform中看到,这个比较重要*/
	priv->devices_kset = kset_create_and_add("devices", NULL,
						 &priv->subsys.kobj);
	if (!priv->devices_kset) {
		retval = -ENOMEM;
		goto bus_devices_fail;
	}
	/*创建drivers目录,可以在/sys/bus/platform中看到,这个比较重要*/
	priv->drivers_kset = kset_create_and_add("drivers", NULL,
						 &priv->subsys.kobj);
	if (!priv->drivers_kset) {
		retval = -ENOMEM;
		goto bus_drivers_fail;
	}

	/*以下两个链表在以后匹配设备和匹配驱动时有重要作用*/
	/*初始化设备链表,以后注册的SPI等platform设备都会被添加到该列表*/
	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
	/*初始化驱动链表,以后注册的SPI等platform驱动都会被添加到该列表*/
	klist_init(&priv->klist_drivers, NULL, NULL);

	/*创建几个和热拔插(CONFIG_HOTPLUG)有关的文件,本例中没用到*/
	retval = add_probe_files(bus);
	if (retval)
		goto bus_probe_files_fail;

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

至此,我们创建了platform_bus_type,并在/sys/bus下创建了platform目录。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值