前言
Android 9.0
Telechips tcc803x
用户如何在userspace直接访问板上的i2c外设寄存器?
一、实现原理
通过I2C_RDWR这个IO控制命令将i2c_msg数组传递给kernel去执行。
二、实现步骤
Talk is cheap, show my codes.
I2CCtr.h
#ifndef XXX
#define XXX
#pragma once
namespace I2C
{
typedef enum i2c_opt_state
{
I2C_IOCTL_OK = 0, /**< i2c ioctl OK */
I2C_IOCTL_NG, /**< i2c ioctl NG */
I2C_READ_OK, /**< i2c read OK */
I2C_READ_NG, /**< i2c read NG */
I2C_WRITE_OK, /**< i2c write OK */
I2C_WRITE_NG, /**< i2c write NG */
} i2c_opt_state;
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_RD 0x0001 /* read data, from slave to master */
/* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
/* Telechips */
#define I2C_M_NO_STOP 0x0020
#define I2C_M_MODE 0x0040
#define I2C_M_WR_RD 0x0080
#define I2C_M_WM899x_CODEC 0x0100
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
struct i2c_rdwr_ioctl_data {
struct i2c_msg __user *msgs; /* pointers to i2c_msgs */
__u32 nmsgs; /* number of i2c_msgs */
};
class I2CCtr
{
public:
I2CCtr(void);
virtual ~I2CCtr(void);
int Open(const char *dev);
int Write(const unsigned short slaveAddr, const unsigned short subAddr,
const unsigned char *buf, const unsigned short len);
int Read(const unsigned short slaveAddr, const unsigned short regAddr,
unsigned char *buf, const unsigned short len);
private:
int WriteInternal(const unsigned short slaveAddr, const unsigned short subAddr,
const unsigned char *buf, const unsigned short len);
int ReadInternal(const unsigned short slaveAddr, const unsigned short regAddr,
unsigned char *buf, const unsigned short len);
int fd;
struct i2c_rdwr_ioctl_data iic_data;
};
}
#endif // XXX
i2c_msg、i2c_rdwr_ioctl_data在不同平台上可以会不一样。替换kernel里的定义即可。
I2CCtr.cpp
#define LOG_TAG "I2C"
#include <log/log.h>
//#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "I2CCtr.h"
I2CCtr::I2CCtr(void)
: fd(-1)
{
}
I2CCtr::~I2CCtr(void)
{
if (fd > 0) {
close(fd);
free(iic_data.msgs);
}
}
int I2CCtr::Open(const char *dev)
{
if (fd == -1) {
fd = open(dev, O_RDWR);
if (fd < 0) {
ALOGE("%s open Error[%s].", dev, strerror(errno));
} else {
ALOGI("I2C Open Success!!");
iic_data.nmsgs = 2;
iic_data.msgs = (struct i2c_msg*) malloc(iic_data.nmsgs*sizeof(struct i2c_msg));
if (!iic_data.msgs) {
ALOGE("Memory alloc error");
close(fd);
fd = -1;
} else {
ioctl(fd, I2C_TIMEOUT, 2);
ioctl(fd, I2C_RETRIES, 1);
}
}
}
return fd;
}
int I2CCtr::Read(const unsigned short slaveAddr, const unsigned short regAddr,
unsigned char *dataBuf, const unsigned short len)
{
if (fd < 0) {
ALOGW("I2C is not open.");
return I2C_READ_NG;
}
return ReadInternal(slaveAddr, regAddr, dataBuf, len);
}
int I2CCtr::ReadInternal(const unsigned short slaveAddr, const unsigned short regAddr,
unsigned char *dataBuf, const unsigned short len)
{
unsigned char ucRegAddr = regAddr;
iic_data.nmsgs = 2;
(iic_data.msgs[0]).addr = slaveAddr;
(iic_data.msgs[0]).len = 1;
(iic_data.msgs[0]).buf = &ucRegAddr;
(iic_data.msgs[1]).addr = slaveAddr;
(iic_data.msgs[1]).flags = I2C_M_RD;
(iic_data.msgs[1]).len = len;
(iic_data.msgs[1]).buf = dataBuf;
if (ioctl(fd, I2C_RDWR, (unsigned long)&iic_data) < 0) {
ALOGE("Error[%s]: Failed to read from slave:0x%x reg:0x%x", strerror(errno), slaveAddr, regAddr);
return I2C_READ_NG;
}
return I2C_READ_OK;
}
int I2CCtr::Write(const unsigned short slaveAddr, const unsigned short regAddr,
const unsigned char *buf, const unsigned short len)
{
if (fd < 0) {
ALOGW("I2C is not open.");
return I2C_WRITE_NG;
}
return WriteInternal(slaveAddr, regAddr, buf, len);
}
int I2CCtr::WriteInternal(const unsigned short slaveAddr, const unsigned short regAddr,
const unsigned char *buf, const unsigned short len)
{
unsigned char data[len + 1];
iic_data.nmsgs = 1;
(iic_data.msgs[0]).len = 1 + len;
(iic_data.msgs[0]).addr = slaveAddr;
(iic_data.msgs[0]).flags = 0; //write
(iic_data.msgs[0]).buf = data;
data[0] = regAddr;
memcpy(data + 1, buf, len);
int ret = ioctl(fd, I2C_RDWR, (unsigned long)&iic_data);
if( ret < 0 ) {
ALOGE("Error[%s]: Failed to write into slave:0x%x reg:0x%x", strerror(errno), slaveAddr, regAddr);
return I2C_WRITE_NG;
}
return I2C_WRITE_OK;
}
三、使用示例
I2C::I2CCtr mI2CCtr = new I2C::I2CCtr();
mI2CCtr->Open("/dev/i2c-7");
const unsigned short len = 1;
unsigned char buf[len] = {0};
unsigned short regAddr = 0xFE;
buf[0] = value; // 写入value值
if (mI2CCtr->Write(SLAVEADDRESS_ADV7511W, regAddr, buf, len) == I2C::I2C_WRITE_NG) {
ALOGE("Error to write slave:0x%x reg:0x%x ", SLAVEADDRESS_ADV7511W, regAddr);
} else {
ALOGV("Write 0x%x into slave:0x%x reg:0x%x ", buf[0], SLAVEADDRESS_ADV7511W, regAddr);
}
if (mI2CCtr->Read(SLAVEADDRESS_ADV7511W, regAddr, buf, len) == I2C::I2C_READ_OK) {
ALOGV("Read 0x%x from slave:0x%x reg:0x%x ", buf[0], SLAVEADDRESS_ADV7511W, regAddr);
} else {
ALOGE("Error to read slave:0x%x reg:0x%x ", SLAVEADDRESS_ADV7511W, regAddr);
}
注意:操作i2c结节时,可能会出现selinux avc:denied错误。修改te文件添加权限即可。
四、总结
Linux上,一切皆为文件。操作i2c也如此。