Linux設(shè)備驅(qū)動(dòng)概述
- 操作系統(tǒng)內(nèi)核是通過(guò)各種驅(qū)動(dòng)程序來(lái)駕馭硬件設(shè)備,它為用戶屏蔽了各種各樣的設(shè)備。
- 設(shè)備驅(qū)動(dòng)程序是操作系統(tǒng)內(nèi)核和機(jī)器硬件之間的接口,系統(tǒng)調(diào)用是操作系統(tǒng)內(nèi)核和應(yīng)用程序之間的接口。
- 在應(yīng)用程序看來(lái),硬件設(shè)備只是一個(gè)設(shè)備文件, 應(yīng)用程序可以象操作普通文件一樣對(duì)硬件設(shè)備進(jìn)行操作.
Linux下設(shè)備可以分為三種:
- 字符設(shè)備:數(shù)據(jù)的傳輸是以字節(jié)流的形式傳輸,如鍵盤(pán)、鼠標(biāo)、觸摸屏、攝像頭,LCD顯示屏等等。
- 塊設(shè)備:數(shù)據(jù)是以塊為單位傳輸?shù)?。如硬盤(pán)、U盤(pán)等存儲(chǔ)設(shè)備。
- 網(wǎng)絡(luò)設(shè)備:網(wǎng)絡(luò)是linux內(nèi)核的一大功能模塊,網(wǎng)絡(luò)設(shè)備在內(nèi)核總獨(dú)立成為一類設(shè)備。提供專用API(socket編程)。
Linux系統(tǒng)中,應(yīng)用程序訪問(wèn)外設(shè)是通過(guò)文件的形式來(lái)進(jìn)行的,Linux將所有的外設(shè)都看做文件,統(tǒng)一存放在/dev目錄下。
應(yīng)用程序使用內(nèi)核提供的標(biāo)準(zhǔn)系統(tǒng)調(diào)用來(lái)與內(nèi)核中的驅(qū)動(dòng)程序進(jìn)行通訊,這些系統(tǒng)調(diào)用有:
open(), read(), write(), ioctl(), close() 等等。
linux如何管理文件
- Linux把設(shè)備納入文件系統(tǒng)的范疇來(lái)管理。
- 每個(gè)設(shè)備在Linux系統(tǒng)上看起來(lái)都像一個(gè)文件,它們存放在/dev目錄中,稱為"設(shè)備節(jié)點(diǎn)"。
-
每當(dāng)用戶程序要訪問(wèn)某個(gè)設(shè)備時(shí),通過(guò)系統(tǒng)調(diào)用,內(nèi)核根據(jù)設(shè)備結(jié)點(diǎn)的信息調(diào)用相應(yīng)的驅(qū)動(dòng)程序。當(dāng)驅(qū)動(dòng)程序執(zhí)行完后,又返回至用戶進(jìn)程。
Linux下設(shè)備的屬性
- 設(shè)備的類型:字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備;
- 主設(shè)備號(hào):標(biāo)識(shí)設(shè)備對(duì)應(yīng)的驅(qū)動(dòng)程序。一般“一個(gè)主設(shè)備號(hào)對(duì)應(yīng)一個(gè)驅(qū)動(dòng)程序”
- 次設(shè)備號(hào):每個(gè)驅(qū)動(dòng)程序負(fù)責(zé)管理它所驅(qū)動(dòng)的幾個(gè)硬件實(shí)例,這些硬件實(shí)例則由次設(shè)備號(hào)來(lái)表示。同一驅(qū)動(dòng)下的實(shí)例編號(hào),用于確定設(shè)備文件所指的設(shè)備。
- 文件名:設(shè)備文件名字。
設(shè)備號(hào)
- 設(shè)備文件的主、次設(shè)備號(hào)用于表示一個(gè)硬件設(shè)備;
- 對(duì)于每個(gè)類型的設(shè)備,其主、次設(shè)備號(hào)都有相關(guān)的規(guī)定說(shuō)明;
- 在內(nèi)核源碼里面的Documentation\devices.txt說(shuō)明文件中,就規(guī)定了各種設(shè)備的主、次設(shè)備號(hào)。
一些重要的數(shù)據(jù)結(jié)構(gòu)
- 大部分驅(qū)動(dòng)程序涉及三個(gè)重要的內(nèi)核數(shù)據(jù)結(jié)構(gòu):
文件操作file_operations結(jié)構(gòu)體
- 結(jié)構(gòu)體file_operations在頭文件 linux/fs.h中定義,用來(lái)存儲(chǔ)驅(qū)動(dòng)內(nèi)核模塊提供的對(duì)設(shè)備進(jìn)行各種操作的函數(shù)的指針。
struct file_operations {
struct module *owner;
ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
......
}
file_operations重要的成員
owner : 指向擁有該結(jié)構(gòu)體的模塊的指針,內(nèi)核使用該指針維護(hù)模塊使用計(jì)數(shù)。
llseek : 用來(lái)修改文件的當(dāng)前讀寫(xiě)位置,把新位置作為返回值返回,loff_t是在LINUX中定義的長(zhǎng)偏移量 .
read : 用來(lái)從設(shè)備中讀取數(shù)據(jù)。非負(fù)返回值表示成功讀取的直接數(shù)。
write : 向設(shè)備發(fā)送數(shù)據(jù)。
ioctl : 提供一種執(zhí)行設(shè)備特定命令的方法。
-
unsigned int (*poll) (struct file *, struct poll_table_struct *);
- 系統(tǒng)調(diào)用select和poll的后端實(shí)現(xiàn),用這兩個(gè)系統(tǒng)調(diào)用來(lái)查詢 設(shè)備是否可讀寫(xiě),或是否處于某種狀態(tài)。如果poll為空,則驅(qū)動(dòng)設(shè)備會(huì)被認(rèn)為即可讀又可寫(xiě),返回值是一個(gè)狀態(tài)掩碼。
-
int (*mmap) (struct file *, struct vm_area_struct *);?將設(shè)備內(nèi)存映射到進(jìn)程地址空間
文件對(duì)象file結(jié)構(gòu)體
文件對(duì)象file代表著一個(gè)打開(kāi)的文件。進(jìn)程通過(guò)文件描述符fd與已打開(kāi)文件的file結(jié)構(gòu)相聯(lián)系。
struct file 在<linux/fs.h>中定義。
指向結(jié)構(gòu)體struct file的指針通常命名為filp,或者file。建議使用文件指針filp。
-
文件對(duì)象file結(jié)構(gòu)體的成員
- struct file_operations *f_op;
與文件相關(guān)的操作結(jié)構(gòu)體指針。與文件相關(guān)的操作是在打開(kāi)文件的時(shí)候確定下來(lái)的,也就是確定該指針的值。可在需要的時(shí)候,改變指針?biāo)赶虻奈募僮鹘Y(jié)構(gòu)體。用C語(yǔ)言實(shí)現(xiàn)面向?qū)ο缶幊痰姆椒ㄖ剌d。 -
其他成員可先忽略,后面具體實(shí)例分析。因?yàn)樵O(shè)備驅(qū)動(dòng)模塊并不自己直接填充結(jié)構(gòu)體 file,只是使用file中的數(shù)據(jù)。
索引節(jié)點(diǎn)inode結(jié)構(gòu)體
- struct file_operations *f_op;
文件打開(kāi),在內(nèi)存建立副本后,由唯一的索引節(jié)點(diǎn)inode描述。
-
與file結(jié)構(gòu)不同。
- file結(jié)構(gòu)是進(jìn)程使用的結(jié)構(gòu),進(jìn)程每打開(kāi)一個(gè)文件,就建立一個(gè)file結(jié)構(gòu)。不同的進(jìn)程打開(kāi)同一個(gè)文件,建立不同的file結(jié)構(gòu)。
- inode結(jié)構(gòu)是內(nèi)核使用的結(jié)構(gòu),文件在內(nèi)存建立副本,就建立一個(gè)inode結(jié)構(gòu)來(lái)描述。一個(gè)文件在內(nèi)存里面只有一個(gè)inode結(jié)構(gòu)對(duì)應(yīng)。
inode結(jié)構(gòu)包含大量描述文件信息的成員變量。
但是對(duì)于描述設(shè)備文件的inode,跟設(shè)備驅(qū)動(dòng)有關(guān)的成員只有兩個(gè)。
dev_t i_rdev; 包含真正的設(shè)備號(hào)。
struct file_operations *i_fop;在生成設(shè)備文件的時(shí)候,這個(gè)文件操作成員被賦予一個(gè)默認(rèn)值;
-
從inode中獲得主設(shè)備號(hào)和次設(shè)備號(hào)的宏:
- unsigned int iminor(struct inode *inode);
- unsigned int imajor(struct inode *inode);
struct inode 結(jié)構(gòu)代表一個(gè)實(shí)實(shí)在在文件,每個(gè)文件只對(duì)應(yīng)一個(gè)inode;
struct file 結(jié)構(gòu)代表一個(gè)打開(kāi)的文件,同一個(gè)文件可以對(duì)應(yīng)多個(gè)file結(jié)構(gòu);
struct file_operations結(jié)構(gòu)代表底層操作硬件函數(shù)的集合
怎么注冊(cè)一個(gè)字符設(shè)備
注冊(cè)一個(gè)字符設(shè)備的早期方法:
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
major 是給定的主設(shè)備號(hào)。為0代表自動(dòng)分配設(shè)備號(hào)
name 是驅(qū)動(dòng)的名字(將出現(xiàn)在 /proc/devices),
fops 是設(shè)備驅(qū)動(dòng)的file_operations 結(jié)構(gòu)。
register_chrdev 將給設(shè)備分配 0 - 255 的次設(shè)備號(hào), 并且為每一個(gè)建立一個(gè)缺省的 cdev 結(jié)構(gòu)。從系統(tǒng)中卸載字符設(shè)備的函數(shù):
int unregister_chrdev(unsigned int major, const char *name);-
驅(qū)動(dòng)程序是以內(nèi)核模塊的形式表現(xiàn)的,linux內(nèi)核的模塊機(jī)制是:在插入模塊時(shí),執(zhí)行模塊初始化函數(shù);在卸載模塊時(shí),執(zhí)行模塊卸載函數(shù)。
- 驅(qū)動(dòng)程序就是利用這種機(jī)制,在模塊初始化函數(shù)中,進(jìn)行設(shè)備的設(shè)置、注冊(cè)等。
- 在模塊卸載函數(shù)進(jìn)行設(shè)備的注銷工作。
舉個(gè)簡(jiǎn)單的字符設(shè)備例子
-
編寫(xiě)底層操作函數(shù)--open、release方法:
-
編寫(xiě)底層操作函數(shù)--read、write方法:
-
將底層操作函數(shù)設(shè)置到一個(gè)file_operations結(jié)構(gòu)體變量中
創(chuàng)建一個(gè)結(jié)構(gòu)體變量fops,這個(gè)變量的成員open,release,read,write分別指向?qū)?yīng)的函數(shù)。
-
模塊初始化函數(shù)注冊(cè)設(shè)備;卸載函數(shù)注銷設(shè)備
- 編譯模塊;
- 在ARM板上插入模塊
# insmod first_drv.ko
- 插入模塊之后,可以通過(guò)文件/proc/devices 查看設(shè)備信息
# cat /proc/devices
找到 first_drv的主設(shè)備號(hào)是249,如下圖

