1. 程式人生 > >Zephyr OS 驅動篇之裝置初始化順序

Zephyr OS 驅動篇之裝置初始化順序




Zephyr OS 驅動篇之裝置初始化順序
在前面的 Zephyr OS 驅動篇之裝置驅動模型 中已講解了 Zephyr OS 中的裝置驅動模型。Zephyr OS 將裝置分為 PRIMARY、SECONDARY、NANOKERNEL 等五個等級,並在系統啟動的相應階段初始化該等級內的所有裝置。那麼問題來了,每個等級內有很多裝置,它們的初始化時有依賴關係嗎,即它們需要按照某個順序初始化嗎?

答案是:YES!

再看看裝置的定義:
  1. #define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \
  2.                 level, prio, api) \
  3.     \
  4.     static struct device_config __config_##dev_name __used \
  5.     __attribute__((__section__(".devconfig.init"))) = { \
  6.         .name = drv_name, .init = (init_fn), \
  7.         .config_info = (cfg_info) \
  8.     }; \
  9.     \
  10.     static struct device (__device_##dev_name) __used \
  11.     __attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
  12.          .config = &(__config_##dev_name), \
  13.          .driver_api = api, \
  14.          .driver_data = data \
  15.     }
複製程式碼 在這個巨集定義中,有兩個引數至關重要,level 和 prio。這兩個引數沒有出現在具體的程式碼中,而是出現在 __attribute__ 屬性中:
  1. __attribute__((__section__(".init_" #level STRINGIFY(prio))))
複製程式碼 編譯器在預編譯的時候會將 “#” 後面到引數轉換為字串,例如 #PRIMARY 將被轉換為 “PRIMARY”。
TRINGIFY(s) 的作用也是將引數 s 轉換為字串 “s”, 其程式碼如下:
  1. #define _STRINGIFY(x) #x
  2. #define STRINGIFY(s) _STRINGIFY(s)
複製程式碼 舉個例如,如果在呼叫 DEVICE_AND_API_INIT 時傳入的引數 level 和 prio 分別為 PRIMARY 和 50,那麼編譯器就會將上面那段程式碼放到名為 .init_PRIMARY50 的段中。



現在轉移視線,在 linker-defs.h 中有如下程式碼
  1. #define    DEVICE_INIT_SECTIONS()            \
  2.         __device_init_start = .;        \
  3.         DEVICE_INIT_LEVEL(PRIMARY)        \
  4.         DEVICE_INIT_LEVEL(SECONDARY)    \
  5.         DEVICE_INIT_LEVEL(NANOKERNEL)    \
  6.         DEVICE_INIT_LEVEL(MICROKERNEL)    \
  7.         DEVICE_INIT_LEVEL(APPLICATION)    \
  8.         __device_init_end = .;        \
  9.         DEVICE_BUSY_BITFIELD()        \

  10. DEVICE_INIT_LEVEL 的定義如下:
  11. #define DEVICE_INIT_LEVEL(level)                \
  12.         __device_##level##_start = .;            \
  13.         KEEP(*(SORT(.init_##level[0-9])));        \
  14.         KEEP(*(SORT(.init_##level[1-9][0-9])));    \
複製程式碼 在連結指令碼檔案 linker.ld 中將會呼叫上述程式碼。上面程式碼的大意是將所有的裝置定義的程式碼按照裝置等級(level)依次排列,且在每個等級中,按數字(prio)的大小從小到大依次排列。

假設系統一共定義了十個裝置,它們的引數如下:

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)排序。