|
嵌入式系統(tǒng)中,操作系統(tǒng)是通過(guò)各種驅(qū)動(dòng)程序來(lái)駕馭硬件設(shè)備的。設(shè)備驅(qū)動(dòng)程序是操作系統(tǒng)內(nèi)核和硬件設(shè)備之間的接口,它為應(yīng)用程序屏蔽了硬件的細(xì)節(jié),這樣在應(yīng)用程序看來(lái),硬件設(shè)備只是一個(gè)設(shè)備文件,可以像操作普通文件一樣對(duì)硬件設(shè)備進(jìn)行操作。設(shè)備驅(qū)動(dòng)程序是內(nèi)核的一部分,完成以下功能:
“
· 驅(qū)動(dòng)程序的注冊(cè)和注銷(xiāo)。
· 設(shè)備的打開(kāi)和釋放。
· 設(shè)備的讀寫(xiě)操作。
· 設(shè)備的控制操作。
· 設(shè)備的中斷和輪詢(xún)處理。
Linux主要將設(shè)備分為三類(lèi):字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備。字符設(shè)備是指發(fā)送和接收數(shù)據(jù)以字符的形式進(jìn)行,沒(méi)有緩沖區(qū)的設(shè)備;塊設(shè)備是指發(fā)送和接收數(shù)據(jù)以整個(gè)數(shù)據(jù)緩沖區(qū)的形式進(jìn)行的設(shè)備;網(wǎng)絡(luò)設(shè)備是指網(wǎng)絡(luò)設(shè)備訪問(wèn)的BSD socket 接口。下面以字符設(shè)備為例,寫(xiě)出其驅(qū)動(dòng)編寫(xiě)框架:
一、 編寫(xiě)驅(qū)動(dòng)程序初始化函數(shù)
驅(qū)動(dòng)程序的初始化在函數(shù)xxx_init()中完成,包括對(duì)硬件初始化、中斷函數(shù)、向內(nèi)核注冊(cè)驅(qū)動(dòng)程序等。
首先理解硬件結(jié)構(gòu),搞清楚其功能,接口寄存器以及CPU怎么訪問(wèn)控制這些寄存器等。
其次向內(nèi)核注冊(cè)驅(qū)動(dòng)程序。設(shè)備驅(qū)動(dòng)程序可以直接編譯進(jìn)內(nèi)核,在系統(tǒng)啟動(dòng)的時(shí)候初始化,也可以在需要的時(shí)候以模塊的方式動(dòng)態(tài)加載到內(nèi)核中去。每個(gè)字符設(shè)備或是塊設(shè)備都是通過(guò)register_chrdev()函數(shù)注冊(cè),調(diào)用該函數(shù)后就可以向系統(tǒng)申請(qǐng)主設(shè)備號(hào),操作成功,設(shè)備名就會(huì)出現(xiàn)在/proc/devices里。
此外,在關(guān)閉設(shè)備時(shí),需要先解除原先設(shè)備的注冊(cè),需要有清除函數(shù),在xxx_exit()中通過(guò)unregister_chrdev()函數(shù)在實(shí)現(xiàn),此后設(shè)備就會(huì)從/proc/devices里消失。
當(dāng)驅(qū)動(dòng)程序被編譯成模塊時(shí),使用insmod加載模塊,模塊的初始化函數(shù)xxx_init()被調(diào)用,向內(nèi)核注冊(cè)驅(qū)動(dòng)程序;使用rmmod卸載模塊,模塊的清除函數(shù)xxx_exit()被調(diào)用。
二、 構(gòu)造file_operations結(jié)構(gòu)中要用到的各個(gè)成員函數(shù)
Linux操作系統(tǒng)將所有的設(shè)備都看成文件,以操作文件的方式訪問(wèn)設(shè)備。應(yīng)用程序不能直接操作硬件,使用統(tǒng)一的接口函數(shù)調(diào)用硬件驅(qū)動(dòng)程序,這組接口被成為系統(tǒng)調(diào)用。每個(gè)系統(tǒng)調(diào)用中都有一個(gè)與之對(duì)應(yīng)的函數(shù)(open、release、read、write、ioctl等),在字符驅(qū)動(dòng)程序中,這些函數(shù)集合在一個(gè)file_operations類(lèi)型的數(shù)據(jù)結(jié)構(gòu)中。以一個(gè)鍵盤(pán)驅(qū)動(dòng)程序?yàn)槔?br />
struct file_operations Key7279_fops =
{
.open = Key7279_Open,
.ioctl = Key7279_Ioctl,
.release = Key7279_Close,
.read = Key7279_Read,
};
1、 設(shè)備的打開(kāi)和釋放
打開(kāi)設(shè)備是由open()函數(shù)來(lái)完成,在大部分設(shè)備驅(qū)動(dòng)中open完成如下工作:
◇ 遞增計(jì)數(shù)器
◇ 檢查特定設(shè)備的特殊情況
◇ 初始化設(shè)備
◇ 識(shí)別次設(shè)備號(hào)
釋放設(shè)備由release()函數(shù)來(lái)完成。當(dāng)一個(gè)進(jìn)程釋放設(shè)備時(shí),其它進(jìn)程還能繼續(xù)使用該設(shè)備,只是該進(jìn)程暫時(shí)停止對(duì)該設(shè)備的的使用,而當(dāng)一個(gè)進(jìn)程關(guān)閉設(shè)備時(shí),其它進(jìn)程必須重新打開(kāi)此設(shè)備才能使用。Release完成如下工作:
◇ 遞減計(jì)數(shù)
◇ 在最后一次釋放設(shè)備操作時(shí)關(guān)閉設(shè)備
2、 設(shè)備的讀寫(xiě)操作
讀寫(xiě)設(shè)備的主要任務(wù)就是把內(nèi)核空間的數(shù)據(jù)復(fù)制到用戶(hù)空間,或者是從用戶(hù)空間復(fù)制到內(nèi)核空間,也就是將內(nèi)核空間緩沖區(qū)里的數(shù)據(jù)復(fù)制到用戶(hù)空間的緩沖區(qū)中或者相反。字符設(shè)備使用各自的read()函數(shù)和write()函數(shù)來(lái)進(jìn)行數(shù)據(jù)讀寫(xiě)。
3、 設(shè)備的控制操作
大部分設(shè)備除了讀寫(xiě)能力,還可進(jìn)行超出簡(jiǎn)單的數(shù)據(jù)傳輸之外的操作,所以設(shè)備驅(qū)動(dòng)也必須具備進(jìn)行各種硬件控制操作的能力. 這些操作常常通過(guò) ioctl 方法來(lái)支持。與讀寫(xiě)操作不同,ioctl()的用法與具體設(shè)備密切相關(guān)。以鍵盤(pán)Key7279_Ioctl為例:
static int Key7279_Ioctl(struct inode *inode,struct file *file,unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case Key7279_GETKEY:
return key7279_getkey();
default:
printk("Unkown Keyboard Command ID.\n");
}
return 0;
}
cmd的取值及含義都與具體的設(shè)備有關(guān),除了ioctl(),設(shè)備驅(qū)動(dòng)程序還可能有其他控制函數(shù),比如llseek()等。
當(dāng)應(yīng)用程序使用open、release等函數(shù)打開(kāi)某個(gè)設(shè)備時(shí),設(shè)備驅(qū)動(dòng)程序的file_operations結(jié)構(gòu)中的相應(yīng)成員就會(huì)被調(diào)用。
三、設(shè)備的中斷和輪詢(xún)處理
對(duì)于不支持中斷的設(shè)備,讀寫(xiě)時(shí)需要輪詢(xún)?cè)O(shè)備狀態(tài),以及是否需要繼續(xù)進(jìn)行數(shù)據(jù)傳輸。例如,打印機(jī)。如果設(shè)備支持中斷,則可按照中斷方式進(jìn)行。
模塊在使用中斷前要先請(qǐng)求一個(gè)中斷通道(或者 IRQ中斷請(qǐng)求),并在使用后釋放它。通過(guò)request_irq()函數(shù)來(lái)注冊(cè)中斷,free_irq()函數(shù)來(lái)釋放。
四、驅(qū)動(dòng)程序的測(cè)試
對(duì)驅(qū)動(dòng)程序的調(diào)試可以通過(guò)打印的方式來(lái)進(jìn)行,就是通過(guò)在驅(qū)動(dòng)程序中添加printk()打印函數(shù),來(lái)跟蹤驅(qū)動(dòng)程序的執(zhí)行過(guò)程,以此來(lái)判斷問(wèn)題。
以上是我根據(jù)自己的學(xué)習(xí)總結(jié)的,可能寫(xiě)的比較簡(jiǎn)單,對(duì)于比較復(fù)雜的驅(qū)動(dòng)函數(shù),會(huì)添加更多的函數(shù),但是大體的框架就是這樣了。
基于操作系統(tǒng)的驅(qū)動(dòng)就是在無(wú)操作系統(tǒng)下的硬件接口函數(shù)加上操作系統(tǒng)外套
實(shí)現(xiàn)一個(gè)嵌入式Linux設(shè)備驅(qū)動(dòng)程序的大致流程如下:
(l)查看原理圖,理解設(shè)備的工作原理。
(2)定義主設(shè)備號(hào)。設(shè)備由一個(gè)主設(shè)備號(hào)和一個(gè)次設(shè)備號(hào)來(lái)標(biāo)識(shí)。主設(shè)備號(hào)唯一標(biāo)識(shí)了設(shè)
備類(lèi)型,即設(shè)備驅(qū)動(dòng)程序類(lèi)型,它是塊設(shè)備表或字符設(shè)備表中設(shè)備表項(xiàng)的索引。次設(shè)備號(hào)僅
由設(shè)備驅(qū)動(dòng)程序解釋?zhuān)瑓^(qū)分被一個(gè)設(shè)備驅(qū)動(dòng)控制下的某個(gè)獨(dú)立的設(shè)備。
(3)實(shí)現(xiàn)初始化函數(shù)。在驅(qū)動(dòng)程序中實(shí)現(xiàn)驅(qū)動(dòng)的注冊(cè)和卸載。
(4)設(shè)計(jì)所要實(shí)現(xiàn)的文件操作,定義file--operations結(jié)構(gòu)。
(5)實(shí)現(xiàn)所需的文件操作調(diào)用,如read,write等。
(6)實(shí)現(xiàn)中斷服務(wù),并用request--irq向內(nèi)核注冊(cè),中斷并不是每個(gè)設(shè)備驅(qū)動(dòng)所必需的。
(7)編譯該驅(qū)動(dòng)程序到內(nèi)核中,或者用insmod命令加載模塊。
(8)測(cè)試該設(shè)備,編寫(xiě)應(yīng)用程序,對(duì)驅(qū)動(dòng)程序進(jìn)行測(cè)試。
典型字符設(shè)備驅(qū)動(dòng)編寫(xiě)框架:
1 編寫(xiě)硬件接口函數(shù)
2 建立文件系統(tǒng)與設(shè)備驅(qū)動(dòng)程序間的接口,如:struct file_operations結(jié)構(gòu)體
3 注冊(cè)設(shè)備到chrdevfs全局?jǐn)?shù)組中,注冊(cè)或注銷(xiāo)設(shè)備可以在任何時(shí)候,但一般在模塊加載時(shí)注冊(cè)設(shè)備,在模塊退出時(shí)注銷(xiāo)設(shè)備。(module_init();module_exit();)
4 以模塊方式編譯驅(qū)動(dòng)源碼,并將其加載到內(nèi)核中
5 創(chuàng)建設(shè)備節(jié)點(diǎn),mknode
6 編寫(xiě)應(yīng)用程序訪問(wèn)底層設(shè)備
想要了解嵌入式、物聯(lián)網(wǎng)相關(guān)技術(shù)的可以聯(lián)系宋工企鵝號(hào)三五二四六五九零八八Tel:173--1795--1908
|
|