Linux設(shè)備驅(qū)動程序課件_第1頁
Linux設(shè)備驅(qū)動程序課件_第2頁
Linux設(shè)備驅(qū)動程序課件_第3頁
Linux設(shè)備驅(qū)動程序課件_第4頁
Linux設(shè)備驅(qū)動程序課件_第5頁
已閱讀5頁,還剩117頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

Linux設(shè)備驅(qū)動廣州嵌入式軟件公共技術(shù)支持中心梁老師2007年07月Linux設(shè)備驅(qū)動廣州嵌入式軟件公共技術(shù)支持中心設(shè)備驅(qū)動概述操作系統(tǒng)是通過各種驅(qū)動程序來駕馭硬件設(shè)備,它為用戶屏蔽了各種各樣的設(shè)備,硬件設(shè)備的抽象。設(shè)備驅(qū)動程序:處理和管理硬件控制器的軟件。設(shè)備驅(qū)動程序是操作系統(tǒng)內(nèi)核和機器硬件之間的接口。設(shè)備驅(qū)動概述操作系統(tǒng)是通過各種驅(qū)動程序來駕馭硬件設(shè)備,它為用設(shè)備驅(qū)動概述設(shè)備由兩部分組成,一個是被稱為控制器的電器部分,另一個是機械部分。一組寄存器組被賦予到各個控制器。I/O端口包含4組寄存器,即狀態(tài)寄存器,控制寄存器,數(shù)據(jù)輸入寄存器,數(shù)據(jù)輸出寄存器。狀態(tài)寄存器擁有可以被CPU讀取的(狀態(tài))位,用來指示當(dāng)前命令是否執(zhí)行完畢,或者字節(jié)是否可以被讀出或?qū)懭?,以及任何錯誤提示??刂萍拇嫫鲃t用于啟動一條命令(指令)或者改變設(shè)備的(工作)模式。數(shù)據(jù)輸入寄存器用于獲取輸入的數(shù)據(jù)。數(shù)據(jù)輸出寄存器則向CPU發(fā)送結(jié)果。處理器和設(shè)備之間的基本界面是控制和狀態(tài)寄存器。設(shè)備驅(qū)動概述設(shè)備由兩部分組成,一個是被稱為控制器的電器部分,設(shè)備驅(qū)動概述寄存器擁有在I/O空間明確定義的地址范圍。通常這些地址在啟動時被分配。如果設(shè)備是靜態(tài)加載的,各個設(shè)備的地址范圍可能被預(yù)分配。這意味內(nèi)核包含了已存在設(shè)備的驅(qū)動程序。通過運行“cat/proc/ioports”命令檢查其所使用的地址范圍。第一列輸出顯示了端口的范圍而第二列則是擁用這些端口的設(shè)備。

設(shè)備驅(qū)動概述寄存器擁有在I/O空間明確定義的地址范圍。設(shè)備驅(qū)動概述設(shè)備驅(qū)動的概念是非常抽象的并且處于一臺計算上所運行軟件的最低層。由于直接到設(shè)備的硬件特性的限制。每個設(shè)備驅(qū)動都只管理一種單一類型的設(shè)備。如果一個應(yīng)用程序向設(shè)備提出(操作)要求。內(nèi)核會聯(lián)系到對應(yīng)的設(shè)備驅(qū)動,設(shè)備驅(qū)動接著向特定的設(shè)備發(fā)出命令。設(shè)備驅(qū)動是一個函數(shù)集合:包含了許多調(diào)用入口,類似于open,close,read,write,ioctl,llseek等。

設(shè)備驅(qū)動概述設(shè)備驅(qū)動的概念是非常抽象的并且處于一臺計算上所運設(shè)備驅(qū)動概述Linux操作系統(tǒng)把設(shè)備納入文件系統(tǒng)的范疇來管理。文件操作是對設(shè)備操作的組織和抽象。設(shè)備操作則是對文件操作的最終實現(xiàn)。每個設(shè)備都對應(yīng)一個文件名,在內(nèi)核中也就對應(yīng)一個索引節(jié)點。

對文件操作的系統(tǒng)調(diào)用大都適用于設(shè)備文件。

從應(yīng)用程序的角度看,設(shè)備文件邏輯上的空間是一個線性空間(起始地址為0,每讀取一個字節(jié)加1)。從這個邏輯空間到具體設(shè)備物理空間(如磁盤的磁道、扇區(qū))的映射則是由內(nèi)核提供,并被劃分為文件操作和設(shè)備驅(qū)動兩個層次。設(shè)備驅(qū)動概述Linux操作系統(tǒng)把設(shè)備納入文件系統(tǒng)的范疇來管理設(shè)備驅(qū)動概述Linux將設(shè)備分成兩大類。一類像鍵盤那樣以字符(字節(jié))為單位,逐個字符進行輸入/輸出的設(shè)備,稱為字符設(shè)備。一類是像磁盤那樣以塊或扇區(qū)為單位,成塊進行輸入/輸出的設(shè)備,稱為塊設(shè)備。文件系統(tǒng)通常都建立在塊設(shè)備上。設(shè)備驅(qū)動概述Linux將設(shè)備分成兩大類。設(shè)備驅(qū)動概述文件操作和設(shè)備驅(qū)動是對一個具體的設(shè)備操作的不同層次。從這種觀點出發(fā),從概念上可以把一個系統(tǒng)劃分為應(yīng)用、文件系統(tǒng)和設(shè)備驅(qū)動三個層次。設(shè)備驅(qū)動概述文件操作和設(shè)備驅(qū)動是對一個具體的設(shè)備操作的不同層設(shè)備驅(qū)動概述設(shè)備驅(qū)動概述設(shè)備驅(qū)動概述要使一項設(shè)備可以被應(yīng)用程序訪問,首先要在系統(tǒng)中建立一個代表此設(shè)備的設(shè)備文件,這是通過系統(tǒng)調(diào)用mknode()實現(xiàn)的。此外,更重要的是在設(shè)備驅(qū)動層要有這種設(shè)備的驅(qū)動程序。設(shè)備驅(qū)動概述要使一項設(shè)備可以被應(yīng)用程序訪問,首先要在系統(tǒng)中建設(shè)備驅(qū)動概述設(shè)備文件:任何設(shè)備都被當(dāng)作路徑/dev的設(shè)備文件處理,并通過這些設(shè)備文件提供訪問硬件的方法。每個設(shè)備文件除了設(shè)備名外,還有類型、主設(shè)備號、次設(shè)備號這三個屬性。設(shè)備文件是通過mknod系統(tǒng)調(diào)用創(chuàng)建的。其原型為:mknod(constchar*filename,intmode,dev_tdev)mknod/dev/led0c2530設(shè)備驅(qū)動概述設(shè)備文件:設(shè)備驅(qū)動概述主設(shè)備號和次設(shè)備號:主設(shè)備號標(biāo)識設(shè)備對應(yīng)的驅(qū)動程序。一般“一個主設(shè)備號對應(yīng)一個驅(qū)動程序”次設(shè)備號用于確定設(shè)備文件所指的設(shè)備。可通過ls–l“設(shè)備文件名”命令查看設(shè)備的主次設(shè)備號,以及設(shè)備的類型。設(shè)備驅(qū)動概述主設(shè)備號和次設(shè)備號:設(shè)備驅(qū)動概述主設(shè)備號和次設(shè)備號的內(nèi)部表達:Dev_t類型用于保存設(shè)備號,稱為設(shè)備編號。/linux/types.h文件中定義。目前設(shè)備編號dev_t是一個32位的整數(shù),其中12位表示主設(shè)備號,20位表示次設(shè)備號。通過設(shè)備編號獲取主次設(shè)備號:MAJOR(dev_tdev);MINOR(dev_tdev);通過主次設(shè)備號合成設(shè)備編號:MKDEV(intmajor,intminor);Dev_t格式以后可能會發(fā)生變化,但只要使用這些宏,就可保證設(shè)備驅(qū)動程序的正確性。設(shè)備驅(qū)動概述主設(shè)備號和次設(shè)備號的內(nèi)部表達:一些重要的數(shù)據(jù)結(jié)構(gòu)大部分驅(qū)動程序涉及三個重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu):文件操作file_operations結(jié)構(gòu)體文件對象file結(jié)構(gòu)體索引節(jié)點inode結(jié)構(gòu)體一些重要的數(shù)據(jù)結(jié)構(gòu)大部分驅(qū)動程序涉及三個重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu):一些重要的數(shù)據(jù)結(jié)構(gòu)文件操作結(jié)構(gòu)體file_operations結(jié)構(gòu)體file_operations在頭文件linux/fs.h中定義,用來存儲驅(qū)動內(nèi)核模塊提供的對設(shè)備進行各種操作的函數(shù)的指針。結(jié)構(gòu)體的每個域都對應(yīng)著驅(qū)動模塊用來處理某個被請求的事務(wù)的函數(shù)的地址。structfile_operations{ structmodule*owner; loff_t(*llseek)(structfile*,loff_t,int); ssize_t(*read)(structfile*,char__user*,size_t,loff_t*); ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);

