- Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解(第2版)
- 華清遠(yuǎn)見(jiàn)嵌入式培訓(xùn)中心 宋寶華編著
- 995字
- 2018-12-27 10:06:02
第1篇 Linux設(shè)備驅(qū)動(dòng)入門(mén)
Linux設(shè)備驅(qū)動(dòng)概述及開(kāi)發(fā)環(huán)境構(gòu)建
驅(qū)動(dòng)設(shè)計(jì)的硬件基礎(chǔ)
Linux內(nèi)核及內(nèi)核編程
第1章 Linux設(shè)備驅(qū)動(dòng)概述及開(kāi)發(fā)環(huán)境構(gòu)建
本章導(dǎo)讀
本章將介紹Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)的基本概念,并對(duì)本書(shū)所基于的平臺(tái)和開(kāi)發(fā)環(huán)境進(jìn)行講解。
1.1節(jié)闡明了設(shè)備驅(qū)動(dòng)的概念和作用。
1.2節(jié)和1.3節(jié)分別講解在無(wú)操作系統(tǒng)情況下和有操作系統(tǒng)情況下設(shè)備驅(qū)動(dòng)的設(shè)計(jì),通過(guò)對(duì)兩者不同的分析講解設(shè)備驅(qū)動(dòng)與硬件和操作系統(tǒng)的關(guān)系。
1.4節(jié)對(duì) Linux 操作系統(tǒng)的設(shè)備驅(qū)動(dòng)進(jìn)行了概要性的介紹,給出了設(shè)備驅(qū)動(dòng)與整個(gè)軟硬件系統(tǒng)的關(guān)系,分析了Linux設(shè)備驅(qū)動(dòng)的重點(diǎn)、難點(diǎn)和學(xué)習(xí)方法。
1.5節(jié)對(duì)本書(shū)所基于的LDD6410 ARM11開(kāi)發(fā)板和開(kāi)發(fā)環(huán)境的安裝進(jìn)行了介紹。
本章的最后給出了一個(gè)設(shè)備驅(qū)動(dòng)的“Hello World”實(shí)例,即最簡(jiǎn)單的LED驅(qū)動(dòng)在無(wú)操作系統(tǒng)情況下和Linux操作系統(tǒng)下的實(shí)現(xiàn)。
1.1 設(shè)備驅(qū)動(dòng)的作用
任何一個(gè)計(jì)算機(jī)系統(tǒng)的運(yùn)轉(zhuǎn)都是系統(tǒng)中軟硬件共同努力的結(jié)果,沒(méi)有硬件的軟件是空中樓閣,而沒(méi)有軟件的硬件則只是一堆廢鐵。硬件是底層基礎(chǔ),是所有軟件得以運(yùn)行的平臺(tái),代碼最終會(huì)落實(shí)為硬件上的組合邏輯與時(shí)序邏輯。軟件則實(shí)現(xiàn)了具體應(yīng)用,它按照各種不同的業(yè)務(wù)需求而設(shè)計(jì),完成了用戶的最終訴求。硬件較固定,軟件則很靈活,可以適應(yīng)各種復(fù)雜多變的應(yīng)用。可以說(shuō),計(jì)算機(jī)系統(tǒng)的軟硬件互相成就了對(duì)方。
但是,軟硬件之間同樣存在著悖論,那就是軟件和硬件不應(yīng)該互相滲透入對(duì)方的領(lǐng)地。為盡可能快速地完成設(shè)計(jì),應(yīng)用軟件工程師不想也不必關(guān)心硬件,而硬件工程師也難有足夠的閑暇和能力來(lái)顧及軟件。譬如,應(yīng)用軟件工程師在調(diào)用套接字發(fā)送和接收數(shù)據(jù)包的時(shí)候,不必關(guān)心網(wǎng)卡上的中斷、寄存器、存儲(chǔ)空間、I/O 端口、片選以及其他任何硬件詞匯;在使用 printf()函數(shù)輸出信息的時(shí)候,他不用知道底層究竟是怎樣把相應(yīng)的信息輸出到屏幕或者串口。
也就是說(shuō),應(yīng)用軟件工程師需要看到一個(gè)沒(méi)有硬件的純粹的軟件世界,硬件必須被透明地呈現(xiàn)給他。誰(shuí)來(lái)實(shí)現(xiàn)硬件對(duì)應(yīng)用軟件工程師的隱形?這個(gè)光榮而艱巨的任務(wù)就落在了驅(qū)動(dòng)工程師的頭上。
對(duì)設(shè)備驅(qū)動(dòng)最通俗的解釋就是“驅(qū)使硬件設(shè)備行動(dòng)”。驅(qū)動(dòng)與底層硬件直接打交道,按照硬件設(shè)備的具體工作方式,讀寫(xiě)設(shè)備的寄存器,完成設(shè)備的輪詢、中斷處理、DMA 通信,進(jìn)行物理內(nèi)存向虛擬內(nèi)存的映射等,最終讓通信設(shè)備能收發(fā)數(shù)據(jù),讓顯示設(shè)備能顯示文字和畫(huà)面,讓存儲(chǔ)設(shè)備能記錄文件和數(shù)據(jù)。
由此可見(jiàn),設(shè)備驅(qū)動(dòng)充當(dāng)了硬件和應(yīng)用軟件之間的紐帶,它使得應(yīng)用軟件只需要調(diào)用系統(tǒng)軟件的應(yīng)用編程接口(API)就可讓硬件去完成要求的工作。在系統(tǒng)中沒(méi)有操作系統(tǒng)的情況下,工程師可以根據(jù)硬件設(shè)備的特點(diǎn)自行定義接口,如對(duì)串口定義 SerialSend()、SerialRecv(),對(duì) LED定義LightOn()、LightOff(),對(duì)Flash定義FlashWrite()、FlashRead()等。而在有操作系統(tǒng)的情況下,驅(qū)動(dòng)的架構(gòu)則由相應(yīng)的操作系統(tǒng)定義,驅(qū)動(dòng)工程師必須按照相應(yīng)的架構(gòu)設(shè)計(jì)驅(qū)動(dòng),這樣,驅(qū)動(dòng)才能良好地整合入操作系統(tǒng)的內(nèi)核。
驅(qū)動(dòng)程序溝通著硬件和應(yīng)用軟件,而驅(qū)動(dòng)工程師則溝通著硬件工程師和應(yīng)用軟件工程師。目前,隨著通信、電子行業(yè)的迅速發(fā)展,全世界每天都會(huì)有大量的新芯片被生產(chǎn),大量的新電路板被設(shè)計(jì),也因此,會(huì)有大量設(shè)備驅(qū)動(dòng)需要開(kāi)發(fā)。這些驅(qū)動(dòng),或運(yùn)行在簡(jiǎn)單的單任務(wù)環(huán)境,或運(yùn)行在VxWorks、Linux、Windows等多任務(wù)操作系統(tǒng)環(huán)境,發(fā)揮著不可替代的作用。
1.2 無(wú)操作系統(tǒng)時(shí)的設(shè)備驅(qū)動(dòng)
并不是任何一個(gè)計(jì)算機(jī)系統(tǒng)都一定要運(yùn)行操作系統(tǒng),在許多情況下,操作系統(tǒng)都不必存在。對(duì)于功能比較單一、控制并不復(fù)雜的系統(tǒng),譬如ASIC內(nèi)部、公交車(chē)的刷卡機(jī)、電冰箱、微波爐、簡(jiǎn)單的手機(jī)和小靈通等,并不需要多任務(wù)調(diào)度、文件系統(tǒng)、內(nèi)存管理等復(fù)雜功能,用單任務(wù)架構(gòu)完全可以良好地支持它們的工作。一個(gè)無(wú)限循環(huán)中夾雜對(duì)設(shè)備中斷的檢測(cè)或者對(duì)設(shè)備的輪詢是這種系統(tǒng)中軟件的典型架構(gòu),如代碼清單1.1。
代碼清單1.1 單任務(wù)軟件典型架構(gòu)
1 int main(int argc, char* argv[]) 2 { 3 while (1) 4 { 5 if (serialInt == 1) 6 /*有串口中斷*/ 7 { 8 ProcessSerialInt(); /*處理串口中斷*/ 9 serialInt = 0; /*中斷標(biāo)志變量清0*/ 10 } 11 if (keyInt == 1) 12 /*有按鍵中斷*/ 13 { 14 ProcessKeyInt(); /*處理按鍵中斷*/ 15 keyInt = 0; /*中斷標(biāo)志變量清0*/ 16 } 17 status = CheckXXX(); 18 switch (status) 19 { 20 ... 21 } 22 ... 23 } 24 }
在這樣的系統(tǒng)中,雖然不存在操作系統(tǒng),但是設(shè)備驅(qū)動(dòng)則無(wú)論如何都必須存在。一般情況下,每一種設(shè)備驅(qū)動(dòng)都會(huì)定義為一個(gè)軟件模塊,包含.h文件和.c文件,前者定義該設(shè)備驅(qū)動(dòng)的數(shù)據(jù)結(jié)構(gòu)并聲明外部函數(shù),后者進(jìn)行驅(qū)動(dòng)的具體實(shí)現(xiàn)。譬如,可以如代碼清單1.2那樣定義一個(gè)串口的驅(qū)動(dòng)。
代碼清單1.2無(wú)操作系統(tǒng)情況下串口的驅(qū)動(dòng)
1 /********************** 2 *serial.h文件 3 **********************/ 4 extern void SerialInit(void); 5 extern void SerialSend(const char buf*,int count); 6 extern void SerialRecv(char buf*,int count); 7 8 /********************** 9 *serial.c文件 10 **********************/ 11 /*初始化串口*/ 12 void SerialInit(void) 13 { 14 ... 15 } 16 /*串口發(fā)送*/ 17 void SerialSend(const char buf*,int count) 18 { 19 ... 20 } 21 /*串口接收*/ 22 void SerialRecv(char buf*,int count) 23 { 24 ... 25 } 26 /*串口中斷處理函數(shù)*/ 27 void SerialIsr(void) 28 { 29 ... 30 serialInt = 1; 31 }
其他模塊想要使用這個(gè)設(shè)備的時(shí)候,只需要包含設(shè)備驅(qū)動(dòng)的頭文件 serial.h,然后調(diào)用其中的外部接口函數(shù)。如我們要從串口上發(fā)送“Hello World”字符串,使用語(yǔ)句SerialSend(“Hello World”,11)即可。
由此可見(jiàn),在沒(méi)有操作系統(tǒng)的情況下,設(shè)備驅(qū)動(dòng)的接口被直接提交給了應(yīng)用軟件工程師,應(yīng)用軟件沒(méi)有跨越任何層次就直接訪問(wèn)了設(shè)備驅(qū)動(dòng)的接口。驅(qū)動(dòng)包含的接口函數(shù)也與硬件的功能直接吻合,沒(méi)有任何附加功能。圖1.1所示為無(wú)操作系統(tǒng)情況下硬件、驅(qū)動(dòng)與應(yīng)用軟件的關(guān)系。

