天天看點

Linux裝置驅動之I2C架構分析

一:前言

I2c是philips提出的外設總線.I2C隻有兩條線,一條串行資料線:SDA,一條是時鐘線SCL.正因為這樣,它友善了工程人員 的布線.另外,I2C是一種多主機控制總線.它和USB總線不同,USB是基于master-slave機制,任何裝置的通信必須由主機發起才可以.而 I2C 是基于multi master機制.一同總線上可允許多個master.關于I2C協定的知識,這裡不再贅述.可自行下載下傳spec閱讀即可.

二:I2C架構概述

在linux中,I2C驅動架構如下所示:

Linux裝置驅動之I2C架構分析

如 上圖所示,每一條I2C對應一個adapter.在kernel中,每一個adapter提供了一個描述的結構(struct i2c_adapter),也定義了adapter支援的操作(struct i2c_adapter).再通過i2c core層将i2c裝置與i2c adapter關聯起來.

這個圖隻是提供了一個大概的架構.在下面的代碼分析中,從下至上的來分析這個架構圖.以下的代碼分析是基于linux 2.6.26.分析的代碼基本位于: linux-2.6.26.3/drivers/i2c/位置.

三:adapter注冊

在 kernel中提供了兩個adapter注冊接口,分别為i2c_add_adapter()和 i2c_add_numbered_adapter().由于在系統中可能存在多個adapter,因為将每一條I2C總線對應一個編号,下文中稱為 I2C總線号.這個總線号的PCI中的總線号不同.它和硬體無關,隻是軟體上便于區分而已.

對于i2c_add_adapter()而言,它使用的是動态總線号,即由系統給其分析一個總線号,而i2c_add_numbered_adapter()則是自己指定總線号,如果這個總線号非法或者是被占用,就會注冊失敗.

分别來看一下這兩個函數的代碼:

int i2c_add_adapter(struct i2c_adapter *adapter)

{

    int id, res = 0;

retry:

    if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)

        return -ENOMEM;

    mutex_lock(&core_lock);

    res = idr_get_new_above(&i2c_adapter_idr, adapter,

                __i2c_first_dynamic_bus_num, &id);

    mutex_unlock(&core_lock);

    if (res < 0) {

        if (res == -EAGAIN)

            goto retry;

        return res;

    }

    adapter->nr = id;

    return i2c_register_adapter(adapter);

}

在 這裡涉及到一個idr結構.idr結構本來是為了配合page cache中的radix tree而設計的.在這裡我們隻需要知道,它是一種高效的搜尋樹,且這個樹預先存放了一些記憶體.避免在記憶體不夠的時候出現問題.所在,在往idr中插入結 構的時候,首先要調用idr_pre_get()為它預留足夠的空閑記憶體,然後再調用idr_get_new_above()将結構插入idr中,該函數 以參數的形式傳回一個id.以後憑這個id就可以在idr中找到相對應的結構了.對這個資料結構操作不太了解的可以查閱本站<< linux檔案系統之檔案的讀寫>>中有關radix tree的分析.

注意一下 idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id)的參數的含義,它是将adapter結構插入到i2c_adapter_idr中,存放位置的id必須要大于或者等于 __i2c_first_dynamic_bus_num,

然後将對應的id号存放在adapter->nr中.調用i2c_register_adapter(adapter)對這個adapter進行進一步注冊.

看一下另外一人注冊函數: i2c_add_numbered_adapter( ),如下所示:

int i2c_add_numbered_adapter(struct i2c_adapter *adap)

{

    int id;

    int status;

    if (adap->nr & ~MAX_ID_MASK)

        return -EINVAL;

retry:

    if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)

        return -ENOMEM;

    mutex_lock(&core_lock);

    status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);

    if (status == 0 && id != adap->nr) {

        status = -EBUSY;

        idr_remove(&i2c_adapter_idr, id);

    }

    mutex_unlock(&core_lock);

    if (status == -EAGAIN)

        goto retry;

    if (status == 0)

        status = i2c_register_adapter(adap);

    return status;

}

對比一下就知道差别了,在這裡它已經指定好了adapter->nr了.如果配置設定的id不和指定的相等,便傳回錯誤.

過一步跟蹤i2c_register_adapter().代碼如下:

static int i2c_register_adapter(struct i2c_adapter *adap)

{

    int res = 0, dummy;

    mutex_init(&adap->bus_lock);

    mutex_init(&adap->clist_lock);

    INIT_LIST_HEAD(&adap->clients);

    mutex_lock(&core_lock);

    if (adap->dev.parent == NULL) {

        adap->dev.parent = &platform_bus;

        pr_debug("I2C adapter driver [%s] forgot to specify "

            "physical device/n", adap->name);

    }

    sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);

    adap->dev.release = &i2c_adapter_dev_release;

    adap->dev.class = &i2c_adapter_class;

    res = device_register(&adap->dev);

    if (res)

        goto out_list;

    dev_dbg(&adap->dev, "adapter [%s] registered/n", adap->name);

    if (adap->nr < __i2c_first_dynamic_bus_num)

        i2c_scan_static_board_info(adap);

    dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,

                i2c_do_add_adapter);

out_unlock:

    mutex_unlock(&core_lock);

    return res;

out_list:

    idr_remove(&i2c_adapter_idr, adap->nr);

    goto out_unlock;

}

首先對adapter和adapter中内嵌的struct device結構進行必須的初始化.之後将adapter内嵌的struct device注冊.

在這裡注意一下adapter->dev的初始化.它的類别為i2c_adapter_class,如果沒有父結點,則将其父結點設為platform_bus.adapter->dev的名字為i2c + 總線号.

測試一下:

[[email protected] i2c]$ cd /sys/class/i2c-adapter/

[[email protected] i2c-adapter]$ ls

i2c-0

可以看到,在我的PC上,有一個I2C adapter,看下詳細資訊:

[[email protected] i2c-adapter]$ tree

