close
來源:

Ref:
*

Elay:  以下節錄、整理 new style 部分


linux中I2C Driver 架構:
 

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

這個圖只是提供了一個大概的框架,在下面的代碼分析中,從下至上的來分析這個框架圖。以下的代碼分析是基於linux 2.6.26,source code 在: linux-2.6.26.3/drivers/i2c/。


I2C 有兩種類型的 driver,一種是new-style drivers,另外一種是legacy drivers。New-style drivers是在2.6 最近版本才加入的,最主要的區別是在adapter和i2c driver的比對方式.


new-style 形式的adapter註冊

new-style drivers 相關code 如下:

    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() 所分得的bus number 肯定不會小於 __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);
     /* dynamic bus numbers will be assigned after the last static one */
    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裝置的一些情況,比如所在的Bus、名稱、地址、IRQ .. 等。這些資訊會被存放到 __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 bus上,那麼就會呼叫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));
 
    /* a new style driver may be bound to this device when we
     * return from this function, or any later moment (e.g. maybe
     * hotplugging will load the driver module).  and the device
     * refcount model is the standard driver model one.
     */
    status = i2c_attach_client(client);
    if (status < 0) {
        kfree(client);
        client = NULL;
    }
    return client;
}

這裡又遇到一個新的結構: struct i2c_client,其實就只是用來包裝嵌入struct device 的 I2C 裝置,和之前遇到的 struct usb_device 結構的作用是一樣的。
首先,在client 裡保存該裝置相關資訊,其中 client->adapter 指向了它所在的adapter、client->name 為 info->name。

初始化完成後會呼叫i2c_attach_client( ),看這個函數的字面意思,是將client 關聯起來:

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()嗎? 怎麼沒看到它的關係過程呢?
這是因為在code裡已經設定 client->dev 所在的bus為 i2c_bus_type ,所以只需要有bus為i2c_bus_type的driver註冊,就會產生probe。這個過程等後面分析i2c driver的時候再來詳細分析。

四: 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);
}

繼續trace:

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    int res;
 
    /* new style driver methods can't mix with legacy ones */
    //如果是一個new style 的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;
        }
    }
 
    /* add the driver to the list of i2c drivers in the driver core */
    //關聯到i2c_bus_types
    driver->driver.owner = owner;
    driver->driver.bus = &i2c_bus_type;
 
    /* for new style drivers, when registration returns the driver core
     * will have called probe() for all matching-but-unbound devices.
     */
    //註冊內嵌的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);
 
    /* legacy drivers scan i2c busses directly */
    //列舉所有的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 一起考慮:
- 如果是news style形式,在註冊adapter時將它之上的i2c 裝置轉換成了struct client.struct,而 client->dev->bus 又指定了和 i2c driver 同一個bus,因此可發生probe。
- 如果是legacy形式,直接找到對應的對象,呼叫driver->attach_adapter()。
 
五: i2c_bus_type的相關操作

I2c_bus_type 的操作主要使用於 new-style 形式的driver中,接下來分析一下對應的 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);
 
    /* make legacy i2c drivers bypass driver model probing entirely;
     * such drivers scan each i2c adapter/bus themselves.
     */
    if (!is_newstyle_driver(driver))
        return 0;
 
    /* match on an id table if there is one */
    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()
 
arrow
arrow
    全站熱搜

    huenlil 發表在 痞客邦 留言(0) 人氣()