圖1.1 無(wú)操作系統(tǒng)時(shí)硬件、驅(qū)動(dòng)和應(yīng)用軟件的關(guān)系
有的工程師把單任務(wù)系統(tǒng)設(shè)計(jì)成了如圖1.2所示的結(jié)構(gòu),即設(shè)備驅(qū)動(dòng)和具體的應(yīng)用軟件模塊之間平等,驅(qū)動(dòng)中包含了業(yè)務(wù)層面上的處理,這顯然是不合理的,不符合軟件設(shè)計(jì)中高內(nèi)聚、低耦合的要求。

圖1.2 驅(qū)動(dòng)與應(yīng)用高耦合的不合理設(shè)計(jì)
另一種不合理的設(shè)計(jì)是直接在應(yīng)用中操作硬件的寄存器,而不單獨(dú)設(shè)計(jì)驅(qū)動(dòng)模塊,如圖1.3所示。這種設(shè)計(jì)意味著系統(tǒng)中不存在或未能充分利用可被重用的驅(qū)動(dòng)代碼。

圖1.3 應(yīng)用直接訪問(wèn)硬件的不合理設(shè)計(jì)
1.3 有操作系統(tǒng)時(shí)的設(shè)備驅(qū)動(dòng)
1.2節(jié)中我們看到一個(gè)干凈利落的設(shè)備驅(qū)動(dòng),它直接運(yùn)行在硬件之上,不與任何操作系統(tǒng)關(guān)聯(lián)。當(dāng)系統(tǒng)中包含操作系統(tǒng)后,設(shè)備驅(qū)動(dòng)會(huì)變得怎樣?
首先,無(wú)操作系統(tǒng)時(shí)設(shè)備驅(qū)動(dòng)的硬件操作工作仍然是必不可少的,沒(méi)有這一部分,驅(qū)動(dòng)不可能與硬件打交道。
其次,我們還需要將驅(qū)動(dòng)融入內(nèi)核。為了實(shí)現(xiàn)這種融合,必須在所有設(shè)備的驅(qū)動(dòng)中設(shè)計(jì)面向操作系統(tǒng)內(nèi)核的接口,這樣的接口由操作系統(tǒng)規(guī)定,對(duì)一類設(shè)備而言結(jié)構(gòu)一致,獨(dú)立于具體的設(shè)備。
由此可見(jiàn),當(dāng)系統(tǒng)中存在操作系統(tǒng)的時(shí)候,驅(qū)動(dòng)變成了連接硬件和內(nèi)核的橋梁。如圖1.4,操作系統(tǒng)的存在勢(shì)必要求設(shè)備驅(qū)動(dòng)附加更多的代碼和功能,把單一的“驅(qū)使硬件設(shè)備行動(dòng)”變成了操作系統(tǒng)內(nèi)與硬件交互的模塊,它對(duì)外呈現(xiàn)為操作系統(tǒng)的API,不再給應(yīng)用軟件工程師直接提供接口。

