【linux】驅動-7-平台設備驅動


前言

區分設備驅動模型平台設備驅動模型
設備驅動模型 可以理解為 匯流排、設備、驅動
平台設備驅動模型 就是那些 Linux 內核管理沒有物理匯流排(即是不需要特殊時序控制的設備)(也是Linux內核沒有自動創建相應驅動匯流排的設備類型)的設備的一套 Linux 平台匯流排、平台模型、平台驅動的模型。

7. 平台設備驅動

為解決驅動程式碼和設備資訊耦合問題,linux提出了設備驅動模型。

注意,前面的匯流排、設備、驅動是一個軟體層面的抽象,與 SOC 中物理匯流排概念不一樣。

物理匯流排:晶片與各個功能外設之間傳送資訊的公共通訊幹線,包括數據匯流排、地址匯流排和控制匯流排,以此來傳輸各種通訊時序
驅動匯流排:負責管理驅動和設備。制定設備和驅動的匹配規則。一旦匯流排上註冊了新設備/驅動,匯流排便執行匹配程式。

對於常見的 I2C、SPI、USB物理匯流排Linux 內核都會自動創建與之對應的 驅動匯流排。所以 I2C設備、SPI設備、USB設備都會掛在在相應的匯流排上。

相對的,實際項目開發中還有很多結構簡單的設備是不需要特殊的時序控制的。也就沒有相應的物理匯流排,Linux 也不會為它們創建相應的驅動匯流排。如 LED、RTC時鐘、按鍵等等。

但是為了這些簡單的設備也能遵循設備驅動模型,Linux 內核引入了一種虛擬匯流排–平台匯流排
平台匯流排 用於管理、掛在那些沒有物理匯流排的設備,且,這些設備被稱為平台設備,對應的設備驅動被稱為平台驅動

平台設備使用 platform_device 結構體進行表示,繼承了設備驅動模型中的 device 結構體。
平台驅動使用 platform_driver 結構體進行表示,繼承了設備驅動模型中的 device_driver 結構體。

7.1 平台匯流排

Linux內核只有一條平台匯流排,用於圖一管理簡單設備–platform_bus_type

7.1.1 平台匯流排註冊和匹配方式

最先比較

  • 最先比較 platform_device.driver_overrideplatform_driver.driver.name

  • 可以設置 platform_devicedriver_override,強制選擇某個 platform_driver

其次比較

  • 其次比較 platform_device.nameplatform_driver.id_table[i].name

  • platform_driver.id_tableplatform_device_id 指針,表示該 drv 支援若干個 device,它裡面列出了各個 device{.name, .driver_data},其中的 name 表示該 drv 支援的設備的名字,driver_data是些提供給該 device 的私有數據。

最後比較

  • 最後比較 platform_device.nameplatform_driver.driver.name

  • 由於 platform_driver.id_table 可能為空,所以,接下來就可以使用 platform_driver.driver.name 來匹配。

7.1.2 源碼分析

平台匯流排

  • 內核使用 bus_type 來抽象系統中的匯流排。相應地,內核使用 platform_bus_type 來描述平台匯流排。
  • 源碼:
struct bus_type platform_bus_type = {

    .name           = "platform",
    .dev_groups     = platform_dev_groups,
    .match          = platform_match,
    .uevent         = platform_uevent,
    .pm             = &platform_dev_pm_ops,

};

EXPORT_SYMBOL_GPL(platform_bus_type);
  • 位於 內核源碼/driver/base/platform.c

  • 該匯流排在Linux內核啟動的時候自動進行註冊

  • 平台匯流排初始化函數源碼:

int __init platform_bus_init(void){
    int error;
    ...
    error =  bus_register(&platform_bus_type);
    ...
    return error;}
  • 位於 內核源碼/driver/base/platform.c
  • error = bus_register(&platform_bus_type); 就是向Linux內核註冊 plateform_bus_type 平台匯流排。

建議:以上匹配規則及源碼,可以追蹤函數 platform_match 源碼來分析。

7.2 平台設備

7.2.1 platform_device

平台設備

  • 使用 platform_device 描述平台設備。
  • 源碼:
struct platform_device {
     const char *name;
     int id;
     struct device dev;
     u32 num_resources;
     struct resource *resource;
     const struct platform_device_id *id_entry;
     /* 省略部分成員 */
 };
  • 位於 內核源碼/include/linux/platform_device.h
  • name:設備名稱,匯流排進行匹配時,會比較設備和驅動的名稱是否一致;
  • id:指定設備的編號,Linux支援同名的設備,而同名設備之間則是通過該編號進行區分;
  • dev:Linux設備模型中的device結構體,platform_device 通過繼承該結構體可復用它的相關程式碼,方便內核管理平台設備;
  • num_resources:記錄資源的個數,當結構體成員 resource 存放的是數組時,需要記錄 resource 數組的個數,內核提供了宏定義 ARRAY_SIZE 用於計算數組的個數;
  • resource:平台設備提供給驅動的資源,如irq,dma,記憶體等等;
  • id_entry:平台匯流排提供的另一種匹配方式,原理依然是通過比較字元串,這裡的 id_entry 用於保存匹配的結果;

7.2.2 設備資訊

