linux驅(qū)動(dòng)之i2c框架

一崭添、前言

I2C總線 是一種常用的總線協(xié)議原茅,在設(shè)備中經(jīng)晨岳看到,比如 sensor员咽、陀螺儀等都是使用 I2C總線毒涧。而 Linux內(nèi)核 也提供成熟的 I2C框架,工程師可以根據(jù)硬件特性直接使用該框架編寫驅(qū)動(dòng)程序贝室。本文將著重闡述 Linux內(nèi)核 關(guān)于 I2C總線 的一些概念和實(shí)現(xiàn)

PS:本文默認(rèn)讀者已經(jīng)熟悉I2C協(xié)議契讲,請(qǐng)不了解I2C協(xié)議的讀者自行查閱其他資料了解

二、I2C總線

2.1 重要概念

在了解 Linux內(nèi)核I2C框架 之前滑频,需要對(duì)框架的一些重要概念進(jìn)行認(rèn)識(shí)捡偏,這樣有助于理解框架的設(shè)計(jì)及思路,加深印象峡迷。

  • i2c_bus_typei2c_bus_typeLinux內(nèi)核設(shè)備框架 中的 總線银伟。該 變量 是一個(gè)全局變量,用于 匹配和刪除I2C設(shè)備和I2C驅(qū)動(dòng) 绘搞,并負(fù)責(zé)提供 匹配規(guī)則彤避。i2c_bus_typei2c_device_match 會(huì)查看 驅(qū)動(dòng)設(shè)備 是否 匹配,如果匹配則通過 i2c_device_probe 調(diào)用 驅(qū)動(dòng)的probe函數(shù)夯辖。
    其代碼如下:
struct bus_type i2c_bus_type = {
    .name       = "i2c",
    .match      = i2c_device_match,
    .probe      = i2c_device_probe,
    .remove     = i2c_device_remove,
    .shutdown   = i2c_device_shutdown,
};
  • i2c_adapteri2c_adapter 稱為 I2C適配器琉预。所謂 I2C適配器 就是 Soc 上的 I2C控制器,而 i2c_adapter 就是 硬件I2C控制器驅(qū)動(dòng)實(shí)現(xiàn)蒿褂,即實(shí)現(xiàn)了 CPU通過I2C控制器與外界進(jìn)行數(shù)據(jù)交換
struct i2c_adapter {
    /* I2C適配器的通信方法 */
    const struct i2c_algorithm *algo;
    /* I2C適配器的device結(jié)構(gòu)體圆米,表明其也是一個(gè)設(shè)備 */
    struct device dev;
};
  • i2c_algorithm i2c_algorithmI2C適配器通信方法 實(shí)現(xiàn),一般通過該結(jié)構(gòu)體實(shí)現(xiàn) 數(shù)據(jù)的發(fā)送和接受啄栓。
struct i2c_algorithm {
    /* 主機(jī)發(fā)送函數(shù) */
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
               int num);
    /* 從機(jī)發(fā)送函數(shù) */
    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
               unsigned short flags, char read_write,
               u8 command, int size, union i2c_smbus_data *data);
    /* 返回通信方法適用的適配器特性 */
    u32 (*functionality) (struct i2c_adapter *);
};
  • i2c_clienti2c_client 就是 I2C設(shè)備娄帖,該結(jié)構(gòu)體描述了 I2C設(shè)備硬件信息
struct i2c_client {
    /* 設(shè)備I2C地址 */
    unsigned short addr;        /* chip address - NOTE: 7bit    */
    /* 設(shè)備名稱 */
    char name[I2C_NAME_SIZE];
    /* I2C適配器 */
    struct i2c_adapter *adapter;    /* the adapter we sit on    */
    /* device結(jié)構(gòu)體,表明其為一個(gè)設(shè)備 */
    struct device dev;      /* the device structure     */
};
  • i2c_driveri2c_driver 就是 I2C驅(qū)動(dòng)程序昙楚,就是 I2C硬件設(shè)備端的實(shí)現(xiàn)近速。一般掛接在 I2C適配器 上,并通過 I2C適配器CPU 交換數(shù)據(jù)。
