设备驱动,字符设备驱动、(总线)设备驱动模型、sysfs文件系统、平台设备驱动
以下內(nèi)容轉(zhuǎn)載于微信公眾號:嵌入式企鵝圈。如有侵權(quán),請告知刪除。
? ? 學(xué)習(xí)Linux設(shè)備驅(qū)動開發(fā)的過程中自然會遇到字符設(shè)備驅(qū)動、平臺設(shè)備驅(qū)動、設(shè)備驅(qū)動模型和sysfs等相關(guān)概念和技術(shù)。
? ? 對于初學(xué)者來說會非常困惑,甚至對Linux有一定基礎(chǔ)的工程師而言,能夠較好理解這些相關(guān)技術(shù)也相對不錯了。
? ? 要深刻理解其中的原理需要非常熟悉設(shè)備驅(qū)動相關(guān)的框架和模型代碼。
? ? 網(wǎng)絡(luò)上有關(guān)這些技術(shù)的文章不少,但多是對其中的某一點進(jìn)行闡述,很難找到對這些技術(shù)進(jìn)行比較和關(guān)聯(lián)的分析。
? ? 對于開發(fā)者而言,能夠熟悉某一點并分享出來已很難得,但對于專注傳授技術(shù)和經(jīng)驗給學(xué)習(xí)者而言,橫向比較關(guān)聯(lián)各個驅(qū)動相關(guān)的知識點和縱向剖析Linux整個驅(qū)動軟件層次是非常有必要的,也非常有意義。
? ? 本文依然是從需求的角度去理解以上知識點,存在即是合理,以上技術(shù)知識能夠存在,即代表其有一定的作用。我們著重去理解每一個技術(shù)點的作用,并明確其在驅(qū)動開發(fā)中的角色。
一、設(shè)備驅(qū)動
- (設(shè)備的)驅(qū)動;
- Linux設(shè)備驅(qū)動分三種,包括字符設(shè)備驅(qū)動、塊設(shè)備驅(qū)動和網(wǎng)絡(luò)設(shè)備驅(qū)動。
- 字符設(shè)備只能按字節(jié)流先后順序訪問設(shè)備內(nèi)存,不能隨機(jī)訪問。鼠標(biāo)、觸摸屏、LCD等是字符設(shè)備的代表。
- 塊設(shè)備可以隨機(jī)訪問設(shè)備內(nèi)存的任意地址,硬盤、SD卡、NAND FLASH是塊設(shè)備的代表。
- 網(wǎng)絡(luò)設(shè)備指的是網(wǎng)卡一類使用socket套接字進(jìn)行通信的設(shè)備。
二、字符設(shè)備驅(qū)動
? ? 字符設(shè)備驅(qū)動框架,請參考嵌入式企鵝圈的兩篇文章:《Linux字符設(shè)備驅(qū)動剖析》和《Linux設(shè)備文件的創(chuàng)建和mdev》。
1、字符設(shè)備驅(qū)動縱向關(guān)系
- 從《Linux字符設(shè)備驅(qū)動剖析》可以看出,應(yīng)用層訪問設(shè)備驅(qū)動非常簡單,即通過open接口最終獲得設(shè)備驅(qū)動的操作接口集struct file_opertions。
- 而open接口傳入的參數(shù)是/dev目錄下的設(shè)備名,設(shè)備名對應(yīng)的設(shè)備文件節(jié)點inode會存儲設(shè)備號。
- 而驅(qū)動框架中的全局?jǐn)?shù)組cdev_map則維護(hù)設(shè)備號和file_opertions的關(guān)系。
- 即應(yīng)用層到底層的關(guān)系主要是(忽略VFS這一層):設(shè)備名-->設(shè)備號-->file_opertions;
- Open函數(shù)返回的局部fd和file_opertions的關(guān)系(忽略進(jìn)程數(shù)據(jù)結(jié)構(gòu)):fd-->file(當(dāng)前進(jìn)程數(shù)據(jù)結(jié)構(gòu)成員)-> file_opertions;
- 這樣,通過fd即可以獲得file_opertions,即可以通過read、write等接口來調(diào)用驅(qū)動的讀操作函數(shù)和寫操作函數(shù)、ioctl函數(shù)等。
2、字符設(shè)備驅(qū)動的任務(wù)
- 字符設(shè)備驅(qū)動最本質(zhì)的任務(wù),應(yīng)該是提供file_opertions的各個open、read、write、ioctl等接口的實現(xiàn)。
- 另外從以上的描述中,為了讓應(yīng)用層能夠調(diào)用到底層的file_opertions還涉及到以下任務(wù):
- 申請設(shè)備號,并將設(shè)備號和file_opertions注冊(cdev_add接口)到驅(qū)動框架中的cdev_map數(shù)組。這點應(yīng)該在字符設(shè)備驅(qū)動中負(fù)責(zé),涉及到其主動向系統(tǒng)報備自己的存在。
- 在/dev目錄中創(chuàng)建設(shè)備文件,內(nèi)容包括設(shè)備號。這一點是否由字符設(shè)備驅(qū)動來負(fù)責(zé)商榷。字符設(shè)備驅(qū)動位于內(nèi)核層,如果由其負(fù)責(zé)這個任務(wù),那么驅(qū)動就得知道它要創(chuàng)建的設(shè)備名。簡單的字符驅(qū)動還好,如果是USB等可插拔的設(shè)備,驅(qū)動怎么知道自己要創(chuàng)建什么設(shè)備名呢?有人說可以寫明一套規(guī)則。確實如此,但如果把這套規(guī)則放到應(yīng)用層,由應(yīng)用程序開發(fā)人員去明確這個規(guī)則(mdev正是這樣做的),會不會更好?因為是應(yīng)用程序直接編程訪問這個設(shè)備名對應(yīng)的設(shè)備驅(qū)動的。所以字符設(shè)備驅(qū)動不應(yīng)該直接負(fù)責(zé)設(shè)備文件的創(chuàng)建。
3、誰來創(chuàng)建設(shè)備文件
- 一種方法就是用戶在shell中使用mknod命令創(chuàng)建設(shè)備文件,同時傳入設(shè)備名和設(shè)備號。這是人工的做法,很不科學(xué)。但它是一種演示的方法。
- 另一種方法就是依賴設(shè)備模型來輔助創(chuàng)建設(shè)備文件。這也是設(shè)備模型的作用之一。
4、字符設(shè)備驅(qū)動編程流程
(1)定義struct file_opertions my_fops并實現(xiàn)其中的各個接口,如open、read、write、ioctl等接口。
(2)實現(xiàn)驅(qū)動的入口函數(shù),如chardev_init
static int __init chardev_init(void) {alloc_chrdev_region(&devno,…);//申請設(shè)備號my_cdev=cdev_alloc();cdev_init(my_cdev,&my_fops);cdev_add(my_fops,devno, 1);//注冊設(shè)備號和file_opertions}(3)module_init(chardev_init);//宏定義該初始化入口函數(shù)。卸載流程不做解釋。
(4)insmod加載這個module后,可以人工在shell命令行利用mknod創(chuàng)建設(shè)備文件。
(5)應(yīng)用層即可以用open來打開設(shè)備文件來進(jìn)行訪問了。
5、總結(jié)
- 可以看出,字符設(shè)備驅(qū)動的核心框架跟設(shè)備模型、平臺設(shè)備驅(qū)動沒有直接關(guān)系,不用他們也一樣能夠正常工作。
三、(總線)設(shè)備驅(qū)動模型
? ? 我們主要談及設(shè)備驅(qū)動模型在linux驅(qū)動中的作用和角色,有關(guān)設(shè)備模型的原理和實現(xiàn)我們另文再述。1、(總線)設(shè)備驅(qū)動模型的作用
(1)設(shè)備驅(qū)動模型實現(xiàn)uevent機(jī)制,調(diào)用應(yīng)用層的medv來自動創(chuàng)建設(shè)備文件。這在上面已經(jīng)論述過。
(2)設(shè)備驅(qū)動模型通過sysfs文件系統(tǒng)向用戶層提供設(shè)備驅(qū)動視圖,如下。
?
- 上圖只是可視化的一種表達(dá),有助于大家去理解設(shè)備模型,類似于windows的設(shè)備管理程序;
- 在嵌入式linux里面并沒有相關(guān)應(yīng)用通過圖形的方式來展現(xiàn)這種關(guān)系。但是用戶可以通過命令窗口利用ls命名逐級訪問/sys文件夾來獲得各種總線、設(shè)備、驅(qū)動的信息和關(guān)系。可以看出,在/sys頂級目錄,有三個關(guān)鍵的子目錄,就是設(shè)備類、設(shè)備和總線。
- 設(shè)備是具體的一個個設(shè)備,在/sys/devices/是創(chuàng)建了實際的文件節(jié)點。而其他目錄,如設(shè)備類和總線以下的子目錄中出現(xiàn)的設(shè)備,都是用符號鏈接指向/sys/devices/目錄下的文件。
- 設(shè)備類是對/sys/devices/下的各種設(shè)備進(jìn)行歸類,以體現(xiàn)一類設(shè)備的公共屬性,如鼠標(biāo)和觸摸屏都是屬于input設(shè)備類。
- 總線目錄是總線、設(shè)備、驅(qū)動模型的核心目錄。因為設(shè)備和驅(qū)動都是依附在某種總線上的,如USB、PCI和平臺總線等。設(shè)備和驅(qū)動正是依靠總線的管理功能才能找到對方,如設(shè)備注冊到總線時去尋找驅(qū)動,而驅(qū)動注冊的時候去尋找其能夠支持的設(shè)備。
- 最重要的是,如果沒有設(shè)備模型,那應(yīng)用層很難知曉驅(qū)動和設(shè)備的關(guān)系,因為字符設(shè)備驅(qū)動并沒有提供這些信息,那對于設(shè)備驅(qū)動的管理者而言會非常麻煩。事實上,內(nèi)核中的總線class、設(shè)備device和驅(qū)動device_driver都不會將所有的信息暴露給用戶層,例如這三個數(shù)據(jù)結(jié)構(gòu)都有對應(yīng)的private數(shù)據(jù)結(jié)構(gòu),它用于內(nèi)核對上下級總線設(shè)備驅(qū)動的鏈表關(guān)系維護(hù)。如果暴露給用戶層,那容易被用戶層修改而使系統(tǒng)混亂。實際上,用戶層只關(guān)心三者的視圖關(guān)聯(lián),至于他們的關(guān)聯(lián)在底層怎么實現(xiàn)則不需要關(guān)心。
(3)設(shè)備驅(qū)動模型提供統(tǒng)一的電源管理機(jī)制。
- 很明顯,我們在字符設(shè)備驅(qū)動的file_operations接口中并沒有看到電源管理方面的接口。但對于操作系統(tǒng)來說,電源功耗管理必不可少。
- 電源管理其實不應(yīng)該由應(yīng)用開發(fā)人員來負(fù)責(zé),而是應(yīng)該由系統(tǒng)來負(fù)責(zé),例如手機(jī)很久沒有觸摸了,那會進(jìn)入休眠狀態(tài)。
- 這種狀態(tài)的改變應(yīng)該由系統(tǒng)來完成,而各種設(shè)備進(jìn)入睡眠模式也應(yīng)該由系統(tǒng)來完成。因此file_operations不提供電源管理的接口給應(yīng)用程序是合理的。
- 而設(shè)備模型作為系統(tǒng)管理的一種機(jī)制,由它來提供電源管理是非常合理的。
- 如設(shè)備device數(shù)據(jù)結(jié)構(gòu)有struct dev_pm_info power功耗屬性參數(shù),驅(qū)動device_driver數(shù)據(jù)結(jié)構(gòu)有struct dev_pm_ops *pm功耗操作接口。
(4)設(shè)備驅(qū)動模型提供各種對象實例的引用計數(shù),防止對象被應(yīng)用層誤刪。
- 設(shè)備模型的所有數(shù)據(jù)結(jié)構(gòu)均是繼承kobject而來,而kobject就提供基礎(chǔ)的計數(shù)功能。
(5)設(shè)備驅(qū)動模型提供多一種方式給應(yīng)用層,用戶和內(nèi)核可以通過sysfs進(jìn)行交互,如通過修改/sys目錄下設(shè)備的文件內(nèi)容,即可以直接修改設(shè)備對應(yīng)的參數(shù)。
2、設(shè)備驅(qū)動模型的核心接口
bus_register(struct bus_type *bus) 注冊總線 device_add(struct device *dev) 注冊設(shè)備 driver_register(struct device_driver*drv) 注冊驅(qū)動 class_create(owner, name) 創(chuàng)建設(shè)備類 ……
3、設(shè)備驅(qū)動模型和字符設(shè)備驅(qū)動區(qū)別
- 設(shè)備驅(qū)動模型側(cè)重于內(nèi)核對總線、設(shè)備和驅(qū)動的管理,并向應(yīng)用層暴露這些管理的信息,而字符設(shè)備驅(qū)動則側(cè)重于設(shè)備驅(qū)動的功能實現(xiàn)。
四、sysfs文件系統(tǒng)
1、sysfs文件系統(tǒng)和設(shè)備驅(qū)動模型的關(guān)系
Sysfs文件系統(tǒng)是設(shè)備驅(qū)動模型得以向用戶暴露其管理信息的載體。它們之間的關(guān)系如下:
(1)設(shè)備驅(qū)動模型的上下級關(guān)系(如子設(shè)備和所屬父設(shè)備)通過sysfs文件系統(tǒng)的父目錄和子目錄來體現(xiàn)。
(2)設(shè)備驅(qū)動模型的平級關(guān)系(如設(shè)備類管理的設(shè)備和具體的設(shè)備的關(guān)系)則通過sysfs文件系統(tǒng)的目錄符號鏈接來實現(xiàn)。
(3)設(shè)備驅(qū)動模型的屬性(如設(shè)備的參數(shù)和設(shè)備名,設(shè)備號等)則通過sysfs文件系統(tǒng)的文件內(nèi)容來記錄實現(xiàn)。
(4)設(shè)備驅(qū)動模型數(shù)據(jù)結(jié)構(gòu)中的kobject對應(yīng)于sysfs文件系統(tǒng)中的目錄,而數(shù)據(jù)結(jié)構(gòu)中的struct attribute成員則對應(yīng)于sysfs文件系統(tǒng)中的文件。
- 對應(yīng)的意思是指含有kobject的device、device_driver、bus等在向系統(tǒng)注冊時,會(調(diào)用sysfs的create_dir接口來)創(chuàng)建對應(yīng)的目錄;
- 含有struct attribute成員屬性的device、device_driver、bus等在向系統(tǒng)注冊時,會(調(diào)用sysfs的sysfs_create_file接口來)創(chuàng)建文件。
2、sysfs核心接口
sysfs_create_file(struct kobject * kobj,const struct attribute * attr)創(chuàng)建屬性文件 sysfs_create_dir(struct kobject * kobj)創(chuàng)建目錄 int sysfs_open_file(struct inode *inode,struct file *file)打開sysfs文件系統(tǒng)格式的文件 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、屬性文件的讀寫
(1)sysfs_read_file是sysfs文件系統(tǒng)的讀寫入口,但是驅(qū)動需要向系統(tǒng)提供真正的讀寫操作,即struct sysfs_ops數(shù)據(jù)結(jié)構(gòu)中的show和store接口。(2)Sysfs文件系統(tǒng)是基于內(nèi)存的,掉電即消失。sysfs所有的操作接口均是對內(nèi)存中的內(nèi)核數(shù)據(jù)結(jié)構(gòu)進(jìn)行訪問操作。
假如用戶用cat命令去讀取一個屬性文件(如dev)的內(nèi)容,那么會產(chǎn)生以下流程:
- fd=open(“dev”)->vfs_open(“dev”)->sysfs_open(“dev”),獲取該文件的句柄;
- read()->vfs_read()->sysfs_read_file()->sysfs_ops->show(),該show接口即是設(shè)備在注冊時產(chǎn)生屬性文件,并向系統(tǒng)提供該文件的讀接口。而讀接口的實現(xiàn),自然是對該屬性參數(shù)的讀訪問。
(3)/sys掛載了sysfs文件系統(tǒng),因此所有對/sys目錄下的文件或者目錄的操作,都會通過sysfs文件的接口進(jìn)行訪問。
五、平臺設(shè)備驅(qū)動
- “平臺設(shè)備驅(qū)動”中的“平臺”,指的是平臺總線,即platform_bus_type,是linux眾多總線中的一種,如USB總線、PCI總線、I2C總線等等。
- 平臺總線是一種虛擬的總線,專門用來管理SOC上的控制器(如看門狗、LCD、RTC等等),它們都是(CPU的總線上)直接取址訪問設(shè)備的。而USB、PCI等設(shè)備都有通過特定的時序來訪問SOC芯片以外的設(shè)備。
- 平臺設(shè)備驅(qū)動是總線設(shè)備驅(qū)動模型上的一個子集。將平臺視為一種總線的概念,那兩者的關(guān)系就會容易理解。
1、平臺設(shè)備驅(qū)動和設(shè)備驅(qū)動模型的關(guān)系
(1)在設(shè)備驅(qū)動模型視圖上,平臺設(shè)備驅(qū)動接口創(chuàng)建了相關(guān)的平臺設(shè)備類(/sys/class/platform_bus)、平臺總線(/sys/bus/platform)、平臺設(shè)備(/sys/devices/)。
(2)平臺設(shè)備(platform_device)、平臺設(shè)備驅(qū)動(platform_driver)均注冊到平臺總線上,即在/sys/bus/platform/目錄下創(chuàng)建相應(yīng)的設(shè)備和驅(qū)動目錄。而平臺總線負(fù)責(zé)匹配注冊到其上面的設(shè)備和驅(qū)動,匹配成功后會調(diào)用驅(qū)動的probe接口。
(3)平臺設(shè)備驅(qū)動,利用設(shè)備驅(qū)動模型接口,來輔助創(chuàng)建對應(yīng)的設(shè)備文件(位于/dev/目錄下)。
(4)相關(guān)的接口包括
platform_device_register(struct platform_device *pdev) 注冊平臺設(shè)備 platform_driver_register(struct platform_driver *drv) 注冊平臺設(shè)備驅(qū)動
兩個接口的實現(xiàn)里面都會對平臺驅(qū)動和設(shè)備進(jìn)行匹配,匹配成功會調(diào)用驅(qū)動的probe接口。
2. 平臺設(shè)備驅(qū)動和字符設(shè)備驅(qū)動的關(guān)系
我們假設(shè)這個平臺設(shè)備是字符設(shè)備。- 平臺設(shè)備驅(qū)動和字符設(shè)備驅(qū)動的關(guān)系始于驅(qū)動的probe接口,即在probe接口中實現(xiàn)字符設(shè)備驅(qū)動所要完成的任務(wù),即通過alloc_chrdev_region申請設(shè)備號,和通過cdev_add注冊驅(qū)動的struct file_opertions。
- 另外為了自動創(chuàng)建應(yīng)用層訪問的設(shè)備文件,還要調(diào)用class_create和device_create接口在平臺設(shè)備類下創(chuàng)建對應(yīng)的設(shè)備類和設(shè)備,并發(fā)出uevent事件,調(diào)用mdev來創(chuàng)建設(shè)備文件。
3、平臺設(shè)備驅(qū)動的開發(fā)流程
(1)將字符設(shè)備驅(qū)動的char_init函數(shù)的實現(xiàn)搬到platform_driver的probe接口中。(2)在char_init中調(diào)用platform_device_register和platform_driver_register分別注冊設(shè)備和驅(qū)動。
- 其實對于移植好的系統(tǒng),platform_device_register是在linux啟動的過程中完成的。
- 因此char_init一般只有platform_driver_register注冊驅(qū)動。
詳細(xì)的平臺設(shè)備驅(qū)動的實現(xiàn)原理和開發(fā)流程另文再述。本次的重點是為了闡述字符設(shè)備驅(qū)動、設(shè)備驅(qū)動模型、sysfs和平臺設(shè)備驅(qū)動之間的關(guān)系。
總結(jié)
以上是生活随笔為你收集整理的设备驱动,字符设备驱动、(总线)设备驱动模型、sysfs文件系统、平台设备驱动的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Keras中文文档】Layer Con
- 下一篇: 基于Java的学生管理系统