天天看點

學Linux驅動: 應該先了解驅動模型mount -t sysfs sysfs /sysifdef CONFIG_I2C_COMPATendififdef CONFIG_I2C_COMPATendif

學Linux驅動: 應該先了解驅動模型

[導讀] Linux裝置林林總總,嵌入式開發一個繞不開的話題就是裝置驅動開發,在做具體裝置驅動開發之前,有必要對Linux設驅動模型有一個相對清晰的認識,将會幫助驅動開發,明白具體驅動接口操作符相應都做些什麼。

個人對于驅動模型的了解概括起來就是一句話:利用面向對象程式設計思想,實作裝置分層管理軟體體系結構。

注:代碼分析基于linux-5.4.31

為啥要驅動模型

随着系統結構演化越來越複雜,Linux核心對裝置描述衍生出一般性的抽象描述,形成一個分層體系結構,進而引入了裝置驅動模型。這樣描述還是不夠讓人了解,來看一下這些需求就好了解些:

Linux核心可以在各種體系結構和硬體平台上運作,是以需要最大限度地提高代碼在平台之間的可重用性。

分層實作也實作了軟體工程的高内聚-低耦合的設計思想。低耦合展現在對外提供統一的抽象通路接口,高内聚将相關度緊密的集中抽象實作。

Linux核心驅動程式模型是先前在核心中使用的所有不同驅動程式模型的統一。 它旨在通過将一組資料和操作整合到全局可通路的資料結構中,來擴充基于基礎總線來橋接裝置驅動程式。

傳統的驅動模型為它們所控制的裝置實作了某種類似于樹的結構(有時隻是一個清單)。不同類型的總線之間沒有任何一緻性。

驅動模型抽象了啥

目前驅動程式模型為描述總線和總線下可能出現的裝置提供了一個通用的、統一的模型。統一總線模型包括一組所有總線都具有的公共屬性和一組公共回調,如總線探測期間的裝置發現、總線關閉、總線電源管理等。

通用的裝置和橋接接口反映了現代計算機的目标:即執行無縫裝置“即插即用”,電源管理和熱插拔的能力。 特别是,英特爾和微軟規定的模型(即ACPI)可確定與x86相容的系統上幾乎任何總線上的幾乎所有裝置都可以在此範式下工作。 當然,雖然大多數總線都支援其中大多數操作,但并不是每條總線都能夠支援所有此類操作。

那麼哪些通用需求被抽象出來了呢?

電源系統和系統關機,對于電源管理與系統關機對于裝置相關的操作進行抽象實作。關機為什麼要被抽象出來管理,比如裝置操作正在進行此時系統收到關機指令,那麼在裝置模型層就會周遊系統裝置硬體,確定系統正确關機。

使用者空間通路:sysfs虛拟檔案系統實作與裝置模型對外的通路抽象,這也是為什麼說Linux 裝置也是檔案的由來。實際從軟體架構層面看,這其實是一個軟體橋接子產品,抽象出統一使用者通路接口,橋接了裝置驅動。

熱插拔管理:熱插拔管理機制定義統一的抽象接口操作符kset_hotplug_ops,不同裝置利用操作符實作差異化。

裝置類型:裝置分類機制,從高層級抽象描述裝置類型,具體可以在sysfs下面展現。

使用者空間通路

由于具有系統中所有裝置的完整分層視圖,是以将完整的分層視圖導出到使用者空間變得相對容易。 這是通過實作名為sysfs虛拟檔案系統來完成的。

sysfs的自動挂載通常是通過/etc/fstab檔案中的以下條目來完成的:

none /sys sysfs defaults 0 0

對于Debian系統而言,可能在/lib/init/fstab采用下面的形式挂載:

none /sys sysfs nodev,noexec,nosuid 0 0

當然也可以采用手動方式挂載:

mount -t sysfs sysfs /sys

當将裝置插入樹中時,都會為其建立一個目錄。該目錄可以填充在發現的每個層(全局層,總線層或裝置層)中。

全局層目前建立兩個檔案-'name'和'power'。 前者報告裝置名稱。 後者報告裝置的目前電源狀态。 它還将用于設定目前電源狀态。

總線層為探測總線時發現的裝置建立檔案。 例如,PCI層目前為每個PCI裝置建立“ irq”和“resource”檔案。

特定于裝置的驅動程式也可以在其目錄中導出檔案,以暴露特定于裝置的資料或可用接口。

驅動模型實作

先來梳理一下内部幾個主要與驅動模型相關的資料結構:

./include/linux/Device.h 定義裝置驅動主要資料結構

bus_type:抽象描述總線類型,如USB/PCI/I2C/MMC等

device_driver:實作具體連接配接在總線上的裝置驅動。

device:描述連接配接在總線上的裝置

./include/linux/Kobject.h中定義了隐藏在背景的類似于基類的資料結構:

kset:可以認為是kobject的頂層容器類。每個kset内部都包含了自己的kobject.

kobject:在 sysfs 中出現的每個對象都對應一個 kobject, 它和核心互動來建立它的可見表述,每一個 kobject 對應 檔案系統 /sys 裡的一個 目錄,目錄的名字就是結構體中的 name

bus_type

bus_type用以驅動總線,具體的驅動USB/I2C/PCI/MMC等:

注冊總線,利用bus_register注冊總線,bus_unregister删除總線。如下例子,每種總線須定義一個bus_type對象,并利用bus_register注冊總線,或bus_unregister删除總線。

/i2c-core-base.c/