.

`-- i2c-0

    |-- device -> ../../../devices/pci0000:00/0000:00:1f.3/i2c-0

    |-- name

    |-- subsystem -> ../../../class/i2c-adapter

    `-- uevent

3 directories, 2 files

可以看到,該adapter是一個PCI裝置.

繼續往下看:

之後,在注釋中看到,有兩種類型的driver,一種是new-style drivers,另外一種是legacy drivers

New-style drivers是在2.6近版的kernel加入的.它們最主要的差別是在adapter和i2c driver的比對上.

3.1: new-style 形式的adapter注冊

對于第一種,也就是new-style drivers,将相關代碼再次列出如下:

    if (adap->nr < __i2c_first_dynamic_bus_num)

        i2c_scan_static_board_info(adap);

如果adap->nr 小于__i2c_first_dynamic_bus_num的話,就會進入到i2c_scan_static_board_info().

結合我們之前分析的adapter的兩種注冊分式: i2c_add_adapter()所分得的總線号肯會不會小于__i2c_first_dynamic_bus_num.隻有i2c_add_numbered_adapter()才有可能滿足:

(adap->nr < __i2c_first_dynamic_bus_num)

而 且必須要調用i2c_register_board_info()将闆子上的I2C裝置資訊預先注冊時才會更改 __i2c_first_dynamic_bus_num的值.在x86上隻沒有使用i2c_register_board_info()的.因 此,x86平台上的分析可以忽略掉new-style driver的方式.不過,還是詳細分析這種情況下.

首先看一下i2c_register_board_info(),如下:

int __init

i2c_register_board_info(int busnum,

    struct i2c_board_info const *info, unsigned len)

{

    int status;

    mutex_lock(&__i2c_board_lock);

    if (busnum >= __i2c_first_dynamic_bus_num)

        __i2c_first_dynamic_bus_num = busnum + 1;

    for (status = 0; len; len--, info++) {

        struct i2c_devinfo  *devinfo;

        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);

        if (!devinfo) {

            pr_debug("i2c-core: can't register boardinfo!/n");

            status = -ENOMEM;

            break;

        }

        devinfo->busnum = busnum;

        devinfo->board_info = *info;

        list_add_tail(&devinfo->list, &__i2c_board_list);

    }

    mutex_unlock(&__i2c_board_lock);

    return status;

}

這個函數比較簡單, struct i2c_board_info用來表示I2C裝置的一些情況,比如所在的總線.名稱,位址,中斷号等.最後,這些資訊會被存放到__i2c_board_list連結清單.

跟蹤i2c_scan_static_board_info():代碼如下:

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)

{

    struct i2c_devinfo  *devinfo;

    mutex_lock(&__i2c_board_lock);

    list_for_each_entry(devinfo, &__i2c_board_list, list) {

        if (devinfo->busnum == adapter->nr

                && !i2c_new_device(adapter,

                        &devinfo->board_info))

            printk(KERN_ERR "i2c-core: can't create i2c%d-%04x/n",

                i2c_adapter_id(adapter),

                devinfo->board_info.addr);

    }

    mutex_unlock(&__i2c_board_lock);

}

該函數周遊挂在__i2c_board_list連結清單上面的i2c裝置的資訊,也就是我們在啟動的時候指出的i2c裝置的資訊.

如果指定裝置是位于adapter所在的I2C總線上,那麼,就調用i2c_new_device().代碼如下:

struct i2c_client *

i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

{

    struct i2c_client  *client;

    int        status;

    client = kzalloc(sizeof *client, GFP_KERNEL);

    if (!client)

        return NULL;

    client->adapter = adap;

    client->dev.platform_data = info->platform_data;

    device_init_wakeup(&client->dev, info->flags & I2C_CLIENT_WAKE);

    client->flags = info->flags & ~I2C_CLIENT_WAKE;

    client->addr = info->addr;

    client->irq = info->irq;

    strlcpy(client->name, info->type, sizeof(client->name));

    status = i2c_attach_client(client);

    if (status < 0) {

        kfree(client);

        client = NULL;

    }

    return client;

}

我們又遇到了一個新的結構:struct i2c_client,不要被這個結構吓倒了,其實它就是一個嵌入struct device的I2C裝置的封裝.它和我們之前遇到的struct usb_device結構的作用是一樣的.

首先,在clinet裡儲存該裝置的相關消息.特别的, client->adapter指向了它所在的adapter.

特别的,clinet->name為info->name.也是指定好了的.

一切初始化完成之後,便會調用i2c_attach_client( ).看這個函數的字面意思,是将clinet關聯起來.到底怎麼樣關聯呢?繼續往下看:

int i2c_attach_client(struct i2c_client *client)

{

    struct i2c_adapter *adapter = client->adapter;

    int res = 0;

    //初始化client内嵌的dev結構

    //父結點為所在的adapter,所在bus為i2c_bus_type

    client->dev.parent = &client->adapter->dev;

    client->dev.bus = &i2c_bus_type;

    //如果client已經指定了driver,将driver和内嵌的dev關聯起來 

    if (client->driver)

        client->dev.driver = &client->driver->driver;

    //指定了driver, 但不是newstyle的

    if (client->driver && !is_newstyle_driver(client->driver)) {

        client->dev.release = i2c_client_release;

        client->dev.uevent_suppress = 1;

    } else

        client->dev.release = i2c_client_dev_release;

    //clinet->dev的名稱

    snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),

        "%d-%04x", i2c_adapter_id(adapter), client->addr);

    //将内嵌的dev注冊

    res = device_register(&client->dev);

    if (res)

        goto out_err;

    //将clinet鍊到adapter->clients中

    mutex_lock(&adapter->clist_lock);

    list_add_tail(&client->list, &adapter->clients);

    mutex_unlock(&adapter->clist_lock);

    dev_dbg(&adapter->dev, "client [%s] registered with bus id %s/n",

        client->name, client->dev.bus_id);

    //如果adapter->cleinet_reqister存在,就調用它

    if (adapter->client_register)  {

        if (adapter->client_register(client)) {

            dev_dbg(&adapter->dev, "client_register "

                "failed for client [%s] at 0x%02x/n",

                client->name, client->addr);

        }

    }

    return 0;

