Linux設備驅動程序在Linux內核源代碼中占有很大比例,從2.0、2.2到 2.4版本的內核,源代碼的長度日益增加,其實主要是設備驅動程序在增加。它是一個遵循POSIX標準的免費操作系統。具有BSD和SYSV的擴展特性。與其他操作系統相比,嵌入式Linux系統以其可應用于多種硬件平臺、內核高效穩定、源碼開放、軟件豐富、網絡通信和文件管理機制完善等優良特性而正被作為研究熱點,越來越多的研究人員采用Linux平臺來開發自己的產品。
基于嵌入式Linux內核的系統設備驅動程序開發設計
設備驅動程序的編寫
設備驅動程序是linux內核的一部分,是操作系統內核和機器硬件之間的接口,它由一組函數和一些私有數據組成,是連接應用程序與具體硬件的橋梁。Linux的一個基本特點是它對硬件設備的管理抽象化,系統中的每一個設備都用一個特殊的文件來表示。所有的硬件設備都像普通的文件一樣看待,使用與操作系統相同的標準系統來進行打開、讀寫和關閉。
在Linux 操作系統下有3類主要的設備文件類型:塊設備、字符設備、網絡設備。字符設備是指存取時沒有緩存的設備。可像文件一樣訪問字符設備,字符設備驅動程序負責實現這些行為。系統的控制臺和并口就是字符設備的例子,它們可以很好地用“流”來描述。塊設備是文件系統的宿主,如磁盤。 Linux允許像字符設備那樣讀取塊設備——允許一次傳輸任意數目的字節。結果是,字符設備和塊設備讀取數方式一致。而網絡設備不同于字符設備和塊設備, 它面向的上一層不是文件系統而是網絡協議層,是通過BSD套接口訪問數據。與設備相對應的是三類設備驅動程序,字符設備驅動程序、塊設備驅動程序、網絡設備驅動程序。
字符設備驅動程序、塊設備驅動程序與網絡設備驅動程序的結構體是不同的。
在linux 源代碼linux/ include / linux/ fs. h中定義了字符設備和塊設備驅動程序中必須使用的file_operaTIons結構,每個設備驅動都實現這個接口所定義的部分或全部函數。隨著內核的不斷升級, file_operaTIons結構也越來越大,不同的版本的內核會稍有不同。file_operaTIons定義如下:
struct file_operaTIons{
int( * lseek) ( struct inode * , struct file * , off_t , int) ; int( *release) ( struct inode * , struct file * ) ;
int( * read) ( struct inode * , struct file * , char * , int) ; int( * fsync) ( struct inode *, struct file * ) ;
int( *write) ( struct inode * , struct file * , const char *, int) ; int( * fasync) ( struct inode * , struct file *, int) ;
int( * readdir) ( struct inode , struct file , void * , dilldir) ; int( *check_media_change) ( kdev_t dev) ;
int(*select) ( struct inode *, struct file * , int, select_table * ) ; int( * revalidate) ( kdev_t dev) ; };
int ( * ioctl) ( struct inode * , struct file *, unsigned int, unsigned long) ;
int( *mmap) ( struct inode * , struct file * , struct vm_area_struct * ) ;
int( * open) ( struct inode *, struct file *) ;
應用程序只有通過對設備文件的open、release、read、write、ioctl等才能訪問字符設備和塊設備。用戶自己定義好 file_operaTIons結構后,編寫出設備實際所需要的各操作函數,對于不需要的操作函數用NULL初始化,這些操作函數將被注冊到內核,當應用程序對設備相應的設備文件進行文件操作時,內核會找到相應的操作函數,并進行調用。如果操作函數使用NULL,操作函數就進行默認處理。
對于字符設備而言,llseek( ),read( ),write(),ioctl( ),open( ),release( )這些函數是不可缺的;對于塊設備,open( ),release( ),ioctl(),check_media_change( ),revalidate( )是不可缺少的。
網絡設備結構體 net_device 定義在 includelinuxnetdevice.h 里,如下所示:
struct net_device
{
char name ; int (*init)(struct
net_device *dev);
unsigned short flags ; int (*open)
(struct net_device *dev);
unsigned long base_addr; int
(*stop)(struct net_device *dev)
unsigned int irq ; int
(*hard_start_xmit)(struct sk_buff *skb,
unsigned char dev_addr; struct
net_device *dev);
unsigned char addr_len; int
(*set_mac_address)( struct net_device
unsigned long trans_start; *dev,void* addr);
……
}
定義好net_device結構體后,根據實際情況編寫操作函數,其中hard_start_xmit()函數是用來發送數據的,set_mac_address()是進行網絡參數設置的。
當linux初始化時將調用初始化函數int device_init( ),該函數包括以下內容:
注冊所用設備。linux用設備號來標識字符設備和塊設備。設備號分為主設備號和從設備號,最終形成設備接點。設備節點在訪問字符設備和塊設備的設備驅動程序時將使用。通常主設備號標識設備對應的驅動程序,大多數設備是“一個主設備號對應一個驅動程序”,如:虛擬控制臺和串口終端由驅動程序4管理。次設備號由內核使用,用于確定設備文件所指的設備。字符設備和塊設備注冊時必須先定義好設備號。
字符設備注冊函數如下:
int register_chrdev(unsigned int major ,const char *name, struct file_oprations *fops);
其中 major是主設備號。
由于對網絡設備驅動程序的訪問不需要設備節點,它的注冊函數如下:
int register_netdev(struct net_device *dev)
注冊設備所用的中斷。中斷在現代計算機結構中有重要的地位,操作系統必須提供程序響應中斷的能力。一般是把一個中斷處理程序注冊到系統中去。操作系統在硬件中斷發生后調用驅動程序的處理程序。
注冊中斷所用的函數如下:
int request_irq (unsigned irq,void(*handler)(int,void*,struct pt_regs*),unsigned long flags,const char*device,void* dev_id);
其中,irq是中斷向量;handler是中斷處理函數;flags是中斷處理中的掩碼;devices是設備名;dev_id是在中斷共享使用的id。
當linux不使用該設備時,就要調用清除函數void_devicie_exit ( ),它同初始化函數相對應的,主要是:
注銷設備,字符設備注銷函數如下:
int unregister_chrdev(unsigned int major ,const char *name, struct file_oprations *fops);
注銷中斷,注銷中斷所用的函數如下:
int free_irq (unsigned irq,void(*handler)(int,void*,struct pt_regs*),unsigned long flags,const char*device,void* dev_id);
釋放資源,模塊初始化和清除函數采用module_init(device_init),module_exit(device_exit) 形式
編寫服務子程序
服務于I/O請求的子程序,又稱為驅動程序的上半部分。調用這部分是由于系統調用的結果。這部分程序在執行的時候。系統仍認為是和進行調用的進程屬于同一個進程。 只是用戶態變成了核心態,具有進行此系統調用的用戶程序的運行環境。因此可以在其中調用sleep等與進程運行環境有關的函數。
中斷服務子程序,又稱為驅動程序的下半部分。在Linux系統中。并不是直接從中斷向量表中調用設備驅動程序的中斷服務子程序,而是由Linux系統來接收硬件中斷,再由系統調用中斷服務子程序。中斷可以產生在任何一個進程運行的時候,因此在中斷服務程序被調用的時候。不能依賴于仟何進程的狀態,也就不能調用任何與進程運行環境相關的函數。因為設備驅動程序一般支持同一類型的若干設備,所以一般在系統調用中斷服務程序的時候,都帶有一個或多個參數,以唯一標識請求服務的設備。
設備驅動程序的使用
直接將驅動程序編譯進linux內核
將設備驅動程序復制到 linux/drivers相關的子目錄下,比如字符設備驅動程序 就放在linux/drivers/char下。
修改linux/drivers相關的子目錄的Makefile,
如obj-$(config_dev_driver) +=dev_driver.o,這樣在編譯內核時將會編譯dev_driver.c,生成 dev_driver.o.
對內核進行重新編譯時,進行相關的配置,比如要使用AT91RM9200的UART,就要如下配置:
Character devices -》 Serial drivers -》AT91RM9200 serial port support
將驅動程序編譯成驅動模塊
在設備驅動程序中要有兩個重要函數:
module_init(dev_init),module_exit(dev_exit)
利用相應的交叉編譯器以及編譯命令將驅動程序dev_driver.c編譯成dev_driver.o 這樣的動態驅動模塊。利用insmod命令給系統安裝驅動模塊,如果在/dev目錄下沒有相應的設備文件,就可以使用mknod創建一個設備文件。利用 rmmod命令卸載驅動模塊,設備文件的刪除可以用rm命令。
結語
設備驅動程序的開發是在Linux環境中最復雜的編程任務之一 。它需要和硬件打交道,容易引起系統崩潰,而且很難調試。掌握設備驅動程序的開發技術,將使得開發嵌入式Linux的系統更為迅速和有效。
責任編輯:ct
評論
查看更多