Linux設備驅動開發---設備樹的概念

文章目錄

  • 1 設備樹機制
    • 命名約定
    • 別名、標簽和phandle
    • DT編譯器
  • 2 表示和尋址設備
    • SPI和I2C尋址
    • 平臺設備尋址
  • 3 處理資源
    • 提取特定應用數據
      • 文本字符串
      • 單元格和無符號的32位整數
      • 布爾
      • 提取并分析子節點
  • 4 平臺驅動程序與DT
    • OF匹配風格
      • 處理非設備樹平臺
    • 平臺數據與DT

設備樹(DT)是易于閱讀的硬件描述文件,它采用JSON式的格式化風格,在這種簡單的樹形結構中, 設備表示為帶有屬性的節點。屬性可以為空(只有鍵,用來描述布爾值),也可以是鍵值對,其中的值可以是任意的字節流。

1 設備樹機制

將選項CONFIG_OF設置為Y即可在內核中啟用DT。要在驅動程序中調用DT API,必須添加以下頭文件

#include <linux/of.h>
#include <linux/of_device.h>

DT支持一些數據類型:

  • 文本字符串用雙引號表示。可以使用逗號來創建字符串列表
  • 單元格是由尖括號分隔的無符號32位整數
  • 布爾類型不過是空屬性,其值是true或false取決于屬性存在于否

比如:

node_lable:nodename@reg{srting-property="a string";					//一個字符串string-list="read","write";					//字符串列表onw-int-property=<197>;						//一個整型數字int-list-property=<0xbeef 123 0xabcd 4>;	//整型列表byte-array-property=[0x01 0x02 0x35 0x47];	//字節數組bool-property;								//布爾數據mixed-list-property="a string",<oxabcd 45>,<35>,[0x01 0x23 0x45];	//混合類型	
};

命名約定

每個節點都必須由<name>[@<address>]形式的名稱,其中<name>是一個字符串,其最多可以為31個字符,[@<address>]是可選的,具體取決于節點代表的設備是否為可尋址的。<address>是用來訪問設備的主要地址。設備命名的一個例子如下:

i2c@021a0000{compatible = "fls,imx6q-i2c","fls,ima21-i2c";reg = <0x021a0000 0x4000>;...
}

別名、標簽和phandle

node_lable:nodename@reg{...
};gpio1:gpio@0209c000{...
};
aliases{...gpio1 = &gpio1;...
};

標簽不過是標記節點的方法,可以用唯一的名稱來標識節點。DT編譯器將該名稱轉換為唯一的32位值。在上面的例子中,gpio1和node_label都是標簽。之后可以用標簽來引用節點,因為標簽對于節點是唯一的。

指針句柄(point handle,簡寫為phandle)是與節點相關聯的32位值,用于唯一標識該結點,以便可以從另一個結點的屬性引用該節點。標簽用于一個指向節點的指針,使用&mylabel可以指向標簽為mylabel的節點,比如gpio1 = &gpio1。&gpio1被轉換為phandle,以便引用gpio1節點。

為了在查找節點時不遍歷整棵樹,引入了別名的概念。在DT中,別名節點可以看做是快速查找表,可以使用函數find_node_by_alias()來查找指定別名的節點。別名不是直接在DT源中使用,而是由Linux內核來引用。

DT編譯器

DT有兩種形式:文本形式(代表源,也稱作DTS)和二進制塊形式(代表編譯后的DT),也稱作DTB。源文件的拓展名是.dts。.dtsi是SOC級定義,.dts文件代表開發板定義。就像源文件(.c)和包含頭文件(.h)一樣,應該把.dtsi作為頭文件包含在.dts文件中。而二進制文件使用.dtb拓展名。

2 表示和尋址設備

每個設備在DT中至少有一個節點。某些屬性對于設備是通用的,這些屬性是reg、#address-cells和#size-cells,它們的用途是在其所在總線上進行設備尋址。主要的尋址屬性是reg,其含義取決于設備所在的總線。size-cells和address-cells的前綴#可以翻譯為length。
每個可尋址設備都具有reg屬性,該屬性是reg = <address0 size0>,<address1 size1>…形式的元組列表,其中每個元組代表設備的地址范圍。#size-cells(長度單元)指示使用多少個32位單元來表示size0,如果與大小無關,則可以是0。 #address-cells(地址單元)指示用多少個32位單元來表示address0 。

