Linux I2C子系统驱动阐述

来源:互联网 发布:万人网络营销软件站 编辑:程序博客网 时间:2024/06/03 02:30

一、开场白:

        大家好,今天文章的主题是对Linux kernel I2C子系统驱动的阐述。软件平台介绍:Linux版本3.0.36,Android版本4.2.2。如有错误的地方还请大家指出,互相学习。


二、驱动背景:

        上篇文章《Linux基本设备驱动阐述》中已经介绍了一些Linux I2C驱动的基本知识,从中我们可以知道Linux I2C驱动的编写由两部分组成:I2C总线驱动编写、I2C设备驱动编写。I2C总线驱动(i2c-adapter)主要负责驱动具体CPU I2C通信模块,完成基本的I2C数据通信功能;I2C设备驱动(i2c-driver)主要负责驱动具体的外设硬件,让外设进入正常的工作状态。完成两部分驱动的编写,最基本的内容就是完善Linux kernel提供出来的相关接口,而调用和管理这些接口就是I2C核心框架(i2c-core)。综上可知Linux I2C子系统由三部分组成:I2C核心框架(i2c-core)、I2C总线驱动(i2c-adapter)、I2C设备驱动(i2c-driver)。我们可以通过下图来简单表示下Linux I2C子系统的基本内容:I2C设备驱动(i2c-driver)和I2C总线驱动(i2c-adapter)通过填充和完善I2C核心框架提供的接口,来保证Linux I2C子系统的正常工作通信。


从上图我们也可以知道要分析Linux I2C子系统的工作原理与流程,就需要从I2C核心框架对外提供的接口开始分析(i2c-driver with i2c-core & i2c-adapter with i2c-core)。而通过上篇文章《Linux基本设备驱动阐述》的介绍,相信大家都比较熟悉I2C设备驱动的开发。因此本篇通过使用上篇文章所编写的TPS65185设备驱动,来完成I2C设备驱动与I2C核心框架之间的接口分析,进而完成I2C核心框架的分析和I2C总线驱动的编写。(说明1:下面的分析中I2C核心框架用i2c-core代替、I2C设备驱动用i2c-driver代替、I2C总线驱动用i2c-adapter代替;说明2:上篇文章中I2C设备注册必须得放在MECHINE_START中的.init_machine成员函数中,具体原因下面篇幅进行解释)。


三、子系统分析:

        TPS65185设备驱动与i2c-core的交互接口有三部分:I2C设备注册接口i2c_register_board_info、I2C设备驱动注册接口i2c_add_driver、I2C设备通信接口i2c_transfer,因此我们按照接口的调用先后来完成对这三个接口的分析。

3.1、首先我们从I2C设备注册接口(i2c_register_board_info)开始分析,TPS65185设备驱动中与此接口相关的代码如下所示:

/* 设备信息 */static struct i2c_board_info __initdata i2c0_info[] = {{.type        = "tps65185",.addr        = 0x1d,.flags        = 0,.platform_data = NULL,},};/* 板级初始化函数 */void __init init_machine(void) {    .........    i2c_register_board_info(0, i2c0_info, ARRAY_SIZE(i2c0_info));    .........    return;}
由上可知I2C设备的注册,即是完成2c_board_info数据结构的注册,具体的处理过程我们需要通过分析i2c_register_board_info函数来完成。此函数的代码简化后如下所示:

// file : kernel/drivers/i2c/i2c-boardinfo.c/* These symbols are exported ONLY FOR the i2c core. * No other users will be supported. *///......LIST_HEAD(__i2c_board_list);EXPORT_SYMBOL_GPL(__i2c_board_list);int __i2c_first_dynamic_bus_num;EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);int __initi2c_register_board_info(int busnum,    struct i2c_board_info const *info, unsigned len){    .........    /* dynamic bus numbers will be assigned after the last static one      * 当注册的I2C设备总线号大于Linux I2C子系统可用总线号时,         * 将Linux I2C可用总线号涵盖注册的I2C设备总线号      */    if (busnum >= __i2c_first_dynamic_bus_num)                __i2c_first_dynamic_bus_num = busnum + 1;        for (status = 0; len; len--, info++) {        // I2C设备的数据结构,其由两个成员 busnum、board_info        struct i2c_devinfo    *devinfo;                            devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);                if (!devinfo) {            pr_debug("i2c-core: can't register boardinfo!\n");            status = -ENOMEM;            break;        }        devinfo->busnum = busnum;                            devinfo->board_info = *info;        // 将初始化后的I2C设备数据结构添加至__i2c_board_list链表中        list_add_tail(&devinfo->list, &__i2c_board_list);        }    .........    return status;}
i2c_register_board_info函数比较简单,其主要由三个动作组成:更新I2C可用总线号(__i2c_first_dynamic_bus_num)、I2C设备结构(i2c_devinfo)初始化、更新I2C设备列表(__i2c_board_list)。__i2c_first_dynamic_bus_num和__i2c_board_list两个数据结的定义即初始化是由i2c_boardinfo完成的,而其使用都是i2c-core进行的,因此我们进入i2c-core来进行相关分析。i2c-core对这两个数据结构的使用如下所示:

