i2c驱动程序.docx
《i2c驱动程序.docx》由会员分享,可在线阅读,更多相关《i2c驱动程序.docx(117页珍藏版)》请在冰豆网上搜索。
i2c驱动程序
目录
1IIC子系统初始化1
1.1IIC子系统初始化i2c_init1
1.2IIC子系统退出函数i2c_exit1
2i2c_bus_type总线2
2.1总线的match方法2
2.2总线的probe方法2
2.3一些函数3
2.3.1i2c_match_id()函数3
3i2c_driver驱动3
3.1i2c_driver注册4
3.1.1i2c_add_driver添加一个驱动4
3.1.2i2c_register_driver注册一个驱动4
3.2驱动注册中重要函数5
4适配器adapter驱动程序5
4.1i2c_adapter适配器5
4.1.1IIC适配器加载函数6
4.1.3i2c_register_adapter()适配器注册函数8
4.1.4适配器卸载函数i2c_del_adapter()9
4.2s3c24xx_i2c适配器(板子具体扩充)13
4.3适配器通信方法14
4.3.1i2c_msg结构和i2c_transfer14
4.3.1.1i2c_transfer15
4.3.2i2c_algorithm结构16
4.3.3s3c24xx_i2c_algorithm结构体(板子具体扩展)16
4.3.4s3c24xx_i2c_irq中断函数19
4.3.5一些其它通讯函数23
5IIC设备层驱动程序24
5.1IIC设备驱动模块加载和卸载24
5.2platform_device设备24
5.2.1s3c2410_platform_i2c25
5.2.2s3c_i2c0_set_platdata25
5.3.3default_i2c_data026
5.3platform_driver驱动26
5.3.1probe函数26
5.3.2remove函数29
5.4一些函数29
6i2c_client31
6.1i2c_client_type32
6.2生成i2c_clent33
6.2.1i2c_detect根据驱动上的所有地址数据产生所有的i2c设备33
6.2.2i2c_detect_address根据client和driver产生一个i2c设备35
6.2.3i2c_new_device实例化一个i2c设备36
6.2.4adapter注册时37
6.2.5i2c_driver注册时38
6.3其它函数39
6.3.1i2c_smbus_xfer39
6.3.2i2c_smbus_xfer_emulated39
6.4.i2c_board_info43
7i2c-dev.c设备驱动45
7.1i2c_dev_init45
7.2i2c_dev设备46
7.3新增的i2c_driver驱动---i2cdev_driver46
7.3.1i2cdev_attach_adapter获得一个i2c_dev并注册添加属性46
7.3.2i2cdev_detach_adapter函数47
7.3.3属性dev_attr_name47
7.4文件操作48
7.4.1open函数48
7.4.2read函数49
7.4.3write函数50
7.4.4unlocked_ioctl函数--i2cdev_ioctl51
7.5一些函数54
7.5.1get_free_i2c_dev获得一个新的i2c_dev结构并加入i2c_dev_list链表54
7.5.2i2c_dev_get_by_minor遍历i2c_dev_list链表查找此设备号为index的i2c_dev54
7.5.3i2c_get_adapter和i2c_put_adapter55
7.5.4i2c_new_dummy55
8at24驱动—非系统55
8.1模块初始化55
8.2数据结构56
8.2.1at24_data56
8.2.2at24_ids56
8.3at24驱动模块at24_driver57
8.3.1驱动的probe函数57
8.3.2驱动的remove函数60
8.3.3at24_bin_read函数60
8.3.4at24_bin_write函数62
8.4一些函数64
8.4.1at24_translate_offset64
1IIC子系统初始化
1.1IIC子系统初始化i2c_init
staticint__initi2c_init(void)
{
intretval;
retval=bus_register(&i2c_bus_type);//注册IIC总线i2c_bus_type
if(retval)
returnretval;
#ifdefCONFIG_I2C_COMPAT
i2c_adapter_compat_class=class_compat_register("i2c-adapter");
if(!
i2c_adapter_compat_class){
retval=-ENOMEM;
gotobus_err;
}
#endif
retval=i2c_add_driver(&dummy_driver);
//调用i2c_register_driver(THIS_MODULE,driver),这个dummy_driver是个空驱动
if(retval)
gotoclass_err;
return0;
class_err:
#ifdefCONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
bus_unregister(&i2c_bus_type);
returnretval;
}
1.2IIC子系统退出函数i2c_exit
staticvoid__exiti2c_exit(void)
{
i2c_del_driver(&dummy_driver);//注销IIC设备驱动程序
#ifdefCONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
#endif
bus_unregister(&i2c_bus_type);//注销IIC总线i2c_bus_type
}
postcore_initcall(i2c_init);
module_exit(i2c_exit);
2i2c_bus_type总线
structbus_typei2c_bus_type={
.name="i2c",
.match=i2c_device_match,
.probe=i2c_device_probe,
.remove=i2c_device_remove,
.shutdown=i2c_device_shutdown,
.suspend=i2c_device_suspend,
.resume=i2c_device_resume,
};
2.1总线的match方法
staticinti2c_device_match(structdevice*dev,structdevice_driver*drv)
{
structi2c_client*client=i2c_verify_client(dev);//设备的类型必须是i2c_client_type才能正常获得client
//而只有client的device_type是i2c_client_type,adapter的不是i2c_client_type型的。
从而若dev是adapter中的dev的话是不会再往下走,直接返回0
structi2c_driver*driver;
if(!
client)
return0;
driver=to_i2c_driver(drv);
/*matchonanidtableifthereisone*/
if(driver->id_table)
returni2c_match_id(driver->id_table,client)!
=NULL;//驱动的id和设备的名字是否匹配,匹配返回1
return0;
}//match成功后系统就把dev的driver设成driver了
staticconststructi2c_device_id*i2c_match_id(conststructi2c_device_id*id,conststructi2c_client*client)
{
while(id->name[0]){
if(strcmp(client->name,id->name)==0)
returnid;
id++;
}
returnNULL;
}
总的来说是i2c_driver驱动的id_table的名字和i2c_client设备的名字匹配。
不管adapter设备。
match后系统把i2c_client->dev的driver设成是此i2c_driver->driver。
2.2总线的probe方法
实际调研i2c_driver的probe函数,由于match方法只有是i2c_client_type类型才能匹配,然后才能调用probe,所以此处的dev也只有是client类型才行。
staticinti2c_device_probe(structdevice*dev)
{
structi2c_client*client=i2c_verify_client(dev);
structi2c_driver*driver;
intstatus;
if(!
client)
return0;
driver=to_i2c_driver(dev->driver);
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");
//比较i2c_driver的id_table和i2c_client名字是否匹配
status=driver->probe(client,i2c_match_id(driver->id_table,client));
if(status)
client->driver=NULL;
returnstatus;
}
2.3一些函数
2.3.1i2c_match_id()函数
i2c_match_id比较id和i2c_client名字是否匹配
staticconststructi2c_device_id*i2c_match_id(conststructi2c_device_id*id,conststructi2c_client*client)
{
while(id->name[0]){
if(strcmp(client->name,id->name)==0)
returnid;
id++;
}
returnNULL;
}
3i2c_driver驱动
structi2c_driver{
unsignedintclass;//驱动的类型
//注意出现一个新总线或移除一个新总线时的驱动。
应该尽量避免使用这个,可能未来会取消
int(*attach_adapter)(structi2c_adapter*);//检测到适配器时调用(传统驱动),感觉i2c_detect调用后调用attach_adapter函数,或是i2c_new_device后调用的
int(*detach_adapter)(structi2c_adapter*);//卸载适配器时调用(传统驱动)
//标准驱动模型接口,支持动态插入,要么只定义下面的的新类型的设备驱动函数,要么之定义上面两个不可以动态插入和拔出的旧的函数。
int(*probe)(structi2c_client*,conststructi2c_device_id*);//新类型设备的探测函数
int(*remove)(structi2c_client*);//新类型设备的移除函数
void(*shutdown)(structi2c_client*);//关闭设备
int(*suspend)(structi2c_client*,pm_message_tmesg);//挂起设备
int(*resume)(structi2c_client*);//恢复设备
//使用命令是设备完成特殊的功能,类似ioctl函数
int(*command)(structi2c_client*client,unsignedintcmd,void*arg);
structdevice_driverdriver;//设备驱动结构体
conststructi2c_device_id*id_table;//设备ID表
//自动设备创建调用用的设备检测回调函数
int(*detect)(structi2c_client*,intkind,structi2c_board_info*);//自动探测设备的回调函数
conststructi2c_client_address_data*address_data;//设备所在的地址范围
structlist_headclients;//指向驱动支持的设备
};
其中设备ID数据结构为:
structi2c_device_id{
charname[I2C_NAME_SIZE];
kernel_ulong_tdriver_data/*Dataprivatetothedriver*/
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
structi2c_driver–代表一个I2C设备驱动,对应一套驱动方法,一个i2c_driver上可以支持多个同等类型的i2c_client。
driver.owner应该设定为模块拥有者。
driver.name应该设定为驱动的名字。
对于自动设备检测,必须定义detect和address_data。
应该设置class,否则只会创建强制设定了模块参数的设备。
detect函数至少应该填充i2c_board_info的name字段,成功检测后处理,可能也会处理flags字段。
若没有detect,则针对列举的设备驱动仍会正常运行。
不会支持检测到的设备。
传递给detect的i2c_client结构不是一个真正的i2c_client。
它只被粗略初始化使得可以调用i2c_smbus_read_byte_data和friends。
不要用它做任何事情。
通常,不允许调用dev_dbg和它的friends。
3.1i2c_driver注册
3.1.1i2c_add_driver添加一个驱动
使用i2c_add_driver函数注册一个驱动。
staticinlineinti2c_add_driver(structi2c_driver*driver)
{
returni2c_register_driver(THIS_MODULE,driver);
}
3.1.2i2c_register_driver注册一个驱动
1.注册i2c_driver中的driver,会调用总线的probe和match方法,只匹配和探测client设备。
2.初始化驱动支持的设备链表
3.对于drv的总线上的所有设备运行__attach_adapter,对于drv的总线上的所有adapter设备根据此驱动上的所有地址数据产生adapter所有的i2c设备;并调用driver的attach_adapter函数。
staticstructi2c_driverdummy_driver={
.driver.name="dummy",
.probe=dummy_probe,
.remove=dummy_remove,
.id_table=dummy_id,
};
inti2c_register_driver(structmodule*owner,structi2c_driver*driver)
{
intres;
if(unlikely(WARN_ON(!
i2c_bus_type.p)))//总线已经注册过的话则已经定义了私有数据
return-EAGAIN;
driver->driver.owner=owner;
driver->driver.bus=&i2c_bus_type;
//注册返回时,驱动核心已经调用probe匹配未绑定设备
//driver_register会对于总线的所有设备(进入match和probe后发现这里的设备只是client设备)调用match和probe
//(因为i2c_driver->driver的match和probe没有定义,所以调用总线的match和Probe,而总线的probe调用了i2c_driver的probe函数)
//只初始化了owner和bus没有初始化driver中别的函数。
res=driver_register(&driver->driver);
if(res)
returnres;
pr_debug("i2c-core:
driver[%s]registered\n",driver->driver.name);
INIT_LIST_HEAD(&driver->clients);//初始化驱动支持的设备链表
/*Walktheadaptersthatarealreadypresent*/
mutex_lock(&core_lock);
bus_for_each_dev(&i2c_bus_type,NULL,driver,__attach_adapter);
//对于drv的总线上的所有设备(进入函数里会发现是所有adpter设备)运行__attach_adapter,也就是针对所有总线上的所有adapter设备执行i2c_driver的attach函数,产生对应的i2c_client设备。
mutex_unlock(&core_lock);
return0;
}
3.2驱动注册中重要函数
4适配器adapter驱动程序
4.1i2c_adapter适配器
structi2c_adapter{
structmodule*owner;//THIS_MODULE
unsignedintid;//定义于i2c_id.h中
unsignedintclass;/*允许探测的驱动类型*/
conststructi2c_algorithm*algo;/*指向适配器的驱动程序*/
void*algo_data;//指向适配器的私有数据,根据不同情况使用的方法不同
/*datafieldsthatarevalidforalldevices*/
u8level;/*nestinglevelforlockdep*/
structmutexbus_lock;//获得总线的锁
inttimeout;/*injiffies超时*/
intretries;//重试次数
structdevicedev;/*指向适配器设备结构体*/
//adapter的子设备应该是client中的dev
//device_for_each_child(&adapter->dev,&addr,__i2c_check_addr);
//之后__i2c_check_addr函数中可以i2c_verify_client();获得子设备对应的client。
intnr;//好像是iic控制器数量,也用于次设备号
charname[48];//适配器名称
structcompletiondev_released;//用于同步的完成量
};
i2c_adapter用来辨识一个物理i2c总线控制器,带有必要的访问算法。
相当于一个IIC总线的控制器。
系统中可以有多个总线适配器。
I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。
i2c_adapter通过device结构连接到i2c总线上,aglo指针指向具体的总线通信方法s3c24xx_i2c_algorithm结构体。
4.1.1IIC适配器加载函数
4.1.1.1i2c_add_adapter()函数
当驱动开发人员拿到一块新的电路板,并研究了响应的IIC适配器后,就应该使用内核提供的框架函数向IIC子系统中添加一个新的适配器。
这个过程如下所示。
声明一个i2cadapter,使用动态busnumber。
此时busnumber不重要。
例如,USB链接或PCI插卡动态增加的I2C适配器。
返回0,则分配了新的busnumber(实际上分配了一个新的ID号)并存贮到adap->nr,并使制定的adapter对于client来说生效了否则返回负的errno(ENOMEM/EAGAIN)。
inti2c_add_adapter(structi2c_adapter*adapter)
{
intid,res=0;
retry:
if(idr_pre_get(&i2c_adapter_idr,GFP_KERNEL)==0)//为ID号分配内存
return-ENOMEM;
mutex_lock(&core_lock);
res=idr_get_new_above(&i2c_adapter_idr,adapter,__i2c_first_dynamic_bus_num,&id);
//使ID号id与指针adapter关联,