正文:
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
留言列表