1.內核中所有已分配的字符設備編號都記錄在一個名為 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];
1>內核並不是為每一個字符設備編號定義一個 char_device_struct 結構,而是為一組對應同一個字符設備驅動的設備編號范圍定義一個 char_device_struct 結構。chrdevs 散列表的大小是 255,散列算法是把每組字符設備編號范圍的主設備號以 255 取模插入相應的散列桶中。同一個散列桶中的字符設備編號范圍是按起始次設備號遞增排序的。
2.注冊
內核提供了三個函數來注冊一組字符設備編號,這三個函數分別是 register_chrdev_region()、alloc_chrdev_region() 和
register_chrdev()。這三個函數都會調用一個共用的
__register_chrdev_region() 函數來注冊一組設備編號范圍(即一個 char_device_struct 結構)。
1>int register_chrdev_region(dev_t from, unsigned count, const char *name)
from :要分配的設備編號范圍的初始值(次設備號常設為0);
Count:連續編號范圍.
name:編號相關聯的設備名稱. (/proc/devices);
2>動態分配:
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
firstminor是請求的最小的次編號;
count是請求的連續設備編號的總數;
name為設備名,返回值小於0表示分配失敗。
然後通過major=MMOR(dev)獲取主設備號
3>釋放:
Void unregist_chrdev_region(dev_t first,unsigned int count);
調用Documentation/devices.txt中能夠找到已分配的設備號.
3.__register_chrdev_region() 函數的實現代碼
/*
84 * Register a single major with a specified minor range.
85 *
86 * If major == 0 this functions will dynamically allocate a major and return
87 * its number.
88 *
89 * If major > 0 this function will attempt to reserve the passed range of
90 * minors and will return zero on success.
91 *
92 * Returns a -ve errno on failure.
93 */
94static struct char_device_struct *
95__register_chrdev_region(unsigned int major, unsigned int baseminor,
96 int minorct, const char *name)
97{
98 struct char_device_struct *cd, **cp;
99 int ret = 0;
100 int i;
101
102 cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
103 if (cd == NULL)
104 return ERR_PTR(-ENOMEM);
105
106 mutex_lock(&chrdevs_lock);
107
108 /* temporary */
109 if (major == 0) {
110 for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
111 if (chrdevs[i] == NULL)
112 break;
113 }
114
115 if (i == 0) {
116 ret = -EBUSY;
117 goto out;
118 }
119 major = i;
120 ret = major;
121 }
122
123 cd->major = major;
124 cd->baseminor = baseminor;
125 cd->minorct = minorct;
126 strlcpy(cd->name, name, sizeof(cd->name));
127
128 i = major_to_index(major);
129
130 for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
131 if ((*cp)->major > major ||
132 ((*cp)->major == major &&
133 (((*cp)->baseminor >= baseminor) ||
134 ((*cp)->baseminor + (*cp)->minorct > baseminor))))
135 break;
136
137 /* Check for overlapping minor ranges. */
138 if (*cp && (*cp)->major == major) {
139 int old_min = (*cp)->baseminor;
140 int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
141 int new_min = baseminor;
142 int new_max = baseminor + minorct - 1;
143
144 /* New driver overlaps from the left. */
145 if (new_max >= old_min && new_max <= old_max) {
146 ret = -EBUSY;
147 goto out;
148 }
149
150 /* New driver overlaps from the right. */
151 if (new_min <= old_max && new_min >= old_min) {
152 ret = -EBUSY;
153 goto out;
154 }
155 }
156
157 cd->next = *cp;
158 *cp = cd;
159 mutex_unlock(&chrdevs_lock);
160 return cd;
161out:
162 mutex_unlock(&chrdevs_lock);
163 kfree(cd);
164 return ERR_PTR(ret);
165}