可尋址設備繼承它們父節點的#size-cells和#address-cells,其指定的#size-cells和#address-cells影響子設備。

SPI和I2C尋址

SPI和I2C設備都屬于非內存映射設備,因為它們的地址對CPU不可訪問。而總線控制器驅動程序將代表CPU執行間接訪問。每個I2C/SPI設備都表示為在I2C/SPI總線節點上的子節點。對于非存儲映射的設備,#size-cells表示為0,尋址元組中的size元素為空。這意味著這種設備的reg屬性總是只有一個單元:

&i2c3{...temperature-sensor@49{...reg=<0x49>;...};pcf8523:rtc@68{...reg = <0x68>;...};...
};&ecspi{...cs-gpios = <&gpio4 17 0>,<&gpio5 17 0>,<&gpio6 17 0>;...ad7606r8_0:ad7606r8@1{...reg = <1>;...};...
};

reg屬性只是一個保存地址值的單元格,I2C設備的reg屬性用于指定總線上設備的地址。對于SPI,reg表示從控制器節點 所具有的芯片選擇列表中 分配給設備的芯片選擇線的索引。例如對于ad7606r8 ADC,芯片選擇索引是1,對應于cs-gpios中的<&gpio5 17 0>,cs-gpios是控制器節點的芯片選擇列表。

平臺設備尋址

下面介紹的內存映射設備,其內存可由CPU訪問。在這里,reg屬性仍然定義設備的地址,這是可以訪問設備的內存區域列表。每個區域用單元格元組表示,其中第一個單元格是內存區域的基地址,第二個單元格是該區域的大小。每個元組代表設備使用的地址范圍。

這種設備應該在具有特殊值compatible= "simple-bus"的節點內聲明,這意味著簡單的內存映射總線,沒有特定的處理和驅動程序:

soc{#address-cells = <1>;#size-cells = <1>;compatible = "simple-bus";aips-bus@02000000{...#address-cells = <1>;#size-cells = <1>;reg = <0x02000000 0x100000>;...spba-bus@02000000{...#address-cells = <1>#size-cells = <1>;reg = <0x02000000 0x400000>;...ecspi:ecspi@02008000{...#address-cells = <1>;#size-cells = <0>;reg = <0x02008000 0x400>;...};...};...};...
};

父結點compatible= “simple-bus”,其在節點將被注冊為平臺設備。設置#size-cells=<0>也能夠看到SPI總線控制器怎樣改變其子節點的尋址方式的。從內核設備樹文檔可以查找所有 綁定信息:Document/devicetree/binding/。

3 處理資源

當驅動程序期望某種類型的資源列表時,由于編寫開發板設備樹的人通常不是寫驅動程序的人,因此不能保證該列表是以驅動程序期望的方式排序。例如,驅動程序可能期望其設備節點具有2條IRQ線路,一條用于索引0處的Tx事件,另一條用于索引1處的Rx。如果這種順序得不到滿足,驅動就會發生異常行為。為了避免這種不匹配,引入了命名資源的概念,它由定義資源列表命名組成,因此無論索引什么,給定的名稱總將與資源相匹配。
命名資源的相關屬性如下:

  • reg-names:reg屬性中的內存區域列表
  • clock-names:clocks屬性中命名clocks
  • interrupt-names:為interrupts屬性中的每個中斷指定一個名稱
  • dma-names:用于dma屬性

例如:

fake_device{...reg=<0x4a06400 0x800>,<0x4a064800 0x200>,<0x4a064c00 0x200>;reg-name="config","ohci","ehci";interrupters=<0 66 IRQ_TYPE_LEVEL_HIGH>,<0 67 IRQ_TYPE_LEVEL_HIGH>;interrupter-names="ohci","ehci";clocks=<&clks IMAX6QDL_CLK_UART_IPG>,<&clks IMAX6QDL_CLK_UART_SERTAL>;clock-names="ipg","per";dmas=<&sdma 25 4 0>,<&sdma 26 4 0>;dma-names="rx","tx";
};	

驅動程序中提取每個命名資源代碼如下所示:

struct resource *res1,*res2;
res1 = platform_get_resource_byname(pdev,IORESOURCE_MEM,"ohci");
res2 = platform_get_resource_byname(pdev,IORESOURCE_MEM,"config");struct dma_chan *dma_chan_rx,*dma_chan_tx;
dma_chan_rx=dma_request_slave_channel(&pdev->dev,"rx");
dma_chan_tx=dma_request_slave_channel(&pdev->dev,"tx");int txirq,rxirq;
txirq = platform_get_irq_byname(pdev,"ohci");
rxirq = platform_get_irq_byname(pdev,"ehci");struct *clk_ipg,*clk_per;
clk_ipg = devm_clk_get(&pdev->dev,"ipg");
clk_per = devm_clk_get(&pdev->dev,"per");

這樣,就可以確保把正確的名字映射到正確的資源上,而不用再使用索引了。

提取特定應用數據

特定應用數據時公共屬性之外的數據(既不是資源,也不是IO、調度器等),可以分配給設備的任意屬性和子節點,這樣的屬性名稱通常以制造商代碼作前綴。它們可以是任何字符串、布爾值或整型值。

文本字符串

下面是一個string屬性:

string-property = "a string";

驅動程序使用of_property_read_string()讀取字符串值,其原型定義如下:

int of_property_read_string(const struct device_node *np,const char *propname,const char **out_string);

以下代碼說明如何使用它:

const char *my_string = NULL;
of_property_read_string(pdev->dev.of_node,"string-property",&my_string);

單元格和無符號的32位整數

下面是unsigned int屬性:

one-int-property=<197>;
int-list-property = <1350000 0x54dae47 1250000 1200000>;

應該使用of_property_read_u32()讀取單元格值。其原型定義如下:

int of_property_read_u32(const struct device_node *np,const char *propname,u32 *out_value);

驅動中使用:

unsigned int number;
of_property_read_u32(pdev->dev.of_node,"one-cell-property",&number);

可以使用of_property_read_u32_array()讀取單元格列表,其原型如下:

int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values, size_t sz);

sz是要讀取數組元素的數量。比如:

unsigned int cells_array[4];
of_property_read_u32_array(pdev->dev.of_node,"int-list-property",cells_array,4);

布爾

使用of_property_read_bool()讀取布爾屬性。屬性的名稱在函數的第二個參數中給出:

bool my_bool = of_property_read_bool(pdev->dev.of_node,"bool-property");
if(my_bool)
{/* 布爾值為真 */
}
else{/* 布爾值為假 */
}

提取并分析子節點

使用for_each_child_of_node()遍歷指定節點的子節點:

struct device_node *np = pdev->dev.of_node;
struct device_node *sub_np;
for_each_child_of_node(np,sub_np)
{...
}

4 平臺驅動程序與DT

平臺驅動程序也使用DT,也就是說,這是現在推薦的處理平臺設備的方法,不在需要使用開發板文件,甚至不需要在設備的屬性更改時重新編譯內核。

OF匹配風格

OF匹配風格是平臺核心執行的第一種匹配機制,以匹配設備及其驅動程序。它使用設備樹的compatible屬性來匹配of_match_table中的設備項,設備項是struct driver子結構的一個字段。每個設備節點都有compatible屬性,它是字符串或字符串列表。任何驅動程序只要聲明compatible屬性中列出的字符串之一,就將觸發匹配,并看到probe函數執行。

DT匹配項在內核中被描述為struct of_device_id結構的實例,該結構定義在liunx/mod_devicetable.h中,如下所示:

struct of_device_id{...char compatible[128];const void *data;
};
  • compatible:這是用來匹配DT設備節點兼容屬性的字符串字符串,它們必須完全相同才可以匹配
  • data:這可以指向任何結構,這個結構可以用作每個設備類型的配置數據。

of_match_table是指針,可以傳遞struct of_device_id數組,使驅動程序兼容多個設備:

static const struct of_device_id imx_uart_dt_ids[] = {{ .compatible = "fsl,imx6q-uart",},{ .compatible = "fsl,imx1-uart",},{ .compatible = "fsl,imx21-uart",},....
};

填充了of_device_id 數組,就必須把它傳遞到平臺驅動程序的of_match_table字段:

static struct platform_driver serial_imx_driver = {....driver = {.name = "imax-uart",.of_match_table = imx_uart_dt_ids,...};
};

這一步,只有驅動程序知道of_device_id數組,要使內核也或得通知(這樣可以把of_device_id 數組存儲到平臺內核維護的設備列表中),該數組必須用MODULE_DEVICE_TABLE(設備類型,設備表)注冊:

