正文:


    Linux下的設備驅動,顧名思義,就是由設備和驅動來組成的,i2c總線上的設備驅動也不例外。


一、i2c設備的註冊


第一步:
    記得以前的i2c設備驅動,設備部分喜歡驅動運行的時候動態建立,新式的驅動傾向於向傳統的linux下設備驅動看齊,採用靜態定義的方式來註冊設備,使用介面為:
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);//申請表示i2c設備的結構體空間
        if (!devinfo) {
            pr_debug("i2c-core: can't register boardinfo!\n");
            status = -ENOMEM;
            break;
        }
        /* 填寫i2c設備描述結構*/
        devinfo->busnum = busnum;
        devinfo->board_info = *info;
        list_add_tail(&devinfo->list, &__i2c_board_list);//添加到全域鍊錶__i2c_board_list中
    }


    mutex_unlock(&__i2c_board_lock);


    return status;
}


第二步:
    系統初始化的時候,會根據板級i2c設備配置信息,建立i2c客戶端設備(i2c_client),添加到i2c子系統中:
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
............................................


//篇幅問題 請自行查看原始碼


}


struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
............................................


//篇幅問題 請自行查看原始碼


}
int i2c_attach_client(struct i2c_client *client)
{
............................................


//篇幅問題 請自行查看原始碼


}


注:
    特別要提一下的是這個“i2c_check_addr”,引用<<i2c 原始碼情景分析>>裡的話:“i2c 設備的7 位地址是就當前i2c 總線而言的,是“相對地址”。不同的i2c 總線上的設備可以使用相同的7 位地址,但是它們所在的i2c 總線不同。所以在系統中一個i2c 設備的“絕對地址”由二元組(i2c 適配器的ID 和設備在該總線上的7 位地址)表示。”,所以這個函數的作用主要是排除同一i2c總線上出現多個地址相同的設備。


二、驅動的註冊:


第一步:


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)
{
............................................


//篇幅問題 請自行查看原始碼



    /* new style driver methods can't mix with legacy ones */
    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;
        }
    }


............................................


//篇幅問題 請自行查看原始碼


    res = driver_register(&driver->driver); //註冊驅動
    if (res)
        return res;
............................................


//篇幅問題 請自行查看原始碼


}


第二步:
    還記得在第一部分,我們註冊了一i2c設備,其總線類型為 i2c_bus_type ,現在我們又在這總線上註冊一驅動,那不得了,滿足了設備+驅動的條件, i2c_bus_type 總線上探測函數要被執行了。


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,
};


static int i2c_device_probe(struct device *dev)
{
............................................


//篇幅問題 請自行查看原始碼


    status = driver->probe(client, i2c_match_id(driver->id_table, client));//呼叫具體i2c設備驅動的探測函數
    if (status)
        client->driver = NULL;
    return status;
}


    OK,剩下的就是具體i2c設備驅動的功夫了,在此不作深入研究。


int i2c_attach_client(struct i2c_client *client)
{
............................................


//篇幅問題 請自行查看原始碼
}


注:
    特別要提一下的是這個“i2c_check_addr”,引用<<i2c 原始碼情景分析>>裡的話:“i2c 設備的7 位地址是就當前i2c 總線而言的,是“相對地址”。不同的i2c 總線上的設備可以使用相同的7 位地址,但是它們所在的i2c 總線不同。所以在系統中一個i2c 設備的“絕對地址”由二元組(i2c 適配器的ID 和設備在該總線上的7 位地址)表示。”,所以這個函數的作用主要是排除同一i2c總線上出現多個地址相同的設備。


二、驅動的註冊:


第一步:


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)
{
............................................


//篇幅問題 請自行查看原始碼


    /* new style driver methods can't mix with legacy ones */
    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 */
    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.
     */
    res = driver_register(&driver->driver); //註冊驅動
    if (res)
        return res;
............................................


//篇幅問題 請自行查看原始碼
}


第二步:
    還記得在第一部分,我們註冊了一i2c設備,其總線類型為 i2c_bus_type ,現在我們又在這總線上註冊一驅動,那不得了,滿足了設備+驅動的條件, i2c_bus_type 總線上探測函數要被執行了。


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,
};


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);
    int status;


    if (!driver->probe || !driver->id_table)
        return -ENODEV;
    client->driver = driver;
    if (!device_can_wakeup(&client->dev))
        device_init_wakeup(&client->dev,
                    client->flags & I2C_CLIENT_WAKE);
    dev_dbg(dev, "probe\n");


    status = driver->probe(client, i2c_match_id(driver->id_table, client));//呼叫具體i2c設備驅動的探測函數
    if (status)
        client->driver = NULL;
    return status;
}


    OK,剩下的就是具體i2c設備驅動的功夫了,在此不作深入研究。


 


lfc 2009-10-27 17:54 發表評論
原文地址: http://www.cnitblog.com/luofuchong/archive/2009/10/27/62162.html


 

arrow
arrow
    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()