




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
8.1Linux下的設備驅動程序簡介8.2設備驅動程序的開發(fā)過程8.3串口驅動程序設計8.4LCD驅動程序設計分析8.5中斷處理練習題第8章嵌入式Linux系統(tǒng)的驅動開發(fā)8.1Linux下的設備驅動程序簡介
8.1.1設備驅動程序的概念
設備驅動程序是操作系統(tǒng)內核和機器硬件之間的接口。如圖8-1所示,設備驅動程序為應用程序屏蔽了硬件的細節(jié)。在應用程序看來,硬件設備只是一個設備文件,應用程序可以像操作普通文件一樣對硬件設備進行操作。圖8-1驅動設備與虛擬文件的關系設備驅動可以理解為操作系統(tǒng)的一部分,對于一個特定的硬件設備來說,其對應的設備程序是不同的,比如說網(wǎng)卡、USB、串口、鍵盤等。設備驅動程序可以完成以下功能:
(1)對設備進行初始化和釋放。
(2)把數(shù)據(jù)從內核傳送到硬件和從硬件讀取數(shù)據(jù)。
(3)讀取應用程序傳送給設備文件的數(shù)據(jù)和回送應用程序請求的數(shù)據(jù)。
(4)檢測和處理設備出現(xiàn)的錯誤。8.1.2驅動設備的分類
Linux系統(tǒng)將設備分成三種基本的類型:字符設備、塊設備和網(wǎng)絡設備。
(1)字符設備(characterdevice)是指存取時沒有緩存,只能順序讀/寫的設備,由字符設備程序來實現(xiàn)這種特征。字符設備驅動程序通常至少實現(xiàn)open、close、read和write系統(tǒng)調用。字符設備可以通過文件系統(tǒng)節(jié)點來訪問,比如?/dev/tty1和?/dev/lp0等。這些設備文件和普通文件的差別在于對普通文件的訪問位置可以前后移動,而大多數(shù)字符設備不支持這種操作。典型的字符設備有按鍵、串行口等。
(2)塊設備(blockdevice)跟字符設備一樣是通過設備文件進行的。一般塊設備都有緩存支持,并且必須能夠支持隨機存取,而字符設備則沒有這樣的要求。通常,塊設備是文件系統(tǒng)的宿主,對于嵌入式系統(tǒng)來說,更常見于Flash、CF卡等存儲媒質中??梢赃@么說,塊設備的管理是為存儲提供優(yōu)化的;而字符設備的管理是為操作提供優(yōu)化的。
(3)網(wǎng)絡設備(net-workdevice)在Linux里做專門的處理,它是從BSDUnix的網(wǎng)絡組件移植而來的。與字符設備和塊設備不同,網(wǎng)絡沒有對應地映射到文件系統(tǒng)的設備節(jié)點。在Linux中,網(wǎng)絡設備訪問采用Socket機制實現(xiàn)。8.1.3設備文件
在Linux系統(tǒng)中,字符設備和塊設備是通過文件節(jié)點訪問的。在Linux的文件系統(tǒng)中,可以找到(或者使用mknod創(chuàng)建)設備對應的文件名,稱這種文件為設備文件。對于設備文件的輸入/輸出(I/O)操作,系統(tǒng)會調用對應的設備驅動程序。習慣上,這些設備文件存在于系統(tǒng)的/dev目錄下。因為系統(tǒng)不是靠路徑去關聯(lián)設備文件和對應的驅動程序的,所以也可通過mknod命令把設備文件創(chuàng)建在其他位置,不過很少有人這么做。通過如下命令:
Ls-l/dev可以列出系統(tǒng)的設備文件。下面列出的是X86平臺下Linux系統(tǒng)/dev目錄中的部分內容:與標準的文件相比,設備文件除了具體文件的訪問權限(包括所有者(owner)、組(group)、創(chuàng)建日期和文件名等屬性)以外,還標示了設備的類型、主設備號和次設備號。例如,上述列出的第一個設備文件名xdb60,“brw-rw----”說明此文件節(jié)點是塊設備文件(b);所有者(root)和組(disk)可對這個設備有讀取(r)和寫(w)的權限;此設備的主設備號是13,次設備號是124。有關主設備號和次設備號后面會詳細介紹。8.1.4主設備號和次設備號
Linux采用主設備號和次設備號來標識一個具體設備。主設備號用于標識設備類型,每個類型的設備需要一個對應的設備驅動程序。一個主設備號可以有多個具體設備與之對應,在驅動程序內采用次設備號來區(qū)分這些不同的具體設備。
在內核中,dev_t類型(在<linux/tyes.h>中定義)用來保存設備編號——包括主設備號和次設備號。在內核的2.6.0版本中,dev_t是一個32位的數(shù),其中的12位用來表示主設備號,而其余20位用來表示次設備號。當然,我們的代碼不應該對設備編號的組織做任何假定,而應該始終使用<linux/tyes.h>中定義的宏。要知道dev_t的主設備號或次設備號,應
使用:
MAJOR(dev_tdev)
MANOR(dev_tdev)
相反,如果需要將主設備號和次設備號轉換成dev_t類型,則使用:
MKDEV(intmajor,intminor)
一部分主設備號系統(tǒng)靜態(tài)地分配給大部分常見設備。在內核源碼樹的Documentation/devices.txt文件中可以找到這些設備的清單。將某個已經(jīng)分配好的靜態(tài)編號用于新的驅動程序的機會非常小,但是尚未被分配的新編號是可以使用的。因此,作為驅動程序開發(fā)人員,可以簡單選用一個尚未使用的編號,或者通過動態(tài)分配的方式分配主設備號。8.1.5Linux設備驅動程序結構
設備驅動程序從總體上看可以分為以下兩部分。
(1)驅動與內核接口層:實現(xiàn)驅動模塊在Linux內核的注冊加載和卸載清除工作。其主要任務是在模塊加載時向內核注冊驅動,以及實現(xiàn)虛擬文件系統(tǒng)的設備操作接口。對于采用中斷處理的設備,此部分還包括中斷處理函數(shù)的注冊與注銷。
(2)硬件設備接口層:主要描述驅動與設備的交互。它主要包括硬件探測、設備初始化、設備讀/寫訪問、設備控制操作。硬件探測主要是在驅動注冊加載時檢測設備是否存在,設備初始化主要是檢測到設備后對它進行初始化操作。設備的讀/寫訪問主要完成從設備接收數(shù)據(jù)和將數(shù)據(jù)發(fā)送給設備的操作。硬件設備接口層有時還包括一些設備的控制操作以及設備設定的工作參數(shù)等。
Linux系統(tǒng)中用戶對設備的操作采用文件接口實現(xiàn)。用戶采用標準的文件操作訪問設備,虛擬文件系統(tǒng)將用戶的這種文件訪問操作轉換為對設備的具體操作。為實現(xiàn)這種用戶文件操作到設備操作的轉換,虛擬文件系統(tǒng)向下為設備驅動提供了一個標準化的文件操作實現(xiàn)接口。這個接口由include/fs.h文件中的file_operations結構定義。該結構定義的操作大致上可分為三大類:設備的打開和釋放操作、讀/寫操作和控制操作。例如:可以看出,file_operations結構中的成員幾乎全部是函數(shù)指針,實際上虛擬文件系統(tǒng)通過這個結構定義了一個函數(shù)跳轉表,每個進程對設備的操作,都是根據(jù)設備的major、minor設備號,轉換成對file_operations結構的訪問。下面介紹結構體中幾個常用的成員。
①“structmodule*owner;”:第一個file_operations字段并不是一個操作,它是指向擁有該結構的模塊的指針。內核使用任何這個字段以避免在模塊的操作正在被使用時卸載該模塊。
②“ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);”:用來從設備中讀取數(shù)據(jù)。該函數(shù)指針被賦予NULL值時,將導致read系統(tǒng)調用出錯并返回-EINVAL。函數(shù)返回非負值表示成功讀取的字節(jié)數(shù)。③“ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);”:向設備發(fā)送數(shù)據(jù)。如果沒有這個函數(shù),write系統(tǒng)調用會向程序返回一個-EINVAL。如果方法為NULL,則表示成功寫入的字節(jié)數(shù)。
④“int(*open)(structinode*,structfile*);”:盡管這是對設備文件執(zhí)行的第一個操作,然而卻并不要求驅動程序一定要聲明一個相應的方法。如果這個入口為NULL,則設備的打開操作永遠成功,但系統(tǒng)不會通知驅動程序。
⑤“int(*release)(structinode*,structfile*);”:當filez結構被釋放時,將調用這個操作。與open相仿,也可以將release設置為NULL。
8.2設備驅動程序的開發(fā)過程
8.2.1模塊化驅動程序設計
Linux內核是一個整體的結構,因此向內核添加任何東西或者刪除某些功能,都十分困難。為了解決這個問題,引入了內核機制,從而可以動態(tài)地向內核中添加或刪除模塊。我們編寫驅動程序,就是編寫一個個的模塊。模塊可以不被編譯在內核中(也可以編譯到內核里),因而控制了內核的大小;而模塊一旦被編譯到內核中,它就和內核其他部分一樣。下面我們介紹最簡單的“helloworld”模塊,其完整代碼如下:這個模塊定義了兩個函數(shù),其中一個在模塊被裝載到內核時調用(hello_init),而另一個則在模塊被移除時調用(hello_exit)。module_init和module_exit用來聲明模塊初始化和清除函數(shù)的宏。另外一個特殊宏(MODULE_LICENSE)用來告訴內核,該模塊采用自由許可證;如果沒有這樣的聲明,內核在裝載時會產(chǎn)生告警。模塊通過insmod與rmmod進行加載和卸載。從“helloworld”模塊我們可以看到,編寫一個模塊并沒有想像的那么困難—至少當模塊不需要完成什么有價值的工作時。真正的困難在于理解設備并最大化其性能。本章將深入討論模塊化問題,而對于與設備相關的問題則在以后的章節(jié)再進行介紹。8.2.2字符設備注冊和初始化
1.字符設備的注冊新方法
前面我們提到,內核內部使用structcdev結構來表示字符設備。在內核調用設備的操作之前,必須分配并注冊一個或者多個上述結構。為此,我們的代碼應包含<linux/cdev.h>,其中定義了這個結構以及與其相關的一些輔助函數(shù)。分配和初始化上述結構的方式有兩種。如果讀者打算運行時獲取一個獨立的cdev結構,則應該編寫如下代碼:
structcdev*my_cdev=cdev_alloc();
my_cdev->ops=&my_fops;
這時,我們可以將cdev結構嵌入到自己的設備特定結構中。這種情況下,我們需要用下面的代碼來初始化已分配到的結構:
voidcdev_init(structcdev*cdev,structfile_operations*fops);另外,還有一個structcdev的字段需要初始化。和file_operations結構類似,structcdev也是一個所有者字段,應被設置為THIS_MODULE。在cdev結構中,num是該設備對應的第一個設備號,count是應該和該設備關聯(lián)的設備編號的數(shù)量。count經(jīng)常取1,但是在某些情形下,會有多個編號對應于一個特定的設備。
在使用cdev_add時,需要牢記重要的一點。首先,這個調用可能會失敗。如果它返回一個負的錯誤碼,則設備不會被添加到系統(tǒng)中。但這個調用幾乎總會成功返回,此時,我們又面臨另一個問題:只要cdev_add返回了,我們的設備就“活”了,它的操作就會被內核調用。因此,在驅動程序還沒完全準備好處理設備上的操作時,就不能調用cdev_add。要從系統(tǒng)中移除一個字符設備,應做如下調用:
voidcdev_del(structcdev*dev);
注意:在將cdev結構傳遞到cdev_del函數(shù)之后,就不應該再訪問cdev結構了。
2.字符設備的早期方法
在Linux2.6內核中還有相當數(shù)量的字符設備驅動程序不使用我們前面描述過的cdev接口,其實這是尚未升級到2.6接口的老代碼。因為這些代碼也可以工作,所以在較長的時間內升級可能不會發(fā)生。為了完整起見,我們將描述老的字符設備注冊接口,但新的代碼不應該使用這些老的接口,因為這種機制將會在將來的內核中消失。注冊一個字符設備驅動程序的經(jīng)典方式是:
intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);這里,major是設備的主設備號,name是驅動程序的名稱,而fops是默認的file_operations結構。register_chrdev的調用將為給定的主設備號注冊0~255作為次設備號,并為每個設備建立一個對應的默認cdev結構。使用這一接口的驅動程序必須能夠處理所有256個次設備號上的open調用(不論它們是否真正地對應于實際的設備),而且也不能使用大于255的主設備號和次設備號。如果使用register_chrdev函數(shù),則將自己的設備從系統(tǒng)中移除的正確函數(shù)是:
intunregister_chrdev(unsignedintmajor,constchar*name);
major和name必須與傳遞給register_chrdev函數(shù)的值保持一致,否則該調用會失敗。8.2.3中斷管理
在Unix/Linux系統(tǒng)里,對于中斷的處理屬于系統(tǒng)核心的部分,如果設備與系統(tǒng)之間以中斷方式進行數(shù)據(jù)交換,就必須把設備的驅動程序作為系統(tǒng)核心的一部分。在沒有硬件以前,中斷產(chǎn)生的效果并不是很直觀,不過我們依然可以通過proc文件系統(tǒng)或其他軟件確認中斷函數(shù)成功。設備驅動程序通過調用request_irq函數(shù),申請中斷;通過free_irq函數(shù)來釋放中斷。它們在kenel/irq/manage.c文件中定義如下:
intrequest_irq(unsignedintirq,irq_handler_thandler,unsignedlongirqflags,constchar
*devname,void*dev_id);
voidfree_irq(unsignedintirq,void*dev_id);參數(shù)irq表示所要申請的中斷號;handler為向系統(tǒng)登記的中斷處理子程序,中斷產(chǎn)生由系統(tǒng)來調用;devicename為設備名,將會出現(xiàn)在/proc/interrupts文件里;dev_id為申請時告訴系統(tǒng)的設備標識;irq_flags是申請時的選項,它決定中斷程序的一些特性,其中最重要的是中斷處理程序是快速處理程序(flag里設置了SA_INTERRUPT)還是慢速處理程序(沒有設置SA_
INTERRUPT)??焖偬幚沓绦蜻\行時,所有的中斷都被屏蔽;而慢速處理程序運行時,除了正在處理的中斷外,其他中斷都沒有屏蔽。在Linux申請中斷是在flag里設置SA_SHIRQ,這些處理程序之間以dev_id來區(qū)分。如果中斷由某個處理程序獨占,則dev_id可以為NULL。Request_irq返回0時表示成功,返回?-INVAL時表示irq>15或者handler==NULL,返回-EBUSY時表示中斷已經(jīng)被占用且不能共享。有些時候我們無法申請中斷,Linux內核通過register_irq注冊這個中斷,并把中斷信息添加到irqaction數(shù)組中,從而將中斷號和中斷服務聯(lián)系起來。irqaction的數(shù)據(jù)結構在linux/interrupt.h文件中被定義。根據(jù)設備的中斷號可以在數(shù)組irq_action中檢索到設備的中斷信息。Linux內核提供了中斷探測機制,以幫助程序員完成中斷注冊過程。動態(tài)分配中斷號可以確保每一次都能正確地安裝中斷處理程序。這一系列函數(shù)都在linux/interrupt.h文件中聲明。中斷是驅動程序中不可缺少的內容,但是使用不當會引起不可預料的后果。程序員必須對硬件運行機制非常了解,以決定何時使用中斷、何時禁用中斷。Linux內核提供了一組函數(shù)幫助我們控制中斷:
voiddisable_irq_nosync(unsignedintirq);
voiddisable_irq(unsignedintirq);
voidenable_irq(unsignedintirq);8.2.4設備驅動開發(fā)的基本函數(shù)
前面講了這么多,但還沒具體介紹某個驅動。這一節(jié)我們以S3C2410的按鍵驅動為例來介紹設備開發(fā)的基本函數(shù)。按鍵驅動比較簡單,但包含了驅動程序的幾個函數(shù):
staticint__inits3c2410_button_init(void)
//初始化硬件模塊
staticvoid__exits3c2410_button_exit(void)
//卸載硬件模塊
module_init(s3c2410_button_init);
//加載硬件模塊
module_exit(s3c2410_button_exit);
//卸載硬件模塊
驅動程序開發(fā)的最主要的也是最基本的就是上面的幾個子函數(shù),這些函數(shù)是驅動程序中所必須擁有的函數(shù)。驅動程序中初始化和卸載其實是相反的過程。
初始化硬件s3c2410_button_init(void)包括:
register_chrdev(0,DEVICE_NAME,&s3c2410_button_fops); //注冊設備的函數(shù)
其中:0為需要動態(tài)分配一個設備號,如果注冊成功,則返回設備號,這個設備號以后卸載時要用到;DEVICE_NAME為所定義的設備名,自己定義,加載模塊后就可以查到;&s3c2410_button_fops為一個應用文件表,定義了通過映射怎么操作設備等。
devfs_mk_cdev(MKDEV(s3c2410_button_major,0),S_IWUSR,DEVICE_NAME);
//加載設備到文件系統(tǒng)
卸載硬件則是一個相反的過程,s3c2410_button_exit()包括:
devfs_remove(DEVICE_NAME); //剛好相反的過程
unregister_chrdev(s3c2410_button_major,DEVICE_NAME); //卸載設備的函數(shù),主要傳遞了設備
//號和設備名接下來主要是&s3c2410_button_fops表的建立過程,其實主要是一些建立對應的關系過程:
staticstructfile_operationss3c2410_button_fops={
.owner=THIS_MODULE,
.open=s3c2410_button_open,
.release=s3c2410_button_close,
.read=s3c2410_button_read,
};在這里主要實現(xiàn)打開文件、關閉文件、讀文件幾個功能,在驅動所寫的程序中也就是這三個函數(shù):
s3c2410_button_open();
s3c2410_button_close();
s3c2410_button_read();也就是通過這個結構表的建立,可以在應用程序中通過open、release、read來調用這幾個函數(shù)。這幾個函數(shù)的內部結構如下:對于open、close函數(shù),其實系統(tǒng)已經(jīng)默認做了很多調用,也就是說,如果只是打開文件或關閉文件,里面置空就行了,系統(tǒng)會自動完成用戶所需要的工作;而read函數(shù)則是需要自己編寫的,其實現(xiàn)的一個基本功能就是內核到用戶之間參數(shù)的傳遞,也就是把通過系統(tǒng)模式所讀到的數(shù)據(jù)通過:
copy_to_user(buff,(char*)&button_ret,sizeof(unsignedchar));傳遞給用戶使用,這樣可以防止用戶直接訪問系統(tǒng)區(qū)域而導致安全隱患。當然在read里面還要有一些訪問各種端口所得到的數(shù)據(jù)等,這也就是給用戶使用的數(shù)據(jù)。
如果具備了以上幾個函數(shù),也就具有了一個驅動的大致框架結構,這樣就可以正常地加載或卸載驅動,不過具體到要驅動做些事情還是需要在各個子函數(shù)里添加一些需要用到的變量及用到的參數(shù),還要看怎樣驅動才能得到想要的結果。
8.3串口驅動程序設計
8.3.1終端設備和控制臺
在Linux中,TTY(Teletype的縮寫)也許是跟終端有關系的最為混亂的術語。終端設備是Unix操作系統(tǒng)中一個非常重要的對象,內容十分豐富和復雜,是Unix人機接口的基礎,其對于Unix的意義不亞于文件系統(tǒng)對于操作系統(tǒng)。因此,了解終端設備的工作細節(jié)有助于改善人機接口。在Linux下,常用的作為終端的輸入/輸出設備有幀緩沖顯示臺、鍵盤、網(wǎng)絡和串口。應用程序通過終端接口設備使用特定的接口規(guī)程與終端進行交互。終端設備的主設備號為4,如果使用設備文件系統(tǒng),則虛擬終端設備在/dev/vc/目錄下,1~63號子設備是表示不同的虛擬終端,0號子設備是代表當前活動的虛擬終端。終端包括了3種類型:控制臺(console)、串口(serialport)和偽終端(Pseudo-TTY,PTY)。與操作系統(tǒng)內核本身交互的終端稱為控制臺,它可以是內核本身的內部顯示終端,也可以是通過串口連接的外部亞終端。內核終端(控制臺)對用戶來說具有若干虛擬終端子設備,它們共享同一個物理終端,但對于同一硬件(如LCD顯示屏)來說,同一時刻只能有一個虛擬終端操作??刂婆_在Linux系統(tǒng)內核啟動時,可通過命令行(console=…)被指定;如果沒有指定控制臺,則系統(tǒng)將把第一個注冊的終端設備(TTY)作為控制臺。串口終端是最原始的終端類型,串口驅動程序通常都是通過TTY設備驅動注冊的。8.3.2Linux串口驅動程序分析
最常用的URAT控制器是與16c550寄存器兼容的控制器。在Linux內核源碼中,也給出了完善的16c550的串口驅動程序。這里,將結合ARMLinux和ARM2410平臺上擴展的16c550串口模塊,分析Linux串口驅動程序結構。
雖然TTY設備驅動程序最開始是為串口服務的,但它還是過于復雜,添加一個新的串口驅動程序并不容易。Linux內核中給串口驅動程序的開發(fā)提供了一個更好的接口。其主要代碼在drivers/serial/serial/serial_core.c中實現(xiàn)。
在Linux2.6內核源碼中,16c550兼容驅動程序drivers/serial/serial_lh7a40x.c。函數(shù)lh7a40xuart_init是驅動程序的入口:這是比較簡單的,它只完成了兩項工作:
(1)初始化系統(tǒng)的I/O口或者總線時序;
(2)通過uart_register_driver函數(shù)注冊串口驅動程序。
函數(shù)uart_register_driver在drivers/serial/serial/serial_core.c中被定義,其原型為:
intuart_register_driver(structuart_driver*drv)
它只有一個參數(shù),就是結構體structuart_driver。這是一個很復雜的結構體,它在include/linux/serial.h中被定義。對于Linux2.6內核,它包括:可以看到Linux2.6內核的結構體structuart_driver中已經(jīng)取消在Linux2.4中TTYS設備、CUA設備的定義。
注冊UART驅動程序時,在結構體uart_register_driver中最主要的也是最復雜的成員就是structuart_port*,它也在include/linux/serial_core.h中被定義:其中:成員iobase是16c550寄存器的基地址,對于ARM等多數(shù)統(tǒng)一編制的處理器,它是通過MMU映射以后的虛擬地址;uartclk是16c550的輸入時鐘。
ops是一套比較復雜的串口操作函數(shù),它在include/linux
/serial_core.h中被定義為:結構體structuart_ops是連接serial_core和底層硬件的最主要的一系列函數(shù)。這里,針對主要成員進行說明。
tx_empty—測試發(fā)送FIFO或移位寄存器是否為空。如果是,則返回TIOCSER_TEMT;否則,返回0。如果驅動程序不支持此操作,則可以直接返回TIOCSER_TEMT。
set_mctrl—設置Modem控制線的輸出狀態(tài)。參數(shù)mctrl可以是TIOCM_TRS(RTS信號)、TIOCM_DTR(DRT信號)、TIOCM_OUT1(OUT1信號)和TIOCM_OUT2(OUT2信號)的組合。其對應位置1,表明信號有效;若清0,則表明信號無效。
get_mctrl—返回Modem控制線輸入狀態(tài)。返回值可以是TIOCM_DCD(DCD信號)、TIOCM_CTS(CTS信號)、TIOCM_DSR(DSR信號)和TIOCM_RI(RI信號)的組合。其對應位置1,表明信號有效。如果硬件不支持CTS、DCD、DSR,則驅動程序應設置其對應位;如果不支持RI,則清除RIOCM_RI。
值得注意的是,如果使用設備文件系統(tǒng),則在Linux內核串口驅動程序對應設備中的文件就容易混亂。uart_register_
driver函數(shù)在注冊串口驅動時,用結構體structuart_driver中的normal_name成員作為設備文件名。例如,在16c550的驅動程序中(文件drivers/serial/serial_lh7a40x.c)有定義:驅動程序已經(jīng)對設備文件系統(tǒng)有支持。如果使用它,則第一個串口終端的設備文件名就是/dev/tts/0。可是,此驅動在注冊控制臺設備時有定義:這里使用的控制臺名就是ttyAM。因此,如果想使用此串口作為控制臺,則必須在系統(tǒng)驅動命令行中指定console
=ttyAM。
8.4LCD驅動程序設計分析
8.4.1LCD控制器
LCD控制器的功能是顯示驅動信號,進而驅動LCD。用戶只需要通過讀/寫一系列的寄存器完成配置和顯示驅動。在LCD驅動設計的過程中首要的是配置LCD控制器,而在配置LCD控制器中最重要的一步則是幀緩沖區(qū)(framebuffer)的指定。用戶所要顯示的內容皆是從緩沖區(qū)中讀出,從而顯示到屏幕上。幀緩沖區(qū)的大小由屏幕的分辨率和顯示色彩數(shù)決定。驅動幀緩沖的實現(xiàn)是整個驅動開發(fā)過程的重點。
S3C2410中的LCD控制器可支持STN和TFT兩種液晶。對于STN液晶平板,該LCD控制器可支持4位雙掃描、4位單掃描和8位單掃描三種顯示類型,支持4級和16級灰度級單色顯示模式,支持256色和4096色顯示,可接多種分辨率的LCD,例如640?×?480、320?×?240和160?×?160等,在256色顯示模式時,最大可支持4096?×?1024、2048?×?2048和1024?×?4096顯示。TFT液晶平板可支持1/2/4/8b/p(bitsperpixel)調色板顯示模式和16b/p非調色板真彩顯示模式。8.4.2framebuffer設備驅動程序分析
framebuffer設備驅動程序是Linux專門提供給用戶程序的一個直接面向顯示緩沖區(qū)的統(tǒng)一接口,應用程序不需要知道底層硬件的任何信息,它只和framebuffer驅動程序抽象出來的接口打交道。framebuffer使得有GUI的程序移植起來很容易。
另外,內核的framebuffer驅動程序中還包括了控制臺字符顯示的代碼。在Linux2.4或者2.6內核中,與framebuffer控制臺相關的代碼被單獨放到了fbcon等相關的文件中,它們包括各種不同格式顯示緩沖區(qū)的字符輸出、各種字體數(shù)據(jù)文件等,這大大簡化了framebuffer上控制臺驅動的移植過程。盡管如此,framebuffer驅動程序也是一個比較復雜的結構,從頭編寫一個framebuffer驅動并不容易。幸運的是,內核代碼中提供了很多移植好的代碼可供參考,vfb驅動程序就是一個很好的開始。
實際上,vfb驅動程序并不是一個針對硬件的framebuffer。它是在系統(tǒng)內存中開辟一塊空間作為虛擬顯示緩沖,模擬framebuffer的行為。因此,vfb也不依賴于任何硬件。其他framebuffer驅動程序的移植都可以基于vfb。vfb的源碼在linux/drivers/video/vfb.c文件中。這里以2.6.22內核中vfb驅動程序為例,說明Linuxframebuffer驅動程序的結構。vfb驅動程序的入口在vfb_ini函數(shù)中。首先,通過fb_get_options函數(shù)從Linux驅動命令行參數(shù)獲得vfb相關的控制,并通過vfb_setup開啟vfb設備;接著,通過platform_driver_register注冊vfb設備驅動程序。platform_driver_register是2.6內核中提供的注冊驅動程序的方法,其原型在linux/platform_device.h中定義:
intplatform_driver_register(structplatform_driver*);
它使得系統(tǒng)加載、檢測、卸載驅動程序有一個統(tǒng)一的接口。系統(tǒng)會調用drv->probe來檢測是否存在有效的設備。在vfb驅動程序中,對應的調用是vfb_probe函數(shù)。首先,利用vmallac分配了一個顯示緩沖區(qū)。vfb是利用系統(tǒng)內存虛擬的顯示緩沖設備,所以可使用vmalloc分配內存。如果是針對系統(tǒng)特定地址的顯示設備,則通常用ioremap映射地址空間到內核。framebuffer_alloc函數(shù)的作用是分配一個structfb_info空間,這是framebuffer驅動程序中最核心的結構,它記錄了當前framebuffer硬件設備的狀態(tài),其定義如下:它包括了若干個framebuffer驅動程序的結構體,其中最主要的有:
fb_var_screeninfo—定義了當前設置的顯示輸出狀態(tài),包括顯示分辨率、顏色深度等信息。通常這些信息是可被用戶程序改變的。
fb_fix_screeninfo—定義了顯示輸出設備自身的屬性,如顯示緩沖區(qū)映射的地址。這些屬性是不可被用戶程序改變的。
fb_ops—用于framebuffer具體操作的函數(shù),主要包括檢測并調整用戶的fb_fix_screeninfo設置,矩形填充、復制,光標控制,電源管理,mmap等。
vfb_probe的過程主要是填充結構體fb_info的過程。最后,調用platform_driver_register函數(shù)注冊一個framebuffer驅動程序。函數(shù)platform_driver_register只需要一個參數(shù),就是structfb_info。
成功地注冊一個framebuffer設備后,如果使用了設備文件系統(tǒng),則其對應的設備文件路徑是/dev/fb/,第一個注冊的framebuffer設備是/dev/fb/0,這是一個字符設備。8.4.3LCD驅動開發(fā)的主要工作
1.編寫初始化函數(shù)
初始化函數(shù)首先初始化LCD控制器,通過寫寄存器設置顯示模式和顏色數(shù),然后分配LCD顯示緩沖區(qū)。在Linux中可以用kmalloc()函數(shù)分配一段連續(xù)的空間。緩沖區(qū)大小為:點陣行數(shù)×點陣列數(shù)×用于表示一個像素的比特數(shù)/8。緩沖區(qū)通常分配在大容量的片外SDRAM中,起始地址保存在LCD控制寄存器中。本文采用的LCD顯示方式為640?×?480,16位彩色,則需要分配的顯示緩沖區(qū)為640?×?480?×?2?=?600kb。最后是初始化一個fb_info結構,填充其中的成員變量,并調用register_framebuffer(&fb_info),將fb_info登記入內核。
2.編寫成員函數(shù)
編寫結構fb_info中函數(shù)指針fb_ops對應的成員函數(shù),對于嵌入式系統(tǒng)的簡單實現(xiàn),只需要下列三個函數(shù)就可以了。
structfb_ops在include/linux/fb.h中被定義。這些函數(shù)都是用來設置/獲取fb_info結構中的成員變量的。當應用程序對設備文件進行ioctl操作時會調用它們。對于fb_get_fix(),應用程序傳入的是fb_fix_screeninfo結構,在函數(shù)中對其成員變量賦值,主要是smem_start(緩沖區(qū)起始地址)和smem_len(緩沖區(qū)長度),最終返回給應用程序。而fb_set_var()函數(shù)的傳入?yún)?shù)是fb_var_screeninfo,函數(shù)中需要對xres、yres和bits_per_pixel賦值。對于/dev/fb,對顯示設備的操作主要有以下幾種:
(1)讀/寫(read/write)/dev/fb:相當于讀/寫屏幕緩沖區(qū)。
(2)映射(map)操作:由于Linux工作在保護模式,每個應用程序都有自己的虛擬地址空間,在應用程序中是不能直接訪問物理緩沖區(qū)地址的,因此,Linux在文件操作file_operations結構中提供了mmap函數(shù),可將文件的內容映射到用戶空間。對于幀緩沖設備,則可通過映射操作,將屏幕緩沖區(qū)的物理地址映射到用戶空間的一段虛擬地址中,之后用戶就可以通過讀/寫虛擬地址訪問屏幕緩沖區(qū),在屏幕上繪圖了。
(3)?I/O控制:對于幀緩沖設備,對設備文件的ioctl操作可讀取/設置顯示設備及屏幕的參數(shù),如分辨率、顯示顏色數(shù)和屏幕大小等。ioctl的操作是由底層的驅動程序來完成的。
在應用程序中,操作/dev/fb的一般步驟如下:打開/dev/fb設備文件;用ioctl操作取得當前顯示屏幕的參數(shù)(如屏幕分辨率和每個像素的比特數(shù)),根據(jù)屏幕參數(shù)可計算屏幕緩沖區(qū)的大??;將屏幕緩沖區(qū)映射到用戶空間;映射后即可直接讀/寫屏幕緩沖區(qū),進行繪圖和圖片顯示了。
8.5中斷處理
中斷處理是實現(xiàn)外部I/O異步操作的重要方法。通過中斷機制,操作系統(tǒng)可以先請求外部I/O操作,然后轉去執(zhí)行其他任務,當設備完成請求時,再通過中斷通知操作系統(tǒng)。操作系統(tǒng)在響應一個特定中斷時,會執(zhí)行相應的中斷處理程序(一段短小的代碼片段,常以函數(shù)形式出現(xiàn),故也稱為中斷處理函數(shù))。Linux內核對外部設備的中斷處理是在設備驅動程序中實現(xiàn)的,也就是說,中斷處理函數(shù)是Linux設備驅動的一部分,每個設備中斷在其驅動程序中都可以有一個對應的中斷處理函數(shù)。8.5.1中斷程序分析
Linux的設備驅動程序可以用request_irq()函數(shù)注冊中斷處理程序,用于處理設備產(chǎn)生的中斷,此函數(shù)的定義如下:
intrequest_irq(unsignedintirq,
irqretun_t(*handler)(int,void*,structptregs*);
unsignedlongflags,
constchar*
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 保安公司聘用合同范本
- 農(nóng)藥設備轉讓合同范本
- 分期貸款賣房合同范本
- u盤供貨合同范例
- 合營合同范本
- 策劃你的畢業(yè)之旅
- 勞動與技術合同范本
- 員工花名冊合同范例
- 商戶和商場簽合同范本
- 勞動合同范本最
- 南方醫(yī)大內科學教案04消化系統(tǒng)疾病-8炎癥性腸病
- (完整版)標書密封條格式word
- 五氟化銻的理化性質及危險特性表
- 煤礦用鋼絲繩芯阻燃輸送帶(MT668-2008)
- 全省安全生產(chǎn)檢測檢驗機構名單及業(yè)務范圍
- 辦公用品供貨服務計劃方案
- DB37∕T 5107-2018 城鎮(zhèn)排水管道檢測與評估技術規(guī)程
- 酒精溶液體積濃度、質量濃度與密度對照表
- 主要腸內營養(yǎng)制劑成分比較
- 老年人各系統(tǒng)的老化改變
- 小學五年級綜合實踐課教案
評論
0/150
提交評論