struct i2c_driver {
    /* 驅(qū)動(dòng)的probe函數(shù) */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    /* 驅(qū)動(dòng)的remove函數(shù) */
    int (*remove)(struct i2c_client *);
    /* I2C驅(qū)動(dòng)的device_driver結(jié)構(gòu)體数焊,表明其也是一個(gè)設(shè)備驅(qū)動(dòng) */
    struct device_driver driver;
    /* 驅(qū)動(dòng)ID table永淌,用于匹配 */
    const struct i2c_device_id *id_table;
    /* 該驅(qū)動(dòng)擁有的I2C地址 */
    const unsigned short *address_list;
    /* 該驅(qū)動(dòng)擁有的I2C設(shè)備鏈表 */
    struct list_head clients;
};

筆者粗略的畫了下他們之間的關(guān)系圖崎场,如下所示:

關(guān)系圖

虛線表示對(duì)應(yīng)關(guān)系佩耳,實(shí)現(xiàn)表示數(shù)據(jù)

  1. 硬件信息 通過 設(shè)備樹 解析到了 i2c_client
  2. i2c_driver匹配信息驅(qū)動(dòng)程序 中填充。
  3. i2c_clienti2c_driver 通過 i2c_bus_type 進(jìn)行匹配谭跨,如果成功 i2c_bus_type 調(diào)用 i2c_driverprobe函數(shù)
  4. i2c_driver 通過 i2c_adapter 向外發(fā)送數(shù)據(jù)

2.2 代碼分層

了解完主要的數(shù)據(jù)結(jié)構(gòu)干厚,下面看看 I2C框架 的主體代碼組成,其源碼目錄在 /drivers/i2c螃宙。下分多個(gè)文件和文件夾蛮瞄,包括 I2C框架代碼不同體系結(jié)構(gòu)的I2C驅(qū)動(dòng)代碼 等谆扎。
I2C框架的主體代碼和目錄 分別如下:

  • i2c-core.cI2C框架核心功能源碼挂捅,該文件使得 I2C框架 實(shí)現(xiàn)了 硬件軟件 的分層。
  • i2c-dev.c:該源碼實(shí)現(xiàn)了 I2C適配器設(shè)備文件的功能堂湖,給 I2C適配器 分配一個(gè) 設(shè)備號(hào) 并將其實(shí)現(xiàn)為 /dev目錄 下的 設(shè)備闲先。用戶可以通過該 設(shè)備文件 直接在 應(yīng)用層 操作 Soc的I2C控制器
  • busses:不同 Soc的I2C控制器 的驅(qū)動(dòng)代碼。

2.3 代碼講解

2.3.1 I2C驅(qū)動(dòng)注冊(cè)

編寫 I2C設(shè)備驅(qū)動(dòng) 時(shí)一般按照下面幾個(gè)步驟進(jìn)行:

  1. 使用 i2c_add_driver 添加驅(qū)動(dòng)
  2. 實(shí)現(xiàn) I2C設(shè)備驅(qū)動(dòng)操作集无蜂,比如 probe伺糠、remove
  3. 實(shí)現(xiàn) I2C設(shè)備文件操作集,比如 read斥季、write

實(shí)現(xiàn)設(shè)備調(diào)用 i2c_add_driver函數(shù) 有多種方式训桶,如下:

  • 使用 宏module_i2c_driver 直接添加驅(qū)動(dòng),該宏會(huì)在 驅(qū)動(dòng)裝載 時(shí)自動(dòng)調(diào)用 i2c_add_driver函數(shù)酣倾,其本質(zhì)是注冊(cè) 驅(qū)動(dòng)初始函數(shù)驅(qū)動(dòng)去初始化函數(shù)舵揭。
  • 與一般設(shè)備一樣,通過設(shè)備樹或多種方法 手動(dòng)調(diào)用i2c_add_driver

