Linux Platform驅動模型(一) _設備信息

来源:http://www.cnblogs.com/xiaojiang1025/archive/2017/02/05/6367061.html
-Advertisement-
Play Games

我在 "Linux字元設備驅動框架" 一文中簡單介紹了Linux字元設備編程模型,在那個模型中,只要應用程式 open() 了相應的設備文件,就可以使用ioctl通過驅動程式來控制我們的硬體,這種模型直觀,但是從軟體設計的角度看,卻是一種十分糟糕的方式,它有一個致命的問題,就是設備信息和驅動代碼冗餘 ...


我在Linux字元設備驅動框架一文中簡單介紹了Linux字元設備編程模型,在那個模型中,只要應用程式open()了相應的設備文件,就可以使用ioctl通過驅動程式來控制我們的硬體,這種模型直觀,但是從軟體設計的角度看,卻是一種十分糟糕的方式,它有一個致命的問題,就是設備信息和驅動代碼冗餘在一起,一旦硬體信息發生改變甚至設備已經不在了,就必須要修改驅動源碼,非常的麻煩,為瞭解決這種驅動代碼和設備信息耦合的問題,Linux提出了platform bus(平臺匯流排)的概念,即使用虛擬匯流排將設備信息和驅動程式進行分離,設備樹的提出就是進一步深化這種思想,將設備信息進行更好的整理。平臺匯流排會維護兩條鏈表,分別管理設備和驅動,當一個設備被註冊到匯流排上的時候,匯流排會根據其名字搜索對應的驅動,如果找到就將設備信息導入驅動程式並執行驅動;當一個驅動被註冊到平臺匯流排的時候,匯流排也會搜索設備。總之,平臺匯流排負責將設備信息和驅動代碼匹配,這樣就可以做到驅動和設備信息的分離。

在設備樹出現之前,設備信息只能使用C語言的方式進行編寫,在3.0之後,設備信息就開始同時支持兩種編寫方式——設備樹、C語言,如果用設備樹,手動將設備信息寫到設備樹中之後,內核就可以自動從設備樹中提取相應的設備信息並將其封裝成相應的platform_device對象,i2c_device對象並註冊到相應的匯流排中,如果使用設備樹,我們就不需要對設備信息再進行編碼。如果使用C語言,顯然,我們需要將使用內核提供的結構將設備信息進行手動封裝,這種封裝又分為兩種形式,一種是使用平臺文件(靜態),將整個板子的所有設備都寫在一個文件中並編譯進內核。另一種是使用模塊(動態),將我們需要的設備信息編譯成模塊在insmod進內核。對於ARM平臺,使用設備樹封裝設備信息是將來的趨勢,但是由於歷史原因,當下的內核中這三種方式並存。封裝好後再創建相應的xxx_device實例最後註冊到匯流排中。針對平臺匯流排的設備信息,我在Linux設備樹語法詳解一文中已經討論了設備樹的寫法,所以,本文主要討論4個問題:

  1. 如何使用C語言封裝設備信息?
  2. 設備樹的設備信息和C語言的設備信息如何轉換?
  3. 如何將C語言設備信息封裝到platform_device結構中?
  4. 如何將封裝好的platform_device結構註冊到平臺匯流排中?

何為資源?

所謂的設備信息,主要分為兩種:硬體信息、軟體信息,硬體信息主要包括xxx控制器在xxx地址上,xxx設備占用了xxx中斷號,即地址資源中斷資源等。內核提供了struct resource來對這些資源進行封裝。軟體信息的種類就比較多樣,比如網卡設備中的MAC地址等等,這些信息需要我們以私有數據的形式封裝的設備對象(內核使用面向對象的思想編寫,一個設備的設備信息是一個對象,即一個結構體實例,一個設備的驅動方法也是一個對象)中,這部分信息就需要我們自定義結構進行封裝。

struct resource那點事

這個結構用來描述一個地址資源或中斷資源,除了這個結構,內核還提供了一些巨集來幫助我們快速的創建一個resource對象。

//include/linux/ioport.h
 18 struct resource {    
 19         resource_size_t start;
 20         resource_size_t end;
 21         const char *name;
 22         unsigned long flags;
 23         unsigned long desc;
 24         struct resource *parent, *sibling, *child;
 25 };