MODULE_DEVICE_TABLE(of,imx_uart_dt_ids);

這樣,驅動程序就兼容DT,接下來聲明與驅動程序兼容的設備:

uart1:serial@02020000{compatible = "fsl,imx6q-uart","fls,imx21-uart";reg = <0x02020000 0x4000>;...
};

這里提供了兩個兼容的字符串,如果第一個與任何驅動程序都不匹配,則平臺核心將執行第二個匹配。
當發生匹配時,將以struct platform_device 結構作為參數調用驅動程序的probe函數。

處理非設備樹平臺

使用CONFIG_OF選項在內核中啟用DT支持。如果內核沒有啟用DT支持,則可能會希望避免使用DT API。其實現的方法是檢查CONFIG_OF是否設置。過去常常這樣做:

#ifdef CONFIG_OFstatic const struct of_device_id imx_uart_dt_ids[] = {{ .compatible = "fsl,imx6q-uart",},{ .compatible = "fsl,imx1-uart",},{ .compatible = "fsl,imx21-uart",},....};....
#endif

這不是唯一的選擇,還可以使用of_match_ptr宏,當OF被禁用時它簡單地返回NULL,查看源代碼,在include/linux/of.h里面。

#ifdef CONFIG_OF
...
#define of_match_ptr(_ptr)	(_ptr)
...
#else /* CONFIG_OF */
...
#define of_match_ptr(_ptr)	NULL
...
#endif /* CONFIG_OF */

平臺數據與DT

如果驅動程序需要平臺數據,則應該檢查dev.platfrom_data指針。非空值表示驅動程序已經在開發板配置文件中以舊的方法實例化,并且DT不會處理它。對于從DT實例化的驅動程序,dev.platform_data將為NULL,平臺設備將在DT項上獲得一個指針,該指針對應于dev.of_node指針中的設備,可以從該指針中提取資源并使用OF API分析和提取應用數據

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379491.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379491.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379491.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【轉】C#中數組復制的4種方法

C#中數組復制的4種方法 from&#xff1a;http://blog.csdn.net/burningcpu/article/details/1434167今天旁邊的同事MM叫我調了一段程序&#xff0c;她想復制一個數組&#xff0c;int[] pins {9,3,4,9};int [] alias pins;這里出了錯誤&#xff0c;也是錯誤的根源&#xff0c…

Java StringBuilder codePointAt()方法與示例

StringBuilder類codePointAt()方法 (StringBuilder Class codePointAt() method) codePointAt() method is available in java.lang package. codePointAt()方法在java.lang包中可用。 codePointAt() method is used to return the Unicode code point at the given indices an…

用戶虛擬地址轉化成物理地址,物理地址轉換成內核虛擬地址,內核虛擬地址轉換成物理地址,虛擬地址和對應頁的關系