i2c_add_driver 調(diào)用圖譜如下:

i2c_add_driver(宏)
  ->i2c_register_driver(函數(shù))
    ->driver_register(想內(nèi)核注冊(cè)驅(qū)動(dòng))
      ->bus_add_driver(在i2c_bus_type上注冊(cè)驅(qū)動(dòng))
    ->i2c_for_each_dev(driver, __process_new_driver);(為每個(gè)驅(qū)動(dòng)調(diào)用__process_new_driver函數(shù))
      ->__process_new_driver
        ->i2c_do_add_adapter(加驅(qū)動(dòng)添加到對(duì)應(yīng)的I2C適配器)
          ->i2c_detect(I2C適配器探測(cè)是否存在驅(qū)動(dòng)所對(duì)應(yīng)的I2C設(shè)備)
            ->i2c_detect_address(I2C適配器使用地址對(duì)I2C設(shè)備進(jìn)行探測(cè))
              ->i2c_new_device(生成新的i2c_client(用于描述I2C設(shè)備硬件信息)躁锡,并掛接在I2C驅(qū)動(dòng)上)

代碼如下:

#define module_i2c_driver(__i2c_driver) \
    module_driver(__i2c_driver, i2c_add_driver, \
            i2c_del_driver)
#define i2c_add_driver(driver) \
    i2c_register_driver(THIS_MODULE, driver)

struct bus_type i2c_bus_type = {
    .name       = "i2c",
    .match      = i2c_device_match,
    .probe      = i2c_device_probe,
    .remove     = i2c_device_remove,
    .shutdown   = i2c_device_shutdown,
};

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    ....
    /* 注冊(cè)i2c_driver的總線為i2c_bus_type  */
    driver->driver.bus = &i2c_bus_type;
    /* 初始化i2c_driver的i2c_client鏈表午绳,一個(gè)driver可以有多個(gè)client,掛接在該鏈表上 */
    INIT_LIST_HEAD(&driver->clients);
    ....
    /* 注冊(cè)驅(qū)動(dòng) */
    res = driver_register(&driver->driver);
    ....
    /* 對(duì)i2c_driver上的每一個(gè)i2c_client都調(diào)用__process_new_driver */
    i2c_for_each_dev(driver, __process_new_driver);

    return 0;
}

下面分為 2個(gè) 階段來闡述注冊(cè)過程:

  • driver_register:注冊(cè)驅(qū)動(dòng)
  • i2c_for_each_dev:遍歷設(shè)備并調(diào)用 __process_new_driver 創(chuàng)建 i2c_client

下面先看看 driver_register 的代碼流程:

int driver_register(struct device_driver *drv)
{
    ......
    /* 將驅(qū)動(dòng)添加到總線上 */
    ret = bus_add_driver(drv);
    ......
    return ret;
}

int bus_add_driver(struct device_driver *drv)
{
     /* 將驅(qū)動(dòng)添加到總線的驅(qū)動(dòng)鏈表(bus->p->klist_drivers) */
    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
    ......
    if (drv->bus->p->drivers_autoprobe) {
        if (driver_allows_async_probing(drv)) {
            ......
        } else {
            error = driver_attach(drv);
            ......
        }
    }
    ......
    return 0;

}

int driver_attach(struct device_driver *drv)
{
    /* 遍歷總線上的所有驅(qū)動(dòng)并調(diào)用__driver_attach */
    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, fn)
{
    /* 
      遍歷i2c_bus_type總線的所有設(shè)備鏈表(bus->p->klist_devices)的所有設(shè)備稚铣,執(zhí)行fn函數(shù) 
      這里的設(shè)備就是i2c_client中的struct device成員
      在設(shè)備樹解析時(shí)會(huì)根據(jù)i2c節(jié)點(diǎn)創(chuàng)建i2c_client箱叁,并將其struct device成員鏈入i2c_bus_type的bus->p->klist_devices鏈表
    */
    while ((dev = next_device(&i)) && !error)
        error = fn(dev, data);
}

static int __driver_attach(struct device *dev, void *data)
{
    struct device_driver *drv = data;
    int ret;
    /* 匹配設(shè)備和驅(qū)動(dòng),如果不成功則返回 */
    ret = driver_match_device(drv, dev);
        if (ret == 0) {
        /* no match */
        return 0;
    }
    ......
    /* 匹配設(shè)備和驅(qū)動(dòng)成功惕医,調(diào)用probe函數(shù) */
    if (!dev->driver)
        driver_probe_device(drv, dev);
    ......
    return 0;
}
static inline int driver_match_device(struct device_driver *drv,
                      struct device *dev)
{
    /* 調(diào)用i2c_bus_type總線的macth函數(shù)耕漱,即i2c_device_match函數(shù) */
    return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{

    ......
    driver = to_i2c_driver(drv);

    /* 調(diào)用i2c_match_id進(jìn)行配 */
    if (i2c_match_id(driver->id_table, client))
        return 1;

    return 0;
}

const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client)
{
    /* 匹配驅(qū)動(dòng)的名稱和設(shè)備名稱,并返回結(jié)果 */
    while (id->name[0]) {
        if (strcmp(client->name, id->name) == 0)
            return id;
        id++;
    }
}

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
    ......
    /* 如果匹配到設(shè)備則調(diào)用probe函數(shù) */
    ret = really_probe(dev, drv);
    ......
}

static int really_probe(struct device *dev, struct device_driver *drv)
{
    /* 調(diào)用i2c_bus_type的probe函數(shù)抬伺,即i2c_device_probe函數(shù) */
    if (dev->bus->probe) {
        ret = dev->bus->probe(dev);
        if (ret)
            goto probe_failed;
    } 
}
static int i2c_device_probe(struct device *dev)
{
    /* 
      調(diào)用i2c驅(qū)動(dòng)的probe函數(shù) 
    */
    driver->probe(client, i2c_match_id(driver->id_table, client));
}

到了這里螟够,driver_register 的流程基本結(jié)束,下面主要執(zhí)行 i2c_for_each_dev,代碼流程如下:

int i2c_for_each_dev(void *data, fn)
{
    ......
    /* 對(duì)i2c_bus_type總線上的設(shè)備進(jìn)行遍歷 */
    res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
    ......
}
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int fn)
{
    ......
    /* 
      找出掛在bus->p->klist_devices鏈表上的首個(gè)設(shè)備(struct device)的迭代器。
      這里可能有讀者有疑問赤嚼,這些struct device什么時(shí)鏈入bus->p->klist_devices的呢
      在設(shè)備樹解析期間闯估,如果設(shè)備樹的i2c節(jié)點(diǎn)有設(shè)備節(jié)點(diǎn),則會(huì)調(diào)用i2c_new_device萧福。
      該函數(shù)會(huì)創(chuàng)建一個(gè)i2c_client,并將該i2c_client的struct device成員鏈入bus->p->klist_devices鏈表
      其中的bus就是i2c_bus_type
    */
    klist_iter_init_node(&bus->p->klist_devices, &i,
                 (start ? &start->p->knode_bus : NULL));
    /* 遍歷總線上的設(shè)備鏈表(bus->p->klist_devices)的所有設(shè)備辈赋,調(diào)用fn函數(shù) */
    while ((dev = next_device(&i)) && !error)
        error = fn(dev, data);  
    ......
}
static int __process_new_driver(struct device *dev, void *data)
{
    if (dev->type != &i2c_adapter_type)
        return 0;
    /* 
      根據(jù)函數(shù)傳遞進(jìn)來的參數(shù)可知鲫忍,data參數(shù)就是i2c_driver結(jié)構(gòu)體 
      而to_i2c_adapter(dev)用于找出該設(shè)備對(duì)應(yīng)的i2c_adapter。
      這里也是同理钥屈,在設(shè)備樹解析時(shí)悟民,會(huì)根據(jù)設(shè)備所在的i2c節(jié)點(diǎn)(設(shè)備樹的i2c節(jié)點(diǎn)對(duì)應(yīng)一個(gè)i2c_控制器)
      將設(shè)備鏈入對(duì)應(yīng)的i2c_adapter結(jié)構(gòu)體
    */
    return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}
static int i2c_do_add_adapter(struct i2c_driver *driver,
                  struct i2c_adapter *adap)
{
    /* 使用對(duì)應(yīng)的i2c_adapter探測(cè)驅(qū)動(dòng)所對(duì)應(yīng)的設(shè)備是否存在 */
    i2c_detect(adap, driver);
    ......
    return 0;
}
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{
    ......
    /* 獲取驅(qū)動(dòng)的I2C地址鏈表 */
    address_list = driver->address_list;
    /* 
      如果i2c_driver沒實(shí)現(xiàn)detect函數(shù),那么將會(huì)在這里返回 
      有些i2c驅(qū)動(dòng)沒有實(shí)現(xiàn)detect函數(shù)篷就,所以注冊(cè)流程到這里就結(jié)束了
    */
    if (!driver->detect || !address_list)
        return 0;

    for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
        ......
        /* i2c_detect_address會(huì)根據(jù)地址鏈表去探測(cè)是否存在設(shè)備 */
        temp_client->addr = address_list[i];
        err = i2c_detect_address(temp_client, driver);
        ......
    }
}
static int i2c_detect_address(struct i2c_client *temp_client,
                  struct i2c_driver *driver)
{
    struct i2c_board_info info;
    struct i2c_adapter *adapter = temp_client->adapter;
    int addr = temp_client->addr;
    int err;

    ......
    memset(&info, 0, sizeof(struct i2c_board_info));
    info.addr = addr;
    /* 調(diào)用驅(qū)動(dòng)的detecr函數(shù) */
    err = driver->detect(temp_client, &info);
    if (err) {
        /* 失敗就返回-ENODEV */
        return err == -ENODEV ? 0 : err;
    }

    /* Consistency check */
    if (info.type[0] == '\0') {
        ......
    } else {
        /* 如果探測(cè)成功則使用i2c_new_device創(chuàng)建client并添加到driver的clients鏈表 */
        client = i2c_new_device(adapter, &info);
        if (client)
            list_add_tail(&client->detected, &driver->clients);
        else
            dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
                info.type, info.addr);
    }
    return 0;
}

有些 I2C驅(qū)動(dòng) 并不會(huì)實(shí)現(xiàn) detect函數(shù)射亏,所以在知道到 driver_register 時(shí)已經(jīng)完成大部分工作,在 執(zhí)行 i2c_for_each_dev 時(shí)就已經(jīng)早早退出了竭业。

前面的代碼多次提到過 i2c_client結(jié)構(gòu)體struct device結(jié)構(gòu)智润,從前面的代碼講解中我們已經(jīng)知道 設(shè)備樹解析 時(shí)會(huì)根據(jù) I2C節(jié)點(diǎn) 的信息創(chuàng)建對(duì)應(yīng)的 i2c_clientstruct device,那么這里也提一下如何在 設(shè)備樹 中添加 I2C設(shè)備節(jié)點(diǎn)信息

Documentation/devicetree/bindings/i2c 目錄下的 i2c.txt文檔 中可以查看 i2c_adapter 的設(shè)備節(jié)點(diǎn)信息永品,而在同一目錄下存在 多個(gè)Soc平臺(tái)i2c適配器說明做鹰。

I2C設(shè)備 一般是作為 slaver(從機(jī)) 存在,而一般的 I2C從機(jī)節(jié)點(diǎn)信息 有以下屬性:

  • compatible:用于 驅(qū)動(dòng)匹配
  • regI2C設(shè)備 的總線地址

