Linux驅(qū)動(dòng)之平臺(tái)設(shè)備(張棲銀詳談)

一、設(shè)備驅(qū)動(dòng)的分層與分離

相信經(jīng)過(guò)前面對(duì)字符設(shè)備驅(qū)動(dòng)、雜項(xiàng)設(shè)備驅(qū)動(dòng)和輸入子系統(tǒng)的介紹,大家對(duì)Linux系統(tǒng)中的驅(qū)動(dòng)已經(jīng)基本算是入門了。可是我們發(fā)現(xiàn),掌握這些知識(shí)之后,還不足以支撐我們?nèi)シ治鯨CD、UART、USB等Linux內(nèi)核驅(qū)動(dòng)。原因主要有如下兩點(diǎn):

  • 內(nèi)核中大量的使用了同步、互斥機(jī)制,以及內(nèi)核提供的編程接口;
  • LCD、UART、USB都有自己的設(shè)備驅(qū)動(dòng)框架,而且都使用了平臺(tái)設(shè)備驅(qū)動(dòng);

針對(duì)第一點(diǎn),大家可以去看看宋寶華的《Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解》的3、4、7、8、9、10、11章節(jié)的內(nèi)容,里面大部分知識(shí)在字符設(shè)備驅(qū)動(dòng)、塊設(shè)備驅(qū)動(dòng)、LCD驅(qū)動(dòng)、USB驅(qū)動(dòng)中大量的使用,學(xué)好這些基礎(chǔ)知識(shí),對(duì)閱讀Linux內(nèi)核源代碼也是非常的有幫助的。

針對(duì)第二點(diǎn),就是本文介紹的重點(diǎn)內(nèi)容,希望能帶給廣大閱讀者、技術(shù)宅帶來(lái)幫助。

在開始介紹platform之前,首先來(lái)介紹一下Linux設(shè)備驅(qū)動(dòng)中的分層和分離的思想,便于大家理解Linux內(nèi)核中的設(shè)備、總線啊、驅(qū)動(dòng)模型,從而更加清晰的認(rèn)識(shí)到Linux內(nèi)核引入platform的重要作用和意義。

1.1 設(shè)備驅(qū)動(dòng)的分層思想

在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,可以為某一類相似的事物定義一個(gè)基類,而具體的事物可以繼承這個(gè)基類中的函數(shù)。如果對(duì)于繼承的這個(gè)事物而言,其某成員函數(shù)的實(shí)現(xiàn)與基類一致,那它就可以直接繼承基類的函數(shù);相反,它也可以重載之。這種面向?qū)ο蟮脑O(shè)計(jì)思想極大地提高了代碼的可重用能力,是對(duì)現(xiàn)實(shí)世界事物間關(guān)系的一種良好呈現(xiàn)。

Linux內(nèi)核完全由C語(yǔ)言和匯編語(yǔ)言寫成,但是卻頻繁用到了面向?qū)ο蟮脑O(shè)計(jì)思想。在設(shè)備驅(qū)動(dòng)方面,往往為同類的設(shè)備設(shè)計(jì)了一個(gè)框架,而框架中的核心層則實(shí)現(xiàn)了該設(shè)備通用的一些功能。同樣的,如果具體的設(shè)備不想使用核心層的函數(shù),它也可以重載之。舉個(gè)例子:

return_type core_funca(xxx_device *bottom_dev, paraml_type param1, paraml_type param2)
{
    if(bottom_dev->funca)
        return bottom_dev->funca(param1, param2);  //調(diào)用重載代碼
    /* 核心通用的funca代碼 */
    . . . . . .
}

上述core_funca的實(shí)現(xiàn)中,會(huì)檢查底層設(shè)備是否重載了funca(),如果重載了,就調(diào)用底層的代碼,否則就直接使用通用層代碼。這樣做的好處是:核心層的代碼可以處理絕大多數(shù)該類設(shè)備的funca()對(duì)應(yīng)的功能,只有少數(shù)特殊設(shè)備需要重新實(shí)現(xiàn)funca()。

下面再來(lái)看一個(gè)例子:

return_type core_funca(xxx_device *bottom_dev, paraml_type param1, paraml_type param2)
{
    /* 通用步驟代碼A */
    typea_dev_commonA();
    . . . . . .
    /* 底層操作ops1 */
    bottom_dev->funca_ops1()
    . . . . . .
    /* 通用步驟代碼B */
    typea_dev_commonB();
    . . . . . .
    /* 底層操作ops2 */
    bottom_dev->funca_ops2()
    . . . . . .
    /* 通用步驟代碼C */
    typea_dev_commonB();
    . . . . . .
    /* 底層操作ops3 */
    bottom_dev->funca_ops3()
    . . . . . .
}

