最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Linux驅(qū)動之platform總線詳解

 更新時間:2021年12月14日 16:22:29   作者:->NULL  
大家好,本篇文章主要講的是Linux驅(qū)動之platform總線詳解,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽

1、platform 總線簡介

1.1、Linux 驅(qū)動的分離和分層思想

1.1.1、Linux 驅(qū)動的分離

? ? ? ? 先講 Linux 驅(qū)動的分離,Linux 操作系統(tǒng)支持在各類 CPU 上運行,因為每一種 CPU 對設(shè)備的驅(qū)動不一樣,這樣就造成了 Linux 內(nèi)核中積累了大量代碼,并且這些代碼關(guān)于同一設(shè)備的描述大致相同,這就使得內(nèi)核代碼很冗余。以 CPU 通過 I2C 控制 MPU6050 為例:

? ? ? ? 從圖可以看出每一種平臺下都有一套主機驅(qū)動和一套設(shè)備驅(qū)動,因為每個平臺的 I2C 控制器不同,所以這個主機驅(qū)動得每個平臺配一個自己的,但大家所用的 MPU6050 是一樣的,所以完全可以就共用一套設(shè)備驅(qū)動代碼。完善后框架如下:

????????當(dāng)然,這只是對于 I2C 下的 MPU6050 這個設(shè)備,實際情況下,I2C 下肯定會掛載很多設(shè)備,根據(jù)這個思路,我們可以得到框架為:

? ? ? ? ?而在實際開發(fā)中,I2C 主機驅(qū)動半導(dǎo)體廠家會編寫好,設(shè)備驅(qū)動也由設(shè)備廠家編寫好,我們只需要提供設(shè)備信息即可,如設(shè)備接到那個 I2C 接口上,I2C 速度為多少。這樣就相當(dāng)于把設(shè)備信息從設(shè)備驅(qū)動中剝離出來,而設(shè)備驅(qū)動也會用標(biāo)準(zhǔn)方法去獲取設(shè)備信息(如從設(shè)備樹中獲取設(shè)備信息)。這樣就相當(dāng)于驅(qū)動只負責(zé)驅(qū)動,設(shè)備(信息)只負責(zé)設(shè)備,想辦法將兩者進行匹配即可,來做這個匹配工作的就是總線,這就構(gòu)成了 Linux 中的 總線-驅(qū)動-設(shè)備 模型。結(jié)構(gòu)圖如下:

1.2、platform 平臺驅(qū)動模型

? ? ? ? 上面我們講做設(shè)備驅(qū)動的分離,得到 總線-驅(qū)動-設(shè)備 模型,這個總線就是我平常所說的 I2C、SPI、USB 等總線。但問題是有些設(shè)備是不需要通過某一跟總線的,這是就引入了 platform 總線。

????????這里需要注意的是,platform 總線是區(qū)別于 USB、SPI、I2C 這些總線的虛擬總線。說它虛擬是因為 SoC 與一些外設(shè)如 LED、定時器、蜂鳴器是通過內(nèi)存的尋址空間來進行尋址的,所以 CPU 與這些設(shè)備通信壓根就不需要總線,那么硬件上也就沒有這樣一個總線。但內(nèi)核有對這些設(shè)備做統(tǒng)一管理的需求,所以就對這些直接通過內(nèi)存尋址的設(shè)備虛擬了一條 platform 總線,所有直接通過內(nèi)存尋址的設(shè)備都映射到這條虛擬總線上。

? ? ? ? platform 總線的優(yōu)點:

? ? ? ? 1、通過 platform 總線,可以遍歷所有掛載在?platform 總線上的設(shè)備;

? ? ? ? 2、實現(xiàn)設(shè)備和驅(qū)動的分離,通過 platform 總線,設(shè)備和驅(qū)動是分開注冊的,因為有?probe 函數(shù),可以隨時檢測與設(shè)備匹配的驅(qū)動,匹配成功就會把這個驅(qū)動向內(nèi)核注冊;

? ? ? ? 3、一個驅(qū)動可供同類的幾個設(shè)備使用,這個功能的實現(xiàn)是因為驅(qū)動注冊過程中有一個遍歷設(shè)備的操作。? ? ??

2、platform 框架

2.1、platform 總線

? ? ? ? Linux 內(nèi)核用 bus_type 結(jié)構(gòu)體來表示總線,我們所用的 I2C、SPI、USB 都是用這個結(jié)構(gòu)體來定義的。該結(jié)構(gòu)體如下:

/* include/linux/device.h */
 
struct bus_type {
    const char *name;                                  /* 總線名字 */
    const char *dev_name; 
    struct device *dev_root;
    struct device_attribute *dev_attrs;
    const struct attribute_group **bus_groups;         /* 總線屬性 */
    const struct attribute_group **dev_groups;         /* 設(shè)備屬性 */
    const struct attribute_group **drv_groups;         /* 驅(qū)動屬性 */
 
    int (*match)(struct device *dev, struct device_driver *drv);      /* 設(shè)備驅(qū)動匹配函數(shù) */
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);
 
    int (*online)(struct device *dev);
    int (*offline)(struct device *dev);
    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);
    const struct dev_pm_ops *pm;
    const struct iommu_ops *iommu_ops;
    struct subsys_private *p;
    struct lock_class_key lock_key;
};

? ? ? ? ?platform 總線是 bus_type 類型的常量,之所以說它是常量是因為這個變量已經(jīng)被 Linux 內(nèi)核賦值好了,其結(jié)構(gòu)體成員對應(yīng)的函數(shù)也已經(jīng)在內(nèi)核里面寫好。

定義如下:

/* drivers/base/platform.c */
 
struct bus_type platform_bus_type = {
    .name = "platform",
    .dev_groups = platform_dev_groups,
    .match = platform_match,       /* 匹配函數(shù) */
    .uevent = platform_uevent,
    .pm = &platform_dev_pm_ops,
};

? ? ? ? platform_bus_type 中的 platform_match 就是我們前面所說的做驅(qū)動和設(shè)備匹配的函數(shù),該函數(shù)定義如下:

/* drivers/base/platform.c */
 
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);
 
    /*When driver_override is set,only bind to the matching driver*/
    if (pdev->driver_override)
        return !strcmp(pdev->driver_override, drv->name);
 
    /* 設(shè)備樹OF類型匹配
       驅(qū)動基類的 of_match_table 里的 compatible 匹配表與設(shè)備樹
       每一個設(shè)備節(jié)點的 compatible 屬性作比較,有相同就表示匹配成功 */
    if (of_driver_match_device(dev, drv))
        return 1;
 
    /* ACPI 匹配 */
    if (acpi_driver_match_device(dev, drv))
        return 1;
 
    /* id_table 匹配
       platform 驅(qū)動里的 id_table 數(shù)組會保存很多 id 信息  */
    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;
 
    /* name 匹配
       直接粗暴比較platform 的驅(qū)動和設(shè)備里面的 name 信息 */
    return (strcmp(pdev->name, drv->name) == 0);
}

? ? ? ? 這個匹配函數(shù)什么時候用,在哪里用,我們不妨先留一個懸念。

2.2、platform 驅(qū)動

2.2.1、platform 驅(qū)動定義

? ? ? ? platform 驅(qū)動用結(jié)構(gòu)體 platform_driver 來表示,該結(jié)構(gòu)體內(nèi)容為:

/* include/linux/platform_device.h */
 
struct platform_driver {
    int (*probe)(struct platform_device *);    /* platform驅(qū)動和platform設(shè)備匹配后會執(zhí)行這個probe函數(shù) */
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *);
    struct device_driver driver;                /* 驅(qū)動基類 */
    const struct platform_device_id *id_table;  /* id_table表 */
    bool prevent_deferred_probe;
};

? ? ? ? ?platform_driver 中?const struct platform_device_id *id_table 是 id_table 表,在 platform 總線匹配驅(qū)動和設(shè)備時?id_table 表匹配法時使用的,這個?id_table 表其實是一個數(shù)組,里面的每個元素類型都為?platform_device_id,platform_device_id 是一個結(jié)構(gòu)體,內(nèi)容如下:

struct platform_device_id {
    char name[PLATFORM_NAME_SIZE];
    kernel_ulong_t driver_data;
};

? ? ? ? platform_driver 中?driver 是一個驅(qū)動基類,相當(dāng)于驅(qū)動具有的最基礎(chǔ)的屬性,在不同總線下具有的屬性則存放在?platform_driver 結(jié)構(gòu)體下。

? ? ? ? 驅(qū)動基類結(jié)構(gòu)體?device_driver 內(nèi)容為:

/* include/linux/device.h */
 
