linux的I2C驱动——ID匹配

xiaoxiao2021-02-28  79

以下基于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; }

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

转载请注明原文地址: https://www.6miu.com/read-51157.html

最新回复(0)