天天看点

I2C_Adapter驱动创建讲解与编写参考资料一、回顾二、I2C_Adapter驱动框架三、编写驱动程序框架四、相关源码

参考资料

  • Linux内核文档:
    • Documentation\devicetree\bindings\i2c\i2c-gpio.txt

  • Linux内核驱动程序:使用GPIO模拟I2C
    • drivers\i2c\busses\i2c-gpio.c

  • Linux内核真正的I2C控制器驱动程序
    • drivers\i2c\busses\i2c-imx.c

一、回顾

1.1 I2C驱动程序的层次

  • App:应用程序,想做某些事情。
  • I2C device driver:内核中的i2c驱动和i2c设备,驱动知道怎么做这些事情。
  • I2C Core:i2c一些通用的函数,对于这些事情的辅助作用。
  • I2C Controller Driver:一些相关的函数,具体做这些事情的控制器。

1.2 I2C总线驱动模型

左右两边一一匹配比较,不写了。

二、I2C_Adapter驱动框架

2.1 i2c_adapter

i2c_adpater里面有两个比较重要的结构体:

const struct i2c_algorithm *algo;

int nr;

一个是算法,一个是第几条总线。

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   */
    const struct i2c_lock_operations *lock_ops;
    struct rt_mutex bus_lock;
    struct rt_mutex mux_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;

    struct i2c_bus_recovery_info *bus_recovery_info;
    const struct i2c_adapter_quirks *quirks;
};
           

2.2 i2c_algorithm

然后在i2c_algorithm中主要是

master_xfer

functionality

函数。

struct i2c_algorithm {
    /* If an adapter algorithm can't do I2C-level access, set master_xfer
       to NULL. If an adapter algorithm can do SMBus access, set
       smbus_xfer. If set to NULL, the SMBus protocol is simulated
       using common I2C messages */
    /* master_xfer should return the number of messages successfully
       processed, or a negative value on error */
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
               int num);
    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
               unsigned short flags, char read_write,
               u8 command, int size, union i2c_smbus_data *data);

    /* To determine what the adapter supports */
    u32 (*functionality) (struct i2c_adapter *);

#if IS_ENABLED(CONFIG_I2C_SLAVE)
    int (*reg_slave)(struct i2c_client *client);
    int (*unreg_slave)(struct i2c_client *client);
#endif
};
           
  • master_xfer:这是最重要的函数,它实现了一般的I2C传输,用来传输一个或多个i2c_msg
  • master_xfer_atomic:
    • 可选的函数,功能跟master_xfer一样,在atomic context环境下使用,原子版本
    • 比如在关机之前、所有中断都关闭的情况下,用来访问电源管理芯片
  • smbus_xfer:实现SMBus传输,如果不提供这个函数,SMBus传输会使用master_xfer来模拟
  • smbus_xfer_atomic:
    • 可选的函数,功能跟smbus_xfer一样,在atomic context环境下使用
    • 比如在关机之前、所有中断都关闭的情况下,用来访问电源管理芯片
  • functionality:返回所支持的flags:各类I2C_FUNC_*
  • reg_slave/unreg_slave:
    • 有些I2C Adapter也可工作与Slave模式,用来实现或模拟一个I2C设备
  • reg_salve就是让把一个i2c_client注册到I2C Adapter,换句话说就是让这个I2C Adapter模拟该i2c_client
    • unreg_slave:反注册

三、编写驱动程序框架

分配、设置、注册一个i2c_adapter结构体,这个结构体是在i2c_driver的probe函数里注册的。

  • i2c_adapter的核心是i2c_algorithm
  • i2c_algorithm的核心是masterxfer函数

3.1 所涉及的函数

  • 分配

    struct i2c_adapter *adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);

  • 设置
adap->owner = THIS_MODULE;
adap->algo = &example_algo;
           
  • 注册:i2c_add_adapter / i2c_add_umbered_adapter
ret = i2c_add_adapter(adap);	//不管adap->nr是什么,都动态设置adap->nr
ret = i2c_add_numbered_adapter(adap);	//如果adap->nr == -1,则动态分配nr;佛则使用该nr
           
  • 反注册

    i2c_del_adapter(adap);

3.2 编写设备树文件

