在进入正题之前,我们先对前面的内容做个小结吧:
系统启动的时候,会先调用s3c2410_i2c中的平台代码初始化I2C硬件设备,同时这个文件也实现了I2C通讯的物理操作,最关键的是,这个文件中注册了一个适配器。
然后进入i2c-dev.c文件,注册I2C总线,也就是/dev/i2c-0设备。同时在这个文件中实现了字符型驱动所支持的操作函数:read,write,ioctl等操作。
最后是具体的I2C设备,他们挂载在i2c-dev上,所以这些设备不需要再实现字符型驱动的操作函数了,只需要实现探测,卸载等操作函数。
--------------------------------------------------------------------------------
有了前面对I2C驱动文件的文件,我们基本已经清楚I2C操作函数的实现方法了,在此基本上,我们修改了I2C部分最后的测试程序,成功的读出了OFN的ID号。
这个程序是基于“友善之臂”的example修改的。这里为了保证代码的简洁,我删掉了辅助测试的代码
#define I2C_DEV "/dev/i2c-0"
int main(void){
struct eeprom e;
res = eeprom_open(I2C_DEV, CHIP_ADDR, EEPROM_TYPE_8BIT_ADDR, &e);
if(res < 0){
return 0;
}
value = eeprom_read_byte(&e, REG_AV02_PRODUCT_ID);
eeprom_close(&e);
return 0;
}
struct eeprom
{
char *dev; // device file i.e. /dev/i2c-N
int addr; // i2c address
int fd; // file descriptor
int type; // eeprom type
};
int eeprom_open(char *dev_fqn, int addr, int type, struct eeprom* e)
{
int funcs, fd, r;
e->fd = e->addr = 0;
e->dev = 0;
fd = open(dev_fqn, O_RDWR);
if(fd <= 0)
{
return -1;
}
//获取适配器支持的操作列表
if((r = ioctl(fd, I2C_FUNCS, &funcs) < 0))
{
return -1;
}
// 检查这些操作是否支持,如果不支持会print出错误信息
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_BYTE );
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_BYTE );
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_BYTE_DATA );
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_BYTE_DATA );
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_WORD_DATA );
CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_WORD_DATA );
// set working device
// if( ( r = ioctl(fd, I2C_SLAVE, addr)) < 0) //这个参数是用在I2C设备没有注册(insmod)的时候
if( ( r = ioctl(fd, I2C_SLAVE_FORCE, addr)) < 0) //这个参数是用在I2C设备已经注册了的时候(跳过地址的检查),实现的代码可以看i2c-dev.c中对IOCTL的分析
{
return -1;
}
e->fd = fd;
e->addr = addr;
e->dev = dev_fqn;
e->type = type;
return 0;
}
这个是CHECK-I2C-FUNC的实现代码
#define CHECK_I2C_FUNC( var, label ) \
do { if(0 == (var & label)) { \
fprintf(stderr, "\nError: " \
#label " function is required. Program halted.\n\n"); \
exit(1); } \
} while(0);
读操作代码如下:
int eeprom_read_byte(struct eeprom* e, __u16 mem_addr)
{
int r;
ioctl(e->fd, BLKFLSBUF); // clear kernel read buffer
if(e->type == EEPROM_TYPE_8BIT_ADDR)
{
__u8 buf = mem_addr & 0x0ff;
r = i2c_write_1b(e, buf); //这里很关键,IIC发出要读的地址
}
if (r < 0)
return r;
r = i2c_smbus_read_byte(e->fd); //读数据
return r;
}
继续看i2c_write_1b的实现:
static int i2c_write_1b(struct eeprom *e, __u8 buf)
{
int r;
// we must simulate a plain I2C byte write with SMBus functions
r = i2c_smbus_write_byte(e->fd, buf);
if(r < 0)
fprintf(stderr, "Error i2c_write_1b: %s\n", strerror(errno));
usleep(10);
return r;
}
然后是i2c_smbus_write_byte/i2c_smbus_read_byte
static inline __s32 i2c_smbus_read_byte(int file)
{
union i2c_smbus_data data;
if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data))
return -1;
else
return 0x0FF & data.byte;
}
static inline __s32 i2c_smbus_write_byte(int file, __u8 value)
{
return i2c_smbus_access(file,I2C_SMBUS_WRITE,value,
I2C_SMBUS_BYTE,NULL);
}
可见最后调用的是i2c_smbus_access
static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command,
int size, union i2c_smbus_data *data)
{
struct i2c_smbus_ioctl_data args;
args.read_write = read_write;
args.command = command;
args.size = size;
args.data = data;
return ioctl(file,I2C_SMBUS,&args); //最后的实现代码是IOCTL的I2C_SMBUS操作,这个函数的实现看i2c-dev.c的相关部分
}
另外,打开设备成功之后,也可以直接用read/write函数来读写设备,不过需要注意的是,read函数不会重新发开始位,也就是说,I2C通讯协议中,他只会执行后半段的操作,而前半段,写入I2C地址的操作,他执行不了。除非调用者自己设置好复杂的msg包....