MTK6575之android驅動學習1(框架梳理)

一、內核執行流程

 

 

內核初始化設備驅動的過程:

第一個C函數從main.c (kernel\init)開始,暫且不論匯編文件

[html] view plaincopyprint?

start_kernel()->rest_init()->do_basic_setup()->do_initcalls()  

 

 

函數do_initcalls如下:

[html] 

static void __init do_initcalls(void)  

{  

initcall_t *fn;  

  

/*循環調用__initcall_start與__initcall_end之間函數*/  

  

for (fn = __early_initcall_end; fn < __initcall_end; fn++)  

do_one_initcall(*fn);  

  

  

/* Make sure there is no pending stuff from the initcall sequence */  

flush_scheduled_work();  

}  

 

 

 

 

 

二、而關於module_init(x)是如何被加載的

 

[html]  

1.#define module_init(x) __initcall(x);  

  

  

2.#define __initcall(fn) device_initcall(fn)  

  

  

3.#define device_initcall(fn) __define_initcall("6",fn)  

  

  

4.#define __define_initcall(level,fn) \     

static initcall_t __initcall_##fn __attribute_used__   \  

__attribute__((__section__(".initcall" level ".init"))) = fn  

  

  

從而可以得出:  

module_init(x) = static initcall_t __initcall_##fn __attribute_used__   \  

__attribute__((__section__(".initcall" level ".init"))) = x  

 

 

 

首先分析:

1.其中 initcall_t 是個函數指針類型:typedef int (*initcall_t)(void);

2.屬性 __attribute__((__section__())) 則表示把對象放在一個這個由括號中的名稱所指代的section中。

所以這個宏定義的的含義是:

1) 聲明一個名稱為__initcall_##fn的函數指針(其中##表示替換連接,);

2) 將這個函數指針初始化為fn;

3) 編譯的時候需要把這個函數指針變量放置到名稱為 ".initcall" level ".init"

的section中(比如level="1",代表這個section的名稱是 ".initcall 1 .init")。

4)舉例說明:假如我們有這個宏定義module_init(kpd_mod_init);那麼經過預編譯替換之後就發生如下變化

將函數 kpd_mod_init 的首地址放到section的名稱是 ".initcall 6 .init" 的段中去。

註意:為什麼是6?因為我們這裡使用的是device,#define device_initcall(fn) __define_initcall("6",fn)

會被替換成6。具體對應規則如下:

[html] 

#define core_initcall(fn)         __define_initcall("1",fn)  

#define postcore_initcall(fn)     __define_initcall("2",fn)  

#define arch_initcall(fn)         __define_initcall("3",fn)  

#define subsys_initcall(fn)       __define_initcall("4",fn)  

#define fs_initcall(fn)           __define_initcall("5",fn)  

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

#define late_initcall(fn)         __define_initcall("7",fn)  

 

 

 

在連接腳本段.initcall中將依次存放著

[html]  

".initcall 1 .init"  

".initcall 2 .init"  

".initcall 3 .init"  

".initcall 4 .init"  

".initcall 5 .init"  

".initcall 6 .init"  

".initcall 7 .init"  

 

 

 

 

三、現在回到之前提到的

 

 

[html]  

do_one_initcall(*fn);  

 

 

註意這也是個回調函數,關於回調函數的進一步說明可以參考有關資料

不難得出,在這裡將會依次執行,上述提到的那7個段中的函數,進行有關的初始化。

假如您希望某個初始化函數在內核初始化階段就被調用,那麼您就應該使用宏__define_initcall(level,fn)

或其7個衍生宏來把這個初始化函數fn的起始地址按照初始化的順序放置到相關的section 中。 內核初始化

時的do_initcalls()將從這個section中按順序找到這些函數來執行。

 

 

幾個註意點:

1.module_init這個是隻有在該模塊以module方式存在的時候才有意義,當你加載該模塊的時候才會去調用它

,加載哪個模塊,就調用哪個模塊的module_init。如果那個模塊本身直接編譯進內核瞭,那它的代碼就直接

放在內核中相應的區域瞭,系統啟動的時候自動就調用這些函數瞭。在我使用的MTK平臺都是將其編譯進內核

瞭的。

2.*(.initcall6.init)在這裡.initcall6.init是一個數據段的名稱,這句話的意思是所有聲明要放在這個段

的數據都將按順序存放在這個段裡。每個module_init都將定義一個函數指針,這些函數指針被指定要放在

.initcall6.init這個段裡,它們將按順序排列。這樣在內核啟動的時候,它會根據這些函數指針按順序調用

所有的函數。

 

 

四、接下來可以開始分析我們的kpd驅動

 

 

1.我們知道加入我們把驅動編譯進內核的話,那麼內核啟動初始化的階段會執行module_init(kpd_mod_init);

也就是kpd_mod_init這個函數Kpd.c (mediatek\platform\mt6575\kernel\drivers\keypad)

2.在這個初始化函數中,會有如下流程

kpd_mod_init() -> platform_driver_register(&kpd_pdrv) -> kpd_pdrv_probe(struct platform_device *pdev)

註意瞭,上述流程將是內核啟動後自動進行的初始化。當然我們假設driver和device是可以在bus上綁定的。

3.所以接下來我們可以看看這個kpd_pdrv_probe到底是做瞭些什麼事情。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *