一.概述:
1.在Linux中有一句哲學“Linux下皆檔案”。
裝置驅動程式為應用程式屏蔽了硬體的細節,這樣在應用程式看來,硬體裝置只是一個裝置檔案,應用程式可以像操作普通檔案一樣對硬體裝置進行操作。
但是裝置檔案和普通檔案還是又差別的。
那麼裝置和普通檔案之間又有什麼區分呢?
先看看兩個圖:
普通檔案:
-rw-r--r-- 1 stella stella 3699 2011-05-10 16:02 my_USBTMCAPP.c
-rwxr-xr-x 1 stella stella 8763 2011-05-08 11:27 tiger
-rw-r--r-- 1 stella stella 441 2011-05-08 11:27 tiger.c
裝置檔案:
crw------- 1 root root 252, 4 2011-05-11 16:42 usbmon4
crw------- 1 root root 252, 5 2011-05-11 16:42 usbmon5
crw-rw---- 1 root tty 7, 0 2011-05-11 16:42 vcs
crw-rw---- 1 root tty 7, 1 2011-05-11 16:42 vcs1
1>存取權限之前的字母是b或c,分別表示塊裝置和字元裝置。
2>裝置檔案沒有檔案長度,而增加了另外的兩個值,分別是主裝置號和從裝置號。二者共同形成一個唯一的號碼,核心可由此尋找對應的裝置驅動程式。
3>之所以給裝置檔案分配名稱,是因為使用者更容易記憶符號名而不是數字,但名稱無法表示裝置檔案的實際功能,這主要是通過主從裝置號表示一個裝置的,裝置檔案所處的目錄也與其功能不相干。
儘管如此,命名裝置檔案時仍然採用了一中標準方法:
mknod 用於建立裝置檔案。
2.核心採用主從裝置號來標識匹配的驅動程式
為什麼要採用兩個號碼來標識驅動程式呢?
1>首先,系統可能包含幾個同樣類型的裝置,由同一個裝置驅動程式管理(將同樣的代碼多次載入到核心也沒有意義)
2>其次,可以將同類裝置合并起來,便於插入到核心的資料結構中進行管理
3.Linux裝置驅動程式的分類:
1>Linux裝置驅動程式分為字元裝置驅動(無緩衝且只能順序存取),塊裝置驅動程式(有緩衝且可以隨機存取)。每個字元裝置和塊裝置都必須有主次裝置號,主裝置號相同的裝置是同類裝置(使用同一驅動程式)
(1)塊裝置:系統中能夠隨機(不需要按順序)訪問固定大小資料片(chunk)的裝置被稱作塊裝置;他們都是以安裝檔案系統的方式使用的
(2)字元裝置:字元裝置按照字元流動的方式被有序訪問
2>這些裝置中,有些裝置是對實際物理硬體的抽象,而有些裝置則是核心自身提供的功能(不依賴於特定的物理硬體,又稱為“虛擬設備”)
(1)每個裝置在/dev目錄下都有一個對應的檔案(節點)
可以用下面的命令進行查看
cd /dev
ls -al
日期的前兩列給出了對應裝置的主裝置號和次裝置號
(2)可以通過cat /proc/devices命令查看當前已經載入的裝置驅動程式的主裝置號,第一列為主裝置號,第二列為裝置名稱
3>塊裝置和字元裝置的主裝置號可能是相同的。因此,除非同時指定裝置號和裝置類型(塊裝置/字元裝置),否則找到的驅動程式可能不是唯一的
4.主從裝置號
1>在核心中,dev_t類型用來儲存裝置編號(包括主裝置號和次裝置號),dev_t是一個32位的數,12位表示主裝置號,20為表示次裝置號
(1)主裝置號 = MAJOR(dev_t dev)
(2)次裝置號 = MINOR(dev_t dev)
(3)裝置編號 = MKDEV(int major,int minor)
2>主裝置號是與驅動對應的概念,同一類裝置一般使用相同的主裝置號,不同類的裝置一般使用不同的主裝置號。因為同一驅動可支援多個同類裝置,因此用次裝置號來描述使用該驅動的裝置的序號,序號一般從0開始
5.MKDEV()宏的實現
#define MKDEV(ma,mi) (((ma) << MINORBITS)|(mi))
#define MKDEV(ma,mi) ((ma)<<8 | (mi))
擷取裝置在裝置表中的位置
6.dev_t是無符號長整型號
1>typedef u_long dev_t;
2>typeddef unsigned long u_long;
7.分配和釋放裝置號:
(1)int register_chrdev_region(dev_t first,unsigned int count,char *name);
(2)int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
(3)void unregister_chrdev_region(dev_t first,unsigned int count);
1>register_chardev_region函數用於已知起始裝置號的情況;在靜態申請時,如果主裝置號沒有和系統中某個字元裝置的主裝置號 重複,則此函數肯定會成功返回;如果系統中某個登入的字元裝置的主裝置號與申請的主裝置號相同,則核心還需要檢查這兩個裝置的次裝置號範圍有沒有重合,若有重合部分,則register_chrdev_region返回-EBUSY,表示系統正忙,申請的裝置號已被佔用;若無重合部分,則register_chrdev_region返回0,表示成功
2>alloc_chrdev_region()用於裝置號未知,向系統動態申請裝置號的情況。動態分配裝置號,是指在驅動程式中通過調用此函數,系統將為驅動程式動態分配一個主裝置號,將分配到的主裝置號與參數baseminor組合成一個裝置號,通過輸出參數dev返回給使用者
3>alloc_chrdev_region()比register_chrdev_region()對比的優點在於它會自動避開裝置號重複的衝突,但是當用alloc_chrdev_region動態申請裝置號時,在申請完後,要通過major=MMOR(dev)擷取主裝置號;並且如果使用者使用靜態申請裝置號,只要使用者申請的主裝置號在0~2^12-1之間,次裝置號在0~2^20-1之間,並且裝置號不衝突,核心就會成功註冊該裝置號;而如果使用者使用了動態分配裝置號,核心為使用者指派的主裝置號只會在1~254之間,如果這254個裝置號全部被佔用,則動態分配裝置號會失敗,但是常用的裝置只有三十多個,所以基本上不用擔心動態分配的時候核心會返回失敗
4>在調用cdev_del()函數從系統登出字元裝置之後,unregister_chrdev_region()應該被調用以釋放原先申請的裝置號
-----------------------------------------------------------------------------------------
關於register_chrdev_region()函數系列的具體實現可以參看《Linux字元裝置驅動之register_chrdev_region》
-----------------------------------------------------------------------------------------
8. 註冊字元裝置
在獲得了裝置號範圍之後,需要將裝置添加到字元裝置資料庫中,以啟用裝置。這需要用cdev_init函數初始化一個struct cdev的執行個體,然後調用cdev_add函數
(1)void cdev_init(struct cdev *cdev,struct file_operation *fops);
(2)int cdev_add(struct cdev *dev,dev_t num,unsigned int count)
(3)void cdev_del(stuct cdev *dev)
1> cdev_init()函數用於初始化cdev的成員,並建立cdev和file_operations之間的串連
2>cdev_add()函數和cdev_del()函數分別向系統添加和刪除一個cdev,完成字元裝置的註冊和登出。對cdev_add()的調用通常發生在字元裝置驅動模組載入函數中,而對cdev_del()函數的調用則通常發生在字元裝置驅動模組卸載函數中。
3>先要申請裝置號,才能註冊字元裝置,順序不能亂
-----------------------------------------------------------------------------------------
關於cdev_init ()函數系列的具體實現可以參看《Linux字元裝置驅動之cdev_init ()》
-----------------------------------------------------------------------------------------
9.在核心空間和使用者空間之間拷貝資料使用
(1)unsigned long copy_to_user(void _user* to,const void *from,unsigned long count);
(2)unsigned long copy_from_user(void *to,const void __user *from,unsigned long count);
由於核心空間與使用者空間的記憶體不能直接互訪問,因此藉助函數copy_from_user()完成使用者空間到核心空間的複製。函數copy_to_user()完成核心空間到使用者空間的複製
轉載自:http://blog.csdn.net/tigerjb/article/details/6412438