OFN鼠标驱动(七) -- I2C部分最后的测试程序

在进入正题之前,我们先对前面的内容做个小结吧:

系统启动的时候,会先调用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包....

 

 

你可能感兴趣的:(Linux驱动)