文章目錄1. 用戶虛擬地址轉換成物理地址2. 內核虛擬地址轉換成物理地址3. 物理地址轉換成內核虛擬地址4 內核虛擬地址和對應頁5 根據進程號獲取進程描述符1. 用戶虛擬地址轉換成物理地址 static void get_pgtable_macro(void) {printk("PAGE_OFFSET 0x%lx\n", PAGE…

簡單三層架構(登錄)

1&#xff0c;首先導包 dao //獲取數據String username request.getParameter("username");String password request.getParameter("password");//傳遞到Service層UserService service new UserService();//這里的UserService 需要創建到service包下Use…

通過隱藏option實現select的聯動效果

開始的時候需求是根據一定條件隱藏一部分<option>標簽&#xff0c;類似聯動效果&#xff0c;但是目前的html規范并沒有為<option>提供隱藏的效果&#xff0c;因此常用的設置display或者visibility無效。網上大部分解決方案是刪除<option>節點或<option>…

Java SimpleTimeZone setEndRule()方法與示例

SimpleTimeZone類setEndRule()方法 (SimpleTimeZone Class setEndRule() method) Syntax: 句法&#xff1a; public void setEndRule(int en_mm, int en_dd, int en_time);public void setEndRule(int en_mm, int en_dd, int en_dow, int en_time);public void setEndRule(int…

Linux設備驅動開發--- DMA

文章目錄1 設置DMA映射緩存一致性和DMADMA映射一致映射流式DMA映射2 完成的概念3 DMA引擎API分配DMA從通道設置從設備和控制器指定參數獲取事務描述符提交事務發布待處理DMA請求并等待回調通知4 程序單緩沖區映射分散聚集映射DMA是計算機系統的一項功能&#xff0c;它允許設備在…

類加載器

一、類加載器 1&#xff0c;什么是類加載器&#xff1f; 類加載器就是用來加載字節碼文件 2&#xff0c;類加載器的種類有哪些&#xff1f; 1&#xff09;BootStrap&#xff1a;引導類加載器&#xff1a;加載都是最基礎的文件 2&#xff09;ExtClassLoader&#xff1a;擴展類加…

一個用java讀取XML文件的簡單方法(轉)

XML文件 book.xml <book> <person> <first>Kiran</first> <last>Pai</last> <age>22</age> </person> <person> <first>Bill</first> <last>Gates</last> <age>46</age&g…

Java ObjectStreamField getName()方法與示例

ObjectStreamField類的getName()方法 (ObjectStreamField Class getName() method) getName() method is available in java.io package. getName()方法在java.io包中可用。 getName() method is used to get the name of this ObjectStreamField field. getName()方法用于獲取…

【css】CSS中折疊margin的問題

為什么要翻譯這篇說明&#xff1f;css2本有人已翻譯過&#xff0c;但看一下&#xff0c;很粗糙&#xff08;不是說自己就怎么怎么樣啊&#xff0c;翻譯者真的是很值得敬佩的&#xff01;&#xff09;&#xff0c;近來跟css與xhtml接觸得越來越多&#xff0c;但接觸得越多&#…

算法---鏈表

文章目錄反轉鏈表合并兩個有序鏈表刪除重復元素反轉鏈表 反轉鏈表包括兩種&#xff0c;反轉全部元素或者反轉部分元素。在這里&#xff0c;我們約定&#xff1a;數據元素類型是struct LinkNode&#xff0c;要反轉鏈表的第一個節點是head&#xff0c;head的前面一個節點是pre&a…

SSM

二、環境設置&#xff08;MyEclipse&#xff09; 1&#xff0c;字體設置 window–>Preference->General->Appearance->Colors and Fonts->Basic Text->Font 2&#xff0c;workspace字符集設置 window–>Preference->General->Appearance->W…

IOS NSArray,NSDictionary

小結&#xff1a; NSArray有序的集合&#xff1b; NSDictionary無序的集合&#xff0c;可排序&#xff1b; 增刪改查 ------NSArray----------- create : 1)NSArray *array [NSArray arrayWithObjects:"Henry","Jones", "Susan", "Smith&q…

Java PropertyPermission equals()方法與示例

PropertyPermission類equals()方法 (PropertyPermission Class equals() method) equals() method is available in java.util package. equals()方法在java.util包中可用。 equals() method is used to check whether this object and the given object (ob) are equal or not…

c#配合oracle快速導入excel方法--原創(6萬條記錄5分鐘左右)

原理&#xff1a;用c#采用讀取Excel數據源方式將數據讀入c#的datatable,循環datatable,將datatable中的數據用stringbuilder拼成insert into (字段名) valus (值);每5條插入一個符號&#xff08;作用是將sql字符串限制在4000字符以內&#xff09;&#xff0c;然后將拼成的字符串…

English最俗語法大全

一、先分析兩個長難句 1,It is a truth universally acknowledged that a single man in possession of a good fortune must be in want of a wife. 人們公認這樣一個事實&#xff0c;一個有錢的單身男人一定想要娶一個妻子。 in want of want 想要 university widely 廣泛的…

tfs 內網和外網切換的方法。

C:\Windows\System32\drivers\etc的hosts文件配置一個123.67.128.109 geo-dept-3轉載于:https://www.cnblogs.com/lwflt/archive/2012/07/23/2604731.html

observable_Java Observable countObservers()方法與示例

observable可觀察的類countObservers()方法 (Observable Class countObservers() method) countObservers() method is available in java.util package. countObservers()方法在java.util包中可用。 countObservers() method is used to count the number of observers exists…

設計模式--Strategy 策略模式

所謂策略模式(Strategy Pattern)&#xff0c;就是將策略 (算法) 封裝為一個對象&#xff0c;易于相互替換&#xff0c;如同 USB 設備一樣可即插即用&#xff1b;如果將策略、具體的算法和行為&#xff0c;編碼在某個類或客戶程序內部&#xff0c;將導至事后的修改和擴展不易。 …