圖1.4 硬件、驅(qū)動(dòng)、操作系統(tǒng)和應(yīng)用程序的關(guān)系
那么我們要問(wèn),有了操作系統(tǒng)之后,驅(qū)動(dòng)反而變得復(fù)雜,那要操作系統(tǒng)干什么?
首先,一個(gè)復(fù)雜的軟件系統(tǒng)需要處理多個(gè)并發(fā)的任務(wù),沒(méi)有操作系統(tǒng),想完成多任務(wù)并發(fā)是很困難的。
其次,操作系統(tǒng)給我們提供內(nèi)存管理機(jī)制。一個(gè)典型的例子是,對(duì)于多數(shù)含MMU的處理器而言, Windows、Linux 等操作系統(tǒng)可以讓每個(gè)進(jìn)程都可以獨(dú)立地訪問(wèn)4GB的內(nèi)存空間。
上述優(yōu)點(diǎn)似乎并沒(méi)有體現(xiàn)在設(shè)備驅(qū)動(dòng)身上,操作系統(tǒng)的存在給設(shè)備驅(qū)動(dòng)究竟帶來(lái)了什么實(shí)質(zhì)的好處?
簡(jiǎn)而言之,操作系統(tǒng)通過(guò)給驅(qū)動(dòng)制造麻煩來(lái)達(dá)到給上層應(yīng)用提供便利的目的。當(dāng)驅(qū)動(dòng)都按照操作系統(tǒng)給出的獨(dú)立于設(shè)備的接口而設(shè)計(jì),那么,應(yīng)用程序?qū)⒖墒褂媒y(tǒng)一的系統(tǒng)調(diào)用接口來(lái)訪問(wèn)各種設(shè)備。對(duì)于類UNIX的VxWorks、Linux等操作系統(tǒng)而言,當(dāng)應(yīng)用程序通過(guò)write()、read()等函數(shù)讀寫(xiě)文件就可訪問(wèn)各種字符設(shè)備和塊設(shè)備,而不論設(shè)備的具體類型和工作方式,那將是怎樣的便利?
1.4 Linux設(shè)備驅(qū)動(dòng)
1.4.1 設(shè)備的分類及特點(diǎn)
計(jì)算機(jī)系統(tǒng)的硬件主要由 CPU、存儲(chǔ)器和外設(shè)組成。隨著 IC制作工藝的發(fā)展,目前,芯片的集成度越來(lái)越高,往往在 CPU 內(nèi)部就集成了存儲(chǔ)器和外設(shè)適配器。譬如,相當(dāng)多的ARM、PowerPC、MIPS等處理器都集成了UART、I2C控制器、USB控制器、SDRAM控制器等,有的處理器還集成了片內(nèi)RAM和Flash。
驅(qū)動(dòng)針對(duì)的對(duì)象是存儲(chǔ)器和外設(shè)(包括CPU內(nèi)部集成的存儲(chǔ)器和外設(shè)),而不是針對(duì)CPU核。Linux將存儲(chǔ)器和外設(shè)分為3個(gè)基礎(chǔ)大類。
● 字符設(shè)備。
● 塊設(shè)備。
● 網(wǎng)絡(luò)設(shè)備。
字符設(shè)備指那些必須以串行順序依次進(jìn)行訪問(wèn)的設(shè)備,如觸摸屏、磁帶驅(qū)動(dòng)器、鼠標(biāo)等。塊設(shè)備可以用任意順序進(jìn)行訪問(wèn),以塊為單位進(jìn)行操作,如硬盤(pán)、軟驅(qū)等。字符設(shè)備不經(jīng)過(guò)系統(tǒng)的快速緩沖,而塊設(shè)備經(jīng)過(guò)系統(tǒng)的快速緩沖。但是,字符設(shè)備和塊設(shè)備并沒(méi)有明顯的界限,如對(duì)于Flash設(shè)備,符合塊設(shè)備的特點(diǎn),但是我們?nèi)匀豢梢园阉鳛橐粋€(gè)字符設(shè)備來(lái)訪問(wèn)。
字符設(shè)備和塊設(shè)備的驅(qū)動(dòng)設(shè)計(jì)呈現(xiàn)出很大的差異,但是對(duì)于用戶而言,他們都使用文件系統(tǒng)的操作接口open()、close()、read()、write()等進(jìn)行訪問(wèn)。
在Linux系統(tǒng)中,網(wǎng)絡(luò)設(shè)備面向數(shù)據(jù)包的接收和發(fā)送而設(shè)計(jì),它并不對(duì)應(yīng)于文件系統(tǒng)的節(jié)點(diǎn)。內(nèi)核與網(wǎng)絡(luò)設(shè)備的通信與內(nèi)核和字符設(shè)備、網(wǎng)絡(luò)設(shè)備的通信方式完全不同。
另外一種設(shè)備分類方法中所稱的I2C驅(qū)動(dòng)、USB驅(qū)動(dòng)、PCI驅(qū)動(dòng)、LCD驅(qū)動(dòng)等本身可歸納入3個(gè)基礎(chǔ)大類,但是對(duì)于這些復(fù)雜的設(shè)備,Linux也定義了獨(dú)特的驅(qū)動(dòng)體系結(jié)構(gòu)。
1.4.2 Linux設(shè)備驅(qū)動(dòng)與整個(gè)軟硬件系統(tǒng)的關(guān)系
如圖1.5所示,除網(wǎng)絡(luò)設(shè)備外,字符設(shè)備與塊設(shè)備都被映射到Linux文件系統(tǒng)的文件和目錄,通過(guò)文件系統(tǒng)的系統(tǒng)調(diào)用接口 open()、write()、read()、close()等即可訪問(wèn)字符設(shè)備和塊設(shè)備。所有的字符設(shè)備和塊設(shè)備都被統(tǒng)一地呈現(xiàn)給用戶。塊設(shè)備比字符設(shè)備復(fù)雜,在它上面會(huì)首先建立一個(gè)磁盤(pán)/Flash文件系統(tǒng),如FAT、EXT3、YAFFS2、JFFS2、UBIFS等。FAT、EXT3、YAFFS2、JFFS2、UBIFS定義了文件和目錄在存儲(chǔ)介質(zhì)上的組織。

