对于注册的i2c适配器,用户空间也可以使用它们。在Linux内核代码文件/include/linux/i2c-dev.c中实现了I2C适配器设备文件的功能,针对每个适配器生成一个主设备号为89的设备节点(次设备号为0-255),I2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read(),write(),和ioctl()等文件操作接口,在用户空间的应用层就可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。
i2c适配器的设备节点是/dev/i2c-x,其中x是数字,代表适配器的编号。由于适配器编号是动态分配的(和注册次序有关),所以想了解哪一个适配器对应什么编号,可以查看/sys/class/i2c-dev/目录下的文件内容。如:[root@zlg /]# cat /sys/class/i2c-dev/i2c-0/name
PNX4008-I2C0
[root@zlg /]# cat /sys/class/i2c-dev/i2c-1/name
PNX4008-I2C1
[root@zlg /]# cat /sys/class/i2c-dev/i2c-2/name
USB-I2C
int fd;
if ((fd = open("/dev/i2c-1",O_RDWR))< 0) {
/* 错误处理 */
exit(1);
}
说明:
打开适配器对应的设备节点,i2c-dev为打开的线程建立一个i2c_client,但是这个i2c_client并不加到i2c_adapter的client链表当中。当用户关闭设备节点时,它自动被释放。
2.ioctl()
查看include/linux/i2c-dev.h文件,可以看到i2c-dev支持的IOCTL命令。如i2c-dev IOCTL命令
#define I2C_RETRIES 0x0701 /*设置收不到ACK时的重试次数*/
#define I2C_TIMEOUT 0x0702 /* 设置超时时限的jiffies */
#define I2C_SLAVE 0x0703 /*设置从机地址 */
#define I2C_SLAVE_FORCE 0x0706 /* 强制设置从机地址 */
#define I2C_TENBIT 0x0704 /*选择地址位长:=0 for 7bit , != 0 for 10 bit */
#define I2C_FUNCS 0x0705 /*获取适配器支持的功能 */
#define I2C_RDWR 0x0707 /*Combined R/W transfer (one STOP only) */
#define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */
#define I2C_SMBUS 0x0720 /*SMBus transfer */
如:
a. 设置重试次数
ioctl(fd, I2C_RETRIES,m);
这句话设置适配器收不到ACK时重试的次数为m。默认的重试次数为1。
b. 设置超时
ioctl(fd, I2C_TIMEOUT,m);
设置SMBus的超时时间为m,单位为jiffies。
c. 设置从机地址
ioctl(fd, I2C_SLAVE,addr);
ioctl(fd, #defineI2C_SLAVE_FORCE, addr);
在调用read()和write()函数之前必须设置从机地址。这两行都可以设置从机的地址,区别是第二行无论内核中是否已有驱动在使用这个地址都会成功,第一行则只在该地址空闲的情况下成功。由于i2c-dev创建的i2c_client不加入i2c_adapter的client列表,所以不能防止其它线程使用同一地址,也不能防止驱动模块占用同一地址。
d. 设置地址模式
ioctl(file,I2C_TENBIT,select)
如果select不等于0选择10比特地址模式,如果等于0选择7比特模式,默认7比特。只有适配器支持I2C_FUNC_10BIT_ADDR,这个请求才是有效的。e. 获取适配器功能ioctl(file,I2C_FUNCS,(unsignedlong *)funcs)
i2c发送或者接收一次数据都以数据包 struct i2c_msg 封装
struct i2c_msg {
__u16 addr; // 从机地址
__u16 flags; // 标志
#define I2C_M_TEN 0x0010 // 十位地址标志
#define I2C_M_RD 0x0001 // 接收数据标志
__u16 len; // 数据长度
__u8 *buf; // 数据指针
};
---------注意 i2c_master_send 和 i2c_master_recv 都是对 i2c_transfer的封装。
------具体调用时的差异.------
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
return buf;
}
------------------------------------
4. i2c-dev使用例程
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
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 < 0) {
perror("openerror");
exit(1);
}
e2prom_data.msgs= (struct i2c_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= (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*/
/*从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, (unsigned long)&e2prom_data);
if(ret < 0) {
perror("readerror");
exit(1);
}
printf("read data: %d from address: %#x\n", data,rdwr_addr);
free(e2prom_data.msgs);
close(fd);
return0;
}
画了一个流程图框图 大致概括了 Linux i2c 子系统驱动框架。通常只需要实现i2c设备驱动层。i2c控制器驱动层大部分情况内核已经提供。
(1)板级信息注册,系统会根据该信息生成对应的i2c_client结构。
static struct i2c_board_info i2c_devices[] __initdata = {
{I2C_BOARD_INFO("24c02", 0x50), },
{}
};
i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));
(2) 设备驱动实现
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEBUG 1
#ifdef DEBUG
#define dbg(x...) printk(x)
#else
#define dbg(x...) (void)(0)
#endif
#define I2C_MAJOR 89
#define DEVICE_NAME "at24c02"
static struct class *my_dev_class;
static struct i2c_client *my_client;
static struct i2c_driver my_i2c_driver;
static struct i2c_device_id my_ids[] = {
{"24c01",0x50},
{"24c02",0x50},
{"24c08",0x50},
{}
};
MODULE_DEVICE_TABLE(i2c,my_ids);
static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int res;
struct device *dev;
dbg("probe:name = %s,flag =%d,addr = %d,adapter = %d,driver = %s\n",client->name,
client->flags,client->addr,client->adapter->nr,client->driver->driver.name );
dev = device_create(my_dev_class, &client->dev,
MKDEV(I2C_MAJOR, 0), NULL,
DEVICE_NAME);
if (IS_ERR(dev))
{
dbg("device create error\n");
goto out;
}
my_client = client;
return 0;
out:
return -1;
}
static int my_i2c_remove(struct i2c_client *client)
{
dbg("remove\n");
return 0;
}
static ssize_t at24c02_read(struct file *fd, char *buf, ssize_t count, loff_t *offset)
{
char *tmp;
int ret;
char data_byte;
char reg_addr = 0,i;
struct i2c_client *client = (struct i2c_client*) fd->private_data;
struct i2c_msg msgs[2];
dbg("read:count = %d,offset = %ld\n",count,*offset);
tmp = kmalloc(count,GFP_KERNEL);
if (!tmp)
{
dbg("malloc error in read function\n");
goto out;
}
reg_addr = *offset;
msgs[0].addr = client->addr;
msgs[0].flags = client->flags & (I2C_M_TEN|I2C_CLIENT_PEC) ;
msgs[0].len = 1;
msgs[0].buf = (char *)®_addr;
msgs[1].addr= client->addr;
msgs[1].flags = client->flags & (I2C_M_TEN|I2C_CLIENT_PEC);
msgs[1].flags |= I2C_M_RD;
msgs[1].len = count;
msgs[1].buf = (char*)tmp;
ret = i2c_transfer(client->adapter,&msgs,2);
if (ret != 2)
goto out;
if (copy_to_user(buf, tmp, count))
goto out;
kfree(tmp);
return count;
out:
kfree(tmp);
return -1;
}
static int at24c02_ioctl(struct file *fd, unsigned int cmd, unsigned long arg)
{
dbg("ioctl code ...\n");
return 0;
}
static ssize_t at24c02_write(struct file *fd, char *buf, ssize_t count, loff_t *offset)
{
int ret,i;
char *tmp;
int errflg;
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client*) fd->private_data;
char tmp_data[2];
dbg("write:count = %d,offset = %ld\n",count,*offset);
tmp = kmalloc(count, GFP_KERNEL);
if (!tmp)
goto out;
if (copy_from_user(tmp, buf, count))
goto out;
msg.addr = client->addr;
msg.flags = client->flags & (I2C_M_TEN | I2C_CLIENT_PEC);
for (i = 0; i < count; i++) {
msg.len = 2;
tmp_data[0] = *offset + i;
tmp_data[1] = tmp[i];
msg.buf = tmp_data;
ret = i2c_transfer(client->adapter,&msg,1);
if (ret != 1)
goto out;
msleep(1);
}
kfree(tmp);
return ((ret == 1) ? count:ret);
out:
kfree(tmp);
return -1;
}
static int at24c02_open(struct inode *inode, struct file *fd)
{
fd->private_data =(void*)my_client;
return 0;
}
static int at24c02_release(struct inode *inode, struct file *fd)
{
dbg("release\n");
fd->private_data = NULL;
return 0;
}
static const struct file_operations i2c_fops = {
.owner = THIS_MODULE,
.open = at24c02_open,
.read = at24c02_read,
.write = at24c02_write,
.unlocked_ioctl = at24c02_ioctl,
.release = at24c02_release,
};
static struct i2c_driver my_i2c_driver = {
.driver = {
.name = "i2c_demo",
.owner = THIS_MODULE,
},
.probe = my_i2c_probe,
.remove = my_i2c_remove,
.id_table = my_ids,
};
static int __init my_i2c_init(void)
{
int res;
res = register_chrdev(I2C_MAJOR,DEVICE_NAME,&i2c_fops);
if (res)
{
dbg("register_chrdev error\n");
return -1;
}
my_dev_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(my_dev_class))
{
dbg("create class error\n");
unregister_chrdev(I2C_MAJOR, DEVICE_NAME);
return -1;
}
return i2c_add_driver(&my_i2c_driver);
}
static void __exit my_i2c_exit(void)
{
unregister_chrdev(I2C_MAJOR, DEVICE_NAME);
class_destroy(my_dev_class);
i2c_del_driver(&my_i2c_driver);
}
MODULE_AUTHOR("itspy");
MODULE_DESCRIPTION("i2c client driver demo");
MODULE_LICENSE("GPL");
module_init(my_i2c_init);
module_exit(my_i2c_exit);
http://blog.csdn.net/bingqingsuimeng/article/details/7937898
http://blog.csdn.net/onetwothreef/article/details/50275237
http://blog.chinaunix.net/uid-27041925-id-3671725.html 等等。