一、i2c_client、i2c_driver和i2c_adapter结构的创建
我们先来看看这三个结构是如何被定义的。
在mach-zhaocj2440.c文件中的zhaocj2440_init函数内,有下面一句:
i2c_register_board_info(0,zhaocj2440_i2c_devs,ARRAY_SIZE(zhaocj2440_i2c_devs));
i2c_register_board_info函数是在drivers/i2c/i2c-boardinfo.c中被定义,作用是静态注册声明开发板上的IIC信息。其中该函数的参数zhaocj2440_i2c_devs的作用是定义板上的IIC设备信息,它是在mach-zhaocj2440.c内定义的。由于我的开发板上的IIC设备为AT24C02的eeprom,因此zhaocj2440_i2c_devs应该重新定义为:
static struct i2c_board_info zhaocj2440_i2c_devs[] __initdata = {
{
I2C_BOARD_INFO("24c02", 0x50), //宏定义:.type=24c02, .addr=0x50,
.platform_data= &at24c02,
},
};
而at24c02定义为:
static struct at24_platform_data at24c02 = {
.byte_len = SZ_2K / 8, //容量大小
.page_size = 8, //每页的字节数
};
i2c_board_info.type就是i2c_client.name变量,i2c_board_info.addr就是i2c_client.addr变量,所以只要定义了zhaocj2440_i2c_devs,也就是定义了i2c_client结构。关于这一点,我们在后面会讲到。
我们再来看drivers/misc/eeprom/at24.c文件中的模块初始化函数at24_init:
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);
//为IIC添加驱动
return i2c_add_driver(&at24_driver);
}
i2c_add_driver函数通过宏定义为i2c_register_driver函数,它的作用是注册IIC驱动,这里的驱动是at24_driver,它的定义为:
static struct i2c_driver at24_driver = {
.driver= {
.name= "at24",
.owner= THIS_MODULE,
},
.probe= at24_probe,
.remove= __devexit_p(at24_remove),
.id_table= at24_ids,
};
IIC适配器是通过i2c-s3c2410.c文件内的s3c24xx_i2c_probe函数创建的。该函数调用了i2c_add_numbered_adapter函数,它又调用了i2c_register_adapter函数,从而完成了对i2c_adapter的创建和注册。
二、i2c_client、i2c_driver和i2c_adapter三者的绑定
这样,i2c_client、i2c_driver和i2c_adapter都被创建了,并都各自放到了总线上,那它们是如何关联在一起的呢?
在s3c24xx_i2c_probe函数内调用了of_i2c_register_devices函数,该函数提取出了i2c_board_info设备信息,并且又调用了i2c_new_device函数,该函数主要完成了两项工作,一是通过把i2c_board_info赋值给i2c_client,从而创建了i2c_client,因此正如我们在前面介绍过的,定义了i2c_board_info,也就是定义了i2c_client;二是把刚刚创建的IIC设备与在s3c24xx_i2c_probe函数创建的IIC适配器绑定在了一起,如下面这句:
client->adapter= adap;
如前文所述,i2c_register_adapter函数用于注册i2c_adapter,i2c_register_driver函数用于注册i2c_driver。i2c_register_adapter函数有下面一句:
adap->dev.bus= &i2c_bus_type;
i2c_register_driver函数有下面一句:
driver->driver.bus= &i2c_bus_type;
总之,都定义了IIC的总线类型——i2c_bus_type:
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,
};
当设备或驱动添加到总线上时,会调用match指向的函数,用于找到是否有设备和驱动相匹配;当设备或驱动添加到总线上时,也会调用probe指向的函数,用于初始化与驱动相匹配的设备。因此无论是在创建i2c_adapter,还是i2c_driver的时候,都会调用这两个函数。
static int i2c_device_match(struct device *dev, structdevice_driver *drv)
{
structi2c_client *client = i2c_verify_client(dev);
structi2c_driver *driver;
if(!client)
return0;
/*Attempt an OF style match */
//设备和驱动是否匹配
if(of_driver_match_device(dev, drv))
return1;
driver= to_i2c_driver(drv);
/*match on an id table if there is one */
//如果驱动有设备列表,则用下面判断设备和驱动是否匹配
if(driver->id_table)
returni2c_match_id(driver->id_table,client) != NULL;
return0;
}
i2c_device_match函数返回非零值,表示设备和驱动匹配上了。at24_driver中有一个设备列表项——at24_ids:
static const struct i2c_device_id at24_ids[] = {
/*needs 8 addresses as A0-A2 are ignored */
{"24c00",AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
/*old variants can't be handled with this generic entry! */
{"24c01",AT24_DEVICE_MAGIC(1024 / 8, 0) },
{"24c02",AT24_DEVICE_MAGIC(2048 / 8, 0) },
/*spd is a 24c02 in memory DIMMs */
{"spd", AT24_DEVICE_MAGIC(2048 / 8,
AT24_FLAG_READONLY| AT24_FLAG_IRUGO) },
{"24c04",AT24_DEVICE_MAGIC(4096 / 8, 0) },
/*24rf08 quirk is handled at i2c-core*/
{"24c08",AT24_DEVICE_MAGIC(8192 / 8, 0) },
{"24c16",AT24_DEVICE_MAGIC(16384 / 8, 0) },
{"24c32",AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
{"24c64",AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
{"24c128",AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
{"24c256",AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
{"24c512",AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
{"24c1024",AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
{"at24", 0 },
{/* END OF LIST */ }
};
上面结构中的一项"24c02",与我们前面定义的i2c_client.name一致,因此i2c_client和i2c_driver匹配上了。
static int i2c_device_probe(struct device *dev)
{
structi2c_client *client = i2c_verify_client(dev);
structi2c_driver *driver;
intstatus;
if(!client)
return0;
//提取IIC驱动
driver = to_i2c_driver(dev->driver);
if(!driver->probe || !driver->id_table)
return-ENODEV;
//i2c_client和i2c_driver绑定在了一起
client->driver= driver;
if(!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags& I2C_CLIENT_WAKE);
dev_dbg(dev,"probe\n");
//调用probe函数,也就是at24_probe函数
status= driver->probe(client, i2c_match_id(driver->id_table,client));
if(status) {
client->driver= NULL;
i2c_set_clientdata(client, NULL);
}
returnstatus;
}
经过上面的分析,可以看出IIC设备中的驱动指向了IIC驱动,IIC设备中的适配器指向了IIC适配器,这样三者就都绑定在了一起,成为了一个整体。
三、IIC设备的操作
系统对IIC设备的操作是在at24_probe函数完成的:
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
structat24_platform_data chip;
boolwritable;
intuse_smbus = 0;
structat24_data *at24;
interr;
unsignedi, num_addresses;
kernel_ulong_tmagic;
if(client->dev.platform_data) {
chip= *(struct at24_platform_data *)client->dev.platform_data;
}else {
if(!id->driver_data) {
err= -ENODEV;
gotoerr_out;
}
magic = id->driver_data;
chip.byte_len= BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic>>= AT24_SIZE_BYTELEN;
chip.flags= magic & AT24_BITMASK(AT24_SIZE_FLAGS);
/*
* This is slow, but we can't know all eeproms,so we better
* play safe. Specifying custom eeprom-typesvia platform_data
* is recommended anyhow.
*/
chip.page_size= 1;
/*update chipdata if OF is present */
at24_get_ofdata(client,&chip);
chip.setup= NULL;
chip.context= NULL;
}
//判断IIC设备(这里指的就是eeprom)容量是否为2的次幂
if(!is_power_of_2(chip.byte_len))
dev_warn(&client->dev,
"byte_lenlooks suspicious (no power of 2)!\n");
//判断eeprom的每页的大小
if(!chip.page_size) {
dev_err(&client->dev,"page_size must not be 0!\n");
err= -EINVAL;
gotoerr_out;
}
//判断eeprom每页的大小是否为2的次幂
if(!is_power_of_2(chip.page_size))
dev_warn(&client->dev,
"page_sizelooks suspicious (no power of 2)!\n");
/*Use I2C operationsunless we're stuck with SMBus extensions. */
if(!i2c_check_functionality(client->adapter,I2C_FUNC_I2C)) {
if(chip.flags & AT24_FLAG_ADDR16) {
err= -EPFNOSUPPORT;
gotoerr_out;
}
if(i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
use_smbus= I2C_SMBUS_I2C_BLOCK_DATA;
}else if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
use_smbus= I2C_SMBUS_WORD_DATA;
}else if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
use_smbus= I2C_SMBUS_BYTE_DATA;
}else {
err= -EPFNOSUPPORT;
gotoerr_out;
}
}
if(chip.flags & AT24_FLAG_TAKE8ADDR)
num_addresses= 8;
else
num_addresses= DIV_ROUND_UP(chip.byte_len,
(chip.flags &AT24_FLAG_ADDR16) ? 65536 : 256);
at24= kzalloc(sizeof(struct at24_data) +
num_addresses* sizeof(struct i2c_client *),GFP_KERNEL);
if(!at24) {
err= -ENOMEM;
gotoerr_out;
}
mutex_init(&at24->lock);
at24->use_smbus= use_smbus;
at24->chip= chip;
at24->num_addresses= num_addresses;
/*
* Export the EEPROM bytes through sysfs, sincethat's convenient.
* By default, only root should see the data(maybe passwords etc)
*/
//下面是定义一些关于sysfs的属性
sysfs_bin_attr_init(&at24->bin);
at24->bin.attr.name ="eeprom"; //名称
at24->bin.attr.mode = chip.flags &AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
at24->bin.read= at24_bin_read; //读操作
at24->bin.size = chip.byte_len;
at24->macc.read= at24_macc_read;
writable= !(chip.flags & AT24_FLAG_READONLY);
if(writable) {
if(!use_smbus || i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
unsignedwrite_max = chip.page_size;
at24->macc.write= at24_macc_write;
at24->bin.write =at24_bin_write; //写操作
at24->bin.attr.mode|= S_IWUSR;
if(write_max > io_limit)
write_max= io_limit;
if(use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
write_max= I2C_SMBUS_BLOCK_MAX;
at24->write_max= write_max;
/*buffer (data + address at the beginning) */
at24->writebuf= kmalloc(write_max + 2, GFP_KERNEL);
if(!at24->writebuf) {
err= -ENOMEM;
gotoerr_struct;
}
}else {
dev_warn(&client->dev,
"cannotwrite due to controller restrictions.");
}
}
at24->client[0]= client;
/*use dummy devices for multiple-address chips */
for (i = 1; i < num_addresses; i++){
at24->client[i]= i2c_new_dummy(client->adapter,
client->addr+ i);
if(!at24->client[i]) {
dev_err(&client->dev,"address 0x%02x unavailable\n",
client->addr+ i);
err= -EADDRINUSE;
gotoerr_clients;
}
}
//为sysfs创建二进制文件
err= sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
if(err)
gotoerr_clients;
i2c_set_clientdata(client, at24);
dev_info(&client->dev,"%zu byte %s EEPROM, %s, %u bytes/write\n",
at24->bin.size,client->name,
writable? "writable" : "read-only", at24->write_max);
if(use_smbus == I2C_SMBUS_WORD_DATA||
use_smbus == I2C_SMBUS_BYTE_DATA) {
dev_notice(&client->dev,"Falling back to %s reads, "
"performance will suffer\n",use_smbus ==
I2C_SMBUS_WORD_DATA? "word" : "byte");
}
/*export data to kernel code */
if(chip.setup)
chip.setup(&at24->macc,chip.context);
return0;
err_clients:
for(i = 1; i < num_addresses; i++)
if(at24->client[i])
i2c_unregister_device(at24->client[i]);
kfree(at24->writebuf);
err_struct:
kfree(at24);
err_out:
dev_dbg(&client->dev,"probe error %d\n", err);
returnerr;
}
在这里,读文件的函数为at24_bin_read,它又调用了at24_read函数,最后又调用at24_eeprom_read函数;同样的,写文件的函数为at24_bin_write,它又调用了at24_write函数,最后又调用at24_eeprom_write函数。at24_eeprom_read和at24_eeprom_write最终都要调用i2c_transfer函数,来真正完成对AT24C02的读写操作。
i2c_transfer函数已经在上一篇文章分析过了,这里就不再重复。