在進入正題之前,我們先對前面的內容做個小結吧:
系統啓動的時候,會先調用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包....