I2C从驱动到应用(中篇)
Linux中對I2C的支持非常全面,既提供了內(nèi)核態(tài)的訪問方式,也提供了用戶態(tài)的訪問方法。
Linux中對I2C的支持可以分為兩個層面,一個是adapter和algorithm,對應(yīng)的是i2c控制器;再一個是driver和client.Linux內(nèi)核提供了豐富的接口來實添加i2c設(shè)備驅(qū)動。要添加一個i2c設(shè)備驅(qū)動,需要幾個固定的步驟。首先,需要往i2c設(shè)備列表里添加一組設(shè)備ID foo_idtable:
static struct i2c_device_id foo_idtable[] = {
{ "foo", my_id_for_foo },
{ "bar", my_id_for_bar },
{ }
};
MODULE_DEVICE_TABLE(i2c, foo_idtable);
然后填充i2c driver的數(shù)據(jù)結(jié)構(gòu):
static struct i2c_driver foo_driver = {
.driver = {
.name ? = "foo",
.pm = &foo_pm_ops, ?/* optional */
},
.id_table ? = foo_idtable,
.probe ? ? ?= foo_probe,
.remove ? ? = foo_remove,
/* if device autodetection is needed: */
.class ? ? ?= I2C_CLASS_SOMETHING,
.detect ? ? = foo_detect,
.address_list ?= normal_i2c,
.shutdown ? = foo_shutdown, /* optional */
.command ? ?= foo_command, ?/* optional, deprecated */
}
剩下的就是逐一初始化foo_probe()、foo_remove()、foo_detect()等函數(shù)。一旦準備好了相應(yīng)的client結(jié)構(gòu),就可以實現(xiàn)foo_read_valule()和foo_write_value()函數(shù),當(dāng)然這兩者都是基于底層公共的i2c/smbus讀寫函數(shù):i2c_smbus_read/write_byte_data/word()去實現(xiàn)。
如果確實有i2c設(shè)備掛載在某個i2c總線上,可以通過填充i2c_board_info數(shù)據(jù)結(jié)構(gòu)來構(gòu)造這個設(shè)備的實例,然后調(diào)用i2c_new_device()來探測實際接入的i2c設(shè)備。當(dāng)然也可能事先無法確認系統(tǒng)上有什么類型的i2c設(shè)備,這個時候需要定義一個call back函數(shù),放在probe()后面,讓它去確認是否有指定類型的i2c設(shè)備掛在系統(tǒng)上。使用完設(shè)備后,可以調(diào)用i2c_unregister_device()來注銷之前注冊的設(shè)備。當(dāng)然執(zhí)行這些所有操作的前提是設(shè)備對應(yīng)的驅(qū)動已經(jīng)被初始化好并且加載到內(nèi)核,可以參考下面的代碼來實現(xiàn)初始化和退出操作:
static int __init foo_init(void)
{ ?
return i2c_add_driver(&foo_driver);
}
module_init(foo_init);
static void __exit foo_cleanup(void)
{ ?
i2c_del_driver(&foo_driver);
}
module_exit(foo_cleanup);
The module_i2c_driver() macro can be used to reduce above code.
module_i2c_driver(foo_driver);
如果驅(qū)動中,需要向設(shè)備發(fā)送或者從設(shè)備接受數(shù)據(jù),可以調(diào)用:
int i2c_master_send(struct i2c_client *client, const char *buf, int count);
int i2c_master_recv(struct i2c_client *client, char *buf, int count);
這兩組函數(shù)來實現(xiàn),更多的函數(shù)在linux/i2c.h中有說明。
當(dāng)然linux內(nèi)核中也提供了設(shè)備作為從設(shè)備的驅(qū)動,這中情況下的系統(tǒng)層次圖如下所示:
e.g. sysfs ? ? ?I2C slave events ? ? ? ?I/O registers
+-----------+ ?v ? ?+---------+ ? ? v ? ? +--------+ ?v ?+------------+
| Userspace +........+ Backend +-----------+ Driver +-----+ Controller |
+-----------+ ? ? ? +---------+ ? ? ? ? ? +--------+ ? ? +------------+
| |
----------------------------------------------------------------+-- I2C
--------------------------------------------------------------+---- Bus
不同于PCI/USB設(shè)備,I2C沒有提供硬件上自動枚舉的能力,因此在初始化i2c設(shè)備之前需要顯式地指定設(shè)備的地址。內(nèi)核提供了多個顯式初始化一個i2c設(shè)備的方法:
1、通過bus number聲明一個i2c設(shè)備:
這種情況尤其適用于i2c作為系統(tǒng)總線的嵌入式系統(tǒng),以omp2 h4為例,用戶需要注冊i2c board info:
Example (from omap2 h4):
static struct i2c_board_info h4_i2c_board_info[] __initdata = {
{ ?
I2C_BOARD_INFO("isp1301_omap", 0x2d),
.irq ? ? ? ?= OMAP_GPIO_IRQ(125),
},
{ ?/* EEPROM on mainboard */
I2C_BOARD_INFO("24c01", 0x52),
.platform_data = &m24c01,
},
{ ?/* EEPROM on cpu card */
I2C_BOARD_INFO("24c01", 0x57),
.platform_data = &m24c01,
},
};
static void __init omap_h4_init(void)
{
(...)
i2c_register_board_info(1, h4_i2c_board_info,
ARRAY_SIZE(h4_i2c_board_info));
(...)
}
2、通過設(shè)備樹申明一個i2c設(shè)備:
這種方式需要把心建的設(shè)備節(jié)點掛在master conrollor對應(yīng)的設(shè)備樹上,也就是說它必須是master controller的子節(jié)點,如下面的例子所示:
i2c1: i2c@400a0000 {
/* ... master properties skipped ... */
clock-frequency = <100000>;
flash@50 {
compatible = "atmel,24c256";
reg = <0x50>;
};
pca9532: gpio@60 {
compatible = "nxp,pca9532";
gpio-controller;
#gpio-cells = <2>;
reg = <0x60>;
};
};
3、通過ACPI申明一個I2C設(shè)備:
ACPI表里能夠申明I2C設(shè)備:Documentation/acpi/enumeration.txt。
4、顯式地實例化i2c設(shè)備:
這也是通過填充i2c_board_info數(shù)據(jù)結(jié)構(gòu),然后調(diào)用i2c_new_device()實現(xiàn)的,如下面的代碼所示:
static struct i2c_board_info sfe4001_hwmon_info = {
I2C_BOARD_INFO("max6647", 0x4e),
};
int sfe4001_init(struct efx_nic *efx)
{
(...)
efx->board_info.hwmon_client =
i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);
(...)
}
上面的代碼在i2c bus上實例化了一個i2c設(shè)備。
5、對一些特殊的設(shè)備進行自動探測
有的系統(tǒng)上并沒有提供足夠多的關(guān)于i2c設(shè)備和拓撲信息,這個時候就需要依賴i2c-core去探測設(shè)備,這就要求:
-
-
i2c設(shè)備驅(qū)動必須實現(xiàn)detect()函數(shù);
-
只有掛載了這種設(shè)備的i2c總線能夠且允許被探測。
6、利用/sysfs通過用戶態(tài)初始化i2c設(shè)備
比如我們需要把位于i2c地址0x50的eerom添加到設(shè)備當(dāng)中去,可以直接操作sysfs實現(xiàn):
echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
本文轉(zhuǎn)自存儲之廚51CTO博客,原文鏈接:http://blog.51cto.com/xiamachao/1704679?,如需轉(zhuǎn)載請自行聯(lián)系原作者
總結(jié)
以上是生活随笔為你收集整理的I2C从驱动到应用(中篇)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vsftpd环境下的创建本地yum源镜像
- 下一篇: QString::QString 中文乱