1. 程式人生 > >Linux Platform平臺裝置驅動模型

Linux Platform平臺裝置驅動模型

Linux2.6起引入了一套新的驅動管理和註冊模型,即平臺裝置platform_device和平臺驅動platform_driver.

Linux中大部分的裝置驅動,都可以使用這套機制,裝置用platform_device表示,驅動用platform_driver表示平臺裝置模型與傳統的devicedriver模型相比,一個十分明顯的優勢在於平臺裝置模型將裝置本身的資源註冊進核心,由核心統一管理。這樣提高了驅動和資源管理的獨立性,並且擁有較好的可移植性和安全性。通過平臺裝置模型開發底層驅動的大致流程為下圖:

 

 

平臺裝置是指處理器上整合的額外功能的附加裝置,如

Watch DogIICIISRTCADC等裝置。這些額外功能裝置是為了節約硬體成本、減少產品功耗、縮小產品形狀而整合到處理器內部的。需要注意的是,平臺裝置並不是與字元裝置、塊裝置和網路裝置並列的概念,而是一種平行的概念,其從另一個角度對裝置進行了概括。如果從核心開發者的角度來看,平臺裝置的引入,是為了更容易開發字元裝置、塊裝置和網路裝置驅動

 

當一個驅動註冊[platform_driver_register()]的時候,他會遍歷所有總線上的裝置來尋找匹配,在啟動的過程驅動的註冊一般比較晚,或者在模組載入的時候 當一個驅動註冊[platform_driver_probe()]的時候, 功能上和使用

platform_driver_register()是一樣的,唯一的區別是它不能被以後其他的device probe了,也就是說這個driver只能和一個device繫結。

 

匯流排裝置驅動模型主要包含匯流排、裝置、驅動三個部分。

       現實匯流排:一個現實的Linux裝置和驅動通常都需要掛接在一種總線上,對於本身依附於PCI、USB、I2C、SPI等的裝置而言(例如USB、I2C等典型的裝置),這自然不是問題。

       虛擬匯流排(platform匯流排):但是在嵌入式系統裡面,對於一些裝置(內部的裝置)可能沒有現成的匯流排,如SoC 系統中整合的獨立的外設控制器、掛接在SoC記憶體空間的外設等確不依附於此類匯流排。基於這一背景,Linux發明了一種虛擬的匯流排,稱為platform匯流排,相應的裝置稱為platform_device,而驅動成為 platform_driver。

        注意1:platform匯流排裝置驅動模型與之前的三類驅動(字元、塊裝置、網路裝置)沒有必然的聯絡。裝置只是搭載到了platform總線上,僅此而已。platform匯流排相比與常規的匯流排模型其優勢主要是platform匯流排是由核心實現的,而不用自己定義匯流排型別,匯流排裝置來載入匯流排。platform匯流排是核心已經實現好的。

         注意2:所謂的platform_device並不是與字元裝置、塊裝置和網路裝置並列的概念,而是Linux系統提供的一種附加手段,例如,在 S3C6410處理器中,把內部整合的I2C、RTC、SPI、LCD、看門狗等控制器都歸納為platform_device,而它們本身就是字元裝置。

 

 

1. 註冊匯流排bus型別:
       在系統初始化階段,會首先向核心註冊各種常用的匯流排型別,比如pci, usb, spi, i2c, platform等等,當然你也可以自己發明一種匯流排型別註冊上去。這部分程式碼一般放在./arch/arm/mach-xxx/board-xxx.c中。
       有兩個重要的連結串列掛在bus上,一個是裝置device連結串列,一個是驅動driver連結串列。
       它包含的最關鍵的函式:match()


2. 註冊裝置:在此之後,會將系統的裝置列表,基本上整個系統的device都在這裡了,一一地註冊進核心,就是呼叫xxx_device_regisger註冊的過程。
           (xxx_device_regisger:將自己加到裝置device連結串列,然後使用匯流排bus匹配對應的driver )


3. 註冊驅動:然後是對於各個device裝置driver的註冊:xxx_drvier_register()。
            (xxx_drvier_register:將自己加到裝置device連結串列,然後使用匯流排bus匹配對應的driver )


4. 裝置和驅動的匹配:
       大部分device和driver的匹配方式就是看名字是否相同,這部分屬於匯流排分內的事情。match的工作是由匯流排(bus)來完成,匹配工作發生在xxx_device_register()或xxx_drvier_register()
       裝置(或驅動)註冊的時候,都會引發匯流排呼叫自己的match函式來尋找目前platform匯流排是否掛載有與該裝置(或驅動)名字匹配的驅動(或裝置),如果存在則將雙方繫結;
        ===>>如果先註冊裝置,驅動還沒有註冊,那麼裝置在被註冊到總線上時,將不會匹配到與自己同名的驅動,然後在驅動註冊到總線上時,因為裝置已註冊,那麼匯流排會立即匹配與繫結這時的同名的裝置與驅動,再呼叫驅動中的probe函式等;
        ===>>如果是驅動先註冊,同裝置驅動一樣先會匹配失敗,匹配失敗將導致其probe暫不呼叫,而要等到設備註冊成功並與自己匹配繫結後才會呼叫。
           (這一點很重要!!!!!)  (這一點很重要!!!!!)  (這一點很重要!!!!!)  (這一點很重要!!!!!)  (這一點很重要!!!!!) (這一點很重要!!!)

 

 

核心版本2.6.30。編進核心的SPI驅動,通過看程式碼我明白了,大致過程是這樣: 1、先建立一個spi_board_info結構描述spi裝置資訊,呼叫spi_register_board_info將這個結構新增到board_list中。 2、然後呼叫spi_register_master註冊SPI控制器驅動,此時會呼叫scan_boardinfo掃描board_list,根據 spi_board_info呼叫spi_new_device生成spi_device結構,用spi_add_device新增裝置。 3、呼叫spi_register_driver註冊spi_driver,通過與device匹配驅動裝置。
下面是問題: 1、上述做法需要在註冊spi控制器驅動即spi master前,先要定義好spi_board_info結構並註冊。但是如果我要編寫一個驅動模組,我應該怎樣註冊spi_device? 2、spi控制器是通過片選訊號來區分多個spi裝置的,這個片選訊號是像I2C地址一樣由裝置定義好的嗎?如何知道某個裝置該用怎樣的片選訊號?

 

 

 

回覆 1# frank529 急用先學,最近剛好看了這一部分: 1、上述做法需要在註冊spi控制器驅動即spi master前,先要定義好spi_board_info結構並註冊。但是如果我要編寫一個驅動模組,我應該怎樣註冊spi_device? spi_device就是就是通過    spi_register_board_info 來註冊的。 通過spi_board_info結構,給定裝置的下面資訊: 裝置名 波特率 裝置掛在哪個spi總線上,如powerpc 5000有SPI A, B, C ,D 片選訊號
#define SPI_AK4104      \ 659{                       \ 660        .modalias       = "ak4104-codec",       \裝置名 661        .max_speed_hz   = 10000,                \波特率 662        .bus_num        = 0,                    \SPI A, B, C ,D 663        .chip_select    = 0,                    \片選(0~7) 664        .controller_data = (void *) GPIO_SPDIF_CS,\平臺數據 665} #define SPI_DAC7512     \ 679{       \ 680        .modalias       = "dac7512",            \裝置名 681        .max_speed_hz   = 1000000,              \波特率 682        .bus_num        = 0,           \ SPI A, B, C ,D 683        .chip_select    = 2,            \片選(0~7) 684        .controller_data = (void *) GPIO_MCLK_DAC_CS,   \ 685}
static struct spi_board_info connector_spi_devices[] __initdata = {          SPI_AK4104,          SPI_DAC7512, }; 呼叫下面函式,就把上面兩個裝置登記到/sys/bus/spi下了
    spi_register_board_info(ARRAY_AND_SIZE(connector_spi_devices)); 2、spi控制器是通過片選訊號來區分多個spi裝置的,這個片選訊號是像I2C地址一樣由裝置定義好的嗎?如何知道某個裝置該用怎樣的片選訊號? 硬體連線決定的。 上面的例子,spi bus A上掛了上面兩個裝置:AK4104/DAC7512 SPI介面有下列訊號線: SCK_A: 時鐘,同兩個裝置的時鐘訊號接在一起 SIN_A:  data in;  同兩個裝置的data out訊號接在一起 SOUT_A data out; 同兩個裝置的data in訊號接在一起 PCS0_A:  bus A chip select 0, 接AK4104的片選 PCS2_A:  :  bus A chip select 2,接DAC7512的片選
