5 客户驱动
5.1 概述
i2c客户驱动是对i2c从设备的实现,一个具体的i2c客户驱动包括两个部分:一部分是i2c_driver,用于将设备挂接于i2c总线;另一部分是设备本身的驱动。
i2c客户驱动程序主要由i2c_driver和i2c_client来描述。
5.2 实例源码分析
好了,我们来深入了解客户驱动代码的实现,drivers/misc/eeprom/at24.c文件支持大多数i2c接口的eeprom
i2c_driver实现
-
staticstruct i2c_driver at24_driver = {
-
.driver= {
-
.name= "at24",
-
.owner= this_module,
-
},
-
.probe= at24_probe,
-
.remove= __devexit_p(at24_remove),
-
.id_table= at24_ids,
-
};
static struct i2c_driver at24_driver = {
.driver= {
.name= "at24",
.owner= this_module,
},
.probe= at24_probe, /* 当i2c_client和i2c_driver匹配时调用 */
.remove= __devexit_p(at24_remove), /* 注销时调用 */
.id_table= at24_ids, /* i2c_driver支持的i2c_client类型 */
};
初始化和卸载
-
staticint __init at24_init(void)
-
{
-
returni2c_add_driver(&at24_driver);
-
}
-
-
staticvoid __exit at24_exit(void)
-
{
-
i2c_del_driver(&at24_driver);
-
}
static int __init at24_init(void)
{
returni2c_add_driver(&at24_driver);
}
static void __exit at24_exit(void)
{
i2c_del_driver(&at24_driver);
}
at24_probe函数
-
staticint at24_probe(struct i2c_client*client, conststruct i2c_device_id *id)
-
{
-
……
-
-
-
-
-
-
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;
-
……
-
}
-
……
-
-
err = sysfs_create_bin_file(&client->dev.kobj,&at24->bin);
-
if(err)
-
gotoerr_clients;
-
-
i2c_set_clientdata(client,at24);
-
……
-
}
static int at24_probe(struct i2c_client*client, const struct i2c_device_id *id)
{
……
/*
* export the eeprom bytes through sysfs, sincethat's convenient.
* by default, only root should see the data(maybe passwords etc)
*/
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;
……
}
……
err = sysfs_create_bin_file(&client->dev.kobj,&at24->bin);
if(err)
gotoerr_clients;
i2c_set_clientdata(client,at24);
……
}
probe函数主要的工作是在sys目录下创建bin节点文件,用户可以同此节点文件来操作eeprom,并提供操作方法(read,write)
5.3 i2c_client实现
at24c不依赖于具体的cpu和i2c控制器硬件特性,因此如果电路板包含该外设,只需要添加对应的i2c_board_info,下面是at24c08 i2c_client在板文件中的实现:
-
staticstruct at24_platform_data at24c08 ={
-
.byte_len = sz_8k / 8,
-
.page_size = 16,
-
};
-
-
staticstruct i2c_board_infomini2440_i2c_devs[] __initdata = {
-
{
-
i2c_board_info("24c08",0x50),
-
.platform_data= &at24c08,
-
},
-
};
-
staticvoid __init mini2440_init(void)
-
{
-
……
-
i2c_register_board_info(0,mini2440_i2c_devs,
-
array_size(mini2440_i2c_devs));
-
……
-
}
static struct at24_platform_data at24c08 ={
.byte_len = sz_8k / 8, /* eeprom的存储大小,单位byte */
.page_size = 16, /* 页大小 byte */
};
static struct i2c_board_infomini2440_i2c_devs[] __initdata = {
{
i2c_board_info("24c08",0x50), /* 24c08设备名,0x50设备地址 */
.platform_data= &at24c08, /* 赋值给client->dev->platform_data */
},
};
static void __init mini2440_init(void)
{
……
i2c_register_board_info(0,mini2440_i2c_devs, /* busnum = 0,busnum是适配器编号,用来识别从设备使用的哪个适配器 */
array_size(mini2440_i2c_devs));
……
}
i2c_register_board_info函数会把i2c从设备硬件特性信息注册到全局链表__i2c_board_list,在调用i2c_add_adapter函数时,会遍历__i2c_board_list获得从设备信息来构造i2c_client。
i2c_client的构建
我们调用i2c_register_board_info函数会把i2c从设备硬件特性信息注册到全局链表__i2c_board_list,但是还没有构建出一个i2c_client结构体,也没有注册进i2c总线。我们来分析一下构造的过程,调用i2c_add_adapter函数时,会遍历__i2c_board_list获得从设备信息来构造i2c_client:i2c_register_adapter()->i2c_scan_static_board_info()->i2c_new_device()->device_register()。
5.4 i2c_driver和i2c_client的match
在调用i2c_add_driver注册i2c_driver和构建i2c_client时,都会调用i2c bus中注册的i2c_device_match()->i2c_match_id()函数通过i2c_driver->id_table->name和client->name来匹配
-
staticconststruct i2c_device_id*i2c_match_id(conststruct i2c_device_id *id,
-
conststruct i2c_client *client)
-
{
-
while(id->name[0]) {
-
if(strcmp(client->name, id->name) == 0)
-
returnid;
-
id ;
-
}
-
returnnull;
-
}
static const struct i2c_device_id*i2c_match_id(const struct i2c_device_id *id,
conststruct i2c_client *client)
{
while(id->name[0]) {
if(strcmp(client->name, id->name) == 0)
returnid;
id ;
}
returnnull;
}
5.5 测试
已在mini2440上实验成功,在/sys/bus/i2c/devices/0-0050/目录下(50代表从设备地址)会产生一个eeprom文件,这个文件相当于是硬件设备eeprom的映射,我们可以像普通文件一样对eeprom文件进行操作,实质上就是就硬件eeprom的操作。重启开发板,你会发现对eeprom文件修改过的内容不会改变,这就证明实验成功了,要知道sys文件系统是无法对数据保存的。
阅读(2644) | 评论(0) | 转发(4) |