轉載http://blog.sina.com.cn/s/blog_620b71230100f71g.html 字元裝置 register_chrdev_region()、alloc_chrdev_region
核心中所有已指派的字元裝置編號都記錄在一個名為 chrdevs 散列表裡。該散列表中的每一個元素是一個 char_device_struct 結構,它的定義如下: static struct char_device_struct { struct char_device_struct *next; // 指向散列衝突鏈表中的下一個元素的指標 unsigned int major; // 主裝置號 unsigned int baseminor; // 起始次裝置號 int minorct; // 裝置編號的範圍大小 char name[64]; // 處理該裝置編號範圍內的裝置驅動的名稱 struct file_operations *fops; // 沒有使用 struct cdev *cdev; // 指向字元裝置驅動程式描述符的指標 } *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(dev_t first,unsigned int count,char *name) First :要分配的裝置編號範圍的初始值(次裝置號常設為0); Count:連續編號範圍. Name:編號相關聯的裝置名稱. (/proc/devices); 動態分配: Int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name); Firstminor : 通常為0; *dev:存放返回的裝置號; 釋放: Void unregist_chrdev_region(dev_t first,unsigned int count); 調用Documentation/devices.txt中能夠找到已指派的裝置號. 所以下面先來看一下 __register_chrdev_region() 函數的實現代碼。 static struct char_device_struct * __register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name) { struct char_device_struct *cd, **cp; int ret = 0; int i; cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL); if (cd == NULL) return ERR_PTR(-ENOMEM); mutex_lock(&chrdevs_lock); if (major == 0) { for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) if (chrdevs[i] == NULL) break; if (i == 0) { ret = -EBUSY; goto out; } major = i; ret = major; } cd->major = major; cd->baseminor = baseminor; cd->minorct = minorct; strncpy(cd->name,name, 64); i = major_to_index(major); for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major > major || ((*cp)->major == major && ( ((*cp)->baseminor >= baseminor) || ((*cp)->baseminor + (*cp)->minorct > baseminor)) )) break; if (*cp && (*cp)->major == major) { int old_min = (*cp)->baseminor; int old_max = (*cp)->baseminor + (*cp)->minorct - 1; int new_min = baseminor; int new_max = baseminor + minorct - 1;
if (new_max >= old_min && new_max <= old_max) { ret = -EBUSY; goto out; }
if (new_min <= old_max && new_min >= old_min) { ret = -EBUSY; goto out; } }
cd->next = *cp; *cp = cd; mutex_unlock(&chrdevs_lock); return cd; out: mutex_unlock(&chrdevs_lock); kfree(cd); return ERR_PTR(ret); } 函數 __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()。 register_chrdev_region() 函數用於分配指定的裝置編號範圍。如果申請的裝置編號範圍跨越了主裝置號,它會把分配範圍內的編號按主裝置號分割成較小的子範圍,並在每個子範圍上調用 __register_chrdev_region() 。如果其中有一次分配失敗的話,那會把之前成功分配的都全部退回。 alloc_chrdev_region() 函數用於動態申請裝置編號範圍,這個函數好像並沒有檢查範圍過大的情況,不過動態分配總是找個空的散列桶,所以問題也不大。通過指標參數返回實際獲得的起始裝置編號。 最後一個 register_chrdev() 是一個老式分配裝置編號範圍的函數。它分配一個單獨主裝置號和 0 ~ 255 的次裝置號範圍。如果申請的主裝置號為 0 則動態分配一個。該函數還需傳入一個 file_operations 結構的指標,函數內部自動分配了一個新的 cdev 結構。關於這些,在後續講字元裝置驅動的註冊時會說明。 登出 和註冊分配字元裝置編號範圍類似,核心提供了兩個登出字元裝置編號範圍的函數,分別是 unregister_chrdev_region() 和 unregister_chrdev() 。它們都調用了 __unregister_chrdev_region() 函數。 |