// file: kernel/drivers/i2c/i2c-core.cstatic int i2c_register_adapter(struct i2c_adapter *adap)    {    .........    /* create pre-declared device nodes     * 设备的预先申明是由i2c_register_board_info完成,由于I2C设备的注册早于i2c-adapter的注册,     * 因此如果此时注册的i2c-adapter->nr总线号在I2C可用总线号中,则会去扫描设备列表创建相关设备节点     */    if (adap->nr < __i2c_first_dynamic_bus_num)                i2c_scan_static_board_info(adap);    /* Notify drivers <完成i2c-adapter与i2c-driver的绑定> */    /* 这种绑定方式在早期内核(2.6之前)被使用,称为legacy方式.     * 2.6+内核引入new-style方式代替legacy方式,由于new-style方式更加符合标准设备驱动模块框架,     * 此部分对new-style方式没有影响.     */    mutex_lock(&core_lock);                                                bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);        mutex_unlock(&core_lock);    .........}static void i2c_scan_static_board_info(struct i2c_adapter *adapter){    struct i2c_devinfo    *devinfo;    // 全局变量__i2c_board_list的访问需要利用互斥锁进行保护    down_read(&__i2c_board_lock);                /* 循环遍历__i2c_board_list设备列表,如果devinfo总线号与adapter总线号相同     * 则调用i2c_new_device完成I2C设备创建注册,i2c_new_device大致动作如下:     * i2c_client的创建及初始化、将此设备添加至Linux设备树device_register     */    list_for_each_entry(devinfo, &__i2c_board_list, list) {                if (devinfo->busnum == adapter->nr                                    && !i2c_new_device(adapter,                                                &devinfo->board_info))            dev_err(&adapter->dev,                "Can't create device at 0x%02x\n",                devinfo->board_info.addr);    }    up_read(&__i2c_board_lock);}
i2c-core对__i2c_first_dynamic_bus_num和__i2c_board_list两个数据成员的使用,由上述两个函数构成:i2c_register_adapter、i2c_scan_static_board_list。通过函数的名称我们就能大概猜出其实现的功能:i2c_register_adapter函数是i2c-core提供给i2c-adapter进行注册的接口;i2c_scan_static_board_list是i2c-core内部用于扫描设备列表用的。i2c-adapter调用i2c_register_adapter进行注册的时候,会通过将adapter本身的总线号nr和I2C可用总线号__i2c_first_dynamic_bus_num进行对比,如果adapter->nr存在于可用总线号中(adapter->nr < __i2c_first_dynamic_bus_num),那么则会调用i2c_scan_static_board_list来扫描I2C设备列表。i2c_scan_static_board_list函数中通过循环遍历__i2c_board_list设备列表,如果__i2c_board_list设备列表中存在设备是挂载在本次注册的adaptor上时,则会调用i2c_new_device来处理。i2c_new_device的代码如下所示:
//file : kernel/drivers/i2c/i2c-core.cstruct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info){    struct i2c_client    *client;    int            status;    // 每个I2C设备都对应一个唯一i2c_client,如TPS65185外设芯片就会对应一个i2c_client    client = kzalloc(sizeof *client, GFP_KERNEL);        if (!client)        return NULL;    /* 每个client都拥有一个adapter成员,其代表着I2C设备的通信方法.     * i2c_transfer通信函数就是通过间接调用adapter->master_xfer通信函数实现数据通信的.     */    client->adapter = adap;    client->dev.platform_data = info->platform_data;    if (info->archdata)        client->dev.archdata = *info->archdata;    client->flags = info->flags;    client->addr = info->addr;        // addr即代表着外设器件的唯一器件地址    client->irq = info->irq;            strlcpy(client->name, info->type, sizeof(client->name));    .........        /* 每个client都拥有一个dev成员,dev初始化完成后,会将其注册至Linux设备树上     * (device_register),后面i2c_driver注册的时候(driver_register),     * 会通过通过bus_for_each_dev函数去搜索Linux设备树,每次从Linux设备树上搜索     * 的结果就是之前注册的dev成员,利用container_of即可获取出i2c_client成员.     */    client->dev.parent = &client->adapter->dev;    client->dev.bus = &i2c_bus_type;        // 标记此设备是挂载的总线i2c_bus上        client->dev.type = &i2c_client_type;    // 标记此设备类型是i2c_client_type    client->dev.of_node = info->of_node;    .........        status = device_register(&client->dev);    // 将设备添加到Linux设备树中    if (status)        goto out_err;    dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",        client->name, dev_name(&client->dev));    return client;}
i2c_new_device函数的功能可以分解成三部分:申请创建i2c_client;为i2c_client记录下与其对应的adapter;为i2c_client记录相关设备信息(eg. addr);初始化i2c_client->device成员,并将其挂载至Linux设备树上;上述动作完成后,i2c_driver在注册的时候就能搜索出此设备,从而进行device和driver的匹配。

从上面的讨论中,我们可以总结出以下几点:1、init_machine函数中会调用i2c_register_board_info函数;2、i2c_register_board_info函数会根据I2C设备的相关信息来初始化两个非常重要的数据成员,I2C系统可用总线号__i2c_first_dynamic_bus_num和I2C设备列表__i2c_board_list;3、上述两个数据成员被i2c_register_adapter函数用于匹配I2C设备和I2C总线,当匹配成功后创建并初始化i2c_client,并将配对成功的设备添加至Linux设备树中。4、i2c_register_adapter接口是i2c-core提供给i2c-adapter注册所用接口,此函数会在i2c-adapter模块加载的时候被调用;到此为止我们对i2c_register_board_info的设备注册的接口分析到此结束,下面开始对I2C驱动注册的接口展开分析。


3.2、通过分析TPS65185设备驱动的源码可知,I2C驱动注册的结构为i2c_add_driver。而i2c_add_driver函数定义在kernel/include/linux/i2.h文件中,相关源码定义如下:

