i2c--test
硬件
http://www.i2c-bus.org/how-i2c-hardware-works/
http://www.xinqi.cn/pdf/cun/at24cxx.pdf
http://download.csdn.net/detail/songqqnew/4438714
http://download.csdn.net/detail/songqqnew/4438746
测试
***********************************************************************************************使用ioctl( ,I2C_SMBUS, )操作iic
板子的文件系统里已经有一个/usr/bin/i2c的程序,用于测试i2c,比如
i2c -r
i2c -w
源码如下
2.打开设备文件/dev/i2c/0,读取数据
read_from_eeprom(&e, 0, 256);----->eeprom_read_byte
write_to_eeprom(&e, 0);--------->eeprom_write_byte
而读和写最终都是通过ioctl(file,I2C_SMBUS,&args);方法实现的。如下
***********************************************************************************************使用ioctl( ,I2C_RDWR, )操作iic
如下的测试代码很直观,借来贴上了。
refer to http://blog.csdn.net/cjok376240497/article/details/6982883
分析:
根据24cxx手册,
写字节操作如下
![](http://my.csdn.net/uploads/201207/20/1342766718_6830.png)
即步骤为
start---设备地址(7位)+写标志0(1位)---要写入的内存地址----要写入的数据
注意:设备地址是0x50=101 0000,这个地址的确定见下图,由于a0a1a2接地了,所以设备地址是0x50(只算前7位,不算最后一位的r/w位,所以一条总线最多可以辨别127个设备)。在驱动中会根据读写要求自动将地址左移一位然后加上读写标志位组成一个字节发送到sda引脚。
i2c-s3c2410.c
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << 1;
...}
![](http://my.csdn.net/uploads/201207/20/1342794918_6182.jpg)
从指定地址读操作如下:
![](http://my.csdn.net/uploads/201207/20/1342793374_2519.jpg)
即步骤为:
start---设备地址(7位)+写标志0(1位)---要读的内存地址---start---设备地址(7位)+读标志1(1位)---读出的数据
注意:这个操作有允许多个start信号,所以程序中使用了两个msg---每个msg都会发送一个start信号
从当前地址读操作如下:
![](http://my.csdn.net/uploads/201207/20/1342794191_2972.jpg)
即步骤为:
start---设备地址(7位)+读标志1(1位)---读出的数据
注意:此处可以读一个字节,也可以读多个字节,只要arm在读到第一个字节之后接着去读IICDS寄存器,24cx就会满足arm把下一个地址的数据也发出
************************************************************************************************使用read,write操作iic
如果使用read write操作i2c比如下
************************************************************************************************
总之,如下
![](http://my.csdn.net/uploads/201207/21/1342857823_3366.png)
使用write,read之前,需要使用ioctl(fd,I2C_SLAVE,addr)设置地址,然后直接write或read真正的数据即可
使用ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data); 时,不必事先使用ioctl设置从机地址,因为e2prom_data结构中包括地址了,如下
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
************************************************************************************************消息第一个字节的发送
master_xfer最终调用i2c-s3c2410.c的s3c24xx_i2c_message_start发送消息,具体发送的仅是消息的地址如下
根据6410手册,不同操作模式下的读写顺序为
主机发送
![](http://my.csdn.net/uploads/201207/21/1342852245_4769.png)
主机接收
![](http://my.csdn.net/uploads/201207/21/1342853087_6361.png)
下面的从机发送或接收暂时没用到,但也贴出来吧
从机发送
![](http://my.csdn.net/uploads/201207/21/1342853877_9290.png)
从机接收
![](http://my.csdn.net/uploads/201207/21/1342853894_6454.png)
其中IICSTAT寄存器
![](http://my.csdn.net/uploads/201207/21/1342852501_5221.png)
************************************************************************************************消息其他字节的发送或接收
函数s3c24xx_i2c_message_start是作为iic主机读或写时,用于发送消息的第一个字节(即要寻址的从机地址)-----那么消息的其他字节什么时候发送呢??在读消息时,就是其他字节什么时候读呢---在中断函数里面去读或继续发送。
附i2c-dev.c源码
http://www.i2c-bus.org/how-i2c-hardware-works/
http://www.xinqi.cn/pdf/cun/at24cxx.pdf
http://download.csdn.net/detail/songqqnew/4438714
http://download.csdn.net/detail/songqqnew/4438746
测试
***********************************************************************************************使用ioctl( ,I2C_SMBUS, )操作iic
板子的文件系统里已经有一个/usr/bin/i2c的程序,用于测试i2c,比如
i2c -r
i2c -w
源码如下
//24cxx.h #ifndef _24CXX_H_ #define _24CXX_H_ #include <linux/i2c-dev.h> #include <linux/i2c.h> #define EEPROM_TYPE_UNKNOWN 0 #define EEPROM_TYPE_8BIT_ADDR 1 #define EEPROM_TYPE_16BIT_ADDR 2 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*); int eeprom_close(struct eeprom *e); int eeprom_read_byte(struct eeprom* e, __u16 mem_addr); int eeprom_read_current_byte(struct eeprom *e); int eeprom_write_byte(struct eeprom *e, __u16 mem_addr, __u8 data); #endif
//eeprog.c #include <stdio.h> #include <fcntl.h> #include <getopt.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include "24cXX.h" #define usage_if(a) do { do_usage_if( a , __LINE__); } while(0); void do_usage_if(int b, int line) { const static char *eeprog_usage = "I2C-24C08(256 bytes) Read/Write Program, ONLY FOR TEST!\n" "FriendlyARM Computer Tech. 2009\n"; if(!b) return; fprintf(stderr, "%s\n[line %d]\n", eeprog_usage, line); exit(1); } #define die_if(a, msg) do { do_die_if( a , msg, __LINE__); } while(0); void do_die_if(int b, char* msg, int line) { if(!b) return; fprintf(stderr, "Error at line %d: %s\n", line, msg); fprintf(stderr, " sysmsg: %s\n", strerror(errno)); exit(1); } static int read_from_eeprom(struct eeprom *e, int addr, int size) { int ch, i; for(i = 0; i < size; ++i, ++addr) { die_if((ch = eeprom_read_byte(e, addr)) < 0, "read error"); if( (i % 16) == 0 ) printf("\n %.4x| ", addr); else if( (i % 8) == 0 ) printf(" "); printf("%.2x ", ch); fflush(stdout); } fprintf(stderr, "\n\n"); return 0; } static int write_to_eeprom(struct eeprom *e, int addr) { int i; for(i=0, /*addr=0*/; i<256; i++, addr++) { if( (i % 16) == 0 ) printf("\n %.4x| ", addr); else if( (i % 8) == 0 ) printf(" "); printf("%.2x ", i); fflush(stdout); die_if(eeprom_write_byte(e, addr, i), "write error"); } fprintf(stderr, "\n\n"); return 0; } int main(int argc, char** argv) { struct eeprom e; int op; op = 0; usage_if(argc != 2 || argv[1][0] != '-' || argv[1][2] != '\0'); op = argv[1][1]; fprintf(stderr, "Open /dev/i2c/0 with 8bit mode\n"); die_if(eeprom_open("/dev/i2c/0", 0x50, EEPROM_TYPE_8BIT_ADDR, &e) < 0, "unable to open eeprom device file " "(check that the file exists and that it's readable)"); switch(op) { case 'r': fprintf(stderr, " Reading 256 bytes from 0x0\n"); read_from_eeprom(&e, 0, 256); break; case 'w': fprintf(stderr, " Writing 0x00-0xff into 24C08 \n"); write_to_eeprom(&e, 0); break; default: usage_if(1); exit(1); } eeprom_close(&e); return 0; }1.定义结构体变量struct eeprom e;
2.打开设备文件/dev/i2c/0,读取数据
read_from_eeprom(&e, 0, 256);----->eeprom_read_byte
write_to_eeprom(&e, 0);--------->eeprom_write_byte
而读和写最终都是通过ioctl(file,I2C_SMBUS,&args);方法实现的。如下
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_RDWR, )操作iic
如下的测试代码很直观,借来贴上了。
refer to http://blog.csdn.net/cjok376240497/article/details/6982883
#include <stdio.h> #include <linux/types.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ioctl.h> #include <errno.h> #include <assert.h> #include <string.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> int main() { int fd, ret; unsigned char rdwr_addr = 0x66; /* e2prom 读写地址 */ unsigned char device_addr = 0x50; /* e2prom 设备地址 */ unsigned char data = 0x7; /* 向e2prom写的数据 */ struct i2c_rdwr_ioctl_data e2prom_data; fd= open("/dev/i2c/0", O_RDWR); if(fd < 0) { perror("openerror"); exit(1); } e2prom_data.msgs= (struct i2c_msg *)malloc(e2prom_data.nmsgs * \ sizeof(struct i2c_msg)); if(e2prom_data.msgs == NULL) { perror("mallocerror"); exit(1); } ioctl(fd,I2C_TIMEOUT, 1); /* 设置超时 */ ioctl(fd,I2C_RETRIES, 2); /* 设置重试次数 */ //向指定地址写入1字节数据 /*向e2prom的rdwr_addr地址写入数据data*/ e2prom_data.nmsgs= 1; //http://blog.csdn.net/hongtao_liu/article/details/4964244 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= (unsigned char *)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, (unsigned long)&e2prom_data); if(ret < 0) { perror("writedata error"); exit(1); } //printf("writedata: %d to address: %#x\n", data, rdwr_addr); data= 0; /* be zero*/ //从指定地址读 #if 1 /*从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; #endif //从当前地址读 #if 0 /*从e2prom的当前地址读取数据存入buf*/ e2prom_data.nmsgs= 1; e2prom_data.msgs[0].len= 5; e2prom_data.msgs[0].addr= device_addr; e2prom_data.msgs[0].flags= 1; /* read */ e2prom_data.msgs[0].buf=(unsigned char *)malloc(5); #endif ret= ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data); if(ret < 0) { perror("readerror"); exit(1); } //printf("read data: %d from address: %#x\n", e2prom_data.msgs[0].buf[0],rdwr_addr); free(e2prom_data.msgs); close(fd); return 0; }从指定地址读,驱动执行如下动作
[root@FORLINX6410]# DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 338): DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 339): rdwr_arg.nmsgs=1 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 343): rdwr_pa[0].addr=0x50//设备地址 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 344): rdwr_pa[0].len=2 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 347): rdwr_pa[0].buf[0]=0x66//要写的内存地址 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 347): rdwr_pa[0].buf[1]=0x7//要写的数据 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 338): DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 339): rdwr_arg.nmsgs=2 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 343): rdwr_pa[0].addr=0x50//设备地址 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 344): rdwr_pa[0].len=1 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 347): rdwr_pa[0].buf[0]=0x66//要读的内存地址 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 343): rdwr_pa[1].addr=0x50//设备地址 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 344): rdwr_pa[1].len=1 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 347): rdwr_pa[1].buf[0]=0x7//读到的数据从当前地址读,驱动执行如下动作
[root@FORLINX6410]# DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 338): DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 339): rdwr_arg.nmsgs=1 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 343): rdwr_pa[0].addr=0x50 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 344): rdwr_pa[0].len=2 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 347): rdwr_pa[0].buf[0]=0x66 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 347): rdwr_pa[0].buf[1]=0x7 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 338): DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 339): rdwr_arg.nmsgs=1 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 343): rdwr_pa[0].addr=0x50//设备地址 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 344): rdwr_pa[0].len=5//要读的数据个数 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 347): rdwr_pa[0].buf[0]=0x67//读到的数据,以下均是 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 347): rdwr_pa[0].buf[1]=0x68 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 347): rdwr_pa[0].buf[2]=0x69 DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 347): rdwr_pa[0].buf[3]=0x6a DBG(drivers/i2c/i2c-dev.c, i2cdev_ioctl_rdrw(), 347): rdwr_pa[0].buf[4]=0x6b
分析:
根据24cxx手册,
写字节操作如下
![](http://my.csdn.net/uploads/201207/20/1342766718_6830.png)
即步骤为
start---设备地址(7位)+写标志0(1位)---要写入的内存地址----要写入的数据
注意:设备地址是0x50=101 0000,这个地址的确定见下图,由于a0a1a2接地了,所以设备地址是0x50(只算前7位,不算最后一位的r/w位,所以一条总线最多可以辨别127个设备)。在驱动中会根据读写要求自动将地址左移一位然后加上读写标志位组成一个字节发送到sda引脚。
i2c-s3c2410.c
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << 1;
...}
![](http://my.csdn.net/uploads/201207/20/1342794918_6182.jpg)
从指定地址读操作如下:
![](http://my.csdn.net/uploads/201207/20/1342793374_2519.jpg)
即步骤为:
start---设备地址(7位)+写标志0(1位)---要读的内存地址---start---设备地址(7位)+读标志1(1位)---读出的数据
注意:这个操作有允许多个start信号,所以程序中使用了两个msg---每个msg都会发送一个start信号
从当前地址读操作如下:
![](http://my.csdn.net/uploads/201207/20/1342794191_2972.jpg)
即步骤为:
start---设备地址(7位)+读标志1(1位)---读出的数据
注意:此处可以读一个字节,也可以读多个字节,只要arm在读到第一个字节之后接着去读IICDS寄存器,24cx就会满足arm把下一个地址的数据也发出
************************************************************************************************使用read,write操作iic
如果使用read write操作i2c比如下
ioctl(fd,I2C_SLAVE,0x33);//设置从机地址 write(fd,buf,num1);//向iic从机写入数据 memset(buf, 0, sizeof(buf)); read(fd,buf,num2);//从iic从机接收数据则上面的驱动会打印类似如下
DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 203): 发送 1 条写msg,写入的数据如下: DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 208): msg.flags=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 209): msg.addr=0x33 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 210): msg.len=7 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[0]=0x1 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[1]=0x38 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[2]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[3]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[4]=0xc7 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[5]=0xff DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[6]=0x17 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 165): 发送 1 条读msg,读到的数据如下: DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 170): msg.flags=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 171): msg.addr=0x33 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 172): msg.len=15 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[0]=0x1 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[1]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[2]=0x8 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[3]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[4]=0x69 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[5]=0x80 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[6]=0x2 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[7]=0x1e DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[8]=0x3 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[9]=0x15 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[10]=0x2 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[11]=0x1 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[12]=0xd3 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[13]=0xfe DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[14]=0x17 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 203): 发送 1 条写msg,写入的数据如下: DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 208): msg.flags=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 209): msg.addr=0x33 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 210): msg.len=8 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[0]=0x1 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[1]=0x32 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[2]=0x1 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[3]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[4]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[5]=0xcc DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[6]=0xff DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[7]=0x17 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 165): 发送 1 条读msg,读到的数据如下: DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 170): msg.flags=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 171): msg.addr=0x33 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 172): msg.len=11 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[0]=0x1 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[1]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[2]=0x4 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[3]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[4]=0x26 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[5]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[6]=0xff DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[7]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[8]=0xd6 DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[9]=0xfe DBG(drivers/i2c/i2c-dev.c, i2cdev_read(), 175): msg.buf[10]=0x17 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 203): 发送 1 条写msg,写入的数据如下: DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 208): msg.flags=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 209): msg.addr=0x33 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 210): msg.len=60 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[0]=0x1 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[1]=0x37 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[2]=0x35 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[3]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[4]=0x2 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[5]=0x66 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[6]=0x70 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[7]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[8]=0x8f DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[9]=0x82 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[10]=0x8e DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[11]=0x83 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[12]=0xe0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[13]=0x54 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[14]=0x1 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[15]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[16]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[17]=0x0 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[18]=0x70 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[19]=0xf8 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[20]=0x22 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[21]=0x78 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[22]=0xff DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[23]=0xe4 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[24]=0xf6 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[25]=0xd8 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[26]=0xfd DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[27]=0x2 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[28]=0x67 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[29]=0x1 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[30]=0x41 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[31]=0x13 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[32]=0x9 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[33]=0xff DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[34]=0x63 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[35]=0x9c DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[36]=0xe DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[37]=0xf6 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[38]=0x75 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[39]=0x30 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[40]=0x47 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[41]=0x35 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[42]=0x39 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[43]=0xa5 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[44]=0x32 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[45]=0x1 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[46]=0x2c DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[47]=0xe3 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[48]=0x29 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[49]=0x21 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[50]=0x26 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[51]=0x38 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[52]=0x23 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[53]=0xdf DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[54]=0x21 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[55]=0xed DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[56]=0x20 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[57]=0xd2 DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[58]=0xea DBG(drivers/i2c/i2c-dev.c, i2cdev_write(), 213): msg.buf[59]=0x17很明显,read和write每次只能发送一个msg,对应一个start信号。完全可以用ioctl( ,I2C_RDWR,1)代替。
************************************************************************************************
总之,如下
![](http://my.csdn.net/uploads/201207/21/1342857823_3366.png)
使用write,read之前,需要使用ioctl(fd,I2C_SLAVE,addr)设置地址,然后直接write或read真正的数据即可
使用ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data); 时,不必事先使用ioctl设置从机地址,因为e2prom_data结构中包括地址了,如下
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
************************************************************************************************消息第一个字节的发送
master_xfer最终调用i2c-s3c2410.c的s3c24xx_i2c_message_start发送消息,具体发送的仅是消息的地址如下
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg) { unsigned int addr = (msg->addr & 0x7f) << 1; unsigned long stat; unsigned long iiccon; stat = 0; stat |= S3C2410_IICSTAT_TXRXEN; if (msg->flags & I2C_M_RD) { stat |= S3C2410_IICSTAT_MASTER_RX; addr |= 1; } else stat |= S3C2410_IICSTAT_MASTER_TX; if (msg->flags & I2C_M_REV_DIR_ADDR) addr ^= 1; /* todo - check for wether ack wanted or not */ s3c24xx_i2c_enable_ack(i2c);//使能ack iiccon = readl(i2c->regs + S3C2410_IICCON); writel(stat, i2c->regs + S3C2410_IICSTAT); dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); writeb(addr, i2c->regs + S3C2410_IICDS);//写从机地址到IICDS移位寄存器,包含一个读写标志位----第一个发送的字节总是地址 /* delay here to ensure the data byte has gotten onto the bus * before the transaction is started */ ndelay(i2c->tx_setup); dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon); writel(iiccon, i2c->regs + S3C2410_IICCON); stat |= S3C2410_IICSTAT_START; writel(stat, i2c->regs + S3C2410_IICSTAT);//开始发送 #if 0 int i; printk("msg->addr=0x%x\n",msg->addr); printk("msg->flags=0x%x\n",msg->flags); printk("msg->len=%d\n",msg->len); for(i=0;i<msg->len;i++) { printk("msg->buf[%d]=0x%x\n",i,msg->buf[i]); } printk("--\n"); #endif }
根据6410手册,不同操作模式下的读写顺序为
主机发送
![](http://my.csdn.net/uploads/201207/21/1342852245_4769.png)
主机接收
![](http://my.csdn.net/uploads/201207/21/1342853087_6361.png)
下面的从机发送或接收暂时没用到,但也贴出来吧
从机发送
![](http://my.csdn.net/uploads/201207/21/1342853877_9290.png)
从机接收
![](http://my.csdn.net/uploads/201207/21/1342853894_6454.png)
其中IICSTAT寄存器
![](http://my.csdn.net/uploads/201207/21/1342852501_5221.png)
************************************************************************************************消息其他字节的发送或接收
函数s3c24xx_i2c_message_start是作为iic主机读或写时,用于发送消息的第一个字节(即要寻址的从机地址)-----那么消息的其他字节什么时候发送呢??在读消息时,就是其他字节什么时候读呢---在中断函数里面去读或继续发送。
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) { struct s3c24xx_i2c *i2c = dev_id; unsigned long status; unsigned long tmp; status = readl(i2c->regs + S3C2410_IICSTAT); if (status & S3C2410_IICSTAT_ARBITR) { /* deal with arbitration loss */ dev_err(i2c->dev, "deal with arbitration loss\n"); } if (i2c->state == STATE_IDLE) { dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n"); tmp = readl(i2c->regs + S3C2410_IICCON); tmp &= ~S3C2410_IICCON_IRQPEND; writel(tmp, i2c->regs + S3C2410_IICCON); goto out; } /* pretty much this leaves us with the fact that we've * transmitted or received whatever byte we last sent */ i2s_s3c_irq_nextbyte(i2c, status);//见下面 out: return IRQ_HANDLED; }
/* i2s_s3c_irq_nextbyte * * process an interrupt and work out what to do */ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) { unsigned long tmp; unsigned char byte; int ret = 0; switch (i2c->state) { case STATE_IDLE: dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__); goto out; break; case STATE_STOP: dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__); s3c24xx_i2c_disable_irq(i2c); goto out_ack; case STATE_START: /* last thing we did was send a start condition on the * bus, or started a new i2c message */ if (iicstat & S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags & I2C_M_IGNORE_NAK)) { /* ack was not received... */ dev_dbg(i2c->dev, "ack was not received\n"); s3c24xx_i2c_stop(i2c, -ENXIO); goto out_ack; } if (i2c->msg->flags & I2C_M_RD) i2c->state = STATE_READ; else i2c->state = STATE_WRITE; /* terminate the transfer if there is nothing to do * as this is used by the i2c probe to find devices. */ if (is_lastmsg(i2c) && i2c->msg->len == 0) { s3c24xx_i2c_stop(i2c, 0); goto out_ack; } if (i2c->state == STATE_READ) goto prepare_read; /* fall through to the write state, as we will need to * send a byte as well */ case STATE_WRITE://如果此消息是写标志 /* we are writing data to the device... check for the * end of the message, and if so, work out what to do */ if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { if (iicstat & S3C2410_IICSTAT_LASTBIT) { dev_dbg(i2c->dev, "WRITE: No Ack\n"); s3c24xx_i2c_stop(i2c, -ECONNREFUSED); goto out_ack; } } retry_write: if (!is_msgend(i2c)) {//如果不是此消息的buf没有发完 byte = i2c->msg->buf[i2c->msg_ptr++]; writeb(byte, i2c->regs + S3C2410_IICDS);//写消息的其他字节依次写入IICDS寄存器 /* delay after writing the byte to allow the * data setup time on the bus, as writing the * data to the register causes the first bit * to appear on SDA, and SCL will change as * soon as the interrupt is acknowledged */ ndelay(i2c->tx_setup); } else if (!is_lastmsg(i2c)) {//如果是消息的最后一个字节 /* we need to go to the next i2c message */ dev_dbg(i2c->dev, "WRITE: Next Message\n"); i2c->msg_ptr = 0; i2c->msg_idx++; i2c->msg++; /* check to see if we need to do another message */ if (i2c->msg->flags & I2C_M_NOSTART) { if (i2c->msg->flags & I2C_M_RD) { /* cannot do this, the controller * forces us to send a new START * when we change direction */ s3c24xx_i2c_stop(i2c, -EINVAL); } goto retry_write; } else { /* send the new start */ s3c24xx_i2c_message_start(i2c, i2c->msg); i2c->state = STATE_START; } } else { /* send stop */ s3c24xx_i2c_stop(i2c, 0); } break; case STATE_READ://如果此消息为读标志 /* we have a byte of data in the data register, do * something with it, and then work out wether we are * going to do any more read/write */ byte = readb(i2c->regs + S3C2410_IICDS);//从IICDS移位寄存器中读出数据 i2c->msg->buf[i2c->msg_ptr++] = byte;//数据依次写入消息的buf prepare_read: if (is_msglast(i2c)) { /* last byte of buffer */ if (is_lastmsg(i2c)) s3c24xx_i2c_disable_ack(i2c); } else if (is_msgend(i2c)) { /* ok, we've read the entire buffer, see if there * is anything else we need to do */ if (is_lastmsg(i2c)) { /* last message, send stop and complete */ dev_dbg(i2c->dev, "READ: Send Stop\n"); s3c24xx_i2c_stop(i2c, 0); } else { /* go to the next transfer */ dev_dbg(i2c->dev, "READ: Next Transfer\n"); i2c->msg_ptr = 0; i2c->msg_idx++; i2c->msg++; } } break; } /* acknowlegde the IRQ and get back on with the work */ out_ack: tmp = readl(i2c->regs + S3C2410_IICCON); tmp &= ~S3C2410_IICCON_IRQPEND; writel(tmp, i2c->regs + S3C2410_IICCON); out: return ret; }************************************************************************************************
附i2c-dev.c源码
/* i2c-dev.c - i2c-bus driver, char device interface Copyright (C) 1995-97 Simon G. Vogl Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl> Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. But I have used so much of his original code and ideas that it seems only fair to recognize him as co-author -- Frodo */ /* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk@telos.de> */ #define DEBUG #ifdef DEBUG #define DBG(...) printk(" DBG(%s, %s(), %d): ", __FILE__, __FUNCTION__, __LINE__); printk(__VA_ARGS__) #else #define DBG(...) #endif #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/list.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> #include <linux/jiffies.h> #include <linux/uaccess.h> static struct i2c_driver i2cdev_driver; /* * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a * slave (i2c_client) with which messages will be exchanged. It's coupled * with a character special file which is accessed by user mode drivers. * * The list of i2c_dev structures is parallel to the i2c_adapter lists * maintained by the driver model, and is updated using notifications * delivered to the i2cdev_driver. */ struct i2c_dev { struct list_head list; struct i2c_adapter *adap; struct device *dev; }; #define I2C_MINORS 256 static LIST_HEAD(i2c_dev_list); static DEFINE_SPINLOCK(i2c_dev_list_lock); static struct i2c_dev *i2c_dev_get_by_minor(unsigned index) { struct i2c_dev *i2c_dev; spin_lock(&i2c_dev_list_lock); list_for_each_entry(i2c_dev, &i2c_dev_list, list) { if (i2c_dev->adap->nr == index) goto found; } i2c_dev = NULL; found: spin_unlock(&i2c_dev_list_lock); return i2c_dev; } static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap) { struct i2c_dev *i2c_dev; if (adap->nr >= I2C_MINORS) { printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n", adap->nr); return ERR_PTR(-ENODEV); } i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); if (!i2c_dev) return ERR_PTR(-ENOMEM); i2c_dev->adap = adap; spin_lock(&i2c_dev_list_lock); list_add_tail(&i2c_dev->list, &i2c_dev_list); spin_unlock(&i2c_dev_list_lock); return i2c_dev; } static void return_i2c_dev(struct i2c_dev *i2c_dev) { spin_lock(&i2c_dev_list_lock); list_del(&i2c_dev->list); spin_unlock(&i2c_dev_list_lock); kfree(i2c_dev); } static ssize_t show_adapter_name(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt)); if (!i2c_dev) return -ENODEV; return sprintf(buf, "%s\n", i2c_dev->adap->name); } static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL); /* ------------------------------------------------------------------------- */ /* * After opening an instance of this character special file, a file * descriptor starts out associated only with an i2c_adapter (and bus). * * Using the I2C_RDWR ioctl(), you can then *immediately* issue i2c_msg * traffic to any devices on the bus used by that adapter. That's because * the i2c_msg vectors embed all the addressing information they need, and * are submitted directly to an i2c_adapter. However, SMBus-only adapters * don't support that interface. * * To use read()/write() system calls on that file descriptor, or to use * SMBus interfaces (and work with SMBus-only hosts!), you must first issue * an I2C_SLAVE (or I2C_SLAVE_FORCE) ioctl. That configures an anonymous * (never registered) i2c_client so it holds the addressing information * needed by those system calls and by this SMBus interface. */ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { char *tmp; int ret; struct i2c_client *client = file->private_data; if (count > 8192) count = 8192; tmp = kmalloc(count, GFP_KERNEL); if (tmp == NULL) return -ENOMEM; pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n", iminor(file->f_path.dentry->d_inode), count); ret = i2c_master_recv(client, tmp, count); if (ret >= 0) { #if 1 DBG("发送 1 条读msg,读到的数据如下:\n"); struct i2c_msg msg; msg.addr = client->addr; msg.len = count; msg.buf = tmp; DBG("msg.flags=0x%x\n",msg.flags); DBG("msg.addr=0x%x\n",msg.addr); DBG("msg.len=%d\n",msg.len); int i; for(i=0;i<msg.len;i++){ DBG("msg.buf[%d]=0x%x\n",i,msg.buf[i]); } #endif ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret; } kfree(tmp); return ret; } static ssize_t i2cdev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) { int ret; char *tmp; struct i2c_client *client = file->private_data; if (count > 8192) count = 8192; tmp = memdup_user(buf, count); if (IS_ERR(tmp)) return PTR_ERR(tmp); pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n", iminor(file->f_path.dentry->d_inode), count); #if 1 DBG("发送 1 条写msg,写入的数据如下:\n"); struct i2c_msg msg; msg.addr = client->addr; msg.len = count; msg.buf = (char *)buf; DBG("msg.flags=0x%x\n",msg.flags); DBG("msg.addr=0x%x\n",msg.addr); DBG("msg.len=%d\n",msg.len); int i; for(i=0;i<msg.len;i++){ DBG("msg.buf[%d]=0x%x\n",i,msg.buf[i]); } #endif ret = i2c_master_send(client, tmp, count); kfree(tmp); return ret; } static int i2cdev_check(struct device *dev, void *addrp) { struct i2c_client *client = i2c_verify_client(dev); if (!client || client->addr != *(unsigned int *)addrp) return 0; return dev->driver ? -EBUSY : 0; } /* walk up mux tree */ static int i2cdev_check_mux_parents(struct i2c_adapter *adapter, int addr) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); int result; result = device_for_each_child(&adapter->dev, &addr, i2cdev_check); if (!result && parent) result = i2cdev_check_mux_parents(parent, addr); return result; } /* recurse down mux tree */ static int i2cdev_check_mux_children(struct device *dev, void *addrp) { int result; if (dev->type == &i2c_adapter_type) result = device_for_each_child(dev, addrp, i2cdev_check_mux_children); else result = i2cdev_check(dev, addrp); return result; } /* This address checking function differs from the one in i2c-core in that it considers an address with a registered device, but no driver bound to it, as NOT busy. */ static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); int result = 0; if (parent) result = i2cdev_check_mux_parents(parent, addr); if (!result) result = device_for_each_child(&adapter->dev, &addr, i2cdev_check_mux_children); return result; } static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client, unsigned long arg) { struct i2c_rdwr_ioctl_data rdwr_arg; struct i2c_msg *rdwr_pa; u8 __user **data_ptrs; int i, res; if (copy_from_user(&rdwr_arg, (struct i2c_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(struct i2c_msg), GFP_KERNEL); if (!rdwr_pa) return -ENOMEM; if (copy_from_user(rdwr_pa, rdwr_arg.msgs, rdwr_arg.nmsgs * sizeof(struct i2c_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 < rdwr_arg.nmsgs; 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 < 0) { int j; for (j = 0; j < i; ++j) kfree(rdwr_pa[j].buf); kfree(data_ptrs); kfree(rdwr_pa); return res; } res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs); #if 1 DBG("\n"); DBG("rdwr_arg.nmsgs=%d\n",rdwr_arg.nmsgs); int k,ii; for(ii=0;ii<i;ii++) { DBG("rdwr_pa[%d].addr=0x%x\n",ii,rdwr_pa[ii].addr); DBG("rdwr_pa[%d].len=%d\n",ii,rdwr_pa[ii].len); for(k=0;k<rdwr_pa[ii].len;k++) { DBG("rdwr_pa[%d].buf[%d]=0x%x\n",ii,k,rdwr_pa[ii].buf[k]); } } #endif 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); return res; } static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, unsigned long arg) { struct i2c_smbus_ioctl_data data_arg; union i2c_smbus_data temp; int datasize, res; if (copy_from_user(&data_arg, (struct i2c_smbus_ioctl_data __user *) arg, sizeof(struct i2c_smbus_ioctl_data))) return -EFAULT; if ((data_arg.size != I2C_SMBUS_BYTE) && (data_arg.size != I2C_SMBUS_QUICK) && (data_arg.size != I2C_SMBUS_BYTE_DATA) && (data_arg.size != I2C_SMBUS_WORD_DATA) && (data_arg.size != I2C_SMBUS_PROC_CALL) && (data_arg.size != I2C_SMBUS_BLOCK_DATA) && (data_arg.size != I2C_SMBUS_I2C_BLOCK_BROKEN) && (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) && (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) { dev_dbg(&client->adapter->dev, "size out of range (%x) in ioctl I2C_SMBUS.\n", data_arg.size); return -EINVAL; } /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, so the check is valid if size==I2C_SMBUS_QUICK too. */ if ((data_arg.read_write != I2C_SMBUS_READ) && (data_arg.read_write != I2C_SMBUS_WRITE)) { dev_dbg(&client->adapter->dev, "read_write out of range (%x) in ioctl I2C_SMBUS.\n", data_arg.read_write); return -EINVAL; } /* Note that command values are always valid! */ if ((data_arg.size == I2C_SMBUS_QUICK) || ((data_arg.size == I2C_SMBUS_BYTE) && (data_arg.read_write == I2C_SMBUS_WRITE))) /* These are special: we do not use data */ return i2c_smbus_xfer(client->adapter, client->addr, client->flags, data_arg.read_write, data_arg.command, data_arg.size, NULL); if (data_arg.data == NULL) { dev_dbg(&client->adapter->dev, "data is NULL pointer in ioctl I2C_SMBUS.\n"); return -EINVAL; } if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || (data_arg.size == I2C_SMBUS_BYTE)) datasize = sizeof(data_arg.data->byte); else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || (data_arg.size == I2C_SMBUS_PROC_CALL)) datasize = sizeof(data_arg.data->word); else /* size == smbus block, i2c block, or block proc. call */ datasize = sizeof(data_arg.data->block); if ((data_arg.size == I2C_SMBUS_PROC_CALL) || (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || (data_arg.size == I2C_SMBUS_I2C_BLOCK_DATA) || (data_arg.read_write == I2C_SMBUS_WRITE)) { if (copy_from_user(&temp, data_arg.data, datasize)) return -EFAULT; } if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) { /* Convert old I2C block commands to the new convention. This preserves binary compatibility. */ data_arg.size = I2C_SMBUS_I2C_BLOCK_DATA; if (data_arg.read_write == I2C_SMBUS_READ) temp.block[0] = I2C_SMBUS_BLOCK_MAX; } //DBG("\n"); res = i2c_smbus_xfer(client->adapter, client->addr, client->flags, data_arg.read_write, data_arg.command, data_arg.size, &temp); if (!res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || (data_arg.read_write == I2C_SMBUS_READ))) { if (copy_to_user(data_arg.data, &temp, datasize)) return -EFAULT; } //DBG("\n"); return res; } static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct i2c_client *client = file->private_data; unsigned long funcs; dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n", cmd, arg); switch (cmd) { case I2C_SLAVE: case I2C_SLAVE_FORCE: /* NOTE: devices set up to work with "new style" drivers * can't use I2C_SLAVE, even when the device node is not * bound to a driver. Only I2C_SLAVE_FORCE will work. * * Setting the PEC flag here won't affect kernel drivers, * which will be using the i2c_client node registered with * the driver model core. Likewise, when that client has * the PEC flag already set, the i2c-dev driver won't see * (or use) this setting. */ if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) return -EINVAL; if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg)) return -EBUSY; /* REVISIT: address could become busy later */ client->addr = arg; return 0; case I2C_TENBIT: if (arg) client->flags |= I2C_M_TEN; else client->flags &= ~I2C_M_TEN; return 0; case I2C_PEC: if (arg) client->flags |= I2C_CLIENT_PEC; else client->flags &= ~I2C_CLIENT_PEC; return 0; case I2C_FUNCS: funcs = i2c_get_functionality(client->adapter); return put_user(funcs, (unsigned long __user *)arg); case I2C_RDWR: return i2cdev_ioctl_rdrw(client, arg); case I2C_SMBUS: return i2cdev_ioctl_smbus(client, arg); case I2C_RETRIES: client->adapter->retries = arg; break; case I2C_TIMEOUT: /* For historical reasons, user-space sets the timeout * value in units of 10 ms. */ client->adapter->timeout = msecs_to_jiffies(arg * 10); break; default: /* NOTE: returning a fault code here could cause trouble * in buggy userspace code. Some old kernel bugs returned * zero in this case, and userspace code might accidentally * have depended on that bug. */ return -ENOTTY; } return 0; } static int i2cdev_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); struct i2c_client *client; struct i2c_adapter *adap; struct i2c_dev *i2c_dev; i2c_dev = i2c_dev_get_by_minor(minor); if (!i2c_dev) return -ENODEV; adap = i2c_get_adapter(i2c_dev->adap->nr); if (!adap) return -ENODEV; /* This creates an anonymous i2c_client, which may later be * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. * * This client is ** NEVER REGISTERED ** with the driver model * or I2C core code!! It just holds private copies of addressing * information and maybe a PEC flag. */ client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) { i2c_put_adapter(adap); return -ENOMEM; } snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); client->driver = &i2cdev_driver; client->adapter = adap; file->private_data = client; return 0; } static int i2cdev_release(struct inode *inode, struct file *file) { struct i2c_client *client = file->private_data; i2c_put_adapter(client->adapter); kfree(client); file->private_data = NULL; return 0; } static const struct file_operations i2cdev_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = i2cdev_read, .write = i2cdev_write, .unlocked_ioctl = i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release, }; /* ------------------------------------------------------------------------- */ /* * The legacy "i2cdev_driver" is used primarily to get notifications when * I2C adapters are added or removed, so that each one gets an i2c_dev * and is thus made available to userspace driver code. */ static struct class *i2c_dev_class; static int i2cdev_attach_adapter(struct i2c_adapter *adap) { struct i2c_dev *i2c_dev; int res; i2c_dev = get_free_i2c_dev(adap); if (IS_ERR(i2c_dev)) return PTR_ERR(i2c_dev); /* register this i2c device with the driver core */ i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, MKDEV(I2C_MAJOR, adap->nr), NULL, "i2c-%d", adap->nr); if (IS_ERR(i2c_dev->dev)) { res = PTR_ERR(i2c_dev->dev); goto error; } res = device_create_file(i2c_dev->dev, &dev_attr_name); if (res) goto error_destroy; pr_debug("i2c-dev: adapter [%s] registered as minor %d\n", adap->name, adap->nr); return 0; error_destroy: device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); error: return_i2c_dev(i2c_dev); return res; } static int i2cdev_detach_adapter(struct i2c_adapter *adap) { struct i2c_dev *i2c_dev; i2c_dev = i2c_dev_get_by_minor(adap->nr); if (!i2c_dev) /* attach_adapter must have failed */ return 0; device_remove_file(i2c_dev->dev, &dev_attr_name); return_i2c_dev(i2c_dev); device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name); return 0; } static struct i2c_driver i2cdev_driver = { .driver = { .name = "dev_driver", }, .attach_adapter = i2cdev_attach_adapter, .detach_adapter = i2cdev_detach_adapter, }; /* ------------------------------------------------------------------------- */ /* * module load/unload record keeping */ static int __init i2c_dev_init(void) { int res; printk(KERN_INFO "i2c /dev entries driver\n"); res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops); if (res) goto out; i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); if (IS_ERR(i2c_dev_class)) { res = PTR_ERR(i2c_dev_class); goto out_unreg_chrdev; } res = i2c_add_driver(&i2cdev_driver); if (res) goto out_unreg_class; return 0; out_unreg_class: class_destroy(i2c_dev_class); out_unreg_chrdev: unregister_chrdev(I2C_MAJOR, "i2c"); out: printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__); return res; } static void __exit i2c_dev_exit(void) { i2c_del_driver(&i2cdev_driver); class_destroy(i2c_dev_class); unregister_chrdev(I2C_MAJOR, "i2c"); } MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " "Simon G. Vogl <simon@tk.uni-linz.ac.at>"); MODULE_DESCRIPTION("I2C /dev entries driver"); MODULE_LICENSE("GPL"); module_init