在正式的編寫Linux驅動之前,先將硬件拋開,先來搞清楚幾件事:
1.驅動程序如何編寫?模板是怎樣的?
2.如何編譯驅動程序?
3.Linux內核驅動模塊之間是否可以進行參數傳遞?
4.如何加載驅動程序到內核空間?
5.內核空間如何輸出調試信息?
一、設備驅動模板
主要做的幾件事:
1.將此內核驅動模塊加載到內核中。
2.從內核中將驅動模塊卸載。
3.聲明遵循的開源協議。
如下代碼:
如上Demo為一個基本的內核模塊,其中包括:
1.兩個基本的、必要的頭文件#include<linux/init.h>和#include<linux/module.h>。任何一個內核模塊均包含這兩個頭文件。
2.module_init(hello_init);作為驅動模塊的加載函數,將相應的驅動模塊hello_init加載到Linux內核中。static int __init hello_init(void)函數作為 這個驅動模塊的初始化函數。必須注意的是,此初始化函數必須通過__init聲明,表示此段初始化函數代碼存放在__init段中(具體可以分析Linux系統的鏈接器腳本文件);module_init函數通過回調的方式將hello_init加載到內核。
3.module_exit(hello_exit);將已存在的驅動模塊hello_exit從內核中卸載,值得注意的是驅動模塊的卸載實現函數static void __exit hello_exit(void)也必須要通過__exit聲明。同樣的module_exit函數通過回調的方式調用hello_exit函數,實現卸載此驅動模塊。hello_exit為卸載的實現方法函數。
4.MODULE_LICENSE("GPL");聲明此驅動模塊支持GPL開源協議。GPL,是General Public License的縮寫,是一份GNU通用公共授權非正式的中文翻譯。它并非由自由軟件基金會所發表,亦非使用GNU通用公共授權的軟件的法定發布條款─直有GNU通用公共授權英文原文的版本始具有此等效力。下圖為GPL的logo:
在Linux內核中的軟件代碼,基本上都需要遵循GPL協議。如若代碼無GPL聲明,在使用時將會報警。但萬事無絕對,還是可以繞過GPL協議的,在此不再多說,可找度娘和谷歌。
5.內核中的調試信息輸出直接通過printk函數即可,使用方法和用戶空間的printf相似。以上程序中的printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);表示打印當前函數的信息。
此即為Linux內核驅動模塊的基本程序,關于驅動所實現的功能,就由程序員發揮了,具體后續詳述。
二、Makefile分析
已經編寫好的Demo需要進行編譯,后才能使用。而Makefile為好的管理工具。
如上為本驅動程序的Makefile文件,其中驅動文件名稱為hello_driver.c。詳解如下:
從Makefile分析可知,驅動程序需要依賴于已經編譯成功了的Linux內核源碼而編譯;并且也利用到了Linux內核的頂層目錄的Makefile文件。后編譯生成.ko文件,此即為Linux內核驅動模塊。如下圖:
實際上在這里所使用方法是,將驅動代碼在Linux內核之外進行編譯,然后生成內核驅動模塊。
而在工程項目開發中,則通常直接在Linux內核源碼內添加內核驅動代碼,直接在Linux內核源碼上編譯。具體的方式在此先不談。
三、裝載驅動程序
編譯得到的ko文件為內核驅動模塊,可以直接直接在用戶空間中裝載驅動模塊。
1.開發的方式簡述
(1)本文所使用的開發模型
如上圖為本文所使用的開發模型,一個硬件平臺想要啟動成功Linux操作系統,至少需要滿足3個條件(1)bootloader(常用uboot);(2)Linux內核鏡像(zImage/uImage);(3)根文件系統filesystem。
在這里因為是開發學習,所以只在板卡上燒錄了bootloader(uboot),通過在bootloader(uboot)配置引導啟動參數,實現,當開發平臺上電啟動時,uboot會通過網絡的方式在指定的TFTP服務器上臨時下載可執行的Linux內核鏡像zImage/uImage,并引導Linux kernel啟動當Linux Kernel啟動成功后,在讀取uboot設置的參數,通過NFS服務器在指定的平臺上掛載根文件系統,直到啟動成功。本文的TFTP服務器和NFS服務器在Ubuntu開發環境上搭建。當然 ,重要的前提是板卡與Ubuntu開發環境組成局域網關系。
以下為uboot參數的配置:
這種開發模式基本上常用于三星平臺的教學平臺上。
(2)產品項目常用的開發模型
對于產品的開發而言,基本上不會再使用TFTP和NFS進開發了。而是直接在板卡上燒錄bootloader、zImage和根文件系統filesystem。然后直接從板級啟動。這樣的調試方式就和使用TFTP和NFS的方式有所差別了。此種方式本文不做過多敘述。
2.加載驅動模塊到內核中
1.將編譯生成的驅動模塊拷貝到根文件系統中。
將上文中編譯生成的hello_driver.ko文件拷貝到根文件系統filesystem。
命令:cp hello_driver.ko /opt/filesystem/
注:/opt/filesystem/為板卡通過NFS掛載的根文件系統目錄。存在于開發環境Ubuntu上。
拷貝成功后,可以在板卡串口輸出的調試界面上看到了相應的文件,如下圖:
3.加載驅動模塊
內核驅動模塊的安裝使用insmod命令。
內核模塊安裝: insmod xxx.ko
如上圖,執行insmod hello_driver.ko命令后,會有相應的調試信息輸出。實際上它的輸出為static int __init hello_init(void)函數中的printk的打印信息。表示本函數名和所在代碼的行數。
這里表示安裝驅動模塊成功。
4.卸載驅動模塊
Linux提供了remod命令來卸載內核驅動模塊。
內核模塊卸載: rmmod xxx
如上圖,執行rmmod hello_driver目錄后,出現錯誤,原因是文件系統為剛剛編譯的、純凈的文件系統,所在/lib/目錄下并不存在modules目錄,解決方法是,在/lib/目錄下創建一個modules目錄即可。
執行命令:mkdir /lib/modules
再執行命令rmmod hello_driver卸載內核驅動模塊,如下圖:
還是發生了錯誤,提示找不到3.0.8目錄。實際上因為本文筆者所使用的Linux內核源碼為3.0.8版本的,而模塊驅動的運行依賴/lib/modules/內核版本號目錄,所以實際上需要的是/lib/modules/3.0.8/目錄。解決方式是在/lib/modules/目錄下再創建一個3.0.8目錄。
命令:mkdir /lib/modules/3.0.8
執行命令:remod hello_driver 卸載內核模塊驅動。
如上圖表示卸載成功了,并且卸載函數中的printk打印出了卸載函數的函數名和所在行數。
至此,基本上一個內核驅動模塊的模板和使用已經簡述完畢。