// file : kernel/drivers/test/tps65185.cstatic struct i2c_driver tps65185_driver = {    .probe    =   tps65185_probe,    .remove   =   tps65185_remove,    .suspend  =   tps65185_suspend,    .resume   =   tps65185_resume,    .id_table =   tps65185_id_table,    .driver   = {        .name = "tps65185 driver",        .owner= THIS_MODULE,    },  }; /* 驱动模块加载函数 */static int __init tps65185_init(void) {    i2c_add_driver(&tps65185_driver);    return 0;}// file : kernel/include/linux/i2c.hstatic inline int i2c_add_driver(struct i2c_driver *driver){     return i2c_register_driver(THIS_MODULE, driver);}
从上面源码可以看出,i2c_add_driver函数是通过调用i2c_register_driver接口来完成I2C驱动的注册。i2c_register_driver函数则是由i2c-core提供实现,此接口函数的代码量非常小,仅仅是通过利用driver_register接口来完成驱动的注册。其源码如下所示:
// file: kernel/drivers/i2c/i2c-core.c/* * An i2c_driver is used with one or more i2c_client (device) nodes to access * i2c slave chips, on a bus instance associated with some i2c_adapter. */int i2c_register_driver(struct module *owner, struct i2c_driver *driver){    int res;    /* Can't register until after driver model init */    if (unlikely(WARN_ON(!i2c_bus_type.p)))        return -EAGAIN;    /* add the driver to the list of i2c drivers in the driver core */    driver->driver.owner = owner;    driver->driver.bus = &i2c_bus_type;    /* When registration returns, the driver core     * will have called probe() for all matching-but-unbound devices.     */    res = driver_register(&driver->driver);    if (res)        return res;    .........        /* Walk the adapters that are already present */    /* 此函数用于绑定driver和client,      * I2C设备驱动为legacy形式时才有效,new-style形式是无效      */     i2c_for_each_dev(driver, __process_new_driver);    return 0;}
对于TPS65185设备驱动而言,其传给i2c_register_driver的i2c_driver参数为tps65185_driver。上篇文章《Linux基本设备驱动阐述》中,我们有对其probe、remove、suspend、resume、id_table五个成员进行分析,唯独没有进行详细阐述的就是tps65185->driver成员。struct device_driver和struct device作为Linux设备驱动模型最重要的两个数据成员,对这两个数据成员最重要的接口分别为driver_register和device_register。struct device_driver和struct device这两个成员帮助了内核统一设备驱动操作接口,这样一来Linux基本的设备驱动框架就不需要直接与各个子系统定义的数据结构进行交互。在i2c_register_driver接口中,我们初始化了tps65185_driver->driver的bus成员为i2c_bus_type,这样一来此驱动则属于I2C总线上的设备驱动。之后调用bus_for_each_dev查找设备的时候,则会仅仅搜索I2C总线上注册的设备。tps65185_driver->driver成员初始化完成之后则调用driver_register函数进行驱动的注册动作。从上面代码的注释中我们可以了解到,driver_register在设备驱动匹配成功后会去调用设备驱动的probe函数。为什么会调用到设备驱动的probe呢,我相信大家都会对此感兴趣的。下面我们就对driver_register进行一个简单的分析,本次分析主要目的就是查出driver_register是怎么样调用到设备驱动的probe成员的。

分析设备驱动的probe函数函数调用过程,最直接的方式就是在设备驱动程序的probe函数中添加dump_stack()函数。dump_stack()函数会查找相关堆栈信息,最后以log的形式打印出内核调用的函数流程。现在我们就在tps65185_probe函数中添加dump_stack(),重新启动机器通过串口输出的相关log如下所示:

<4> [<c043dac4>] (unwind_backtrace+0x0/0xe0) from [<c05e47c4>] (tps65185_probe+0xc/0xb8)<4> [<c05e47c4>] (tps65185_probe+0xc/0xb8) from [<c06a8b94>] (i2c_device_probe+0xa8/0xd4)<4> [<c06a8b94>] (i2c_device_probe+0xa8/0xd4) from [<c061b9e8>] (driver_probe_device+0xc8/0x1a8)<4> [<c061b9e8>] (driver_probe_device+0xc8/0x1a8) from [<c061bb28>] (__driver_attach+0x60/0x84)<4> [<c061bb28>] (__driver_attach+0x60/0x84) from [<c061ac38>] (bus_for_each_dev+0x4c/0x84)<4> [<c061ac38>] (bus_for_each_dev+0x4c/0x84) from [<c061b304>] (bus_add_driver+0xbc/0x248)<4> [<c061b304>] (bus_add_driver+0xbc/0x248) from [<c061bffc>] (driver_register+0xa8/0x138)<4> [<c061bffc>] (driver_register+0xa8/0x138) from [<c06aadb4>] (i2c_register_driver+0x40/0xac)<4> [<c06aadb4>] (i2c_register_driver+0x40/0xac) from [<c0418fec>] (tps65185_init+0x10/0x1c)<4> [<c0418fec>] (tps65185_init+0x10/0x1c) from [<c0433634>] (do_one_initcall+0x90/0x160)<4> [<c0433634>] (do_one_initcall+0x90/0x160) from [<c0408920>] (kernel_init+0x94/0x13c)<4> [<c0408920>] (kernel_init+0x94/0x13c) from [<c0439238>] (kernel_thread_exit+0x0/0x8)
从上面的内核log就很明显的看出,内核调用到TPS65185驱动probe函数的流程了。在分析这段log之前,我先简单介绍下内核的相关启动流程。我们知道最初内核启动的过程中,首先会运行一段汇编代码head.S,然后就会调用到第一个C函数start_kernel()。start_kernel()完成部分初始化之后,则会启动内核第一个线程kernel_init(),熟悉了这个之后,我们就可以开始分析上面的log了。首先我们来分析kernel_init()这个线程的具体代码:
// file: kernel/init/main.cstatic int __init kernel_init(void * unused){.........do_basic_setup();.........}static void __init do_basic_setup(void){    cpuset_init_smp();    usermodehelper_init();    init_tmpfs();    driver_init();    init_irq_proc();    do_ctors();    do_initcalls();}extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];static void __init do_initcalls(void){    initcall_t *fn;    for (fn = __early_initcall_end; fn < __initcall_end; fn++)        do_one_initcall(*fn);}
从上面代码的注释中,我们可以了解到kernel_init调用do_basic_setup函数来完成硬件平台相关的初始化动作。do_basic_setup调用了driver_init函数和do_initcalls函数,driver_init函数完成了驱动框架的初始化,do_initcalls完成平台硬件的初始化。为了不偏离文章主线太远,driver_init函数我们这里不做分析,有兴趣的读者可以自行分析。do_initcalls完成具体硬件的初始化,主要是通过调用位于init代码段的所有函数来完成的,简单的说就是按照指定的优先级来调用被__init字段修饰的函数。TPS65185驱动加载函数就是位于__init代码段的,它的优先级为subsys_initcall_sync,因此do_initcalls函数是会间接调用到tps65185_init驱动模块加载函数的。结合上面对i2c_register_driver的初步分析,我们已经理解清楚了dump_stack打印出的以下log部分的调用流程:
<4> [<c061bffc>] (driver_register+0xa8/0x138) from [<c06aadb4>] (i2c_register_driver+0x40/0xac)<4> [<c06aadb4>] (i2c_register_driver+0x40/0xac) from [<c0418fec>] (tps65185_init+0x10/0x1c)<4> [<c0418fec>] (tps65185_init+0x10/0x1c) from [<c0433634>] (do_one_initcall+0x90/0x160)<4> [<c0433634>] (do_one_initcall+0x90/0x160) from [<c0408920>] (kernel_init+0x94/0x13c)<4> [<c0408920>] (kernel_init+0x94/0x13c) from [<c0439238>] (kernel_thread_exit+0x0/0x8)
driver_register部分涉及相关的知识点非常多,为了不偏离主线,下面的分析我们则需要以dump_stack的log为参照来进行分析。driver_register部分简化后的代码如下所示:
// file: kernel/drivers/base/driver.cint driver_register(struct device_driver *drv){    .........    // 判断驱动是否已经注册过    other = driver_find(drv->name, drv->bus);    .........    // 驱动具体注册    ret = bus_add_driver(drv);    .........}// file: kernel/drivers/base/bus.cint bus_add_driver(struct device_driver *drv){    .........    /* drivers_autoprob成员默认值为 1,此条件一般成立。     * 调用driver_attach完成驱动与设备的匹配。     */    if (drv->bus->p->drivers_autoprobe) {        error = driver_attach(drv);        if (error)        {            printk(KERN_ERR "driver_attach failed\n");            goto out_unregister;        }    }    // 设备与驱动匹配成功,完成设备文件节点的创建等    .........}// file: kernel/drivers/base/dd.cint driver_attach(struct device_driver *drv){    /* 循环遍历对应总线的设备树,获取出device后调用     * __driver_attch测试设备与驱动是否匹配     */    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);}static int __driver_attach(struct device *dev, void *data){    struct device_driver *drv = data;    /*     * Lock device and try to bind to it. We drop the error     * here and always return 0, because we need to keep trying     * to bind to devices and some drivers will return an error     * simply if it didn't support the device.     *     * driver_probe_device() will spit a warning if there     * is an error.     */    // 调用drv->bus->mach函数测试驱动与设备是否匹配    if (!driver_match_device(drv, dev))        return 0;    .........    /* 调用drv->bus->probe函数,间接完成设备驱动的挂载     * 也就是说设备驱动的每次挂载,都会引起相应总线的probe动作     */    device_lock(dev);    if (!dev->driver)        driver_probe_device(drv, dev);        device_unlock(dev);    ........    return 0;}
device_register函数通过driver_find对传入的设备驱动driver进行初步的查重后,通过调用bus_add_driver函数完成设备驱动的进一步注册。bus_add_driver函数通过driver_attach函数对驱动和设备进行匹配,如果匹配成功则继续完成设备节点创建等动作,如果匹配失败则退出bus_add_driver。driver_attach函数通过bus_for_each_dev函数循环遍历drv->bus总线设备树,遍历获得的设备通过__driver_attach函数完成驱动进行具体的匹配动作。__driver_attach函数通过driver_match_device函数验证设备与驱动是否匹配,如果匹配成功则调用driver_probe_device函数来完成设备驱动的挂载动作。这两部分源码如下所示:

// file: kernel/drivers/base/base.hstatic inline int driver_match_device(struct device_driver *drv,      struct device *dev){    // 调用对应总线的match成员函数,验证驱动与设备是否匹配    return drv->bus->match ? drv->bus->match(dev, drv) : 1;}// file: kernel/drivers/base/dd.cint driver_probe_device(struct device_driver *drv, struct device *dev){    int ret = 0;    if (!device_is_registered(dev))        return -ENODEV;    .........    ret = really_probe(dev, drv);    .........}static int really_probe(struct device *dev, struct device_driver *drv){    // 设备dev记录下,与其匹配的driver方法    dev->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_match_device函数是通过调用drv->bus总线的match成员函数来验证配是否成功,driver_probe_device函数也是通过调用dev->bus总线的probe成员函数来完成具体驱动的probe动作。由上面对i2c_register_driver接口的分析可以知道drv->bus = &i2c_bus_type,因此driver_match_device函数则是调用i2c_bus_type.match函数完成。由上面对i2c_new_device接口的分析也可以知道dev->bus = &i2c_bus_type,因此driver_probe_device函数则是调用i2c_bus_type.probe函数完成。下面给出i2c_bus_type的相关代码:
// file: kernel/drivers/i2c/i2c-core.cstruct bus_type i2c_bus_type = {    .name        = "i2c",    .match        = i2c_device_match,    .probe        = i2c_device_probe,    .remove        = i2c_device_remove,    .shutdown    = i2c_device_shutdown,    .pm            = &i2c_device_pm_ops,};static int i2c_device_match(struct device *dev, struct device_driver *drv){    /* 每个i2c device都会对应唯一一个client,     * 因此可以采用container_of形式反向获取出client成员     */    struct i2c_client    *client = i2c_verify_client(dev);    struct i2c_driver    *driver;    if (!client)        return 0;    /* Attempt an OF style match */    if (of_driver_match_device(dev, drv))        return 1;    driver = to_i2c_driver(drv);    /* match on an id table if there is one */    if (driver->id_table)        // 如果驱动的id_table存在,则继续进行匹配验证        return i2c_match_id(driver->id_table, client) != NULL;    return 0;}static 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;}static int i2c_device_probe(struct device *dev){    /* 每个i2c device都会对应唯一一个client,     * 因此可以采用container_of形式反向获取出client成员     */    struct i2c_client    *client = i2c_verify_client(dev);    struct i2c_driver    *driver;    int status;    if (!client)        return 0;    driver = to_i2c_driver(dev->driver);    if (!driver->probe || !driver->id_table)        return -ENODEV;    client->driver = driver;    ........    // 调用真正设备驱动的probe成员函数    status = driver->probe(client, i2c_match_id(driver->id_table, client));    ........}
i2c_bus_type.match成员即为i2c_device_match函数,i2c_bus_type.probe成员即为i2c_device_probe函数。i2c_device_match函数较为简单,就是通过对比client->type名称是否与driver->id_table中的name名称是否一样来完成,由于其代码较为简单就不进行详细分析。i2c_device_probe函数对driver成员进行简单的防错处理后,则调用driver->probe函数完成设备驱动的probe动作。对于TPS65185设备驱动程序而言,driver->probe函数即为tps65185_probe函数。这样一来内核是怎么调用到设备驱动的probe函数就分析完毕了,具体的调用流程也和dump_stack打印出来的log一致。本次分析设备驱动的probe调用流程,可以用下图来进行总结。内核在启动(kernel/init)的时候会主动去调用驱动模块的加载函数(tps65185_init),模块驱动的加载函数(tps65185_init)会调用相关驱动子系统的驱动注册函数(kernel/drivers/i2c),相关驱动子系统(kernel/drivers/i2c)通过linux的基本设备驱动框架(kernel/drivers/base)来完成驱动的完整注册,基本的驱动框架(kernel/drivers/base)在驱动注册过程中需要调用到具体子系统的匹配探测接口(kernel/drivers/i2c),才能正确的完成设备驱动的注册。


理解清楚内核是如何调用到设备驱动的probe成员函数,对我们编写和调试设备驱动的帮助其实不大,因为这样最多只能够让我们对linux的设备驱动模型有个初步的理解。虽然理解整个调用流程仅仅是为了学习,但是认识设备驱动的probe是由其对应总线的probe所调用是非常重要的。设备驱动的probe的传入参数实际上是总线的probe所构造的,理解这一点对我们驱动的开发是非常有好处的。以i2c_bus_type.probe成员函数为例,i2c_device_probe通过将client->driver = driver一句,就将client与设备驱动进行了关联。而在之前的i2c_new_device接口中client->adapter = adapter一句,就将client与总线驱动进行了关联。最后i2c_device_probe通过将client传递给设备驱动的probe接口,这样一来driver和adapter就通过client进行的间接的关联,也就是说设备驱动在调用i2c_transfer接口的时候,通过传入client->adapter参数就选用出正确的总线驱动来进行i2c通信。理解清楚这个流程后,如果设备驱动在运行的过程中如果出现i2c不能通信,那么我们就可以根据此思路加上添加相关log来进行调试。到此为止我们对i2c_register_driver驱动注册接口的分析就结束了,下面我们开始对i2c_transfer接口进行相关分析。


3.3、从上篇文章《Linux基本设备驱动阐述》中,我们可以了解到i2c-core提供了两类通信接口供设备驱动使用:普通的I2C通信接口(i2c_master_send、i2c_master_recv、i2c_transfer),Smbus通信接口(i2c_smbus_xfer)。对于普通的I2C通信接口而言,通过接口参数完成对msg的封装后,最后都是调用i2c_transfer函数来完成实际的物理通信。i2c_transfer接口则是通过调用具体的总线驱动adapter->algo->master_xfer接口来完成实际的物理通信。对于Smbus方式而言,通过接口参数完成对msg的封装后,最后是调用i2c_smbus_xfer。当我们实际的硬件支持Smbus方式的时候,i2c_smbus_xfer则会调用总线驱动的adapter->algo->smbus_xfer接口来完成实际的物理通信;而若实际硬件不支持的话,则会调用i2c_smbus_xfer_emulator软件仿真来完成实际的物理通信。而i2c_smbus_xfef_emulator接口其实就是通过调用i2c_transfer接口来实现软件模拟对Smbus协议的支持。对于RK3026硬件平台而言,是不能直接支持Smbus协议的,因此对于Smbus通信接口的实现则是通过软件模拟来代替。具体相关代码如下所示:

