? ? ? Linux的設備驅動模型,或者說,Linux的設備驅動框架,都是同一個意思。應該這樣理解,(Linux的設備)驅動框架,即某類設備對應的驅動的框架。
這里是“Linux總線設備驅動框架”,應該這樣理解,(Linux的總線設備)驅動框架,即總線式設備對應的驅動的框架。
以下內容源于微信公眾號:嵌入式企鵝圈。有格式內容上的修改,如有侵權,請告知刪除。
1、總線
總線代表著同類設備需要共同遵守的工作時序,不同的總線對于物理電平的要求是不一樣的,對于每個比特的電平維持寬度也是不一樣,而總線上傳遞的命令也會有自己的格式約束。
以I2C總線為例,在同一組I2C總線上連接著不同的I2C設備。
2、設備
設備代表真實的、具體的物理器件,在軟件上用器件的獨特的參數屬性來代表該器件。
如I2C總線上連接的I2C從設備都有一個標識自己的設備地址,由這個設備地址來確定主設備發過來的命令是否該由它來響應。
3、驅動
(1)驅動代表著操作設備的方式和流程。對于應用來說,應用程序open打開設備后,接著就read訪問這個設備,驅動就是如何實現這個訪問的具體的過程。
(2)驅動主要包括兩部分
第一是通過對SOC的控制寄存器進行編程,按總線要求輸出時序和命令,成功地與外圍設備進行交互;
第二是對第一步中得到的數據進行處理,并向應用層提供特定格式的數據。
(3)注意點
不同總線的設備的驅動過程是不一樣的,比如,USB鼠標的驅動和I2C EEPROM的讀時序肯定是不一樣的,訪問時序的產生和控制也是驅動的一部分。
同種總線不同設備類型的設備驅動也是不一樣的。如I2C電容屏設備,對于讀read來說就是在datasheet規定的地址上去讀觸摸點的X和Y坐標,而I2C EEPROM的讀操作是讀取存儲的內容,兩種設備的datasheet是不一樣的,驅動自然是不一樣的。
同種總線的同類設備的設備驅動也可能是不一樣的。例如對于觸摸屏,TSC2003只支持單點觸控,而FT5X06支持多點觸摸。在獲取觸控坐標時,前者只需要獲得一個點的數據就返回,而后者則需要先獲得當前有幾個點的數據,然后再把所有點的坐標都讀出來。
(3)在驅動的操作中,一般都會用到GPIO和中斷等硬件資源。
如SDA和SCL會連接到SOC芯片的具體的兩個GPIO引腳,而I2C讀寫時一般都采用中斷控制的方式(查詢讀寫是否完成比較低效,浪費CPU)。
如果我們在驅動中直接針對具體的引腳來編程,那這個驅動的平臺可移植性就比較差,因為不同的產品設計可能引腳不一樣。
為了提高驅動的可移植性,Linux把驅動要用到的GPIO和中斷等資源剝離給設備去管理。
即在設備里面包含其自己的設備屬性,還包括了其連接到SOC所用到的資源。而驅動重點關注操作的流程和方法。
4、再談總線
第1點中談到的總線只是物理意義上的表述,即總線就是在行業中制定出標準,明確規定時序的格式。我們在第3點中談到,在軟件層面上,時序的產生和控制由驅動負責。那我們要思考在軟件層面上,總線的職責是什么?
總線在軟件層面主要是負責管理設備和驅動。
設備要讓系統感知自己的存在,設備需要向總線注冊自己;同樣地,驅動要讓系統感知自己的存在,也需要向總線注冊自己。設備和驅動在初始化時必須要明確自己是哪種總線的,I2C設備和驅動不能向USB總線注冊吧。
多個設備和多個驅動都注冊到同一個總線上,那設備怎么找到最適合自己的驅動呢,或者說驅動怎么找到其所支持的設備呢?
這個也是由總線負責,總線就像是一個紅娘,負責在設備和驅動中牽線。
設備會向總線提出自己對驅動的條件(最簡單的也是最精確的就是指定對方的名字了),而驅動也會向總線告知自己能夠支持的設備的條件(一般是型號ID等,最簡單的也可以是設備的名字)。
設備在注冊的時候,總線就會遍歷注冊在它上面的驅動,找到最適合這個設備的驅動,然后填入設備的結構成員中;驅動注冊的時候,總線也會遍歷注冊在其之上的設備,找到其支持的設備(可以是多個,驅動和設備的關系是1:N),并將設備填入驅動的支持列表中。我們稱總線這個牽線的行為是match。牽好線之后,設備和驅動之間的交互紅娘可不管了。
總線在匹配設備和驅動之后驅動要考慮一個這樣的問題,設備對應的軟件數據結構代表著靜態的信息,真實的物理設備此時是否正常還不一定,因此驅動需要探測這個設備是否正常。我們稱這個行為為probe,至于如何探測,那是驅動才知道干的事情,總線只管吩咐得了。所以我們可以猜測在總線的管理代碼中會有這樣的邏輯:
[cpp]?view plain?copy
if(match(device,?driver)?==?OK)??
driver->probe();??
5、再談驅動
假設設備正常,探測成功,這時就代表應用程序可以通過驅動來訪問操作這個設備了。事實上是這樣嗎?仔細想想還少了什么東西。應用層通過什么來訪問操作這個設備?想起來嗎?“嵌入式企鵝圈”的第一篇文章《Linux字符設備驅動剖析》中曾清晰地分析了Linux字符設備驅動的開發和訪問過程,在開篇即提到應用程序如何訪問設備:
[cpp]?view plain?copy
int?fd?=?open(“設備文件名”);??
read(fd,?buf,?len);??
write(fd,?buf,?len);??
這個應用程序涉及的驅動兩個問題,一是設備文件名從何而來;二是應用層的API如open、read和write等對應驅動的哪些接口,是如何對應的。這些都是驅動要解決的問題。
總線匹配設備和驅動之后,驅動探測到設備正常,這時驅動已經做好準備讓應用層來差遣,但是設備文件名如果沒有創建,應用程序也不知從何入手。所以在驅動的probe探測成功之后,立即創建設備文件是最合適的時機。通過sysfs文件系統、uevent事件通知機制、后臺應用服務mdev程序,三者的配合,在/dev目錄創建對應的設備文件。
驅動要提供應用層API如open、read、write、ioctl等操作的對應接口,而且這些接口要向系統報備(注冊)自己,否則系統也不知道怎么調用驅動,因為在上面的描述中從始至終都是設備、驅動和總線三個東西在唱戲,它們跟系統,嚴格意義是跟Linux的虛擬文件系統和設備文件系統還沒建立起關系來。
對于第二個問題,驅動要包括以下步驟:
設備要提供(struct file_operation結構所定義的)接口(即函數的具體實現)。這些接口將會對應到應用層的設備訪問操作(即應用層的API)。在這些接口中,其會根據第3點中提到的需求去完成自己的操作任務。
[cpp]?view plain?copy
struct?file_operations???
{??
int?(*open)?(struct?inode?*,?struct?file?*);??
int?(*ioctl)?(struct?inode?*,?struct?file?*,?...);??
ssize_t?(*read)?(struct?file?*,?char?__user?*,...);??
ssize_t?(*write)?(struct?file?*,?const?char?__user?*,?...);??
…??
}??
應用層正常的訪問流程是:應用層操作->虛擬文件系統操作->具體文件系統操作->具體設備驅動的操作。
虛擬文件系統VFS系統已經存在;
具體文件系統操作對于字符設備來說非常簡單,我們姑且認為是字符設備文件系統devfs;
此時字符設備驅動要做的是,將自己的struct file_operations向devfs注冊(對于字符設備驅動,使用的是cdev_add函數)。
詳細的分析過程可以參考《Linux字符設備驅動剖析》。
所以我們可以想象在驅動driver的結構體中有一個probe接口(即Driver->probe()),驅動要實現這個接口。這個probe接口要完成的工作包括:
探測設備是否正常;
cdev_add(struct file_operations),注冊操作接口;
device_create(),創建設備文件。
6、繼續談驅動
做好以上準備,剩下的就是等著應用程序來訪問操作了。這里闡述一下設備驅動的struct file_operations中的接口都要做什么。挑幾個主要的來講講。
(1)open一般會進行驅動的初始化
可能包括硬件的初始化、軟件的初始化。
我們在第3點談驅動的時候,曾說明為了讓驅動更具移植性,會將驅動driver過程中使用到的具體GPIO和IRQ中斷等資源列入設備device的屬性內容。這時device數據結構中斷的GPIO和IRQ的標識都來源于SOC datasheet的物理地址定義。
Linux在運行過程中,會使用SOC的MMU內存管理單元來管理自己的內存,會將內存分為兩部分,內核空間(3G-4G)和用戶空間(0-3G)。
這兩塊地址空間都是虛擬線性地址空間,即程序編譯鏈接之后對應的地址空間;
虛擬地址空間需要通過MMU和頁表來映射到實際的物理內存空間,才能最終訪問到物理內存和物理IO等資源。
而驅動操作硬件都處在內核空間,在open函數中主要包括以下操作:
(通過系統提供的資源獲取接口)獲取到GPIO和IRQ等資源;
通過ioremap接口將GPIO和IRQ從物理地址空間映射到3G-4G中的虛擬地址空間;
根據具體的控制規格設置GPIO和IRQ相關的寄存器。
以上初始化的動作可能會出現在驅動probe探測的代碼中,那open的接口可以什么都不做。
(2)read
驅動的open如果成功,那整個訪問流程已經成功一大半了,因為open的流程足夠漫長和復雜。
而read只是從(用戶空間的fd文件句柄中)找到所屬進程的file文件結構,然后即可找出file_operations->read,其即是驅動的read接口。
那就按著外網設備的規格和總線的時候進行操作,達到read設備的目的。Write也一樣。
(3)ioctl一般是對設備進行參數設置。
?
評論
查看更多