i2c driver

一. 简介

       I2C(Inter-Intergrated circuit)及子集smbus(System Mangement Bus)接口是嵌入式系统中比较常见的设备接口,这类设备主要有eeprom,hwmon,rtc等。I2C及SMBUS为两线接口,分别为SDA(串行数线),SCL(串行时钟);SDA是双向数据线,可以读写命令来控制SDA方向,I2C支持最高传输速率为100kbit/s,通常I2C设备为7位地址,但也支持10位地址;

        在linux系统中,I2C主要由主机适配器和设备驱动程序组成,具体结构图如下所示(按我自己的理解修改,如果错了,请指出,谢谢):


       如图所示,i2c_client  为 I2C总线上的I2C设备,用户可以通过两种方式访问I2C设备,一种是通过device driver向/sysfs注册设备,另一种是通过内核已有的i2c-dev模块实现/dev/i2c-x访问;

        I2C驱动主要指三个方面驱动:1. device driver,I2C设备驱动;2. adatper ,主机适配器驱动,主要实现master_xfer,smbus_xfer和functionality函数;3.通过i2c-dev访问设备时的用户空间驱动。

       本文接下来会对内核已经实现的I2C-dev模块源码进行分析,并列举例子说明如果通过I2C-DEV访问I2C设备。用户通过I2C-DEV模块访问i2c设备的结构示意如下图所示:


 二. I2C-dev模块分析

a.主要数据结构:

I2C总线上的I2C设备结构:

struct i2c_client {

        unsigned short flags;/* div., see below*/

        unsigned short addr;/* 7位设备地址;*/

        char name[I2C_NAME_SIZE];

        struct i2c_adapter *adapter;/*I2C主机控制器*/

        struct i2c_driver *driver;

        struct device dev;/* the device structure*/

        int irq;/* irq issued by device*/

        struct list_head detected;

};

/*flags for the client struct: */

#define I2C_CLIENT_PEC 0x04/* 数据包出错检测 */
#define I2C_CLIENT_TEN 0x10/* 使用10位设备地址 */
#define I2C_CLIENT_WAKE 0x80/* for board_info; true iff can wake */

I2C设备驱动结构:

struct i2c_driver {

        unsigned int class;

        /* 添加和卸载I2C设备,老版本*/

        int (*attach_adapter)(struct i2c_adapter *) __deprecated;

        int (*detach_adapter)(struct i2c_adapter *) __deprecated;

        /* 添加和卸载I2C设备,新版本*/

        int (*probe)(struct i2c_client *, const struct i2c_device_id *);

        int (*remove)(struct i2c_client *);

        /*休眠和唤醒  */

        void (*shutdown)(struct i2c_client *);

        int (*suspend)(struct i2c_client *, pm_message_t mesg);

        int (*resume)(struct i2c_client *);

        void (*alert)(struct i2c_client *, unsigned int data);

        int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

        struct device_driver driver;

        const struct i2c_device_id *id_table;

        /* Device detection callback for automatic device creation */

        int (*detect)(struct i2c_client *, struct i2c_board_info *);

        const unsigned short *address_list;

        struct list_head clients;

};

I2C主机控制器提供访问设备硬件接口:

struct i2c_algorithm {

        * 用于i2c模式下的收发函数接口*/

        int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);

        /*用于SMBUS模式下的收发函数接口*/

        int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,  unsigned short flags, char read_write,  u8 command, int size, union i2c_smbus_data *data);

        /*用于查询I2C主控制器所支持访问接口,如I2C_FUNC_SMBUS_BYTE,查看询是否支持smbus单字节读和写操作*/

        u32 (*functionality) (struct i2c_adapter *);

};

表示I2C主机控制器设备:

struct i2c_dev{

struct list_head list;

struct i2c_adapter *adap;

struct device *dev;

}

I2C主机控制器:

/*
 * i2c_adapter is the structure used to identify a physical i2c bus along
 * with the access algorithms necessary to access it.
 */
struct i2c_adapter {

        struct module *owner;

        unsigned int class; /* classes to allow probing for */

        const struct i2c_algorithm *algo; /* the algorithm to access the bus */