// file: kernel/drivers/i2c/i2c-core.cint i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num){    unsigned long orig_jiffies;    int ret, try;    /* 判定硬件驱动是否支持I2C协议通信 */    if (adap->algo->master_xfer) {        .........        /* 由于adapter为具体的硬件,所以对其操作需要加锁处理,         * 保证每次只有一个线程在操作adapter,防止硬件使用竟态         */        if (in_atomic() || irqs_disabled()) {            ret = i2c_trylock_adapter(adap);            if (!ret)                return -EAGAIN;        } else {            i2c_lock_adapter(adap);        }        /* 调用总线驱动 adap->alog->master_xfer 完成真正的i2c数据通信 */        orig_jiffies = jiffies;        for (ret = 0, try = 0; try <= adap->retries; try++) {            ret = adap->algo->master_xfer(adap, msgs, num);            .........        }        i2c_unlock_adapter(adap);        return ret;    } else {        dev_dbg(&adap->dev, "I2C level transfers not supported\n");        return -EOPNOTSUPP;    }}s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,           char read_write, u8 command, int protocol,           union i2c_smbus_data *data){    unsigned long orig_jiffies;    int try;    s32 res;    flags &= I2C_M_TEN | I2C_CLIENT_PEC;    /* 判断硬件是否支持Smbus协议 */    if (adapter->algo->smbus_xfer) {        i2c_lock_adapter(adapter);        /* Retry automatically on arbitration loss */        orig_jiffies = jiffies;        for (res = 0, try = 0; try <= adapter->retries; try++) {            /* 调用总线驱动 adap->alog->smbus_xfer 完成真正的i2c数据通信 */            res = adapter->algo->smbus_xfer(adapter, addr, flags,                            read_write, command, protocol, data);            .........        }        i2c_unlock_adapter(adapter);    } else         /* 调用软件模拟Smbus协议 */        res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,                          command, protocol, data);    return res;}static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,                   unsigned short flags,                   char read_write, u8 command, int size,                   union i2c_smbus_data *data){    /* So we need to generate a series of msgs. In the case of writing, we      need to use only one message; when reading, we need two. We initialize      most things with sane defaults, to keep the code below somewhat      simpler. */    unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];    unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];    int num = read_write == I2C_SMBUS_READ ? 2 : 1;    struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0, 100000, 0, 0 },                              { addr, flags | I2C_M_RD, 0, msgbuf1, 100000, 0, 0 }                            };    int i;    u8 partial_pec = 0;    int status;    /* Smbus协议发送数据 */    msgbuf0[0] = command;    switch (size) {    case I2C_SMBUS_QUICK:        .........    case I2C_SMBUS_BYTE:        .........    case I2C_SMBUS_BYTE_DATA:        .........    case I2C_SMBUS_WORD_DATA:        .........    case I2C_SMBUS_PROC_CALL:        .........    case I2C_SMBUS_BLOCK_DATA:        .........    case I2C_SMBUS_BLOCK_PROC_CALL:        .........    case I2C_SMBUS_I2C_BLOCK_DATA:        .........    default:        .........    }    .........    /* 调用普通I2C通信接口来模拟Smbus协议 */    status = i2c_transfer(adapter, msg, num);    if (status < 0)        return status;    .........    /* Smbus协议读取数据 */    if (read_write == I2C_SMBUS_READ)        switch (size) {        case I2C_SMBUS_BYTE:            .........        case I2C_SMBUS_BYTE_DATA:            .........        case I2C_SMBUS_WORD_DATA:        case I2C_SMBUS_PROC_CALL:            .........        case I2C_SMBUS_I2C_BLOCK_DATA:            .........        case I2C_SMBUS_BLOCK_DATA:        case I2C_SMBUS_BLOCK_PROC_CALL:            .........        }    return 0;}

通过上面对i2c_transfer和i2c_smbus_xfer接口的分析,我们就可以知道I2C总线驱动在数据通信上总共有两个接口algo->master_xfer和algo->smbus_xfer,对于RK3026硬件平台而言,由于不支持硬件Smbus协议,因此RK30 I2C总线驱动只需要完善algo->master_xfer接口即可。最后其实设备驱动在进行数据通信之前,一般会在设备驱动程序的probe函数中调用i2c_check_functionality接口来查询总线驱动是否支持i2c-core所封装的这两套通信方式,i2c_check_functionality其实也是通过调用algo->functionality函数完成的,其相关代码如下所示:

/* Return 1 if adapter supports everything we need, 0 if not. */static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func){return (func & i2c_get_functionality(adap)) == func;}/* Return the functionality mask */static inline u32 i2c_get_functionality(struct i2c_adapter *adap){return adap->algo->functionality(adap);}

到目前为止,我们对I2C核心的相关接口已经分析完毕了。总结3.1内容我们可以知道,I2C设备client是怎么和I2C总线驱动adapter关联起来的;总结3.2内容我们可以知道,I2C驱动driver是怎么和I2C设备client关联起来的。总结3.3我们可以知道,I2C驱动driver是怎样和I2C总线驱动adapter进行交互的。通过总结上面三点,我们就基本理清楚Linux I2C子系统的工作流程。相信现在大家对本篇开始部分的I2C工作流程图会有更加深刻的了。



四、驱动编写

