(基于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不沾身.