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

来源:https://www.cnblogs.com/embInn/archive/2020/06/02/13034226.html
-Advertisement-
Play Games

[導讀] Linux設備林林總總,嵌入式開發一個繞不開的話題就是設備驅動開發,在做具體設備驅動開發之前,有必要對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:
#ifdef CONFIG_I2C_COMPAT
	class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
	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
/*在該類型的匯流排上發現的每個設備都會在匯流排的設備目錄中獲得到物理層次結構中該設備目錄的符號鏈接*/
/sys/bus/pci/
          |-- 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虛擬文件系統中設備/匯流排/設備驅動樹狀分層結構的最關鍵的底層實現由來。

總體上而言:

通過上面一些關鍵數據結構關係分析,匯流排設備驅動模型最終目的是實現如下這樣一個分層驅動模型。

文章出自微信公眾號:嵌入式客棧,更多內容,請關註本人公眾號,嚴禁商業使用,違法必究


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天在學習winform項目時,看到同時是這樣解決text文本框變換值的: winform界面是這樣的 於是我在想,為什麼不直接進行賦值來進行變換,而通過替換局部字元串來變換呢?兩者之間有什麼區別?到底哪一種的性能會好點?於是帶著種種疑問,設計瞭如下實驗: using System; using S ...
  • 1.我們要知道為啥用框架? 代碼可讀性提高,加快開發速度,提高代碼穩定性,重覆代碼的封裝。 2.用框架有啥好處 結構清晰,調用方便,能低代碼開發。 3.怎麼用框架 根據邏輯邏輯與功能現實,把不同分類的東西,按照一定的規則,區分好模塊,一個令你滿意的框架就出來了 註意點: 技術薄弱的學員,不要著急學框 ...
  • Elasticsearch 入門(三)使用Elasticsearch.Net & NEST作為elasticsearch客戶端 GitHub倉庫 https://github.com/elastic/elasticsearch-net 選擇 Elasticsearch.Net 作為客戶端 連接方式 ...
  • LINQ 的優勢並不是提供了什麼新功能,而是讓我們能夠用更新、更簡單、更優雅的方法來實現原有的功能。不過通常來講,這類功能所帶來的就是對性能上的影響——LINQ 也不例外。本篇文章的主要目的就是讓你瞭解 LINQ 查詢對性能的影響。我們將介紹最基本的 LINQ 性能分析方法,並提供一些數據。還會給出 ...
  • 搞學習 猿學:http://yuanxue365.com/簡書:https://www.jianshu.com/doyoudo:http://www.doyoudo.com/好知網:http://www.howzhi.com/學小易(大學生搜題)https://www.51xuexiaoyi.com ...
  • 上一篇(https://www.cnblogs.com/meowv/p/12974439.html)完成了全網各大平臺的熱點新聞數據的抓取,本篇繼續圍繞抓取完成後的操作做一個提醒。當每次抓取完數據後,自動發送郵件進行提醒。 在開始正題之前還是先玩一玩之前的說到卻沒有用到的一個庫PuppeteerSh ...
  • [導讀] 前文分析了Linux設備驅動的驅動模型,本文來聊聊Platform_driver/Platform_device這個類。做嵌入式Linux的驅動,這個也是繞不開的,所以來學習分析總結一下。 上文閱讀: 註:代碼分析基於linux-5.4.31 為什麼有Platform_driver 前文談 ...
  • 預設/dev下是沒有spi設備的JetPack版本JetPack-L4T-3.2.1-linux-x64_b23.run下載Kernelsudo git clone http://github.com/jetsonhacks/buildJetsonTX2Kernel.gitgit checkout ... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...