linux設備驅動程序-i2c(1):i2c總線的添加與實現

linux設備驅動程序-i2c(1):i2c總線的添加與實現

(基於4.14內核版本)
在上一章節linux設備驅動程序-i2c(0)-i2c設備驅動源碼實現中,咱們演示了i2c設備驅動程序的源碼實現,從這一章節開始,咱們來剖析i2c設備驅動程序框架的實現原理。html

前情回顧

在這以前,建議各位先閱讀博主以前的兩篇博客以創建基本linux內核串行通訊框架的概念:
linux設備驅動程序--串行通訊驅動框架分析
linux設備驅動程序--busnode

i2c總線的初始化

分析i2c框架天然是從i2c總線的初始化開始,一切內核中i2c的相關操做都將創建在i2c總線的基礎上。linux

在實際驅動開發過程當中,i2c總線也是集成在系統中的,驅動開發者不須要關心i2c總線的初始化,只須要將相應的i2c_client和i2c_driver掛載在總線上進行匹配便可。框架

將總線註冊到系統

那麼,i2c總線在系統中是如何初始化得來的呢?函數

答案就在文件i2c-core-base.c中,它的過程是這樣的:post

static int __init i2c_init(void)
{
    ...
    bus_register(&i2c_bus_type);
    ...
}
postcore_initcall(i2c_init);

在i2c_init函數中,使用bus_register()將i2c總線註冊到系統中,那麼這個i2c_init()函數是在哪裏被調用的呢?code

在內核啓動的過程當中,進入C語言環境的第一個入口函數是start_kernel(),可是i2c_init()並不是由start_kernel()間接調用,而是藉助於linux內核中的initcall機制被調用,簡而言之,就是將這個函數地址在編譯階段放入特定的段中,在內核初始化時由某個啓動函數將這些段中的函數一一取出並執行。orm

i2c總線經過postcore_initcall()將init函數註冊到系統中,postcore_initcall的詳解能夠參考另外一篇博客:linux init機制htm

總線對應的參數

在上一節中的bus_register()註冊函數中,從函數名能夠看出,將i2c_bus_type註冊到bus系統中,咱們再來看看i2c_bus_type是何方神聖:blog

struct bus_type {
    ...
    int (*match)(struct device *dev, struct device_driver *drv);
    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);

    struct subsys_private *p;
    ...
};

struct bus_type i2c_bus_type = {
    .name       = "i2c",
    .match      = i2c_device_match,
    .probe      = i2c_device_probe,
    .remove     = i2c_device_remove,
    .shutdown   = i2c_device_shutdown,
};

當這個模塊被加載進系統時,就會執行i2c_init函數來進行初始化。

struct bus_type結構體描述了linux中的各類bus,好比spi bus,i2c bus,platform bus等等,能夠看到,在i2c_bus_type中,定義了match(),probe(),remove()和shutdown()函數,match()函數就是當有新的i2c_client或者i2c_driver添加進來時,試圖尋找與新設備匹配的項,返回匹配的結果。

remove()和shutdown(),顧名思義,這兩個函數是管理的驅動移除行爲的。

而對於probe函數,在以前的章節中提到:當相應的device和driver經由總線匹配上時,會調用driver部分提供的probe函數。

那麼:

  • 總線的probe函數和具體驅動driver部分的probe函數是怎麼一個關係呢?
  • match函數和probe函數究竟是怎麼被調用的,以及執行一些什麼行爲呢?

帶着這兩個疑問,咱們接着往下看。

match()和probe()

在這裏咱們難免要回顧一下前面章節所說的,做爲一個驅動開發者,若是咱們要開發某些基於i2c的設備驅動,須要實現的框架流程是怎樣的:

  • 填充一個i2c_driver結構體,若是以設備樹方式匹配,須要填充of_match_table部分,若是是其餘總線方式匹配,須要填充.driver.name或者.id_table屬性,而後提供一個.probe函數以及一個.remove函數,probe中對設備進行操做,remove負責回收資源。 將i2c_driver註冊到i2c總線中。
  • 若是是設備樹形式,提供相應的設備樹節點。 若是是總線匹配形式,提供一個i2c_client部分註冊到i2c總線中。
  • 當雙方註冊到i2c總線時,device(i2c_client)和driver部分匹配,調用driver提供的probe函數。

linux下的標準總線方式的驅動框架,可是問題是,爲何device和driver都註冊進去以後,就會調用driver部分提供的probe函數呢?

走進源代碼

爲了解決這些問題,最好的辦法就是看源代碼,假設咱們是以設備樹的方式進行匹配,device(即i2c_client)部分已經被註冊到系統中,此時咱們向系統中註冊(insmod由driver部分程序編譯的模塊)相應的driver部分,接下來咱們跟蹤driver部分註冊到i2c總線的流程,看看它是怎麼實現的:
...
static struct i2c_driver drv = {
...
.probe = drv_probe,
.remove = drv_remove,
.id_table = drv_id_table,
};
static __init xxx_init(){
i2c_add_driver(&drv);
}
...

