4.一樣的精靈,不一樣的API(1)

4.一樣的精靈,不一樣的API(1)

usb_register()這個函數是用來向USB核心層,即USB Core,註冊一個USB設備驅動的,而這裏我們註冊的是Hub的驅動程序所對應的struct usb_driver結構體變量。定義於drivers/usb/ core/hub.c中:

  1. 2841 static struct usb_driver hub_driver = {  
  2. 2842    .name =         "hub",  
  3. 2843    .probe =        hub_probe,  
  4. 2844    .disconnect =   hub_disconnect,  
  5. 2845        .suspend =      hub_suspend,  
  6. 2846    .resume =       hub_resume,  
  7. 2847    .pre_reset =    hub_pre_reset,  
  8. 2848    .post_reset =   hub_post_reset,  
  9. 2849    .ioctl =        hub_ioctl,  
  10. 2850        .id_table =     hub_id_table,  
  11. 2851    .supports_autosuspend = 1,  
  12. 2852 };  

這裏面最重要的一個函數就是hub_probe,很多事情都在這期間發生了。每個USB設備(或者說所有設備)的驅動都會有一個probe函數,比如U盤的probe函數就是storage_probe(),不過storage_probe()被調用需要有兩個前提:第一個前提是usb-storage被加載了,第二個前提是U盤等設備插入了被檢測到了。

而Hub,說它特別,我可絕不是"忽悠"你。Hub本身就有兩種:一種是普通的Hub,一種是Root Hub。對於普通Hub,它完全可能也和U盤一樣,在某個時刻被插入,然後在這種情況下hub_probe被調用,但是對於Root Hub就不需要這麼多廢話了,Root Hub肯定是有的,只要你有USB主機控制器,就一定會有Root Hub,所以hub_probe()基本上很自然地就被調用了,不用說非得等待某個插入事件的發生,沒這個必要。當然沒有USB主機控制器就沒有USB設備能工作。那麼USB Core這整個模塊你就沒有必要分析了。所以,只要你有USB主機控制器,那麼在USB主機控制器的驅動程序初始化的過程中,它就會調用hub_probe()來探測Root Hub,不管你的主機控制器是OHCI、UHCI還是EHCI的接口。

如果register一切順利的話,那麼返回值爲0。如果返回值爲負數,就說明出錯了。現在假設這一步沒有出錯。

usb_hub_init()的2862行,這行代碼其實是很有技術含量的,不過對於寫驅動的人來說,其作用就和當年的kernel_thread()相當。不過kernel_thread()返回值是一個int型的,而kthread_run()返回的卻是struct task_struct結構體指針。這裏等號左邊的khubd_task是我們自己定義的一個struct task_struct指針:

  1. 88 static struct task_struct *khubd_task; 

struct task_struct不用多說,記錄進程的數據結構。每一個進程都用一個struct task_struct結構體變量來表示。所以這裏所做的就是記錄下創建好的內核進程,以便日後要卸載模塊時可以用另一個函數來結束這個內核進程(你也可以叫內核線程),到時我們會調用kthread_stop(khubd_task)函數來結束這個內核線程,這個函數的調用我們將會在usb_hub_cleanup()函數中看到。而usb_hub_cleanup()正是Hub裏面和usb_hub_init()相對應的函數。

2863行,判斷khubd_task,IS_ERR是一個宏,用來判斷指針的。當你創建了一個進程,你當然想知道這個進程創建成功了沒有。

以前我們注意到每次申請內存時都會做一次判斷,你說創建進程是不是也要申請內存?不申請內存誰來記錄struct task_struct?很顯然,要進行判斷。以前我們判斷的是指針是否爲空。以後接觸代碼多了你會發現,其實Linux內核中有很多種內存申請的方式,而這些方式所返回的內存地址也是不一樣的,所以並不是每一次我們都只要判斷指針是否爲空就可以了。事實上,每一次調用kthread_run()之後,我們都會用一個IS_ERR()來判斷指針是否有效。IS_ERR()爲1就表示指針有錯,或者準確一點說叫做指針無效。

什麼叫指針無效?後面會專門解釋,讓我們繼續往下看,只需要記得,如果你不希望發生缺頁異常這樣的錯誤的話,每次調用完kthread_run()之後要用IS_ERR()來檢測返回的指針。如果IS_ERR()返回值是0,那麼說明沒有問題,於是返回值爲 0,也就是說usb_hub_init()就這麼結束了。反之,就會執行usb_deregister(),因爲內核線程沒有成功創建,hub就沒法驅動起來了。

最後函數在2870行,返回值爲-1。回到usb_init()函數中我們會知道,接下來usb_hub_ cleanup()就會被調用。usb_hub_cleanup()同樣定義於drivers/usb/core/hub.c中:

  1. 2873 void usb_hub_cleanup(void)  
  2. 2874 {  
  3. 2875    kthread_stop(khubd_task);  
  4. 2876  
  5. 2877    /*  
  6. 2878     * Hub resources are freed for us by usb_deregister. It calls  
  7. 2879         * usb_driver_purge on every device which in turn calls that  
  8. 2880     * devices disconnect function if it is using this driver.  
  9. 2881     * The hub_disconnect function takes care of releasing the  
  10. 2882     * individual hub resources. -greg  
  11. 2883     */  
  12. 2884    usb_deregister(&hub_driver);  
  13. 2885 } /* usb_hub_cleanup() */  

這個函數我想沒有任何必要解釋了吧。kthread_stop()和剛纔的kthread_run()對應,usb_deregister()和usb_register()對應。

總之,如果創建子進程出了問題,那麼一切都免談。

反之,如果成功了,那麼kthread_run()的三個參數就是我們要關注的了:第一個是hub_thread(),子進程將從這裏開始執行。第二個是hub_thread(),傳遞的是NULL,第三個參數就是精靈進程的名字ps -el,如下所示:

  1. localhost:/usr/src/linux-2.6.22/drivers/usb/core # ps -el | grep khubd  
  2. 1 S     0  1963    27  0  70  -5 -     0 hub_th ?        00:00:00 khubd  

你就會發現有這麼一個精靈進程運行着。所以,下一步,讓我們進入hub_thread()來查看這個子進程吧。

以下是關於IS_ERR的介紹文字。如果你對內存管理沒有任何興趣,就不用往下看了。要想明白IS_ERR(),首先你得知道有一種空間叫做內核空間,不清楚也不要緊。結合IS_ERR()的代碼來看,來自include/linux/err.h:

  1. 16 #define MAX_ERRNO       4095  
  2. 17  
  3. 18 #ifndef __ASSEMBLY__  
  4. 19  
  5. 20 #define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)  
  6. 21  
  7. 22 static inline void *ERR_PTR(long error)  
  8. 23 {  
  9. 24          return (void *) error;  
  10. 25 }  
  11. 26  
  12. 27 static inline long PTR_ERR(const void *ptr)  
  13. 28 {  
  14. 29      return (long) ptr;  
  15. 30 }  
  16. 31  
  17. 32 static inline long IS_ERR(const void *ptr)  
  18. 33 {  
  19. 34      return IS_ERR_VALUE((unsigned long)ptr);  
  20. 35 }  
  21. 36  
  22. 37 #endif  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章