1. Linux内核支持I2C通用设备驱动(用户态驱动:由应用层实现对硬件的控制可以称之为用户态驱动),实现文件位于drivers/i2c/i2c-dev.c,设备文件为/dev/i2c-0
2. I2C通用设备驱动以字符设备注册进内核的
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,
};
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
3. 对设备文件进行读写时,可以调用read、write或者ioctl等方法,他们都是通过调用函数i2c_transfer来实现对I2C设备的操作的
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
int ret;
/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that should be reported as
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
if (in_atomic() || irqs_disabled()) {
ret = mutex_trylock(&adap->bus_lock);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
mutex_lock_nested(&adap->bus_lock, adap->level);
}
ret = adap->algo->master_xfer(adap,msgs,num);
mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
4. i2c_transfer通过代码可以看出,i2c_transfer 通过调用相应的 adapter 的 master_xfer 方法实现的,而 master_xfer 主要是根据 struct i2c_msg 类型的msgs来进行处理的。
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, 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 */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
5. I2C用户态驱动简单示例
(1)通过read、write实现对I2C设备的操作
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <malloc.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#define SLAVE_ADDRESS 0x50
#define I2C_WRITE_FLAG 0x00
#define I2C_READ_FLAG 0x01
#define USAGE "xxx -r address length\nxxx -w address data1 data2...\n"
void i2c_write_bytes(int fd, unsigned char address, unsigned char* data, unsigned short len)
{
unsigned char* write_data = malloc(len + 1);
write_data[0] = address;
memcpy(&write_data[1], data, len);
ioctl(fd, I2C_SLAVE, SLAVE_ADDRESS);
ioctl(fd, I2C_TIMEOUT, 1);
ioctl(fd, I2C_RETRIES, 1);
write(fd, write_data, len + 1);
printf("Write data success\n");
if(write_data != NULL)
{
free(write_data);
write_data = NULL;
}
}
void i2c_read_bytes(int fd, unsigned char address, unsigned char* buf, unsigned short len)
{
ioctl(fd, I2C_SLAVE, SLAVE_ADDRESS);
ioctl(fd, I2C_TIMEOUT, 1);
ioctl(fd, I2C_RETRIES, 1);
write(fd, &address, 1);
read(fd, buf, len);
printf("buf[0] = 0x%x\n", buf[0]);
printf("Read data success\n");
}
int main(int argc, char* argv[])
{
int opt;
int fd = -1;
unsigned char address;
unsigned short len = 0, i = 0;
unsigned char buf[256] = {0};
// Open device file
fd = open("/dev/i2c-0", O_RDWR);
if(fd < 0)
{
printf("Open file error\n");
goto Exit;
}
while((opt = getopt(argc, argv, "w:r:")) != -1)
{
switch(opt)
{
case 'w':
printf("optarg = %s\n", optarg);
printf("optind = %d\n", optind);
printf("argc = %d\n", argc);
printf("argv[optind] = %s\n", argv[optind]);
address = (unsigned char)strtol(optarg, NULL, 0);
printf("address = %x\n", address);
for(len = 0; optind < argc; optind++, len++)
{
buf[len] = (unsigned char)strtol(argv[optind], NULL, 0);
}
printf("len = %x\n", len);
i2c_write_bytes(fd, address, buf, len);
break;
case 'r':
printf("optarg = %s\n", optarg);
printf("optind = %d\n", optind);
printf("argv[optind] = %s\n", argv[optind]);
address = (unsigned char)strtol(optarg, NULL, 0);
printf("address = 0x%x\n", address);
len = (unsigned short)strtol(argv[optind], NULL, 0);
printf("len = 0x%x\n", len);
i2c_read_bytes(fd, address, buf, len);
printf("Read content:\n");
for(i = 0; i < len; i++)
{
printf("0x%x ", buf[i]);
}
printf("\n");
break;
default:
printf("Invalid parameter\n");
printf(USAGE);
break;
}
}
Exit:
close(fd);
return 0;
}
(2)通过ioctl实现对I2C设备的操作
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <malloc.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#define SLAVE_ADDRESS 0x50
#define I2C_WRITE_FLAG 0x00
#define I2C_READ_FLAG 0x01
#define USAGE "xxx -r address length\nxxx -w address data1 data2...\n"
void i2c_write_bytes(int fd, unsigned char address, unsigned char* data, unsigned short len)
{
struct i2c_rdwr_ioctl_data e2prom_write_data;
e2prom_write_data.nmsgs = 1;
e2prom_write_data.msgs = malloc(sizeof(struct i2c_msg) * e2prom_write_data.nmsgs);
e2prom_write_data.msgs[0].addr = SLAVE_ADDRESS;
e2prom_write_data.msgs[0].flags = I2C_WRITE_FLAG;
e2prom_write_data.msgs[0].len = len + 1;//address data
e2prom_write_data.msgs[0].buf = malloc(e2prom_write_data.msgs[0].len);
e2prom_write_data.msgs[0].buf[0] = address;
memcpy(&(e2prom_write_data.msgs[0].buf[1]), data, (size_t)len);
// Using ioctl to write data
ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_write_data);
printf("Write data success\n");
if(e2prom_write_data.msgs != NULL)
{
free(e2prom_write_data.msgs);
e2prom_write_data.msgs = NULL;
}
}
void i2c_read_bytes(int fd, unsigned char address, unsigned char* buf, unsigned short len)
{
struct i2c_rdwr_ioctl_data e2prom_read_data;
e2prom_read_data.nmsgs = 2;//Need writing address first, then reading
e2prom_read_data.msgs = malloc(sizeof(struct i2c_msg) * e2prom_read_data.nmsgs);
e2prom_read_data.msgs[0].addr = SLAVE_ADDRESS;
e2prom_read_data.msgs[0].flags = I2C_WRITE_FLAG;
e2prom_read_data.msgs[0].len = 1;
e2prom_read_data.msgs[0].buf = malloc(e2prom_read_data.msgs[0].len);
e2prom_read_data.msgs[0].buf[0] = address;
e2prom_read_data.msgs[1].addr = SLAVE_ADDRESS;
e2prom_read_data.msgs[1].flags = I2C_READ_FLAG;
e2prom_read_data.msgs[1].len = len;
e2prom_read_data.msgs[1].buf = malloc(e2prom_read_data.msgs[0].len);
e2prom_read_data.msgs[1].buf[0] = 0x00;
// Using ioctl to read data
ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_read_data);
printf("e2prom_read_data.msgs[1].buf[0] = 0x%x\n", e2prom_read_data.msgs[1].buf[0]);
memcpy((void*)buf, (void*)(e2prom_read_data.msgs[1].buf), (unsigned int)len);
if(e2prom_read_data.msgs != NULL)
{
free(e2prom_read_data.msgs);
e2prom_read_data.msgs = NULL;
}
}
int main(int argc, char* argv[])
{
int opt;
int fd = -1;
unsigned char address;
unsigned short len = 0, i = 0;
unsigned char buf[256] = {0};
// Open device file
fd = open("/dev/i2c-0", O_RDWR);
if(fd < 0)
{
printf("Open file error\n");
goto Exit;
}
while((opt = getopt(argc, argv, "w:r:")) != -1)
{
switch(opt)
{
case 'w':
printf("optarg = %s\n", optarg);
printf("optind = %d\n", optind);
printf("argc = %d\n", argc);
printf("argv[optind] = %s\n", argv[optind]);
address = (unsigned char)strtol(optarg, NULL, 0);
printf("address = %x\n", address);
for(len = 0; optind < argc; optind++,len++)
{
buf[len] = (unsigned char)strtol(argv[optind], NULL, 0);
}
printf("len = %x\n", len);
i2c_write_bytes(fd, address, buf, len);
break;
case 'r':
printf("optarg = %s\n", optarg);
printf("optind = %d\n", optind);
printf("argv[optind] = %s\n", argv[optind]);
address = (unsigned char)strtol(optarg, NULL, 0);
printf("address = 0x%x\n", address);
len = (unsigned short)strtol(argv[optind], NULL, 0);
printf("len = 0x%x\n", len);
i2c_read_bytes(fd, address, buf, len);
printf("Read content:\n");
for(i = 0; i < len; i++)
{
printf("0x%x ", buf[i]);
}
printf("\n");
break;
default:
printf("Invalid parameter\n");
printf(USAGE);
break;
}
}
Exit:
close(fd);
return 0;
}