下面為 設(shè)備樹 代碼例子:

i2c0@0xXXXXXXXX {
        /* i2c控制器的硬件信息 */
        ......
        /* i2c設(shè)備節(jié)點(diǎn)信息 */
        i2c_test: i2c_test@34{
                compatible = "xxxx";
                /* reg表明該I2C設(shè)備的地址為0x34 */
                reg = <0x34>;
        };
};

2.3.2 I2C設(shè)備注冊(cè)

I2C驅(qū)動(dòng)注冊(cè) 中使用的是默認(rèn)的 自動(dòng)注冊(cè)設(shè)備鼎姐,也就是通過 設(shè)備樹钾麸,由操作系統(tǒng)幫我們完成 設(shè)備的生成和注冊(cè)

在解析 I2C設(shè)備節(jié)點(diǎn) 時(shí)炕桨,一般調(diào)用的是:

  • of_i2c_register_device
  • of_i2c_register_devices

它們最終都會(huì)調(diào)用到 i2c_new_device 來創(chuàng)建 I2C設(shè)備饭尝。

如果在某些場(chǎng)景,比如 設(shè)備樹 沒有 I2C設(shè)備節(jié)點(diǎn)献宫, 想 手動(dòng)注冊(cè)I2C設(shè)備 钥平,那么就可以通過手動(dòng)調(diào)用 i2c_new_device 來創(chuàng)建和注冊(cè) I2C設(shè)備

其函數(shù)調(diào)用圖譜如下:

->i2c_new_device
    ->device_register
        ->device_add
            ->bus_add_device
            ->bus_probe_device
                ->__device_attach
                    ->driver_match_device
                    ->driver_probe_device
struct i2c_client* i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    ......
    /* 創(chuàng)建完i2c_client后對(duì)其進(jìn)行一列的初始化 */
    client->adapter = adap;
    client->dev.platform_data = info->platform_data;
    client->flags = info->flags;
    client->addr = info->addr;
    client->irq = info->irq;
    strlcpy(client->name, info->type, sizeof(client->name));
    ......
    /* 設(shè)備i2c_client的struct device成員 */
    client->dev.parent = &client->adapter->dev;
    client->dev.bus = &i2c_bus_type;
    client->dev.type = &i2c_client_type;
    client->dev.of_node = info->of_node;
    client->dev.fwnode = info->fwnode;
    ......
    i2c_dev_set_name(adap, client);
    ......
    /* 注冊(cè)shebei */
    status = device_register(&client->dev);
    ......
    return client;

}
int device_register(struct device *dev)
{
    device_initialize(dev);
    /* 添加設(shè)備 */
    return device_add(dev);
}
int device_add(struct device *dev)
{
    ......
    /* 將設(shè)備添加是bus上 */
    error = bus_add_device(dev);

    ......
  /* 探測(cè)bus上是否有與設(shè)備匹配的驅(qū)動(dòng) */
    bus_probe_device(dev);
    ......
}
int bus_add_device(struct device *dev)
{
    struct bus_type *bus = bus_get(dev->bus);

    ......

    if (bus) {
      /* 將設(shè)備添加到bus的klist_devices鏈表歐尚姊途,該成員在注冊(cè)驅(qū)動(dòng)時(shí)會(huì)使用到涉瘾。用于匹配 */
        klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
    }
    return 0;

}
void bus_probe_device(struct device *dev)
{
    struct bus_type *bus = dev->bus;
    struct subsys_interface *sif;
    ......
    if (bus->p->drivers_autoprobe)
    /* device_initial_probe直接調(diào)用__device_attach */
        device_initial_probe(dev);
    ......
}
static int __device_attach(struct device *dev, bool allow_async)
{
    ......
    /* 對(duì)bus上的每個(gè)設(shè)備都調(diào)用__device_attach_driver */
    ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
    ......
}
static int __device_attach_driver(struct device_driver *drv, void *_data)
{

    ......
    /* 匹配設(shè)備與驅(qū)動(dòng) */
    ret = driver_match_device(drv, dev);
    ......
    /* 匹配成功則調(diào)用probe */
    return driver_probe_device(drv, dev);
}