圖1.5 Linux設(shè)備驅(qū)動(dòng)與整個(gè)軟硬件系統(tǒng)的關(guān)系
應(yīng)用程序可以使用Linux的系統(tǒng)調(diào)用接口編程,但也可使用C庫(kù)函數(shù),出于代碼可移植性的目的,后者更值得推薦。C庫(kù)函數(shù)本身也通過(guò)系統(tǒng)調(diào)用接口而實(shí)現(xiàn),如C庫(kù)函數(shù)fopen()、fwrite()、fread()、fclose()分別會(huì)調(diào)用操作系統(tǒng)的API open()、write()、read()、close()。
1.4.3 Linux設(shè)備驅(qū)動(dòng)的重點(diǎn)、難點(diǎn)
Linux設(shè)備驅(qū)動(dòng)的學(xué)習(xí)是一項(xiàng)浩繁的工程,包含如下的重點(diǎn)、難點(diǎn)。
● 編寫(xiě) Linux 設(shè)備驅(qū)動(dòng)要求工程師有非常好的硬件基礎(chǔ),懂得 SRAM、Flash、SDRAM、磁盤(pán)的讀寫(xiě)方式,UART、I2C、USB等設(shè)備的接口以及輪詢、中斷、DMA的原理,PCI總線的工作方式以及CPU的內(nèi)存管理單元(MMU)等。
● 編寫(xiě)Linux設(shè)備驅(qū)動(dòng)要求工程師有非常好的C語(yǔ)言基礎(chǔ),能靈活地運(yùn)用C語(yǔ)言的結(jié)構(gòu)體、指針、函數(shù)指針及內(nèi)存動(dòng)態(tài)申請(qǐng)和釋放等。
● 編寫(xiě)Linux設(shè)備驅(qū)動(dòng)要求工程師有一定的Linux內(nèi)核基礎(chǔ),雖然并不要求工程師對(duì)內(nèi)核各個(gè)部分有深入的研究,但至少要明白驅(qū)動(dòng)與內(nèi)核的接口。尤其是對(duì)于塊設(shè)備、網(wǎng)絡(luò)設(shè)備、Flash設(shè)備、串口設(shè)備等復(fù)雜設(shè)備,內(nèi)核定義的驅(qū)動(dòng)體系架構(gòu)本身就非常復(fù)雜。
● 編寫(xiě)Linux設(shè)備驅(qū)動(dòng)要求工程師有非常好的多任務(wù)并發(fā)控制和同步的基礎(chǔ),因?yàn)樵隍?qū)動(dòng)中會(huì)大量使用自旋鎖、互斥、信號(hào)量、等待隊(duì)列等并發(fā)與同步機(jī)制。
上述經(jīng)驗(yàn)值的獲取并非朝夕之事,因此要求我們有足夠的學(xué)習(xí)恒心和毅力。對(duì)這些重點(diǎn)、難點(diǎn),本書(shū)都會(huì)有相應(yīng)章節(jié)進(jìn)行講解。
動(dòng)手實(shí)踐永遠(yuǎn)是學(xué)習(xí)任何軟件開(kāi)發(fā)的最好方法,學(xué)習(xí)Linux設(shè)備驅(qū)動(dòng)也不例外。因此,本書(shū)專門(mén)配備了一款基于S3C6410的ARM11開(kāi)發(fā)板LDD6410(全稱Linux Device Drivers 6410,即Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)6410專用板),本書(shū)中的所有實(shí)例均可在該電路板上直接執(zhí)行。
閱讀經(jīng)典書(shū)籍和參與 Linux 社區(qū)的討論也是非常好的學(xué)習(xí)方法。Linux 內(nèi)核源代碼中包含了一個(gè)Documentation 目錄,其中包含了一批內(nèi)核設(shè)計(jì)的文檔,全部是文本文件。很遺憾,這些文檔的組織不太好,內(nèi)容也不夠細(xì)致。本書(shū)的參考目錄中給出了一些優(yōu)秀的參考書(shū)籍和Linux網(wǎng)站,并進(jìn)行了簡(jiǎn)單的介紹。
學(xué)習(xí)Linux設(shè)備驅(qū)動(dòng)的一個(gè)注意事項(xiàng)是要避免管中窺豹、只見(jiàn)樹(shù)木不見(jiàn)森林,因?yàn)楦黝怢inux設(shè)備驅(qū)動(dòng)都從屬于一個(gè)Linux設(shè)備驅(qū)動(dòng)的架構(gòu),單純而片面地學(xué)習(xí)幾個(gè)函數(shù)、幾個(gè)數(shù)據(jù)結(jié)構(gòu)是不可能理清驅(qū)動(dòng)中各組成部分之間的關(guān)系的。因此,Linux 驅(qū)動(dòng)的分析方法是點(diǎn)面結(jié)合,將對(duì)函數(shù)和數(shù)據(jù)結(jié)構(gòu)的理解放在整體架構(gòu)的背景之中。這是本書(shū)各章節(jié)講解驅(qū)動(dòng)的方法。
1.5 Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)環(huán)境構(gòu)建
1.5.1 PC上的Linux環(huán)境
本書(shū)配套光盤(pán)提供了一個(gè)Ubuntu的VirtualBox虛擬機(jī)映像,該虛擬機(jī)上安裝了所有本書(shū)涉及的源代碼、工具鏈和各種開(kāi)發(fā)工具,讀者無(wú)需再安裝和配置任何環(huán)境。該虛擬機(jī)可運(yùn)行于Windows等操作系統(tǒng)中,運(yùn)行方法如下。
(1)解壓縮安裝盤(pán)內(nèi)的虛擬機(jī)磁盤(pán)映像virtual-disk.rar到本地硬盤(pán)得到virtual-disk.vdi(至少需要16GB的空閑磁盤(pán)空間)。
(2)安裝安裝盤(pán)內(nèi)的VirtualBox虛擬機(jī)軟件。
(3)建立一個(gè)虛擬機(jī)。
① 單擊“新建”按鈕,指定虛擬機(jī)使用Linux Ubuntu系統(tǒng),如圖1.6所示。

圖1.6 VirtualBox指定使用Ubuntu
② 單擊“下一步”按鈕,如圖1.7所示,使用推薦的內(nèi)存384MB。

