字符設備驅動、平臺設備驅動、設備驅動模型、sysfs的比較和關聯

Linux驅動開發經驗總結,絕對乾貨!

學習Linux設備驅動開發的過程中自然會遇到字符設備驅動、平臺設備驅動、設備驅動模型和sysfs等相關概念和技術。對於初學者來說會非常困惑,甚至對Linux有一定基礎的工程師而言,能夠較好理解這些相關技術也相對不錯了。要深刻理解其中的原理需要非常熟悉設備驅動相關的框架和模型代碼。網絡上有關這些技術的文章不少,但多是對其中的某一點進行闡述,很難找到對這些技術進行比較和關聯的分析。對於開發者而言,能夠熟悉某一點並分享出來已很難得,但對於專注傳授技術和經驗給學習者而言,橫向比較關聯各個驅動相關的知識點和縱向剖析Linux整個驅動軟件層次是非常有必要的,也非常有意義。

本文依然是從需求的角度去理解以上知識點,存在即是合理,以上技術知識能夠存在,即代表其有一定的作用。我們着重去理解每一個技術點的作用,並明確其在驅動開發中的角色。

一、設備驅動

Linux設備驅動分三種,包括字符設備驅動、塊設備驅動和網絡設備驅動。字符設備只能按字節流先後順序訪問設備內存,不能隨機訪問。鼠標、觸摸屏、LCD等是字符設備的代表。塊設備可以隨機訪問設備內存的任意地址,硬盤、SD卡、NAND FLASH是塊設備的代表。網絡設備指的是網卡一類使用socket套接字進行通信的設備。本文以字符設備爲例講述相關知識。

二、字符設備驅動

字符設備驅動框架請參考嵌入式企鵝圈的兩篇文章:

 Linux字符設備驅動剖析

Linux 設備文件的創建和mdev

1.    字符設備驅動縱向關係

從< Linux字符設備驅動剖析>可以看出,應用層訪問設備驅動非常簡單,即是通過open接口來最終獲得設備驅動的操作接口集struct file_opertions.而open接口傳入的參數是/dev目錄下的設備名。而從<Linux 設備文件的創建和mdev>可以知道,設備名對應的設備文件節點inode會存儲設備號,而驅動框架中的全局數組cdev_map則維護設備號和file_opertions的關係。即應用層到底層的關係主要是(忽略VFS這一層):

設備名-->設備號-->file_opertions

Open函數返回的局部fd和file_opertions的關係(忽略進程數據結構)如下:

fd-->file(當前進程數據結構成員)-> file_opertions

這樣,通過fd即可以獲得file_opertions,即可以通過read、write等接口來調用驅動的讀操作函數和寫操作函數、ioctl函數等。

2.    字符設備驅動的任務

1)字符設備驅動最本質的任務應該是提供file_opertions的各個open、read、write、ioctl等接口的實現。

另外從以上的描述中,爲了讓應用層能夠調用到底層的file_opertions還涉及到以下任務:

2)申請設備號,並將設備號和file_opertions註冊(cdev_add接口)到驅動框架中的cdev_map數組。這點應該在字符設備驅動中負責,涉及到其主動向系統報備自己的存在。

3)在/dev目錄中創建設備文件,內容包括設備號。這一點是否由字符設備驅動來負責商榷。字符設備驅動位於內核層,如果由其負責這個任務,那麼驅動就得知道它要創建的設備名。簡單的字符驅動還好,如果是USB等可插拔的設備,驅動怎麼知道自己要創建什麼設備名呢?有人說可以寫明一套規則。確實如此,但如果把這套規則放到應用層,由應用程序開發人員去明確這個規則(mdev正是這樣做的),會不會更好?因爲是應用程序直接編程訪問這個設備名對應的設備驅動的。所以字符設備驅動不應該直接負責設備文件的創建。

3. 誰來創建設備文件

       總得有人出來做吧,否則應用層怎麼訪問啊?

       一種方法就是用戶在shell中使用mknod命令創建設備文件,同時傳入設備名和設備號。這是人工的做法,很不科學。但它是一種演示的方法。

       另外一種方法就是依賴設備模型來輔助創建設備文件。這也是設備模型的作用之一。

4. 字符設備驅動編程流程

       1)定義struct file_opertions my_fops並實現其中的各個接口,如open、read、write、ioctl等接口。

       2)實現驅動的入口函數,如chardev_init

              static int __init chardev_init(void){

                     alloc_chrdev_region(&devno,…);//申請設備號

                     my_cdev=cdev_alloc();

                     cdev_init(my_cdev,&my_fops);

                     cdev_add(my_fops,devno, 1);//註冊設備號和file_opertions

              }

       3)module_init(chardev_init);//宏定義該初始化入口函數。卸載流程不做解釋。

       4)insmod加載這個module後,可以人工在shell命令行利用mknod創建設備文件。

       5)應用層即可以用open來打開設備文件來進行訪問了。