- 建立設(shè)備文件
# mknod /dev/first_drv c 249 0
- 建立好設(shè)備文件之后,應(yīng)用程序就可以通過(guò)設(shè)備文件來(lái)訪問(wèn)驅(qū)動(dòng)程序了。
-
應(yīng)用測(cè)試程序如圖:
-
在開(kāi)發(fā)板上執(zhí)行測(cè)試程序的效果如下
簡(jiǎn)單的總結(jié)一下驅(qū)動(dòng)開(kāi)發(fā)的流程

- 字符設(shè)備驅(qū)動(dòng)程序的編寫(xiě)框架是:
- 編寫(xiě)底層硬件的操作函數(shù),將這些函數(shù)集合在一個(gè)file_operations結(jié)構(gòu)中;
- 在模塊的入口函數(shù)中,申請(qǐng)?jiān)O(shè)備號(hào),初始化并注冊(cè)一個(gè)cdev結(jié)構(gòu);
- 在模塊的出口函數(shù)中,注銷cdev結(jié)構(gòu),注銷設(shè)備號(hào);
- 可以通過(guò)文件 /proc/devices 查看設(shè)備信息,找到動(dòng)態(tài)分配的主設(shè)備號(hào)
- 手動(dòng)建立設(shè)備文件通過(guò)mknod命令
進(jìn)階字符設(shè)備寫(xiě)法請(qǐng)看下一篇文章