跟蹤i2c_add_driver:

#define i2c_add_driver(driver)  i2c_register_driver(THIS_MODULE, driver)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    ...
    driver->driver.bus = &i2c_bus_type;  //設置當前driver的bus類爲i2c_bus_type
    driver_register(&driver->driver);
    ...
}
int driver_register(struct device_driver *drv)
{
    ...
    bus_add_driver(drv);
    ...
}

通過一系列的代碼跟蹤,找到了bus_add_driver(),根據名稱能夠知道,這個函數就是將當前i2c_driver添加到i2c_bus_type(即i2c總線)中。

接着往下看:

int bus_add_driver(struct device_driver *drv)
{
    ...
    struct bus_type *bus;
    struct driver_private *priv;
    bus = bus_get(drv->bus);            //獲取的bus爲上一節中介紹的i2c_bus_type
    priv->driver = drv;
    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
    ...
    ...
    driver_attach(drv);
    ...
}

從第一部分能夠看到,將當前drv鏈入到bus->p->klist_drivers鏈表中,那麼能夠猜到,在註冊device部分的時候,就會將device鏈入到bus->p->klist_devices中。

而後,再看driver_attach(drv):

int driver_attach(struct device_driver *drv)
{
    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

int bus_for_each_dev(struct bus_type *bus, struct device *start,void *data, int (*fn)(struct device *, void *))
{
    ...
    while ((dev = next_device(&i)) && !error)
        error = fn(dev, data);
    ...
}
static int __driver_attach(struct device *dev, void *data)
{
    ...
    int ret;
    struct device_driver *drv = data;
    ret = driver_match_device(drv, dev);
    check_ret();
    driver_probe_device(drv, dev);
    ...
}

driver_attach調用bus_for_each_dev,傳入當前驅動bus(即i2c_bus_type),當前驅動drv,以及一個函數__driver_attach。

bus_for_each_dev對每一個device部分調用__driver_attach。

在__driver_attach中,對每一個drv和dev調用driver_match_device(),並根據函數返回值決定是否繼續執行調用driver_probe_device()。

從函數名來看,這兩個函數大概就是咱們在前文中提到的match和probe函數了,咱們不妨再跟蹤看看,先看看driver_match_device()函數:

static inline int driver_match_device(struct device_driver *drv,struct device *dev)
{
    return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

果真不出所料,這個函數十分簡單,若是當前驅動的所屬的bus有相應的match函數,就調用match函數,不然返回1.

當前driver所屬的總線爲i2c_bus_type,根據上文i2c總線的初始化部分能夠知道,i2c總線在初始化時提供了相應的match函數,因此,總線的match函數被調用,並返回匹配的結果。

接下來咱們再看看driver_probe_device()函數:

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
    ...
    really_probe(dev, drv);
    ...
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
    ...
    if (dev->bus->probe) {
    ret = dev->bus->probe(dev);
    if (ret)
        goto probe_failed;
    } 
    else if (drv->probe) {
        ret = drv->probe(dev);
        if (ret)
            goto probe_failed;
    }
    ...
}

在driver_probe_device中又調用了really_probe,在really_probe()中,先判斷當前bus總線中是否註冊probe()函數若是有註冊,就調用總線probe函數,不然,就調用當前drv註冊的probe()函數。

到這裏,咱們解決了上一節中的一個疑問:總線和driver部分都有probe函數時,程序是怎麼處理的?

答案已經顯而易見了:優先調用總線probe函數。

並且咱們理清了總線的match()函數和probe()函數是如何被調用的。

那麼,上一節中還有一個疑問沒有解決:總線的match和probe函數執行了一些什麼行爲?

總線probe函數

咱們直接根據i2c總線初始化時設置的probe函數來查看:

struct bus_type i2c_bus_type = {
    .match      = i2c_device_match,
    .probe      = i2c_device_probe,
};
static int i2c_device_probe(struct device *dev)
{
    ...
    struct i2c_driver   *driver;
    driver = to_i2c_driver(dev->driver);
    if (driver->probe_new)
        status = driver->probe_new(client);
    else if (driver->probe)
        status = driver->probe(client,
                    i2c_match_id(driver->id_table, client));
    else
        status = -EINVAL;
    ...
}

對於總線probe函數,獲取匹配成功的device,再由device獲取driver,優先調用driver->probe_new,由於driver中沒有設置,直接調用driver的probe函數。

總線probe和driver的probe函數的關係就是:當match返回成功時,優先調用總線probe,總線probe完成一系列的初始化,再調用driver的probe函數,若是總線probe函數不存在,就直接調用driver的probe函數,因此當咱們在系統中添加了相應的device和driver部分以後,driver的probe函數就會被調用。

總線match函數

對於總線match函數,咱們直接查看在i2c總線初始化時的函數定義:

struct bus_type i2c_bus_type = {
    .match      = i2c_device_match,
    .probe      = i2c_device_probe,
};

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
    struct i2c_client   *client = i2c_verify_client(dev);
    struct i2c_driver   *driver;

    if (i2c_of_match_device(drv->of_match_table, client))
        return 1;

    if (acpi_driver_match_device(dev, drv))
        return 1;

    driver = to_i2c_driver(drv);

    if (i2c_match_id(driver->id_table, client))
        return 1;

    return 0;
}

