linux的I2C驱动——ID匹配

以下基于3.0内核版本的源码进行讲解,驱动代码路径为drivers/misc/eeprom/at24.c。I2C核心代码路径为drivers/I2C

模块入口

module_init(at24_init);

module_init()是一个宏定义,位于include/linux/init.h。
如果将驱动编译入内核,定义如下:

#define device_initcall(fn)     __define_initcall("6",fn,6)
#define __initcall(fn) device_initcall(fn)
#define module_init(x)  __initcall(x);

这样在内核初始化的时候就会直接对该驱动进行初始化了。关于内核初始化部分,后续再讲述。
如果以模块方式编译,定义如下:

#define module_init(initfn)                 \
    static inline initcall_t __inittest(void)       \
    { return initfn; }                  \
    int init_module(void) __attribute__((alias(#initfn)));

__inittest仅仅是为了检测定义的函数是否符合initcall_t类型,如果不是__inittest类型在编译时将会报错

模块初始化

/*drivers/misc/eeprom/at24.c*/
static int __init at24_init(void)
{
    if (!io_limit) {
        pr_err("at24: io_limit must not be 0!\n");
        return -EINVAL;
    }

    io_limit = rounddown_pow_of_two(io_limit);
    return i2c_add_driver(&at24_driver);
}
/*include/linux/i2c.h*/
static inline int i2c_add_driver(struct i2c_driver *driver)
{
    return i2c_register_driver(THIS_MODULE, driver);
}

注册函数

/*drivers/i2c/i2c-core.c*/
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    int res;
    /* 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);

    ……

    INIT_LIST_HEAD(&driver->clients);
    /* Walk the adapters that are already present */
    i2c_for_each_dev(driver, __process_new_driver);

    return 0;
}
EXPORT_SYMBOL(i2c_register_driver);

调用driver_register函数在总线上注册驱动

/*drivers/base/driver.c*/
int driver_register(struct device_driver *drv)
{
    int ret;
    struct device_driver *other;

    ……
    ret = bus_add_driver(drv);
    if (ret)
        return ret;
    ret = driver_add_groups(drv, drv->groups);
    if (ret)
        bus_remove_driver(drv);
    return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
/*drivers/base/bus.c*/
int bus_add_driver(struct device_driver *drv)
{
    struct bus_type *bus;
    struct driver_private *priv;
    int error = 0;

    bus = bus_get(drv->bus);
    if (!bus)
        return -EINVAL;

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

    priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    if (!priv) {
        error = -ENOMEM;
        goto out_put_bus;
    }
    klist_init(&priv->klist_devices, NULL, NULL);
    priv->driver = drv;
    drv->p = priv;
    priv->kobj.kset = bus->p->drivers_kset;
    error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
                     "%s", drv->name);//添加到总线链表
    if (error)
        goto out_unregister;

    if (drv->bus->p->drivers_autoprobe) {
        error = driver_attach(drv);
        if (error)
            goto out_unregister;
    }
    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
    module_add_driver(drv->owner, drv);
}

设备和驱动开始匹配

/*drivers/base/dd.c*/
int driver_attach(struct device_driver *drv)
{
    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);

通过next_device函数进行设备搜索,然后再通过__driver_attach函数进行匹配。直到next_device函数搜索到匹配设备,或者到链表尾端。

/*drivers/base/bus.c*/
int bus_for_each_dev(struct bus_type *bus, struct device *start,
             void *data, int (*fn)(struct device *, void *))
{
    struct klist_iter i;
    struct device *dev;
    int error = 0;

    if (!bus)
        return -EINVAL;

    klist_iter_init_node(&bus->p->klist_devices, &i,
                 (start ? &start->p->knode_bus : NULL));
    while ((dev = next_device(&i)) && !error)
        error = fn(dev, data);
    klist_iter_exit(&i);
    return error;
}

next_device通过链表查询设备

/*drivers/base/bus.c*/
static struct device *next_device(struct klist_iter *i)
{
    struct klist_node *n = klist_next(i);
    struct device *dev = NULL;
    struct device_private *dev_prv;

    if (n) {
        dev_prv = to_device_private_bus(n);
        dev = dev_prv->device;
    }
    return dev;
}

匹配函数,通过driver_match_device函数去匹配。匹配完会通过driver_probe_device函数去调用probe函数。

/*drivers/base/dd.c*/
static int __driver_attach(struct device *dev, void *data)
{
    struct device_driver *drv = data;

    if (!driver_match_device(drv, dev))
        return 0;

    if (dev->parent)    /* Needed for USB */
        device_lock(dev->parent);
    device_lock(dev);
    if (!dev->driver)
        driver_probe_device(drv, dev);
    device_unlock(dev);
    if (dev->parent)
        device_unlock(dev->parent);

    return 0;
}

这里来讲解一下,匹配对象。
主要涉及到以下几个参数:

/*drivers/misc/eeprom/at24.c*/
static const struct i2c_device_id at24_ids[] = {
    { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
    { "at24", 0 },
    { /* END OF LIST */ }
};
MODULE_DEVICE_TABLE(i2c, at24_ids);
static struct i2c_driver at24_driver = {
    .driver = {
        .name = "at24",
        .owner = THIS_MODULE,
    },
    .probe = at24_probe,
    .remove = __devexit_p(at24_remove),
    .id_table = at24_ids,
};
/*arch/arm/mach-xxx/mach_xxx.c*/
static struct i2c_board_info at24xx[]={
    {
        I2C_BOARD_INFO("at24c02",0x50);
    },
};
i2c_register_board_info(0,at24xx,ARRAY_SIZE(at24xx));

所谓的匹配是将i2c_driver和i2c_client进行匹配,主要是将at24_ids与at24xx进行匹配。接着回到上面的匹配函数。

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

在这里调用了总线的match函数。在i2c_register_driver函数中,已经定义了总线参数 driver->driver.bus = &i2c_bus_type
我们来看一下这个结构体,对应的函数。

/*drivers/I2C/i2c_core.c*/
struct 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,
};

具体代码如下

/*drivers/I2C/i2c-core.c*/
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 (!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)
        return i2c_match_id(driver->id_table, client) != NULL;

    return 0;
}

通过i2c_match_id函数进行最后的匹配

/*drivers/I2C/i2c-core.c*/
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;
}

主要匹配设备的名字。到此就设备和驱动就匹配上了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值