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包....

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章