。。。

}

一些重要的數(shù)據(jù)結(jié)構(gòu)文件操作結(jié)構(gòu)體file_operation一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員Structmodule*owner,指向擁有該結(jié)構(gòu)體的模塊的指針。方法llseek用來修改文件的當(dāng)前讀寫位置,把新位置作為返回值返回。方法read用來從設(shè)備中讀取數(shù)據(jù)。非負(fù)返回值表示成功讀取的直接數(shù)。方法write向設(shè)備發(fā)送數(shù)據(jù)。方法ioctl提供一種執(zhí)行設(shè)備特定命令的方法。一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員驅(qū)動內(nèi)核模塊是不需要實現(xiàn)每個函數(shù)的。相對應(yīng)的file_operations的項就為NULL。Gcc的語法擴展,使得可以定義該結(jié)構(gòu)體:

structfile_operationsfops={ read:device_read, write:device_write, open:device_open, release:device_release};這種語法清晰,沒有顯示聲明的結(jié)構(gòu)體成員都被gcc初始化為NULL。一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員標(biāo)準(zhǔn)C的標(biāo)記化結(jié)構(gòu)體的初始化方法:

structfile_operationsfops={ .read=device_read, .write=device_write, .open=device_open, .release=device_release };

推薦使用該方法,提高移植性,方法允許對結(jié)構(gòu)體成員進行重新排列。沒有顯示聲明的結(jié)構(gòu)體成員同樣都被gcc初始化為NULL。指向結(jié)構(gòu)體file_operations的指針通常命名為fops。一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員一些重要的數(shù)據(jù)結(jié)構(gòu)文件對象file結(jié)構(gòu)體 文件對象file代表著一個打開的文件。進程通過文件描述符fd與已打開文件的file結(jié)構(gòu)相聯(lián)系。進程通過它對文件的線性邏輯空間進行操作。例如:file->f_op->read();Structfile在<linux/fs.h>中定義。指向結(jié)構(gòu)體structfile的指針通常命名為filp,或者file。建議使用文件指針filp。一些重要的數(shù)據(jù)結(jié)構(gòu)文件對象file結(jié)構(gòu)體 一些重要的數(shù)據(jù)結(jié)構(gòu)文件對象file結(jié)構(gòu)體的成員Structfile_operations*f_op;

與文件相關(guān)的操作結(jié)構(gòu)體指針。與文件相關(guān)的操作是在打開文件的時候確定下來的,也就是確定該指針的值。可在需要的時候,改變指針?biāo)赶虻奈募僮鹘Y(jié)構(gòu)體。用C語言實現(xiàn)面向?qū)ο缶幊痰姆椒ㄖ剌d。其他成員可先忽略,后面具體實例分析。因為設(shè)備驅(qū)動模塊并不自己直接填充結(jié)構(gòu)體file,只是使用file中的數(shù)據(jù)。一些重要的數(shù)據(jù)結(jié)構(gòu)文件對象file結(jié)構(gòu)體的成員一些重要的數(shù)據(jù)結(jié)構(gòu)索引節(jié)點inode結(jié)構(gòu)文件打開,在內(nèi)存建立副本后,由唯一的索引節(jié)點inode描述。與file結(jié)構(gòu)不同。file結(jié)構(gòu)是進程使用的結(jié)構(gòu),進程每打開一個文件,就建立一個file結(jié)構(gòu)。不同的進程打開同一個文件,建立不同的file結(jié)構(gòu)。Inode結(jié)構(gòu)是內(nèi)核使用的結(jié)構(gòu),文件在內(nèi)存建立副本,就建立一個inode結(jié)構(gòu)來描述。一個文件在內(nèi)存里面只有一個inode結(jié)構(gòu)對應(yīng)。一些重要的數(shù)據(jù)結(jié)構(gòu)索引節(jié)點inode結(jié)構(gòu)一些重要的數(shù)據(jù)結(jié)構(gòu)索引節(jié)點inode結(jié)構(gòu)Inode結(jié)構(gòu)包含大量描述文件信息的成員變量。但是對于描述設(shè)備文件的inode,跟設(shè)備驅(qū)動有關(guān)的成員只有兩個。Dev_ti_rdev;包含真正的設(shè)備編號。Structcdev*i_cdev;指向cdev結(jié)構(gòu)體的指針。cdev是表示字符設(shè)備的內(nèi)核數(shù)據(jù)結(jié)構(gòu)。從inode中獲得主設(shè)備號和次設(shè)備號的宏:Unsignedintiminor(structinode*inode);Unsignedintimajor(structinode*inode);一些重要的數(shù)據(jù)結(jié)構(gòu)索引節(jié)點inode結(jié)構(gòu)驅(qū)動程序中的內(nèi)存分配在Linux內(nèi)核模式下,不能使用用戶態(tài)的malloc()和free()函數(shù)申請和釋放內(nèi)存。內(nèi)核編程最常用的內(nèi)存申請和釋放函數(shù)為kmalloc()和kfree(),其原型為:include/linux/kernel.hvoid*kmalloc(unsignedintlen,intpriority);voidkfree(void*__ptr);priority參數(shù):通常設(shè)置為GFP_KERNEL,可能會引起睡眠.如果在中斷服務(wù)程序里申請內(nèi)存則要用GFP_ATOMIC參數(shù),在中斷中是不允許睡眠的。驅(qū)動程序中的內(nèi)存分配在Linux內(nèi)核模式下,不能使用用戶態(tài)的初始化和卸載函數(shù)驅(qū)動程序是內(nèi)核的一部分,因此我們需要給其添加模塊初始化函數(shù),該函數(shù)用來完成對所控設(shè)備的初始化工作,并調(diào)用register_chrdev()函數(shù)注冊字符設(shè)備.intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);major是給定的主設(shè)備號。為0代表什么?name是驅(qū)動的名字(將出現(xiàn)在/proc/devices),fops是設(shè)備驅(qū)動的file_operations結(jié)構(gòu)。register_chrdev將給設(shè)備分配0-255的次設(shè)備號,并且為每一個建立一個缺省的cdev結(jié)構(gòu)。與模塊初始化函數(shù)對應(yīng)的就是模塊卸載函數(shù),需要調(diào)用register_chrdev()的"反函數(shù)"初始化和卸載函數(shù)驅(qū)動程序是內(nèi)核的一部分,因此我們需要給其添加設(shè)備操作函數(shù)集的定義file_operations結(jié)構(gòu)體,驅(qū)動程序只是利用其中的一部分。對于字符設(shè)備來說,要提供的主要入口有:open()、release()、read()、write()、ioctl()等。設(shè)備操作函數(shù)集的定義file_operations結(jié)構(gòu)體,驅(qū)設(shè)備操作函數(shù)集的定義open()函數(shù)對設(shè)備特殊文件進行open()系統(tǒng)調(diào)用時,將調(diào)用驅(qū)動程序的open()函數(shù):int(*open)(structinode*,structfile*);參數(shù)inode為設(shè)備特殊文件的inode(索引結(jié)點)結(jié)構(gòu)的指針,參數(shù)file是指向這一設(shè)備的文件結(jié)構(gòu)的指針。open()的主要任務(wù)是確定硬件處在就緒狀態(tài)、驗證次設(shè)備號的合法性(次設(shè)備號可以用MINOR(inode->i-rdev)取得)、控制使用設(shè)備的進程數(shù)、根據(jù)執(zhí)行情況返回狀態(tài)碼(0表示成功,負(fù)數(shù)表示存在錯誤)等;設(shè)備操作函數(shù)集的定義open()函數(shù)設(shè)備操作函數(shù)集的定義release()函數(shù)當(dāng)最后一個打開設(shè)備的用戶進程執(zhí)行close()系統(tǒng)調(diào)用時,內(nèi)核將調(diào)用驅(qū)動程序的release()函數(shù):void(*release)(structinode*,structfile*);release函數(shù)的主要任務(wù)是清理未結(jié)束的輸入/輸出操作、釋放資源、用戶自定義排他標(biāo)志的復(fù)位等.設(shè)備操作函數(shù)集的定義release()函數(shù)設(shè)備操作函數(shù)集的定義read()函數(shù)Read的任務(wù),就是從設(shè)備拷貝數(shù)據(jù)到用戶空間。當(dāng)對設(shè)備特殊文件進行read()系統(tǒng)調(diào)用時,將調(diào)用驅(qū)動程序read()函數(shù):

