1. 程式人生 > >Linux下的PCI裝置驅動自動載入問題MODULE_DEVICE_TABLE

Linux下的PCI裝置驅動自動載入問題MODULE_DEVICE_TABLE

以前做驅動時,一般將驅動複製到/lib/modules/$(uname -r)/kernel/driver/目錄後,執行depmod都可以自動載入,但是客戶反映公司一款驅動無法自動載入。後經過與其它版本程式碼對比,才發現是MODULE_DEVICE_TABLE沒有設定引起的異常。

在Linux IIC驅動中看到一段程式碼:

static struct platform_device_id xx_driver_ids[] = {
{
.name= "s3c2410-i2c",
.driver_data   = TYPE_S3C2410,
}, {
.name= "s3c2440-i2c",
.driver_data   = TYPE_S3C2440,
}, { },
};
MODULE_DEVICE_TABLE(platform, xx_driver_ids);

MODULE_DEVICE_TABLE一般用在熱插拔的裝置驅動中。

上述xx_driver_ids結構,是此驅動所支援的裝置列表

作用是:將xx_driver_ids結構輸出到使用者空間,這樣模組載入系統在載入模組時,就知道了什麼模組對應什麼硬體裝置。

用法是:MODULE_DEVICE_TABLE(裝置型別,裝置表),其中,裝置型別,包括USB,PCI等,也可以自己起名字,上述程式碼中是針對不同的平臺分的類;裝置表也是自己定義的,它的最後一項必須是空,用來標識結束。

按照此方法安裝後系統後,在/lib/modules/$(uname -r)/目錄中,modules.alias.modules.pcimap檔案中應該均可以找到驅動模組名稱。此時就可以正常開機自動載入裝置驅動了。

1. MODULE_DEVICE_TABLE (pci, skel_table);
該巨集生成一個名為__mod_pci_device_table的區域性變數,該變數指向第二個引數。核心構建時,depmod程式會在所有模組中搜索符號__mod_pci_device_table,把資料(裝置列表)從模組中抽出,新增到對映檔案/lib/modules/KERNEL_VERSION/modules.pcimap中,當depmod結束之後,所有的PCI裝置連同他們的模組名字都被該檔案列出。當核心告知熱插拔系統一個新的PCI裝置被發現時,熱插拔系統使用modules.pcimap檔案來找尋恰當的驅動程式。 
MODULE_DEVICE_TABLE的第一個引數是裝置的型別,如果是USB裝置,那自然是usb(如果是PCI裝置,那將是pci,這兩個子系統用同一個巨集來註冊所支援的裝置)。後面一個引數是裝置表,這個裝置表的最後一個元素是空的,用於標識結束。例:假如程式碼定義了USB_SKEL_VENDOR_ID是 0xfff0,USB_SKEL_PRODUCT_ID是0xfff0,也就是說,當有一個裝置接到集線器時,usb子系統就會檢查這個裝置的 vendor ID和product ID,如果他們的值是0xfff0時,那麼子系統就會呼叫這個模組作為裝置的驅動。
2. 其他相關巨集的定義      
這些巨集定義在<linux/module.h>下
1)MODULE_AUTHOR(name) 定義驅動的程式設計者,name為string
2)MODULE_LICENSE(license) 定義驅動的license,一般為GPL,或相關公司的license
3)MODULE_DESCRIPTION(desc) 對驅動程式的描述,string
4)MODULE_SUPPORTED_DEVICE(name) 驅動程式所支援的裝置,string
5)MODULE_PARM(var,type)
提供在執行時通過控制檯將引數傳遞給模組(在insmod時)。如果我們想用這個巨集來傳遞命令列引數,那麼在我們的模組中定義一個全域性變數.在insmod模組時,便可以用引數的形式,將具體的實參傳遞給模組中的那個全域性變數.
MODULE_PARM(name,type)
MODULE_PARM(name,type)
MODULE_PARM(name,type)有兩個引數,一個是這個全域性變數的名稱,另一個是這個全域性變數的型別.
而他的型別有一下幾種:
b:
b:
b:位元型
h:短整型
i:整型
l:長整型
s:字串型
在傳遞字串型的引數時,這個全域性變數需要在模組中用Char *來宣告!insmod會自動為其分配記憶體空間。
例如:
int a = 3;
char *st;
MODULE_PARM(a,”i”);
MODULE_PARM(st,”s”);
int a = 3;
char *st;
MODULE_PARM(a,”i”);
MODULE_PARM(st,”s”);
int a = 3;
char *st;
MODULE_PARM(a,”i”);
MODULE_PARM(st,”s”);
在insmod是我們加這樣的引數:
insmode a.o “a = 3″, “st = hello world”
這裡最重要的是,MODULE_PARM()也支援我們最常用的陣列型別。用短線‘-’把兩個數字分開,分別表示陣列引數中的最小位數和最大位數。例如:
int array[8];
MODULE_PARM(array,”1-8i”);
在命令列我們使用加這樣的引數:
insmod a.o “array = 38745,123,4000″
insmod a.o “array = 38745,123,4000″
insmod a.o “array = 38745,123,4000″ 
在那些模組程式設計時,我們往往給這些全域性變數以預設值,如果我們才insmod時沒有傳入引數時,模組會使用這些預設值,而如果我們傳入引數時,這些預設值便被覆蓋掉。
6)MODULE_PARM_DESC(var,desc) 對變數的描述
7)GPL_HEADER()
8)THIS_MODULE 指向全域性變數 __this_module (struct module)的指標。
9)系統對每個模組維護一個usage counter,以便決定何時可以安全的解除安裝模組。
下面的巨集用來對該usage counter操作,usage counter可以通過/proc/modules檔案檢視
MOD_INC_USE_COUNT 
MOD_DEC_USE_COUNT
MOD_IN_USE
MODULE_DEVICE_TABLE
10)EXPORT_SYMTAB 預處理巨集,當在程式中用EXPORT_SYMBOL等巨集時需要定義該巨集。例如,可以在Makefile中定義:-DEXPORT_SYMTAB
__EXPORT_SYMBOL(sym,str)
EXPORT_SYMBOL(var)
11)EXPORT_SYMBOL_NOVERS(var) 匯出一個符合到核心符號表,匯出後,該符合可以供其他模組使用。這個巨集有助於編寫驅動程式時清楚的劃分出層次。可以通過/proc/ksyms檔案或ksyms命令檢視核心符號表。EXPORT_SYMBOL_NOVERS(var),匯出是不帶版本資訊。在使用該巨集時,需定義 EXPORT_SYMBOL_GPL(var)
12)EXPORT_NO_SYMBOLS 顯示指出,該模組不向核心符合表匯出符號
13)SET_MODULE_OWNER