/ {
	i2c-bus-virtual {
		compatible = "100ask,i2c-bus-virtual";				//使用字符串匹配i2c_adpter_drv
	};
};
           

3.3 编写adapter_drv的思路

  1. adapter驱动是一个platform_driver的驱动,所以我们先增加platform的init函数和exit函数。
  2. 在注册时需要一个platform_driver的结构体,所以我们需要创建一个i2c_bus_virtual_driver结构体。
  3. 在i2c_bus_virtual_driver中包括,probe函数,remove函数,driver结构体匹配设备树
  4. 在probe函数中需要创建一个全局的i2c_adapter结构体,里面包括传输算法,属于第几个总线。
  5. 在remove函数中去删除i2c_adapter结构体。
  6. 因为只是模拟eeprom的设备,所以就在传输函数上增加了对数据存储和读取的模拟。

四、相关源码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/slab.h>

static struct i2c_adapter *g_adapter;

static unsigned char eeprom_buffer[512];
static int eeprom_cur_addr = 0;

static void eeprom_emulate_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
        int i;
        if(msg->flags | I2C_M_RD) {
                for(i=0;i<msg->len;i++) {
                        msg->buf[i] = eeprom_buffer[eeprom_cur_addr++];
                        if(eeprom_cur_addr == 512)
                                eeprom_cur_addr = 0;
                }
        } else {
                if(msg->len >= 1) {
                        eeprom_cur_addr = msg->buf[0];
                        for(i=0;i<msg->len-1;i++) {
                                eeprom_buffer[eeprom_cur_addr++] = msg->buf[i];
                                if(eeprom_cur_addr == 512)
                                        eeprom_cur_addr = 0;
                        }
                }
        }
}

static int i2c_bus_virtual_xfer(struct i2c_adapter *i2c_adap,
                struct i2c_msg msgs[], int num)
{
        int i;
        //emulate eeprom addr=0x50
        for(i=0;i<num;i++) {
                // do transfer msgs[i]
                if(msgs[i].addr == 0x50) {
                        eeprom_emulate_xfer(i2c_adap, &msgs[i]);
                } else {
                        i = -EIO;
                        break;
                }
        }
        return i;
}

static u32 i2c_bus_virtual_func(struct i2c_adapter *adap)
{
        return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
                        I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA);
}

const struct i2c_algorithm i2c_bus_virtual_algo = {
        .master_xfer = i2c_bus_virtual_xfer,
        .functionality = i2c_bus_virtual_func,
};

static int i2c_bus_virtual_probe(struct platform_device *pdev)
{
        /* get info from device tree, to set i2c_adapter or hardware  */

        /* alloc, set, register i2c_adapter */
        g_adapter = kzalloc(sizeof(*g_adapter), GFP_KERNEL);

        g_adapter->owner = THIS_MODULE;
        g_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        g_adapter->nr = -1;
        g_adapter->algo = &i2c_bus_virtual_algo;
        snprintf(g_adapter->name, sizeof(g_adapter->name), "i2c-bus-virtual");

        i2c_add_adapter(g_adapter);             //i2c_add_numerd_adapter(g_adapter);

        return 0;
}

static int i2c_bus_virtual_remove(struct platform_device *dev)
{
        i2c_del_adapter(g_adapter);
        return 0;
}

static const struct of_device_id i2c_bus_virtual_ids[] = {
        {       .compatible = "100ask,i2c-bus-virtual", },
        {       /* end of list */ }
};

struct platform_driver i2c_bus_virtual_driver = {
        .driver = {
                .name = "i2c-bus-virtual",
                .of_match_table = i2c_bus_virtual_ids,
        },
        .probe = i2c_bus_virtual_probe,
        .remove = i2c_bus_virtual_remove,
};

static int i2c_bus_virtual_init(void)
{
        int ret;

        ret = platform_driver_register(&i2c_bus_virtual_driver);
        if(ret)
                printk(KERN_ERR"i2c_driver register failed\n");

        return 0;
}

static void i2c_bus_virtual_exit(void)
{
        platform_driver_unregister(&i2c_bus_virtual_driver);
}

module_init(i2c_bus_virtual_init);
module_exit(i2c_bus_virtual_exit);

MODULE_AUTHOR("tuo");
MODULE_LICENSE("GPL");
           

继续阅读