ssize_tread(structfile*filp,char__user*buff, size_tcount,loff_t*offp);filp是文件對象指針,count

是請求的傳輸數(shù)據(jù)大小.buff

參數(shù)對write來說是指向持有被寫入數(shù)據(jù)的緩存,對read則是放入新數(shù)據(jù)的空緩存.offp

是指向一個“l(fā)ongoffsettype”的指針,它指出用戶正在存取的文件位置.返回值是“signedsizetype”類型;設(shè)備操作函數(shù)集的定義read()函數(shù)設(shè)備操作函數(shù)集的定義write()函數(shù)Write的任務(wù),則從用戶空間拷貝數(shù)據(jù)到設(shè)備。當(dāng)設(shè)備特殊文件進行write()系統(tǒng)調(diào)用時,將調(diào)用驅(qū)動程序的write()函數(shù):

ssize_twrite(structfile*filp,constchar__user *buff,size_tcount,loff_t*offp);filp是文件對象指針,count

是請求的傳輸數(shù)據(jù)大小.buff

參數(shù)對write來說是指向持有被寫入數(shù)據(jù)的緩存,對read則是放入新數(shù)據(jù)的空緩存.offp

是指向一個“l(fā)ongoffsettype”的指針,它指出用戶正在存取的文件位置.返回值是“signedsizetype”類型;設(shè)備操作函數(shù)集的定義write()函數(shù)設(shè)備操作函數(shù)集的定義read和write方法的buff參數(shù)是用戶空間指針,不能被內(nèi)核代碼直接解引用。__user字符串只是形式上的說明,表明是用戶空間地址。驅(qū)動必須能夠存取用戶空間緩存以完成它的工作。內(nèi)核如何解決這個問題?為安全起見,內(nèi)核提供專用的函數(shù)來完成對用戶空間的存取。這些專用函數(shù)在<asm/uaccess.h>中聲明。

unsignedlongcopy_to_user(void__user*to,constvoid*from,unsignedlongcount); unsignedlongcopy_from_user(void*to,constvoid__user*from,unsignedlongcount);大多數(shù)讀寫函數(shù)都會調(diào)用這兩個函數(shù),用于跟應(yīng)用程序空間交流信息。設(shè)備操作函數(shù)集的定義read和write方法的bufRead和Write方法典型的Read函數(shù)對參數(shù)的使用。Read和Write方法典型的Read函數(shù)對參數(shù)的使用。S3C2410的I/O介紹S3C2410有117個復(fù)用功能輸入輸出端口引腳,這些引腳是:PortA(GPA):32個輸入/輸出端口PortB(GPB):11個輸入/輸出端口PortC(GPC):16個輸入/輸出端口PortD(GPD):16個輸入/輸出端口PortE(GPE):16個輸入/輸出端口PortF(GPF):8個輸入/輸出端口PortG(GPG):16個輸入/輸出端口PortH(GPH):11個輸入/輸出端口S3C2410的I/O介紹S3C2410有117個復(fù)S3C2410的I/O介紹端口控制說明端口配置寄存器(GPACON――GPHCON) 大部分的引腳是復(fù)用的,所以必須對于每個引腳要求定義一個功能,端口配置寄存器定義了每個引腳的功能。端口數(shù)據(jù)寄存器(GPADAT――GPHDAT) 如果端口配置成輸出端口,數(shù)據(jù)能夠被寫到端口數(shù)據(jù)寄存器的對應(yīng)位,然后通過管腳輸出。如果端口配置成輸入端口,能從端口數(shù)據(jù)寄存器對應(yīng)的位中讀出管腳上的電平端口上拉寄存器(GPBUP――GPHUP) 端口上拉寄存器控制著每個端口組的上拉寄存器的使能或禁止,當(dāng)對應(yīng)位為0,這個引腳的上拉寄存器是允許的,當(dāng)為1時,上拉寄存器是禁止的。S3C2410的I/O介紹端口控制說明MIZI提供的S3C2410.H使用一個32位的數(shù)來表示端口的使用情況。模式|上拉|端口|端口引腳MODE|PULLUP|PORT|OFS不需要自己手動組合,通過宏定義以及SHIFT和MASK組合。見程序MIZI提供的S3C2410.H使用一個32位的數(shù)來表示端口MIZI提供的S3C2410.H端口的表示#definePORTA_OFS 0#definePORTB_OFS 1#definePORTC_OFS 2#definePORTD_OFS 3#definePORTE_OFS 4#definePORTF_OFS 5#definePORTG_OFS 6#definePORTH_OFS 7MIZI提供的S3C2410.H端口的表示MIZI提供的S3C2410.H端口引腳的表示#defineGPIO_A0 MAKE_GPIO_NUM(PORTA_OFS,0)#defineGPIO_A1 MAKE_GPIO_NUM(PORTA_OFS,1)#defineGPIO_A2 MAKE_GPIO_NUM(PORTA_OFS,2)#defineGPIO_A3 MAKE_GPIO_NUM(PORTA_OFS,3)。。。#defineMAKE_GPIO_NUM(p,o) ((p<<GPIO_PORT_SHIFTT)|(o<<GPIO_OFS_SHIFT))MIZI提供的S3C2410.H端口引腳的表示MIZI提供的S3C2410.Hset_gpio_ctrl(x)

功能:配置端口引腳的功能,設(shè)置IO口控制寄存器和上拉寄存器用法:set_gpio_ctrl(模式|上拉?|IO腳)模式|是否上拉|IO腳,在S3C2410.h中都有其定義好的名字。