圖1.7 VirtualBox中內(nèi)存設(shè)定
③ 指定虛擬機(jī)磁盤(pán)映像為第一步解壓縮得到的virtual-disk.vdi,如圖1.8所示。

圖1.8 VirtualBox中磁盤(pán)設(shè)定
④ 完成設(shè)置,如圖1.9所示。
之后就可以啟動(dòng)虛擬機(jī),賬號(hào)和密碼都是“l(fā)ihacker”。本書(shū)配套源代碼都位于lihacker主目錄的develop目錄下,幾個(gè)主要項(xiàng)目針對(duì)/home/lihacker/develop/的子目錄如下。
LDD6410開(kāi)發(fā)板內(nèi)核源代碼:svn/ldd6410-2-6-28-read-only/linux-2.6.28-samsung。
LDD6410開(kāi)發(fā)板U-BOOT源代碼:svn/ldd6410-read-only/s3c-u-boot-1.1.6。

圖1.9 VirtualBox中完成設(shè)定
LDD6410 開(kāi)發(fā)板文件系統(tǒng)用的busybox、jpegview、mplayer、appweb 等:svn/ldd6410-read-only/utils。
LDD6410開(kāi)發(fā)板及常用Linux用戶空間驅(qū)動(dòng)測(cè)試程序:svn/ldd6410-read-only/tests。
書(shū)中g(shù)lobalmem、globalfifo等驅(qū)動(dòng)實(shí)例:svn/ldd6410-read-only/training/kernel。
Android的源代碼:git/myandroid。
NDK:android-ndk-r3。
eclipse:?jiǎn)螕糇烂嫔系摹癮ndroid-eclipse”圖標(biāo),即可運(yùn)行附帶ADT的eclipse開(kāi)發(fā)工具。
1.5.2 LDD6410開(kāi)發(fā)板
LDD6410是本書(shū)專配的一款高端ARM11處理器開(kāi)發(fā)板(其結(jié)構(gòu)如圖1.10所示,實(shí)物如圖1.11所示),采用三星公司最新推出S3C6410處理器,芯片擁有強(qiáng)大的內(nèi)部資源和視頻處理能力,板上集成了豐富的外圍接口,其主要特點(diǎn)如下。
(1)運(yùn)行于533MHz的ARM11處理器(最高主頻可達(dá)到667MHz)。
(2)運(yùn)行于266MHz的DDR內(nèi)存,128MB。
(3)1MB NOR Flash。
(4)256MB NAND Flash。
(5)WM9714 AC97聲卡。
(6)VGA輸出接口(可達(dá)1024×768@60Hz)。
(7)TV輸出接口。
(8)USB2.0 OTG接口及USB1.1 host接口。

圖1.10 LDD6410的結(jié)構(gòu)圖

