原文出處:http://www.CUOXin.com/jacklu/p/4722563.html
Linux設備分類設備的驅動程序也要像裸機程序那樣進行一些硬件操作,不同的是驅動程序需要"融合進內核里",因此需要在驅動程序中加入操作系統規定的接口,這些接口都是獨立于設備的。雖然操作系統為驅動程序設計者帶來了"麻煩",卻為應用程序設計者帶來了"便利"。
Linux下設備分為三類:字符設備、塊設備、網絡設備。
字符設備是指必須以串行順序訪問的設備,比如觸屏;塊設備是指可以以任意順序訪問的設備,即以塊為單位進行操作,比如鍵盤;
字符設備不經過Cache,塊設備數據經過Cache。兩者的驅動程序設計差異較大。除了網絡設備外,字符設備和塊設備的驅動程序都被映射到文件系統中,通過調用open、read、write、close就能訪問。需要說明一點,C語言的fopen、fread、fwrite、fclose實際上也是做相應的系統調用。下圖是一個Linux下不同驅動種類的結構關系圖:

做好驅動程序開發,需要開發者有良好的硬件基礎、C語言基礎、Linux內核基礎以及多任務并發和控制的基礎。
Linux上瀏覽內核源碼,推薦使用的工具是vim+cscope或者vim+ctags。
有無操作系統的驅動程序區別下面以led驅動為例,來說明有無操作系統的區別。
一般處理器有GPIO有兩個寄存器,即控制寄存器和數據寄存器。
無操作系統時,一般需要的函數有三個,即
LightInit()//設置控制寄存器為輸出模式LightOn()//打開LedLightOff()//熄滅Led
Linux操作系統下,可以使用字符設備驅動程序框架來編寫,根據Linux下的編程習慣,可以重新將其命名為light_init(),light_on,light_off
代碼結構如下:
//定義設備結構體struct light_dev{ struct cdev cdev;//字符設備結構體 unsigned char value;}struct light_dev *light_devp;int light_major = LIGHT_MAJOR; MODULE_AUTHOR(" ");MODULE_LICENSE(" "); int light_open(struct inode *inode, struct file *filp){}int light_release(struct inode *inode, struct file *filp){}//讀寫設備ssize_t light_read(struct file *filp, char _user *buf, size_t count, loff_t *f_pos){}ssize_t light_write(struct file *filp, const char _user *buf, size_t count, loff_t *f_pos){}//ioctlint light_ioctl(struct inode *inode, struct file *flip, unsigned int cmd, unsigned long arg){}struct file_Operations light_fops={ .ower = THIS_MODULE; .read = light_read; .write = light_write; .open = light_open; .release = light_release; .ioctl = light_ioctl;}//設置字符設備cdev結構體static void light_setup_cdev(struct light_dev *dev, int index){}//模塊加載函數int light_init(void)//模塊卸載函數int light_cleanup(void) module_init(light_init)module_exit(light_cleanup)這只是一個程序的結構,可以看出,與裸機的驅動程序相比,Linux下的驅動程序代碼復雜很多。
Linux設備驅動開發的硬件基礎RISC和CISC計算機的區別:RISC指令周期短,代碼量大;CISC指令復雜,指令周期長,代碼量小。
目前ROM基本上都使用Flash,NOR(或非)和NAND(與非)是兩種主流的Flash。
NOR Flash的特點是可在芯片內執行代碼,接口簡單,不需要再加額外的控制芯片;NAND Flash的特點是塊訪問,接口需要再加入控制芯片,不能在芯片內部執行代碼。NAND 的發生位反轉的幾率大于NOR,在使用時,應采用錯誤檢測、錯誤改正算法(EDC/ECC)。Flash都是只能將1寫為0,在燒寫前,需要將Flash全置位,所有字節都為0xff。
DRAM以電荷形式存儲在電容器中,需要定期刷新;SDRAM也是DRAM的范疇。CAM是以內容尋址的特殊RAM,輸入需要查詢的數據,輸出數據地址和匹配標識,在數據檢索中有很大優勢。
*開漏輸出、集電極開路輸出是指需要上拉電阻才能輸出高電平。
驅動工程師對硬件比IC工程師要更宏觀。驅動工程師一般不需要分析時序圖,但是許多企業的驅動工程師還需要承擔電路板的調試工作,因此還需要了解一些電路時序的分析。
真實的電路必須滿足芯片手冊上的建立時間和保持時間的最低要求。查看datasheet時,沒有必要通讀全屏,要學會查看主要的信息內容。
Linux內核代碼結構Linux內核主要由進程調度、內存管理、虛擬文件系統、網絡接口、進程通信組成。
內核空間和用戶空間CPU內部往往都實現了不同的操作模式。
比如ARM的七種工作模式:
ARM+Linux采用SWI,從usr模式進入svc模式;x86處理器包含4個不同的特權級(0-3)下,Linux的用戶代碼運行在特權級3,系統內核運行在特權級0
Linux只能通過系統調用或者硬件中斷完成從用戶空間到內核空間的控制轉換。
內核的編譯與加載在linux內核中增加程序需要完成以下3項工作:
將代碼加入到linux的相應目錄;
在目錄的Kconfig中加入相應的編譯配置選項;
在目錄的Makefile中增加新項目的編譯條目。
Linux下的C編碼風格Windows下,宏全部大寫,變量第一個單詞小寫,其后每一個單詞的首字母都大寫,函數名每個單詞的首字母都大寫。
例如:
#define MYLINUXint myLinux;int MyLinux(void);
而Linux 下,采用如下的風格:
#define MYLINUXint my_linux;int my_linux(void);
Linux代碼縮進使用8個字符,對于結構體、if等{不另起一行,函數另起一行。
do{}while(0)主要用于宏定義中,其使用完全是為了保證宏定義無錯誤的編譯。
goto只用于出現錯誤解決錯誤時。
參考資料:
《Linux設備驅動開發詳解》 宋寶華
新聞熱點
疑難解答