主設備號和次設備號
對字符設備的訪問是通過文件系統內的設備名稱進行的,這些名稱被稱為特殊文件、設備文件、或者簡單稱之為文件系統樹的節點,它們通常位於/dev目錄。字符設備驅動程序的設備文件可以通過ls -l命令輸出的第一列中的c字符來識別,塊設備也出現在/dev下,但它們由字符b來標識;
通過執行ls -l 命令,可以看到在修改日期之前,有兩個用逗號分隔的數字,分別為主設備號和次設備號。
主設備號標識設備對應的驅動程序;linux設備內核允許多個驅動程序共享主設備號;
次設備號由內核使用,用用戶正確確定設備文件所指的設備。依賴驅動程序的編寫方式,我們可以通過次設備號獲得一個指向內核設備的直接指針,也可以將次設備號當做設備本地數組的索引。不管哪種方式,除了知道次設備號用來指向驅動程序所實現的設備之外,內核本身不關心關於此設備號的其他信息;
crw-rw----. 1 root video 10, 175 Nov 25 17:09 agpgart crw-------. 1 root root 10, 235 Nov 25 17:09 autofs drwxr-xr-x. 2 root root 140 Nov 25 17:09 block drwxr-xr-x. 2 root root 80 Nov 25 17:09 bsg crw-------. 1 root root 10, 234 Nov 25 17:09 btrfs-control drwxr-xr-x. 3 root root 60 Nov 25 17:09 bus lrwxrwxrwx. 1 root root 3 Nov 25 17:09 cdrom -> sr0 drwxr-xr-x. 2 root root 3120 Nov 25 17:10 char crw-------. 1 root root 5, 1 Nov 25 17:09 console lrwxrwxrwx. 1 root root 11 Nov 25 17:09 core -> /proc/kcore drwxr-xr-x. 6 root root 120 Nov 25 17:09 cpu crw-------. 1 root root 10, 62 Nov 25 17:09 cpu_dma_latency crw-------. 1 root root 10, 203 Nov 25 17:09 cuse drwxr-xr-x. 5 root root 100 Nov 25 17:09 disk crw-rw----+ 1 root audio 14, 9 Nov 25 17:09 dmmidi
設備編號的內部表達
內核中,dev_t類型(位於<linux/types.h>)用來保存設備編號,包括主設備號和次設備號;
1 typedef __u32 __kernel_dev_t; 2 3 typedef __kernel_dev_t dev_t;
可見dev_t類型是個32位的無符號整數,其中12位用來表示主設備號,其余20位表示次設備號;我們不應該對設備編號的組織做假定,而應該始終使用如下兩個宏(位於linux/kdev_t.h)來獲取主次設備號;
1 #define MINORBITS 20 2 #define MINORMASK ((1U << MINORBITS) - 1) 3 4 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) 5 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
如果需要將主次設備號轉換成dev_t類型,則使用下面宏(位於linux/kdev_t.h):
1 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
分配和釋放設備編號
指定范圍分配編號:
在建立一個字符設備之前,我們的驅動程序首先要做的事情就是獲取一個或者多個設備編號,完成該工作的必要函數是register_chrdev_region,該函數在<linux/fs.h>中聲明:
1 int register_chrdev_region(dev_t from, unsigned count, const char *name)
參數from標識要分配的設備號范圍的起始值,次設備號經常設置為0,但對該函數不是必須的;
參數count標識分配設備號的個數,如果count非常大,則所請求的范圍可能和下一個主設備號重疊,但是只要我們所請求的編號范圍是可用的,則不會帶來問題;
參數name是和該編號范圍關聯的設備名稱,它將出現在/proc/devices和sysfs中;
動態分配設備編號:
在不知道要分配的設備號的時候,可以使用動態分配的方式,函數如下:
1 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
參數dev是僅用於輸出的參數,在成功完成調用后將保存已分配范圍的第一個編號;
參數baseminor是要使用的被請求的第一個次設備號,通常是0;
參數count標識分配設備號的個數;
參數name是和該編號范圍關聯的設備名稱;
始終使用動態分配設備編號:
對於新的驅動程序,強烈建議不要隨便選擇一個當前設備未使用的設備號作為主設備號,而應該使用動態分配機制獲取主設備號;驅動程序應該使用使用alloc_chrdev_region而不是register_chrdev_region函數;
動態分配的缺點:由於分配的主設備號不能始終保持一致,素以無法預先創建設備節點;對於驅動程序的一般做法是,為了加載一個使用動態主設備號的設備驅動程序,對insmod的調用可替換成一個簡單的腳本,該腳本在調用insmod之后讀取/proc/devices以獲得新分配的主設備號,然后創建對應的設備文件;
釋放設備編號:
在不使用設備編號的時候需要進行釋放,函數如下:
1 void unregister_chrdev_region(dev_t from, unsigned count)