out_err:

    dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "

        "(%d)/n", client->name, client->addr, res);

    return res;

}

參考上面添加的注釋,應該很容易了解這段代碼了,就不加詳細分析了.這個函數的名字不是i2c_attach_client()麼?怎麼沒看到它的關系過程呢?

這是因為:在代碼中設定了client->dev所在的bus為i2c_bus_type .以為隻需要有bus為i2c_bus_type的driver注冊,就會産生probe了.這個過程呆後面分析i2c driver的時候再來詳細分析.

3.2: legacy形式的adapter注冊

Legacy形式的adapter注冊代碼片段如下:

    dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,

                i2c_do_add_adapter);

這段代碼周遊挂在i2c_bus_type上的驅動,然後對每一個驅動和adapter調用i2c_do_add_adapter().

代碼如下:

static int i2c_do_add_adapter(struct device_driver *d, void *data)

{

    struct i2c_driver *driver = to_i2c_driver(d);

    struct i2c_adapter *adap = data;

    if (driver->attach_adapter) {

        driver->attach_adapter(adap);

    }

    return 0;

}

該函數很簡單,就是調用driver的attach_adapter()接口.

到此為止,adapter的注冊已經分析完了.

四:i2c driver注冊

在分析i2c driver的時候,有必要先分析一下i2c架構的初始化

代碼如下:

static int __init i2c_init(void)

{

    int retval;

    retval = bus_register(&i2c_bus_type);

    if (retval)

        return retval;

    retval = class_register(&i2c_adapter_class);

    if (retval)

        goto bus_err;

    retval = i2c_add_driver(&dummy_driver);

    if (retval)

        goto class_err;

    return 0;

class_err:

    class_unregister(&i2c_adapter_class);

bus_err:

    bus_unregister(&i2c_bus_type);

    return retval;

}

subsys_initcall(i2c_init);

很明顯,i2c_init()會在系統初始化的時候被調用.

在i2c_init中,先注冊了i2c_bus_type的bus,i2c_adapter_class的class.然後再調用i2c_add_driver()注冊了一個i2c driver.

I2c_bus_type結構如下:

static struct bus_type i2c_bus_type = {

    .name      = "i2c",

    .dev_attrs  = i2c_dev_attrs,

    .match      = i2c_device_match,

    .uevent    = i2c_device_uevent,

    .probe      = i2c_device_probe,

    .remove    = i2c_device_remove,

    .shutdown  = i2c_device_shutdown,

    .suspend    = i2c_device_suspend,

    .resume    = i2c_device_resume,

};

這個結構先放在這裡吧,以後還會用到裡面的資訊的.

從上面的初始化函數裡也看到了,注冊i2c driver的接口為i2c_add_driver().代碼如下:

static inline int i2c_add_driver(struct i2c_driver *driver)

{

    return i2c_register_driver(THIS_MODULE, driver);

}

繼續跟蹤:

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

{

    int res;

    //如果是一個newstyle的driver.但又定義了attach_adapter/detach_adapter.非法

    if (is_newstyle_driver(driver)) {

        if (driver->attach_adapter || driver->detach_adapter

                || driver->detach_client) {

            printk(KERN_WARNING

                    "i2c-core: driver [%s] is confused/n",

                    driver->driver.name);

            return -EINVAL;

        }

    }

    //關聯到i2c_bus_types

    driver->driver.owner = owner;

    driver->driver.bus = &i2c_bus_type;

    //注冊内嵌的driver

    res = driver_register(&driver->driver);

    if (res)

        return res;

    mutex_lock(&core_lock);

    pr_debug("i2c-core: driver [%s] registered/n", driver->driver.name);

    //周遊所有的adapter,對其都調用driver->attach_adapter

    if (driver->attach_adapter) {

        struct i2c_adapter *adapter;

        down(&i2c_adapter_class.sem);

        list_for_each_entry(adapter, &i2c_adapter_class.devices,

                    dev.node) {

            driver->attach_adapter(adapter);

        }

        up(&i2c_adapter_class.sem);

    }

    mutex_unlock(&core_lock);

    return 0;

}

這裡也有兩種形式的區分,對于第一種,隻需要将内嵌的driver注冊就可以了,對于legacy的情況,對每一個adapter都調用driver->attach_adapter().

現在,我們可以将adapter和i2c driver關聯起來考慮一下了:

1:如果是news style形式的,在注冊adapter的時候,将它上面的i2c 裝置轉換成了struct client.struct client->dev->bus又指定了和i2c driver同一個bus.因為,它們可以發生probe.

2:如果是legacy形式,就直接找到對應的對象,調用driver->attach_adapter().

五: i2c_bus_type的相關操作

I2c_bus_type的操作主要存在于new-style形式的驅動中.接下來分析一下對應的probe過程:

5.1:match過程分析

Match對應的操作函數為i2c_device_match().代碼如下

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

{

    struct i2c_client  *client = to_i2c_client(dev);

    struct i2c_driver  *driver = to_i2c_driver(drv);

    if (!is_newstyle_driver(driver))

        return 0;

    if (driver->id_table)

        return i2c_match_id(driver->id_table, client) != NULL;

    return 0;

}

如果該驅動不是一個new-style形式的.或者driver沒有定義比對的id_table.都會比對失敗.

繼續跟蹤進i2c_match_id():

static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,

                        const struct i2c_client *client)

{

    while (id->name[0]) {

        if (strcmp(client->name, id->name) == 0)

            return id;

        id++;

    }

    return NULL;

}

由此可見.如果client的名字和driver->id_table[]中的名稱比對即為成功.

