linux的I2C驱动——ID匹配
来源:互联网 发布:淘宝童装店铺排名 编辑:程序博客网 时间:2024/06/10 02:43
以下基于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;}
主要匹配设备的名字。到此就设备和驱动就匹配上了。
阅读全文
0 0
- linux的I2C驱动——ID匹配
- linux驱动的i2c驱动
- linux内核的I2C子系统详解3——i2c-core.c初步分析、I2C总线的匹配机制
- linux的I2C驱动——移植篇
- linux的I2C驱动——读写操作
- linux内核的I2C子系统详解1——I2C总线概览、驱动框架概览
- Linux驱动学习——I2C
- Linux设备驱动——I2C总线
- Linux的I2C驱动架构
- Linux的I2C驱动架构
- Linux的i2c驱动详解
- Linux的i2c驱动详解
- Linux的I2C驱动架构
- Linux的I2C驱动架构
- Linux的i2c驱动详解
- Linux的I2C驱动讲解
- Linux的i2c驱动详解
- Linux的i2c驱动详解
- cms垃圾回收
- 实现鼠标放到一个div上显示出另一个隐藏的div
- java基础总结 第二天
- sdio卡识别与sd的异同
- 网站SEO编辑如何正确更新网站内容?
- linux的I2C驱动——ID匹配
- Numpy中的pad函数
- 矩阵快速幂 洛谷P3390
- FTP
- JZOJ 3769. 【NOI2015模拟8.14】A+B
- 单例模式
- HDU 6046 hash(鸽巢原理+unordered_map)
- 你必须知道的23个最有用的Elasticseaerch检索技巧
- python中的常见问题(持续更新)