上述代碼假定為了實(shí)現(xiàn)funca(),對(duì)于同類設(shè)備而言,其操作流程是一致的,都要經(jīng)過(guò)“通用代碼A、底層ops1、通用代碼B、底層ops2、通用代碼C、底層ops3”這幾步,分層設(shè)計(jì)明顯帶來(lái)的好處是:對(duì)于通用代碼A、B、C,具體的底層驅(qū)動(dòng)不需要再實(shí)現(xiàn),而僅僅只關(guān)心其底層的操作ops1、ops2和ops3。

圖1.1中明確反映了設(shè)備驅(qū)動(dòng)的核心層與具體設(shè)備驅(qū)動(dòng)的關(guān)系,實(shí)際上,這種分層可能只有兩層,也可能是多層的。

圖1.1 Linux設(shè)備驅(qū)動(dòng)的分層設(shè)計(jì)思想


image
image

這樣的分層化的設(shè)計(jì)在Linux的input、RTC、MTD、I2C、SPI、TTY、USB等諸多設(shè)備驅(qū)動(dòng)類型中屢見不鮮。針對(duì)input,前面我們已經(jīng)介紹了,接下來(lái)本文將介紹platform,后續(xù)還將持續(xù)更新RTC、I2C、SPI、LCD、USB等設(shè)備驅(qū)動(dòng)詳細(xì)分析,歡迎大家訂閱。

1.2 設(shè)備驅(qū)動(dòng)的分離思想

在Linux設(shè)備驅(qū)動(dòng)框架的設(shè)計(jì)中,除了有分層設(shè)計(jì)實(shí)現(xiàn)以外,還有分離的思想。這里所謂的設(shè)備驅(qū)動(dòng)的分離思想,實(shí)際上是指主機(jī)驅(qū)動(dòng)與外設(shè)驅(qū)動(dòng)分離的思想。舉一個(gè)簡(jiǎn)單的例子,假設(shè)我們要通過(guò)SPI總線訪問(wèn)某個(gè)外設(shè),在這個(gè)訪問(wèn)過(guò)程中,要通過(guò)操作CPU_xxx上的SPI控制器的寄存器來(lái)達(dá)到訪問(wèn)SPI外設(shè)YYY的目的,最簡(jiǎn)單的方法是:

return_type xxx_write_spi_yyy(. . .)
{
    xxx_write_spi_host_ctrl_reg(ctrl);
    xxx_write_spi_host_data_reg(buf);
    while(!(xxx_spi_host_status_reg() & SPI_DATA_TRANSFER_DONE));
    . . .
}

如果按照這種方式來(lái)設(shè)計(jì)驅(qū)動(dòng),結(jié)果是對(duì)于任何一個(gè)SPI外設(shè)來(lái)講,它的驅(qū)動(dòng)代碼都是CPU相關(guān)的。也就是說(shuō),當(dāng)用在CPU_xxx上的時(shí)候,它訪問(wèn)CPU_xxx的SPI主機(jī)控制寄存器,當(dāng)用在CPU_xxx1的時(shí)候,它訪問(wèn)CPU_xxx1的SPI主機(jī)控制寄存器:

return_type xxx1_write_spi_yyy(. . .)
{
    xxx1_write_spi_host_ctrl_reg(ctrl);
    xxx1_write_spi_host_data_reg(buf);
    while(!(xxx1_spi_host_status_reg() & SPI_DATA_TRANSFER_DONE));
    . . .
}

這樣顯然是不能接受的,因?yàn)檫@意味著外設(shè)YYY用在不同的CPU_xxx和CPU_xxx1上的時(shí)候需要不同的驅(qū)動(dòng)。那么,我們可以用圖1.2所示的思想對(duì)主機(jī)控制器驅(qū)動(dòng)和外設(shè)驅(qū)動(dòng)進(jìn)行分離。這樣的結(jié)果是:外設(shè)a、b、c的驅(qū)動(dòng)與主機(jī)控制器A、B、C的驅(qū)動(dòng)不想關(guān),主機(jī)控制器驅(qū)動(dòng)不關(guān)心外設(shè),而外設(shè)驅(qū)動(dòng)也不關(guān)心主機(jī),外設(shè)只是訪問(wèn)核心層通用的API進(jìn)行數(shù)據(jù)傳輸,主機(jī)和外設(shè)之間可以進(jìn)行任意的組合。

圖1.2 Linux設(shè)備驅(qū)動(dòng)的主機(jī)、外設(shè)驅(qū)動(dòng)分離的思想


image
image

