深入淺出:Linux裝置驅動之字元裝置驅動
一、linux系統將裝置分為3類:字元裝置、塊裝置、網路裝置。使用驅動程式:
- 字元裝置:是指只能一個位元組一個位元組讀寫的裝置,不能隨機讀取裝置記憶體中的某一資料,讀取資料需要按照先後資料。字元裝置是面向流的裝置,常見的字元裝置有滑鼠、鍵盤、串列埠、控制檯和LED裝置等。
- 塊裝置:是指可以從裝置的任意位置讀取一定長度資料的裝置。塊裝置包括硬碟、磁碟、U盤和SD卡等。
每一個字元裝置或塊裝置都在/dev目錄下對應一個裝置檔案。linux使用者程式通過裝置檔案(或稱裝置節點)來使用驅動程式操作字元裝置和塊裝置。
二、字元裝置、字元裝置驅動與使用者空間訪問該裝置的程式三者之間的關係。
如圖,在Linux核心中使用cdev結構體來描述字元裝置,通過其成員dev_t來定義裝置號(分為主、次裝置號)以確定字元裝置的唯一性。通過其成員file_operations來定義字元裝置驅動提供給VFS的介面函式,如常見的open()、read()、write()等。
在Linux字元裝置驅動中,模組載入函式通過register_chrdev_region( ) 或alloc_chrdev_region( )來靜態或者動態獲取裝置號,通過cdev_init( )建立cdev與file_operations之間的連線,通過cdev_add( )向系統新增一個cdev以完成註冊。模組解除安裝函式通過cdev_del( )來登出cdev,通過unregister_chrdev_region( )來釋放裝置號。
使用者空間訪問該裝置的程式通過Linux系統呼叫,如open( )、read( )、write( ),來“呼叫”file_operations來定義字元裝置驅動提供給VFS的介面函式。
三、字元裝置驅動模型
1. 驅動初始化
1.1. 分配cdev
在2.6的核心中使用cdev結構體來描述字元裝置,在驅動中分配cdev,主要是分配一個cdev結構體與申請裝置號,以按鍵驅動為例:
1
/*……*/
2
/*
分配cdev*/
3
struct
cdev btn_cdev;
4
/*……*/
5
/*
1.1 申請裝置號*/
6
if (major){
7
//靜態
8
dev_id = MKDEV(major, 0);
9
register_chrdev_region(dev_id, 1, "button" );
10
} else
{
11
//動態
12
alloc_chardev_region(&dev_id, 0, 1, "button" );
13
major = MAJOR(dev_id);
14
}
15
/*……*/
|
從上面的程式碼可以看出,申請裝置號有動靜之分,其實裝置號還有主次之分。
在Linux中以主裝置號用來標識與裝置檔案相連的驅動程式。次編號被驅動程式用來辨別操作的是哪個裝置。cdev 結構體的 dev_t 成員定義了裝置號,為 32 位,其中高 12 位為主裝置號,低20 位為次裝置號。
裝置號的獲得與生成:
獲得:主裝置號:MAJOR(dev_t dev);
次裝置號:MINOR(dev_t dev);
生成:MKDEV(int major,int minor);
裝置號申請的動靜之分:
靜態:
1 2 |
1
int
register_chrdev_region(dev_t from, unsigned count, const
char
*name);
2
/*功能:申請使用從from開始的count
個裝置號(主裝置號不變,次裝置號增加)*/
|
靜態申請相對較簡單,但是一旦驅動被廣泛使用,這個隨機選定的主裝置號可能會導致裝置號衝突,而使驅動程式無法註冊。
動態:
1 2 |
1
int
alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const
char
*name);
2
/*功能:請求核心動態分配count個裝置號,且次裝置號從baseminor開始。*/
|
動態申請簡單,易於驅動推廣,但是無法在安裝驅動前建立裝置檔案(因為安裝前還沒有分配到主裝置號)。
1.2. 初始化cdev
1 2 |
|