        void *algo_data;

        /* data fields that are valid for all devices*/

        struct rt_mutex bus_lock;

        int timeout;/* in jiffies */

        int retries;

        struct device dev;/* the adapter device */

        int nr;

        char name[48];

        struct completion dev_released;

        struct mutex userspace_clients_lock;

        struct list_head userspace_clients;

};

用于I2C模式下收发数据的信息结构:

struct i2c_msg {

        __u16 addr;/* I2C设备7位地址*/

        __u16 flags;     /*读写及其它标志*

        #define I2C_M_TEN0x0010/* 使用10地址 */

        #define I2C_M_RD0x0001/* 读 */

        #define I2C_M_NOSTART0x4000/* if I2C_FUNC_PROTOCOL_MANGLING */

        #define I2C_M_REV_DIR_ADDR0x2000/* if I2C_FUNC_PROTOCOL_MANGLING */

        #define I2C_M_IGNORE_NAK0x1000/* if I2C_FUNC_PROTOCOL_MANGLING */

        #define I2C_M_NO_RD_ACK0x0800/* if I2C_FUNC_PROTOCOL_MANGLING */

        #define I2C_M_RECV_LEN0x0400/* length will be first received byte */

        __u16 len;/* 要发送数据长度,以字节为单位*/

        __u8 *buf;/* 要发送数据*/

};

使用I2C_RDWR ioctl接口访问设备时使用数据结构:

struct i2c_rdwr_ioctl_data {

        strcut i2c_msg __user  *msgs;  /*要发送的i2c_msgs*/

        u32 nmsgs;/*要发送i2c_msg数量*/

}

使用SMBUS模式下收发数据结构:

union i2c_smbus_data {

        u8 byte;

        u16 word;

        u8 block[I2C_SMBUS_BLOCK_MXX+2];

}

使用i2c_smbus ioctl接口访问设备时使用数据结构:

strcut i2c_smbus_ioctl_data{

        u8 read_write;/*读还是写*/

        u8 command;

        u32 size;

        union i2c_smbus_data __user  *data;  /*要发送数据*/

}

b.源码分析:

1.I2C-dev模块注册及卸载函数:

static int __init i2c_dev_init(void)

{

        printk(KERN_INFO "i2c /dev entries driver\n");

        /*注册字符设备*/

        res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);

        if (res)

                goto out;

        /*创建i2c-dev类,在/sys/class/下生成i2c-dev目录*/

        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;

        }

        /* 增加通知链机制,当I2C总线上检测到有设备加入时,就会调用i2cdev_nofifier结构中定义通知函数 */

        res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);

        if (res)

                goto out_unreg_class;

        /*遍历I2C总线,创建相应设备节点 */

        i2c_for_each_dev(NULL, i2cdev_attach_adapter);

                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)

{

        bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier);

        i2c_for_each_dev(NULL, i2cdev_detach_adapter);

        class_destroy(i2c_dev_class);

        unregister_chrdev(I2C_MAJOR, "i2c");

}

module_init(i2c_dev_init);
module_exit(i2c_dev_exit);

        当系统调用device_register注册设备时,如果为i2c设备,I2C总线就能收到设备添加通知从而调用i2cdev_notifier_call函数:

int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
 void *data)
{

        struct device *dev = data;

        switch (action) {

        /*收到设备加入时, 就会调用i2cdev_attach_adapter生成设备节点*/

        case BUS_NOTIFY_ADD_DEVICE:

                return i2cdev_attach_adapter(dev, NULL);

        case BUS_NOTIFY_DEL_DEVICE:

                return i2cdev_detach_adapter(dev, NULL);

        }

         return 0;

}