struct device_driver {
    const char *name;                               /* platform 總線來匹配設(shè)備與驅(qū)動的第四種方        
                                                       法就是直接粗暴匹配兩者的 name 字段 */
    struct bus_type *bus;
    struct module *owner;
    const char *mod_name; 
    bool suppress_bind_attrs; 
    const struct of_device_id *of_match_table;      /* 采用設(shè)備樹時驅(qū)動使用的的匹配表 */
    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;
};

? ? ? ? ?driver 中 of_match_table 也是一個匹配表,這個匹配表是 platform 總線給驅(qū)動和設(shè)備做匹配時使用設(shè)備樹匹配時用的,也是一個數(shù)組,數(shù)組元素都為?of_device_id 類型,該類型結(jié)構(gòu)體如下:

/* include/linux/mod_devicetable.h */
 
struct of_device_id {
    char name[32];
    char type[32];
    char compatible[128];   /* 使用設(shè)備樹匹配時就是把設(shè)備節(jié)點的 compatible 屬性值和 of_match_table 中
                               每個項目的這個 compatible 作比較,如果有相等的就表示設(shè)備和驅(qū)動匹配成功 */
    const void *data;
};

2.2.2、platform 驅(qū)動注冊

? ? ? ? 用?platform_driver 結(jié)構(gòu)體定義好 platform 驅(qū)動后,用 platform_driver_register 函數(shù)向 Linux 內(nèi)核注冊 platform 驅(qū)動,函數(shù)大致流程如下:

platform_driver_register (drv)
    -> __platform_driver_register
        -> drv->driver.probe = platform_drv_probe;       /* 把 platform_drv_probe 這個函數(shù)賦給
                                                            platform 驅(qū)動里的驅(qū)動基類 drier 的 probe 函數(shù) */
        -> driver_registe (&drv->driver)                 /* 向 Linux 內(nèi)核注冊驅(qū)動基類 driver  */
            -> ...... 
                -> drv->driver->probe                    /* 最終執(zhí)行驅(qū)動基類 driver 的 probe 函數(shù),
                                                            其實就是上面給的 platform_drv_probe 函數(shù) */
                    -> platform_drv_probe
                        -> drv->probe                    /* platform_drv_probe 函數(shù)又會執(zhí)行                 
                                                            platform 驅(qū)動 drv 的 probe 函數(shù) */

? ? ? ? 上面的分析中從 driver_register (&drv->driver) 到 drv->driver->probe 這一步我們用省略號代替了,現(xiàn)在來做一下分析:

driver_register(&drv->driver)
    -> bus_add_driver                             /* 向總線添加驅(qū)動 */
        -> driver_attach 
            -> bus_for_each_dev                   /* 查找總線下每一個設(shè)備,即遍歷操作 */ 
                -> __driver_attach                /* 每個設(shè)備都調(diào)用此函數(shù) */
                    -> driver_match_device        /* 檢查是否匹配 */
                        -> 調(diào)用bus下的match匹配函數(shù)
                    -> driver_probe_device        /* 匹配成功后執(zhí)行此函數(shù) */
                        -> really_probe 
                            -> drv->probe         /* 執(zhí)行drv下的probe函數(shù) */

? ? ? ? 根據(jù)?driver_register 函數(shù)流程,我們就知道了總線的 match 匹配函數(shù)會在這里遍歷使用,這就回答了我們之前留下的一個問題:總線 match 函數(shù)在哪里用,一旦匹配成功就會進入到驅(qū)動的 probe 函數(shù)。?

????????根據(jù) platform_driver_register 函數(shù)流程,我們可以得出一個結(jié)論:向 Linux 內(nèi)核注冊 platform driver 過程里面會有一個遍歷驅(qū)動和設(shè)備匹配的過程,匹配成功后最終會執(zhí)行 platform driver 的 probe 函數(shù),過程中 的驅(qū)動基類 driver 的 probe 函數(shù)和 platform_drv_probe 函數(shù)都是達到這個目的的中轉(zhuǎn)函數(shù)而已。

? ? ? ? 值得注意的是,最終會執(zhí)行的 platform driver 的 probe 函數(shù)是由我們來寫的,所以主動權(quán)又回到我們手里。

2.3、platform 設(shè)備

2.3.1、platform 設(shè)備定義

