Platform驱动设备模型:platform_device, bus, platform_driver三者初始化顺序

  • Post author:
  • Post category:其他


背景介绍:本文是基于内核4.19.x,不同内核版本略有不同

由于本人才疏学浅有讲的不对的地方请指正,这也算是个人的一个笔记。

本文概况讲一下platform_device, platform bus, platform_driver三者的注册流程。由于内核版本3.x引入devicetree后,所以这里有必需说一下devicetree在什么时候解析,而又在什么时候创建成platform_device。

先上图:

图一

1.先看看devicetree是在什么时候转化成device_node

bootloader除了做一些必要的硬件初始化后,最后一步是将kernel镜像,devicetree的dtb文件加载到内存,并指定rootfs。最后跳转到kernel的地址进行执行。首先会执行head.S里面的汇编,后面会跳转到start_kernel, 这就到了熟悉的C语言了。

根据上面的函数调用流程图(图一),start_kernel->setup_arch->unflatten_device_tree, unflatten_device_tree这个函数会将dtb文件解析,并把每个dtb的node,放入到device_node结构中去。根节点是struct device_node *of_root, 这是一个全局指针,通过结构内的parent, child, sibling把所有device_node通过树状结构连接起来。而这个树根就是of_root。

struct device_node {
	const char *name;
	const char *type;
	phandle phandle;
	const char *full_name;
	struct fwnode_handle fwnode;

	struct	property *properties;
	struct	property *deadprops;	/* removed properties */
	struct	device_node *parent;  //指向父节点
	struct	device_node *child;   //指向孩子节点
	struct	device_node *sibling; //指向兄弟节点
#if defined(CONFIG_OF_KOBJ)
	struct	kobject kobj;
#endif
	unsigned long _flags;
	void	*data;
#if defined(CONFIG_SPARC)
	const char *path_component_name;
	unsigned int unique_id;
	struct of_irq_controller *irq_trans;
#endif
};

2.platform bus注册时间

start_kernel->rest_init->kernel_init->kernel_init_freeable->do_basic_setup->driver_init->platform_bus_init

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,   //platform_device, platform_driver的匹配函数        
	.uevent		= platform_uevent,
	.dma_configure	= platform_dma_configure,
	.pm		= &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();
/* 
 * 下面主要是创建了一个叫做platform的device,后续所有的
 * platform_device注册后都会放在platform下,在sysfs表现为/sys/devices/platform 
 *
 */
	error = device_register(&platform_bus);  
	if (error) {
		put_device(&platform_bus);
		return error;
	}
/*
 *  下面主要注册platform bus,最主要的事情,应该有一下几点
 *  初始化bus的存放devices的链表,用于存放device
 *  初始化bus的存放drivers的链表,用于存放driver
 *  注册platform_match回调函数,用于platform_device, platform_driver的匹配函数
 *  在sysfs中表现为/sys/bus/platform, 并在其下面创建了devices,drivers两个文件夹
 */
	error =  bus_register(&platform_bus_type); 
	if (error)
		device_unregister(&platform_bus);
	of_platform_register_reconfig_notifier();
	return error;
}

由于后面platform_device, platform_driver注册时都会用到platform bus中的链表,所以platform bus要最先初始化。

3.platform_device, platform_driver的初始化以及注册


3.1 为什么是这里是platform_device比platform_driver先注册呢?

arch_initcall_sync(of_platform_default_populate_init);

#define arch_initcall_sync(fn)        __define_initcall(fn, 3s)


3.2 platform_driver注册是放在哪个段的呢?

这里随便找一个platform_driver注册

static struct platform_driver gpio_led_driver = {
	.probe		= gpio_led_probe,
	.shutdown	= gpio_led_shutdown,
	.driver		= {
		.name	= "leds-gpio",
		.of_match_table = of_gpio_leds_match,
	},
};

module_platform_driver(gpio_led_driver);


#define module_platform_driver(__platform_driver) \
	module_driver(__platform_driver, platform_driver_register, \
			platform_driver_unregister)


#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
	return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
	__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

将宏展开最后得到:

static init __init gpio_led_driver_init(void)
{
        return platform_driver_register(&gpio_led_driver);
}
module_init(gpio_led_driver_init);

#define module_init(x)    __initcall(x);

#define __initcall(fn) device_initcall(fn)

#define device_initcall(fn)        __define_initcall(fn, 6)

module_init最后会将函数放在device_initcall这个代码段里。


do_initcalls会依次调用lds脚本里面从上到下的代码段里面的函数,所以platform_device会先初始化注册。


platform_device的创建并注册:of_platform_default_populate_init函数主要的功能就是将根节点为of_root,device_node构成的树状结构中符合规则的device_node分别创建成platform_device,并将其注册。


最后是platform_driver的注册。三者的注册顺序到这里就讲完了。

事实上platform_device, platform_driver谁先注册谁后注册都没有影响。如果你的platform_device是在devicetree里面配置的,那么就是按照上面讲的,先注册platform bus, 再注册platform_device, 最后注册platform_driver。



版权声明:本文为xiliu542原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。