如果先選中chip select 0, 下面的操作就會作用於裝置AK4104 如果先選中chip select 2, 下面的操作就會作用於裝置DAC7512

 

 

在 linux I2C 驅動之----i2c_client 的註冊中介紹了i2c_client的註冊,現在再來說說i2c_driver的註冊過程。

   每一個驅動程式都有 module_init(xxxx_init) 這個語句,它代表系統啟動的時候會自動執行 xxxx 這個函式,也就是說驅動的人口函式是由module_init來定義的。當然還有module_exit(XXXX),它代表系統解除安裝驅動時呼叫(linux系統允許動態載入解除安裝驅動),這一部分這不細說了。

       上文提到的 xxxx_init 是驅動的入口函式,在此函式中,我們一般註冊驅動的driver,比如我今天說的 i2c_driver:

        static int __init xxxx_init(void)        {            return i2c_add_driver(&xxxx_driver);        }

其中xxxx_driver就是我們今天的主角 i2c_driver :

      static struct i2c_driver xxxx_driver =      {          .driver =          {              .owner = THIS_MODULE,               .name = "xxxx",          },          .id_table = xxxx_idtable,         .probe = xxxx_probe,       }

完整的 i2c_driver 非常複雜,上面的定義只是完成了最基本的部分,當然也是必須,也就代表完成了上面的部分你的驅動就可以工作了,當然還有其他的,這裡不細說了。i2c_driver 中的driver.name 要和i2c_client一致,因為這是他們配備的一個依據,id_table 是i2c_device_id結構體的一個物件,裡面定義了i2c驅動對應裝置的i2c地址,probe函式是個非常重要的函式,等下我再來細說。

     現在我來說說 i2c_add_driver 的執行過程。

   (注意:此函式是linux系統i2c 子系統已經為我們做好了的,我們完全不需要了解它的執行過程,只需要明白,當系統中已經註冊了和上文提到的i2c_driver  xxxx_driver 對應的i2c_client,所謂對應的是說,name一樣,i2c裝置的地址也一樣,那麼i2c_driver 與i2c_client配備成功,接著就會呼叫i2c_driver 的probe函式,在此函式中可以做一系列的初始化工作。)

     i2c_add_driver 函式只是呼叫了i2c_register_driver函式,在i2c_register_driver裡呼叫了driver_register(&i2c_driver->driver),

注意 driver_register 是linux系統裝置模型裡面的函數了,每一類linux裝置驅動的註冊最終都會呼叫它,傳遞的引數也由原來的i2c_driver 變成了 device_driver。driver_register做了一些判斷,最後呼叫了 bus_add_driver,然後呼叫 driver_attach,然後呼叫

bus_for_each_dev,這個函式就是搜尋總線上所有的device,我們這裡是i2c匯流排,也即搜尋i2c總線上的i2c_client,呼叫__driver_attach判斷i2c_driver i2c_client是否配備,再呼叫driver_match_device,driver_match_device最終呼叫了bus的match函式,我們這也就是i2c bus的match函式,再呼叫

i2c_match_id,這裡到最後的id 配備了,如果配備成功則正常返回到__driver_attach函式,呼叫driver_probe_device,再呼叫really_probe,最後呼叫bus的probe函式,在bus的probe函式裡利用to_i2c_driver 將device_driver轉換成了i2c_driver,最後呼叫了i2c_driver的probe函式,這個部分有點複雜,其中我可能也沒說的太清楚,建議用source insight自己跟一邊,因為其中有很多是公用的,跟一邊後,其他驅動註冊也就容易了。

1、driver_register把驅動註冊到匯流排

2、驅動註冊到匯流排的實現函式

3、driver_attach()

4、really_probe是我們真正要找的函式