2.4 驅(qū)動(dòng)接口及步驟

2.4.1 I2C框架接口

  1. 增加/刪除I2C適配器
  • i2c_add_adapter
  • i2c_del_adapter
  1. 增加/刪除I2C驅(qū)動(dòng)
  • i2c_add_driver
  • i2c_del_driver
  1. I2C傳輸、發(fā)送和接收
  • i2c_transfer(adap, msgs, num):用于 I2C適配器設(shè)備 之間的 一組 消息交互捷兰。注意其傳入的實(shí)例為 I2C適配器
  • i2c_master_recv:用于 主機(jī)接收立叛,內(nèi)部調(diào)用 i2c_transfer。注意其傳入的實(shí)例為 I2C設(shè)備(i2c_client)
  • i2c_master_send(client, buf, count):用于 主機(jī)發(fā)送贡茅,內(nèi)部調(diào)用 i2c_transfer秘蛇。注意其傳入的實(shí)例為 I2C設(shè)備(i2c_client)

2.4.2 I2C步驟

對(duì) I2C驅(qū)動(dòng) 的移植和編寫主要有 2部分

  • I2C適配器:完成 I2C適配器 的硬件驅(qū)動(dòng)

    1. 探測(cè)及初始化 I2C適配器其做,如 申請(qǐng)寄存器地址申請(qǐng)中斷
    2. 驅(qū)動(dòng) I2C適配器 產(chǎn)生信號(hào)及 中斷處理
    3. 提供 I2C適配器algorithm(即通信方法)
    4. 使用 i2c_add_adapter 添加 I2C適配器
  • I2C驅(qū)動(dòng):完成 I2C設(shè)備外設(shè) 的硬件驅(qū)動(dòng)

    1. 實(shí)現(xiàn) i2c_driver 的接口赁还,如 probe妖泄、remove、suspend艘策、resume
    2. 填充 i2c_device_id表 并賦值給 i2c_driver
    3. 實(shí)現(xiàn) I2C設(shè)備 的具體方法蹈胡,比如 read、write柬焕、ioctl

三审残、參考鏈接

Linux I2C驅(qū)動(dòng)框架
Linux i2c 設(shè)備驅(qū)動(dòng)程序框架詳解
I2C中的重復(fù)起始條件到底是什么意思

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末梭域,一起剝皮案震驚了整個(gè)濱河市斑举,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌病涨,老刑警劉巖富玷,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異既穆,居然都是意外死亡赎懦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門幻工,熙熙樓的掌柜王于貴愁眉苦臉地迎上來励两,“玉大人,你說我怎么就攤上這事囊颅〉被冢” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵踢代,是天一觀的道長盲憎。 經(jīng)常有香客問我,道長胳挎,這世上最難降的妖魔是什么饼疙? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮慕爬,結(jié)果婚禮上窑眯,老公的妹妹穿的比我還像新娘。我一直安慰自己医窿,他們只是感情好磅甩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著留搔,像睡著了一般更胖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天却妨,我揣著相機(jī)與錄音饵逐,去河邊找鬼。 笑死彪标,一個(gè)胖子當(dāng)著我的面吹牛倍权,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捞烟,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼薄声,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了题画?” 一聲冷哼從身側(cè)響起默辨,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苍息,沒想到半個(gè)月后缩幸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竞思,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年表谊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盖喷。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡爆办,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出课梳,到底是詐尸還是另有隱情距辆,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布惦界,位于F島的核電站挑格,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沾歪。R本人自食惡果不足惜漂彤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望灾搏。 院中可真熱鬧挫望,春花似錦、人聲如沸狂窑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泉哈。三九已至蛉幸,卻和暖如春破讨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奕纫。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工提陶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人匹层。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓隙笆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親升筏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子撑柔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345