struct resource
--19--> start表示資源開始的位置,如果是IO地址資源,就是起始物理地址,如果是中斷資源,就是中斷號;
--20--> end表示資源結束的位置,如果是IO地址地址,就是映射的最後一個物理地址,如果是中斷資源,就不用填;
--21--> name就是這個資源的名字。
--22--> flags表示資源類型,提取函數在尋找資源的時候會對比自己傳入的參數和這個成員,理論上只要和可以隨便寫,但是合格的工程師應該使用內核提供的巨集,這些巨集也在"ioport.h"中進行了定義,比如IORESOURCE_MEM表示這個資源是地址資源,IORESOURCE_IRQ表示這個資源是中斷資源...。

//include/linux/ioport.h
 33 #define IORESOURCE_BITS         0x000000ff      /* Bus-specific bits */
 35 #define IORESOURCE_TYPE_BITS    0x00001f00      /* Resource type */
 36 #define IORESOURCE_IO           0x00000100      /* PCI/ISA I/O ports */
 37 #define IORESOURCE_MEM          0x00000200
 38 #define IORESOURCE_REG          0x00000300      /* Register offsets */
 39 #define IORESOURCE_IRQ          0x00000400          
 40 #define IORESOURCE_DMA          0x00000800
 41 #define IORESOURCE_BUS          0x00001000
 ...
147 #define DEFINE_RES_IO(_start, _size)   
152 #define DEFINE_RES_MEM(_start, _size)   
157 #define DEFINE_RES_IRQ(_irq)  
162 #define DEFINE_RES_DMA(_dma)   

有了這幾個屬性,就可以完整的描述一個資源,但如果每個資源都需要單獨管理而不是組成某種數據結構,顯然是一種非常愚蠢的做法,所以內核的resource結構還提供了三個指針:parent,sibling,child(24),分別用來表示資源的父資源,兄弟資源,子資源,這樣內核就可以使用樹結構來高效的管理大量的系統資源,linux內核有兩種樹結構:iomem_resource,ioport_resource,進行板級開發的時候,通常將主板上的ROM資源放入iomem_resource樹的一個節點,而將系統固有的I/O資源掛到ioport_resource樹上。

下麵是一個小例子,分別用兩種寫法表示了地址資源和中斷資源,強烈推薦使用DEFINE_RES_XXX的版本

//IO地址資源,自己填充resource結構體+flags巨集
struct resource res= {
    .start = 0x10000000,
    .end     = 0x20000000-1,
    .flags = IORESOURCE_MEM
};
//IO地址資源,使用內核提供的定義巨集
struct resource res = DEFINE_RES_MEM(0x20000000, 1024);
//中斷資源,自己填充resource結構體+flags巨集
struct resource res = { 
        .start  = 10,
        .flags  = IORESOURCE_IRQ,
};
//中斷資源,使用內核提供的定義巨集
struct resource res = DEFINE_RES_IRQ(11);

下麵是一個資源數組的實例,多個資源的時候就寫成數組,這裡我同時使用了上面兩種寫法。

struct resource res[] = {
    [0] = {
        .start  = 0x10000000,
        .end    = 0x20000000-1,
        .flags  = IORESOURCE_MEM
    },
    [1] = DEFINE_RES_MEM(0x20000000, 1024),
    [2] = {
        .start  = 10,   //中斷號
        .flags  = IORESOURCE_IRQ|IRQF_TRIGGER_RISING //include/linux/interrupt.h
    },
    [3] = DEFINE_RES_IRQ(11),   
};

resource VS dts

至此,我們已經討論了使用設備樹和resource結構兩種方式寫設備信息,顯然,這兩種方式最終是殊途同歸的,這裡我們簡單的討論一個二者之間的轉換問題。下圖是我在Linux設備樹語法詳解一文中用到的dm9000網卡的節點

將它的地址資源寫成resource就是這個樣子,清晰起見,這裡也是兩種寫法:

struct resource res[] = {
    [0] = {
        .start = 0x05000000,
        .end = 0x05000000+0x2-1,
        .flags = IORESOURCE_MEM,
    },
    [1] = DEFINE_RES_MEM(0x05000004,2),
};

platform_device對象

這個對象就是我們最終要註冊到平臺匯流排上的設備信息對象,對設備信息進行編碼,其實就是創建一個platform_device對象,可以看出,platform_device和其他設備一樣,都是device的子類

//include/linux/platform_device.h
 22 struct platform_device {                                    
 23         const char      *name;
 24         int             id;
 25         bool            id_auto;
 26         struct device   dev;
 27         u32             num_resources;
 28         struct resource *resource;
 29 
 30         const struct platform_device_id *id_entry;
 31 
 32         /* MFD cell pointer */
 33         struct mfd_cell *mfd_cell;
 34 
 35         /* arch specific additions */
 36         struct pdev_archdata    archdata;
 37 };