平台設備的工作是為 驅動程式 提供 設備資訊。包括 硬體資訊 和 軟體資訊。

  • 硬體資訊:驅動程式需要使用到的暫存器、中斷號、記憶體資源、IO口等等;
  • 軟體資訊:乙太網設備中的 MAC 地址、I2C 設備中的設備地址等等。

硬體資訊

  • 硬體資訊使用 struct resource 來保存設備所提供的資源。
  • 源碼:
/** 
  * Resources are tree-like, allowing nesting etc..
  */
struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    /* 省略部分成員 */
};
  • 位於 內核源碼/include/linux/ioport.h
  • name:資源名字,可為 NULL;
  • start、end:指定資源的起始地址及結束地址;
    • 對於 IORESOURCE_IO 和 IORESOURCE_MEM 是有起始和結束地址的,若只有一個引腳或者一個通道,那麼 start == end
  • flags:用於指定資源的類型,在 Linux 中,資源包括I/O、Memory、Register、IRQ、DMA、Bus等多種類型,如:
    • IORESOURCE_IO:IO 地址空間,對應於 IO 埠映射方式;
    • IORESOURCE_MEM:外設可直接定址的地址空間;
    • IORESOURCE_IRQ:指定該設備使用哪個中斷;
    • IORESOURCE_DMA:指定 DMA 通道。

設備驅動程式主要目的還是操作設備的暫存器。
不同架構的電腦提供不同的操作介面,主要有IO埠映射IO記憶體映射兩種方式。

IO埠映射方式:

  • 只能通過專門的介面函數才能訪問。

IO記憶體映射方式:

  • 可以像記憶體一樣訪問,去讀寫暫存器。

軟體資訊

  • 軟體資訊需要以私有數據保存;
  • platform_device 結構體中繼承有 device 結構體,成員為 dev,該結構體裡面的 platform_data 可以用於保存設備的私有數據。
    • platform__datavoid * 類型的萬能指針,所以,只需要把私有數據的地址付給 platform_data 即可。

7.2.3 註冊/註銷平台設備

註冊:platform_device_register

  • 註冊平台設備,掛在到平台匯流排上。
  • 函數原型:int platform_device_register(struct platform_device *pdev);位於 內核源碼/drivers/base/platform.c
    • pdedplatform_device 類型結構體指針;
    • 返回:
      • 成功:0;
      • 失敗:負數。

註銷:platform_device_unregister

  • 註銷平台設備。
  • 函數原型:void platform_device_unregister(struct platform_device *pdev);位於 **
    內核源碼/drivers/base/platform.c**。

    • pdedplatform_device 類型結構體指針;

7.3 平台驅動

7.3.1 platform_driver

平台驅動

  • 使用 platform_driver 描述平台驅動。
  • 源碼:
struct platform_driver {

    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    struct device_driver driver;
    const struct platform_device_id *id_table;
};
  • 位於 內核源碼/include/platform_device.h

  • probe:匹配成功後執行的回調函數。

  • remove:移除某個平台設備是的回調函數。

  • driver:Linux 設備模型中用於抽象驅動的 device_driver 結構體,platform_driver 繼承該結構體,也就獲取了設備模型驅動對象的特性。

  • id_table:表示該驅動能夠兼容的設備類型。

  • platform_device_id

    • name:指定驅動名稱。(用於和 platform_device 中的 name 比較,匹配。)
    • driver_data:用於保存設備的配置。
    • 源碼:
struct platform_device_id 
{
    char name[PLATFORM_NAME_SIZE];
    kernel_ulong_t driver_data;
};

7.3.2 註冊/註銷平台驅動

註冊:platform_driver_register

  • 註冊平台驅動,掛在到平台匯流排上。
  • 函數原型:int platform_driver_register(struct platform_driver *drv)
    • drvplatform_driver 類型結構體指針;
    • 返回:
      • 成功:0;
      • 失敗:負數。

註銷:platform_driver_unregister

  • 註銷平台啟動驅動。
  • 函數原型:void platform_driver_unregister(struct platform_driver *drv);位於 **
    內核源碼/drivers/base/platform.c**。

    • drvplatform_driver 類型結構體指針;

7.3.3 平台驅動獲取設備資訊

首先要知道的是,平台設備的硬體資訊保存在 resource 結構體中。而軟體資訊則保存在 platform_data 中。

獲取硬體資訊:platform_get_resource

  • 獲取平台設備提供的資源結構體。一般用在 probe 函數中。、

  • 函數原型:struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);

  • dev:指定獲取哪個平台設備的資源;

  • type:指定獲取資源的類型,如IORESOURCE_MEM、IORESOURCE_IO等;

  • num:指定要獲取的資源編號。每個設備所需要的資源的個數是不一樣的。且不同資源的編號是不一樣的。

  • 返回:

    • 成功:struct resouce 結構體類型指針;
    • 失敗:NULL。
  • 若要獲取的資源類型為 IORESOURCE_IRQ,平台設備驅動還提供以下函數介面,來獲取中斷引腳。

    • 函數原型:int platform_get_irq(struct platform_device *pdev, unsigned int num)
      • pedv:指定需要獲取哪個平台設備的資源;
      • num:指定要獲取的資源編號。
    • 返回:
      • 成功:可用中斷號;
      • 失敗:負數。

獲取軟體資訊:dev_get_platdata
* dev:struct device 結構體類型指針。

static inline void *dev_get_platdata(const struct device *dev)
{
    return dev->platform_data;
}

參考