这几篇文章来分析下linux2.6下I2C驱动的写法,对个人也是一个学习的过程。
在linux系统下,i2c驱动架构分为三个部分:I2C核心、I2C总线驱动、I2C设备驱动,下面首先从设备驱动怎么写开始,主要是分析内核中的代码。本文中分析的内核代码是基于LINUX2.6.19版本。
首先看一个以前做过的I2C驱动。下面是一个设备驱动的模块加载和卸载函数。
static struct i2c_driver tw_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "TW Client",
},
.attach_adapter = tw_attach,
.detach_client = tw_detach,
};
static __init int tw_init(void)
{
u8 err;
err = i2c_add_driver(&tw_i2c_driver);
return err;
}
static void __exit tw_clean(void)
{
i2c_del_driver(&tw_i2c_driver);
}
module_init(tw_init);
module_exit(tw_clean);
从上面模块加载函数中可以看到,调用了I2C核心函数i2c_add_driver,我们看看这个函数做了些什么。
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}
i2c_register_driver的作用是向系统注册该驱动,下面我们看这个函数的具体实现。
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
struct list_head *item;
struct i2c_adapter *adapter;
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;
res = driver_register(&driver->driver);
if (res)
return res;
mutex_lock(&core_lists);
list_add_tail(&driver->list,&drivers);
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
//下面将要找到该设备所接的适配器,调用i2c_driver结构体的attach_adapter
//函数。
/* now look for instances of driver on our adapters */
if (driver->attach_adapter) {
//注意下面是一个循环体,系统总所有的适配器,都放在链表adapters里了,这个
//工作是在I2C总线驱动里做的。
list_for_each(item,&adapters) {
adapter = list_entry(item, struct i2c_adapter, list);
//对于每一个适配器都要调用attach_adapter函数,这个attach_adpter函数是用
//户编写的,需要要在这个函数调用i2c_probe函数来探测该设备是否接在该适配器上面。
driver->attach_adapter(adapter);
}
}
mutex_unlock(&core_lists);
return 0;
}
下面我们看看driver->attach_adapter函数的实现:
static unsigned short normal_i2c[] = { TW_I2C_ADDRESS, I2C_CLIENT_END };
static int tw_attach(struct i2c_adapter *adap)
{
//调用了i2c_probe函数,注意参数addr_data已经在i2c.h中给出了模板定义的,这里只需要给出addr_data.normal_i2c即可,就是给出需要探测的设备从地址。
err = i2c_probe(adap, &addr_data, &tw_detect_client);
clk_disable(clk);
clk_put(clk);
return err;
}
我们在来跟踪i2c_probe函数的实现:
int i2c_probe(struct i2c_adapter *adapter,
struct i2c_client_address_data *address_data,
int (*found_proc) (struct i2c_adapter *, int, int))
{
int i, err;
//获得当前适配器的ID
int adap_id = i2c_adapter_id(adapter);
/* Force entries are done first, and are not affected by ignore
entries */
//首先扫描forces中的地址,这个是不受ignore影响的
if (address_data->forces) {
unsigned short **forces = address_data->forces;
int kind;
for (kind = 0; forces[kind]; kind++) {
for (i = 0; forces[kind][i] != I2C_CLIENT_END;
i += 2) {
if (forces[kind][i] == adap_id
|| forces[kind][i] == ANY_I2C_BUS) {
dev_dbg(&adapter->dev, "found force "
"parameter for adapter %d, "
"addr 0x%02x, kind %d\n",
adap_id, forces[kind][i + 1],
kind);
err = i2c_probe_address(adapter,
forces[kind][i + 1],
kind, found_proc);
if (err)
return err;
}
}
}
}
//下面是有关适配器具体功能支持方面的问题,暂且不管他
/* Stop here if we can't use SMBUS_QUICK */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
if (address_data->probe[0] == I2C_CLIENT_END
&& address_data->normal_i2c[0] == I2C_CLIENT_END)
return 0;
dev_warn(&adapter->dev, "SMBus Quick command not supported, "
"can't probe for chips\n");
return -1;
}
/* Probe entries are done second, and are not affected by ignore
entries either */
//接着会扫描probe项
for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {
if (address_data->probe[i] == adap_id
|| address_data->probe[i] == ANY_I2C_BUS) {
dev_dbg(&adapter->dev, "found probe parameter for "
"adapter %d, addr 0x%02x\n", adap_id,
address_data->probe[i + 1]);
err = i2c_probe_address(adapter,
address_data->probe[i + 1],
-1, found_proc);
if (err)
return err;
}
}
//最为关键的地方,扫描normal项,一般我们设备的地址就是放在这里的。
/* Normal entries are done last, unless shadowed by an ignore entry */
for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
int j, ignore;
ignore = 0;
//下面的地方就是确认扫描的地址不在ignore里面
for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;
j += 2) {
if ((address_data->ignore[j] == adap_id ||
address_data->ignore[j] == ANY_I2C_BUS)
&& address_data->ignore[j + 1]
== address_data->normal_i2c[i]) {
dev_dbg(&adapter->dev, "found ignore "
"parameter for adapter %d, "
"addr 0x%02x\n", adap_id,
address_data->ignore[j + 1]);
ignore = 1;
break;
}
}
if (ignore)
continue;
//当该地址是有效地址后,就调用i2c_probe_address函数来判断该设备是否接在该适配器上面。
dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
"addr 0x%02x\n", adap_id,
address_data->normal_i2c[i]);
err = i2c_probe_address(adapter, address_data->normal_i2c[i],
-1, found_proc);
if (err)
return err;
}
return 0;
}
下面我们来追踪下i2c_probe_address函数的实现过程:
/* ----------------------------------------------------
* the i2c address scanning function
* Will not work for 10-bit addresses!
* ----------------------------------------------------
*/
static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,
int (*found_proc) (struct i2c_adapter *, int, int))
{
int err;
//确定该地址为有效地址
/* Make sure the address is valid */
if (addr < 0x03 || addr > 0x77) {
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
addr);
return -EINVAL;
}
//如果该地址已经被用,则跳过
/* Skip if already in use */
if (i2c_check_addr(adapter, addr))
return 0;
//判断该总线上面是否真的接有这个设备,对于i2c_smbus_xfer在讲总线驱动的时候再详细介绍。
/* Make sure there is something at this address, unless forced */
if (kind < 0) {
if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL) < 0)
return 0;
/* prevent 24RF08 corruption */
if ((addr & ~0x0f) == 0x50)
i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL);
}
//如果有该设备就调用回调函数
/* Finally call the custom detection function */
err = found_proc(adapter, addr, kind);
/* -ENODEV can be returned if there is a chip at the given address
but it isn't supported by this chip driver. We catch it here as
this isn't an error. */
if (err == -ENODEV)
err = 0;
if (err)
dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",
addr, err);
return err;
}
下面我们在看看回调函数做了什么:
/*!
* tw I2C detect_client function
*
* @param adapter struct i2c_adapter *
* @param address int
* @param kind int
*
* @return Error code indicating success or failure
*/
static int tw_detect_client(struct i2c_adapter *adapter, int address,
int kind)
{
//将i2c_client的adapter变量指向其所接的适配器
tw_i2c_client.adapter = adapter;
//调用i2c_attach_client函数将i2c_client依附到适配器,就是将该client接到其对应adapter的client链表上面。
if (i2c_attach_client(&tw_i2c_client)) {
tw_i2c_client.adapter = NULL;
printk(KERN_ERR
"tw_detect_client: i2c_attach_client failed\n");
return -1;
}
}
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int res = 0;
mutex_lock(&adapter->clist_lock);
if (__i2c_check_addr(client->adapter, client->addr)) {
res = -EBUSY;
goto out_unlock;
}
//最关键的地方:将该client插到适配器的clients链表中
list_add_tail(&client->list,&adapter->clients);
client->usage_count = 0;
//下面的都是设备管理模型实现,以后再详细讨论
client->dev.parent = &client->adapter->dev;
client->dev.driver = &client->driver->driver;
client->dev.bus = &i2c_bus_type;
client->dev.release = &i2c_client_release;
snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
"%d-%04x", i2c_adapter_id(adapter), client->addr);
dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
client->name, client->dev.bus_id);
res = device_register(&client->dev);
if (res)
goto out_list;
res = device_create_file(&client->dev, &dev_attr_client_name);
if (res)
goto out_unregister;
mutex_unlock(&adapter->clist_lock);
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_unregister:
init_completion(&client->released); /* Needed? */
device_unregister(&client->dev);
wait_for_completion(&client->released);
out_list:
list_del(&client->list);
dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, res);
out_unlock:
mutex_unlock(&adapter->clist_lock);
return res;
}
现在我们已经找到设备所接的适配器,并且将该设备依附到适配器链表中,接下来我们看看用户如何访问设备。看下面的函数:
/*
* Function definitions
*/
static int tw_i2c_client_xfer(unsigned int addr, u8 page, u8 reg,
char *buf, int num, int tran_flag)
{
struct i2c_msg msg[3];
int ret;
//构造了3条I2C消息,注意这里构造多少条I2C消息,是有芯片决定的。
msg[0].addr = addr;
msg[0].len = 1;
msg[0].buf = &page;
msg[0].flags = tran_flag;
msg[0].flags &= ~I2C_M_RD;
msg[1].addr = addr;
msg[1].len = 1;
msg[1].buf = ®
msg[1].flags = tran_flag;
msg[1].flags &= ~I2C_M_RD;
msg[2].addr = addr;
msg[2].len = num;
msg[2].buf = buf;
msg[2].flags = tran_flag;
if (tran_flag & MXC_I2C_FLAG_READ) {
msg[2].flags |= I2C_M_RD;
} else {
msg[2].flags &= ~I2C_M_RD;
}
//调用i2c_transfer访问设备
ret = i2c_transfer(tw_i2c_client.adapter, msg, 3);
if (ret >= 0)
return 0;
return ret;
}
我们跟踪i2c_transfer函数:
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
int ret;
if (adap->algo->master_xfer) {
mutex_lock(&adap->bus_lock);
//调用了adap->algo->master_xfer函数,这个函数的实现是在i2c总线驱动里做的,在讲总线驱动的时候具体解释。
ret = adap->algo->master_xfer(adap,msgs,num);
mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
}
总结:我们来看看整个I2C设备驱动的设计流程。
1. i2c_add_driver:添加一个I2C驱动
2.i2c_register_driver:直接被调用,将该驱动向系统注册(设备管理模型)。对系统的每个适配器,都调用i2c_probe函数,直到找到设备所接的适配器。
3.i2c_probe:将合法的设备从地址传递给i2c_probe_address函数
4.i2c_probe_address:调用总线驱动函数探测该总线上面是否接有该设备,如果有则调用i2c_attach_client函数
5.i2c_attach_client:将该设备的 i2c_client结构体依附到对应的适配器