? ? ? ? 如果我們用的 Linux 版本支持設(shè)備樹,那就在設(shè)備樹中去描述設(shè)備,如果不支持設(shè)備樹,就要定義好 platform 設(shè)備。這里我們需要考慮的一個點是,總線下的匹配函數(shù) match 在做匹配時是先設(shè)備樹匹配,然后 id_table 表匹配,然后才是 name 字段匹配。支持設(shè)備樹時,直接在設(shè)備樹節(jié)點里面改設(shè)備信息,內(nèi)核啟動時會自動遍歷設(shè)備樹節(jié)點,匹配成功就會自動生成一個 platform_device,給下一步來使用。不是設(shè)備樹的話,這個 platform_device 就是由開發(fā)者來寫。

????????這里我們先不用設(shè)備樹,自己來定義 platform 設(shè)備。platform 設(shè)備用 platform_device 結(jié)構(gòu)體來表示,該結(jié)構(gòu)體定義如下:

/* include/linux/platform_device.h */
 
struct platform_device {
    const char *name;                   /* 設(shè)備名,得和對應(yīng)的 platform 驅(qū)動的 name 一樣,
                                           否則設(shè)備就無法匹配到對應(yīng)驅(qū)動 */
    int id; 
    bool id_auto;
    struct device dev;
    u32 num_resources; 
    struct resource *resource;
    const struct platform_device_id *id_entry;
    char *driver_override; /* Driver name to force a match */
    /* MFD cell pointer */
    struct mfd_cell *mfd_cell;
    /* arch specific additions */
    struct pdev_archdata archdata;
};

2.4、platform 匹配過程

? ? ? ? platform 總線對驅(qū)動和設(shè)備的匹配過程其實上面零零碎碎也已經(jīng)講的差不多了,現(xiàn)在我們匯總起來在過一遍。

? ? ? ? 前面也說過,總線下的驅(qū)動和設(shè)備的匹配是通過總線下的 match 函數(shù)來實現(xiàn)的,不同的總線對應(yīng)的 match 函數(shù)肯定不一樣,這個我們不用管,內(nèi)核都會寫好。我們所用的 platform 總線對應(yīng)的 match 函數(shù)是 platform_match 函數(shù),分析一下這個函數(shù):

platform_match
    -> of_driver_match_device          /* 設(shè)備樹匹配 */
    -> acpi_driver_match_device        /* ACPI 匹配 */
    -> platform_match_id               /* platform_driver->id_table 匹配 */
    -> strcmp(pdev->name, drv->name)   /* name 匹配 */

? ? ? ? ?通過對上面匹配函數(shù)的一個簡單分析,我們知道匹配函數(shù)做匹配的順序是先匹配設(shè)備樹,然后匹配 id_table 表,然后才是暴力匹配 name 字段。對于支持設(shè)備樹的 Linux 版本,我們一上來做設(shè)備樹匹配就完事。不支持設(shè)備樹時,我們就得定義 platform 設(shè)備,再用 id_tabale 表或 name 匹配,一般情況下都是選用 name 匹配。

? ? ? ? 現(xiàn)在我們來具體看一下設(shè)備樹條件下的匹配過程:

of_driver_match_device     /* of函數(shù)一般是用于設(shè)備樹,這也算給了我們提示 */
    -> of_match_device (drv->of_match_table, dev)    
        -> of_match_node  
            -> __of_match_node
                -> __of_device_is_compatible
                    -> __of_find_property(device, "compatible", NULL)   /* 取出compatible屬性值 */

? ? ? ? 看上面的分析我們就知道了這個匹配過程最終是驅(qū)動基類的 of_match_table 里的 compatible 去設(shè)備樹節(jié)點里面的?compatible 屬性作比較。這個就是把設(shè)備樹與 platform 總線串起來的一個機理,從而實現(xiàn)了在設(shè)備樹對應(yīng)節(jié)點里面寫設(shè)備信息,驅(qū)動另外單獨寫的目的,也就是我們前面講的驅(qū)動分離。

?3、總結(jié)

? ? ? ? 在具體的開發(fā)過程中我們并不需要真的去寫一個 platform 總線模型,內(nèi)核中都已經(jīng)給我們定義好了。我們對 platform 總線模型的分析主要是搞清楚如何將驅(qū)動和設(shè)備匹配的,即當(dāng)我們插入設(shè)備是如何找到對應(yīng)驅(qū)動或插入驅(qū)動如何找到對應(yīng)設(shè)備的,并最終調(diào)用 probe 函數(shù)。其實不管是先有驅(qū)動后有設(shè)備、還是先有設(shè)備后有驅(qū)動,最終匹配成功后第一件事都是執(zhí)行驅(qū)動的 probe 函數(shù),所以我們盡可放心的忽略中間曲折的情感糾葛,直接把注意力放在最終的 probe 函數(shù)。

