I2C 总线驱动, I2C总线驱动就是SOC的 I2C控制器驱动,也叫做 I2C适配器驱动。
I2C 设备驱动, I2C设备驱动就是针对具体的 I2C设备而编写的驱动。
I2C框架下的几个重要成员
1. I2C总线
I2C总线结构体在driversi2ci2c-core.c中定义如下:
structbus_typei2c_bus_type={
.name="i2c",
.match=i2c_device_match,
.probe=i2c_device_probe,
.remove=i2c_device_remove,
.shutdown=i2c_device_shutdown,
};
I2C总线对应着/bus下的一条总线,这个i2c总线结构体管理着i2c设备与I2C驱动的匹配,删除等操作,I2C总线会调用i2c_device_match函数看I2C设备和I2C驱动是否匹配,如果匹配就调用i2c_device_probe函数,进而调用I2C驱动的probe函数。
形如:

i2c_device_match会管理I2C设备和I2C总线匹配规则,这将和如何编写I2C驱动程序息息相关。
2. I2C驱动
i2c_driver 类似 platform_driver,是我们编写 I2C 设备驱动重点要处理的内容, i2c_driver 结构体定义在 include/linux/i2c.h 文件中,内容如下:
structi2c_driver{ unsignedintclass; /*Notifiesthedriverthatanewbushasappeared.Youshouldavoid *usingthis,itwillberemovedinanearfuture. */ int(*attach_adapter)(structi2c_adapter*)__deprecated; /*Standarddrivermodelinterfaces*/ int(*probe)(structi2c_client*,conststructi2c_device_id*); int(*remove)(structi2c_client*); /*drivermodelinterfacesthatdon'trelatetoenumeration*/ void(*shutdown)(structi2c_client*); /*Alertcallback,forexamplefortheSMBusalertprotocol. *Theformatandmeaningofthedatavaluedependsontheprotocol. *FortheSMBusalertprotocol,thereisasinglebitofdatapassed *asthealertresponse'slowbit("eventflag"). */ void(*alert)(structi2c_client*,unsignedintdata); /*aioctllikecommandthatcanbeusedtoperformspecificfunctions *withthedevice. */ int(*command)(structi2c_client*client,unsignedintcmd,void*arg); structdevice_driverdriver; conststructi2c_device_id*id_table; /*Devicedetectioncallbackforautomaticdevicecreation*/ int(*detect)(structi2c_client*,structi2c_board_info*); constunsignedshort*address_list; structlist_headclients; };
重点成员如下:
int(*probe)(structi2c_client*,conststructi2c_device_id*)
当 I2C设备和驱动匹配成功以后 probe函数就会执行。
structdevice_driverdriverdevice_driver
驱动结构体,如果使用设备树的话,需要设置device_driver的of_match_table成员变量,也就是驱动的兼容(compatible)属性。
conststructi2c_device_id*id_table
id_table 是传统的、未使用设备树的设备匹配 ID表
3. I2C设备
I2C设备结构体i2c_client 结构体定义在 include/linux/i2c.h 文件中,内容如下:
structi2c_client{
unsignedshortflags;/*div.,seebelow*/
unsignedshortaddr;/*chipaddress-NOTE:7bit*/
/*addressesarestoredinthe_LOWER_7bits*/
charname[I2C_NAME_SIZE];
structi2c_adapter*adapter;/*theadapterwesiton*/
structdevicedev;/*thedevicestructure*/
intirq;/*irqissuedbydevice*/
structlist_headdetected;
#ifIS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_tslave_cb;/*callbackforslavemode*/
#endif
};
重点成员如下:
flags:标志
addr:芯片地址,7 位,存在低 7 位
flagsname[I2C_NAME_SIZE]:名字
adapter:对应的 I2C 适配器
dev:设备结构体
irq:中断
一个设备对应一个 i2c_client,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client。
4. I2C适配器
经过上面的介绍,知道有I2C驱动和I2C设备,我们需要通过I2C驱动去和I2C设备通讯,这其中就需要一个I2C设配器,I2C设配器对应的就是SOC上的I2C控制器。
Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter, i2c_adapter 结构体定义在 include/linux/i2c.h 文件中,结构体内容如下:
/*
*i2c_adapteristhestructureusedtoidentifyaphysicali2cbusalong
*withtheaccessalgorithmsnecessarytoaccessit.
*/
structi2c_adapter{
structmodule*owner;
unsignedintclass;/*classestoallowprobingfor*/
conststructi2c_algorithm*algo;/*thealgorithmtoaccessthebus*//*总线访问算法*/
void*algo_data;
/*datafieldsthatarevalidforalldevices*/
structrt_mutexbus_lock;
inttimeout;/*injiffies*/
intretries;
structdevicedev;/*theadapterdevice*/
intnr;
charname[48];
structcompletiondev_released;
structmutexuserspace_clients_lock;
structlist_headuserspace_clients;
structi2c_bus_recovery_info*bus_recovery_info;
conststructi2c_adapter_quirks*quirks;
};
重点成员如下:
conststructi2c_algorithm*algo
I2C 适配器与 IIC 设备进行通信的方法。
i2c_algorithm 结构体定义在 include/linux/i2c.h 文件中,内容如下:
structi2c_algorithm{
/*Ifanadapteralgorithmcan'tdoI2C-levelaccess,setmaster_xfer
toNULL.IfanadapteralgorithmcandoSMBusaccess,set
smbus_xfer.IfsettoNULL,theSMBusprotocolissimulated
usingcommonI2Cmessages*/
/*master_xfershouldreturnthenumberofmessagessuccessfully
processed,oranegativevalueonerror*/
int(*master_xfer)(structi2c_adapter*adap,structi2c_msg*msgs,
intnum);
int(*smbus_xfer)(structi2c_adapter*adap,u16addr,
unsignedshortflags,charread_write,
u8command,intsize,unioni2c_smbus_data*data);
/*Todeterminewhattheadaptersupports*/
u32(*functionality)(structi2c_adapter*);
#ifIS_ENABLED(CONFIG_I2C_SLAVE)
int(*reg_slave)(structi2c_client*client);
int(*unreg_slave)(structi2c_client*client);
#endif
};
重点成员如下:
master_xfer:I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信。
smbus_xfer:SMBUS 总线的传输函数
I2C 适配器驱动的主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm中的master_xfer函数。完成以后通过 i2c_add_numbered_adapter或 i2c_add_adapter这两个函数向系统注册设置好的 i2c_adapter。
这两个函数的区别在于 i2c_add_adapter 使用动态的总线号,而 i2c_add_numbered_adapter使用静态总线号。
5. 小结
I2C驱动有4个重要的东西:I2C总线、I2C驱动、I2C设备、I2C设备器。
I2C总线:维护着两个链表(I2C驱动、I2C设备),管理I2C设备和I2C驱动的匹配和删除等。
I2C驱动:对应的就是I2C设备的驱动程序。
I2C设备:是具体硬件设备的一个抽象。
I2C适配器:用于I2C驱动和I2C设备间的通用,是SOC上I2C控制器的一个抽象。
Linux I2C总线的运行机制:
注册I2C驱动
将I2C驱动添加到I2C总线的驱动链表中
遍历I2C总线上的设备链表,根据i2c_device_match函数进行匹配,如果匹配调用i2c_device_probe函数
i2c_device_probe函数会调用I2C驱动的probe函数
I2C驱动简单编写流程
一般 SOC 的 I2C总线驱动都是由半导体厂商编写的,这个不需要用户去编写。因此 I2C 总线驱动对于 SOC使用者来说是被屏蔽掉的,我们只要专注于 I2C 设备驱动即可。除非你是在半导体公司上班,工作内容就是写 I2C 适配器驱动。
i2c_driver类似platform_driver,是我们编写I2C设备驱动重点要处理的内容,i2c_driver在上面已经介绍了其结构体的具体内容。
对于我们 I2C 设备驱动编写人来说,重点工作就是构建i2c_driver,构建完成以后需要向Linux内核注册这个i2c_driver。
那么如何注册呢?
使用下面的这个函数:
inti2c_register_driver(structmodule*owner,structi2c_driver*driver)函数参数和返回值含义如下:
owner:一般为 THIS_MODULE。
driver:要注册的 i2c_driver。
返回值:0,成功;负值,失败。
另外 i2c_add_driver 也常常用于注册 i2c_driver, i2c_add_driver 是一个宏,定义如下:
#definei2c_add_driver(driver) i2c_register_driver(THIS_MODULE,driver)
i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册的 i2c_driver。
设备驱动的时候需要将前面注册的 i2c_driver 从 Linux 内核中注销掉,需要用到i2c_del_driver 函数,此函数原型如下:
voidi2c_del_driver(structi2c_driver*driver);函数参数和返回值含义如下:
driver:要注销的 i2c_driver。
返回值:无。
例程框架:
/*i2c驱动的probe函数*/
staticintxxx_probe(structi2c_client*client,
{
/*函数具体程序*/
return0;
}
/*i2c驱动的remove函数*/
staticintxxx_remove(structi2c_client*client)
{
/*函数具体程序*/
return0;
}
/*传统匹配方式ID列表*/
staticconststructi2c_device_idxxx_id[]={
{"xxx",0},
{}
};
/*设备树匹配列表*/
staticconststructof_device_idxxx_of_match[]={
{.compatible="xxx"},
{/*Sentinel*/}
};
/*i2c驱动结构体*/
staticstructi2c_driverxxx_driver={
.probe=xxx_probe,
.remove=xxx_remove,
.driver={
.owner=THIS_MODULE,
.name="xxx",
.of_match_table=xxx_of_match,
},
.id_table=xxx_id,
};
/*驱动入口函数*/
staticint__initxxx_init(void)
{
intret=0;
ret=i2c_add_driver(&xxx_driver);
returnret;
}
/*驱动出口函数*/
staticvoid__exitxxx_exit(void)
{
i2c_del_driver(&xxx_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
当I2C设备和I2C驱动匹配成功以后probe函数就会执行,这些和platform驱动一样,probe函数里面基本就是标准的字符设备驱动那一套了。
审核编辑:汤梓红
-
内核
+关注
关注
3文章
1372浏览量
40273 -
Linux
+关注
关注
87文章
11290浏览量
209291 -
I2C总线
+关注
关注
8文章
390浏览量
60911 -
I2C驱动
+关注
关注
0文章
9浏览量
7045
原文标题:Linux I2C 驱动入门,建议收藏!!!
文章出处:【微信号:混说Linux,微信公众号:混说Linux】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
基于I2C总线的EEPROM驱动程序
《Linux设备驱动开发详解》第15章、Linux的I2C核心、总线与设备驱动
基于嵌入式Linux下的I2C设备驱动的总体思路与框架设计
Linux驱动中的I2C驱动架构详细分析
Linux驱动:I2C设备驱动(基于Freescale i.MX6ULL平台了解I2C的驱动框架,顺便写个简陋的MPU6050驱动)
linux移植MPU6050的I2C驱动

Linux I2C驱动入门知识科普
评论