5.    總結

可以看出,字符設備驅動的核心框架跟設備模型、平臺設備驅動沒有直接關係,不用他們也一樣能夠正常工作。

三、設備驅動模型

我們主要談及設備驅動模型在linux驅動中的作用和角色,有關設備模型的原理和實現我們另文再述。

1. 設備驅動模型的作用

1)設備驅動模型實現uevent機制,調用應用層的medv來自動創建設備文件。這在上面已經論述過。

2)設備驅動模型通過sysfs文件系統向用戶層提供設備驅動視圖,如下。


上圖只是可視化的一種表達,有助於大家去理解設備模型,類似於windows的設備管理程序,在嵌入式linux裏面並沒有相關應用通過圖形的方式來展現這種關係。但是用戶可以通過命令窗口利用ls命名逐級訪問/sys文件夾來獲得各種總線、設備、驅動的信息和關係。可以看出,在/sys頂級目錄,有三個關鍵的子目錄,就是設備類、設備和總線。

設備是具體的一個個設備,在/sys/devices/是創建了實際的文件節點。而其他目錄,如設備類和總線以下的子目錄中出現的設備都是用符號鏈接指向/sys/devices/目錄下的文件。

設備類是對/sys/devices/下的各種設備進行歸類,以體現一類設備的公共屬性,如鼠標和觸摸屏都是屬於input設備類。

總線目錄是總線、設備、驅動模型的核心目錄。因爲設備和驅動都是依附在某種總線上的,如USB、PCI和平臺總線等。設備和驅動正是依靠總線的管理功能才能找到對方,如設備註冊到總線時去尋找驅動,而驅動註冊的時候去尋找其能夠支持的設備。

最重要的是,如果沒有設備模型,那應用層很難知曉驅動和設備的關係,因爲字符設備驅動並沒有提供這些信息,那對於設備驅動的管理者而言會非常麻煩。

事實上,內核中的總線class、設備device和驅動device_driver都不會將所有的信息暴露給用戶層,例如這三個數據結構都有對應的private數據結構,它用於內核對上下級總線設備驅動的鏈表關係維護。如果暴露給用戶層,那容易被用戶層修改而使系統混亂。實際上,用戶層只關心三者的視圖關聯,至於他們的關聯在底層怎麼實現則不需要關心。

3)設備驅動模型提供統一的電源管理機制。很明顯,我們在字符設備驅動的file_operations接口中並沒有看到電源管理方面的接口。而對於操作系統來說,電源功耗管理必不可少。電源管理其實不應該由應用開發人員來負責,而是應該由系統來負責,例如手機很久沒有觸摸了,那會進入休眠狀態。這種狀態的改變應該由系統來完成,而各種設備進入睡眠模式也應該由系統來完成。因此file_operations不提供電源管理的接口給應用程序是合理的。而設備模型作爲系統管理的一種機制,由它來提供電源管理是非常合理的。

       如設備device數據結構有struct dev_pm_info    power功耗屬性參數,驅動device_driver數據結構有struct dev_pm_ops *pm功耗操作接口。

       4)設備驅動模型提供各種對象實例的引用計數,防止對象被應用層誤刪。設備模型的所有數據結構均是繼承kobject而來,而kobject就提供基礎的計數功能。

       5)設備驅動模型提供多一種方式給應用層,用戶和內核可以通過sysfs進行交互,如通過修改/sys目錄下設備的文件內容,即可以直接修改設備對應的參數。

       總結,設備驅動模型側重於內核對總線、設備和驅動的管理,並嚮應用層暴露這些管理的信息,而字符設備驅動則側重於設備驅動的功能實現。

2. 設備驅動模型的核心接口

       bus_register(struct bus_type *bus) 註冊總線

device_add(struct device *dev) 註冊設備

       driver_register(struct device_driver*drv) 註冊驅動

       class_create(owner, name) 創建設備類

       等等

3.    設備驅動模型和字符設備驅動區別

設備驅動模型側重於內核對總線、設備和驅動的管理,並嚮應用層暴露這些管理的信息,而字符設備驅動則側重於設備驅動的功能實現。

四、sysfs文件系統

1.sysfs文件系統和設備驅動模型的關係

Sysfs文件系統是設備驅動模型得以向用戶暴露其管理信息的載體。它們之間的關係如下:

1)設備驅動模型的上下級關係(如子設備和所屬父設備)通過sysfs文件系統的父目錄和子目錄來體現。

2)設備驅動模型的平級關係(如設備類管理的設備和具體的設備的關係)則通過sysfs文件系統的目錄符號鏈接來實現。