到此這篇關(guān)于Linux驅(qū)動之platform總線詳解的文章就介紹到這了,更多相關(guān)Linux驅(qū)動platform總線內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • linux中alarm函數(shù)的實例講解

    linux中alarm函數(shù)的實例講解

    今天小編就為大家分享一篇關(guān)于linux中alarm函數(shù)的實例講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • 詳解CentOS下Nginx如何禁止IP訪問

    詳解CentOS下Nginx如何禁止IP訪問

    服務(wù)器的安全相信是每位運維人員最關(guān)心的問題,不知道大家在使用的時候會不會遇到很多的惡意IP攻擊,最近公司的服務(wù)器就遇到了這個問題,這個時候就要用到Nginx 禁止IP訪問了。本文介紹了詳細的步驟,有同樣問題的朋友們可以參考學(xué)習(xí)。
    2016-10-10
  • linux下php-fpm開啟關(guān)閉使用方法

    linux下php-fpm開啟關(guān)閉使用方法

    自php5.3.3開始,php源碼中包含了php-fpm,不需要單獨通過補丁的方式安裝php-fpm,在源碼安裝的時候直接 configure 中增加參數(shù) –enable-fpm即可,使用方法如下
    2014-03-03
  • 解決CentOS7虛擬機無法上網(wǎng)并設(shè)置CentOS7虛擬機使用靜態(tài)IP上網(wǎng)

    解決CentOS7虛擬機無法上網(wǎng)并設(shè)置CentOS7虛擬機使用靜態(tài)IP上網(wǎng)

    這篇文章主要介紹了解決CentOS7虛擬機無法上網(wǎng)并設(shè)置CentOS7虛擬機使用靜態(tài)IP上網(wǎng),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • CentOS7配置阿里云yum源的方法代碼

    CentOS7配置阿里云yum源的方法代碼

    這篇文章主要介紹了CentOS7配置阿里云yum源的方法代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • 在Linux服務(wù)器上部署War項目教程

    在Linux服務(wù)器上部署War項目教程

    文章講述了如何將War包上傳到Linux服務(wù)器上的步驟,包括使用FTP或SFTP上傳,確認(rèn)并安裝Java運行環(huán)境和Web服務(wù)器(如ApacheTomcat或Nginx),將War包復(fù)制到相應(yīng)的目錄,并重啟服務(wù)以確保部署成功,最后,通過瀏覽器訪問部署的應(yīng)用
    2025-02-02
  • Linux下卸載MySQL8.0版本的操作方法

    Linux下卸載MySQL8.0版本的操作方法

    在本篇文章中小編給各位整理的是關(guān)于Linux下卸載MySQL8.0版本的操作步驟和方法,有需要的朋友們學(xué)習(xí)下。
    2019-11-11
  • apache2.2 支持.net 3.5的設(shè)置方法

    apache2.2 支持.net 3.5的設(shè)置方法

    一直在為asp.net程序的打包發(fā)布頭疼,甚至想過把程序裝好放到vware里!但為什么一直沒有想到apache這位大哥呢?!以至今天才google apache+asp.net。
    2009-10-10
  • Linux cal命令的使用

    Linux cal命令的使用

    這篇文章主要介紹了Linux cal命令的使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • Apache httpd 安裝module mod_expires、mod_deflate的方法

    Apache httpd 安裝module mod_expires、mod_deflate的方法

    Apache httpd 安裝module mod_expires、mod_deflate的方法,需要的朋友可以參考下。
    2011-11-11

最新評論

吕梁市| 吉安市| 通渭县| 新蔡县| 南陵县| 马关县| 建德市| 泰兴市| 黄梅县| 定日县| 大同县| 铜陵市| 土默特左旗| 江永县| 巴青县| 滦南县| 三亚市| 饶平县| 平远县| 璧山县| 仁寿县| 平和县| 夏邑县| 抚州市| 板桥市| 航空| 简阳市| 扬州市| 建昌县| 永和县| 兴安县| 井陉县| 微博| 昌吉市| 错那县| 泰兴市| 宣汉县| 澳门| 福建省| 成安县| 奉化市|