核心中所有已分配的字元裝置編號都記錄在乙個名為 chrdevs 雜湊表裡。該雜湊表中的每乙個元素是乙個 char_device_struct 結構,它的定義如下:
static struct char_device_struct *chrdevs[chrdev_major_hash_size];
注意,核心並不是為每乙個字元裝置編號定義乙個 char_device_struct 結構,而是為一組對應同乙個字元裝置驅動的裝置編號範圍定義乙個 char_device_struct 結構。chrdevs 雜湊表的大小是 255,雜湊演算法是把每組字元裝置編號範圍的主裝置號以 255 取模插入相應的雜湊桶中。同乙個雜湊桶中的字元裝置編號範圍是按起始次裝置號遞增排序的。
註冊
核心提供了三個函式來註冊一組字元裝置編號,這三個函式分別是 register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()。這三個函式都會呼叫乙個共用的 __register_chrdev_region() 函式來註冊一組裝置編號範圍(即乙個 char_device_struct 結構)。所以下面先來看一下 __register_chrdev_region() 函式的實現**。
函式 __register_chrdev_region() 主要執行以下步驟:
1. 分配乙個新的 char_device_struct 結構,並用 0 填充。
2. 如果申請的裝置編號範圍的主裝置號為 0,那麼表示裝置驅動程式請求動態分配乙個主裝置號。動態分配主裝置號的原則是從雜湊表的最後乙個桶向前尋找,那個桶是空的,主裝置號就是相應雜湊桶的序號。所以動態分配的主裝置號總是小於 256,如果每個桶都有字元裝置編號了,那動態分配就會失敗。
3. 根據引數設定 char_device_struct 結構中的初始裝置號,範圍大小及裝置驅動名稱。
4. 計算出主裝置號所對應的雜湊桶,為新的 char_device_struct 結構尋找正確的位置。同時,如果裝置編號範圍有重複的話,則出錯返回。
5. 將新的 char_device_struct 結構插入雜湊表中,並返回 char_device_struct 結構的位址。
分析完 __register_chrdev_region() 後,我們來乙個個看那三個註冊函式。首先是 register_chrdev_region()。
int register_chrdev_region(dev_t from, unsigned count, const char *name)
return 0;
fail:
to = n;
for (n = from; n < to; n = next)
return ptr_err(cd);
}register_chrdev_region() 函式用於分配指定的裝置編號範圍。如果申請的裝置編號範圍跨越了主裝置號,它會把分配範圍內的編號按主裝置號分割成較小的子範圍,並在每個子範圍上呼叫 __register_chrdev_region() 。如果其中有一次分配失敗的話,那會把之前成功分配的都全部退回。
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
alloc_chrdev_region() 函式用於動態申請裝置編號範圍,這個函式好像並沒有檢查範圍過大的情況,不過動態分配總是找個空的雜湊桶,所以問題也不大。通過指標引數返回實際獲得的起始裝置編號。
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
最後乙個 register_chrdev() 是乙個老式分配裝置編號範圍的函式。它分配乙個單獨主裝置號和 0 ~ 255 的次裝置號範圍。如果申請的主裝置號為 0 則動態分配乙個。該函式還需傳入乙個 file_operations 結構的指標,函式內部自動分配了乙個新的 cdev 結構。關於這些,在後續講字元裝置驅動的註冊時會說明。
登出
和註冊分配字元裝置編號範圍類似,核心提供了兩個登出字元裝置編號範圍的函式,分別是 unregister_chrdev_region() 和 unregister_chrdev() 。它們都呼叫了 __unregister_chrdev_region() 函式。由於比較簡單,就不加說明了,只把**貼出來。
static struct char_device_struct * __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
mutex_unlock(&chrdevs_lock);
return cd;
}void unregister_chrdev_region(dev_t from, unsigned count)
}void unregister_chrdev(unsigned int major, const char *name)
注意, unregister_chrdev() 會把 register_chrdev() 中自動分配的 cdev 給登出掉。
字元裝置的註冊
核心中每個字元裝置都對應乙個 cdev 結構的變數,下面是它的定義 struct cdev 乙個 cdev 一般它有兩種定義初始化方式 靜態的和動態的。靜態記憶體定義初始化 struct cdev my cdev cdev init my cdev,fops my cdev.owner this m...
字元裝置的註冊
前面提到,核心內部使用struct cdev結構來表示字元裝置。在核心呼叫裝置的操作之前,必須分配並註冊乙個或者多個上述結構。在 linux cdev.h 中定義了這個結構以及與其相關的一些輔助函式。如果需要動態的初始化,應該編寫如下 struct cdev my cdev cdev alloc m...
Linux字元裝置驅動的註冊
很多學習linux程式設計的新人都會被字元裝置註冊搞糊塗了,我剛開始也一樣糊里糊塗的,看到網上例程有各種版本,就是呼叫module init時傳遞的實參,先記為 init 大家可能還會看到雜項裝置驅動misc register 平台裝置驅動platform device register 但是作為l...