Linux設備樹詳解
時(shi)間:2018-08-16 來(lai)源:未(wei)知
ARM Linux社區為什么要引入設備樹
Linux之(zhi)父Linus Torvalds閑來無事(shi),在翻看(kan)ARM Linux代碼的時(shi)候,有(you)一天終于忍不住了。他在2011年3月17日的ARM Linux郵件(jian)列表(biao)中說道:“This whole ARM thing is a f*cking pain in the ass”。這(zhe)句(ju)話迫使ARM Linux社區引入了設備樹。
Linus Torvalds為什(shen)(shen)么會發(fa)飆呢?而(er)ARM Linux社區的牛人(ren)為什(shen)(shen)么又乖乖地聽(ting)話了(le)?你得首先理(li)解(jie)Linux設備(bei)驅(qu)動(dong)框架(jia)中(zhong)一個非常好的設計:設備(bei)信息(xi)和(he)驅(qu)動(dong)分(fen)離。
為了什么(me)說明設(she)備信息(xi)和驅動分離的概念,先(xian)來看(kan)一個簡(jian)單的模擬代碼實(shi)例:
【例(li)-1】實(shi)現一(yi)個代碼(ma),把要使(shi)用的(de)信息簡單寫死在代碼(ma)中:
int add() /*模擬驅動代碼*/
{
return 3+5; /*模擬(ni)設備(bei)信(xin)息*/
}
優點:簡單
缺點:一(yi)旦加數和被(bei)加數發生變化就得改代(dai)碼
改進設計如(ru)下:
【例-2】實現一個代碼(ma),把要使用的信(xin)息(xi)和操作(zuo)代碼(ma)分離(li)開(kai)來:
struct dev{
int id;
int x;
int y;
}; /*模擬設備信息結構(gou)*/
strcut drv{
int id;
int (*add)(struct dev *info);
}; /*模擬驅動(dong)結構*/
int add(struct dev *info) /*模擬驅(qu)動(dong)代碼(ma)*/
{
return info->x + info->y; /*模擬設備信息-通過參數傳(chuan)遞進來*/
}
struct drv drv = {
.id = 1,
.add = add,
};
/*模擬設(she)備(bei)信息*/
struct dev dev = {
.id = 1,
.x = 3,
.y = 5,
};
/*模擬總線初始化匹配設備(bei)信息和驅動代碼*/
int bus()
{
if(dev.id == drv.id){
return drv.add(&dev);
}
...
}
優點:不(bu)管(guan)加(jia)數和被加(jia)數怎么(me)變化,不(bu)需要(yao)修(xiu)改代碼,僅需要(yao)修(xiu)改信息
缺點:結構比較復雜
那這個設備信(xin)息和驅動分離的(de)(de)設計跟驅動有(you)什么關系呢?熟悉硬件編程的(de)(de)同(tong)學(xue)都知道(dao),硬件一般(ban)的(de)(de)構成(cheng)可(ke)以(yi)使(shi)用下圖簡單表述(shu):