set_gpio_ctrl(GPIO_E11|GPIO_PULLUP_DIS|GPIO_MODE_OUT);MIZI提供的S3C2410.Hset_gpio_ctrl(MIZI提供的S3C2410.Hwrite_gpio_bit(x,v)功能:把端口對應(yīng)的端口數(shù)據(jù)寄存器x位設(shè)置為vwrite_gpio_bit(GPIO_E11,0);read_gpio_bit(x)

功能:把端口數(shù)據(jù)寄存器x位的狀態(tài)讀入,函數(shù)返回值既是其狀態(tài)read_gpio_bit(GPIO_G11);MIZI提供的S3C2410.Hwrite_gpio_bitMIZI提供的S3C2410.Hwrite_gpio_reg(x,v)功能:把端口數(shù)據(jù)寄存器x設(shè)置為vread_gpio_reg(x)

功能:讀取端口數(shù)據(jù)寄存器x,函數(shù)返回值既是其數(shù)據(jù)MIZI提供的S3C2410.Hwrite_gpio_reg按驅(qū)動的框架寫好驅(qū)動,實現(xiàn)初始化、卸載函數(shù),以及file_opertation操作集。對于led使用的gpio函數(shù),內(nèi)核沒有輸出作為公開符號,所以需要手動修改內(nèi)核代碼,代碼放在arch/arm/march-s3c2410/gpio.c,輸出符號:EXPORT_SYMBOL(s3c2410_gpio_setpin);EXPORT_SYMBOL(s3c2410_gpio_cfgpin);EXPORT_SYMBOL(s3c2410_gpio_pullup);編寫驅(qū)動模塊的Makefile。交叉編譯驅(qū)動模塊NFS加載驅(qū)動模塊,進行測試。按驅(qū)動的框架寫好驅(qū)動,實現(xiàn)初始化、卸載函數(shù),以及file_o嵌入式鍵盤驅(qū)動廣州嵌入式軟件公共技術(shù)支持中心梁老師2007年7月嵌入式鍵盤驅(qū)動廣州嵌入式軟件公共技術(shù)支持中心注冊中斷服務(wù)例程

中斷線是一個寶貴且常常有限的資源,特別當(dāng)它們只有15個時。內(nèi)核維護一個中斷線的注冊表。要使用中斷線,就要進行中斷線的申請,也就是IRQ(InterruptReQuirement),因此我們也常把申請一條中斷線稱為申請一個IRQ或者是申請一個中斷號。IRQ線是從0開始順序編號的;第一條IRQ線通常表示成IRQ0。IRQn的缺省向量是n+32。注冊中斷服務(wù)例程中斷線是一個寶貴且常常有限的資源,特別當(dāng)注冊中斷服務(wù)例程

并不是每個設(shè)備都可以向中斷線上發(fā)中斷信號的,只有對某一條確定的中斷線擁有了控制權(quán),才可以向這條中斷線上發(fā)送信號。計算機的外部設(shè)備越來越多,所以15條中斷線已經(jīng)不夠用了,中斷線是非常寶貴的資源。只有當(dāng)設(shè)備需要中斷的時候才申請占用一個IRQ,或者是在申請IRQ時采用共享中斷的方式,這樣可以讓更多的設(shè)備使用中斷。注冊中斷服務(wù)例程并不是每個設(shè)備都可以向中斷線上發(fā)中斷信號的注冊中斷服務(wù)例程

在<linux/interrupt.h>實現(xiàn)中斷注冊接口: intrequest_irq(unsignedintirq, irqreturn_t(*handler)(int,void*,structpt_regs*), unsignedlongflags, constchar*dev_name, void*dev_id ); voidfree_irq(unsignedintirq,void*dev_id);request_irq的返回值是0指示申請成功,為負(fù)值時表示錯誤碼。函數(shù)返回-EBUSY表示已經(jīng)有另一個驅(qū)動占用了所要申請的中斷線。注冊中斷服務(wù)例程在<linux/interrupt.h>注冊中斷服務(wù)例程

request_irq的參數(shù)說明:unsignedintirq,要申請的中斷號。對某些設(shè)備,如傳統(tǒng)PC設(shè)備上的系統(tǒng)時鐘或鍵盤,這個值通常是預(yù)先確定的。而對于大多數(shù)其它設(shè)備來說,這個值要么可以通過探測獲取,要么可以動態(tài)確定。irqreturn_t(*handler)(int,void*,structpt_regs*),要安裝的中斷處理函數(shù)指針。后面介紹。注冊中斷服務(wù)例程request_irq的參數(shù)說明:注冊中斷服務(wù)例程

request_irq的參數(shù)說明:unsignedlongflags,

與中斷管理相關(guān)的位掩碼選項。constchar*dev_name,

用在/proc/interrupts中顯示中斷的擁有者。void*dev_id

這個指針用于共享的中斷線。做為驅(qū)動程序的私有數(shù)據(jù)區(qū)(可用來識別那個設(shè)備產(chǎn)生的中斷)。不使用共享中斷線方式時,可設(shè)置為NULL。注冊中斷服務(wù)例程request_irq的參數(shù)說明:注冊中斷服務(wù)例程

flags參數(shù)的詳細(xì)說明:Flags的每個位有不同含義SA_INTERRUPT

當(dāng)該位被設(shè)置時,表示這是一個“快速”中斷??焖僦袛嗵幚砝踢\行時,屏蔽中斷。SA_SHIRQ

這個位表示中斷可以在設(shè)備間共享。注冊中斷服務(wù)例程flags參數(shù)的詳細(xì)說明:proc文件系統(tǒng)中的中斷信息/proc/interrupts反映系統(tǒng)的中斷信息第一列是IRQ號給出每個中斷線發(fā)生中斷的次數(shù)。給出處理中斷的可編程中斷控制器。給出在該中斷號上注冊中斷處理例程的設(shè)備名稱。proc文件系統(tǒng)中的中斷信息/proc/interrupt實現(xiàn)中斷處理例程首先中斷處理例程也是普通的C程序。特別之處:在中斷時間內(nèi)運行,不能向用戶空間發(fā)送或者接收數(shù)據(jù)。不能做任何導(dǎo)致休眠的操作。不能調(diào)用schedule函數(shù)。無論快速還是慢速中斷處理例程,都應(yīng)該設(shè)計成執(zhí)行時間盡可能短。實現(xiàn)中斷處理例程首先中斷處理例程也是普通的C程序。實現(xiàn)中斷處理例程中斷處理函數(shù)的參數(shù)和返回值irqreturn_t(*handler)(int,void*,structpt_regs*)irqreturn_tshort_interrupt(intirq,void*dev_id,structpt_regs*regs)Irq中斷號Dev_id驅(qū)動程序可用的數(shù)據(jù)區(qū),通??蓚鬟f指向描述設(shè)備的數(shù)據(jù)結(jié)構(gòu)指針。structpt_regs*regs,保存了處理器進入中斷代碼之前的cpu寄存器的值。一般驅(qū)動可不要。實現(xiàn)中斷處理例程中斷處理函數(shù)的參數(shù)和返回值矩陣式鍵盤原理矩陣式鍵盤一般適用于按鍵數(shù)量較多的場合,它由行線和列線組成,按鍵位于行、列的交叉點上。如圖所示,一個4×4的行、列結(jié)構(gòu)可以構(gòu)成一個有16個按鍵的鍵盤。矩陣式鍵盤原理矩陣式鍵盤一般適用于按鍵數(shù)量較多的場合,它由行矩陣式鍵盤原理按鍵設(shè)置在行、列交叉點上,行、列分別連接到按鍵開關(guān)的兩端。行線通過上拉電阻接到十5V上。平時無按鍵動作時,行線處于高電平狀態(tài);而當(dāng)有健按下時,行線電平狀態(tài)將由通過此按鍵的列線電平?jīng)Q定:列線電平如果為低,行線電平為低;列線電平如果為高,則行線電平亦為高。這一點是識別矩陣式鍵盤是否被按下的關(guān)鍵所在。矩陣式鍵盤原理按鍵設(shè)置在行、列交叉點上,行、列分別連接到按鍵矩陣式鍵盤原理矩陣鍵盤按鍵的識別方法分兩步進行:①識別鍵盤哪一行的鍵被按下。讓所有列線均為低電平,檢查各行線電平是否為低。如果有行線為低,則說明該行有鍵被按下,否則說明無鍵被按下。②如果某行有鍵被按下,識別鍵盤哪一列的鍵被按下(亦稱之為掃描法)。逐列置低電平,并置其余各列為高電平.檢查各行線電平的變化。如果行電平變?yōu)榈碗娖?,則可確定此行此列交叉點處按鍵被按下。矩陣式鍵盤原理矩陣鍵盤按鍵的識別方法分兩步進行:鍵盤的硬件實現(xiàn)鍵盤的硬件實現(xiàn)鍵盤的硬件實現(xiàn)4X4矩陣鍵盤四個輸入引腳:

EINT0-----(GPF0)----INPUT EINT2-----(GPF2)----INPUT EINT11-----(GPG3)----INPUT EINT19-----(GPG11)----INPUT四個輸出引腳:

KEYSCAN0---(GPE11)----OUTPUT KEYSCAN1---(GPG6)----OUTPUT KEYSCAN2---(GPE13)----OUTPUT KEYSCAN3---(GPG2)----OUTPUT鍵盤的硬件實現(xiàn)4X4矩陣鍵盤鍵盤的驅(qū)動實現(xiàn)引入結(jié)構(gòu)體key_info對按鍵進行描述staticstructkey_info{ intirq_no;//外部中斷號

unsignedintgpio_port;//輸入端口,EINT unsignedintgpio_port_kscan;//輸出端口,OUTPUT intkey_no;//按鍵序號,或者名字}key_info_tab[16]={ ……}鍵盤的驅(qū)動實現(xiàn)引入結(jié)構(gòu)體key_info對按鍵進行描述鍵盤初始化程序staticint__initmatrix4_buttons_init(void){ 注冊字符設(shè)備register_chrdev(……); 初始化按鍵對應(yīng)的輸出端口buttons_io_port_init(); 采用中斷機制,注冊中斷號request_irqs();}鍵盤初始化程序staticint__initmatri鍵盤初始化程序/*初始化kscan口為輸出0*/staticvoidbuttons_io_port_init(void){ inti; for(i=0;i<sizeofkscan/sizeofkscan[1];i++){

set_gpio_ctrl(kscan[i]|GPIO_PULLUP_DIS|GPIO_MODE_OUT); write_gpio_bit(kscan[i],0); }}鍵盤初始化程序/*初始化kscan口為輸出0*/請求注冊中斷staticintrequest_irqs(void){ for(i=0;i<使用中斷個數(shù);i++){

設(shè)置與外部中斷號相對應(yīng)的GPIO端口以及模式 請求中斷號,并注冊中斷響應(yīng)函數(shù)。

}}請求注冊中斷staticintrequest_irqs(鍵盤驅(qū)動的卸載函數(shù)staticvoid__exitmatrix4_buttons_exit(void){ 釋放中斷free_irqs(…); 注銷字符設(shè)備unregister_chrdev(…);}鍵盤驅(qū)動的卸載函數(shù)staticvoid__exitma按鍵中斷處理buttons_irqstaticvoidbuttons_irq(intirq,void*dev_id,structpt_regs*reg){ 設(shè)置GPIO為輸入狀態(tài) 鍵盤掃描 喚醒按鍵等待隊列的進程 重新設(shè)置GPIO為輸出 重新設(shè)置中斷}按鍵中斷處理buttons_irqstaticvoidLinux設(shè)備驅(qū)動廣州嵌入式軟件公共技術(shù)支持中心梁老師2007年07月Linux設(shè)備驅(qū)動廣州嵌入式軟件公共技術(shù)支持中心設(shè)備驅(qū)動概述操作系統(tǒng)是通過各種驅(qū)動程序來駕馭硬件設(shè)備,它為用戶屏蔽了各種各樣的設(shè)備,硬件設(shè)備的抽象。設(shè)備驅(qū)動程序:處理和管理硬件控制器的軟件。設(shè)備驅(qū)動程序是操作系統(tǒng)內(nèi)核和機器硬件之間的接口。設(shè)備驅(qū)動概述操作系統(tǒng)是通過各種驅(qū)動程序來駕馭硬件設(shè)備,它為用設(shè)備驅(qū)動概述設(shè)備由兩部分組成,一個是被稱為控制器的電器部分,另一個是機械部分。一組寄存器組被賦予到各個控制器。I/O端口包含4組寄存器,即狀態(tài)寄存器,控制寄存器,數(shù)據(jù)輸入寄存器,數(shù)據(jù)輸出寄存器。狀態(tài)寄存器擁有可以被CPU讀取的(狀態(tài))位,用來指示當(dāng)前命令是否執(zhí)行完畢,或者字節(jié)是否可以被讀出或?qū)懭?,以及任何錯誤提示??刂萍拇嫫鲃t用于啟動一條命令(指令)或者改變設(shè)備的(工作)模式。數(shù)據(jù)輸入寄存器用于獲取輸入的數(shù)據(jù)。數(shù)據(jù)輸出寄存器則向CPU發(fā)送結(jié)果。處理器和設(shè)備之間的基本界面是控制和狀態(tài)寄存器。設(shè)備驅(qū)動概述設(shè)備由兩部分組成,一個是被稱為控制器的電器部分,設(shè)備驅(qū)動概述寄存器擁有在I/O空間明確定義的地址范圍。通常這些地址在啟動時被分配。如果設(shè)備是靜態(tài)加載的,各個設(shè)備的地址范圍可能被預(yù)分配。這意味內(nèi)核包含了已存在設(shè)備的驅(qū)動程序。通過運行“cat/proc/ioports”命令檢查其所使用的地址范圍。第一列輸出顯示了端口的范圍而第二列則是擁用這些端口的設(shè)備。

設(shè)備驅(qū)動概述寄存器擁有在I/O空間明確定義的地址范圍。設(shè)備驅(qū)動概述設(shè)備驅(qū)動的概念是非常抽象的并且處于一臺計算上所運行軟件的最低層。由于直接到設(shè)備的硬件特性的限制。每個設(shè)備驅(qū)動都只管理一種單一類型的設(shè)備。如果一個應(yīng)用程序向設(shè)備提出(操作)要求。內(nèi)核會聯(lián)系到對應(yīng)的設(shè)備驅(qū)動,設(shè)備驅(qū)動接著向特定的設(shè)備發(fā)出命令。設(shè)備驅(qū)動是一個函數(shù)集合:包含了許多調(diào)用入口,類似于open,close,read,write,ioctl,llseek等。

設(shè)備驅(qū)動概述設(shè)備驅(qū)動的概念是非常抽象的并且處于一臺計算上所運設(shè)備驅(qū)動概述Linux操作系統(tǒng)把設(shè)備納入文件系統(tǒng)的范疇來管理。文件操作是對設(shè)備操作的組織和抽象。設(shè)備操作則是對文件操作的最終實現(xiàn)。每個設(shè)備都對應(yīng)一個文件名,在內(nèi)核中也就對應(yīng)一個索引節(jié)點。

對文件操作的系統(tǒng)調(diào)用大都適用于設(shè)備文件。