static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{

        struct i2c_adapter *adap;

        struct i2c_dev *i2c_dev;

        int res;

        if (dev->type != &i2c_adapter_type)

                return 0;

        adap = to_i2c_adapter(dev);

        i2c_dev = get_free_i2c_dev(adap);

        if (IS_ERR(i2c_dev))

                return PTR_ERR(i2c_dev);

        /* 在/dev/目录下生成主设备号为89,次设备号为adap->nr的设备文件 */

        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;

        }

       /*在/sys/class/i2c-x/目录下生成name属性文件*/

        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;

}

      通过字符设备注册函数chrdev_register()注册i2c-dev到系统中,通过device_create分别在生成devfs和sysfs设备节点,并通过字符设备的设备节点号将字符设备与device_create()生成的设备节点联系在一起,也就是说对/dev/i2c-x设备进行file_operations操作时,系统通过VFS会自动调用chrdev_register()注册的file_operations操作。设备节点如何找到对应的file_operations可以参考http://blog.chinaunix.net/uid-20543672-id-3203690.html 中的《深入Linux设备驱动程序机制》学习心得---字符设备驱动原理图解,这位兄弟讲得相当的经典;

      当应用层通过open系统调用访问i2c 设备时,就会调用i2dcdev_fops中的i2cdev_open函数:

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);/*通过设备号得到I2C控制器设备*/

if (!i2c_dev)

return -ENODEV;

adap = i2c_get_adapter(i2c_dev->adap->nr);/*得到I2C控制器*/

if (!adap)

return -ENODEV;

client = kzalloc(sizeof(*client), GFP_KERNEL);  /*生成一个I2C设备client*/

if (!client) {

i2c_put_adapter(adap);

return -ENOMEM;

}

client->adapter = adap;

file->private_data = client;

return 0;

}

当使用完I2C设备后就会调用i2dcdev_fops中的i2cdev_release()函数;

当对I2C设备进行读和写时会,就会调用 相应的i2cdev_read()和i2cdev_write();

I2C还可以通过iocotl系统调用实现i2c重发次数,收发超时时间等参数;

static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{

struct i2c_client *client = file->private_data;

unsigned long funcs;


switch (cmd) {

case I2C_SLAVE:

case I2C_SLAVE_FORCE: /*设置I2C从设备设备地址,对于7位地址最多可能支持0~7f,10位地址可支持0~3ff*/

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:  /*查询adapter所支持的功能*/

funcs = i2c_get_functionality(client->adapter);

return put_user(funcs, (unsigned long __user *)arg);

case I2C_RDWR:  /*i2c方式收发*/

return i2cdev_ioctl_rdrw(client, arg);

case I2C_SMBUS: /*smbus方式收发*/

return i2cdev_ioctl_smbus(client, arg);

case I2C_RETRIES:  /*发送失败后重发次数*/

client->adapter->retries = arg;

break;

case I2C_TIMEOUT: /*设置发送的超时时间*/

client->adapter->timeout = msecs_to_jiffies(arg * 10);

break;

default:

return -ENOTTY;

}

return 0;

}

三.通过I2C-DEV模块访问设备例子

写程序: 

#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <linux/types.h>
#include <fcntl.h>
struct i2c_msg {
short addr;
short flags;
#define I2C_M_TEN  0X0010
#define I2C_M_RD  0X0001
#define I2C_M_NOSTART  0X4000
#define I2C_M_REV_DIR_ADDR  0X2000
#define I2C_M_IGNORE_NAK  0X1000
#define I2C_M_NO_RD_ACK  0X0800
#define I2C_M_RECV_LEN  0X0400
short len;
unsigned char  *buf;
};
struct i2c_rdwr_ioctl_data {
struct i2c_msg *msg;
long nmsgs;
};
#define I2C_RETRIES 0X701
#define I2C_RDWR 0x707
#define I2C_TIMEOUT 0x702
#define MAX_MSG_NUM 2
struct i2c_rdwr_ioctl_data  eeprom_data;
int main(void)
{
int fd = 0;
int i;
int ret = 0;
int address = 0;
fd = open("/dev/i2c-0",O_RDWR);
if (fd < 0 ){
printf("Can not open the i2c-0 device.\n");
exit(1);
}
eeprom_data.msg = malloc(sizeof(struct i2c_msg) );
if (eeprom_data.msg == NULL){
printf("Can not request the memory for msg struct.\n");
exit(1);
}
ioctl(fd, I2C_TIMEOUT, 5);
ioctl(fd, I2C_RETRIES, 2);
for(address= 0; address < 100; address++){

eeprom_data.nmsgs = 1;

eeprom_data.msg[0].addr = 0x51;
eeprom_data.msg[0].flags = I2C_M_IGNORE_NAK;
eeprom_data.msg[0].len = 3;
eeprom_data.msg[0].buf = malloc(3);
if (eeprom_data.msg[0].buf == NULL) {
printf("failed to request the write buffer.\n");
free(eeprom_data.msg);
exit(1);
}
eeprom_data.msg[0].buf[0] = address >> 8;
eeprom_data.msg[0].buf[1] = address & 0xff;
eeprom_data.msg[0].buf[3] = address;
 ret = ioctl(fd, I2C_RDWR, &eeprom_data);
if (ret <0){
printf("Fail to write data to eeprom, the error is %d.\n", ret);
free(eeprom_data.msg[0].buf);
free(eeprom_data.msg);
exit(1);
}
printf("Write %d to 0x%d%d\n", address, eeprom_data.msg[0].buf[0],eeprom_data.msg[0].buf[1]);
usleep(1000);

}

free(eeprom_data.msg[0].buf);
close(fd);
exit(0);
}

