日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用

發布時間:2023/12/9 linux 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

關與設備樹的概念,我們在Exynos4412 內核移植(六)—— 設備樹解析?里面已經學習過,下面看一下設備樹在設備驅動開發中起到的作用

? ? ? ???Device Tree是一種描述硬件的數據結構,設備樹源(Device Tree Source)文件(以.dts結尾)就是用來描述目標板硬件信息的。Device Tree由一系列被命名的結點(node)和屬性(property)組成,而結點本身可包含子結點。所謂屬性,其實就是成對出現的name和value。在Device Tree中,可描述的信息包括(原先這些信息大多被hard code到kernel中)。


一、設備樹基礎概念

1、基本數據格式

? ? ? device tree是一個簡單的節點和屬性樹,屬性是鍵值對,節點可以包含屬性和子節點。下面是一個.dts格式的簡單設備樹。

[cpp]?view plaincopy
  • /?{??
  • ????node1?{??
  • ????????a-string-property?=?"A?string";??
  • ????????a-string-list-property?=?"first?string",?"second?string";??
  • ????????a-byte-data-property?=?[0x01?0x23?0x34?0x56];??
  • ????????child-node1?{??
  • ????????????first-child-property;??
  • ????????????second-child-property?=?<1>;??
  • ????????????a-string-property?=?"Hello,?world";??
  • ????????};??
  • ????????child-node2?{??
  • ????????};??
  • ????};??
  • ????node2?{??
  • ????????an-empty-property;??
  • ????????a-cell-property?=?<1?2?3?4>;?/*?each?number?(cell)?is?a?uint32?*/??
  • ????????child-node1?{??
  • ????????};??
  • ????};??
  • };??
  • ? ? ? 該樹并未描述任何東西,也不具備任何實際意義,但它卻揭示了節點和屬性的結構。即:

    a -- 一個的根節點:'/',兩個子節點:node1和node2;node1的子節點:child-node1和child-node2,一些屬性分散在樹之間。

    b -- 屬性是一些簡單的鍵值對(key-value pairs):value可以為空也可以包含任意的字節流。而數據類型并沒有編碼成數據結構,有一些基本數據表示可以在device tree源文件中表示。

    c -- 文本字符串(null 終止)用雙引號來表示:string-property = "a string"

    d -- “Cells”是由尖括號分隔的32位無符號整數:cell-property = <0xbeef 123 0xabcd1234>

    e -- 二進制數據是用方括號分隔:binary-property = [0x01 0x23 0x45 0x67];

    f -- 不同格式的數據可以用逗號連接在一起:mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;

    g -- 逗號也可以用來創建字符串列表:string-list = "red fish", "blue fish";


    二、設備在device tree 中的描述

    ? ? ? ??系統中的每個設備由device tree的一個節點來表示

    1、節點命名

    ? ? ?花些時間談談命名習慣是值得的。每個節點都必須有一個<name>[@<unit-address>]格式的名稱。<name>是一個簡單的ascii字符串,最長為31個字符,總的來說,節點命名是根據它代表什么設備。比如說,一個代表3com以太網適配器的節點應該命名為ethernet,而不是3com509。

    ? ? 如果節點描述的設備有地址的話,就應該加上unit-address,unit-address通常是用來訪問設備的主地址,并在節點的reg屬性中被列出。后面我們將談到reg屬性。


    2、設備

    ? ? ? 接下來將為設備樹添加設備節點:

    [cpp]?view plaincopy
  • /?{??
  • ????compatible?=?"acme,coyotes-revenge";??
  • ??
  • ????cpus?{??
  • ????????cpu@0?{??
  • ????????????compatible?=?"arm,cortex-a9";??
  • ????????};??
  • ????????cpu@1?{??
  • ????????????????????compatible?=?"arm,cortex-a9";??
  • ????????????};??
  • ????????};??
  • ??
  • ????serial@101F0000?{??
  • ????????compatible?=?"arm,pl011";??
  • ????};??
  • ??
  • ????serial@101F2000?{??
  • ????????compatible?=?"arm,pl011";??
  • ????};??
  • ??
  • ????gpio@101F3000?{??
  • ????????compatible?=?"arm,pl061";??
  • ????};??
  • ??
  • ????interrupt-controller@10140000?{??
  • ????????compatible?=?"arm,pl190";??
  • ????};??
  • ??
  • ????spi@10115000?{??
  • ????????compatible?=?"arm,pl022";??
  • ????};??
  • ??????
  • ????external-bus?{??
  • ????????ethernet@0,0?{??
  • ????????????compatible?=?"smc,smc91c111";??
  • ????????};??
  • ??????
  • ????????i2c@1,0?{??
  • ????????????compatible?=?"acme,a1234-i2c-bus";??
  • ????????????rtc@58?{??
  • ????????????????compatible?=?"maxim,ds1338";??
  • ????????????};??
  • ????????????};??
  • ??
  • ????????flash@2,0?{??
  • ????????????compatible?=?"samsung,k8f1315ebm",?"cfi-flash";??
  • ?????????????};??
  • ?????};??
  • };??
  • ? ? ? ? 在上面的設備樹中,系統中的設備節點已經添加進來,樹的層次結構反映了設備如何連到系統中。外部總線上的設備就是外部總線節點的子節點,i2c設備是i2c總線控制節點的子節點。總的來說,層次結構表現的是從CPU視角來看的系統視圖。在這里這棵樹是依然是無效的。它缺少關于設備之間的連接信息。稍后將添加這些數據。

    ? ? ? 設備樹中應當注意:每個設備節點有一個compatible屬性。flash節點的compatible屬性有兩個字符串。請閱讀下一節以了解更多內容。 之前提到的,節點命名應當反映設備的類型,而不是特定型號。請參考ePAPR規范2.2.2節的通用節點命名,應優先使用這些命名。


    3、compatible 屬性

    ? ? ? 樹中的每一個代表了一個設備的節點都要有一個compatible屬性。compatible是OS用來決定綁定到設備的設備驅動的關鍵。

    ? ? ? compatible是字符串的列表。列表中的第一個字符串指定了"<manufacturer>,<model>"格式的節點代表的確切設備,第二個字符串代表了與該設備兼容的其他設備。例如,Freescale MPC8349 SoC有一個串口設備實現了National Semiconductor ns16550寄存器接口。因此MPC8349串口設備的compatible屬性為:compatible = "fsl,mpc8349-uart", "ns16550"。在這里,fsl,mpc8349-uart指定了確切的設備,ns16550表明它與National?Semiconductor 16550 UART是寄存器級兼容的。

    ? ? ?注:由于歷史原因,ns16550沒有制造商前綴,所有新的compatible值都應使用制造商的前綴這種做法使得現有的設備驅動程序可以綁定到一個新設備上,同時仍能唯一準確的識別硬件


    4、編址

    ? ? ? 可編址的設備使用下列屬性來將地址信息編碼進設備樹:

    reg

    #address-cells

    #size-cells

    ? ? ? ?每個可尋址的設備有一個reg屬性,即以下面形式表示的元組列表:

    ? ? ??reg = <address1 length1 [address2 length2] [address3 length3] ... >?

    ? ? ?每個元組,。每個地址值由一個或多個32位整數列表組成,被稱做cells。同樣地,長度值可以是cells列表,也可以為空。

    ? ? ?既然address和length字段是大小可變的變量,父節點的#address-cells和#size-cells屬性用來說明各個子節點有多少個cells。換句話說,正確解釋一個子節點的reg屬性需要父節點的#address-cells#size-cells值


    5、內存映射設備

    ? ? ? 與CPU節點中的單一地址值不同,內存映射設備會被分配一個它能響應的地址范圍。#size-cells用來說明每個子節點種reg元組的長度大小。

    ? ? ?在下面的示例中,每個地址值是1 cell (32位) ,并且每個的長度值也為1 cell,這在32位系統中是非常典型的。64位計算機可以在設備樹中使用2作為#address-cells和#size-cells的值來實現64位尋址。

    [cpp]?view plaincopy
  • serial@101f2000?{??
  • ????compatible?=?"arm,pl011";??
  • ????reg?=?<0x101f2000?0x1000?>;??
  • };??
  • ??
  • gpio@101f3000?{??
  • ????????compatible?=?"arm,pl061";??
  • ????????reg?=?<0x101f3000?0x1000??
  • ???????????????0x101f4000?0x0010>;??
  • };??
  • ??
  • ??
  • interrupt-controller@10140000?{??
  • ????????compatible?=?"arm,pl190";??
  • ????????reg?=?<0x10140000?0x1000?>;??
  • };??
  • ? ? ? 每個設備都被分配了一個基地址及該區域大小。本例中的GPIO設備地址被分成兩個地址范圍:0x101f3000~0x101f3fff和0x101f4000~0x101f400f。


    三、設備樹在platform設備驅動開發中的使用解析

    ? ? ? ? ?我們仍以?Linux 設備驅動開發 —— platform設備驅動應用實例解析?文中的例子來解析設備樹在platform設備驅動中如何使用;

    1、設備樹對platform中platform_device的替換

    ? ? ? ? ?其實我們可以看到,Device Tree 是用來描述設備信息的,每一個設備在設備樹中是以節點的形式表現出來;而在上面的 platform 設備中,我們利用platform_device 來描述一個設備,我們可以看一下二者的對比

    fs4412-beep{ compatible = "fs4412,beep"; reg = <0x114000a0 0x4?0x139D0000 0x14>; };
    a -- fs4412-beep 為節點名,符合咱們前面提到的節點命名規范; 我們通過名字可以知道,該節點描述的設備是beep, 設備名是fs4412-beep;
    b -- compatible = "fs4412,beep";?compatible 屬性, 即一個字符串; ? ? ??前面提到,所有新的compatible值都應使用制造商的前綴,這里是 fs4412;
    c --?reg = <0x114000a0 0x4?0x139D0000 0x14>; ? ? ? ?reg屬性來將地址信息編碼進設備樹,表示該設備的地址范圍;這里是我們用到的寄存器及偏移量;
    static?struct??resource?beep_resource[]?= { [0]?=?{ .start?=?0x114000a0, .end?=?0x114000a0+0x4, .flags?=?IORESOURCE_MEM, }, [1]?=?{ .start?=?0x139D0000, .end?=?0x139D0000+0x14, .flags?=?IORESOURCE_MEM, }, }; static?struct?platform_device?hello_device= { .name?=?"bigbang",//沒用了 .id?=?-1, .dev.release?=?hello_release, .num_resources?=?ARRAY_SIZE(beep_resource?), .resource?=?beep_resource, };

    ? ? ? 可以看到設備樹中的設備節點完全可以替代掉platform_device。


    2、有了設備樹,如何實現device 與 driver 的匹配?

    ? ? ??我們在上一篇還有 platform_device 中,是利用 .name 來實現device與driver的匹配的,但現在設備樹替換掉了device,那我們將如何實現二者的匹配呢?有了設備樹后,platform比較的名字存在哪?

    ? ? ?我們先看一下原來是如何匹配的 ,platform_bus_type 下有個match成員,platform_match 定義如下

    [cpp]?view plaincopy
  • static?int?platform_match(struct?device?*dev,?struct?device_driver?*drv)??
  • {??
  • ????struct?platform_device?*pdev?=?to_platform_device(dev);??
  • ????struct?platform_driver?*pdrv?=?to_platform_driver(drv);??
  • ??
  • ????/*?Attempt?an?OF?style?match?first?*/??
  • ????if?(of_driver_match_device(dev,?drv))??
  • ????????return?1;??
  • ??
  • ????/*?Then?try?ACPI?style?match?*/??
  • ????if?(acpi_driver_match_device(dev,?drv))??
  • ????????return?1;??
  • ??
  • ????/*?Then?try?to?match?against?the?id?table?*/??
  • ????if?(pdrv->id_table)??
  • ????????return?platform_match_id(pdrv->id_table,?pdev)?!=?NULL;??
  • ??
  • ????/*?fall-back?to?driver?name?match?*/??
  • ????return?(strcmp(pdev->name,?drv->name)?==?0);??
  • }??
  • 其中又調用了of_driver_match_device(dev, drv) ,其定義如下: [cpp]?view plaincopy
  • static?inline?int?of_driver_match_device(struct?device?*dev,??
  • ?????????????????????const?struct?device_driver?*drv)??
  • {??
  • ????return?of_match_device(drv->of_match_table,?dev)?!=?NULL;??
  • }??
  • 其調用of_match_device(drv->of_match_table, dev) ,繼續追蹤下去,注意這里的參數 drv->of_match_table [cpp]?view plaincopy
  • const?struct?of_device_id?*of_match_device(const?struct?of_device_id?*matches,??
  • ???????????????????????const?struct?device?*dev)??
  • {??
  • ????if?((!matches)?||?(!dev->of_node))??
  • ????????return?NULL;??
  • ????return?of_match_node(matches,?dev->of_node);??
  • }??
  • EXPORT_SYMBOL(of_match_device);??
  • 又調用 of_match_node(matches, dev->of_node) ?,其中matches 是 struct of_device_id 類型 [cpp]?view plaincopy
  • /**?
  • ?*?of_match_node?-?Tell?if?an?device_node?has?a?matching?of_match?structure?
  • ?*??@matches:???array?of?of?device?match?structures?to?search?in?
  • ?*??@node:??????the?of?device?structure?to?match?against?
  • ?*?
  • ?*??Low?level?utility?function?used?by?device?matching.?
  • ?*/??
  • const?struct?of_device_id?*of_match_node(const?struct?of_device_id?*matches,??
  • ?????????????????????const?struct?device_node?*node)??
  • {??
  • ????const?struct?of_device_id?*match;??
  • ????unsigned?long?flags;??
  • ??
  • ????raw_spin_lock_irqsave(&devtree_lock,?flags);??
  • ????match?=?__of_match_node(matches,?node);??
  • ????raw_spin_unlock_irqrestore(&devtree_lock,?flags);??
  • ????return?match;??
  • }??
  • EXPORT_SYMBOL(of_match_node);??
  • 找到 match = __of_match_node(matches, node); 注意著里的node是struct device_node 類型的 [cpp]?view plaincopy
  • const?struct?of_device_id?*__of_match_node(const?struct?of_device_id?*matches,??
  • ???????????????????????const?struct?device_node?*node)??
  • {??
  • ????const?struct?of_device_id?*best_match?=?NULL;??
  • ????int?score,?best_score?=?0;??
  • ??
  • ????if?(!matches)??
  • ????????return?NULL;??
  • ??
  • ????for?(;?matches->name[0]?||?matches->type[0]?||?matches->compatible[0];?matches++)?{??
  • ????????score?=?__of_device_is_compatible(node,?matches->compatible,??
  • ??????????????????????????matches->type,?matches->name);??
  • ????????if?(score?>?best_score)?{??
  • ????????????best_match?=?matches;??
  • ????????????best_score?=?score;??
  • ????????}??
  • ????}??
  • ??
  • ????return?best_match;??
  • }??
  • 繼續追蹤下去 [cpp]?view plaincopy
  • static?int?__of_device_is_compatible(const?struct?device_node?*device,??
  • ?????????????????????const?char?*compat,?const?char?*type,?const?char?*name)??
  • {??
  • ????struct?property?*prop;??
  • ????const?char?*cp;??
  • ????int?index?=?0,?score?=?0;??
  • ??
  • ????/*?Compatible?match?has?highest?priority?*/??
  • ????if?(compat?&&?compat[0])?{??
  • ????????prop?=?__of_find_property(device,?"compatible",?NULL);??
  • ????????for?(cp?=?of_prop_next_string(prop,?NULL);?cp;??
  • ?????????????cp?=?of_prop_next_string(prop,?cp),?index++)?{??
  • ????????????if?(of_compat_cmp(cp,?compat,?strlen(compat))?==?0)?{??
  • ????????????????score?=?INT_MAX/2?-?(index?<<?2);??
  • ????????????????break;??
  • ????????????}??
  • ????????}??
  • ????????if?(!score)??
  • ????????????return?0;??
  • ????}??
  • ??
  • ????/*?Matching?type?is?better?than?matching?name?*/??
  • ????if?(type?&&?type[0])?{??
  • ????????if?(!device->type?||?of_node_cmp(type,?device->type))??
  • ????????????return?0;??
  • ????????score?+=?2;??
  • ????}??
  • ??
  • ????/*?Matching?name?is?a?bit?better?than?not?*/??
  • ????if?(name?&&?name[0])?{??
  • ????????if?(!device->name?||?of_node_cmp(name,?device->name))??
  • ????????????return?0;??
  • ????????score++;??
  • ????}??
  • ??
  • ????return?score;??
  • }??
  • 看這句 prop = __of_find_property(device, "compatible", NULL);

    可以發先追溯到底,是利用"compatible"來匹配的,即設備樹加載之后,內核會自動把設備樹節點轉換成 platform_device這種格式,同時把名字放到of_node這個地方
    ? ?

    platform_driver 部分

    可以看到原來是利用platform_driver 下的 struct driver 結構體中的 name 成員來匹配的,看一下 struct driver 結構體的定義: [cpp]?view plaincopy
  • struct?device_driver?{??
  • ????const?char??????*name;??
  • ????struct?bus_type?????*bus;??
  • ??
  • ????struct?module???????*owner;??
  • ????const?char??????*mod_name;??/*?used?for?built-in?modules?*/??
  • ??
  • ????bool?suppress_bind_attrs;???/*?disables?bind/unbind?via?sysfs?*/??
  • ??
  • ????const?struct?of_device_id???*of_match_table;??
  • ????const?struct?acpi_device_id?*acpi_match_table;??
  • ??
  • ????int?(*probe)?(struct?device?*dev);??
  • ????int?(*remove)?(struct?device?*dev);??
  • ????void?(*shutdown)?(struct?device?*dev);??
  • ????int?(*suspend)?(struct?device?*dev,?pm_message_t?state);??
  • ????int?(*resume)?(struct?device?*dev);??
  • ????const?struct?attribute_group?**groups;??
  • ??
  • ????const?struct?dev_pm_ops?*pm;??
  • ??
  • ????struct?driver_private?*p;??
  • }??
  • ? ? ? 成員中有const struct of_device_id*of_match_table; 是struct of_device_id 類型,定義如下: [cpp]?view plaincopy
  • /*?
  • ?*?Struct?used?for?matching?a?device?
  • ?*/??
  • struct?of_device_id??
  • {??
  • ????char????name[32];??
  • ????char????type[32];??
  • ????char????compatible[128];??
  • ????const?void?*data;??
  • };??
  • ? ? ? 可以看到其作用就是為了匹配一個設備。我們所要做的就是對 char?compatible[128] 的填充;設備樹加載之后,內核會自動把設備樹節點轉換成 platform_device這種格式,同時把名字放到of_node這個地方。

    3、基于設備樹的driver的結構體的填充

    ? ? ? 匹配的方式發生了改變,那我們的platform_driver 也要修改了

    基于設備樹的driver的結構體的填充:

    [cpp]?view plaincopy
  • static?struct?of_device_id?beep_table[]?=?{??
  • ????{.compatible?=?"fs4412,beep"},??
  • };??
  • static?struct?platform_driver?beep_driver=??
  • {??
  • ????.probe?=?beep_probe,??
  • ????.remove?=?beep_remove,??
  • ????.driver={??
  • ????????.name?=?"bigbang",??
  • ????????.of_match_table?=?beep_table,??
  • ????},??
  • };??
  • 原來的driver是這樣的,可以對比一下 [cpp]?view plaincopy
  • static?struct?platform_driver?beep_driver=??
  • {??
  • ????.driver.name?=?"bigbang",??
  • ????.probe?=?beep_probe,??
  • ????.remove?=?beep_remove,??
  • };??

  • 4、設備樹編譯

    ? ? ? 我們在?arch/arm/boot/dts/exynos4412-fs4412.dts 中添加

    [cpp]?view plaincopy
  • fs4412-beep{??
  • ?????????compatible?=?"fs4412,beep";??
  • ?????????reg?=?<0x114000a0?0x4?0x139D0000?0x14>;??
  • };??
  • 就可以編譯設備樹了

    make?dtbs ?在內核根目錄 vim?arch/arm/boot/dts/exynos4412-fs4412.dts
    sudo?cp??arch/arm/boot/dts/exynos4412-fs4412.dtb?/tftpboot/

    ? ? ?然后,將設備樹下載到0x42000000處,并加載驅動 insmod driver.ko, 測試下驅動。

    總結

    以上是生活随笔為你收集整理的Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。