5.2:probe過程分析

Probe對應的函數為: i2c_device_probe()

static int i2c_device_probe(struct device *dev)

{

    struct i2c_client  *client = to_i2c_client(dev);

    struct i2c_driver  *driver = to_i2c_driver(dev->driver);

    const struct i2c_device_id *id;

    int status;

    if (!driver->probe)

        return -ENODEV;

    client->driver = driver;

    dev_dbg(dev, "probe/n");

    if (driver->id_table)

        id = i2c_match_id(driver->id_table, client);

    else

        id = NULL;

    status = driver->probe(client, id);

    if (status)

        client->driver = NULL;

    return status;

}

這個函數也很簡單,就是将probe流程回溯到i2c driver的probe()

六:其它的擴充

分析完adapter和i2c driver的注冊之後,好像整個架構也差不多了,其它,擴充的東西還有很多.

我們舉一個legacy形式的例子,這個例子是在kernel中随便搜尋出來的:

在linux-2.6.26.3/drivers/hwmon/ad7418.c中,初始化函數為:

static int __init ad7418_init(void)

{

    return i2c_add_driver(&ad7418_driver);

}

i2c_driver ad7418_driver結構如下:

static struct i2c_driver ad7418_driver = {

    .driver = {

        .name  = "ad7418",

    },

    .attach_adapter = ad7418_attach_adapter,

    .detach_client  = ad7418_detach_client,

};

該結構中沒有probe()函數,可以斷定是一個legacy形式的驅動.這類驅動注冊的時候,會調用driver的attach_adapter函數.在這裡也就是ad7418_attach_adapter.

這個函數代碼如下:

static int ad7418_attach_adapter(struct i2c_adapter *adapter)

{

    if (!(adapter->class & I2C_CLASS_HWMON))

        return 0;

    return i2c_probe(adapter, &addr_data, ad7418_detect);

}

在這裡我們又遇到了一個i2c-core中的函數,i2c_probe().在分析這個函數之前,先來看下addr_data是什麼?

#define I2C_CLIENT_MODULE_PARM(var,desc) /

  static unsigned short var[I2C_CLIENT_MAX_OPTS] = I2C_CLIENT_DEFAULTS; /

  static unsigned int var##_num; /

  module_param_array(var, short, &var##_num, 0); /

  MODULE_PARM_DESC(var,desc)

#define I2C_CLIENT_MODULE_PARM_FORCE(name)              /

I2C_CLIENT_MODULE_PARM(force_##name,                    /

              "List of adapter,address pairs which are "    /

              "unquestionably assumed to contain a `"      /

              # name "' chip")

#define I2C_CLIENT_INSMOD_COMMON                    /

I2C_CLIENT_MODULE_PARM(probe, "List of adapter,address pairs to scan "  /

              "additionally");                  /

I2C_CLIENT_MODULE_PARM(ignore, "List of adapter,address pairs not to "  /

              "scan");                      /

static const struct i2c_client_address_data addr_data = {      /

    .normal_i2c = normal_i2c,                  /

    .probe      = probe,                    /

    .ignore    = ignore,                  /

    .forces    = forces,                  /

}

#define I2C_CLIENT_FORCE_TEXT /

    "List of adapter,address pairs to boldly assume to be present"

由此可知道,addr_data中的三個成員都是子產品參數.在加載子產品的時候可以用參數的方式對其指派.三個子產品參數為别為probe,ignore,force.另外需要指出的是normal_i2c不能以子產品參數的方式對其指派,隻能在驅動内部靜态指定.

從子產品參數的模述看來, probe是指"List of adapter,address pairs to scan additionally"

Ignore是指"List of adapter,address pairs not to scan "

Force是指"List of adapter,address pairs to boldly assume to be present"

事實上,它們裡面的資料都是成對出現的.前面一部份表示所在的總線号,ANY_I2C_BUS表示任一總線.後一部份表示裝置的位址.

現在可以來跟蹤i2c_probe()的代碼了.如下:

int i2c_probe(struct i2c_adapter *adapter,

          const struct i2c_client_address_data *address_data,

          int (*found_proc) (struct i2c_adapter *, int, int))

{

    int i, err;

    int adap_id = i2c_adapter_id(adapter);

      //先掃描force裡面的資訊,注意它是一個二級指針.ignore裡的資訊對它是無效的

    if (address_data->forces) {

        const unsigned short * const *forces = address_data->forces;

        int kind;

        for (kind = 0; forces[kind]; kind++) {

            for (i = 0; forces[kind] != I2C_CLIENT_END;

                i += 2) {

                if (forces[kind] == adap_id

                || forces[kind] == ANY_I2C_BUS) {

                    dev_dbg(&adapter->dev, "found force "

                        "parameter for adapter %d, "

                        "addr 0x%02x, kind %d/n",

                        adap_id, forces[kind][i + 1],

                        kind);

                    err = i2c_probe_address(adapter,

                        forces[kind][i + 1],

                        kind, found_proc);

                    if (err)

                        return err;

                }

            }

        }

    }

    //如果adapter不支援quick.不能夠周遊這個adapter上面的裝置

    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {

        if (address_data->probe[0] == I2C_CLIENT_END

        && address_data->normal_i2c[0] == I2C_CLIENT_END)

            return 0;

        dev_warn(&adapter->dev, "SMBus Quick command not supported, "

            "can't probe for chips/n");

        return -1;

    }

      //周遊probe上面的資訊.ignore上的資訊也對它是沒有影響的

    for (i = 0; address_data->probe != I2C_CLIENT_END; i += 2) {

        if (address_data->probe == adap_id

        || address_data->probe == ANY_I2C_BUS) {

            dev_dbg(&adapter->dev, "found probe parameter for "

                "adapter %d, addr 0x%02x/n", adap_id,

                address_data->probe[i + 1]);

            err = i2c_probe_address(adapter,

                        address_data->probe[i + 1],

                        -1, found_proc);

            if (err)

                return err;

        }

    }

    //最後周遊normal_i2c上面的資訊.它上面的資訊不能在ignore中.

    for (i = 0; address_data->normal_i2c != I2C_CLIENT_END; i += 1) {

        int j, ignore;

        ignore = 0;

        for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;

            j += 2) {

            if ((address_data->ignore[j] == adap_id ||

                address_data->ignore[j] == ANY_I2C_BUS)

            && address_data->ignore[j + 1]

                == address_data->normal_i2c) {

                dev_dbg(&adapter->dev, "found ignore "

                    "parameter for adapter %d, "

                    "addr 0x%02x/n", adap_id,

                    address_data->ignore[j + 1]);

                ignore = 1;

                break;

            }

        }

        if (ignore)

            continue;

        dev_dbg(&adapter->dev, "found normal entry for adapter %d, "

            "addr 0x%02x/n", adap_id,

            address_data->normal_i2c);

        err = i2c_probe_address(adapter, address_data->normal_i2c,

                    -1, found_proc);

        if (err)

            return err;

    }

    return 0;

}

這段代碼很簡單,結合代碼上面添加的注釋應該很好了解.如果比對成功,則會調用i2c_probe_address ().這個函數代碼如下:

static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,

                int (*found_proc) (struct i2c_adapter *, int, int))

