Zephyr OS 驱动篇之设备初始化顺序
在前面的 Zephyr OS 驱动篇之设备驱动模型 中已讲解了 Zephyr OS 中的设备驱动模型。Zephyr OS 将设备分为 PRIMARY、SECONDARY、NANOKERNEL 等五个等级,并在系统启动的相应阶段初始化该等级内的所有设备。那么问题来了,每个等级内有很多设备,它们的初始化时有依赖关系吗,即它们需要按照某个顺序初始化吗?
答案是:YES!
再看看设备的定义:
复制代码
在这个宏定义中,有两个参数至关重要,level 和 prio。这两个参数没有出现在具体的代码中,而是出现在 __attribute__ 属性中:
复制代码
编译器在预编译的时候会将 “#” 后面到参数转换为字符串,例如 #PRIMARY 将被转换为 “PRIMARY”。
TRINGIFY(s) 的作用也是将参数 s 转换为字符串 “s”, 其代码如下:
复制代码
举个例如,如果在调用 DEVICE_AND_API_INIT 时传入的参数 level 和 prio 分别为 PRIMARY 和 50,那么编译器就会将上面那段代码放到名为 .init_PRIMARY50 的段中。
现在转移视线,在 linker-defs.h 中有如下代码
复制代码
在链接脚本文件 linker.ld 中将会调用上述代码。上面代码的大意是将所有的设备定义的代码按照设备等级(level)依次排列,且在每个等级中,按数字(prio)的大小从小到大依次排列。
假设系统一共定义了十个设备,它们的参数如下:
编译器会按照下面的顺序依次存放各个设备的代码:
所以系统启动时,各设备的初始化顺序是 H, J, B, D, A, G, I, C, E, F。
总结一下,在系统初始化设备时,除了要按照设备等级(level)排序外,在每个等级内部还要按照优先级(prio)排序。
在前面的 Zephyr OS 驱动篇之设备驱动模型 中已讲解了 Zephyr OS 中的设备驱动模型。Zephyr OS 将设备分为 PRIMARY、SECONDARY、NANOKERNEL 等五个等级,并在系统启动的相应阶段初始化该等级内的所有设备。那么问题来了,每个等级内有很多设备,它们的初始化时有依赖关系吗,即它们需要按照某个顺序初始化吗?
答案是:YES!
再看看设备的定义:
- #define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \
- level, prio, api) \
- \
- static struct device_config __config_##dev_name __used \
- __attribute__((__section__(".devconfig.init"))) = { \
- .name = drv_name, .init = (init_fn), \
- .config_info = (cfg_info) \
- }; \
- \
- static struct device (__device_##dev_name) __used \
- __attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
- .config = &(__config_##dev_name), \
- .driver_api = api, \
- .driver_data = data \
- }
- __attribute__((__section__(".init_" #level STRINGIFY(prio))))
TRINGIFY(s) 的作用也是将参数 s 转换为字符串 “s”, 其代码如下:
- #define _STRINGIFY(x) #x
- #define STRINGIFY(s) _STRINGIFY(s)
现在转移视线,在 linker-defs.h 中有如下代码
- #define DEVICE_INIT_SECTIONS() \
- __device_init_start = .; \
- DEVICE_INIT_LEVEL(PRIMARY) \
- DEVICE_INIT_LEVEL(SECONDARY) \
- DEVICE_INIT_LEVEL(NANOKERNEL) \
- DEVICE_INIT_LEVEL(MICROKERNEL) \
- DEVICE_INIT_LEVEL(APPLICATION) \
- __device_init_end = .; \
- DEVICE_BUSY_BITFIELD() \
- DEVICE_INIT_LEVEL 的定义如下:
- #define DEVICE_INIT_LEVEL(level) \
- __device_##level##_start = .; \
- KEEP(*(SORT(.init_##level[0-9]))); \
- KEEP(*(SORT(.init_##level[1-9][0-9]))); \
假设系统一共定义了十个设备,它们的参数如下:
device | level | prio |
设备 A | PRIMARY | 32 |
设备 B | PRIMARY | 24 |
设备 C | NANOKERNEL | 50 |
设备 D | PRIMARY | 30 |
设备 E | MICROKERNEL | 30 |
设备 F | APPLICATION | 3 |
设备 G | SECONDARY | 18 |
设备 H | PRIMARY | 0 |
设备 I | SECONDARY | 44 |
设备 J | PRIMARY | 20 |
编译器会按照下面的顺序依次存放各个设备的代码:
device | level | prio |
设备 H | PRIMARY | 0 |
设备 J | PRIMARY | 20 |
设备 B | PRIMARY | 24 |
设备 D | PRIMARY | 30 |
设备 A | PRIMARY | 32 |
设备 G | SECONDARY | 18 |
设备 I | SECONDARY | 44 |
设备 C | NANOKERNEL | 50 |
设备 E | MICROKERNEL | 30 |
设备 F | APPLICATION | 3 |
所以系统启动时,各设备的初始化顺序是 H, J, B, D, A, G, I, C, E, F。
总结一下,在系统初始化设备时,除了要按照设备等级(level)排序外,在每个等级内部还要按照优先级(prio)排序。