前言:
记得以前曾研究过Linux内核下i2c子系统,了解了i2c总线上,适配器、设备驱动的注册过程与使用方法,详细请查看:
1、i2c总线上,适配器、设备驱动注册:http://www.cnitblog.com/luofuchong/archive/2008/10/05/49881.html
2、i2c总线使用:http://www.cnitblog.com/luofuchong/archive/2008/10/05/49882.html
最近因为项目的需要,又重新看了一遍linux下的i2c子系统,有了新的体会。俗话说,好记性不如烂笔头,还是记下来比较好,一来当作总结,二来希望起到启发后人的效果^_^
正文:
Linux的世界充斥着各种各样的想法,i2c子系统同样,新、旧架构并存。至于旧的架构,网上有非常详细的介绍,本人不打算再去重复,咱们直奔新的架构去。
Linux下的设备驱动,顾名思义,就是由设备和驱动来组成的,i2c总线上的设备驱动也不例外。
一、i2c设备的注册
第一步:
记得以前的i2c设备驱动,设备部分喜欢驱动运行的时候动态创建,新式的驱动倾向于向传统的linux下设备驱动看齐,采用静态定义的方式来注册设备,使用接口为:
int __init
i2c_register_board_info(int busnum,
struct i2c_board_info const *info, unsigned len)
{
int status;
mutex_lock(&__i2c_board_lock);
/* dynamic bus numbers will be assigned after the last static one */
if (busnum >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = busnum + 1;//????
for (status = 0; len; len--, info++) {
struct i2c_devinfo *devinfo;
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);//申请表示i2c设备的结构体空间
if (!devinfo) {
pr_debug("i2c-core: can't register boardinfo!/n");
status = -ENOMEM;
break;
}
/* 填写i2c设备描述结构 */
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list);//添加到全局链表__i2c_board_list中
}
mutex_unlock(&__i2c_board_lock);
return status;
}
第二步:
系统初始化的时候,会根据板级i2c设备配置信息,创建i2c客户端设备(i2c_client),添加到i2c子系统中:
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
mutex_lock(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) { //遍历全局链表__i2c_board_list
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
printk(KERN_ERR "i2c-core: can't create i2c%d-%04x/n",
i2c_adapter_id(adapter),
devinfo->board_info.addr);
}
mutex_unlock(&__i2c_board_lock);
}
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);//创建i2c_client设备
if (!client)
return NULL;
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;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
/* a new style driver may be bound to this device when we
* return from this function, or any later moment (e.g. maybe
* hotplugging will load the driver module). and the device
* refcount model is the standard driver model one.
*/
status = i2c_attach_client(client);//接入设备到i2c总线上
if (status < 0) {
kfree(client);
client = NULL;
}
return client;
}
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int res;
/* Check for address business */
res = i2c_check_addr(adapter, client->addr);//地址检测
if (res)
return res;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
if (client->driver)
client->dev.driver = &client->driver->driver;
if (client->driver && !is_newstyle_driver(client->driver)) {
client->dev.release = i2c_client_release;
client->dev.uevent_suppress = 1;
} else
client->dev.release = i2c_client_dev_release;
snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
"%d-%04x", i2c_adapter_id(adapter), client->addr);
res = device_register(&client->dev); //注册设备
if (res)
goto out_err;
mutex_lock(&adapter->clist_lock);
list_add_tail(&client->list, &adapter->clients);
mutex_unlock(&adapter->clist_lock);
dev_dbg(&adapter->dev, "client [%s] registered with bus id %s/n",
client->name, client->dev.bus_id);
if (adapter->client_register) {
if (adapter->client_register(client)) {
dev_dbg(&adapter->dev, "client_register "
"failed for client [%s] at 0x%02x/n",
client->name, client->addr);
}
}
return 0;
out_err:
dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
"(%d)/n", client->name, client->addr, res);
return res;
}
注:
特别要提一下的是这个“i2c_check_addr”,引用<<i2c 源代码情景分析>>里的话:“i2c 设备的7 位地址是就当前i2c 总线而言的,是“相对地址”。不同的i2c 总线上的设备可以使用相同的7 位地址,但是它们所在的i2c 总线不同。所以在系统中一个i2c 设备的“绝对地址”由二元组(i2c 适配器的ID 和设备在该总线上的7 位地址)表示。”,所以这个函数的作用主要是排除同一i2c总线上出现多个地址相同的设备。
二、驱动的注册:
第一步:
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}
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;
/* new style driver methods can't mix with legacy ones */
if (is_newstyle_driver(driver)) { //呵呵,新旧架构的驱动可以从这里看出来
if (driver->attach_adapter || driver->detach_adapter
|| driver->detach_client) {
printk(KERN_WARNING
"i2c-core: driver [%s] is confused/n",
driver->driver.name);
return -EINVAL;
}
}
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
/* for new style drivers, 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;
mutex_lock(&core_lock);
pr_debug("i2c-core: driver [%s] registered/n", driver->driver.name);
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
class_for_each_device(&i2c_adapter_class, NULL, driver,
__attach_adapter);
mutex_unlock(&core_lock);
return 0;
}
第二步:
还记得在第一部分,我们注册了一i2c设备,其总线类型为i2c_bus_type,现在我们又在这总线上注册一驱动,那不得了,满足了设备+驱动的条件,i2c_bus_type总线上探测函数要被执行了。
struct bus_type i2c_bus_type = {
.name = "i2c",
.dev_attrs = i2c_dev_attrs,
.match = i2c_device_match,
.uevent = i2c_device_uevent,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
};
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(dev->driver);
int status;
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe/n");
status = driver->probe(client, i2c_match_id(driver->id_table, client));//调用具体i2c设备驱动的探测函数
if (status)
client->driver = NULL;
return status;
}
OK,剩下的就是具体i2c设备驱动的功夫了,在此不作深入研究。
I2C总线使用方法继续研究
最新推荐文章于 2022-03-27 18:32:54 发布