圖1.11 LDD6410實(shí)物圖
(9)SD/SDIO接口,支持SD卡和SDIO設(shè)備。
(10)DM9000百兆網(wǎng)卡。
(11)4.3寸LCD(分辨率為480×272)、觸摸屏。
(12)S3C6410芯片內(nèi)嵌圖形加速,JPEG、多媒體編解碼。
(13)6個(gè)GPIO按鍵。
(14)可擴(kuò)展Camera、WiFi、3G modem等模塊。
(15)可擴(kuò)展外部矩陣鍵盤(pán)。
配套電路板提供了如下軟件。
(1)工具鏈:提供了arm-linux-gcc、arm-linux-gdb、gdbserver、strace用于Android開(kāi)發(fā)的eclipse (帶ADT插件)、JDK和NDK。
(2)U-BOOT:U-BOOT源代碼包含獨(dú)立的LDD6410文件,支持從SD卡、NAND啟動(dòng),支持DM9000網(wǎng)卡引導(dǎo)。
(3)Linux內(nèi)核、BSP和驅(qū)動(dòng):Linux2.6.28內(nèi)核、源代碼,包含獨(dú)立的LDD6410 BSP和完整的設(shè)備驅(qū)動(dòng)。
(4)文件系統(tǒng):基于新版Busybox1.15.1,文件系統(tǒng)集成jpegview、mplayer、appweb等大量應(yīng)用,集成了按鍵、鼠標(biāo)、觸摸屏、LCD等測(cè)試程序,作為驅(qū)動(dòng)的用戶應(yīng)用案例。
(5)Android:提供Android源代碼和文件系統(tǒng)、內(nèi)核電源管理補(bǔ)丁源代碼、內(nèi)核Android驅(qū)動(dòng)源代碼。LDD6410的Android系統(tǒng)支持按鍵、觸摸屏和鼠標(biāo)操作,支持使用LCD和VGA進(jìn)行顯示。(6)QT:LDD6410支持Qt/Embedded4.5.3,移植了Ts_lib和Tslib, ts_calibration,支持使用觸摸屏進(jìn)行操作。
LDD6410支持從SD卡或NAND啟動(dòng),通過(guò)電路板上的SW1可設(shè)置LDD6410的啟動(dòng)模式。從SD卡啟動(dòng)設(shè)備為全ON;從NAND啟動(dòng)時(shí),將1、2設(shè)置為ON,3、4設(shè)置為OFF。
LDD6410開(kāi)發(fā)板的詳細(xì)使用方法,請(qǐng)見(jiàn)配套光盤(pán)中的“LDD6410開(kāi)發(fā)板用戶手冊(cè)”。
1.5.3 工具鏈安裝
本書(shū)配套光盤(pán)的虛擬機(jī)映像中已經(jīng)安裝好了LDD6410的工具鏈,讀者如果想在其他環(huán)境中安裝,只需要從 http://ldd6410.googlecode.com/files/cross-4.2.2-eabi.tar.bz2下載。LDD6410 開(kāi)發(fā)板工具鏈為S3C6410X-ToolChain4.2.2-EABI-V0.0-cross-4.2.2-eabi.tar。安裝步驟如下。
(1)解壓上述工具鏈獲得文件夾:4.2.2-eabi/。
(2)在/usr/local/下面創(chuàng)建目錄 arm/(注意,最好是放到這個(gè)目錄,不然在以后的編譯過(guò)程中可能出現(xiàn)一些錯(cuò)誤)。
(3)將目錄4.2.2-eabi/移動(dòng)到/usr/local/arm/下面。
(4)設(shè)置環(huán)境變量。
編輯/etc/profile 文件,在文件末尾添加:
PATH="$PATH:/usr/local/arm/4.2.2-eabi/usr/bin" export PATH
使環(huán)境變量生效,在終端輸入命令:
source /etc/profile
另外,也可以通過(guò)修改home目錄的.bashrc來(lái)將/usr/local/arm/4.2.2-eabi/usr/bin添加到PATH:
export PATH=/usr/local/arm/4.2.2-eabi/usr/bin/:$PATH
(5)測(cè)試環(huán)境變量是否設(shè)置成功。
在終端輸入:echo $PATH,如果輸出的路徑中包含了/usr/local/arm/4.2.2-eabi/usr/bin,則說(shuō)明環(huán)境變量設(shè)置成功。
(6)測(cè)試交叉編譯工具鏈。
在終端輸入“arm-linux-gcc -v”,顯示如下:
Using built-in specs. Target: arm-unknown-linux-gnueabi Configured with: /home/scsuh/workplace/coffee/buildroot-20071011/toolchain_build_arm /gcc-4.2.2/configure --prefix=/usr --build=i386-pc-linux-gnu --host=i386-pc-linux-gnu --target=arm-unknown-linux-gnueabi --enable-languages=c,c++ --with-sysroot=/usr/local /arm/4.2.2-eabi/ --with-build-time-tools=/usr/local/arm/4.2.2-eabi//usr/arm-unknown-linux- gnueabi/bin --disable-cxa_atexit --enable-target-optspace --with-gnu-ld --enable-shared --with-gmp=/usr/local/arm/4.2.2-eabi/gmp --with-mpfr=/usr/local/arm/4.2.2-eabi//mpfr --disable-nls --enable-threads --disable-multilib --disable-largefile --with-arch=armv4t --with-float=soft --enable-cxx-flags=-msoft-float Thread model: posix gcc version 4.2.2
說(shuō)明交叉編譯工具鏈已經(jīng)安裝成功。
ldd6410-debug-tools.tar.gz調(diào)試工具包包含了strace、gdbserver和arm-linux-gdb,其中 strace、gdbserver用于目標(biāo)板文件系統(tǒng),arm-linux-gdb 運(yùn)行于主機(jī)端,對(duì)目標(biāo)板上的內(nèi)核、內(nèi)核模塊應(yīng)用程序進(jìn)行調(diào)試。
下載地址為 http://ldd6410.googlecode.com/files/ldd6410-debug-tools.tar.gz,光盤(pán)目錄為 toolchains/ldd6410-debug-tools.tar.gz。
解壓ldd6410-debug-tools.tar.gz,將其中的arm-linux-gdb放入主機(jī)上arm-linux-gcc所在的目錄/usr/local/arm/4.2.2-eabi/usr/bin/。
而 strace、gdbserver則可根據(jù)需要放入目標(biāo)機(jī)根文件系統(tǒng)的/usr/sbin目錄。
1.5.4 主機(jī)端nfs和tftp服務(wù)安裝
本書(shū)配套光盤(pán)的虛擬機(jī)映像中已經(jīng)安裝好了nfs和tftp,LDD6410可使用tftp或nfs文件系統(tǒng)與主機(jī)通過(guò)網(wǎng)口交互。如果用戶想在其他環(huán)境下自行安裝,對(duì)于Ubuntu或Debian用戶而言,在主機(jī)端可通過(guò)如下方法安裝tftp服務(wù):
sudo apt-get install tftpd-hpa
開(kāi)啟tftp服務(wù):
sudo /etc/init.d/tftpdhpa start Starting HPA's tftpd: in.tftpd.
對(duì)于Ubuntu或Debian用戶而言,在主機(jī)端可通過(guò)如下方法安裝nfs服務(wù):
apt-get install nfs-kernel-server sudo mkdir /home/nfs sudo chmod 777 /home/nfs
運(yùn)行“sudo vim /etc/exports”或“sudo gedit /etc/exports”,修改該文件內(nèi)容為:
/home/nfs *(sync,rw)
運(yùn)行exportfs rv開(kāi)啟NFS服務(wù):
/etc/init.d/nfs-kernel-server restart
1.5.5 源代碼閱讀和編輯
源代碼是學(xué)習(xí) Linux 的最權(quán)威資料,在 Windows 上閱讀 Linux 源代碼的最佳工具是 Source Insight,在其中建立一個(gè)工程,并將 Linux 的所有源代碼加入該工程,同步這個(gè)工程之后,我們將可以非常方便地在代碼之間進(jìn)行關(guān)聯(lián)閱讀,如圖1.12所示。

圖1.12 在Source Insight中閱讀Linux源代碼
網(wǎng)站http://lxr.linux.no/提供了內(nèi)核版本2.6.11到最新版Linux源代碼的交叉索引,在其中輸入Linux 內(nèi)核中的函數(shù)、數(shù)據(jù)結(jié)構(gòu)或變量的名稱就可以直接得到以超鏈接形式給出的定義和引用它的所有位置。還有一些網(wǎng)站也提供了Linux內(nèi)核中函數(shù)、變量和數(shù)據(jù)結(jié)構(gòu)的搜索能力,在google中搜索“l(fā)inux identifier search”可得。
在Linux主機(jī)上閱讀和編輯Linux源碼的常用方式是vim + cscope或者vim + ctags,vim是一個(gè)文本編輯器,而cscope和ctags則可建立代碼索引,建議讀者盡快使用基于文本界面全鍵盤(pán)操作的vim編輯器,如圖1.13所示。