如果我們不進(jìn)行圖1.2所示的主機(jī)和外設(shè)分離,那么外設(shè)a、b、c和主機(jī)A、B、C進(jìn)行組合的時(shí)候,就需要9個(gè)不同的驅(qū)動(dòng)。設(shè)想一共有m個(gè)主機(jī)控制器i,n個(gè)外設(shè),分離的結(jié)果是需要m+n個(gè)驅(qū)動(dòng),不分離則需要m*n個(gè)驅(qū)動(dòng)。

這樣的分離的設(shè)計(jì)在Linux的I2C、SPI、USB等諸多設(shè)備驅(qū)動(dòng)類型中也屢見不鮮。但是其基礎(chǔ)和核心還是設(shè)備、總線、驅(qū)動(dòng)模型。后續(xù)將對(duì)I2C、SPI、USB設(shè)備驅(qū)動(dòng)進(jìn)行詳細(xì)分析。

Linux設(shè)備分層與分離的思想,對(duì)應(yīng)到Linux內(nèi)核就是device、bus、driver驅(qū)動(dòng)模型,這也是Linux內(nèi)核驅(qū)動(dòng)的核心內(nèi)容。與此相關(guān)的內(nèi)容,可見《深入Linux設(shè)備驅(qū)動(dòng)程序內(nèi)核機(jī)制》第9章 Linux設(shè)備驅(qū)動(dòng)模型,其中還詳細(xì)介紹了kobject和kset結(jié)構(gòu)體。

二、平臺(tái)設(shè)備驅(qū)動(dòng)概述

在理解了Linux內(nèi)核中的分層與分離思想以后,接下來(lái)咱們開始拿內(nèi)核中具體的device、bus、driver設(shè)備驅(qū)動(dòng)模型的實(shí)例進(jìn)行學(xué)習(xí)和理解。由于在Linux的各種類型驅(qū)動(dòng)中,很多設(shè)備都使用到了platform設(shè)備驅(qū)動(dòng)模型,這里就首先拿platform開刀,展開學(xué)習(xí)之旅。

2.1 platform的引入

在設(shè)備驅(qū)動(dòng)程序中經(jīng)常會(huì)見到和platform相關(guān)的字段,分布在驅(qū)動(dòng)程序的多個(gè)角落,這也是2.6內(nèi)核中比較重要的一種機(jī)制,把它原理弄懂,對(duì)以后分析驅(qū)動(dòng)程序很有幫助:在linux2.6設(shè)備模型中,關(guān)心總線、設(shè)備、驅(qū)動(dòng)這三個(gè)實(shí)體,總線將設(shè)備和驅(qū)動(dòng)綁定,在系統(tǒng)每注冊(cè)一個(gè)設(shè)備的時(shí)候,會(huì)尋找與之匹配的驅(qū)動(dòng)。相反,在系統(tǒng)每注冊(cè)一個(gè)驅(qū)動(dòng)的時(shí)候,尋找與之匹配的設(shè)備,匹配是由總線來(lái)完成的。

一個(gè)現(xiàn)實(shí)的Linux 設(shè)備和驅(qū)動(dòng)通常都需要掛接在一種總線上,對(duì)于本身依附于PCI、USB、I2C、SPI等的設(shè)備而言,這自然不是問(wèn)題,但是在嵌入式系統(tǒng)里面,SoC系統(tǒng)中集成的獨(dú)立的外設(shè)控制器、掛接在SoC內(nèi)存空間的外設(shè)等確不依附于此類總線?;谶@一背景,Linux 發(fā)明了一種虛擬的總線,稱為platform總線。SOC系統(tǒng)中集成的獨(dú)立外設(shè)單元(LCD,RTC,WDT等)都被當(dāng)作平臺(tái)設(shè)備來(lái)處理,而它們本身是字符型設(shè)備。

從Linux2.6內(nèi)核起,引入一套新的驅(qū)動(dòng)管理和注冊(cè)機(jī)制:platform_device和platform_driver。++platform是一個(gè)虛擬的地址總線,相比pci、usb,它主要用于描述SOC上的片上資源,比如S3C2440上集成的控制器(lcd、watchdog、rtc等)。platform所描述的資源有一個(gè)共同點(diǎn),就是在CPU的總線上直接取址++。Linux中大部分的設(shè)備驅(qū)動(dòng),都可以使用這套機(jī)制,設(shè)備用platform_device表示,驅(qū)動(dòng)用platform_driver進(jìn)行注冊(cè)。

Linux platform driver機(jī)制和傳統(tǒng)的device driver機(jī)制(通過(guò)driver_register函數(shù)進(jìn)行注冊(cè))相比,一個(gè)十分明顯的++優(yōu)勢(shì)在于platform機(jī)制將設(shè)備本身的資源注冊(cè)進(jìn)內(nèi)核,由內(nèi)核統(tǒng)一管理,在驅(qū)動(dòng)程序中使用這些資源時(shí)通過(guò)platform device提供的標(biāo)準(zhǔn)接口進(jìn)行申請(qǐng)并使用,這樣提高了驅(qū)動(dòng)和資源管理的獨(dú)立性,并且擁有較好的可移植性和安全性(這些標(biāo)準(zhǔn)接口是安全的)++。