從應(yīng)用程序的角度看,設(shè)備文件邏輯上的空間是一個線性空間(起始地址為0,每讀取一個字節(jié)加1)。從這個邏輯空間到具體設(shè)備物理空間(如磁盤的磁道、扇區(qū))的映射則是由內(nèi)核提供,并被劃分為文件操作和設(shè)備驅(qū)動兩個層次。設(shè)備驅(qū)動概述Linux操作系統(tǒng)把設(shè)備納入文件系統(tǒng)的范疇來管理設(shè)備驅(qū)動概述Linux將設(shè)備分成兩大類。一類像鍵盤那樣以字符(字節(jié))為單位,逐個字符進行輸入/輸出的設(shè)備,稱為字符設(shè)備。一類是像磁盤那樣以塊或扇區(qū)為單位,成塊進行輸入/輸出的設(shè)備,稱為塊設(shè)備。文件系統(tǒng)通常都建立在塊設(shè)備上。設(shè)備驅(qū)動概述Linux將設(shè)備分成兩大類。設(shè)備驅(qū)動概述文件操作和設(shè)備驅(qū)動是對一個具體的設(shè)備操作的不同層次。從這種觀點出發(fā),從概念上可以把一個系統(tǒng)劃分為應(yīng)用、文件系統(tǒng)和設(shè)備驅(qū)動三個層次。設(shè)備驅(qū)動概述文件操作和設(shè)備驅(qū)動是對一個具體的設(shè)備操作的不同層設(shè)備驅(qū)動概述設(shè)備驅(qū)動概述設(shè)備驅(qū)動概述要使一項設(shè)備可以被應(yīng)用程序訪問,首先要在系統(tǒng)中建立一個代表此設(shè)備的設(shè)備文件,這是通過系統(tǒng)調(diào)用mknode()實現(xiàn)的。此外,更重要的是在設(shè)備驅(qū)動層要有這種設(shè)備的驅(qū)動程序。設(shè)備驅(qū)動概述要使一項設(shè)備可以被應(yīng)用程序訪問,首先要在系統(tǒng)中建設(shè)備驅(qū)動概述設(shè)備文件:任何設(shè)備都被當(dāng)作路徑/dev的設(shè)備文件處理,并通過這些設(shè)備文件提供訪問硬件的方法。每個設(shè)備文件除了設(shè)備名外,還有類型、主設(shè)備號、次設(shè)備號這三個屬性。設(shè)備文件是通過mknod系統(tǒng)調(diào)用創(chuàng)建的。其原型為:mknod(constchar*filename,intmode,dev_tdev)mknod/dev/led0c2530設(shè)備驅(qū)動概述設(shè)備文件:設(shè)備驅(qū)動概述主設(shè)備號和次設(shè)備號:主設(shè)備號標(biāo)識設(shè)備對應(yīng)的驅(qū)動程序。一般“一個主設(shè)備號對應(yīng)一個驅(qū)動程序”次設(shè)備號用于確定設(shè)備文件所指的設(shè)備??赏ㄟ^ls–l“設(shè)備文件名”命令查看設(shè)備的主次設(shè)備號,以及設(shè)備的類型。設(shè)備驅(qū)動概述主設(shè)備號和次設(shè)備號:設(shè)備驅(qū)動概述主設(shè)備號和次設(shè)備號的內(nèi)部表達:Dev_t類型用于保存設(shè)備號,稱為設(shè)備編號。/linux/types.h文件中定義。目前設(shè)備編號dev_t是一個32位的整數(shù),其中12位表示主設(shè)備號,20位表示次設(shè)備號。通過設(shè)備編號獲取主次設(shè)備號:MAJOR(dev_tdev);MINOR(dev_tdev);通過主次設(shè)備號合成設(shè)備編號:MKDEV(intmajor,intminor);Dev_t格式以后可能會發(fā)生變化,但只要使用這些宏,就可保證設(shè)備驅(qū)動程序的正確性。設(shè)備驅(qū)動概述主設(shè)備號和次設(shè)備號的內(nèi)部表達:一些重要的數(shù)據(jù)結(jié)構(gòu)大部分驅(qū)動程序涉及三個重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu):文件操作file_operations結(jié)構(gòu)體文件對象file結(jié)構(gòu)體索引節(jié)點inode結(jié)構(gòu)體一些重要的數(shù)據(jù)結(jié)構(gòu)大部分驅(qū)動程序涉及三個重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu):一些重要的數(shù)據(jù)結(jié)構(gòu)文件操作結(jié)構(gòu)體file_operations結(jié)構(gòu)體file_operations在頭文件linux/fs.h中定義,用來存儲驅(qū)動內(nèi)核模塊提供的對設(shè)備進行各種操作的函數(shù)的指針。結(jié)構(gòu)體的每個域都對應(yīng)著驅(qū)動模塊用來處理某個被請求的事務(wù)的函數(shù)的地址。structfile_operations{ structmodule*owner; loff_t(*llseek)(structfile*,loff_t,int); ssize_t(*read)(structfile*,char__user*,size_t,loff_t*); ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);

。。。

}

一些重要的數(shù)據(jù)結(jié)構(gòu)文件操作結(jié)構(gòu)體file_operation一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員Structmodule*owner,指向擁有該結(jié)構(gòu)體的模塊的指針。方法llseek用來修改文件的當(dāng)前讀寫位置,把新位置作為返回值返回。方法read用來從設(shè)備中讀取數(shù)據(jù)。非負(fù)返回值表示成功讀取的直接數(shù)。方法write向設(shè)備發(fā)送數(shù)據(jù)。方法ioctl提供一種執(zhí)行設(shè)備特定命令的方法。一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員驅(qū)動內(nèi)核模塊是不需要實現(xiàn)每個函數(shù)的。相對應(yīng)的file_operations的項就為NULL。Gcc的語法擴展,使得可以定義該結(jié)構(gòu)體:

structfile_operationsfops={ read:device_read, write:device_write, open:device_open, release:device_release};這種語法清晰,沒有顯示聲明的結(jié)構(gòu)體成員都被gcc初始化為NULL。一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員標(biāo)準(zhǔn)C的標(biāo)記化結(jié)構(gòu)體的初始化方法:

structfile_operationsfops={ .read=device_read, .write=device_write, .open=device_open, .release=device_release };

推薦使用該方法,提高移植性,方法允許對結(jié)構(gòu)體成員進行重新排列。沒有顯示聲明的結(jié)構(gòu)體成員同樣都被gcc初始化為NULL。指向結(jié)構(gòu)體file_operations的指針通常命名為fops。一些重要的數(shù)據(jù)結(jié)構(gòu)file_operations重要的成員一些重要的數(shù)據(jù)結(jié)構(gòu)文件對象file結(jié)構(gòu)體 文件對象file代表著一個打開的文件。進程通過文件描述符fd與已打開文件的file結(jié)構(gòu)相聯(lián)系。進程通過它對文件的線性邏輯空間進行操作。例如:file->f_op->read();Structfile在<linux/fs.h>中定義。指向結(jié)構(gòu)體structfile的指針通常命名為filp,或者file。建議使用文件指針filp。一些重要的數(shù)據(jù)結(jié)構(gòu)文件對象file結(jié)構(gòu)體 一些重要的數(shù)據(jù)結(jié)構(gòu)文件對象file結(jié)構(gòu)體的成員Structfile_operations*f_op;