读程序:

#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <linux/types.h>
#include <fcntl.h>
struct i2c_msg {
short addr;
short flags;
#define I2C_M_TEN  0X0010
#define I2C_M_RD  0X0001
#define I2C_M_NOSTART  0X4000
#define I2C_M_REV_DIR_ADDR  0X2000
#define I2C_M_IGNORE_NAK  0X1000
#define I2C_M_NO_RD_ACK  0X0800
#define I2C_M_RECV_LEN  0X0400
short len;
unsigned char  *buf;
};
struct i2c_rdwr_ioctl_data {
struct i2c_msg *msg;
long nmsgs;
};
#define I2C_RETRIES 0X701
#define I2C_RDWR 0x707
#define I2C_TIMEOUT 0x702
#define MAX_MSG_NUM 2
struct i2c_rdwr_ioctl_data  eeprom_data;
int main(void)
{
int fd = 0;
int i;
int ret = 0;
fd = open("/dev/i2c-0",O_RDWR);
if (fd < 0 ){
printf("Can not open the i2c-0 device.\n");
exit(1);
}
eeprom_data.msg = malloc(sizeof(struct i2c_msg) * MAX_MSG_NUM);
if (eeprom_data.msg == NULL){
printf("Can not request the memory for msg struct.\n");
exit(1);
}
ioctl(fd, I2C_TIMEOUT, 1);
ioctl(fd, I2C_RETRIES, 2);
eeprom_data.nmsgs = 2;
eeprom_data.msg[0].addr = 0x51;
eeprom_data.msg[0].flags = I2C_M_IGNORE_NAK;
eeprom_data.msg[0].len = 2;
eeprom_data.msg[0].buf = malloc(2);
if (eeprom_data.msg[0].buf == NULL){
printf("Can not request the memory for msg buffer.\n");
free(eeprom_data.msg);
exit(1);
}
eeprom_data.msg[0].buf[0] = 0;
eeprom_data.msg[0].buf[1] = 0;
eeprom_data.msg[1].addr = 0x51;
eeprom_data.msg[1].flags = I2C_M_RD | I2C_M_IGNORE_NAK;
eeprom_data.msg[1].len = 100;
eeprom_data.msg[1].buf = malloc(100);
if (eeprom_data.msg[1].buf == NULL){
printf("Fail to malloc read buf.\n");
free(eeprom_data.msg[0].buf);
free(eeprom_data.msg[1].buf);
free(eeprom_data.msg);
exit(1);
}
ret = ioctl(fd, I2C_RDWR, &eeprom_data);
if (ret <0){
printf("Fail to read data from eeprom.\n");
free(eeprom_data.msg[0].buf);
free(eeprom_data.msg[1].buf);
free(eeprom_data.msg);
exit(1);
}
printf("The data read from eeprom is :\n");
for(i = 0; i < 100 ; i++) {
if (i % 10 == 0){
printf("\n");
printf("%d----.",eeprom_data.msg[1].buf[i]);
}
printf("\n");
}
close(fd);
exit(0);

}


 

你可能感兴趣的:(i2c driver)