操(cao)作外設(she)的(de)驅動代碼邏輯,只要硬件是(shi)一樣(yang)的(de),就不(bu)會變化。但是(shi)外設(she)掛到不(bu)同的(de)主機上,可(ke)(ke)能會存在I/O地址的(de)變化,如果(guo)有(you)中斷也(ye)是(shi)一樣(yang)的(de),中斷號也(ye)可(ke)(ke)能不(bu)同。這些(xie)I/O地址和中斷號就是(shi)設(she)備信息,使用這些(xie)信息來操(cao)作控制硬件的(de)代碼就是(shi)驅動。
如(ru)果采用【例-1】的(de)設(she)(she)計方式(shi),那么同一(yi)個硬件外設(she)(she)接(jie)到不同的(de)主(zhu)機,或是換了地址線/中(zhong)斷線,設(she)(she)備(bei)(bei)信息就(jiu)變化了,得去修改(gai)驅(qu)動。但(dan)是采用【例-2】的(de)方式(shi)進行設(she)(she)計,問(wen)題(ti)就(jiu)迎刃而解:不管同樣(yang)的(de)外設(she)(she)硬件接(jie)到哪里或是那個平(ping)臺(tai),其驅(qu)動代碼(ma)邏輯并(bing)不需(xu)要(yao)改(gai)動,而僅(jin)僅(jin)需(xu)要(yao)改(gai)變下(xia)設(she)(she)備(bei)(bei)信息,主(zhu)要(yao)的(de)就(jiu)是I/O地址和中(zhong)斷號。
說了(le)(le)這么半天(tian),跟引入設(she)(she)備(bei)樹(shu)有(you)什么關(guan)系呢?華(hua)清教學使用的(de)(de)開(kai)發(fa)板(ban)(ban)(A8/A9)都使用DM9000網(wang)卡芯(xin)片。DM9000驅動是開(kai)源的(de)(de),在(zai)(zai)主線內(nei)核源碼中就有(you)。我(wo)們每次基于A8/A9板(ban)(ban)子移植的(de)(de)時候,DM9000驅動并沒有(you)修改過(guo),僅(jin)僅(jin)是選配了(le)(le)下,主要的(de)(de)工(gong)作(zuo)是在(zai)(zai)板(ban)(ban)級文件中添加了(le)(le)設(she)(she)備(bei)信(xin)息。DM9000驅動使用的(de)(de)是platform框架,所以添加了(le)(le)一份DM9000網(wang)卡芯(xin)片的(de)(de)platform_device信(xin)息。問題來(lai)了(le)(le),如(ru)果使用C代碼的(de)(de)形(xing)式來(lai)描(miao)述設(she)(she)備(bei)信(xin)息,則(ze)在(zai)(zai)內(nei)核源碼中,將會(hui)有(you)多份DM9000的(de)(de)platform_device設(she)(she)備(bei)信(xin)息,造成了(le)(le)內(nei)核代碼冗余。
解決這個(ge)問(wen)題的(de)辦法就是引入設(she)備樹,改造【例-2】來(lai)說(shuo)明設(she)備樹的(de)作(zuo)用(yong)。
【例-3】實現一個代碼,不僅把要使用(yong)的(de)信息和操作(zuo)代碼分離開來,而且信息不是(shi)C代碼編寫的(de),而是(shi)文(wen)(wen)本配置文(wen)(wen)件保存的(de):
struct dev{
int id;
int x;
int y;
}; /*模擬設備信息結構*/
strcut drv{
int id;
int (*add)(struct dev *info);
}; /*模(mo)擬驅(qu)動結構*/
int add(struct dev *info) /*模擬驅動代碼(ma)*/
{
return info->x + info->y; /*模擬設備(bei)信息-通(tong)過參數傳遞進(jin)來*/
}
struct drv drv = {
.id = 1,
.add = add,
};
/*模擬(ni)設備(bei)樹-一(yi)個特殊的配(pei)置(zhi)文(wen)件(jian),xxx.dtbs的文(wen)本文(wen)件(jian)*/
/{
......
Dm9000{
x = 3;
y = 5;
};
......
};
/*模(mo)擬(ni)總線初始化(hua)匹配設備信息(xi)和驅動(dong)代碼*/
int bus()
{
/*模擬設備樹初始化處理*/
讀文件(xxx.dtbs);
解(jie)析文(wen)件內容(rong)(根據設備樹的規(gui)則來解(jie)析);
生成struct dev設備信息;
if(dev.id == drv.id){
return drv.add(&dev);
}
...
}
如果像【例-3】這樣(yang),就(jiu)可(ke)以解決(jue)大量設備信息的(de)代碼(ma)冗余問題。
推而廣之,系統的軟硬(ying)件(jian)信息(xi)都可(ke)(ke)以使用設備樹(shu)來(lai)描述。這樣的話,ARM Linux社區就(jiu)不會因為支持板(ban)子和驅動越來(lai)越多造成(cheng)內核源碼中出現很多冗余代碼(主(zhu)要(yao)是板(ban)級文件(jian)),僅僅需(xu)要(yao)移植者,把(ba)系統的軟硬(ying)件(jian)信息(xi)通過設備樹(shu)提(ti)供(gong)出來(lai),選(xuan)配(pei)一(yi)下(xia)內核代碼,就(jiu)可(ke)(ke)以了。

