linux iopen i2c dev,i2c-dev - Linux驱动子系统之I2C_Linux编程_Linux公社-Linux系统门户网站...
[概述]
之前在介紹I2C子系統時,提到過使用i2c-dev.c文件在應用程序中實現我們的I2C從設備驅動。不過,它實現的是一個虛擬,臨時的i2c_client,隨著設備文件的打開而產生,并隨著設備文件的關閉而撤銷。I2c-dev.c針對每個I2C適配器生成一個主設備號為89的設備文件,實現了i2c_driver的成員函數以及文件操作接口,所以i2c-dev.c的主題是”i2c_driver成員函數+字符設備驅動”。
[i2c-dev.c源碼分析]
I2c-dev初始化函數主要做了注冊名為”i2c”的字符設備文件和”i2c-dev”的類。
I2c-dev.c中實現的i2cdev_read和i2cdev_write函數不具有太強的通用性,只適合下面這種單開始信號情況:
而不適合多開始信號的情況:
所以我們經常會使用i2cdev_ioctl函數的I2C_RDWR,在分析i2cdev_ioctl函數之前,我們需要了解一個結構體:
/*?This?is?the?structure?as?used?in?theI2C_RDWR?ioctl?call?*/
structi2c_rdwr_ioctl_data?{
structi2c_msg?__user?*msgs;/*?pointersto?i2c_msgs?*/
__u32nmsgs;/*?number?ofi2c_msgs?*/
};
Msgs?????表示單個開始信號傳遞的數據;
Nmsgs???? 表示有多少個msgs,比如上圖,單開始信號時,nmsgs等于1;多開始信號時,nmsgs等于2
structi2c_msg?{
__u16addr;/*?slave?address?????????????????????????*/
__u16flags;/*?默認為寫入?*/
#define?I2C_M_TEN??????????????????0x0010?????/*this?is?a?ten?bit?chip?address?*/
#define?I2C_M_RD???????????0x0001?????/*?readdata,?from?slave?to?master?*/
#define?I2C_M_NOSTART??????????????????0x4000?????/*?if?I2C_FUNC_PROTOCOL_MANGLING?*/
#define?I2C_M_REV_DIR_ADDR?????0x2000?????/*if?I2C_FUNC_PROTOCOL_MANGLING?*/
#define?I2C_M_IGNORE_NAK??????????0x1000?????/*if?I2C_FUNC_PROTOCOL_MANGLING?*/
#define?I2C_M_NO_RD_ACK???????????0x0800?????/*?if?I2C_FUNC_PROTOCOL_MANGLING?*/
#define?I2C_M_RECV_LEN???????????????0x0400?????/*?length?will?be?first?received?byte?*/
__u16len;/*?msg?length??????????????????????????????*/
__u8*buf;/*?pointer?to?msgdata???????????????????????*/
};
使用i2cdev_ioctl函數的I2C_RDWR指令會調用到i2cdev_ioctl_rdrw函數:
staticnoinlineinti2cdev_ioctl_rdrw(structi2c_client?*client,
unsignedlong?arg)
{
structi2c_rdwr_ioctl_data?rdwr_arg;
structi2c_msg?*rdwr_pa;
u8__user?**data_ptrs;
inti,?res;
if(copy_from_user(&rdwr_arg,
(structi2c_rdwr_ioctl_data?__user?*)arg,
sizeof(rdwr_arg)))
return-EFAULT;
/*Put?an?arbitrary?limit?on?the?number?of?messages?that?can
*?be?sent?at?once?*/
if(rdwr_arg.nmsgs?>?I2C_RDRW_IOCTL_MAX_MSGS)
return-EINVAL;
rdwr_pa=?kmalloc(rdwr_arg.nmsgs?*sizeof(structi2c_msg),?GFP_KERNEL);
if(!rdwr_pa)
return-ENOMEM;
if(copy_from_user(rdwr_pa,?rdwr_arg.msgs,
rdwr_arg.nmsgs?*sizeof(structi2c_msg)))?{
kfree(rdwr_pa);
return-EFAULT;
}
data_ptrs=?kmalloc(rdwr_arg.nmsgs?*sizeof(u8?__user?*),?GFP_KERNEL);
if(data_ptrs?==?NULL)?{
kfree(rdwr_pa);
return-ENOMEM;
}
res=?0;
for(i?=?0;?i?
/*Limit?the?size?of?the?message?to?a?sane?amount;
*?and?don't?let?length?change?either.?*/
if((rdwr_pa[i].len?>?8192)?||
(rdwr_pa[i].flags?&?I2C_M_RECV_LEN))?{
res=?-EINVAL;
break;
}
data_ptrs[i]=?(u8?__user?*)rdwr_pa[i].buf;
rdwr_pa[i].buf=?memdup_user(data_ptrs[i],?rdwr_pa[i].len);
if(IS_ERR(rdwr_pa[i].buf))?{
res=?PTR_ERR(rdwr_pa[i].buf);
break;
}
}
if(res?
intj;
for(j?=?0;?j?
kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa);
returnres;
}
res=?i2c_transfer(client->adapter,?rdwr_pa,?rdwr_arg.nmsgs);
while(i--?>?0)?{
if(res?>=?0?&&?(rdwr_pa[i].flags?&?I2C_M_RD))?{
if(copy_to_user(data_ptrs[i],?rdwr_pa[i].buf,
rdwr_pa[i].len))
res=?-EFAULT;
}
kfree(rdwr_pa[i].buf);
}
kfree(data_ptrs);
kfree(rdwr_pa);
returnres;
}
咋一看,還挺復雜,其實主要做了一件事情:把用戶空間傳遞過來的i2c_rdwr_ioctl_data數據進行錯誤檢查,www.linuxidc.com?然后調用i2c_transfer函數與適配器進行通信,如果是接收數據,代碼會將訪問到的數據傳回i2c_rdwr_ioctl_data的buf中。I2c_transfer最終會調用到I2C適配器具體實現的master_xfer函數來與硬件進行通信。
[eeprom實例]
預備知識
使用的mini2440開發板,eeprom的地址為0x50,實驗完成一個數據的讀寫,先看下讀寫時序
AT24C08任意地址字節寫的時序:
AT24C08任意地址字節寫的時序:
下面的代碼可以按照上面的兩個圖來閱讀:
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
intmain()
{
intfd,?ret;
unsignedchar?rdwr_addr?=?0x42;/*?e2prom?讀寫地址?*/
unsignedchar?device_addr?=?0x50;/*?e2prom?設備地址?*/
unsignedchar?data?=?0x12;/*?向e2prom寫的數據?*/
structi2c_rdwr_ioctl_data?e2prom_data;
fd=?open("/dev/i2c/0",?O_RDWR);
if(fd?
perror("openerror");
exit(1);
}
e2prom_data.msgs=?(structi2c_msg?*)malloc(e2prom_data.nmsgs?*?\
sizeof(structi2c_msg));
if(e2prom_data.msgs?==?NULL)?{
perror("mallocerror");
exit(1);
}
ioctl(fd,I2C_TIMEOUT,?1);/*?設置超時?*/
ioctl(fd,I2C_RETRIES,?2);/*?設置重試次數?*/
/*向e2prom的rdwr_addr地址寫入數據data*/
e2prom_data.nmsgs=?1;
e2prom_data.msgs[0].len=?2;
e2prom_data.msgs[0].addr=?device_addr;
e2prom_data.msgs[0].flags=?0;/*?write?*/
e2prom_data.msgs[0].buf=?(unsignedchar*)malloc(2);
e2prom_data.msgs[0].buf[0]=?rdwr_addr;/*?write?address?*/
e2prom_data.msgs[0].buf[1]=?data;/*?write?data?*/
ret=?ioctl(fd,?I2C_RDWR,?(unsignedlong)&e2prom_data);
if(ret?
perror("writedata?error");
exit(1);
}
printf("writedata:?%d?to?address:?%#x\n",?data,?rdwr_addr);
data=?0;/*?be?zero*/
/*從e2prom的rdwr_addr地址讀取數據存入buf*/
e2prom_data.nmsgs=?2;
e2prom_data.msgs[0].len=?1;
e2prom_data.msgs[0].addr=?device_addr;
//??????e2prom_data.msgs[0].flags=?0;?????/*?write?*/
e2prom_data.msgs[0].buf=?&rdwr_addr;
e2prom_data.msgs[1].len=?1;
e2prom_data.msgs[1].addr=?device_addr;
e2prom_data.msgs[1].flags=?1;/*?read?*/
e2prom_data.msgs[1].buf=?&data;
ret=?ioctl(fd,?I2C_RDWR,?(unsignedlong)&e2prom_data);
if(ret?
perror("readerror");
exit(1);
}
printf("read??data:?%d?from?address:?%#x\n",?data,rdwr_addr);
free(e2prom_data.msgs);
close(fd);
return0;
}
在mini2440開發板上已經實驗成功。
總結
以上是生活随笔為你收集整理的linux iopen i2c dev,i2c-dev - Linux驱动子系统之I2C_Linux编程_Linux公社-Linux系统门户网站...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win7怎么屏蔽迅雷看看视频广告
- 下一篇: linux树莓派网易云音乐,基于树莓派的