嵌入式linux系統(tǒng)設(shè)計(jì)與應(yīng)用 課件 第8、9章 嵌入式Linux驅(qū)動(dòng)程序、嵌入式Linux高級(jí)編程_第1頁(yè)
嵌入式linux系統(tǒng)設(shè)計(jì)與應(yīng)用 課件 第8、9章 嵌入式Linux驅(qū)動(dòng)程序、嵌入式Linux高級(jí)編程_第2頁(yè)
嵌入式linux系統(tǒng)設(shè)計(jì)與應(yīng)用 課件 第8、9章 嵌入式Linux驅(qū)動(dòng)程序、嵌入式Linux高級(jí)編程_第3頁(yè)
嵌入式linux系統(tǒng)設(shè)計(jì)與應(yīng)用 課件 第8、9章 嵌入式Linux驅(qū)動(dòng)程序、嵌入式Linux高級(jí)編程_第4頁(yè)
嵌入式linux系統(tǒng)設(shè)計(jì)與應(yīng)用 課件 第8、9章 嵌入式Linux驅(qū)動(dòng)程序、嵌入式Linux高級(jí)編程_第5頁(yè)
已閱讀5頁(yè),還剩166頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第八章嵌入式Linux驅(qū)動(dòng)程序第八章嵌入式Linux驅(qū)動(dòng)程序(1)設(shè)備驅(qū)動(dòng)開(kāi)發(fā)概述驅(qū)動(dòng)程序處理過(guò)程/設(shè)備驅(qū)動(dòng)程序框架

(2)內(nèi)核設(shè)備模型功能/Sysfs和實(shí)現(xiàn)機(jī)制/Platform總線/設(shè)備樹(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ī)工程師的最愛(ài))2.傳統(tǒng)file_operatiopns設(shè)計(jì)方法(盛久不衰,沿用至今)3.platform總線型設(shè)計(jì)方法(2.6版本主流)4.設(shè)備樹(shù)(內(nèi)核3.x版本后流行)

設(shè)備驅(qū)動(dòng)程序開(kāi)發(fā)概述PartOne8.1Linux設(shè)備驅(qū)動(dòng)程序概述

作為L(zhǎng)inux內(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)檢測(cè)錯(cuò)誤和處理中斷。Linux設(shè)備驅(qū)動(dòng)程序可以分為兩個(gè)主要組成部分:(1)對(duì)子程序進(jìn)行自動(dòng)配置和初始化,檢測(cè)驅(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)的演變,在此過(guò)程中還可以調(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ù),常見(jiàn)的字符設(shè)備有鍵盤、串口、控制臺(tái)等。通過(guò)文件系統(tǒng)節(jié)點(diǎn)可以訪問(wèn)字符設(shè)備,例如/dev/tty1和/dev/lp1。2.塊設(shè)備所謂塊設(shè)備是指對(duì)其信息的存取以“塊”為單位。塊設(shè)備和字符設(shè)備一樣可以通過(guò)文件系統(tǒng)節(jié)點(diǎn)來(lái)訪問(wèn)。在大多數(shù)linux系統(tǒng)中,只能將塊設(shè)備看作多個(gè)塊進(jìn)行訪問(wèn),一個(gè)塊設(shè)備通常是1024B數(shù)據(jù)。3.網(wǎng)絡(luò)設(shè)備網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)通常是通過(guò)套接字(Socket)等接口來(lái)實(shí)現(xiàn)操作。任何網(wǎng)絡(luò)事務(wù)處理都可以通過(guò)接口來(lái)完成和其他宿主機(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ù),常見(jiàn)的字符設(shè)備有鍵盤、串口、控制臺(tái)等。通過(guò)文件系統(tǒng)節(jié)點(diǎn)可以訪問(wè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ì)其信息的存取以“塊”為單位。如常見(jiàn)的光盤、硬磁盤、軟磁盤、磁帶等,塊長(zhǎng)大小通常取512B、1024B或4096B等。塊設(shè)備和字符設(shè)備一樣可以通過(guò)文件系統(tǒng)節(jié)點(diǎn)來(lái)訪問(wèn)。在大多數(shù)linux系統(tǒng)中,只能將塊設(shè)備看作多個(gè)塊進(jìn)行訪問(wèn),一個(gè)塊設(shè)備通常是1024B數(shù)據(jù)。塊設(shè)備的特點(diǎn)是對(duì)設(shè)備的讀寫是以塊為單位的,并且對(duì)設(shè)備的訪問(wèn)是隨機(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)過(guò)系統(tǒng)的緩沖區(qū)管理間接地傳遞給驅(qū)動(dòng)程序處理。

