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文章
1362浏览量
40224 -
Linux
+关注
关注
87文章
11222浏览量
208890 -
I2C总线
+关注
关注
8文章
388浏览量
60824 -
I2C驱动
+关注
关注
0文章
9浏览量
7034
原文标题:Linux I2C 驱动入门,建议收藏!!!
文章出处:【微信号:混说Linux,微信公众号:混说Linux】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论