與文件相關(guān)的操作結(jié)構(gòu)體指針。與文件相關(guān)的操作是在打開文件的時候確定下來的,也就是確定該指針的值??稍谛枰臅r候,改變指針?biāo)赶虻奈募僮鹘Y(jié)構(gòu)體。用C語言實現(xiàn)面向?qū)ο缶幊痰姆椒ㄖ剌d。其他成員可先忽略,后面具體實例分析。因為設(shè)備驅(qū)動模塊并不自己直接填充結(jié)構(gòu)體file,只是使用file中的數(shù)據(jù)。一些重要的數(shù)據(jù)結(jié)構(gòu)文件對象file結(jié)構(gòu)體的成員一些重要的數(shù)據(jù)結(jié)構(gòu)索引節(jié)點inode結(jié)構(gòu)文件打開,在內(nèi)存建立副本后,由唯一的索引節(jié)點inode描述。與file結(jié)構(gòu)不同。file結(jié)構(gòu)是進程使用的結(jié)構(gòu),進程每打開一個文件,就建立一個file結(jié)構(gòu)。不同的進程打開同一個文件,建立不同的file結(jié)構(gòu)。Inode結(jié)構(gòu)是內(nèi)核使用的結(jié)構(gòu),文件在內(nèi)存建立副本,就建立一個inode結(jié)構(gòu)來描述。一個文件在內(nèi)存里面只有一個inode結(jié)構(gòu)對應(yīng)。一些重要的數(shù)據(jù)結(jié)構(gòu)索引節(jié)點inode結(jié)構(gòu)一些重要的數(shù)據(jù)結(jié)構(gòu)索引節(jié)點inode結(jié)構(gòu)Inode結(jié)構(gòu)包含大量描述文件信息的成員變量。但是對于描述設(shè)備文件的inode,跟設(shè)備驅(qū)動有關(guān)的成員只有兩個。Dev_ti_rdev;包含真正的設(shè)備編號。Structcdev*i_cdev;指向cdev結(jié)構(gòu)體的指針。cdev是表示字符設(shè)備的內(nèi)核數(shù)據(jù)結(jié)構(gòu)。從inode中獲得主設(shè)備號和次設(shè)備號的宏:Unsignedintiminor(structinode*inode);Unsignedintimajor(structinode*inode);一些重要的數(shù)據(jù)結(jié)構(gòu)索引節(jié)點inode結(jié)構(gòu)驅(qū)動程序中的內(nèi)存分配在Linux內(nèi)核模式下,不能使用用戶態(tài)的malloc()和free()函數(shù)申請和釋放內(nèi)存。內(nèi)核編程最常用的內(nèi)存申請和釋放函數(shù)為kmalloc()和kfree(),其原型為:include/linux/kernel.hvoid*kmalloc(unsignedintlen,intpriority);voidkfree(void*__ptr);priority參數(shù):通常設(shè)置為GFP_KERNEL,可能會引起睡眠.如果在中斷服務(wù)程序里申請內(nèi)存則要用GFP_ATOMIC參數(shù),在中斷中是不允許睡眠的。驅(qū)動程序中的內(nèi)存分配在Linux內(nèi)核模式下,不能使用用戶態(tài)的初始化和卸載函數(shù)驅(qū)動程序是內(nèi)核的一部分,因此我們需要給其添加模塊初始化函數(shù),該函數(shù)用來完成對所控設(shè)備的初始化工作,并調(diào)用register_chrdev()函數(shù)注冊字符設(shè)備.intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);major是給定的主設(shè)備號。為0代表什么?name是驅(qū)動的名字(將出現(xiàn)在/proc/devices),fops是設(shè)備驅(qū)動的file_operations結(jié)構(gòu)。register_chrdev將給設(shè)備分配0-255的次設(shè)備號,并且為每一個建立一個缺省的cdev結(jié)構(gòu)。與模塊初始化函數(shù)對應(yīng)的就是模塊卸載函數(shù),需要調(diào)用register_chrdev()的"反函數(shù)"初始化和卸載函數(shù)驅(qū)動程序是內(nèi)核的一部分,因此我們需要給其添加設(shè)備操作函數(shù)集的定義file_operations結(jié)構(gòu)體,驅(qū)動程序只是利用其中的一部分。對于字符設(shè)備來說,要提供的主要入口有:open()、release()、read()、write()、ioctl()等。設(shè)備操作函數(shù)集的定義file_operations結(jié)構(gòu)體,驅(qū)設(shè)備操作函數(shù)集的定義open()函數(shù)對設(shè)備特殊文件進行open()系統(tǒng)調(diào)用時,將調(diào)用驅(qū)動程序的open()函數(shù):int(*open)(structinode*,structfile*);參數(shù)inode為設(shè)備特殊文件的inode(索引結(jié)點)結(jié)構(gòu)的指針,參數(shù)file是指向這一設(shè)備的文件結(jié)構(gòu)的指針。open()的主要任務(wù)是確定硬件處在就緒狀態(tài)、驗證次設(shè)備號的合法性(次設(shè)備號可以用MINOR(inode->i-rdev)取得)、控制使用設(shè)備的進程數(shù)、根據(jù)執(zhí)行情況返回狀態(tài)碼(0表示成功,負(fù)數(shù)表示存在錯誤)等;設(shè)備操作函數(shù)集的定義open()函數(shù)設(shè)備操作函數(shù)集的定義release()函數(shù)當(dāng)最后一個打開設(shè)備的用戶進程執(zhí)行close()系統(tǒng)調(diào)用時,內(nèi)核將調(diào)用驅(qū)動程序的release()函數(shù):void(*release)(structinode*,structfile*);release函數(shù)的主要任務(wù)是清理未結(jié)束的輸入/輸出操作、釋放資源、用戶自定義排他標(biāo)志的復(fù)位等.設(shè)備操作函數(shù)集的定義release()函數(shù)設(shè)備操作函數(shù)集的定義read()函數(shù)Read的任務(wù),就是從設(shè)備拷貝數(shù)據(jù)到用戶空間。當(dāng)對設(shè)備特殊文件進行read()系統(tǒng)調(diào)用時,將調(diào)用驅(qū)動程序read()函數(shù):

ssize_tread(structfile*filp,char__user*buff, size_tcount,loff_t*offp);filp是文件對象指針,count

是請求的傳輸數(shù)據(jù)大小.buff

參數(shù)對write來說是指向持有被寫入數(shù)據(jù)的緩存,對read則是放入新數(shù)據(jù)的空緩存.offp

是指向一個“l(fā)ongoffsettype”的指針,它指出用戶正在存取的文件位置.返回值是“signedsizetype”類型;設(shè)備操作函數(shù)集的定義read()函數(shù)設(shè)備操作函數(shù)集的定義write()函數(shù)Write的任務(wù),則從用戶空間拷貝數(shù)據(jù)到設(shè)備。當(dāng)設(shè)備特殊文件進行write()系統(tǒng)調(diào)用時,將調(diào)用驅(qū)動程序的write()函數(shù):

ssize_twrite(structfile*filp,constchar__user *buff,size_tcount,loff_t*offp);filp是文件對象指針,count

是請求的傳輸數(shù)據(jù)大小.buff

參數(shù)對write來說是指向持有被寫入數(shù)據(jù)的緩存,對read則是放入新數(shù)據(jù)的空緩存.offp

是指向一個“l(fā)ongoffsettype”的指針,它指出用戶正在存取的文件位置.返回值是“signedsizetype”類型;設(shè)備操作函數(shù)集的定義write()函數(shù)設(shè)備操作函數(shù)集的定義read和write方法的buff參數(shù)是用戶空間指針,不能被內(nèi)核代碼直接解引用。__user字符串只是形式上的說明,表明是用戶空間地址。驅(qū)動必須能夠存取用戶空間緩存以完成它的工作。內(nèi)核如何解決這個問題?為安全起見,內(nèi)核提供專用的函數(shù)來完成對用戶空間的存取。這些專用函數(shù)在<asm/uaccess.h>中聲明。

