




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第八章嵌入式Linux驅(qū)動(dòng)程序第八章嵌入式Linux驅(qū)動(dòng)程序(1)設(shè)備驅(qū)動(dòng)開發(fā)概述驅(qū)動(dòng)程序處理過程/設(shè)備驅(qū)動(dòng)程序框架
(2)內(nèi)核設(shè)備模型功能/Sysfs和實(shí)現(xiàn)機(jī)制/Platform總線/設(shè)備樹(3)字符設(shè)備驅(qū)動(dòng)程序關(guān)鍵數(shù)據(jù)結(jié)構(gòu)/驅(qū)動(dòng)框架/實(shí)例(4)網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序驅(qū)動(dòng)程序框架/關(guān)鍵數(shù)據(jù)結(jié)構(gòu)/設(shè)計(jì)方法/實(shí)例ARM-Linux驅(qū)動(dòng)程序的4種經(jīng)典方法1.mmap映射型設(shè)計(jì)方法(單片機(jī)工程師的最愛)2.傳統(tǒng)file_operatiopns設(shè)計(jì)方法(盛久不衰,沿用至今)3.platform總線型設(shè)計(jì)方法(2.6版本主流)4.設(shè)備樹(內(nèi)核3.x版本后流行)
設(shè)備驅(qū)動(dòng)程序開發(fā)概述PartOne8.1Linux設(shè)備驅(qū)動(dòng)程序概述
作為Linux內(nèi)核的重要組成部分,設(shè)備驅(qū)動(dòng)程序主要完成以下的功能:(1)對(duì)設(shè)備初始化和釋放。(2)把數(shù)據(jù)從內(nèi)核傳送到硬件和從硬件讀取數(shù)據(jù)。(3)讀取應(yīng)用程序傳送給設(shè)備文件的數(shù)據(jù)和回送應(yīng)用程序請(qǐng)求的數(shù)據(jù)。(4)檢測錯(cuò)誤和處理中斷。Linux設(shè)備驅(qū)動(dòng)程序可以分為兩個(gè)主要組成部分:(1)對(duì)子程序進(jìn)行自動(dòng)配置和初始化,檢測驅(qū)動(dòng)的硬件設(shè)備是否正常,能否正常工作。(2)設(shè)備服務(wù)子程序和中斷服務(wù)子程序,這兩者分別是驅(qū)動(dòng)程序的上下兩部分。驅(qū)動(dòng)上部分即設(shè)備服務(wù)子程序的執(zhí)行是系統(tǒng)調(diào)用的結(jié)果,并且伴隨著用戶態(tài)向核心態(tài)的演變,在此過程中還可以調(diào)用與進(jìn)程運(yùn)行環(huán)境有關(guān)的函數(shù),比如sleep()函數(shù)。驅(qū)動(dòng)程序的下半部分即中斷服務(wù)子程序。Linux設(shè)備驅(qū)動(dòng)程序分類
1.字符設(shè)備字符設(shè)備是傳輸數(shù)據(jù)以字符為單位進(jìn)行的設(shè)備,字符設(shè)備驅(qū)動(dòng)程序通常實(shí)現(xiàn)open、close、read和write等系統(tǒng)調(diào)用函數(shù),常見的字符設(shè)備有鍵盤、串口、控制臺(tái)等。通過文件系統(tǒng)節(jié)點(diǎn)可以訪問字符設(shè)備,例如/dev/tty1和/dev/lp1。2.塊設(shè)備所謂塊設(shè)備是指對(duì)其信息的存取以“塊”為單位。塊設(shè)備和字符設(shè)備一樣可以通過文件系統(tǒng)節(jié)點(diǎn)來訪問。在大多數(shù)linux系統(tǒng)中,只能將塊設(shè)備看作多個(gè)塊進(jìn)行訪問,一個(gè)塊設(shè)備通常是1024B數(shù)據(jù)。3.網(wǎng)絡(luò)設(shè)備網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)通常是通過套接字(Socket)等接口來實(shí)現(xiàn)操作。任何網(wǎng)絡(luò)事務(wù)處理都可以通過接口來完成和其他宿主機(jī)數(shù)據(jù)的交換。內(nèi)核和網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序之間的通信與字符設(shè)備驅(qū)動(dòng)程序和塊設(shè)備驅(qū)動(dòng)程序與內(nèi)核的通信是完全不同的。8.1.1Linux設(shè)備驅(qū)動(dòng)程序分類1.字符設(shè)備字符設(shè)備是傳輸數(shù)據(jù)以字符為單位進(jìn)行的設(shè)備,字符設(shè)備驅(qū)動(dòng)程序通常實(shí)現(xiàn)open、close、read和write等系統(tǒng)調(diào)用函數(shù),常見的字符設(shè)備有鍵盤、串口、控制臺(tái)等。通過文件系統(tǒng)節(jié)點(diǎn)可以訪問字符設(shè)備,例如/dev/tty1和/dev/lp1。字符設(shè)備和普通文件系統(tǒng)之間唯一的區(qū)別是普通文件允許往復(fù)讀寫,而大多數(shù)字符設(shè)備驅(qū)動(dòng)僅是數(shù)據(jù)通道,只能順序讀寫。此外,字符設(shè)備驅(qū)動(dòng)程序不需要緩沖且不以固定大小進(jìn)行操作,它與用戶進(jìn)程之間直接相互傳輸數(shù)據(jù)。2.塊設(shè)備所謂塊設(shè)備是指對(duì)其信息的存取以“塊”為單位。如常見的光盤、硬磁盤、軟磁盤、磁帶等,塊長大小通常取512B、1024B或4096B等。塊設(shè)備和字符設(shè)備一樣可以通過文件系統(tǒng)節(jié)點(diǎn)來訪問。在大多數(shù)linux系統(tǒng)中,只能將塊設(shè)備看作多個(gè)塊進(jìn)行訪問,一個(gè)塊設(shè)備通常是1024B數(shù)據(jù)。塊設(shè)備的特點(diǎn)是對(duì)設(shè)備的讀寫是以塊為單位的,并且對(duì)設(shè)備的訪問是隨機(jī)的。塊設(shè)備和字符設(shè)備的區(qū)別主要在于內(nèi)核內(nèi)部的管理上,其中應(yīng)用程序?qū)τ谧址O(shè)備的每個(gè)I/O操作都會(huì)直接傳遞給系統(tǒng)內(nèi)核對(duì)應(yīng)的驅(qū)動(dòng)程序;而應(yīng)用程序?qū)τ趬K設(shè)備的操作要經(jīng)過系統(tǒng)的緩沖區(qū)管理間接地傳遞給驅(qū)動(dòng)程序處理。
3.網(wǎng)絡(luò)設(shè)備網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)通常是通過套接字(Socket)等接口來實(shí)現(xiàn)操作。任何網(wǎng)絡(luò)事務(wù)處理都可以通過接口來完成和其他宿主機(jī)數(shù)據(jù)的交換。內(nèi)核和網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序之間的通信與字符設(shè)備驅(qū)動(dòng)程序和塊設(shè)備驅(qū)動(dòng)程序與內(nèi)核的通信是完全不同的。1.內(nèi)存與I/O端口編寫驅(qū)動(dòng)程序大多數(shù)情況下其本質(zhì)都是對(duì)內(nèi)存和I/O端口的操作。(1)內(nèi)存Linux通常有以下幾種地址類型:用戶虛擬地址物理地址總線地址內(nèi)核邏輯地址內(nèi)核虛擬地址(2)I/O端口有兩個(gè)重要的內(nèi)核調(diào)用可以保證驅(qū)動(dòng)程序使用正確的端口,它們定義在include/linux/ioport.h中。int__check_region(structresource*,resource_size_t,resource_size_t);該函數(shù)的作用是查看系統(tǒng)I/O表,看是否有別的驅(qū)動(dòng)程序占用某一段I/O口。structresource*__request_region(structresource*, resource_size_tstart, resource_size_tn, constchar*name,intflags);根據(jù)CPU系統(tǒng)結(jié)構(gòu)的不同,CPU對(duì)I/O端口的編址方式通常有兩種:第一種是I/O映射方式,如x86處理器為外設(shè)專門實(shí)現(xiàn)了一個(gè)單獨(dú)的地址空間,稱為I/O地址空間,CPU通過專門的I/O指令來訪問這一空間的地址單元;第二種是內(nèi)存映射方式,RSIC指令系統(tǒng)的CPU(如ARM、PowerPC等)通常只實(shí)現(xiàn)一個(gè)物理地址空間,外設(shè)I/O端口成為了內(nèi)存的一部分,此時(shí)CPU訪問I/O端口就像訪問一個(gè)內(nèi)存單元,不需要單獨(dú)的I/O指令。這兩種方式在硬件實(shí)現(xiàn)上的差異對(duì)軟件來說是完全可見的。2.并發(fā)控制
在驅(qū)動(dòng)程序中經(jīng)常會(huì)出現(xiàn)多個(gè)進(jìn)程同時(shí)訪問相同的資源時(shí)可能會(huì)出現(xiàn)競態(tài)(racecondition),即競爭資源狀態(tài),因此必須對(duì)共享資料進(jìn)行并發(fā)控制。Linux內(nèi)核中解決并發(fā)控制最常用的方法是自旋鎖(spinlocks)和信號(hào)量(semaphores)。(1)自旋鎖自旋鎖是一個(gè)互斥現(xiàn)象的設(shè)備,它只能是兩個(gè)值:locked(鎖定)或unlocked(解鎖)。它通常作為一個(gè)整型值的單位來實(shí)現(xiàn)。在任何時(shí)刻,自旋鎖只能有一個(gè)保持者,也就是說在同一時(shí)刻只能有一個(gè)進(jìn)程獲得鎖。(2)信號(hào)量信號(hào)量是一個(gè)結(jié)合一對(duì)函數(shù)的整型值,這對(duì)函數(shù)通常稱為P操作和V操作。自旋鎖和信號(hào)量有很多相似之處但又有些本質(zhì)的不同。其相同之處主要有:首先它們對(duì)互斥來說都是非常有用的工具;其次在任何時(shí)刻最多只能有一個(gè)線程獲得自旋鎖或信號(hào)量。不同之處主要有:首先自旋鎖可在不能睡眠的代碼中使用,如在中斷服務(wù)程序(ISR)中使用,而信號(hào)量不可以;其次自旋鎖和信號(hào)量的實(shí)現(xiàn)機(jī)制不一樣;最后通常自旋鎖被用在多處理器系統(tǒng)??傮w而言,自旋鎖通常適合保持時(shí)間非常短的情況,它可以在任何上下文中使用,而信號(hào)量用于保持時(shí)間較長的情況,只能在進(jìn)程上下文中使用。3.阻塞與非阻塞在驅(qū)動(dòng)程序的處理過程中我們提到了阻塞的概念,這里進(jìn)行以下說明。阻塞(blocking)和非阻塞(nonblocking)是設(shè)備訪問的兩種不同模式,前者在I/O操作暫時(shí)不可進(jìn)行時(shí)會(huì)讓進(jìn)程睡眠,而后者在I/O操作暫時(shí)不可進(jìn)行時(shí)并不掛起進(jìn)程,它或者放棄,或者不停地查詢,直到可以進(jìn)行操作為止。(1)阻塞與非阻塞操作阻塞操作是指在執(zhí)行設(shè)備操作時(shí),若不能獲得資源則進(jìn)程掛起,直到滿足可操作的條件再進(jìn)行操作。被掛起的進(jìn)程進(jìn)入睡眠狀態(tài),被從調(diào)度器的運(yùn)行隊(duì)列中移走,直到等待條件被滿足。非阻塞操作是在不能進(jìn)行設(shè)備操作時(shí)并不掛起,它會(huì)立即返回,使得應(yīng)用程序可以快速查詢狀態(tài)。(2)異步通知異步通知是指一旦設(shè)備準(zhǔn)備就緒,則該設(shè)備會(huì)主動(dòng)通知應(yīng)用程序,這樣應(yīng)用程序就不需要不斷地查詢?cè)O(shè)備狀態(tài),通常把異步通知稱為信號(hào)驅(qū)動(dòng)的異步I/O(SIGIO),這有點(diǎn)類似于硬件上的中斷。中斷處理Linux將中斷分為兩個(gè)部分:上半部分(tophalf)和下半部分(bottomhalf)。上半部分的功能是注冊(cè)中斷,下半部完成了中斷處理程序的大部分工作中斷的Bottom-half機(jī)制,包括了softirq(軟中斷)、tasklet(任務(wù)隊(duì)列)、workqueue(工作隊(duì)列)等,任務(wù)隊(duì)列(tasklet)下面是tasklet的定義:structtasklet_struct{ structtasklet_struct*next;//指向下一個(gè)tasklet unsignedlongstate;//tasklet的狀態(tài) atomic_tcount;//計(jì)數(shù),1表示禁止 void(*func)(unsignedlong);//處理函數(shù)指針 unsignedlongdata;//處理函數(shù)參數(shù)};在interrupt.h中可以看到tasklet的數(shù)據(jù)結(jié)構(gòu),其狀態(tài)定義了兩個(gè)位的含義:enum{TASKLET_STATE_SCHED,/*正在運(yùn)行*/ TASKLET_STATE_RUN /*已被調(diào)度,準(zhǔn)備運(yùn)行*/};工作隊(duì)列(workqueue)工作隊(duì)列的queue核心代碼如下所示。staticvoidinsert_work(structcpu_workqueue_struct*cwq,
structwork_struct*work,structlist_head*head,
unsignedintextra_flags){
structglobal_cwq*gcwq=cwq->gcwq;
set_work_cwq(work,cwq,extra_flags);
list_add_tail(&work->entry,head);
//head參數(shù)表示的是每cpu一個(gè)的全局隊(duì)列
if(__need_more_worker(gcwq))//如果是諸如高優(yōu)先級(jí)之類的工作或者當(dāng)前已經(jīng)沒有//空閑的工作者了,那么喚醒一個(gè)工作者,系統(tǒng)起碼要保持一個(gè)空閑的工作者進(jìn)程以備用。
wake_up_worker(gcwq);}設(shè)備號(hào)在linux2.6內(nèi)核中,主從設(shè)備被定義為一個(gè)dev_t類型的32位數(shù),其中前12位表示主設(shè)備號(hào),后20位表示從設(shè)備號(hào)。另外,在include/linux/kdev.h中定義了如下的幾個(gè)宏來操作主從設(shè)備號(hào)。#defineMAJOR(dev) ((unsignedint)((dev)>>MINORBITS))#defineMINOR(dev) ((unsignedint)((dev)&MINORMASK))#defineMKDEV(ma,mi) (((ma)<<MINORBITS)|(mi))上述宏分別實(shí)現(xiàn)從32位dev_t類型數(shù)據(jù)中獲得主設(shè)備號(hào)、從設(shè)備號(hào)及將主設(shè)備號(hào)和從設(shè)備號(hào)轉(zhuǎn)換為dev_t類型數(shù)據(jù)的功能。設(shè)備驅(qū)動(dòng)程序框架Linux的設(shè)備驅(qū)動(dòng)程序可以分為以下部分。(1)驅(qū)動(dòng)程序與內(nèi)核的接口,這是通過關(guān)鍵數(shù)據(jù)結(jié)構(gòu)file_operations來完成的。(2)驅(qū)動(dòng)程序與系統(tǒng)引導(dǎo)的接口,這部分利用驅(qū)動(dòng)程序?qū)υO(shè)備進(jìn)行初始化。(3)驅(qū)動(dòng)程序與設(shè)備的接口,描述了驅(qū)動(dòng)程序如何與設(shè)備進(jìn)行交互,這與具體設(shè)備密切相關(guān)。根據(jù)功能劃分,設(shè)備驅(qū)動(dòng)程序代碼通??煞譃橐韵聨讉€(gè)部分:(1)驅(qū)動(dòng)程序的注冊(cè)與注銷(2)設(shè)備的打開與釋放:引用計(jì)數(shù)(3)設(shè)備的讀寫操作(4)設(shè)備的控制操作(5)設(shè)備的輪詢和中斷處理(2)設(shè)備的打開與釋放打開設(shè)備是由調(diào)用定義在incliude/linux/fs.h中的file_operations結(jié)構(gòu)體中的open()函數(shù)完成的。open()函數(shù)主要完成的主要工作:增加設(shè)備的使用計(jì)數(shù)。檢測設(shè)備是否異常,及時(shí)發(fā)現(xiàn)設(shè)備相關(guān)錯(cuò)誤,防止設(shè)備有未知硬件問題。若是首次打開,首先完成設(shè)備初始化。讀取設(shè)備次設(shè)備號(hào)。其函數(shù)原型如下:int(*open)(structinode*,structfile*);驅(qū)動(dòng)程序的加載
通常linux驅(qū)動(dòng)程序可通過兩種方式進(jìn)行加載:一種是將驅(qū)動(dòng)程序編譯成模塊形式進(jìn)行動(dòng)態(tài)加載,常用命令有insmod(加載)、rmmod(卸載)等;另一種是靜態(tài)編譯,即將驅(qū)動(dòng)程序直接編輯放進(jìn)內(nèi)核。動(dòng)態(tài)加載模塊設(shè)計(jì)使Linux內(nèi)核功能更容易擴(kuò)展。而靜態(tài)編譯方法對(duì)于在要求硬件只是完成比較特定、專一的功能的一些嵌入式系統(tǒng)中,具有更高的效率。以網(wǎng)卡DM9000為例說明驅(qū)動(dòng)程序的加載過程
內(nèi)核設(shè)備模型PartTwo8.2內(nèi)核設(shè)備模型
在Linux2.6內(nèi)核及后續(xù)版本中,設(shè)備模型為設(shè)備驅(qū)動(dòng)程序管理、描述設(shè)備抽象數(shù)據(jù)結(jié)構(gòu)之間關(guān)系等提供了一個(gè)有效的手段,其主要功能包括
:(1)電源管理和系統(tǒng)關(guān)機(jī)(2)與用戶空間通信(3)熱插拔(hotplug)設(shè)備管理(4)設(shè)備類型管理(5)對(duì)象生命周期處理linux內(nèi)核各目錄代碼量對(duì)比linux內(nèi)核設(shè)備模型帶來的好處十分之多。首先linux設(shè)備模型是一個(gè)具有清晰結(jié)構(gòu)的組織所有設(shè)備和驅(qū)動(dòng)的樹狀結(jié)構(gòu),用戶就可以通過這棵樹去遍歷所有的設(shè)備,建立設(shè)備和驅(qū)動(dòng)程序之間的聯(lián)系;其次,Linux驅(qū)動(dòng)模型把很多設(shè)備共有的一些操作抽象出來,大大減少重復(fù)開發(fā)的可能;再次,Linux設(shè)備模型提供了一些輔助的機(jī)制,比如引用計(jì)數(shù),讓開發(fā)者可以安全高效的開發(fā)驅(qū)0動(dòng)程序。同時(shí),Linux設(shè)備模型還提供了一個(gè)非常有用的虛擬的基于內(nèi)存的文件系統(tǒng)sysfs。Sysfs解釋了內(nèi)核數(shù)據(jù)結(jié)構(gòu)的輸出、屬性以及它們之間及用戶空間的連接關(guān)系。sysfs
sysfs給用戶提供了一個(gè)從用戶空間去訪問內(nèi)核設(shè)備的方法,它在Linux里的路徑是/sys。這個(gè)目錄并不是存儲(chǔ)在硬盤上的真實(shí)的文件系統(tǒng),只有在系統(tǒng)啟動(dòng)之后才會(huì)建起來。可以使用tree/sys這個(gè)命令顯示sysfs的結(jié)構(gòu)。由于信息量較大,這里只列出第一層目錄結(jié)構(gòu):/sys|--block|--bus|--class|--dev|--devices|--firmware|--fs|--kernel|--module`--power如果只是單純要訪問設(shè)備,一般很少會(huì)直接操作sysfs,因?yàn)閟ysfs非常繁瑣和底層化現(xiàn)象嚴(yán)重,大部分情況下可以使用更加方便的DeviceKit或者libudev。Block目錄從塊設(shè)備的角度來組織設(shè)備,其下的每個(gè)子目錄分別對(duì)應(yīng)系統(tǒng)中的一個(gè)塊設(shè)備;值得注意的是sys/block目錄從內(nèi)核2.6.26已經(jīng)正式轉(zhuǎn)移到sys/class/block中。Sys/block目錄雖然為了向后兼容保持存在,但是其中的內(nèi)容已經(jīng)變?yōu)橹赶蛩鼈冊(cè)趕ys/devices/中真實(shí)設(shè)備的符號(hào)鏈接文件。Bus目錄從系統(tǒng)總線這個(gè)角度來組織設(shè)備,內(nèi)核設(shè)備按照總線類型分層放置的目錄結(jié)構(gòu),它是構(gòu)成linux統(tǒng)一設(shè)備模型的一部分。Class目錄把看以類別的角度看待設(shè)備,比如PCI設(shè)備或者USB設(shè)備等,該目錄是按照設(shè)備功能分類的設(shè)備模型,是linux統(tǒng)一設(shè)備模型的一部分。Dev目錄下維護(hù)一個(gè)按照字符設(shè)備或者塊設(shè)備的設(shè)備號(hào)鏈接到硬件設(shè)備的符號(hào)鏈接,在內(nèi)核2.6.26首次引入。Devices目錄是所有設(shè)備的大本營,系統(tǒng)中的任一設(shè)備在設(shè)備模型中都由一個(gè)device對(duì)象描述,是sysfs下最重要的目錄。該目錄結(jié)構(gòu)就是系統(tǒng)中實(shí)際的設(shè)備拓?fù)浣Y(jié)構(gòu)。Firmware目錄包含了一些比較低階的子系統(tǒng),比如ACPI、EFI等,是系統(tǒng)加載固件機(jī)制的對(duì)用戶空間的接口。Fs目錄里列出的是系統(tǒng)支持的所有文件系統(tǒng),但是目前只有fuse、gfs2等少數(shù)文件系統(tǒng)支持sysfs接口。Kernel目錄下包含的是一些內(nèi)核的配置選項(xiàng),如slab分配器等。Modules目錄下包含的是所有內(nèi)核模塊的信息,內(nèi)核模塊實(shí)際上和設(shè)備之間存在對(duì)應(yīng)聯(lián)系,通過這個(gè)目錄可以找到設(shè)備。Power目錄存放的是系統(tǒng)電源管理的數(shù)據(jù),用戶可以通過它來查詢目前的電源狀態(tài),甚至可以直接“命令”系統(tǒng)進(jìn)入休眠等省電模式。sysfs的實(shí)現(xiàn)機(jī)制kobject
“內(nèi)核對(duì)象”(kernel
object)的設(shè)備管理機(jī)制,該機(jī)制是基于一種底層數(shù)據(jù)結(jié)構(gòu),通過這個(gè)數(shù)據(jù)結(jié)構(gòu),可以使所有設(shè)備在底層都具有一個(gè)公共接口,便于設(shè)備或驅(qū)動(dòng)程序的管理和組織。從面向?qū)ο蟮慕嵌葋碚f,kobject可以看作是所有設(shè)備對(duì)象的基類。Kobject結(jié)構(gòu)struct
kobject{
const
char
*name;//指向設(shè)備名稱的指針struct
list_head
entry;//掛接到所在kset中去的單元struct
kobject
*parent;//指向父對(duì)象的指針struct
kset
*kset;//所屬kset的指針struct
kobj_type
*ktype;//指向其對(duì)象類型描述符的指針struct
sysfs_dirent*sd;//指示在sysfs中的目錄項(xiàng)struct
kref
kref;//對(duì)象引用計(jì)數(shù)unsignedint
state_initialized:1;//標(biāo)記:初始化unsignedint
state_in_sysfs:1;//標(biāo)記在sysfs中;
。。。。。。}kref的定義非常簡單,其結(jié)構(gòu)體里只有一個(gè)原子變量。struct
kref{
atomic_trefcount;};Ktype域是一個(gè)指向kobj-type結(jié)構(gòu)的指針,表示該對(duì)象的類型。
struct
kobj_type{
void
(*release)(struct
kobject*kobj);
const
struct
sysfs_ops*sysfs_ops;
struct
attribute**default_attrs;};Ksetkobject通常通過kset組織成層次化的結(jié)構(gòu),kset是具有相同類型的kobject的集合Kset的定義如下:struct
kset{
struct
list_headlist;//用于連接該kset中所有kobject的鏈表頭
spinlock_tlist_lock;//迭代時(shí)用的鎖
struct
kobjectkobj;//指向代表該集合基類的對(duì)象
const
struct
kset_uevent_ops*uevent_ops;//指向一個(gè)用于處理集合中kobject對(duì)象的熱插拔結(jié)構(gòu)操作的結(jié)構(gòu)體};設(shè)備模型的組織-platform總線
Platform總線就是從2.6
內(nèi)核開始引入的一種虛擬總線,主要用來管理CPU的片上資源,具有更好的移植性。目前,大部分的驅(qū)動(dòng)都是用Platform總線編寫的,除了極少數(shù)情況之外如構(gòu)建內(nèi)核最小系統(tǒng)之內(nèi)的而且能夠采用CPU存儲(chǔ)器總線直接尋址的設(shè)備。Platform總線模型主要包括platform_device、platform_bus、platform_driver三個(gè)部分。Platform總線模型的platform_driver機(jī)制將設(shè)備的本身資源注冊(cè)進(jìn)內(nèi)核,由內(nèi)核統(tǒng)一管理,在驅(qū)動(dòng)程序中使用這些資源時(shí)通過標(biāo)準(zhǔn)接口進(jìn)行申請(qǐng)和使用,具有很高的安全性和可靠性。而模型中的platform_device是一個(gè)具有自我管理功能的子系統(tǒng)。當(dāng)platform模型中總線上有設(shè)備,又有驅(qū)動(dòng)的時(shí)候,就會(huì)進(jìn)行設(shè)備與驅(qū)動(dòng)匹配的過程,總線起到了溝通設(shè)備和驅(qū)動(dòng)的橋梁作用。設(shè)備樹設(shè)備樹的主要作用:一是平臺(tái)標(biāo)識(shí),所謂平臺(tái)標(biāo)識(shí)就是板級(jí)識(shí)別,讓內(nèi)核知道當(dāng)前使用的是哪個(gè)開發(fā)板,這里識(shí)別的方式是根據(jù)root節(jié)點(diǎn)下的compatible字段來匹配。二是運(yùn)行時(shí)配置,比如在內(nèi)核啟動(dòng)的時(shí)候ramdisk的配置,比如bootargs的配置,ramdisk的起始和結(jié)束地址。三是設(shè)備信息集合,這也是最重要的信息,集合了各種設(shè)備控制器設(shè)備樹設(shè)備樹用樹狀結(jié)構(gòu)描述設(shè)備信息,它有以下特性:每個(gè)設(shè)備樹文件都有一個(gè)根節(jié)點(diǎn),每個(gè)設(shè)備都是一個(gè)節(jié)點(diǎn)。節(jié)點(diǎn)間可以嵌套,形成父子關(guān)系,這樣就可以方便地描述設(shè)備間的關(guān)系。每個(gè)設(shè)備的屬性都用一組key-value對(duì)(鍵值對(duì))來描述。每個(gè)屬性的描述用“?!北硎窘Y(jié)束。/{//根節(jié)點(diǎn)node1{//node1是節(jié)點(diǎn)名,是板子的子節(jié)點(diǎn)key=value;//node1的屬性...node2{
//node2是node1的子節(jié)點(diǎn)key=value;//node2的屬性...}}字符設(shè)備驅(qū)動(dòng)設(shè)計(jì)框架PartThree8.38.3.1字符設(shè)備的重要數(shù)據(jù)結(jié)構(gòu)字符設(shè)備驅(qū)動(dòng)程序編寫通常都要涉及到三個(gè)重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu),分別是file_operations結(jié)構(gòu)體、file結(jié)構(gòu)體和inode結(jié)構(gòu)體。File_operations為用戶態(tài)應(yīng)用程序提供接口,是系統(tǒng)調(diào)用和驅(qū)動(dòng)程序關(guān)聯(lián)的重要數(shù)據(jù)結(jié)構(gòu)。File結(jié)構(gòu)體在內(nèi)核代碼include/linux/fs.h中定義,表示一個(gè)抽象的打開的文件,file_operations結(jié)構(gòu)體就是file結(jié)構(gòu)的一個(gè)成員。Inode結(jié)構(gòu)表示一個(gè)文件,而file結(jié)構(gòu)表示一個(gè)打開的文件。這正是二者間最重要的關(guān)系。每個(gè)進(jìn)程為每個(gè)打開的文件分配一個(gè)文件描述符,每個(gè)文件描述符對(duì)應(yīng)一個(gè)file結(jié)構(gòu),同一個(gè)文件被不同的進(jìn)程打開后,在不同的進(jìn)程中會(huì)有不同的file文件結(jié)構(gòu),其中包括了文件的操作方式(只讀\只寫\讀寫),偏移量,以及指向inode的指針等等。這樣,不同的file結(jié)構(gòu)指向了同一個(gè)inode節(jié)點(diǎn)。這里介紹一下字符設(shè)備的分配和初始化,它有兩種不同的方式。cdev_alloc()函數(shù)用于動(dòng)態(tài)分配一個(gè)新的cdev結(jié)構(gòu)體并初始化。一般如果建立新的cdev結(jié)構(gòu)體可以使用該方式,這里給出一個(gè)參考代碼:structcdev*my_cdev=cdev_alloc();my_cdev->owner=THIS_MODULE;my_cdev->ops=&fops;如果需要把cdev結(jié)構(gòu)體嵌入到指定設(shè)備結(jié)構(gòu)中,可以采用靜態(tài)分配方式。cdev_init()函數(shù)可以初始化一個(gè)靜態(tài)分配的cdev結(jié)構(gòu)體,并建立cdev和file_operation之間的連接。與cdev_alloc()唯一不同的是,cdev_init()函數(shù)用于初始化已經(jīng)存在的cdev結(jié)構(gòu)體。這里給出一段參考代碼:structcdevmy_cdev;cdev_init(&my_cdev,&fops);my_cdev.owner=THIS_MODULE;字符設(shè)備驅(qū)動(dòng)程序的重要數(shù)據(jù)結(jié)構(gòu)
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*);
long(*unlocked_ioctl)(structfile*,unsignedint,unsignedlong);……file結(jié)構(gòu)體使用open打開文件時(shí),傳入的flags、mode等參數(shù)會(huì)被記錄在內(nèi)核中對(duì)應(yīng)的structfile結(jié)構(gòu)體里(f_flags、f_mode)打開字符設(shè)備節(jié)點(diǎn)時(shí),內(nèi)核中也有對(duì)應(yīng)的structfile注意這個(gè)結(jié)構(gòu)體中的結(jié)構(gòu)體:structfile_operations*f_op,這是由驅(qū)動(dòng)程序提供的。8.3.2字符設(shè)備驅(qū)動(dòng)框架字符設(shè)備驅(qū)動(dòng)程序的初始化流程一般可以用如下的過程來表示:(1)定義相關(guān)的設(shè)備文件結(jié)構(gòu)體(如file_operation()中的相關(guān)成員函數(shù)的定義)。(2)向內(nèi)核申請(qǐng)主設(shè)備號(hào)(建議采用動(dòng)態(tài)方式)。(3)申請(qǐng)成功后,通過調(diào)用MAJOR()函數(shù)獲取主設(shè)備號(hào)。(4)初始化cdev的結(jié)構(gòu)體,可以通過調(diào)用cdev_init()函數(shù)實(shí)現(xiàn)。(5)通過調(diào)用cdev_add()函數(shù)注冊(cè)cdev到內(nèi)核。(6)注冊(cè)設(shè)備模塊,主要使用module_init()函數(shù)和module_exit()函數(shù)。編寫一個(gè)字符設(shè)備的驅(qū)動(dòng)程序,首先要注冊(cè)一個(gè)設(shè)備號(hào)。內(nèi)核提供了三個(gè)函數(shù)來注冊(cè)一組字符設(shè)備編號(hào),這三個(gè)函數(shù)分別是:alloc_chrdev_region()、register_chrdev_region()和register_chrdev()。其中register_chrdev()在上節(jié)已經(jīng)介紹過。這里首先介紹的是alloc_chrdev_region()函數(shù),該函數(shù)用于動(dòng)態(tài)申請(qǐng)?jiān)O(shè)備號(hào)范圍,通過指針參數(shù)返回實(shí)際分配的起始設(shè)備號(hào)。Register_chrdev_region()函數(shù)用于向內(nèi)核申請(qǐng)分配已知可用的設(shè)備號(hào)(次設(shè)備號(hào)通常為0)范圍。下面是該函數(shù)原型:intregister_chrdev_region(dev_tfrom,unsignedcount,constchar*name)。參數(shù)from是要分配的設(shè)備號(hào)的dev_t類型數(shù)據(jù),表示了要分配的設(shè)備編號(hào)的起始值,參數(shù)count表示了允許分配設(shè)備編號(hào)的范圍。register_chrdev()是一個(gè)老版本內(nèi)核的設(shè)備號(hào)分配函數(shù),不過新內(nèi)核對(duì)其還是兼容的。Register_chrdev()兼容了動(dòng)態(tài)和靜態(tài)兩種分配方式。Register_chrdev()不僅分配了設(shè)備號(hào),同時(shí)也注冊(cè)了設(shè)備。這是register_chrdev()與前兩個(gè)函數(shù)的最大區(qū)別。也就是說,如果使用alloc_chrdev_region()或register_chrdev_region()分配設(shè)備號(hào),還需要對(duì)cdev結(jié)構(gòu)體初始化。而register_chrdev()則把對(duì)cdev結(jié)構(gòu)體的操作封裝在了函數(shù)的內(nèi)部。所以在一般的字符設(shè)備驅(qū)動(dòng)程序中,不會(huì)看到對(duì)cdev的操作。與注冊(cè)分配字符設(shè)備編號(hào)的方法類似,內(nèi)核提供了兩個(gè)注銷字符設(shè)備編號(hào)范圍的函數(shù)unregister_chrdev_region()和unregister_chrdev()。這兩個(gè)函數(shù)實(shí)際上都調(diào)用了__unregister_chrdev_region()函數(shù),原理是一樣的。Register_chrdev()函數(shù)封裝了cdev結(jié)構(gòu)的操作,而alloc_chrdev_region()或register_chrdev_region()只提供了設(shè)備號(hào)的注冊(cè),并未真正的初始化一個(gè)設(shè)備,只有cdev這個(gè)表示設(shè)備的結(jié)構(gòu)體初始化了,才可以說設(shè)備初始化了。字符設(shè)備驅(qū)動(dòng)框架staticunsignedintxxx_open(){…}staticunsignedintxxx_ioctl(){
…}structfile_operationsfops={.owner=THIS_MODULE,.open=xxx_open,.ioctl=xxx_ioctl,//注意新式寫法這里應(yīng)是
.unlocked_ioctl=xxx_ioctl};staticint__initxxx_init(void){...register_chrdev(xxx_dev_no,DEV_NAME,&fops);}staticvoid__exitxxx_exit(void){unregister_chrdev(xxx_dev_no,DEV_NAME);...}module_init(xxx_init);module_exit(xxx_exit);字符設(shè)備驅(qū)動(dòng)程序設(shè)計(jì)
試驗(yàn)程序編寫應(yīng)用程序調(diào)用open函數(shù)打開hello_drv這個(gè)設(shè)備,打開以后可以使用write函數(shù)向hello_drv的寫緩沖區(qū)writebuf中寫入數(shù)據(jù)(不超過100個(gè)字節(jié)),也可以使用read函數(shù)讀取讀緩沖區(qū)readbuf中的數(shù)據(jù)操作,操作完成以后應(yīng)用程序使用close函數(shù)關(guān)閉chrdevbase設(shè)備。Makefile測試程序編寫驅(qū)動(dòng)編寫好以后是需要測試的,一般編寫一個(gè)簡單的測試程序,測試程序運(yùn)行在用戶空間。測試程序很簡單通過輸入相應(yīng)的指令來對(duì)hello_drv設(shè)備執(zhí)行讀或者寫操作。I2C總線驅(qū)動(dòng)圖8-7Linux內(nèi)核I2C總線驅(qū)動(dòng)框架Linux內(nèi)核提供了一個(gè)通用的方法來實(shí)現(xiàn)I2C設(shè)備驅(qū)動(dòng)。這個(gè)通用的設(shè)備驅(qū)動(dòng)由I2C_dev.c文件實(shí)現(xiàn)。在/drivers/I2C目錄下
----Algos/
一些I2C總線適配器通信的算法
----Busses/
I2C總線驅(qū)動(dòng)的方法
----Chips/
I2C設(shè)備驅(qū)動(dòng)
----I2C-boardinfo.c
----I2C-core.c
I2C核心文件,用于聯(lián)系設(shè)備驅(qū)動(dòng)和總線驅(qū)動(dòng),重要函數(shù)為I2C_add_addapter、I2C_add_driver和I2C_transfer函數(shù)
----I2C-dev.c
通用的I2C設(shè)備驅(qū)動(dòng)
----Kconfig
----MakefileLinux內(nèi)核的I2C總線驅(qū)動(dòng)程序框架如圖8-7所示。I2C總線驅(qū)動(dòng)程序主要由3個(gè)部分組成:I2Ccore(I2C核心),adapter(適配器),client(設(shè)備驅(qū)動(dòng))
。(1)I2Ccore(I2C核心)是I2C總線驅(qū)動(dòng)程序體系結(jié)構(gòu)的核心,它為總線設(shè)備驅(qū)動(dòng)提供統(tǒng)一的接口,通過這些接口來訪問在特定I2C設(shè)備驅(qū)動(dòng)程序中實(shí)現(xiàn)的功能,并實(shí)現(xiàn)從I2C
總線驅(qū)動(dòng)體系結(jié)構(gòu)中添加和刪除總線驅(qū)動(dòng)的方法等。(2)adapter部分代表I2C適配器驅(qū)動(dòng),adapter是各個(gè)適配器驅(qū)動(dòng)所構(gòu)成的集合,主要實(shí)現(xiàn)各相應(yīng)適配器數(shù)據(jù)結(jié)構(gòu)I2C_adapter的具體的通信傳輸算法(I2C_algorithm),此算法管理I2C控制器及實(shí)現(xiàn)總線數(shù)據(jù)的發(fā)送接收等操作。(3)Client部分則代表掛載在I2C總線上的設(shè)備驅(qū)動(dòng),Client部分是各個(gè)I2C設(shè)備構(gòu)成的集合,主要實(shí)現(xiàn)各描述I2C設(shè)備的數(shù)據(jù)結(jié)構(gòu)I2C_client及其私有部分,并通過I2Ccore提供的接口實(shí)現(xiàn)設(shè)備的注冊(cè),提供設(shè)備可使用的地址范圍及地址檢測成功后的回調(diào)函數(shù)。I2C總線驅(qū)動(dòng)處于控制中心的I2Ccore實(shí)現(xiàn)了控制策略,具體I2C總線的適配器和設(shè)備的驅(qū)動(dòng)實(shí)現(xiàn)了具體設(shè)備可用的機(jī)制,控制策略和底層機(jī)制通過中間的函數(shù)接口相聯(lián)系。正是中間的函數(shù)接口使得控制策略與底層機(jī)制無關(guān),從而使得控制策略具有良好的可移植性和重用性。在實(shí)際設(shè)計(jì)中,I2C核心提供的接口不需要修改,只需針對(duì)目標(biāo)總線適配器驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng)進(jìn)行必要修改即可。塊設(shè)備驅(qū)動(dòng)程序塊設(shè)備驅(qū)動(dòng)整體框架嵌入式網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)設(shè)計(jì)PartFour8.4嵌入式網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)
嵌入式Linux的網(wǎng)絡(luò)系統(tǒng)主要采用socket機(jī)制,操作系統(tǒng)和驅(qū)動(dòng)程序之間定義專門的數(shù)據(jù)結(jié)構(gòu)sk_buff用來進(jìn)行數(shù)據(jù)包的發(fā)送與接收。Sk_buff數(shù)據(jù)結(jié)構(gòu)Linux內(nèi)核對(duì)sk_buff的操作有分配、釋放、變更等。(1)分配操作structsk_buff*alloc_skb(unsignedintlen,gfp_tpriority);structsk_buff*dev_alloc_skb(unsignedintlen);(2)釋放操作voidkfree_skb(structsk_buff*skb);voiddev_kfree_skb(structsk_buff*skb);voiddev_kfree_skb_irq(structsk_buff*skb);voiddev_kfree_skb_any(structsk_buff*skb);
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025陜西省安全員知識(shí)題庫附答案
- 華能電廠采購合同范本
- 個(gè)人投資項(xiàng)目合同范本
- 2025河北省安全員-C證(專職安全員)考試題庫
- 廠家金融分期購車合同范本
- 勞動(dòng)合同范本美容
- 卡口抓拍合同范本
- 鄉(xiāng)村勞務(wù)服務(wù)合同范本
- 加盟餐飲合同范本
- 2025年吉林省安全員知識(shí)題庫及答案
- 蘇教版科學(xué)五年級(jí)下15《升旗的方法》教案
- 現(xiàn)代工業(yè)發(fā)酵調(diào)控緒論
- 超高性能混凝土項(xiàng)目立項(xiàng)申請(qǐng)(參考模板)
- 電纜橋架招標(biāo)文件范本(含技術(shù)規(guī)范書)
- 試車場各種道路施工方案設(shè)計(jì)
- 部編版四年級(jí)語文下冊(cè)第二單元《習(xí)作:我的奇思妙想》課件PPT
- PS零基礎(chǔ)入門學(xué)習(xí)教程(適合純小白)PPT課件
- XX輸變電工程公司作業(yè)風(fēng)險(xiǎn)評(píng)估數(shù)據(jù)庫(精品模板)
- 涂裝行業(yè)常用日語單詞集
- 頭頸部影像學(xué)表現(xiàn)(詳細(xì)、全面)
- 《國際商務(wù)》PPT課件.ppt
評(píng)論
0/150
提交評(píng)論