{

    int err;

    //位址小于0x03或者大于0x77都是不合法的

    if (addr < 0x03 || addr > 0x77) {

        dev_warn(&adapter->dev, "Invalid probe address 0x%02x/n",

            addr);

        return -EINVAL;

    }

    //adapter上已經有這個裝置了

    if (i2c_check_addr(adapter, addr))

        return 0;

    //如果kind小于0.檢查adapter上是否有這個裝置

    if (kind < 0) {

        if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,

                  I2C_SMBUS_QUICK, NULL) < 0)

            return 0;

        if ((addr & ~0x0f) == 0x50)

            i2c_smbus_xfer(adapter, addr, 0, 0, 0,

                      I2C_SMBUS_QUICK, NULL);

    }

    //調用回調函數

    err = found_proc(adapter, addr, kind);

    if (err == -ENODEV)

        err = 0;

    if (err)

        dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)/n",

            addr, err);

    return err;

}

首 先,對傳入的參數進行一系列的合法性檢查.另外,如果該adapter上已經有了這個位址的裝置了.也會傳回失敗.所有adapter下面的裝置都是以 adapter->dev為父結點的.是以隻需要周遊adapter->dev下面的子裝置就可以得到目前位址是不是被占用了.

如果kind < 0.還得要adapter檢查該總線是否有這個位址的裝置.方法是向這個位址發送一個Read的Quick請求.如果該位址有應答,則說明這個位址上有這個裝置.另外還有一種情況是在24RF08裝置的特例.

如果adapter上确實有這個裝置,就會調用驅動調用時的回調函數.

在上面涉及到了IIC的傳輸方式,有疑問的可以參考intel ICH5手冊的有關smbus部份.

跟蹤i2c_smbus_xfer().代碼如下:

s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,

                  char read_write, u8 command, int size,

                  union i2c_smbus_data * data)

{

    s32 res;

    flags &= I2C_M_TEN | I2C_CLIENT_PEC;

    if (adapter->algo->smbus_xfer) {

        mutex_lock(&adapter->bus_lock);

        res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,

                                        command,size,data);

        mutex_unlock(&adapter->bus_lock);

    } else

        res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,

                                          command,size,data);

    return res;

}

如果adapter有smbus_xfer()函數,則直接調用它發送,否則,也就是在adapter不支援smbus協定的情況下,調用i2c_smbus_xfer_emulated()繼續處理.

跟進i2c_smbus_xfer_emulated().代碼如下:

static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,

                                  unsigned short flags,

                                  char read_write, u8 command, int size,

                                  union i2c_smbus_data * data)

{

    //寫操作隻會進行一次互動,而讀操作,有時會有兩次操作.

    //因為有時候讀操作要先寫command,再從總線上讀資料

    //在這裡為了代碼的簡潔.使用了兩個緩存區,将兩種情況統一起來.

    unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];

    unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];

    //一般來說,讀操作要互動兩次.例外的情況我們在下面會接着分析

    int num = read_write == I2C_SMBUS_READ?2:1;

    //與裝置互動的資料,一般在msg[0]存放寫入裝置的資訊,在msb[1]裡存放接收到的

    //資訊.不過也有例外的

    //msg[2]的初始化,預設發送緩存區占一個位元組,無接收緩存

    struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },

                              { addr, flags | I2C_M_RD, 0, msgbuf1 }

                            };

    int i;

    u8 partial_pec = 0;

    //将要發送的資訊copy到發送緩存區的第一位元組

    msgbuf0[0] = command;

    switch(size) {

        //quick類型的,其它并不傳輸有效資料,隻是将位址寫到總線上,等待應答即可

        //是以将發送緩存區長度置為0 .再根據讀/寫操作,調整msg[0]的标志位

        //這類傳輸隻需要一次總線互動

    case I2C_SMBUS_QUICK:

        msg[0].len = 0;

        msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;

        num = 1;

        break;

    case I2C_SMBUS_BYTE:

        //BYTE類型指一次寫和讀隻有一個位元組.這種情況下,讀和寫都隻會互動一次

        //這種類型的讀有例外,它讀取出來的資料不是放在msg[1]中的,而是存放在msg[0]

        if (read_write == I2C_SMBUS_READ) {

            msg[0].flags = I2C_M_RD | flags;

            num = 1;

        }

        break;

    case I2C_SMBUS_BYTE_DATA:

        //Byte_Data是指指令+資料的傳輸形式.在這種情況下,寫隻需要一次互動,讀卻要兩次

        //第一次将command寫到總線上,第二次要轉換方向.要将裝置位址和read标志寫入總線.

        //應回答之後再進行read操作

        //寫操作占兩位元組,分别是command+data.讀操作的有效資料隻有一個位元組

        //互動次數用初始化值就可以了

        if (read_write == I2C_SMBUS_READ)

            msg[1].len = 1;

        else {

            msg[0].len = 2;

            msgbuf0[1] = data->byte;

        }

        break;

    case I2C_SMBUS_WORD_DATA:

        //Word_Data是指指令+雙位元組的形式.這種情況跟Byte_Data的情況類似

        //兩者相比隻是互動的資料大小不同

        if (read_write == I2C_SMBUS_READ)

            msg[1].len = 2;

        else {

            msg[0].len=3;

            msgbuf0[1] = data->word & 0xff;

            msgbuf0[2] = data->word >> 8;

        }

        break;

    case I2C_SMBUS_PROC_CALL:

        //Proc_Call的方式與write 的Word_Data相似,隻不過寫完Word_Data之後,要等待它的應答

        //應該它需要互動兩次,一次寫一次讀

        num = 2;

        read_write = I2C_SMBUS_READ;

        msg[0].len = 3;

        msg[1].len = 2;

        msgbuf0[1] = data->word & 0xff;

        msgbuf0[2] = data->word >> 8;

        break;

    case I2C_SMBUS_BLOCK_DATA:

        //Block_Data:指command+N段資料的情況.

        //如果是讀操作,它首先要寫command到總線,然後再讀N段資料.要寫的command已經

        //放在msg[0]了.現在隻需要将msg[1]的标志置I2C_M_RECV_LEN位,msg[1]有效長度為1位元組.因為

        //adapter驅動會處理好的.現在現在還不知道要傳多少段資料.

        //對于寫的情況:msg[1]照例不需要.将要寫的資料全部都放到msb[0]中.相應的也要更新

        //msg[0]中的緩存區長度

        if (read_write == I2C_SMBUS_READ) {

            msg[1].flags |= I2C_M_RECV_LEN;

            msg[1].len = 1;

        } else {

            //data->block[0]表示後面有多少段資料.總長度要加2是因為command+count+N段資料

            msg[0].len = data->block[0] + 2;

            if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {

                dev_err(&adapter->dev, "smbus_access called with "

                      "invalid block write size (%d)/n",

                      data->block[0]);

                return -1;

            }

            for (i = 1; i < msg[0].len; i++)

                msgbuf0 = data->block[i-1];

        }

        break;

    case I2C_SMBUS_BLOCK_PROC_CALL:

        //Proc_Call:表示寫完Block_Data之後,要等它的應答消息它和Block_Data相比,隻是多了一部份應答而已

        num = 2;

        read_write = I2C_SMBUS_READ;

        if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {

            dev_err(&adapter->dev, "%s called with invalid "

                "block proc call size (%d)/n", __func__,

                data->block[0]);

            return -1;

        }

        msg[0].len = data->block[0] + 2;

        for (i = 1; i < msg[0].len; i++)

            msgbuf0 = data->block[i-1];

        msg[1].flags |= I2C_M_RECV_LEN;

        msg[1].len = 1;

        break;

    case I2C_SMBUS_I2C_BLOCK_DATA:

        //I2c Block_Data與Block_Data相似,隻不過read的時候,資料長度是預先定義好了的.另外

        //與Block_Data相比,中間不需要傳輸Count字段.(Count表示資料段數目)

        if (read_write == I2C_SMBUS_READ) {

            msg[1].len = data->block[0];

        } else {

            msg[0].len = data->block[0] + 1;

            if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {

                dev_err(&adapter->dev, "i2c_smbus_xfer_emulated called with "

                      "invalid block write size (%d)/n",

                      data->block[0]);

                return -1;

            }

            for (i = 1; i <= data->block[0]; i++)

                msgbuf0 = data->block;

        }

        break;

    default:

        dev_err(&adapter->dev, "smbus_access called with invalid size (%d)/n",

              size);

        return -1;

    }

    //如果啟用了PEC.Quick和I2c Block_Data是不支援PEC的

    i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK

                      && size != I2C_SMBUS_I2C_BLOCK_DATA);

    if (i) {

        //如果第一個操作是寫操作

        if (!(msg[0].flags & I2C_M_RD)) {

            //如果隻是寫操作

            if (num == 1)

                //如果隻有寫操作,寫緩存區要擴充一個位元組,用來存放計算出來的PEC

                i2c_smbus_add_pec(&msg[0]);

            else

                //如果後面還有讀操作,先計算前面寫部份的PEC(注意這種情況下不需要

                //擴充寫緩存區,因為不需要發送PEC.隻會接收到PEC)

                partial_pec = i2c_smbus_msg_pec(0, &msg[0]);

        }

        //如果最後一次是讀消息.還要接收到來自slave的PEC.是以接收緩存區要擴充一個位元組

        if (msg[num-1].flags & I2C_M_RD)

            msg[num-1].len++;

    }

    if (i2c_transfer(adapter, msg, num) < 0)

        return -1;

    //操作完了之後,如果最後一個操作是PEC的讀操作.檢驗後面的PEC是否正确

    if (i && (msg[num-1].flags & I2C_M_RD)) {

        if (i2c_smbus_check_pec(partial_pec, &msg[num-1]) < 0)

            return -1;

    }

    //操作完了,現在可以将資料放到data部份傳回了.

    if (read_write == I2C_SMBUS_READ)

        switch(size) {

            case I2C_SMBUS_BYTE:

                data->byte = msgbuf0[0];

                break;

            case I2C_SMBUS_BYTE_DATA:

                data->byte = msgbuf1[0];

                break;

            case I2C_SMBUS_WORD_DATA:

            case I2C_SMBUS_PROC_CALL:

                data->word = msgbuf1[0] | (msgbuf1[1] << 8);

                break;

            case I2C_SMBUS_I2C_BLOCK_DATA:

                for (i = 0; i < data->block[0]; i++)

                    data->block[i+1] = msgbuf1;

                break;

            case I2C_SMBUS_BLOCK_DATA:

            case I2C_SMBUS_BLOCK_PROC_CALL:

                for (i = 0; i < msgbuf1[0] + 1; i++)

                    data->block = msgbuf1;

                break;

        }

    return 0;

}

