(基於4.14內核版本)
在上一章節linux設備驅動程序-i2c(0)-i2c設備驅動源碼實現中,咱們演示了i2c設備驅動程序的源碼實現,從這一章節開始,咱們來剖析i2c設備驅動程序框架的實現原理。html
在這以前,建議各位先閱讀博主以前的兩篇博客以創建基本linux內核串行通訊框架的概念:
linux設備驅動程序--串行通訊驅動框架分析
linux設備驅動程序--busnode
分析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函數。
那麼:
帶着這兩個疑問,咱們接着往下看。
在這裏咱們難免要回顧一下前面章節所說的,做爲一個驅動開發者,若是咱們要開發某些基於i2c的設備驅動,須要實現的框架流程是怎樣的:
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函數執行了一些什麼行爲?
咱們直接根據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函數,咱們直接查看在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函數並不僅是提供一種匹配方式:
接下來再深刻函數內部,查看匹配的細節部分:
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匹配方式較爲少用且較爲複雜,這裏暫且不作詳細討論
一樣的,咱們查看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不沾身.