在這個對象中,我們主要關心以下幾個成員

struct platform_device
--23-->name就是設備的名字,註意, 模塊名(lsmod)!=設備名(/proc/devices)!=設備文件名(/dev),這個名字就是驅動方法和設備信息匹配的橋梁
--24-->表示這個platform_device對象表徵了幾個設備,當多個設備有共用資源的時候(MFD),裡面填充相應的設備數量,如果只是一個,填-1
--26-->父類對象(include/linux/device.h +722),我們通常關心裡面的platform_datarelease,前者是用來存儲私有設備信息的,後者是供當這個設備的最後引用被刪除時被內核回調,註意和rmmod沒關係。
--27-->資源的數量,即resource數組中元素的個數,我們用ARRAY_SIZE()巨集來確定數組的大小(include/linux/kernel.h +54 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) )
--28-->資源指針,如果是多個資源就是struct resource[]數組名,

static struct platform_device demoobj = {
    //2. init obj
    .name   = "demo0",
    .id = 0,
    .dev    = {
        .platform_data = &priv,
        .release = dev_release,
    },
    .num_resources  = ARRAY_SIZE(res),
    .resource   = res,
};

設備對象的註冊與註銷

準備好了platform_device對象,接下來就可以將其註冊進內核,顯然內核已經為我們準備好了相關的函數

/**
 *註冊:把指定設備添加到內核中平臺匯流排的設備列表,等待匹配,匹配成功則回調驅動中probe;
 */
int platform_device_register(struct platform_device *);
/**
 *註銷:把指定設備從設備列表中刪除,如果驅動已匹配則回調驅動方法和設備信息中的release;
 */
void platform_device_unregister(struct platform_device *);

通常,我們會將platform_device_register寫在模塊載入的函數中,將platform_device_unregister寫在模塊卸載函數中。



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

-Advertisement-
Play Games
更多相關文章
  • 沒有蘋果電腦打包iOS平臺的 Ionic 2程式——《Ionic 2 實例開發》更新內容春節剛過,祝各位新的一年裡萬事如意,一帆風順。《Ionic 2 實例開發》在這段時間里更新瞭如下內容:Ionic 2 中使用管道處理數據Ionic 2 中使用HTTP與遠程伺服器交互數據Ionic 2 中的樣式與 ...
  • 0.視頻地址:http://www.imooc.com/video/3265 1.使用SharePreferences存取數據: public class MainActivity extends Activity { @Override protected void onCreate(Bundle ...
  • 作者:Antonio Leiva 時間:Jan 25, 2017 原文鏈接:https://antonioleiva.com/data-classes-kotlin/ 在前面的文章中,我們已經見到了類,而數據類還可以進一步幫助我們簡化代碼。 數據類是什麼? 數據類是僅僅包含狀態而沒有任何可執行的操作 ...
  • Android 第三方類庫之EventBus 1 PS 工欲善其事必先利其器. Eventbus也是一款在開發中常用的利器 這篇也對EventBus的簡單介紹和使用,與之前個xutils介紹的級別一樣.http://www.cnblogs.com/greentomlee/p/6025470.html... ...
  • 題記:在開發的路途上,有的人走的很深很遠,而對於停留在初級階段的我來說,還要學的、經歷的還有很多... ...
  • 在 "Linux設備樹語法詳解" 和 "Linux Platform驅動模型(一) _設備信息" 中我們討論了設備信息的寫法,本文主要討論平臺匯流排中另外一部分 驅動方法,將試圖回答下麵幾個問題: 1. 如何填充platform_driver對象? 2. 如何將驅動方法對象註冊到平臺匯流排中? 正文前的 ...
  • 第十一節 linux定時任務 標簽(空格分隔): Linux實戰教學筆記 [更多資料點我查看][1] 1.1 定時任務Crond介紹 Crond是linux系統中用來定期執行命令/腳本或指定程式任務的一種服務或軟體,一般情況下,我們安裝完Centos5/6 linux操作系統之後,預設便會啟動Cro ...
  • 當你出現ifconfig與網卡配置文件的名稱不同時,如圖所示情況 ifconfig顯示的網卡名為eth1,而配置文件名字為ifcfg-eth0,所以會導致失敗。 解決方案: 1、刪除/etc/udev/rules.d/70-persistent-net.rules文件,重啟系統。 2、ifconfi ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...