linux i2c—i2c设备驱动匹配过程分析

i2c设备与驱动的匹配,既可以在i2c设备注册时匹配相应驱动,也可以在i2c驱动注册时匹配相应设备,本文将从i2c设备注册匹配分析、i2c驱动注册匹配分析两方面着手。

1.i2c设备注册匹配分析

前文提到,i2c适配器在注册时会扫描设备树上的i2c设备,也在扫描静态(代码中)添加的i2c设备,最后都是通过i2c_new_device函数将i2c设备注册。

我们即从i2c_new_device函数(driver/i2c/i2c-core-base.c)开始分析:

struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

{

struct i2c_client  *client;

      client->adapter = adap;

      client->dev.platform_data = info->platform_data;

      client->flags = info->flags;

      client->addr = info->addr;

      client->init_irq = info->irq;

      client->irq = client->init_irq;

      client->dev.parent = &client->adapter->dev;

      client->dev.bus = &i2c_bus_type;

      client->dev.type = &i2c_client_type;

      client->dev.of_node = of_node_get(info->of_node);

      client->dev.fwnode = info->fwnode;

      i2c_dev_set_name(adap, client, info);



      status = device_register(&client->dev);

}

这个函数将info结构体中的信息填充到  i2c_client  结构体,并且设置了 device 成员内部成员属性。值得注意的是,它设置了 divece.type i2c_client_type,表明这个设备是iic设备,在前面提到iic设备和iic适配器都会通过device挂载到iic总线,它们之间区分便通过这个属性。

函数最后调用了 device_register 将设备注册到系统,注意看,这里只传入了 i2c_client 结构体中的 device 成员,device_register 直接调用了device_add,前文分析i2c适配器的时将要分析过此函数,现在在详细分析下:

int device_add(struct device *dev)

{

    device_add_class_symlinks(dev);//创建类符号链接,相互创建dev和class之间的连接文件

    device_add_attrs(dev);//创建sys目录下设备其他属性文件  ,添加设备属性文件

    bus_add_device(dev);//将设备添加到对应总线

    dpm_sysfs_add(dev);//电源管理相关

    device_pm_add(dev);//添加设备到激活设备列表中,用于电源管理



    if (dev->bus)//呼唤通知链表,通知注册监听该总线的设备,有新设备加入

        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);



//产生一个内核uevent事件,该事件可以被内核以及应用层捕获,属于linux设备模型中热插拔机制

    kobject_uevent(&dev->kobj, KOBJ_ADD);



    bus_probe_device(dev);//在总线上寻找对应的driver

}

其中bus_add_device函数将设备添加到总线设备链表中

int bus_add_device(struct device *dev)

{

      struct bus_type *bus = bus_get(dev->bus);

      if (bus) {

             error = sysfs_create_link(&bus->p->devices_kset->kobj,

                                         &dev->kobj, dev_name(dev));

             error = sysfs_create_link(&dev->kobj,

                           &dev->bus->p->subsys.kobj, "subsystem");

             klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);

      }

}

bus_probe_device函数:

void bus_probe_device(struct device *dev)

{

   if (bus->p->drivers_autoprobe)// drivers_autoprobe为1,在bus_register函数赋值

       device_initial_probe(dev);

}

  继续分析device_initial_probe –>__device_attach:

static int __device_attach(struct device *dev, bool allow_async)

{

     ret = bus_for_each_drv(dev->bus, NULL, &data,

                __device_attach_driver);

}

设备与总线上所有driver进行匹配,匹配函数是 __device_attach_driver ,其代码简化如下:

static int __device_attach_driver(struct device_driver *drv, void *_data)

{

    if (dev->driver)//如果设备已经匹配,则返回

    return -EBUSY;



    ret = driver_match_device(drv, dev);//检测设备与驱动是否匹配,返回大于0表示匹配,其它则进行下一个driver匹配!



    if (ret == 0) {

    /* no match */

        return 0;

    } else if (ret == -EPROBE_DEFER) {

        dev_dbg(dev, "Device match requests probe deferral\n");

        driver_deferred_probe_add(dev);

    } else if (ret < 0) {

        dev_dbg(dev, "Bus failed to match device: %d", ret);

        return ret;

} /* ret > 0 means positive match */



    return driver_probe_device(drv, dev);//匹配成功后,执行总线的probe函数,然后再调用i2c驱动的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;

}

判断总线的match指针是否为空,不为空则执行match指针指向的函数。I2c总线结构体在内核启动时候已经注册,具体结构如下:

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_device_match 函数,代码如下:

static int i2c_device_match(struct device *dev, struct device_driver *drv)

{

    struct i2c_client        *client = i2c_verify_client(dev);//判断当前device是不是iic设备,是的话返回i2c_client结构体

    struct i2c_driver        *driver;



    /* Attempt an OF style match */     //使用drv内部的of_match_table列表来匹配,一般使用设备树注册设备时才会使用这种匹配

    if (i2c_of_match_device(drv->of_match_table, client))用设备树中的 compatible 属性和i2c驱动driver成员中的 of_match_table 来匹配

        return 1;



    /* Then ACPI style match */    //ACPI专用匹配,较少用不关心

    if (acpi_driver_match_device(dev, drv))

        return 1;



    driver = to_i2c_driver(drv);



    /* Finally an I2C match */

    if (i2c_match_id(driver->id_table, client))    // match_id匹配方式

        return 1;



return 0;

}

struct i2c_client *i2c_verify_client(struct device *dev)

{

    return (dev->type == &i2c_client_type)? to_i2c_client(dev): NULL;

}//这个函数很有意思,它会先判断这个device是不是iic设备,对应之前提到的iic适配器(iic控制器)也会作为设备挂在总线上!所以当iic适配器注册到总线时候,虽然也会调用总线匹配函数,但直接就结束了!

可以看到i2c_device_match给出了3中设备与驱动的匹配方式,相比于平台总线匹配,少了一种通过设备名字与驱动名字匹配的方式,所以iic驱动中的名字不重要!

  1. 第一种是设备树类型的匹配方式,device_driver 结构体(表示设备驱动)中有个名为of_match_table的成员变量,此成员变量保存着驱动的compatible匹配表,设备树中的每个设备节点的 compatible 属性会和 of_match_table 表中的所有成员比较,查看是否有相同的条目,如果有的话就表示设备和此驱动匹配,设备和驱动匹配成功以后 probe 函数就会执行。
  2. 第二种不关注
  3. 第三种是id的匹配方式,每个i2c_driver结构体有一个 id_table成员变量,顾名思义,保存了很多 id 信息,用于匹配。

如果匹配成功,那么回到之前的__device_attach_driver函数中,会继续执行driver_probe_device函数:

int driver_probe_device(struct device_driver *drv, struct device *dev)

{

      int ret = 0;



      if (!device_is_registered(dev))

             return -ENODEV;

      if (initcall_debug)

             ret = really_probe_debug(dev, drv);

      else

             ret = really_probe(dev, drv);

}

主要看下really_probe函数,简化如下:

static int really_probe(struct device *dev, struct device_driver *drv)

{

    dev->driver = drv;//将设备中驱动指针指向匹配到的驱动!



    if (dev->bus->probe) {//执行总线的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_bound(dev);//驱动绑定设备

}

可以看到这里执行了i2c_bus_type中的.probe函数,对应的是i2c_device_probe函数,简化后如下:

static int i2c_device_probe(struct device *dev)

{

    if (driver->probe_new)

        status = driver->probe_new(client);

    else if (driver->probe)

        status = driver->probe(client, i2c_match_id(driver->id_table, client));

}

先判断iic驱动中是否使用 probe_new ,如果没有则执行当前的 probe 函数,也就是iic驱动中自己写的probe函数

2.i2c驱动匹配分析

驱动注册使用的是 i2c_add_driver 宏,它的定义是:

#define i2c_add_driver(driver)  i2c_register_driver(THIS_MODULE, driver)

接着看i2c_register_driver

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

{

    driver->driver.bus = &i2c_bus_type;//添加总线



    res = driver_register(&driver->driver);//驱动注册核心函数,注意只传入了driver成员



    /* 遍历所有挂在总线上的iic适配器,用它们去探测driver中指定的iic设备地址列表 */

    i2c_for_each_dev(driver, __process_new_driver);

}

i2c_for_each_dev 函数是用于探测未注册的设备,并将其注册后匹配,我们一般很少用到它这里不展开讲解。我们一般使用的是 driver_register 函数,其代码简化后如下:

int driver_register(struct device_driver *drv)

{

    other = driver_find(drv->name, drv->bus);//判断驱动是否已经注册过,如果注册过则不再继续

    if (other) {

        printk(KERN_ERR "Error: Driver '%s' is already registered, aborting...\n", drv->name);

        return -EBUSY;

}



    ret = bus_add_driver(drv);

}

将驱动添加至总线使用 bus_add_driver 函数,其代码简化后如下

int bus_add_driver(struct device_driver *drv)

{

    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);//将驱动添加到总线

    if (drv->bus->p->drivers_autoprobe) {

        if (driver_allows_async_probing(drv)) {

            pr_debug("bus: '%s': probing driver %s asynchronously\n",drv->bus->name, drv->name);

            async_schedule(driver_attach_async, drv);

        } else {

            error = driver_attach(drv);

            if (error)

                goto out_unregister;

        }

    }

}

将驱动添加到总线后执行 driver_attach 函数,其代码(简化)如下:

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 *))

{

    klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->p->knode_bus : NULL));



    while (!error && (dev = next_device(&i)))    //遍历总线上所有设备,分别执行__driver_attach函数

        error = fn(dev, data);

}

从上面可以看出,驱动加载后会去遍历总线上所有设备,分别执行 __driver_attach 函数,其代码简化后如下:

static int __driver_attach(struct device *dev, void *data)

{

    ret = driver_match_device(drv, dev);//调用总线的match函数,检测设备与驱动是否匹配,返回大于0表示匹配,其它则进行下一个device匹配!

    if (ret == 0) {

        /* no match */

        return 0;

    } else if (ret == -EPROBE_DEFER) {

        dev_dbg(dev, "Device match requests probe deferral\n");

    driver_deferred_probe_add(dev);

    } else if (ret < 0) {

        dev_dbg(dev, "Bus failed to match device: %d", ret);

        return ret;

    } /* ret > 0 means positive match */



    if (!dev->driver)//如果匹配成功,则要判断这个设备是否已经绑定了驱动,举个例子,之前有个设备已经和别的驱动绑定了,但同时也支持这个驱动!

    driver_probe_device(drv, dev);//匹配成功后,执行总线的probe函数,然后再调用i2c驱动的probe函数(自己写的probe)

}

__driver_attach的代码看起来是不是很熟悉?没错,和__device_attach_driver函数基本一致。都是先去调用i2c总线的match函数看看设备与驱动是否匹配,如果匹配就调用i2c总线的probe函数,然后在总线的probe函数中调用i2c驱动的probe函数,在我们自己写的probe函数中注册字符设备,创建设备节点,实现fops集等等,这里就不再多讲解了。.

参考文章:

【linux iic子系统】i2c设备与驱动匹配过程(三)_i2c_add_driver 如何和clent匹配-CSDN博客

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值