天天看點

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");
           

繼續閱讀