圖1.13 vim編輯器
1.6 設(shè)備驅(qū)動(dòng)Hello World:LED驅(qū)動(dòng)
1.6.1 無(wú)操作系統(tǒng)時(shí)的LED驅(qū)動(dòng)
在嵌入式系統(tǒng)的設(shè)計(jì)中,LED 一般直接由 CPU 的GPIO(通用可編程 I/O 口)控制。GPIO一般由兩組寄存器控制,即一組控制寄存器和一組數(shù)據(jù)寄存器。控制寄存器可設(shè)置GPIO口的工作方式為輸入或是輸出。當(dāng)引腳被設(shè)置為輸出時(shí),向數(shù)據(jù)寄存器的對(duì)應(yīng)位寫(xiě)入1和0會(huì)分別在引腳上產(chǎn)生高電平和低電平;當(dāng)引腳設(shè)置為輸入時(shí),讀取數(shù)據(jù)寄存器的對(duì)應(yīng)位可獲得引腳上的電平為高或低。
在本例子中,我們屏蔽具體CPU的差異,假設(shè)在GPIO_REG_CTRL物理地址處的控制寄存器處的第n位寫(xiě)入1可設(shè)置GPIO為輸出,在地址GPIO_REG_DATA物理地址處的數(shù)據(jù)寄存器的第n位寫(xiě)入1或0可在引腳上產(chǎn)生高或低電平,則無(wú)操作系統(tǒng)的情況下,設(shè)備驅(qū)動(dòng)為代碼清單1.3。
代碼清單1.3 無(wú)操作系統(tǒng)時(shí)的LED驅(qū)動(dòng)
1 #define reg_gpio_ctrl *(volatile int *)(ToVirtual(GPIO_REG_CTRL)) 2 #define reg_gpio_data *(volatile int *)(ToVirtual(GPIO_REG_DATA)) 3 /*初始化LED*/ 4 void LightInit(void) 5 { 6 reg_gpio_ctrl |= (1 << n); /*設(shè)置GPIO為輸出*/ 7 } 8 9 /*點(diǎn)亮LED*/ 10 void LightOn(void) 11 { 12 reg_gpio_data |= (1 << n); /*在GPIO上輸出高電平*/ 13 } 14 15 /*熄滅LED*/ 16 void LightOff(void) 17 { 18 reg_gpio_data &= ~(1 << n); /*在GPIO上輸出低電平*/ 19 }
上述程序中的LightInit()、LightOn()、LightOff()都直接作為驅(qū)動(dòng)提供給應(yīng)用程序的外部接口函數(shù)。程序中ToVirtual()的作用是當(dāng)系統(tǒng)啟動(dòng)了硬件MMU之后,根據(jù)物理地址和虛擬地址的映射關(guān)系,將寄存器的物理地址轉(zhuǎn)化為虛擬地址。
1.6.2 Linux下的LED驅(qū)動(dòng)
在Linux下,可以使用字符設(shè)備驅(qū)動(dòng)的框架來(lái)編寫(xiě)對(duì)應(yīng)于代碼清單1.3的LED設(shè)備驅(qū)動(dòng)(這里僅僅是為了講解的方便,到后文我們會(huì)發(fā)現(xiàn),內(nèi)核中實(shí)際實(shí)現(xiàn)了一個(gè)提供sysfs結(jié)點(diǎn)的GPIO LED驅(qū)動(dòng),位于drivers/leds/leds-gpio.c),操作硬件的LightInit()、LightOn()、LightOff()函數(shù)仍然需要,但是,遵循 Linux 編程的命名習(xí)慣,重新將其命名為 light_init()、light_on()、light_off()。這些函數(shù)將被LED設(shè)備驅(qū)動(dòng)中獨(dú)立于設(shè)備的針對(duì)內(nèi)核的接口進(jìn)行調(diào)用,代碼清單1.4給出了Linux下LED的驅(qū)動(dòng),此時(shí)讀者并不需要能讀懂這些代碼。
代碼清單1.4 Linux操作系統(tǒng)下LED的驅(qū)動(dòng)
1 #include .../*包含內(nèi)核中的多個(gè)頭文件*/ 2 /*設(shè)備結(jié)構(gòu)體*/ 3 struct light_dev { 4 struct cdev cdev; /*字符設(shè)備cdev結(jié)構(gòu)體*/ 5 unsigned char vaule; /*LED亮?xí)r為1,熄滅時(shí)為0,用戶可讀寫(xiě)此值*/ 6 }; 7 struct light_dev *light_devp; 8 int light_major = LIGHT_MAJOR; 9 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); 10 MODULE_LICENSE("Dual BSD/GPL"); 11 /*打開(kāi)和關(guān)閉函數(shù)*/ 12 int light_open(struct inode *inode, struct file *filp) 13 { 14 struct light_dev *dev; 15 /* 獲得設(shè)備結(jié)構(gòu)體指針 */ 16 dev = container_of(inode->i_cdev, struct light_dev, cdev); 17 /* 讓設(shè)備結(jié)構(gòu)體作為設(shè)備的私有信息 */ 18 filp->private_data = dev; 19 return 0; 20 } 21 int light_release(struct inode *inode, struct file *filp) 22 { 23 return 0; 24 } 25 /*讀寫(xiě)設(shè)備:可以不需要 */ 26 ssize_t light_read(struct file *filp, char __user *buf, size_t count, 27 loff_t *f_pos) 28 { 29 struct light_dev *dev = filp->private_data; /*獲得設(shè)備結(jié)構(gòu)體 */ 30 if (copy_to_user(buf, &(dev->value), 1)) 31 return -EFAULT; 32 return 1; 33 } 34 ssize_t light_write(struct file *filp, const char __user *buf, size_t count, 35 loff_t *f_pos) 36 { 37 struct light_dev *dev = filp->private_data; 38 if (copy_from_user(&(dev->value), buf, 1)) 39 return -EFAULT; 40 /*根據(jù)寫(xiě)入的值點(diǎn)亮和熄滅LED*/ 41 if (dev->value == 1) 42 light_on(); 43 else 44 light_off(); 45 return 1; 46 } 47 /* ioctl函數(shù) */ 48 int light_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, 49 unsigned long arg) 50 { 51 struct light_dev *dev = filp->private_data; 52 switch (cmd) { 53 case LIGHT_ON: 54 dev->value = 1; 55 light_on(); 56 break; 57 case LIGHT_OFF: 58 dev->value = 0; 59 light_off(); 60 break; 61 default: 62 /* 不能支持的命令 */ 63 return -ENOTTY; 64 } 65 return 0; 66 } 67 struct file_operations light_fops = { 68 .owner = THIS_MODULE, 69 .read = light_read, 70 .write = light_write, 71 .ioctl = light_ioctl, 72 .open = light_open, 73 .release = light_release, 74 }; 75 /*設(shè)置字符設(shè)備cdev結(jié)構(gòu)體*/ 76 static void light_setup_cdev(struct light_dev *dev, int index) 77 { 78 int err, devno = MKDEV(light_major, index); 79 cdev_init(&dev->cdev, &light_fops); 80 dev->cdev.owner = THIS_MODULE; 81 dev->cdev.ops = &light_fops; 82 err = cdev_add(&dev->cdev, devno, 1); 83 if (err) 84 printk(KERN_NOTICE "Error %d adding LED%d", err, index); 85 } 86 /*模塊加載函數(shù)*/ 87 int light_init(void) 88 { 89 int result; 90 dev_t dev = MKDEV(light_major, 0); 91 /* 申請(qǐng)字符設(shè)備號(hào)*/ 92 if (light_major) 93 result = register_chrdev_region(dev, 1, "LED"); 94 else { 95 result = alloc_chrdev_region(&dev, 0, 1, "LED"); 96 light_major = MAJOR(dev); 97 } 98 if (result < 0) 99 return result; 100 /* 分配設(shè)備結(jié)構(gòu)體的內(nèi)存 */ 101 light_devp = kmalloc(sizeof(struct light_dev), GFP_KERNEL); 102 if (!light_devp) { 103 result = -ENOMEM; 104 goto fail_malloc; 105 } 106 memset(light_devp, 0, sizeof(struct light_dev)); 107 light_setup_cdev(light_devp, 0); 108 light_gpio_init(); 109 return 0; 110 fail_malloc: 111 unregister_chrdev_region(dev, light_devp); 112 return result; 113 } 114 /*模塊卸載函數(shù)*/ 115 void light_cleanup(void) 116 { 117 cdev_del(&light_devp->cdev); /*刪除字符設(shè)備結(jié)構(gòu)體*/ 118 kfree(light_devp); /*釋放在light_init中分配的內(nèi)存*/ 119 unregister_chrdev_region(MKDEV(light_major, 0), 1); /*刪除字符設(shè)備*/ 120 } 121 module_init(light_init); 122 module_exit(light_cleanup);
上述代碼的行數(shù)與代碼清單1.3已經(jīng)不能比擬,除了代碼清單1.3中的硬件操作函數(shù)仍然需要外,代碼清單1.4中還包含了大量對(duì)我們暫時(shí)陌生的元素,如結(jié)構(gòu)體file_operations、cdev,Linux內(nèi)核模塊聲明用的MODULE_AUTHOR、MODULE_LICENSE、module_init、module_exit,以及用于字符設(shè)備注冊(cè)、分配和注銷(xiāo)用的函數(shù)register_chrdev_region()、alloc_chrdev_region()、unregister_chrdev_region()等。我們也不能理解為什么驅(qū)動(dòng)中要包含light_init ()、light_cleanup ()、light_read()、light_write()等函數(shù)。
此時(shí),我們只需要有一個(gè)感性認(rèn)識(shí),那就是,上述暫時(shí)陌生的元素都是Linux內(nèi)核給字符設(shè)備定義的為實(shí)現(xiàn)驅(qū)動(dòng)與內(nèi)核接口而定義的。Linux 對(duì)各類設(shè)備的驅(qū)動(dòng)都定義了類似的數(shù)據(jù)結(jié)構(gòu)和函數(shù)。
1.7 全書(shū)結(jié)構(gòu)
本書(shū)第1篇給您打下Linux設(shè)備驅(qū)動(dòng)的基礎(chǔ)。第1章簡(jiǎn)要地介紹了設(shè)備驅(qū)動(dòng)的作用,并從無(wú)操作系統(tǒng)的設(shè)備驅(qū)動(dòng)引出了Linux操作系統(tǒng)下的設(shè)備驅(qū)動(dòng),介紹了本書(shū)所基于的開(kāi)發(fā)環(huán)境。第2章系統(tǒng)地講解了一個(gè)Linux驅(qū)動(dòng)工程師應(yīng)該掌握的硬件知識(shí),為工程師打下Linux驅(qū)動(dòng)編程的硬件基礎(chǔ),講解了各種類型的CPU、存儲(chǔ)器和常見(jiàn)的外設(shè),并闡述了硬件時(shí)序分析方法和數(shù)據(jù)手冊(cè)閱讀方法。第3章將Linux設(shè)備驅(qū)動(dòng)放在Linux2.6內(nèi)核背景中進(jìn)行講解,說(shuō)明Linux內(nèi)核的編程方法。由于驅(qū)動(dòng)編程也在內(nèi)核編程的范疇,因此,這一章實(shí)質(zhì)是為編寫(xiě)Linux設(shè)備驅(qū)動(dòng)打下軟件基礎(chǔ)。
第2篇講解Linux設(shè)備驅(qū)動(dòng)編程的基礎(chǔ)理論、字符設(shè)備驅(qū)動(dòng)及設(shè)備驅(qū)動(dòng)設(shè)計(jì)中涉及的并發(fā)控制、同步等問(wèn)題。第4、5章分別講解Linux內(nèi)核模塊和Linux設(shè)備文件系統(tǒng),第6~9章以虛擬設(shè)備globalmem和globalfifo為主線,逐步給其添加高級(jí)控制功能,第10、11章分別闡述Linux驅(qū)動(dòng)編程中所涉及的中斷和定時(shí)器、內(nèi)核和I/O操作處理方法,本篇的第12章講解了Linux設(shè)備驅(qū)動(dòng)工程化的一些問(wèn)題,屬于承前啟后的一章。
第3篇剖析復(fù)雜設(shè)備驅(qū)動(dòng)的體系架構(gòu),每一章都給出了具體的實(shí)例。所涉及的設(shè)備包括塊設(shè)備、終端設(shè)備、I2C適配器與I2C設(shè)備、網(wǎng)絡(luò)設(shè)備、PCI設(shè)備、USB設(shè)備、LCD設(shè)備、Flash設(shè)備等。這一部分的講解方法是抽象與具體相結(jié)合,先以模板的形式給出各種設(shè)備驅(qū)動(dòng)的設(shè)計(jì),然后用具體實(shí)例設(shè)備的驅(qū)動(dòng)填充對(duì)應(yīng)的模板。
第4篇分析了Linux設(shè)備驅(qū)動(dòng)的調(diào)試和移植方法。由于在Linux設(shè)備驅(qū)動(dòng)的設(shè)計(jì)工作中人們強(qiáng)調(diào)多快好省,因此,如果能方便地把現(xiàn)有的其他平臺(tái)中的驅(qū)動(dòng)移植到Linux2.6平臺(tái),或者將類似設(shè)備的驅(qū)動(dòng)進(jìn)行簡(jiǎn)單修改就運(yùn)用于新的設(shè)備,那將會(huì)極大地縮短工程的實(shí)施時(shí)間。本書(shū)的最后幾章對(duì)Linux設(shè)備驅(qū)動(dòng)移植中涉及的理論以及移植的技巧進(jìn)行了講解。
- 嵌入式Linux開(kāi)發(fā)技術(shù)
- Kubernetes網(wǎng)絡(luò)權(quán)威指南:基礎(chǔ)、原理與實(shí)踐
- Ganglia系統(tǒng)監(jiān)控
- macOS效率手冊(cè)
- RESS Essentials
- Linux運(yùn)維最佳實(shí)踐
- Ceph分布式存儲(chǔ)實(shí)戰(zhàn)
- Linux命令行大全(第2版)
- INSTANT Galleria Howto
- 嵌入式微系統(tǒng)
- Web Penetration Testing with Kali Linux(Third Edition)
- 再也不踩坑的kubernetes實(shí)戰(zhàn)指南
- Mastering Eclipse Plug-in Development
- Microsoft DirectAccess Best Practices and Troubleshooting
- 微信小程序項(xiàng)目開(kāi)發(fā)實(shí)戰(zhàn):用WePY、mpvue、Taro打造高效的小程序