3.網(wǎng)絡(luò)設(shè)備網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)通常是通過(guò)套接字(Socket)等接口來(lái)實(shí)現(xiàn)操作。任何網(wǎng)絡(luò)事務(wù)處理都可以通過(guò)接口來(lái)完成和其他宿主機(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通過(guò)專門的I/O指令來(lái)訪問(wèn)這一空間的地址單元;第二種是內(nèi)存映射方式,RSIC指令系統(tǒng)的CPU(如ARM、PowerPC等)通常只實(shí)現(xiàn)一個(gè)物理地址空間,外設(shè)I/O端口成為了內(nèi)存的一部分,此時(shí)CPU訪問(wèn)I/O端口就像訪問(wèn)一個(gè)內(nèi)存單元,不需要單獨(dú)的I/O指令。這兩種方式在硬件實(shí)現(xiàn)上的差異對(duì)軟件來(lái)說(shuō)是完全可見(jiàn)的。2.并發(fā)控制

在驅(qū)動(dòng)程序中經(jīng)常會(huì)出現(xiàn)多個(gè)進(jìn)程同時(shí)訪問(wèn)相同的資源時(shí)可能會(huì)出現(xiàn)競(jìng)態(tài)(racecondition),即競(jìng)爭(zhēng)資源狀態(tài),因此必須對(duì)共享資料進(jìn)行并發(fā)控制。Linux內(nèi)核中解決并發(fā)控制最常用的方法是自旋鎖(spinlocks)和信號(hào)量(semaphores)。(1)自旋鎖自旋鎖是一個(gè)互斥現(xiàn)象的設(shè)備,它只能是兩個(gè)值:locked(鎖定)或unlocked(解鎖)。它通常作為一個(gè)整型值的單位來(lái)實(shí)現(xiàn)。在任何時(shí)刻,自旋鎖只能有一個(gè)保持者,也就是說(shuō)在同一時(shí)刻只能有一個(gè)進(jìn)程獲得鎖。(2)信號(hào)量信號(hào)量是一個(gè)結(jié)合一對(duì)函數(shù)的整型值,這對(duì)函數(shù)通常稱為P操作和V操作。自旋鎖和信號(hào)量有很多相似之處但又有些本質(zhì)的不同。其相同之處主要有:首先它們對(duì)互斥來(lái)說(shuō)都是非常有用的工具;其次在任何時(shí)刻最多只能有一個(gè)線程獲得自旋鎖或信號(hào)量。不同之處主要有:首先自旋鎖可在不能睡眠的代碼中使用,如在中斷服務(wù)程序(ISR)中使用,而信號(hào)量不可以;其次自旋鎖和信號(hào)量的實(shí)現(xiàn)機(jī)制不一樣;最后通常自旋鎖被用在多處理器系統(tǒng)??傮w而言,自旋鎖通常適合保持時(shí)間非常短的情況,它可以在任何上下文中使用,而信號(hào)量用于保持時(shí)間較長(zhǎng)的情況,只能在進(jìn)程上下文中使用。3.阻塞與非阻塞在驅(qū)動(dòng)程序的處理過(guò)程中我們提到了阻塞的概念,這里進(jìn)行以下說(shuō)明。阻塞(blocking)和非阻塞(nonblocking)是設(shè)備訪問(wèn)的兩種不同模式,前者在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)沒(méi)有//空閑的工作者了,那么喚醒一個(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è)宏來(lái)操作主從設(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ò)關(guān)鍵數(shù)據(jù)結(jié)構(gòu)file_operations來(lái)完成的。(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è)備的打開(kāi)與釋放:引用計(jì)數(shù)(3)設(shè)備的讀寫操作(4)設(shè)備的控制操作(5)設(shè)備的輪詢和中斷處理(2)設(shè)備的打開(kāi)與釋放打開(kāi)設(shè)備是由調(diào)用定義在incliude/linux/fs.h中的file_operations結(jié)構(gòu)體中的open()函數(shù)完成的。open()函數(shù)主要完成的主要工作:增加設(shè)備的使用計(jì)數(shù)。檢測(cè)設(shè)備是否異常,及時(shí)發(fā)現(xiàn)設(shè)備相關(guān)錯(cuò)誤,防止設(shè)備有未知硬件問(wèn)題。若是首次打開(kāi),首先完成設(shè)備初始化。讀取設(shè)備次設(shè)備號(hào)。其函數(shù)原型如下:int(*open)(structinode*,structfile*);驅(qū)動(dòng)程序的加載

