Linux設備(bei)驅動申請(qing)設備(bei)號(hao)過(guo)程分析
時間:2017-11-22 來源:未知
今(jin)天我(wo)(wo)們通過內(nei)核中(zhong)的源碼共同(tong)分析一(yi)(yi)下Linux設(she)備驅(qu)動(dong)中(zhong)申請設(she)備號(hao)(hao)(hao)的過程,首(shou)先在Linux內(nei)核為我(wo)(wo)們提供了兩(liang)(liang)種申請設(she)備號(hao)(hao)(hao)的方(fang)式,一(yi)(yi)是指定設(she)備號(hao)(hao)(hao)注冊,二是動(dong)態分配設(she)備號(hao)(hao)(hao);分別用到一(yi)(yi)下兩(liang)(liang)個函數:
register_chrdev_region();
alloc_chrdev_region();
設備號的數據類(lei)型(xing)是(shi)(shi)dev_t類(lei)型(xing),是(shi)(shi)一個無符號長(chang)整型(xing),在32位(wei)操作系統中,它的大小(xiao)是(shi)(shi)4個字節(jie),32位(wei),高12位(wei)用(yong)來存放(fang)主(zhu)設備號,低(di)20位(wei)用(yong)來存放(fang)次設備號。
通(tong)過主設(she)備號和次設(she)備號合(he)成設(she)備號的宏(hong)如下(xia):
MKDEV(major,minor);
從(cong)設(she)備號(hao)中提取(qu)主設(she)備號(hao)和(he)次設(she)備號(hao)的宏如下:
MAJOR(dev);
MINOR(dev);
接下來我們就來分析設備(bei)號注冊過程:
一、自動分配設備號:
1. 調(diao)用內核提供的自動分(fen)配設備號函數,分(fen)配設備號
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
2. alloc_chrdev_region函(han)數(shu)中繼續調用如下(xia)函(han)數(shu),進行進一步申(shen)請
struct char_device_struct *cd = __register_chrdev_region(0, baseminor, count, name);
/*在該函數中進行判斷,如果major==0,執行if內的語句*/
if (major == 0) { //自動(dong)分配時major == 0
/*
*chrdevs是一個結構體指針數(shu)組,見附(fu)錄1-1
*作用是從struct char_device_struct類型的結(jie)構(gou)體(ti)指針數(shu)組(zu)中找到一個空的結(jie)構(gou)體(ti)指針
*/
for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) { // for(i=254;i>0;i--)
if (chrdevs[i] == NULL)
break;
}
if (i == 0) { //i == 0 ,說明,存放設備號的結(jie)構體指針數組已經用完(wan)
ret = -EBUSY;
goto out;
}
major = i; //否則(ze),主設備號 = i
ret = major;
cd->major = major; // 將主設備號賦值給cd->major
cd->baseminor = baseminor; // 將起始次設(she)備號賦值cd->baseminor
cd->minorct = minorct; // 將(jiang)傳入的(de)第三個(ge)參數賦值給程cd->minorct,
// 表示申(shen)請設備(bei)號的個數
strlcpy(cd->name, name, sizeof(cd->name)); //設(she)備名拷貝
/*
*struct char_device_struct *cd, **cp;
*cp存(cun)放的(de)是結構體(ti)指(zhi)針的(de)地址,基于當前的(de)條件,*cp == NULL ,所(suo)以for循(xun)環條件不成立
*/
--------------------------------------不執行(xing)-------------------------------------
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major > major ||((*cp)->major == major &&(((*cp)->baseminor >= baseminor) ||((*cp)->baseminor + (*cp)->minorct > baseminor))))
break;
---------------------------------------------------------------------------------------
/* **cp == NULL ,所以條件不成(cheng)立*/
---------------------------------------不執行-----------------------------------------
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;
/* New driver overlaps from the left. */
if (new_max >= old_min && new_max <= old_max) {
ret = -EBUSY;
goto out;
}
/* New driver overlaps from the right. */
if (new_min <= old_max && new_min >= old_min) {
ret = -EBUSY;
goto out;
}
}
---------------------------------------------------------------------------------------
cd->next = *cp; //cd ->next = NULL
*cp = cd; //*cp[chrdevs[i]] , cd 結構體(ti)在上面賦(fu)值的
mutex_unlock(&chrdevs_lock);
return cd;
回到alloc_chrdev_region函數的(de)*dev = MKDEV(cd->major, cd->baseminor);
實(shi)際上就(jiu)申請了一個主設備號,次設備號
二(er)、用戶(hu)指(zhi)定設備號(hao)注冊(eg:major = 250;count=3)
1. int register_chrdev_region(dev_t from, unsigned count, const char *name);
2. 在(zai)該函數中進行如下(xia)操作:
dev_t to = from + count; // dev_t to = MKDEV(dev_major,dev_minor) +3;
dev_t n, next;
/*如(ru)果申請的設(she)備編(bian)號(hao)范(fan)圍跨越了主設(she)備號(hao),
*它(ta)會把分配范圍內的編號(hao)(hao)按主設備號(hao)(hao)分割
*成較小的子范(fan)圍,并在每(mei)個子范(fan)圍上調用
*__register_chrdev_region() 。如果其中
*有一次分配失敗的(de)話,那會(hui)把之(zhi)前成功分配的(de)都全部(bu)退回*/
// for(n = MKDEV(dev_major,dev_minor); n<to;n=next)循環一(yi)次(next = to)
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0); // next = MKDEV(250+1,0);
if (next > to) // if(MKDEV(251,0)>MKDEV(250,0)+3) [成立(li)]
next = to; // next = MKDEV(250,0)+3
cd = __register_chrdev_region(MAJOR(n), MINOR(n),next - n, name);
//__register_chrdev_region(250, 0,3, "xxx_demo");
{
cd->major = major; //cd->major = 250;
cd->baseminor = baseminor; //cd->baseminor = 0;
cd->minorct = minorct; //cd->minorct = 3;
strlcpy(cd->name, name, sizeof(cd->name));
i = major_to_index(major); //i = 250;
/*for (cp = &chrdevs[250]; *cp; cp = &(*cp)->next)*/
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if((*cp)->major > major || ((*cp)->major == major &&(((*cp)->baseminor >= baseminor) ||((*cp)->baseminor + (*cp)->minorct > baseminor)))) //正常情況下不會成立,即次(ci)設備號范圍不會重(zhong)復
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;
/* New driver overlaps from the left. */
if (new_max >= old_min && new_max <= old_max) {
ret = -EBUSY;
goto out;
}
/* New driver overlaps from the right. */
if (new_min <= old_max && new_min >= old_min) {
ret = -EBUSY;
goto out;
}
}
/*將設(she)備號的結構體插入到散列(lie)表(biao)中*/
cd->next = *cp
*cp = cd;
}
附錄1-1:
#define CHRDEV_MAJOR_HASH_SIZE 255
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
附錄1-2