3)設備驅動模型的屬性(如設備的參數和設備名,設備號等)則通過sysfs文件系統的文件內容來記錄實現。

       4)設備驅動模型數據結構中的kobject對應於sysfs文件系統中的目錄,而數據結構中的struct attribute成員則對應於sysfs文件系統中的文件。對應的意思是指繼續與kobject的device、device_driver和bus等在向系統註冊的過程中會調用sysfs的create_dir接口來創建對應的目錄,而含有struct attribute成員屬性的device、device_driver和bus等在向系統註冊的過程中則會調用sysfs的sysfs_create_file接口來創建文件。

2.sysfs核心接口

       sysfs_create_file(struct kobject * kobj,const struct attribute * attr)創建屬性文件

       sysfs_create_dir(struct kobject * kobj)創建目錄

       int sysfs_open_file(struct inode *inode,struct file *file)打開sysfs文件系統格式的文件

       sysfs_read_file(struct file *file, char__user *buf, size_t count, loff_t *ppos) 讀操作

       sysfs_write_file(struct file *file, constchar __user *buf, size_t count, loff_t *ppos) 寫操作

3. sysfs文件系統與屬性文件讀寫

       sysfs_read_file是sysfs文件系統的讀寫入口,但是驅動需要向系統提供真正的讀寫操作,也即是struct sysfs_ops數據結構中的show和store接口。

       Sysfs是基於內存的文件系統,掉電即消失,sysfs所有的操作接口均是對內存中的內核數據結構進行訪問操作。假如用戶用cat命令去讀取一個屬性文件(如dev)的內容,那麼會產生以下流程:

       1)fd=open(“dev”)->vfs_open(“dev”)->sysfs_open(“dev”)獲取該文件的句柄

       2)read()->vfs_read()->sysfs_read_file()->sysfs_ops->show()該show接口即是設備在註冊時產生屬性文件,並向系統提供該文件的讀接口。而讀接口的實現中自然是對該屬性參數的讀訪問。

       /sys掛載了sysfs文件系統,因此所有對/sys目錄下的文件或者目錄的操作都會通過sysfs文件的接口進行訪問。

五、平臺設備驅動

平臺設備驅動中的“平臺”指的是平臺總線,即platform_bus_type,是linux衆多總線中的一種,如USB總線、PCI總線、I2C總線等等。只不過平臺總線是一種虛擬的總線,專門用來管理SOC上的控制器(如看門狗、LCD、RTC等等),它們都是CPU的總線上直接取址訪問設備的。而USB、PCI等設備都有通過特定的時序來訪問SOC芯片以外的設備。平臺設備驅動體現的關係是設備驅動模型上的一個子集,將平臺視爲一種總線的概念,那兩者的關係就會容易理解。

1.    平臺設備驅動和設備驅動模型的關係

1)平臺設備驅動接口在設備驅動模型視圖上創建了相關的平臺設備類(/sys/class/platform_bus)、平臺總線(/sys/bus/platform)、平臺設備(/sys/devices/).

2)平臺設備(platform_device)和平臺設備驅動(platform_driver)均註冊到平臺總線上,即在/sys/bus/platform/目錄下創建相應的設備和驅動目錄。

3)平臺總線負責匹配註冊到其上面的設備和驅動,匹配成功後回調用驅動的probe接口。

4)平臺設備驅動利用設備驅動模型接口來輔助創建對應的設備文件(位於/dev/目錄下)。

 

相關的接口包括:

platform_device_register(structplatform_device *pdev) 註冊平臺設備

platform_driver_register(structplatform_driver *drv) 註冊平臺設備驅動

兩個接口的實現裏面都會對平臺驅動和設備進行匹配,匹配成功會調用驅動的probe接口。

 

2. 平臺設備驅動和字符設備驅動的關係

       我們假設這個平臺設備是字符設備。

平臺設備驅動和字符設備驅動的關係始於驅動的probe接口,即在probe接口中實現字符設備驅動所要完成的任務,即通過alloc_chrdev_region申請設備號和通過cdev_add註冊驅動的struct file_opertions.另外爲了自動創建應用層訪問的設備文件,還要調用class_create和device_create接口在平臺設備類下創建對應的設備類和設備,併發出uevent事件,調用mdev來創建設備文件。

3. 平臺設備驅動的開發流程

       1)將字符設備驅動的char_init函數的實現搬到platform_driver的probe接口中。

       2)在char_init中調用platform_device_register和platform_driver_register分別註冊設備和驅動。其實,對於移植好的系統,platform_device_register是在linux啓動的過程中完成的。因此char_init一般只有platform_driver_register註冊驅動。

       詳細的平臺設備驅動的實現原理和開發流程另文再述。本次的重點是爲了闡述字符設備驅動、設備驅動模型、sysfs和平臺設備驅動之間的關係。

 

博主微信公衆號:嵌入式企鵝圈,百分百原創分享嵌入式Linux開發、物聯網IOT開發經驗知識,絕對乾貨!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章