通常linux驅(qū)動(dòng)程序可通過(guò)兩種方式進(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為例說(shuō)明驅(qū)動(dòng)程序的加載過(guò)程

內(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è)備模型帶來(lái)的好處十分之多。首先linux設(shè)備模型是一個(gè)具有清晰結(jié)構(gòu)的組織所有設(shè)備和驅(qū)動(dòng)的樹(shù)狀結(jié)構(gòu),用戶就可以通過(guò)這棵樹(shù)去遍歷所有的設(shè)備,建立設(shè)備和驅(qū)動(dòng)程序之間的聯(lián)系;其次,Linux驅(qū)動(dòng)模型把很多設(shè)備共有的一些操作抽象出來(lái),大大減少重復(fù)開(kāi)發(fā)的可能;再次,Linux設(shè)備模型提供了一些輔助的機(jī)制,比如引用計(jì)數(shù),讓開(kāi)發(fā)者可以安全高效的開(kāi)發(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è)從用戶空間去訪問(wèn)內(nèi)核設(shè)備的方法,它在Linux里的路徑是/sys。這個(gè)目錄并不是存儲(chǔ)在硬盤上的真實(shí)的文件系統(tǒng),只有在系統(tǒng)啟動(dòng)之后才會(huì)建起來(lái)。可以使用tree/sys這個(gè)命令顯示sysfs的結(jié)構(gòu)。由于信息量較大,這里只列出第一層目錄結(jié)構(gòu):/sys|--block|--bus|--class|--dev|--devices|--firmware|--fs|--kernel|--module`--power如果只是單純要訪問(wèn)設(shè)備,一般很少會(huì)直接操作sysfs,因?yàn)閟ysfs非常繁瑣和底層化現(xiàn)象嚴(yán)重,大部分情況下可以使用更加方便的DeviceKit或者libudev。Block目錄從塊設(shè)備的角度來(lái)組織設(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è)角度來(lái)組織設(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è)備的大本營(yíng),系統(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)系,通過(guò)這個(gè)目錄可以找到設(shè)備。Power目錄存放的是系統(tǒng)電源管理的數(shù)據(jù),用戶可以通過(guò)它來(lái)查詢目前的電源狀態(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),通過(guò)這個(gè)數(shù)據(jù)結(jié)構(gòu),可以使所有設(shè)備在底層都具有一個(gè)公共接口,便于設(shè)備或驅(qū)動(dòng)程序的管理和組織。從面向?qū)ο蟮慕嵌葋?lái)說(shuō),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ǎn)單,其結(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通常通過(guò)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)核開(kāi)始引入的一種虛擬總線,主要用來(lá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í)通過(guò)標(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)匹配的過(guò)程,總線起到了溝通設(shè)備和驅(qū)動(dòng)的橋梁作用。設(shè)備樹(shù)設(shè)備樹(shù)的主要作用:一是平臺(tái)標(biāo)識(shí),所謂平臺(tái)標(biāo)識(shí)就是板級(jí)識(shí)別,讓內(nèi)核知道當(dāng)前使用的是哪個(gè)開(kāi)發(fā)板,這里識(shí)別的方式是根據(jù)root節(jié)點(diǎn)下的compatible字段來(lái)匹配。二是運(yùn)行時(shí)配置,比如在內(nèi)核啟動(dòng)的時(shí)候ramdisk的配置,比如bootargs的配置,ramdisk的起始和結(jié)束地址。三是設(shè)備信息集合,這也是最重要的信息,集合了各種設(shè)備控制器設(shè)備樹(shù)設(shè)備樹(shù)用樹(shù)狀結(jié)構(gòu)描述設(shè)備信息,它有以下特性:每個(gè)設(shè)備樹(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ì))來(lái)描述。每個(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è)抽象的打開(kāi)的文件,file_operations結(jié)構(gòu)體就是file結(jié)構(gòu)的一個(gè)成員。Inode結(jié)構(gòu)表示一個(gè)文件,而file結(jié)構(gòu)表示一個(gè)打開(kāi)的文件。這正是二者間最重要的關(guān)系。每個(gè)進(jìn)程為每個(gè)打開(kāi)的文件分配一個(gè)文件描述符,每個(gè)文件描述符對(duì)應(yīng)一個(gè)file結(jié)構(gòu),同一個(gè)文件被不同的進(jìn)程打開(kāi)后,在不同的進(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打開(kāi)文件時(shí),傳入的flags、mode等參數(shù)會(huì)被記錄在內(nèi)核中對(duì)應(yīng)的structfile結(jié)構(gòu)體里(f_flags、f_mode)打開(kāi)字符設(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)程序的初始化流程一般可以用如下的過(guò)程來(lái)表示:(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)成功后,通過(guò)調(diào)用MAJOR()函數(shù)獲取主設(shè)備號(hào)。(4)初始化cdev的結(jié)構(gòu)體,可以通過(guò)調(diào)用cdev_init()函數(shù)實(shí)現(xiàn)。(5)通過(guò)調(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ù)來(lái)注冊(cè)一組字符設(shè)備編號(hào),這三個(gè)函數(shù)分別是:alloc_chrdev_region()、register_chrdev_region()和register_chrdev()。其中register_chrdev()在上節(jié)已經(jīng)介紹過(guò)。這里首先介紹的是alloc_chrdev_region()函數(shù),該函數(shù)用于動(dòng)態(tài)申請(qǐng)?jiān)O(shè)備號(hào)范圍,通過(guò)指針參數(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ù),不過(guò)新內(nèi)核對(duì)其還是兼容的。Register_chrdev()兼容了動(dòng)態(tài)和靜態(tài)兩種分配方式。Register_chrdev()不僅分配了設(shè)備號(hào),同時(shí)也注冊(cè)了設(shè)備。這是register_chrdev()與前兩個(gè)函數(shù)的最大區(qū)別。也就是說(shuō),如果使用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)體初始化了,才可以說(shuō)設(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ù)打開(kāi)hello_drv這個(gè)設(shè)備,打開(kāi)以后可以使用write函數(shù)向hello_drv的寫緩沖區(qū)writebuf中寫入數(shù)據(jù)(不超過(guò)100個(gè)字節(jié)),也可以使用read函數(shù)讀取讀緩沖區(qū)readbuf中的數(shù)據(jù)操作,操作完成以后應(yīng)用程序使用close函數(shù)關(guān)閉chrdevbase設(shè)備。Makefile測(cè)試程序編寫驅(qū)動(dòng)編寫好以后是需要測(cè)試的,一般編寫一個(gè)簡(jiǎn)單的測(cè)試程序,測(cè)試程序運(yùn)行在用戶空間。測(cè)試程序很簡(jiǎn)單通過(guò)輸入相應(yīng)的指令來(lái)對(duì)hello_drv設(shè)備執(zhí)行讀或者寫操作。I2C總線驅(qū)動(dòng)圖8-7Linux內(nèi)核I2C總線驅(qū)動(dòng)框架Linux內(nèi)核提供了一個(gè)通用的方法來(lái)實(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)一的接口,通過(guò)這些接口來(lái)訪問(wèn)在特定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及其私有部分,并通過(guò)I2Ccore提供的接口實(shí)現(xiàn)設(shè)備的注冊(cè),提供設(shè)備可使用的地址范圍及地址檢測(cè)成功后的回調(diào)函數(shù)。I2C總線驅(qū)動(dòng)處于控制中心的I2Ccore實(shí)現(xiàn)了控制策略,具體I2C總線的適配器和設(shè)備的驅(qū)動(dòng)實(shí)現(xiàn)了具體設(shè)備可用的機(jī)制,控制策略和底層機(jī)制通過(guò)中間的函數(shù)接口相聯(lián)系。正是中間的函數(shù)接口使得控制策略與底層機(jī)制無(wú)關(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用來(lái)進(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);(3)變更操作unsignedchar*skb_put(structsk_buff*skb,unsignedintlen);unsignedchar*skb_push(structsk_buff*skb,unsignedintlen);structsk_buff{ structsk_buff *next; structsk_buff *prev; ktime_t tstamp; structsock *sk; structnet_device *dev;//處理該數(shù)據(jù)包的設(shè)備 char cb[48]__aligned(8); unsignedlong _skb_refdst; unsignedint len, data_len; __u16 mac_len, hdr_len;……net_device數(shù)據(jù)結(jié)構(gòu)structnet_device{ char name[IFNAMSIZ]; structpm_qos_request_listpm_qos_req; structhlist_node name_hlist;/*設(shè)備名稱哈希鏈表* char *ifalias; unsignedlong mem_end;/*共享內(nèi)存終止 */ unsignedlong mem_start;/*共享內(nèi)存起始 */ unsignedlong base_addr;/*設(shè)備IO地址 */ unsignedint irq; /*設(shè)備IRQ編號(hào) */…}網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序的設(shè)計(jì)需要完成網(wǎng)絡(luò)設(shè)備的注冊(cè)、初始化與注銷,以及進(jìn)行發(fā)送和接收數(shù)據(jù)處理,并能針對(duì)傳送超時(shí)、中斷等情況進(jìn)行及時(shí)處理。在linux內(nèi)核中提供了設(shè)備驅(qū)動(dòng)功能層主要的數(shù)據(jù)結(jié)構(gòu)和函數(shù)的設(shè)計(jì)模板。普通開(kāi)發(fā)者只需要根據(jù)實(shí)際硬件情況完成“填空”步驟即可完成相關(guān)工作。網(wǎng)卡DM9000驅(qū)動(dòng)程序

staticstructplatform_driverdm9000_driver={ .driver ={.name="dm9000", .owner =THIS_MODULE, .pm =&dm9000_drv_pm_ops,

}, .probe=dm9000_probe, .remove=__devexit_p(dm9000_drv_remove),};staticint__initdm9000_init(void){ printk(KERN_INFO"%sEthernetDriver,V%s\n",CARDNAME,DRV_VERSION); returnplatform_driver_register(&dm9000_driver);}staticvoid__exitdm9000_cleanup(void){ platform_driver_unregister(&dm9000_driver);}module_init(dm9000_init);本章小結(jié)本課介紹了Linux的設(shè)備驅(qū)動(dòng)程序的基本概念,設(shè)計(jì)框架,內(nèi)核設(shè)備模型和字符設(shè)備、網(wǎng)絡(luò)設(shè)備的驅(qū)動(dòng)程序設(shè)計(jì)方法。隨著外圍設(shè)備的日益發(fā)展壯大,驅(qū)動(dòng)程序的設(shè)計(jì)需求也是不斷增多。據(jù)相關(guān)報(bào)道,微軟公司開(kāi)發(fā)研究部門中約有一半數(shù)量的程序員在進(jìn)行設(shè)備驅(qū)動(dòng)程序的設(shè)計(jì)工作。而在嵌入式領(lǐng)域,由于外圍設(shè)備種類眾多,接口不統(tǒng)一,性能要求差異大,這對(duì)設(shè)計(jì)工作也提出了很高的要求。應(yīng)該從實(shí)踐出發(fā),認(rèn)真閱讀Linux內(nèi)核相關(guān)設(shè)備驅(qū)動(dòng)源碼,在總結(jié)分析的基礎(chǔ)上進(jìn)行驅(qū)動(dòng)程序的研究和開(kāi)發(fā)工作。嵌入式Linux系統(tǒng)原理與應(yīng)用第九章嵌入式Linux高級(jí)編程目錄9.1嵌入式Linux下的Socket編程9.2Linux多線程應(yīng)用程序設(shè)計(jì)9.3一個(gè)簡(jiǎn)單的Linux驅(qū)動(dòng)程序9.4通過(guò)YoctoProject構(gòu)建Linux9.5嵌入式人工智能TensorFlowLite9.6基于“ARM-Linux”的嵌入式WEB服務(wù)器設(shè)計(jì)9.7本章小結(jié)

嵌入式Linux下的Socket編程PartOne9.1何為Socket,其功用是什么?嵌入式Linux系統(tǒng)通常通過(guò)提供套接字(socket)來(lái)進(jìn)行網(wǎng)絡(luò)編程,Socket有一個(gè)類似于打開(kāi)文件的函數(shù)socket(),連接建立、數(shù)據(jù)傳輸?shù)炔僮鞫际峭ㄟ^(guò)該socket()函數(shù)實(shí)現(xiàn)。9.1.1Socket函數(shù)簡(jiǎn)介1.基本socket函數(shù)(1)socket函數(shù)原型:intsocket(intdomain,inttype,intprotocol);功能說(shuō)明:調(diào)用成功,返回socket文件描述符;調(diào)用失敗,返回-1,并設(shè)置errno。參數(shù)說(shuō)明:domain指明所使用的協(xié)議族,type參數(shù)指定socket的類型,protocol通常賦值"0"。(2)bind函數(shù)原型:intbind(intsock_fd,structsockaddr_in*my_addr,intaddrlen);功能說(shuō)明:將套接字和指定的端口相連。參數(shù)說(shuō)明:sock_fd是調(diào)用socket函數(shù)返回值,my_addr是一個(gè)指向包含有本機(jī)IP地址及端口號(hào)等信息的sockaddr類型的指針,addrlen為sockaddr的長(zhǎng)度。(3)connect函數(shù)原型:intconnect(intsock_fd,structsockaddr*serv_addr,intaddrlen);功能說(shuō)明:客戶端發(fā)送服務(wù)請(qǐng)求。參數(shù)說(shuō)明:sock_fd是socket函數(shù)返回的socket描述符;serv_addr是包含遠(yuǎn)端主機(jī)IP地址和端口號(hào)的指針;addrlen是結(jié)構(gòu)sockaddr_in的長(zhǎng)度。(4)listen函數(shù)原型:intlisten(intsock_fd,intbacklog);功能說(shuō)明:等待指定的端口的出現(xiàn)客戶端連接參數(shù)說(shuō)明:sock_fd是socket()函數(shù)返回值;backlog指定在請(qǐng)求隊(duì)列中允許的最大請(qǐng)求數(shù)。(5)accecpt函數(shù)原型:intaccept(intsock_fd,structsockadd_in*addr,intaddrlen);功能說(shuō)明:用于接受客戶端的服務(wù)請(qǐng)求。調(diào)用成功返回新的套接字描述符,失敗返回-1參數(shù)說(shuō)明:sock_fd是被監(jiān)聽(tīng)的socket描述符,addr通常是一個(gè)指向sockaddr_in變量的指針,addrlen是結(jié)構(gòu)sockaddr_in的長(zhǎng)度。(6)write函數(shù)原型:ssize_twrite(intfd,constvoid*buf,size_tnbytes);功能說(shuō)明:write函數(shù)將buf中的nbytes字節(jié)內(nèi)容寫入文件描述符fd。調(diào)用成功時(shí)返回寫的字節(jié)數(shù)。(7)read函數(shù)原型:

ssize_tread(intfd,void*buf,size_tnbyte);函數(shù)說(shuō)明:read函數(shù)負(fù)責(zé)從fd中讀取內(nèi)容。當(dāng)讀成功時(shí),read返回實(shí)際所讀的字節(jié)數(shù)(8)close函數(shù)原型:intclose(sock_fd);函數(shù)說(shuō)明:釋放該socket,從而停止在該socket上的任何數(shù)據(jù)操作。2.socket編程的其他函數(shù)(1)網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換函數(shù)功能說(shuō)明:將每一臺(tái)機(jī)器內(nèi)部對(duì)變量的字節(jié)不同存儲(chǔ)順序,轉(zhuǎn)換為網(wǎng)絡(luò)傳輸?shù)慕y(tǒng)一順序。函數(shù)原型定義在netinet/in.h里*unsignedshortinthtons(unsignedshortinthostshort);該函數(shù)將主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,對(duì)無(wú)符號(hào)短型進(jìn)行操作4bytes。*unsignedlonginthtonl(unsignedlonginthostlong);該函數(shù)將主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,對(duì)無(wú)符號(hào)長(zhǎng)型進(jìn)行操作8bytes。*unsignedshortintntohs(unsignedshortintnetshort);該函數(shù)將網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機(jī)字節(jié)順序,對(duì)無(wú)符號(hào)短型進(jìn)行操作4bytes。*unsignedlongintntohl(unsignedlongintnetlong);該函數(shù)將網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機(jī)字節(jié)順序,對(duì)無(wú)符號(hào)長(zhǎng)型進(jìn)行操作8bytes。(2)IP地址轉(zhuǎn)換函數(shù) 功能說(shuō)明:將形式表示為(*.*.*.*)的字符串IP地址與32位網(wǎng)絡(luò)字節(jié)順序的二進(jìn)制形式的IP地址進(jìn)行轉(zhuǎn)換常用的有三個(gè):unsignedlongintinet_addr(constchar*cp);把一個(gè)用(*.*.*.*)表示的IP地址的字符串轉(zhuǎn)換成一個(gè)無(wú)符號(hào)長(zhǎng)整型intinet_aton(constchar*cp,structin_addr*inp);該函數(shù)將字符串形式的IP地址轉(zhuǎn)換成二進(jìn)制形式的IP地址;char*inet_ntoa(structin-addrin);該函數(shù)將32位二進(jìn)制形式的IP地址轉(zhuǎn)換為(*.*.*.*)形式的IP地址,返回一個(gè)指向字符串的指針。(3)字節(jié)處理函數(shù)Linux提供了兩組函數(shù)來(lái)處理多字節(jié)數(shù)據(jù),一組以b(byte)開(kāi)頭,是和BSD系統(tǒng)兼容。另一組以mem(內(nèi)存)開(kāi)頭,是ANSIC提供的函數(shù)。以b開(kāi)頭的函數(shù):voidbzero(void*s,intn);該函數(shù)將參數(shù)s指定的內(nèi)存的前n個(gè)字節(jié)設(shè)置為0,通常它用來(lái)將套接字地址清0voidbcopy(constvoid*src,void*dest,intn);該函數(shù)從參數(shù)src指定的內(nèi)存區(qū)域拷貝指定數(shù)目的字節(jié)內(nèi)容到參數(shù)dest指定的內(nèi)存區(qū)域。intbcmp(constvoid*s1,constvoid*s2,intn);該函數(shù)比較參數(shù)s1指定的內(nèi)存區(qū)域和參數(shù)s2指定的內(nèi)存區(qū)域的前n個(gè)字節(jié)內(nèi)容,以mem開(kāi)頭的函數(shù):void*memset(void*s,intc,size_tn);該函數(shù)將參數(shù)s指定的內(nèi)存區(qū)域的前n個(gè)字節(jié)設(shè)置為參數(shù)c的內(nèi)容。void*memcpy(void*dest,constvoid*src,size_tn);該函數(shù)的功能與bcopy()相同,區(qū)別是函數(shù)bcopy()能處理參數(shù)src和參數(shù)dest所指定的區(qū)域有重疊的情況,memcpy()則不能。intmemcmp(constvoid*s1,constvoid*s2,size_tn);該函數(shù)比較參數(shù)s1和參數(shù)s2指定區(qū)域的前n個(gè)字節(jié)內(nèi)容。套接字是通信的基石,是支持TCP/IP協(xié)議的網(wǎng)絡(luò)通信的基本操作單元。包含進(jìn)行網(wǎng)絡(luò)通信必須的五種信息:連接使用的協(xié)議,本地主機(jī)的IP地址,本地進(jìn)程的協(xié)議端口,遠(yuǎn)地主機(jī)的IP地址,遠(yuǎn)地進(jìn)程的協(xié)議端口。TCP同時(shí)為多個(gè)應(yīng)用程序進(jìn)程提供并發(fā)服務(wù)如何處理?應(yīng)用層和傳輸層通過(guò)socket接口,區(qū)分來(lái)自不同應(yīng)用程序進(jìn)程或網(wǎng)絡(luò)連接的通信,實(shí)現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務(wù)。9.1.2Socket中TCP交互過(guò)程Socket中TCP交互過(guò)程使用vim編輯server.c代碼使用vim編輯client.c代碼用交叉編譯器(arm-Linux-gcc)編譯server.c和client.c。將可執(zhí)行文件server和client拷貝到本地Linux系統(tǒng)中的tftproot目錄cpserver/tftprootcpclient/tftproot9.1.3設(shè)計(jì)步驟telnet進(jìn)開(kāi)發(fā)板,下載server、client,加上可執(zhí)行權(quán)限[root@FriendlyARMwork]#tftp-g-rserver09

server100%|*******************************|8704--:--:--ETA[root@FriendlyARMwork]#tftp-g-rclient09

client100%|*******************************|8704--:--:--ETA[root@FriendlyARMwork]#lsclientserver[root@FriendlyARMwork]#chmod+x*[root@FriendlyARMwork]#ls-l

-rwxr-xr-x1rootroot8265May1003:27client-rwxr-xr-x1rootroot8226May1003:26server

在開(kāi)發(fā)板上運(yùn)行server,在PC機(jī)上運(yùn)行“gcc–oclientclient.c”,編譯出client,然后運(yùn)行clientxxxxxxxx(此處為開(kāi)發(fā)板ip),發(fā)送字符串到開(kāi)發(fā)板。PC端:[lmzl@localhostproj1]$gcc-oclientclient.c[lmzl@localhostproj1]$./client10sendmsgtoserver:hello開(kāi)發(fā)板:[root@FriendlyARMwork]#./server

======waitingforclient'srequest======recvmsgfromclient:helloTCP通信成功

Linux多線程應(yīng)用程序設(shè)計(jì)PartTwo9.2

Linux系統(tǒng)下的多線程遵循POSIX線程接口,稱為pthread,主要涉及線程相關(guān)操作和互斥鎖相關(guān)操作,需要使用頭文件pthread.h,連接時(shí)需要使用庫(kù)libpthread.a。1.標(biāo)志符pthread_t在頭文件/usr/include/bits/pthreadtypes.h中定義typedefunsignedlongintpthread_t;9.2.1線程相關(guān)操作涉及的主要函數(shù)2.pthread_create函數(shù)原型:extern

intpthread_create((pthread_t*__thread,__constpthread_attr_t*__attr,void*(*__start_routine)(void*),void*__arg));功能說(shuō)明:創(chuàng)建一個(gè)線程,當(dāng)創(chuàng)建線程成功時(shí),函數(shù)返回0,若不為0則說(shuō)明創(chuàng)建線程失敗參數(shù)說(shuō)明:第一個(gè)參數(shù)為指向線程標(biāo)志符的指針,第二個(gè)參數(shù)用來(lái)設(shè)置線程屬性,第三個(gè)參數(shù)是線程運(yùn)行函數(shù)的起始地址,最后一個(gè)參數(shù)是運(yùn)行函數(shù)的參數(shù)。3.pthread_join函數(shù)externintpthread_join((pthread_t__th,void**__thread_return));功能說(shuō)明:該函數(shù)是一個(gè)線程阻塞的函數(shù),調(diào)用該函數(shù)將一直等待到被等待的線程結(jié)束為止,當(dāng)函數(shù)返回時(shí),被等待線程的資源被收回。參數(shù)說(shuō)明:第一個(gè)參數(shù)為被等待的線程標(biāo)志符,第二個(gè)參數(shù)為一個(gè)用戶定義的指針,它可以用來(lái)存儲(chǔ)被等待線程的返回值。4.pthread_exit函數(shù)externvoidpthread_exit((void*__retval))__attribute__((__noreturn__));

功能說(shuō)明:結(jié)束線程

參數(shù)說(shuō)明:函數(shù)的返回代碼

互斥鎖用來(lái)保證多個(gè)線程互斥執(zhí)行各自的部分代碼。1.pthread_mutex_init函數(shù)

功能說(shuō)明:生成一個(gè)互斥鎖,

參數(shù)說(shuō)明:NULL參數(shù)表明使用默認(rèn)屬性

說(shuō)明:生成特定屬性的互斥鎖,須調(diào)用函數(shù)pthread_mutexattr_init(),函數(shù)pthread_mutexattr_setpshared()實(shí)現(xiàn)不同進(jìn)程中的線程同步pthread_mutexattr_settype()實(shí)現(xiàn)屬于同一進(jìn)程的不同線程的同步9.2.2互斥鎖相關(guān)涉及的主要函數(shù)2.pthread_mutex_lock和pthread_mutex_unlock函數(shù)

說(shuō)明:pthread_mutex_lock()上鎖

pthread_mutex_unlock()開(kāi)鎖在本地Linux系統(tǒng)中使用vim編輯thread.c代碼

用交叉編譯器(arm-Linux-gcc)編譯thread.c。將可執(zhí)行文件thread拷貝到本地Linux系統(tǒng)中的tftproot目錄,執(zhí)行命令cpserver/tftprootcpclient/tftproot9.2.3設(shè)計(jì)步驟telnet進(jìn)開(kāi)發(fā)板,下載thread,加上可執(zhí)行權(quán)限。[root@FriendlyARMwork]#tftp-g-rthread11thread100%|*******************************|8704--:--:--ETA[root@FriendlyARMwork]#./thread-/bin/sh:./thread:Permissiondenied[root@FriendlyARMwork]#chmod+xthread在開(kāi)發(fā)板上運(yùn)行thread。[root@FriendlyARMwork]#./threadmian:createthread.createthread1.createthread2.mian:waitthreadend.thread2:I'mthread2thread2:number=0thread1:I'mthread1thread1:number=1thread1:number=2thread2:number=3thread1:number=4thread2:number=5thread1:number=6thread1:number=7thread2:number=8thread1:number=9thread2:number=10thread1:mainwaitthread1thread1ended.thread2:mainwaitthread1thread2ended.[root@FriendlyARMwork]#

一個(gè)簡(jiǎn)單的Linux驅(qū)動(dòng)程序PartThree9.3有源蜂鳴器的驅(qū)動(dòng)程序的設(shè)計(jì)過(guò)程

蜂鳴器一般是通過(guò)GPIO(General-PurposeInput/OutputPorts,通用編程I/O端口)的控制來(lái)實(shí)現(xiàn),GPIO是CPU的引腳,可以通過(guò)它們向外輸出高低電平,或者讀入引腳的狀態(tài)。每個(gè)GPIO端口至少需要兩個(gè)寄存器,一個(gè)是用于控制的“通用I/O端口控制寄存器”,一個(gè)是存放數(shù)據(jù)的“通用I/O端口數(shù)據(jù)寄存器”??刂坪蛿?shù)據(jù)寄存器的每一位和GPIO的硬件引腳相對(duì)應(yīng),由控制寄存器設(shè)置每一個(gè)引腳的數(shù)據(jù)流向,數(shù)據(jù)寄存器設(shè)置引腳輸出的高低電平或讀取引腳上的電平。蜂鳴器一般分為無(wú)源蜂鳴器和有源蜂鳴器。無(wú)源蜂鳴器一般不內(nèi)置放大和驅(qū)動(dòng)電路,有源蜂鳴器一般都內(nèi)置放大和驅(qū)動(dòng)電路。設(shè)計(jì)過(guò)程:首先獲得蜂鳴器使用的是XpwmTOUT0引腳的信息再到CPU原理圖上找XpwmTOUT0對(duì)應(yīng)引腳信息。找到XpwmTOUT0引腳對(duì)應(yīng)的是GPH2_0。查看數(shù)據(jù)手冊(cè),查詢GPH2相關(guān)的寄存器。GPH2相關(guān)的寄存器有:GPH2CON,GPH2DAT,GPH2PUD以及GPH2DRV。通過(guò)對(duì)不同寄存器的操作,可以配置GPIO的功能。寄存器物理地址讀/寫屬性描述初始值GPH2CON0Xe020_0C40R/WGPH2CON端口配置寄存器0x00000000GPH2DAT0Xe020_0C44R/WGPH2DAT端口數(shù)據(jù)寄存器0x00GPH2PUD0Xe020_0C48R/WGPH2PUD端口上拉寄存器0x5555GPH2DRV0Xe020_0C4CR/WGPH2DRV端口驅(qū)動(dòng)能力寄存器0x0000GPH2寄存器族GPH2CON位描述初始值GPH2CON[0][3:0]0000=Input0001=Output0010=Reserved0011=KP_COL[0]0100~1110=Reserved1111=EXT_INT[16]0000GPH2CON端口配置寄存器GPH2DAT位描述初始值GPH2DAT[7:0][7:0]8位數(shù)據(jù)輸入或者輸出0x00GPH2DAT端口數(shù)據(jù)寄存器GPH2PUD位描述初始值GPH2PUD[n][2n+1:2n]N=0~700=禁止上拉/下拉01=下拉使能10=上拉使能11=Reserved0x5555GPH2PUD端口上拉寄存器GPH2DRV位描述初始值GPH2DRV[n][2n+1:2n]N=0~700=1x01=2x10=3x11=4x0x00GPH2DRV端口驅(qū)動(dòng)能力寄存器編寫驅(qū)動(dòng)程序在同一目錄下編寫Makefile:obj-m+=buzzer.oPWD:=$(Shellpwd)#PC機(jī)內(nèi)核模塊使用以下兩行進(jìn)行內(nèi)核代碼路徑指定(根據(jù)需要打開(kāi)#注釋)#KERN_VER=$(Shelluname-r)#KERN_DIR=/lib/modules/$(KERN_VER)/build#ARM內(nèi)核模塊使用以下進(jìn)行內(nèi)核代碼路徑指定(根據(jù)需要打開(kāi)#注釋)KERN_DIR=/homw/work/Linux-3.0.8modules: $(MAKE)-C$(KERN_DIR)M=$(PWD)modulesclean: rm–rf*.o*~core.depend.*.cmd*.ko*.mod.c.tmp_versions編寫驅(qū)動(dòng)測(cè)試程序測(cè)試生成buzzer.ko模塊文件編寫驅(qū)動(dòng)測(cè)試程序用arm-Linux-gcc編譯后生成test可執(zhí)行文件,加載內(nèi)核將buzzer.ko下載到開(kāi)發(fā)板,用insmod命令加載buzzer.ko模塊。[root@FriendlyARM/home]#tftp-g-rbuzzer.ko01

buzzer.ko100%|*******************************|69120--:--:--ETA[root@FriendlyARM/home]#insmodbuzzer.ko[1709.164933]initbuzzer.[root@FriendlyARM/home]#lsmod

buzzer31430-Live0xbf1e1000libertas_sdio83290-Live0xbf1db000…檢查buzzer的設(shè)備號(hào):[root@FriendlyARM/home]#cat/proc/devices|grepbuzzer

250buzzer創(chuàng)建字符設(shè)備:[root@FriendlyARM/home]#mknod/dev/buzzerc2500測(cè)試驅(qū)動(dòng):將test程序下載到開(kāi)發(fā)板:[root@FriendlyARM/home]#tftp-g-rtest02

test100%|*******************************|8192--:--:--ETA添加可執(zhí)行權(quán)限:[root@FriendlyARM/home]#chmod+xtest

運(yùn)行測(cè)試程序:[root@FriendlyARM/home]#./test

[4093.658547]openmajor=250,minor=0.Iamtestingmydevice.[4098.658817]closemajor=250,minor=0.

通過(guò)YoctoProject構(gòu)建LinuxPartFour9.4YoctoProject是一個(gè)開(kāi)源協(xié)作項(xiàng)目,幫助開(kāi)發(fā)人員為嵌入式產(chǎn)品創(chuàng)建基于Linux的定制系統(tǒng)。特點(diǎn):(1)YoctoProject適用于任何架構(gòu)。支持Intel,ARM,MIPS,AMD,PPC等硬件廠商。(2)大多數(shù)ODM和芯片供應(yīng)商提供SDK,BSP和其他支持結(jié)構(gòu),以便與YoctoProject一起使用。(3)它專為受限制的嵌入式和物聯(lián)網(wǎng)設(shè)備需求而設(shè)計(jì)。(4)YoctoProject提供全面的工具鏈功能。(5)YoctoProject遵循嚴(yán)格的發(fā)布計(jì)劃,在所有受支持的版本中包含安全補(bǔ)丁。YoctoProject,Poky和OpenEmbeddedYoctoProject一組集成工具,使嵌入式Linux成功運(yùn)行,包括自動(dòng)構(gòu)建和測(cè)試工具,BSP板級(jí)支持包和許可證合規(guī)流程,以及基于Linux的自定義嵌入式操作系統(tǒng)的組件信息。參考嵌入式發(fā)行版(Poky),用于驗(yàn)證Yocto項(xiàng)目。OpenEmbedded構(gòu)建系統(tǒng)。相互關(guān)系:Poky參考嵌入式操作系統(tǒng)實(shí)際上是一個(gè)有效的構(gòu)建示例,它將構(gòu)建一個(gè)小型嵌入式操作系統(tǒng),使用的是它內(nèi)置的構(gòu)建系統(tǒng)。9.4.1YoctoProject概述術(shù)語(yǔ)描述追加文件(appendfile)追加文件擴(kuò)展現(xiàn)有的菜譜。BitBake逐字追加該文件內(nèi)容到對(duì)應(yīng)菜譜,追加文件使用bbappend后綴BitBake作為OpenEmbedded構(gòu)建系統(tǒng)中的構(gòu)建引擎,BitBake是任務(wù)執(zhí)行器和調(diào)度器。類似于make構(gòu)建引擎板級(jí)支持包(BSP)板級(jí)支持包中的二進(jìn)制,代碼和其他支持?jǐn)?shù)據(jù)使一個(gè)特定的操作系統(tǒng)運(yùn)行在特定的目標(biāo)硬件系統(tǒng)上類(class)類提供封裝和基本繼承機(jī)制,是可以在多個(gè)菜譜中使用的元數(shù)據(jù)文件。BitBake類文件使用bbclass后綴配置文件(conf)包含變量的全局定義,用戶定義的變量和硬件配置信息的文件鏡像用于加載到設(shè)備上的Linux發(fā)行版的二進(jìn)制形式。通常包括bootloader,內(nèi)核和根文件系統(tǒng)層(layer)層是組織進(jìn)目錄和文件的元數(shù)據(jù)(配置文件,菜譜等)集合。層允許開(kāi)發(fā)者合并相關(guān)元數(shù)據(jù)以自定義構(gòu)建和擴(kuò)展,并隔離多個(gè)體系結(jié)構(gòu)構(gòu)建的信息元數(shù)據(jù)(metadata)元數(shù)據(jù)包括類,菜譜,配置文件和引用構(gòu)建指令本身的其他信息,以及用于控制構(gòu)建內(nèi)容并影響構(gòu)建方式的數(shù)據(jù)。OpenEmbeddedCore是一組重要的經(jīng)過(guò)驗(yàn)證的元數(shù)據(jù)OpenEmbeddedCore是由基礎(chǔ)菜譜,類和配置文件組成的元數(shù)據(jù)包(package)包是軟件包,其中包含可執(zhí)行的二進(jìn)制文件,庫(kù),文檔,配置信息和其他文件Yocto項(xiàng)目還使用術(shù)語(yǔ)“包”來(lái)表示用于建立各自的軟件包的菜譜和其他元數(shù)據(jù)Poky基本級(jí)功能參考發(fā)行版,Poky用于驗(yàn)證Yocto項(xiàng)目,它也是定制的良好起點(diǎn),另外Poky是oe-core之上的集成層菜譜(recipe)最常見(jiàn)的元數(shù)據(jù)形式,類似于Makefile文件。菜譜將包含用于構(gòu)建包的設(shè)置和任務(wù)(指令

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論