Linux驅動先註冊匯流排,匯流排上可以先掛device,也可以先掛driver,那麼究竟怎麼控制先後的順序呢。 1、初始化巨集 Linux系統使用兩種方式去載入系統中的模塊:動態和靜態。 靜態載入:將所有模塊的程式編譯到Linux內核中,由do_initcall函數載入 核心進程(/init/main. ...
Linux驅動先註冊匯流排,匯流排上可以先掛device,也可以先掛driver,那麼究竟怎麼控制先後的順序呢。
1、初始化巨集
Linux系統使用兩種方式去載入系統中的模塊:動態和靜態。
靜態載入:將所有模塊的程式編譯到Linux內核中,由do_initcall函數載入
核心進程(/init/main.c)kernel_inità do_basic_setup()àdo_initcalls()該函數中會將在__initcall_start和__initcall_end之間定義的各個模塊依次載入。那麼在__initcall_start 和 __initcall_end之間都有些什麼呢?
找到/arch/powerpc/kernel/vmlinux.lds文件,找到.initcall.init段:
.initcall.init : {
__initcall_start = .;
*(.initcall0.init)
*(.initcall0s.init)
*(.initcall1.init)
*(.initcall1s.init)
*(.initcall2.init)
*(.initcall2s.init)
*(.initcall3.init)
*(.initcall3s.init)
*(.initcall4.init)
*(.initcall4s.init)
*(.initcall5.init)
*(.initcall5s.init)
*(.initcallrootfs.init)
*(.initcall6.init)
*(.initcall6s.init)
*(.initcall7.init)
*(.initcall7s.init)
__initcall_end = .;
}
可以看出在這兩個巨集之間依次排列了14個等級的巨集,由於這其中的巨集是按先後順序鏈接的,所以也就表示,這14個巨集有優先順序:0>1>1s>2>2s………>7>7s。
那麼這些巨集有什麼具體的意義呢,這就要看include/linux/init.h文件:
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
這裡就定義了具體的巨集,我們平時用的module_init在靜態編譯時就相當於device_initcall。舉個例子,在2.6.24的內核中:gianfar_device使用的是arch_initcall,而gianfar_driver使用的是module_init,因為arch_initcall的優先順序大於module_init,所以gianfar設備驅動的device先於driver在匯流排上添加。
2、編譯順序
同一級別的初始化是和編譯順序有關的,並不是和設備列表一致。
【問題】
背光碟機動初始化先於LCD驅動初始化,導致LCD驅動初始化時出現閃屏的現象。
【解決過程】
2.1 mach-xxx.c中platform devices列表如下:
/* platform devices */
static struct platform_device *athena_evt_platform_devices[] __initdata = {
//&xxx_led_device,
&xxx_rtc_device,
&xxx_uart0_device,
&xxx_uart1_device,
&xxx_uart2_device,
&xxx_uart3_device,
&xxx_nand_device,
&xxx_i2c_device,
&xxx_lcd_device,
&xxxpwm_backlight_device,
...
};
LCD(xxx_lcd_device)設備先於PWM(xxxpwm_backlight_device)設備。
可見驅動的初始化順序並不是和這個表定義的順序始終保持一致的。(記得PM操作 - resume/suspend的順序
是和這個表的順序保持一致的)
2.2 懷疑和編譯順序有關
Z:\kernel\drivers\video\Makefile:背光碟機動(backlight/)的編譯限於LCD驅動(xxxfb.o)的編譯
obj-$(CONFIG_VT) += console/
obj-$(CONFIG_LOGO) += logo/
obj-y += backlight/ display/
...
obj-$(CONFIG_FB_xxx) += xxxfb.o ak_logo.o
obj-$(CONFIG_FB_AK88) += ak88-fb/
這樣編譯生成的System.map中的順序為:
c001f540 t __initcall_pwm_backlight_init6
c001f544 t __initcall_display_class_init6
c001f548 t __initcall_xxxfb_init6
Makefile更改為:
obj-$(CONFIG_VT) += console/
obj-$(CONFIG_LOGO) += logo/
obj-y += display/
...
obj-$(CONFIG_FB_xxx) += xxxfb.o ak_logo.o
obj-$(CONFIG_FB_AK88) += ak88-fb/
obj-y += backlight/
這樣編譯生成的System.map中的順序為:
c001f53c t __initcall_display_class_init6
c001f540 t __initcall_xxxfb_init6
c001f544 t __initcall_genericbl_init6
c001f548 t __initcall_pwm_backlight_init6
載入運行:
xxxpwm_backlight_device的probe就會在xxx_lcd_device的probe之後執行,即LCD初始化先於PWM的初始化。