unsignedlongcopy_to_user(void__user*to,constvoid*from,unsignedlongcount); unsignedlongcopy_from_user(void*to,constvoid__user*from,unsignedlongcount);大多數(shù)讀寫函數(shù)都會調(diào)用這兩個函數(shù),用于跟應(yīng)用程序空間交流信息。設(shè)備操作函數(shù)集的定義read和write方法的bufRead和Write方法典型的Read函數(shù)對參數(shù)的使用。Read和Write方法典型的Read函數(shù)對參數(shù)的使用。S3C2410的I/O介紹S3C2410有117個復(fù)用功能輸入輸出端口引腳,這些引腳是:PortA(GPA):32個輸入/輸出端口PortB(GPB):11個輸入/輸出端口PortC(GPC):16個輸入/輸出端口PortD(GPD):16個輸入/輸出端口PortE(GPE):16個輸入/輸出端口PortF(GPF):8個輸入/輸出端口PortG(GPG):16個輸入/輸出端口PortH(GPH):11個輸入/輸出端口S3C2410的I/O介紹S3C2410有117個復(fù)S3C2410的I/O介紹端口控制說明端口配置寄存器(GPACON――GPHCON) 大部分的引腳是復(fù)用的,所以必須對于每個引腳要求定義一個功能,端口配置寄存器定義了每個引腳的功能。端口數(shù)據(jù)寄存器(GPADAT――GPHDAT) 如果端口配置成輸出端口,數(shù)據(jù)能夠被寫到端口數(shù)據(jù)寄存器的對應(yīng)位,然后通過管腳輸出。如果端口配置成輸入端口,能從端口數(shù)據(jù)寄存器對應(yīng)的位中讀出管腳上的電平端口上拉寄存器(GPBUP――GPHUP) 端口上拉寄存器控制著每個端口組的上拉寄存器的使能或禁止,當(dāng)對應(yīng)位為0,這個引腳的上拉寄存器是允許的,當(dāng)為1時,上拉寄存器是禁止的。S3C2410的I/O介紹端口控制說明MIZI提供的S3C2410.H使用一個32位的數(shù)來表示端口的使用情況。模式|上拉|端口|端口引腳MODE|PULLUP|PORT|OFS不需要自己手動組合,通過宏定義以及SHIFT和MASK組合。見程序MIZI提供的S3C2410.H使用一個32位的數(shù)來表示端口MIZI提供的S3C2410.H端口的表示#definePORTA_OFS 0#definePORTB_OFS 1#definePORTC_OFS 2#definePORTD_OFS 3#definePORTE_OFS 4#definePORTF_OFS 5#definePORTG_OFS 6#definePORTH_OFS 7MIZI提供的S3C2410.H端口的表示MIZI提供的S3C2410.H端口引腳的表示#defineGPIO_A0 MAKE_GPIO_NUM(PORTA_OFS,0)#defineGPIO_A1 MAKE_GPIO_NUM(PORTA_OFS,1)#defineGPIO_A2 MAKE_GPIO_NUM(PORTA_OFS,2)#defineGPIO_A3 MAKE_GPIO_NUM(PORTA_OFS,3)。。。#defineMAKE_GPIO_NUM(p,o) ((p<<GPIO_PORT_SHIFTT)|(o<<GPIO_OFS_SHIFT))MIZI提供的S3C2410.H端口引腳的表示MIZI提供的S3C2410.Hset_gpio_ctrl(x)

功能:配置端口引腳的功能,設(shè)置IO口控制寄存器和上拉寄存器用法:set_gpio_ctrl(模式|上拉?|IO腳)模式|是否上拉|IO腳,在S3C2410.h中都有其定義好的名字。

set_gpio_ctrl(GPIO_E11|GPIO_PULLUP_DIS|GPIO_MODE_OUT);MIZI提供的S3C2410.Hset_gpio_ctrl(MIZI提供的S3C2410.Hwrite_gpio_bit(x,v)功能:把端口對應(yīng)的端口數(shù)據(jù)寄存器x位設(shè)置為vwrite_gpio_bit(GPIO_E11,0);read_gpio_bit(x)

功能:把端口數(shù)據(jù)寄存器x位的狀態(tài)讀入,函數(shù)返回值既是其狀態(tài)read_gpio_bit(GPIO_G11);MIZI提供的S3C2410.Hwrite_gpio_bitMIZI提供的S3C2410.Hwrite_gpio_reg(x,v)功能:把端口數(shù)據(jù)寄存器x設(shè)置為vread_gpio_reg(x)

功能:讀取端口數(shù)據(jù)寄存器x,函數(shù)返回值既是其數(shù)據(jù)MIZI提供的S3C2410.Hwrite_gpio_reg按驅(qū)動的框架寫好驅(qū)動,實現(xiàn)初始化、卸載函數(shù),以及file_opertation操作集。對于led使用的gpio函數(shù),內(nèi)核沒有輸出作為公開符號,所以需要手動修改內(nèi)核代碼,代碼放在arch/arm/march-s3c2410/gpio.c,輸出符號:EXPORT_SYMBOL(s3c2410_gpio_setpin);EXPORT_SYMBOL(s3c2410_gpio_cfgpin);EXPORT_SYMBOL(s3c2410_gpio_pullup);編寫驅(qū)動模塊的Makefile。交叉編譯驅(qū)動模塊NFS加載驅(qū)動模塊,進行測試。按驅(qū)動的框架寫好驅(qū)動,實現(xiàn)初始化、卸載函數(shù),以及file_o嵌入式鍵盤驅(qū)動廣州嵌入式軟件公共技術(shù)支持中心梁老師2007年7月嵌入式鍵盤驅(qū)動廣州嵌入式軟件公共技術(shù)支持中心注冊中斷服務(wù)例程

中斷線是一個寶貴且常常有限的資源,特別當(dāng)它們只有15個時。內(nèi)核維護一個中斷線的注冊表。要使用中斷線,就要進行中斷線的申請,也就是IRQ(InterruptReQuirement),因此我們也常把申請一條中斷線稱為申請一個IRQ或者是申請一個中斷號。IRQ線是從0開始順序編號的;第一條IRQ線通常表示成IRQ0。IRQn的缺省向量是n+32。注冊中斷服務(wù)例程中斷線是一個寶貴且常常有限的資源,特別當(dāng)注冊中斷服務(wù)例程

并不是每個設(shè)備都可以向中斷線上發(fā)中斷信號的,只有對某一條確定的中斷線擁有了控制權(quán),才可以向這條中斷線上發(fā)送信號。計算機的外部設(shè)備越來越多,所以15條中斷線已經(jīng)不夠用了,中斷線是非常寶貴的資源。只有當(dāng)設(shè)備需要中斷的時候才申請占用一個IRQ,或者是在申請IRQ時采用共享中斷的方式,這樣可以讓更多的設(shè)備使用中斷。注冊中斷服務(wù)例程并不是每個設(shè)備都可以向中斷線上發(fā)中斷信號的注冊中斷服務(wù)例程

在<linux/interrupt.h>實現(xiàn)中斷注冊接口: intrequest_irq(unsignedintirq, irqreturn_t(*handler)(int,void*,structpt_regs*), unsignedlongflags, constchar*dev_name, void*dev_id ); voidfree_irq(unsignedintirq,void*dev_id);request_irq的返回值是0指示申請成功,為負(fù)值時表示錯誤碼。函數(shù)返回-EBUSY表示已經(jīng)有另一個驅(qū)動占用了所要申請的中斷線。注冊中斷服務(wù)例程在<linux/interrupt.h>注冊中斷服務(wù)例程

request_irq的參數(shù)說明:unsignedintirq,要申請的中斷號。對某些設(shè)備,如傳統(tǒng)PC設(shè)備上的系統(tǒng)時鐘或鍵盤,這個值通常是預(yù)先確定的。而對于大多數(shù)其它設(shè)備來說,這個值要么可以通過探測獲取,要么可以動態(tài)確定。irqreturn_t(*handler)(int,void*,structpt_regs*),要安裝的中斷處理函數(shù)指針。后面介紹。注冊中斷服務(wù)例程request_irq的參數(shù)說明:注冊中斷服務(wù)例程

request_irq的參數(shù)說明:unsignedlongflags,

與中斷管理相關(guān)的位掩碼選項。constchar*dev_name,

用在/proc/interrupts中顯示中斷的擁有者。void*dev_id

這個指針用于共享的中斷線。做為驅(qū)動程序的私有數(shù)據(jù)區(qū)(可用來識別那個設(shè)備產(chǎn)生的中斷)。不使用共享中斷線方式時,可設(shè)置為NULL。注冊中斷服務(wù)例程request_irq的參數(shù)說明:注冊中斷服務(wù)例程

flags參數(shù)的詳細(xì)說明:Flags的每個位有不同含義SA_INTERRUPT

當(dāng)該位被設(shè)置時,表示這是一個“快速”中斷。快速中斷處理例程運行時,屏蔽中斷。SA_SHIRQ

這個位表示中斷可以在設(shè)備間共享。注冊中斷服務(wù)例程flags參數(shù)的詳細(xì)說明:proc文件系統(tǒng)中的中斷信息/proc/interrupts反映系統(tǒng)的中斷信息第一列是IRQ號給出每個中斷線發(fā)生中斷的

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論