在這個函數添上了很詳細的注釋,配和intel的datasheet,應該很容易看懂.在上面的互動過程中,調用了子函數i2c_transfer().它的代碼如下所示:

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)

{

    int ret;

    if (adap->algo->master_xfer) {

#ifdef DEBUG

        for (ret = 0; ret < num; ret++) {

            dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "

                "len=%d%s/n", ret, (msgs[ret].flags & I2C_M_RD)

                ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,

                (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");

        }

#endif

        if (in_atomic() || irqs_disabled()) {

            ret = mutex_trylock(&adap->bus_lock);

            if (!ret)

                return -EAGAIN;

        } else {

            mutex_lock_nested(&adap->bus_lock, adap->level);

        }

        ret = adap->algo->master_xfer(adap,msgs,num);

        mutex_unlock(&adap->bus_lock);

        return ret;

    } else {

        dev_dbg(&adap->dev, "I2C level transfers not supported/n");

        return -ENOSYS;

    }

}

因為在這裡的同步用的是mutex.首先判斷判斷是否充許睡眠,如果不允許,嘗試獲鎖.如果獲鎖失敗,則傳回,這樣的操作是避免進入睡眠,我們在後面也可以看到,實際的傳輸工作交給了adap->algo->master_xfer()完成.

在 這裡,我們終于把i2c_probe_address()的執行分析完了,經過這個分析,我們也知道了資料是怎麼樣傳輸的.我們接着 i2c_probe()往下看.如果i2c_probe_address()成功.說明總線上确實有這樣的裝置.那麼就會調用驅動中的回調函數.在 ad7148的驅動中,如下所示:

return i2c_probe(adapter, &addr_data, ad7418_detect);

也就是說,要調用的回調函數是ad7418_detect().這個函數中我們隻分析和i2c架構相關的部份.代碼片段如下所示:

static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind)

{

    struct i2c_client *client;

    ……

    ……

client->addr = address;

    client->adapter = adapter;

    client->driver = &ad7418_driver;

    i2c_set_clientdata(client, data);

    ……

    ……

if ((err = i2c_attach_client(client)))

        goto exit_free;

    ……

    ……

}

結 合上面關于new-style形式的驅動分析.發現這裡走的是同一個套路,即初始化了client.然後調用 i2c_attach_client().後面的流程就跟上面分析的一樣了.隻不過,不相同的是,這裡clinet已經指定了驅動為 ad7418_driver.應該在注冊clinet->dev之後,就不會走bus->match和bus->probe的流程了.

七:i2c dev節點操作

現在來分析上面架構圖中的i2c-dev.c中的部份.這個部份為使用者空間提供了操作adapter的接口.這部份代碼其實對應就晃一個子產品.它的初始化函數為:

module_init(i2c_dev_init);

i2c_dev_init()代碼如下:

static int __init i2c_dev_init(void)

{

    int res;

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

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

    if (res)

        goto out;

    i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");

    if (IS_ERR(i2c_dev_class))

        goto out_unreg_chrdev;

    res = i2c_add_driver(&i2cdev_driver);

    if (res)

        goto out_unreg_class;

    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;

}

首先為主冊了一個主裝置号為I2C_MAJOR(89),操作集為i2cdev_fops的字元裝置.然後注冊了一個名為”i2c-dev”的class.之後再注冊了一個i2c的driver.如下所示:[font=Times New Roman]

res = i2c_add_driver(&i2cdev_driver);

    if (res)

        goto out_unreg_class;

i2cdev_driver定義如下:

static struct i2c_driver i2cdev_driver = {

    .driver = {

        .name  = "dev_driver",

    },

    .id    = I2C_DRIVERID_I2CDEV,

    .attach_adapter = i2cdev_attach_adapter,

    .detach_adapter = i2cdev_detach_adapter,

    .detach_client  = i2cdev_detach_client,

};

也就是說,當它注冊或者有新的adapter注冊後,就會它的attach_adapter()函數.該函數代碼如下:

static int i2cdev_attach_adapter(struct i2c_adapter *adap)

{

    struct i2c_dev *i2c_dev;

    int res;

    i2c_dev = get_free_i2c_dev(adap);

    if (IS_ERR(i2c_dev))

        return PTR_ERR(i2c_dev);

    i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,

                    MKDEV(I2C_MAJOR, adap->nr),

                    "i2c-%d", adap->nr);

    if (IS_ERR(i2c_dev->dev)) {

        res = PTR_ERR(i2c_dev->dev);

        goto error;

    }

    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;

}

這 個函數也很簡單,首先調用get_free_i2c_dev()配置設定并初始化了一個struct i2c_dev結構,使i2c_dev->adap指向操作的adapter.之後,該i2c_dev會被鍊傳入連結表i2c_dev_list中.再 分别以I2C_MAJOR, adap->nr為主次裝置号建立了一個device.如果此時系統配置了udev或者是hotplug,那麼就麼在/dev下自動建立相關的裝置 節點了.

剛才我們說過,所有主裝置号為I2C_MAJOR的裝置節點的操作函數是i2cdev_fops.它的定義如下所示:

static const struct file_operations i2cdev_fops = {

    .owner      = THIS_MODULE,

    .llseek    = no_llseek,

    .read      = i2cdev_read,

    .write      = i2cdev_write,

    .ioctl      = i2cdev_ioctl,

    .open      = i2cdev_open,

    .release    = i2cdev_release,

};

7.1:i2c dev的open操作