platform機(jī)制將設(shè)備本身的資源注冊(cè)進(jìn)內(nèi)核,由內(nèi)核統(tǒng)一管理,在驅(qū)動(dòng)程序中使用這些資源時(shí)通過(guò)platform設(shè)備提供的標(biāo)準(zhǔn)接口進(jìn)行申請(qǐng)并使用,從而達(dá)到了統(tǒng)一管理系統(tǒng)外設(shè)資源的目的,并將驅(qū)動(dòng)和資源分離,提高了驅(qū)動(dòng)和資源管理獨(dú)立性,且擁有更好的移植性。

2.2 platform與chrdev、blockdev和netdev的關(guān)系

注意,前面所講的platform_device并不是與字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備并列的概念,而是Linux系統(tǒng)提供的一種附加手段。比如s3c2410中的看門狗驅(qū)動(dòng),它既是字符設(shè)備驅(qū)動(dòng),也是雜項(xiàng)設(shè)備驅(qū)動(dòng),同時(shí)還是平臺(tái)設(shè)備驅(qū)動(dòng):

字符設(shè)備描述了看門狗的訪問(wèn)方式是串行、順序的,而不是隨機(jī)、緩沖的;混雜設(shè)備意味著看門狗這個(gè)字符設(shè)備被丟在了使用同一設(shè)備號(hào)的混雜設(shè)備里面;平臺(tái)設(shè)備意味著看門狗這個(gè)設(shè)備是屬于平臺(tái)的獨(dú)立模塊,它完全是一項(xiàng)附加信息。

對(duì)于看門狗而言,“字符設(shè)備”是對(duì)其“本質(zhì)”的描述,“混雜設(shè)備”是存放這個(gè)字符設(shè)備的“容器”,“平臺(tái)設(shè)備”則描述了看門狗的一種“特征”或“屬性”。

例如一顆花生,如果我們把它本身看作一個(gè)字符設(shè)備,當(dāng)放在八寶粥里面,它就成了八寶粥混雜設(shè)備之一,而花生本身掛接在花生樹苗的根部,成為整個(gè)花生樹苗中一個(gè)獨(dú)立的硬件單元而存在,因此,也可以定義為一個(gè)平臺(tái)設(shè)備。

下圖所示為看門狗設(shè)備驅(qū)動(dòng)中其作為字符設(shè)備、混雜設(shè)備和平臺(tái)設(shè)備時(shí)的各組成元素,看門狗設(shè)備集三重身份于一身。

2.3 引入platform的好處

為了提高大家的學(xué)習(xí)的興趣,好保證一鼓作氣一氣拿下platform,這里列出內(nèi)核引入platform的好處:

  • 保證了Linux內(nèi)核所有驅(qū)動(dòng)結(jié)構(gòu)的清晰;
  • 這就是Linux內(nèi)核2.6版本之后驅(qū)動(dòng)模型的核心:設(shè)備、總線、驅(qū)動(dòng)模型;
  • 更好的體現(xiàn)Linux內(nèi)核分層的思想,同時(shí)也體現(xiàn)了Linux內(nèi)核分離的思想;
  • 使得每一個(gè)硬件設(shè)備都掛接在一個(gè)總線上,從而又保證配套的sysfs節(jié)點(diǎn)、設(shè)備電源管理都成為可能;
  • 隔離BSP和驅(qū)動(dòng)。在BSP中定義platform設(shè)備和設(shè)備使用的資源、設(shè)備的具體配置信息,而在驅(qū)動(dòng)中,只需要通過(guò)通用API去獲取資源和數(shù)據(jù),做到了板相關(guān)代碼和驅(qū)動(dòng)代碼的分離,使得驅(qū)動(dòng)具有更好的可擴(kuò)展性和跨平臺(tái)性。

三、平臺(tái)設(shè)備驅(qū)動(dòng)詳解

下面結(jié)合Linux-2.6.32.2內(nèi)核版本,重點(diǎn)分析platform內(nèi)核源碼,詳細(xì)講解platform實(shí)現(xiàn)的原理、機(jī)制,以及提供的接口函數(shù)及使用方法,后面章節(jié)還將以實(shí)例講解平臺(tái)設(shè)備驅(qū)動(dòng)的開發(fā)方法。

還有 79% 的精彩內(nèi)容
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
支付 ¥2.99 繼續(xù)閱讀

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容