linux 驱动中I2C设备驱动有两种,其一为用户模式设备驱动,依赖i2c子系统中的i2c-dev驱动,在用户空间去读写i2c设备,另一种就是普通的设备驱动。本文主要讨论第一种:在用户空间读写I2C设备。
首先关于用户空间读写I2C的基本操作, 《i2c驱动之调用ioctl函数进行读写at24c08》 这篇文章看一下就好,不重复说了。然后是MTK平台,I2C驱动的分析,可以看一下 《MTK I2C驱动代码分析》。
下面开始干活:
首先打开文件
fd=open("/dev/i2c-2",O_RDWR);
发现 /dev 路径下并没有i2c-2这个节点,查看 (project)/kernel-3.18/drivers/i2c 下的Makefile
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
而在 (project)/kernel-3.18/arch/arm64/configs/(project)_defconfig中默认情况下,CONFIG_I2C_CHARDEV 这个宏是没有打开的。
# CONFIG_I2C_CHARDEV is not set
把这个宏打开就可以了。这时候又会引出一个权限问题,这个根据SEAndroid的规则,申请相应的权限就好,否则第三方应用无法操作 /dev/i2c-2 这个节点。
然后继续,设置i2c_rdwr_ioctl_data.msgs参数
vcnl4200_i2c_data.nmsgs = 2;
vcnl4200_i2c_data.msgs[0].len = 1;
vcnl4200_i2c_data.msgs[0].flags = 0; //write
vcnl4200_i2c_data.msgs[0].addr = 0x6B;
data[0][0]=0x00;
vcnl4200_i2c_data.msgs[0].buf=data[0];
vcnl4200_i2c_data.msgs[1].len = 2;
vcnl4200_i2c_data.msgs[1].flags = I2C_M_RD; //read
vcnl4200_i2c_data.msgs[1].addr = 0x6B;
data[1][0]=0;
data[1][1]=0;
vcnl4200_i2c_data.msgs[1].buf=data[1];
结果发现无论怎样设置都没有数据输出,即I2C没有波形输出,但是抓log又没有看到报错。最后发现,MTK的内核i2c_msg结构体与linux标准的不一样:
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
#ifdef CONFIG_MTK_I2C_EXTENSION
__u32 timing; /* parameters of timings */
__u32 ext_flag;
#endif
};
mtk做的i2c_msg结构体多了 timing和ext_flag这两个成员变量。按照上面的参数设置,会出现参数校验失败,而这个参数校验失败,系统不会报错。
修改应用的i2c_msg结构体定义,与内核一致。
完整的I2C读数据函数:
unsigned char _i2c_read ( unsigned char device_addr, unsigned char sub_addr, unsigned char * buff, short int ByteNo )
{
unsigned int fd, ret;
struct i2c_rdwr_ioctl_data vcnl4200_i2c_data;
const char * i2c_dev = "/dev/i2c-2";
unsigned char data[2][10];
fd = open ( i2c_dev, O_RDWR );
if ( fd < 0 )
{
LOGE("error (errno=%d)", errno);
LOGE ( "no /dev/i2c-2\r\n" );
return FAIL;
}
ioctl(fd, I2C_TIMEOUT, 2);
ioctl(fd, I2C_RETRIES, 1);
vcnl4200_i2c_data.nmsgs = 2;
vcnl4200_i2c_data.msgs