i2c_device_match就是i2c_driver與i2c_device匹配的部分,在i2c_device_match函數中,能夠看到,match函數並不僅是提供一種匹配方式:

  • i2c_of_match_device,看到of咱們就應該立刻意識到這是設備樹的匹配方式。
  • acpi_driver_match_device則是acpi的匹配方式,acpi的全稱爲the Advanced Configuration & Power Interface,高級設置與電源管理。
  • i2c_match_id(),經過註冊i2c_driver時提供的id_table進行匹配,這是設備樹機制產生以前的主要配對方式。

接下來再深刻函數內部,查看匹配的細節部分:

設備樹匹配方式

const struct of_device_id *i2c_of_match_device(const struct of_device_id *matches,
            struct i2c_client *client)
{
    const struct of_device_id *match;
    match = of_match_device(matches, &client->dev);
    if (match)
        return match;

    return i2c_of_match_device_sysfs(matches, client);
}

在i2c_of_match_device中,調用了of_match_device()和i2c_of_match_device_sysfs()兩個函數,這兩個函數表明了兩種匹配方式,先來看看of_match_device:

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);
}
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;
    match = __of_match_node(matches, node);
    return match;
}
static 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;
    for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
        __of_device_is_compatible(node, matches->compatible,
                        matches->type, matches->name);
        ...
    }
    ...
}

of_match_device調用of_match_node。

of_match_node調用__of_match_node函數。

若是你對設備樹有必定的瞭解,就知道系統在初始化時會將全部設備樹子節點轉換成由struct device_node描述的節點。
瞭解linux設備樹能夠參考個人另外一系列博客:linux設備驅動程序-設備樹(0)-dtb格式
在被調用的__of_match_node函數中,對device_node中的compatible屬性和driver部分的of_match_table中的compatible屬性進行匹配,因爲compatible屬性能夠設置多個,因此程序中會對其進行逐一匹配。

咱們再回頭來看設備樹匹配方式中的i2c_of_match_device_sysfs()匹配方式:

static const struct of_device_id*  i2c_of_match_device_sysfs(const struct of_device_id *matches,struct i2c_client *client)
{
    const char *name;
    for (; matches->compatible[0]; matches++) {
        if (sysfs_streq(client->name, matches->compatible))
            return matches;
        name = strchr(matches->compatible, ',');
        if (!name)
            name = matches->compatible;
        else
            name++;
        if (sysfs_streq(client->name, name))
            return matches;
    }
    return NULL;
}

由i2c_of_match_device_sysfs()的實現能夠看出:當設備樹中不存在設備節點時,driver部分的of_match_table中的compatible屬性試圖去匹配i2c_client(device部分)的.driver.name屬性.

由於設備樹的默認規則,compatible屬性通常形式爲"vender_id,product_id",當compatible全字符串匹配不成功時,取product_id部分再進行匹配,這一部分主要是兼容以前的不支持設備樹的版本。

acpi匹配方式較爲少用且較爲複雜,這裏暫且不作詳細討論

id_table匹配方式

一樣的,咱們查看i2c_match_id(driver->id_table, client)源代碼:

const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
                    const struct i2c_client *client)
{
    while (id->name[0]) {
        if (strcmp(client->name, id->name) == 0)
            return id;
        id++;
    }
    return NULL;
}

這種匹配方式一目瞭然,就是對id_table(driver部分)中的.name屬性和i2c_client(device部分)的.name屬性進行匹配。那麼i2c_client的.name是怎麼來的呢?

在非設備樹的匹配方式中,i2c_client的.name屬性由驅動開發者指定,而在設備樹中,i2c_client由系統對設備樹進行解析而來,i2c_client的name屬性爲設備樹compatible屬性"vender_id,product_id"中的"product_id",因此,在進行匹配時,默認狀況下並不會嚴格地要求
of_match_table中的compatible屬性和設備樹中compatible屬性徹底匹配,driver中.drv.name屬性和.id.name屬性也能夠與設備樹中的"product_id"進行匹配。

好了,關於linux i2c總線的初始化以及運行機制的討論就到此爲止啦,若是朋友們對於這個有什麼疑問或者發現有文章中有什麼錯誤,歡迎留言

原創博客,轉載請註明出處!

祝各位早日實現項目叢中過,bug不沾身.

相關文章
相關標籤/搜索