Open操作對應的函數為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_list連結清單中取得i2c_dev

    i2c_dev = i2c_dev_get_by_minor(minor);

    if (!i2c_dev)

        return -ENODEV;

    //以apapter的總線号從i2c_adapter_idr中找到adapter

    adap = i2c_get_adapter(i2c_dev->adap->nr);

    if (!adap)

        return -ENODEV;

    //配置設定并初始化一個i2c_client結構

    client = kzalloc(sizeof(*client), GFP_KERNEL);

    if (!client) {

        i2c_put_adapter(adap);

        return -ENOMEM;

    }

    snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);

    client->driver = &i2cdev_driver;

    //clinet->adapter指向操作的adapter

    client->adapter = adap;

    //關聯到file

    file->private_data = client;

    return 0;

}

注 意這裡配置設定并初始化了一個struct i2c_client結構.但是沒有注冊這個clinet.此外,這個函數中還有一個比較奇怪的操作.不是在前面已經将i2c_dev->adap 指向要操作的adapter麼?為什麼還要以adapter->nr為關鍵字從i2c_adapter_idr去找這個操作的adapter呢?注 意了,調用i2c_get_adapter()從總線号nr找到操作的adapter的時候,還會增加module的引用計數.這樣可以防止子產品意外被釋 放掉.也許有人會有這樣的疑問,那 i2c_dev->adap->nr操作,如果i2c_dev->adap被釋放掉的話,不是一樣會引起系統崩潰麼?這裡因為,在 i2cdev_attach_adapter()間接的增加了一次adapter的一次引用計數.如下:

tatic int i2cdev_attach_adapter(struct i2c_adapter *adap)

{

......

i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,

                    MKDEV(I2C_MAJOR, adap->nr),

                    "i2c-%d", adap->nr);

......

}

看到了麼,i2c_dev内嵌的device是以adap->dev為父結點,在device_create()中會增次adap->dev的一次引用計數.

好了,open()操作到此就完成了.

7.2:read操作

Read操作對應的操作函數如下示:

static ssize_t i2cdev_read (struct file *file, char __user *buf, size_t count,

                            loff_t *offset)

{

    char *tmp;

    int ret;

    struct i2c_client *client = (struct i2c_client *)file->private_data;

    if (count > 8192)

        count = 8192;

    tmp = kmalloc(count,GFP_KERNEL);

    if (tmp==NULL)

        return -ENOMEM;

    pr_debug("i2c-dev: i2c-%d reading %zd bytes./n",

        iminor(file->f_path.dentry->d_inode), count);

    ret = i2c_master_recv(client,tmp,count);

    if (ret >= 0)

        ret = copy_to_user(buf,tmp,count)?-EFAULT:ret;

    kfree(tmp);

    return ret;

}

首先從file結構中取得struct i2c_clinet.然後在kernel同配置設定相同長度的緩存區,随之調用i2c_master_recv()從裝置中讀取資料.再将讀取出來的資料copy到使用者空間中.

I2c_master_recv()代碼如下:

int i2c_master_recv(struct i2c_client *client, char *buf ,int count)

{

    struct i2c_adapter *adap=client->adapter;

    struct i2c_msg msg;

    int ret;

    msg.addr = client->addr;

    msg.flags = client->flags & I2C_M_TEN;

    msg.flags |= I2C_M_RD;

    msg.len = count;

    msg.buf = buf;

    ret = i2c_transfer(adap, &msg, 1);

    return (ret == 1) ? count : ret;

}

看 完前面的代碼之後,這個函數應該很簡單了,就是為讀操作初始化了一個i2c_msg.然後調用i2c_tanster().代碼中的 client->flags & I2C_M_TEN表示adapter是否采用10位尋址的方式.在這裡就不再詳細分析了.

另 外,有人可能看出了一個問題.這裡clinet->addr是從哪來的呢?對,在read之前應該還要有一步操作來設定 clinet->addr的值.這個過程是ioctl的操作.ioctl可以設定PEC标志,重試次數,逾時時間,和發送接收資料等,我們在這裡隻 看一下clinet->addr的設定.代碼片段如下示:

static int i2cdev_ioctl(struct inode *inode, struct file *file,

        unsigned int cmd, unsigned long arg)

{

    ......

    ......

    switch ( cmd ) {

    case I2C_SLAVE:

    case I2C_SLAVE_FORCE:

        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;

        client->addr = arg;

        return 0;

    ......

    ......

}

由 此可見,調用I2C_SLAVE或者I2C_SLAVE_FORCE的Ioctl就會設定clinet->addr.另外,注釋中也說得很清楚了. 如果是I2C_SLAVE的話,還會調用其所長i2cdev_check_addr().進行位址檢查,如果adapter已經關聯到這個位址的裝置,就 會檢查失敗.

7.2:write操作

Write操作如下所示:

static ssize_t i2cdev_write (struct file *file, const char __user *buf, size_t count,

                            loff_t *offset)

{

    int ret;

    char *tmp;

    struct i2c_client *client = (struct i2c_client *)file->private_data;

    if (count > 8192)

        count = 8192;

    tmp = kmalloc(count,GFP_KERNEL);

    if (tmp==NULL)

        return -ENOMEM;

    if (copy_from_user(tmp,buf,count)) {

        kfree(tmp);

        return -EFAULT;

    }

    pr_debug("i2c-dev: i2c-%d writing %zd bytes./n",

        iminor(file->f_path.dentry->d_inode), count);

    ret = i2c_master_send(client,tmp,count);

    kfree(tmp);

    return ret;

}

該操作比較簡單,就是将使用者空間的資料發送到i2c 裝置.

八:小結

在本節中,分析了i2c的架構設計.這個架構大體上沿用了Linux的裝置驅動架構,不過之中又做了很多變通.在之後的分析中,會分别舉一個adapter和i2c device的例子來較長的描述一下有關i2c driver的設計.

繼續閱讀