Linux 总线、设备、驱动模型的探究
?學(xué)習(xí)交流加
- 個人qq:1126137994
- ?個人微信:liu1126137994
- ?學(xué)習(xí)交流資源分享qq群:962535112
之前一直做項(xiàng)目,做項(xiàng)目的過程雖然也學(xué)習(xí)到了不少知識,但是,一直沒有好好研究總線設(shè)備驅(qū)動的機(jī)制,今天來學(xué)習(xí)總結(jié)一下!
1、設(shè)備驅(qū)動模型的需求:
????總線,設(shè)備驅(qū)動程序,其實(shí)就是軟件工程中的高內(nèi)聚,低耦合!所謂高內(nèi)聚低耦合是模塊內(nèi)各元素聯(lián)系越緊密就代表內(nèi)聚性就越高,模塊間聯(lián)系越不緊密就代表耦合性低。所以高內(nèi)聚、低耦合強(qiáng)調(diào)的就是內(nèi)部要緊緊抱團(tuán)。設(shè)備和驅(qū)動就是基于這種模型去實(shí)現(xiàn)彼此隔離不相干的。
? ? 那么linux內(nèi)核為什么要采取這種高內(nèi)聚,低耦合的特性來設(shè)計(jì)設(shè)備驅(qū)動呢?
我們拿DM9000網(wǎng)卡驅(qū)動程序來分析。DM9000網(wǎng)卡驅(qū)動程序是需要接在CPU的內(nèi)部總線的,需要地址總線,控制總線,數(shù)據(jù)總線!以及pin管腳等!
那么我們就需要在DM9000網(wǎng)卡驅(qū)動里面定義基地址、中斷號等!我們使用的DM9000網(wǎng)卡的基地址0x20000000+0x300=0x20000300,中斷號是7。那么:
#define DM9000_BASE 0x20000300 #define DM9000_INTERRUPT 7int DM9000_send() {writel(GITCHAT_BASE + REG, 1);... }int DM9000_init() {request_init(GITCHAT_INTERRUPT, ...);... }0x20000300 #define DM9000_INTERRUPT 7int DM9000_send() {writel(GITCHAT_BASE + REG, 1);... }int DM9000_init() {request_init(GITCHAT_INTERRUPT, ...);... }????但是世界上的板子千千萬,有三星、華為、飛思卡爾……每個板子的信息也都不一樣,站在驅(qū)動的角度看,當(dāng)每次重新?lián)Q板子的時候,DM9000_BASE 和DM9000_INTERRUPTR?不再一樣,那驅(qū)動代碼也要隨之改變。這樣的話一萬個開發(fā)板要寫一萬個驅(qū)動了,這就是文章剛開始提到的高內(nèi)聚、低耦合的應(yīng)用場景。
????驅(qū)動想以不變應(yīng)萬變的姿態(tài)適配各種設(shè)備連接的話就要實(shí)現(xiàn)設(shè)備驅(qū)動模型。基本上我們可以認(rèn)為驅(qū)動不會因?yàn)?CPU 的改變而改變,所以它應(yīng)該是跨平臺的。自然像?“#define DM9000_BASE 0x20000300,#define DM9000_INTERRUPT 7”這樣描述和 CPU 相關(guān)信息的代碼不應(yīng)該出現(xiàn)在驅(qū)動里。
?
?
?
2、設(shè)備驅(qū)動模型的實(shí)現(xiàn):
? ??現(xiàn)在 CPU 板級信息和驅(qū)動分開的需求已經(jīng)刻不容緩。但是基地址、中斷號等板級信息始終和驅(qū)動是有一定聯(lián)系的,因?yàn)轵?qū)動畢竟要取出基地址、中斷號等。怎么取?有一種方法是 GITCHAT 驅(qū)動滿世界去詢問各個板子:請問你的基地址是多少?中斷號是幾?這仍然是一個耦合的情況!!!
????對軟件工程熟悉的讀者肯定立刻想到能不能設(shè)計(jì)一個類似接口適配器的類(adapter)去適配不同的板級信息,這樣板子上的基地址、中斷號等信息都在一個 adapter 里去維護(hù),然后驅(qū)動通過這個 adapter 不同的 API 去獲取對應(yīng)的硬件信息。沒錯,Linux 內(nèi)核里就是運(yùn)用了這種設(shè)計(jì)思想去對設(shè)備和驅(qū)動進(jìn)行適配隔離的,只不過在內(nèi)核里我們不叫做適配層,而取名為總線,意為通過這個總線去把驅(qū)動和對應(yīng)的設(shè)備綁定一起,如圖:
?
?
基于這種設(shè)計(jì)思想,Linux 把設(shè)備驅(qū)動分為了總線、設(shè)備和驅(qū)動三個實(shí)體,這三個實(shí)體在內(nèi)核里的職責(zé)分別如下:
?
????模型設(shè)計(jì)好后,下面來看一下具體驅(qū)動的實(shí)踐,首先把板子的硬件信息填入設(shè)備端,然后讓設(shè)備向總線注冊,這樣總線就間接的知道了設(shè)備的硬件信息。比如一個板子上有一個 DM9000,首先向總線注冊:
/* lyy 以下為添加* The DM9000 has no eeprom, and it's MAC address is set by* the bootloader before starting the kernel.*//* DM9000AEP 10/100 ethernet controller */#define MACH_SMDK2440_DM9K_BASE (S3C2410_CS4 + 0x300)static struct resource smdk2440_dm9k_resource[] = {[0] = {.start = MACH_SMDK2440_DM9K_BASE,.end = MACH_SMDK2440_DM9K_BASE + 3,.flags = IORESOURCE_MEM},[1] = {.start = MACH_SMDK2440_DM9K_BASE + 4,.end = MACH_SMDK2440_DM9K_BASE + 7,.flags = IORESOURCE_MEM},[2] = {.start = IRQ_EINT7,.end = IRQ_EINT7,.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,} };static struct dm9000_plat_data smdk2440_dm9k_pdata = {.flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM), };static struct platform_device smdk2440_device_eth = {.name = "dm9000",.id = -1,.num_resources = ARRAY_SIZE(smdk2440_dm9k_resource),.resource = smdk2440_dm9k_resource,.dev = {.platform_data = &smdk2440_dm9k_pdata,}, };/* lyy:以上為添加 */在結(jié)構(gòu)體smdk2440_devices中添加網(wǎng)卡成員:
static struct platform_device *smdk2440_devices[] __initdata = {&s3c_device_ohci,&s3c_device_lcd,&s3c_device_wdt,&s3c_device_i2c0,&s3c_device_iis,&smdk2440_device_eth, /* lyy:添加 */ };在static void __init smdk2440_machine_init(void)函數(shù)中添加設(shè)備到bus總線:
static void __init smdk2440_machine_init(void) {s3c24xx_fb_set_platdata(&smdk2440_fb_info);s3c_i2c0_set_platdata(NULL);platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));//lyy添加smdk_machine_init(); }????現(xiàn)在 platform 總線上自然知道了板子上關(guān)于 DM9000網(wǎng)卡設(shè)備的硬件信息,一旦 DM9000 的驅(qū)動也被注冊的話,總線就會把驅(qū)動和設(shè)備綁定起來,從而驅(qū)動就獲得了基地址、中斷號等板級信息??偩€存在的目的就是把設(shè)備和對應(yīng)的驅(qū)動綁定起來,讓內(nèi)核成為該是誰的就是誰的和諧世界,有點(diǎn)像我們生活中紅娘的角色,把有緣人通過紅線牽在一起。設(shè)備注冊總線的代碼例子看完了,下面看下驅(qū)動注冊總線的代碼示例:
static int __devinit dm9000_probe(struct platform_device *pdev) {}.... db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);.... }????從代碼中看到驅(qū)動是通過總線 API 接口?platform_get_resource?取得板級信息,這樣驅(qū)動和設(shè)備之間就實(shí)現(xiàn)了高內(nèi)聚、低耦合的設(shè)計(jì),無論你設(shè)備怎么換,我驅(qū)動就可以巋然不動。
????設(shè)備向總線注冊了板級信息,驅(qū)動也向總線注冊了驅(qū)動模塊,但總線是怎么做到驅(qū)動和設(shè)備匹配的呢?接下來就講下設(shè)備和驅(qū)動是怎么通過總線進(jìn)行“聯(lián)姻”的。
????總線里有很多匹配方式,比如:
static int platform_match(struct device *dev, struct device_driver *drv) {struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);/* Attempt an OF style match first */if (of_driver_match_device(dev, drv))return 1;/* Then try to match against the id table */if (pdrv->id_table)return platform_match_id(pdrv->id_table, pdev) != NULL;/* fall-back to driver name match */return (strcmp(pdev->name, drv->name) == 0); }????從上面可知 platform 總線下的設(shè)備和驅(qū)動是通過名字進(jìn)行匹配的,先去匹配?platform_driver?中的?id_table?表中的各個名字與?platform_device->name?名字是否相同,如果相同則匹配。
?
3、設(shè)備驅(qū)動模型的改善:
?
相信通過上面的學(xué)習(xí),相信對于設(shè)備、驅(qū)動通過總線來匹配的模型已經(jīng)有所了解。如果寫代碼的話應(yīng)該是下面結(jié)構(gòu)圖所示:
????最底層是不同板子的板級文件代碼,中間層是內(nèi)核的總線,最上層是對應(yīng)的驅(qū)動,現(xiàn)在描述板級的代碼已經(jīng)和驅(qū)動解耦了,這也是 Linux 設(shè)備驅(qū)動模型最早的實(shí)現(xiàn)機(jī)制,但隨著時代的發(fā)展,就像是人類的貪婪促進(jìn)了社會的進(jìn)步一樣,開發(fā)人員對這種模型有了更高的要求,雖然驅(qū)動和設(shè)備解耦了,但是天下設(shè)備千千萬,每次設(shè)備的需求改動都要去修改 board-xxx.c 設(shè)備文件的話,這樣下去,有太多的板級文件需要維護(hù)。完美的 Linux 怎么會允許這樣的事情存在,于是乎,設(shè)備樹(DTS)就登向了歷史舞臺,下一篇內(nèi)容將探討設(shè)備樹的實(shí)現(xiàn)原理和用法。
?
想一起探討以及獲得各種學(xué)習(xí)資源加我:?
qq:1126137994?
微信:liu1126137994?
可以共同交流關(guān)于嵌入式,操作系統(tǒng),C++語言,C語言,數(shù)據(jù)結(jié)構(gòu)與算法等技術(shù)問題。
總結(jié)
以上是生活随笔為你收集整理的Linux 总线、设备、驱动模型的探究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用chrome开发的时候关掉AdBloc
- 下一篇: Linux下安装和配置solr/tomc