总结3.1~3.3的知识可知,对于I2C总线驱动需要完善或调用i2c-core提供的接口主要由三个:i2c_register_adapter总线注册接口、adapter->algo->master_sfer数据通信接口,adapter->algo->functionality功能查询接口。下面我们就开始简单实现Linux I2C总线的驱动程序。RK3026平台拥有多个I2C模块,因此我们采用PLATFORM形式来完成I2C总线驱动与CPU I2C模块的匹配。首先给出基本的驱动框架:

// file: kernel/drivers/i2c/busses/i2c-test.c#include <linux/fs.h>#include <linux/err.h>#include <linux/init.h>#include <linux/time.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/uaccess.h>#include <linux/io.h>#include <linux/i2c.h>#include <linux/clk.h>#include <linux/slab.h>#include <linux/mutex.h>#include <linux/cpufreq.h>#include <linux/wakelock.h>#include <linux/interrupt.h>#include <linux/miscdevice.h>#include <linux/platform_device.h>#include <mach/board.h>#include <mach/iomux.h>#include <mach/gpio.h>#include <asm/irq.h>/* platform_driver module */static int rk30_i2c_adapter_probe(struct platform_device * pdev) {return 0;}static int rk30_i2c_adapter_remove(struct platform_device * pdev) {return 0;}static struct platform_driver i2c_adapter_driver = {.probe= rk30_i2c_adapter_probe,.remove = rk30_i2c_adapter_remove,.driver = {.owner = THIS_MODULE,.name  = "rk30-i2c",},};static int __init i2c_adapter_init(void) {return platform_driver_register(&i2c_adapter_driver);} static void __exit i2c_adapter_exit(void) {platform_driver_unregister(&i2c_adapter_driver);}subsys_initcall(i2c_adapter_init);module_exit(i2c_adapter_exit);
上面的驱动框架相信大家已经非常熟悉了,所以具体的代码这里就不再做解释,如果有需要可以回顾下上篇文章《Linux基本设备驱动阐述》。下面我们开始probe函数的编写,通过上面对Linux I2C子系统的分析,我们可以知道总线驱动必须有的一个动作就是向i2c-core注册总线驱动i2c_register_adapter。因此probe就至少包含两个动作:adapter成员的初始化,adapter成员的注册。adapter成员的初始化主要是对:adapter->owner、总线驱动的名称adapter->name、数据通信方法adapter->algo、总线号adapter->nr等。其中非常重要的一个成员初始化就是adapter-algo,因为其代表着总线驱动核心-数据通信方法。adapter的注册接口采用i2c_add_numbered_adapter,此接口适用于静态注册总线驱动所用,即总线驱动的总线号是由我们静态分配的(这样做主要是便于I2C总线号就能与CPU I2Cx进行对应)。因此对于adapter数据的操作,代码如下所示:

// file: kernel/drivers/i2c/busses/i2c-test.c/**************   data struct defien *************/struct rk30_i2c_platformdata {    int sda_pin;    int scl_pin;    int bus_num;};struct rk30_i2c {    spinlock_t lock;    void __iomem * reg_base_addr;    struct i2c_adapter adapter;    struct rk30_i2c_platformdata * pdata;};static int rk30_i2c_xfer(struct i2c_adapter * adapter,             struct i2c_msg * msgs, int num) {    return 0;    }static u32 rk30_i2c_func(struct    i2c_adapter * adapter) {    /* 支持基本I2C通信、支持模拟Smbus通信 */    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL \        | I2C_FUNC_PROTOCOL_MANGLING;}static const struct i2c_algorithm rk30_i2c_algorithm = {    .master_xfer   = rk30_i2c_xfer,    .functionality = rk30_i2c_func,};static int rk30_i2c_adapter_probe(struct platform_device * pdev) {    .........    /* init i2c_adapter */    pi2c->adapter.owner = THIS_MODULE;    pi2c->adapter.retries = 5;    pi2c->adapter.timeout = msecs_to_jiffies(100);    pi2c->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;    strlcpy(pi2c->adapter.name, "rk30_i2c", sizeof(pi2c->adapter.name));    pi2c->adapter.algo_data = (void *)pi2c;    pi2c->adapter.nr = pi2c->pdata->bus_num;    pi2c->adapter.algo = &rk30_i2c_algorithm;    ret = i2c_add_numbered_adapter(&pi2c->adapter);    if(ret < 0) {        printk("err[%s]: add adapter failed.\n", __func__);        ret = -EINVAL;        goto ADD_ADAPTER_ERR;    }    .........}

rk30_i2c_func接口代表著总线驱动所能支持的协议类型,I2C_FUNC_I2C代表著基本的I2C数据通信支持,I2C_FUNC_SMBUS_EMUL代表着模拟Smbus协议支持,I2C_FUNC_PROTOCOL_MANGLING代表着msgs中不发送start信号。由于本人能力有限,目前只能读懂和调试现有的数据通信驱动,还不能很好的独立完成数据通信的编写,因此本文对于rk30_i2c_xfer接口不给予详细的编写(大致流程也是通过自旋锁、I2C中断和工作队列三者配合来完成)。adapter成员的初始化以及相关注册都已经完善了,剩下的部分则主要是具体硬件的初始化了:管教初始化、时钟初始化、中断初始化。因此probe接口的内容如下所示:

/**************   data struct defien *************/struct rk30_i2c_platformdata {int sda_pin;int scl_pin;int bus_num;};struct rk30_i2c {spinlock_t lock;void __iomem * reg_base_addr;struct i2c_adapter adapter;struct rk30_i2c_platformdata * pdata;};/**************  rk30 i2c hardware init ***************/static int rk30_i2c_io_init(struct rk30_i2c * pi2c) {// platform_get_resource_byname();// request_mem_region();// ioremap();return 0;}static int rk30_i2c_clk_init(struct rk30_i2c * pi2c) {return 0;}static int rk30_i2c_irq_init(struct rk30_i2c * pi2c) {// free_irq();return 0;}static int rk30_i2c_io_deinit(struct rk30_i2c * pi2c) {// iounmap();return 0;}static int rk30_i2c_clk_deinit(struct rk30_i2c * pi2c) {return 0;}static int rk30_i2c_irq_deinit(struct rk30_i2c * pi2c) {// platform_get_irq();// request_irq();return 0;}static void rk30_i2c_enable_clk(struct rk30_i2c * pi2c, int bus_num) {return;}static void rk30_i2c_disable_clk(struct rk30_i2c * pi2c, int bus_num) {return;}static void rk30_i2c_set_clk(struct rk30_i2c * pi2c, int bus_num, unsigned long scl_rate) {return;}static void rk30_i2c_do_xfer(struct rk30_i2c * pi2c, struct i2c_msg * msgs, int num) {return;}/*********************************************************/static int rk30_i2c_xfer(struct i2c_adapter * adapter, struct i2c_msg * msgs, int num) {unsigned long scl_rate = 0;struct rk30_i2c * pi2c = (struct rk30_i2c *)adapter->algo_data;rk30_i2c_enable_clk(pi2c, num);rk30_i2c_set_clk(pi2c, num, scl_rate);rk30_i2c_do_xfer(pi2c, msgs, num);rk30_i2c_disable_clk(pi2c, num);return 0;}static u32 rk30_i2c_func(structi2c_adapter * adapter) {return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL \| I2C_FUNC_PROTOCOL_MANGLING;}static const struct i2c_algorithm rk30_i2c_algorithm = {.master_xfer   = rk30_i2c_xfer,.functionality = rk30_i2c_func,};static int rk30_i2c_adapter_probe(struct platform_device * pdev) {int ret = 0;struct rk30_i2c * pi2c = NULL;if(pdev->dev.platform_data == NULL) {printk("err[%s]: unknow platform_data.\n", __func__);ret = -EINVAL;return ret;}pi2c = (struct rk30_i2c *)kmalloc(sizeof(struct rk30_i2c), GFP_KERNEL);if(pi2c == NULL) {printk("err[%s]: memory alloc failed.\n", __func__);ret = -ENOMEM;goto NO_MEM_ERR;}pi2c->pdata = (struct rk30_i2c_platformdata *)pdev->dev.platform_data;spin_lock_init(&pi2c->lock);/* init i2c_adapter */pi2c->adapter.owner = THIS_MODULE;pi2c->adapter.retries = 5;pi2c->adapter.timeout = msecs_to_jiffies(100);pi2c->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;strlcpy(pi2c->adapter.name, "rk30_i2c", sizeof(pi2c->adapter.name));pi2c->adapter.algo_data = (void *)pi2c;pi2c->adapter.nr = pi2c->pdata->bus_num;pi2c->adapter.algo = &rk30_i2c_algorithm;ret = i2c_add_numbered_adapter(&pi2c->adapter);if(ret < 0) {printk("err[%s]: add adapter failed.\n", __func__);ret = -EINVAL;goto ADD_ADAPTER_ERR;}/* init hw i2c */ret = rk30_i2c_io_init(pi2c);if(ret < 0) {printk("err[%s]: rk30 i2c io init failed.\n", __func__);ret = -EINVAL;goto IO_INIT_ERR;}ret = rk30_i2c_clk_init(pi2c);if(ret < 0) {printk("err[%s]: rk30 i2c clk init failed.\n", __func__);goto CLK_INIT_ERR;}ret = rk30_i2c_irq_init(pi2c);if(ret < 0) {printk("err[%s]: rk30 i2c irq init failed.\n", __func__);goto IRQ_INIT_ERR;}/* storage pi2c to pdev */platform_set_drvdata(pdev, pi2c);return 0;IRQ_INIT_ERR:rk30_i2c_clk_deinit(pi2c);CLK_INIT_ERR:rk30_i2c_io_deinit(pi2c);IO_INIT_ERR:i2c_del_adapter(&pi2c->adapter);ADD_ADAPTER_ERR:kfree(pi2c);NO_MEM_ERR:return ret;}

remove接口与probe接口恰好相反,因此remove接口也给出如下所示:

static int rk30_i2c_adapter_remove(struct platform_device * pdev) {struct rk30_i2c * pi2c = (struct rk30_i2c *)platform_get_drvdata(pdev);rk30_i2c_irq_deinit(pi2c);rk30_i2c_io_deinit(pi2c);rk30_i2c_clk_deinit(pi2c);kfree(pi2c);return 0;}
到此为止I2C总线驱动的框架结构就已经完成了,I2C总线驱动的编写主要还是如对I2C子系统分析所说,主要是完善algo->master_xfer、algo->functionality两个接口的编写。而其中最为核心重要的就是rk30_i2c_xfer接口完成数据通信,对于此接口本篇却没有具体的实现,希望读者自己参阅相关代码进行进一步的学习。


五、结尾

    本篇文章对Linux I2C子系统分析和Linux I2C总线驱动编写的分析就已经结束了,不知道读者有没有收获到些许东西,对实际驱动编写调试能不能起到帮助。常用的外设总线除了I2C外,还有SPI、SDIO、USB总线。Linux SPI子系统和Linux I2C子系统两者比较相似,有兴趣的读者可以去分析学习下。至于SDIO、USB总线比较复杂,本人现在还没有接触过,希望在以后工作锻炼中能够熟悉这两大总线,然后再和大家进行交流学习。

    谢谢!

0 0