struct bus_type i2c_bus_type = {

.name        = "i2c",
.match        = i2c_device_match,
.probe        = i2c_device_probe,
.remove        = i2c_device_remove,
.shutdown    = i2c_device_shutdown,           

};

EXPORT_SYMBOL_GPL(i2c_bus_type);

static int __init i2c_init(void)

{

int retval;

retval = of_alias_get_highest_id("i2c");

down_write(&__i2c_board_lock);
if (retval >= __i2c_first_dynamic_bus_num)
    __i2c_first_dynamic_bus_num = retval + 1;
up_write(&__i2c_board_lock);
/*注冊I2C總線*/
retval = bus_register(&i2c_bus_type);
if (retval)
    return retval;

is_registered = true;
           

ifdef CONFIG_I2C_COMPAT

i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
    retval = -ENOMEM;
    goto bus_err;
}           

endif

retval = i2c_add_driver(&dummy_driver);
if (retval)
    goto class_err;

if (IS_ENABLED(CONFIG_OF_DYNAMIC))
    WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
if (IS_ENABLED(CONFIG_ACPI))
    WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));

return 0;
           

class_err:

class_compat_unregister(i2c_adapter_compat_class);           

bus_err:

is_registered = false;
/*錯誤時删除總線*/
bus_unregister(&i2c_bus_type);
return retval;           

}

注冊擴充卡驅動程式(USB控制器,I2C擴充卡等),以檢測連接配接的裝置,并提供與裝置的通信機制

圖中的match函數接口用于将驅動程式與裝置進行比對。match回調的目的是使總線有機會通過比較驅動程式支援的裝置ID與特定裝置的裝置ID來确定特定驅動程式是否支援特定裝置,而不會犧牲特定于總線的功能或類型安全性 。當向總線注冊驅動程式時,将周遊總線的裝置清單,并為每個沒有與之關聯的驅動程式的裝置調用match回調。

提供API函數以實作擴充卡驅動以及裝置驅動。

同時dev_pm_ops *pm實作對于總線的功耗管理接口抽象。對于特定總線實作這個操作符對應的函數。

struct dev_pm_ops {

int (*prepare)(struct device *dev);
void (*complete)(struct device *dev);
int (*suspend)(struct device *dev);
int (*resume)(struct device *dev);
int (*freeze)(struct device *dev);
int (*thaw)(struct device *dev);
int (*poweroff)(struct device *dev);
int (*restore)(struct device *dev);
int (*suspend_late)(struct device *dev);
int (*resume_early)(struct device *dev);
int (*freeze_late)(struct device *dev);
int (*thaw_early)(struct device *dev);
int (*poweroff_late)(struct device *dev);
int (*restore_early)(struct device *dev);
int (*suspend_noirq)(struct device *dev);
int (*resume_noirq)(struct device *dev);
int (*freeze_noirq)(struct device *dev);
int (*thaw_noirq)(struct device *dev);
int (*poweroff_noirq)(struct device *dev);
int (*restore_noirq)(struct device *dev);
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);           

iommu_ops 操作符提供總線相關的IOMMU抽象。

裝置驅動注冊到總線上時,将在sysfs管理總線/裝置/裝置驅動的層次關系,以PCI為例:

/在總線上注冊的驅動程式會在總線的驅動程式目錄中獲得一個目錄/

/sys/bus/pci/

|-- devices
    `-- drivers
        |-- Intel ICH
        |-- Intel ICH Joystick
        |-- agpgart
        `-- e100           

/在該類型的總線上發現的每個裝置都會在總線的裝置目錄中獲得到實體層次結構中該裝置目錄的符号連結/

|-- devices
      |   |-- 00:00.0 -> ../../../root/pci0/00:00.0
      |   |-- 00:01.0 -> ../../../root/pci0/00:01.0
      |   `-- 00:02.0 -> ../../../root/pci0/00:02.0
      `-- drivers           

總線屬性:bus_groups/裝置屬性dev_groups/驅動屬性drv_groups。

device

作用:抽象描述具體的裝置

裝置注冊:發現裝置的總線驅動程式使用下面的函數來向核心注冊裝置

int device_register(struct device * dev);

利用device_unregister()從總線上删除裝置

device_driver

作用:抽象描述連接配接在總線上的具體裝置的驅動

驅動注冊,通過下面的函數将裝置驅動程式注冊

int driver_register(struct device_driver *drv);

使用它使用以下指令從驅動程式目錄中添加和删除屬性

int driver_create_file(struct device_driver , const struct driver_attribute );

void driver_remove_file(struct device_driver , const struct driver_attribute );

class

作用:抽象裝置的高層視圖,描述的是裝置的集合。抽象了同類型的裝置的底層實作細節。比如所有的網絡接口都位于/sys/class/net下

struct subsys_private *p描述類連結清單

kobject/kset

kobject類似于面向對象中的核心基類,核心利用它将各個對象連接配接起來組成分層的機構體系,其parent指針将形成一個樹狀分層結構。

kset内部包含了kobject。重心在描述對象的聚集于集合。這也是set一詞的含義。每一個kset添加到系統中,都将在sysfs中建立一個目錄

kobject/kset一起實作了sysfs虛拟檔案系統中裝置/總線/裝置驅動樹狀分層結構的最關鍵的底層實作由來。

總體上而言:

通過上面一些關鍵資料結構關系分析,總線裝置驅動模型最終目的是實作如下這樣一個分層驅動模型。

文章出自微信公衆号:嵌入式客棧

原文位址

https://www.cnblogs.com/embInn/p/13034226.html

繼續閱讀