I2C从驱动到应用(中篇)
Linux中對I2C的支持非常全面,既提供了內核態的訪問方式,也提供了用戶態的訪問方法。
Linux中對I2C的支持可以分為兩個層面,一個是adapter和algorithm,對應的是i2c控制器;再一個是driver和client.Linux內核提供了豐富的接口來實添加i2c設備驅動。要添加一個i2c設備驅動,需要幾個固定的步驟。首先,需要往i2c設備列表里添加一組設備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的數據結構:
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()等函數。一旦準備好了相應的client結構,就可以實現foo_read_valule()和foo_write_value()函數,當然這兩者都是基于底層公共的i2c/smbus讀寫函數:i2c_smbus_read/write_byte_data/word()去實現。
如果確實有i2c設備掛載在某個i2c總線上,可以通過填充i2c_board_info數據結構來構造這個設備的實例,然后調用i2c_new_device()來探測實際接入的i2c設備。當然也可能事先無法確認系統上有什么類型的i2c設備,這個時候需要定義一個call back函數,放在probe()后面,讓它去確認是否有指定類型的i2c設備掛在系統上。使用完設備后,可以調用i2c_unregister_device()來注銷之前注冊的設備。當然執行這些所有操作的前提是設備對應的驅動已經被初始化好并且加載到內核,可以參考下面的代碼來實現初始化和退出操作:
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);
如果驅動中,需要向設備發送或者從設備接受數據,可以調用:
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);
這兩組函數來實現,更多的函數在linux/i2c.h中有說明。
當然linux內核中也提供了設備作為從設備的驅動,這中情況下的系統層次圖如下所示:
e.g. sysfs ? ? ?I2C slave events ? ? ? ?I/O registers
+-----------+ ?v ? ?+---------+ ? ? v ? ? +--------+ ?v ?+------------+
| Userspace +........+ Backend +-----------+ Driver +-----+ Controller |
+-----------+ ? ? ? +---------+ ? ? ? ? ? +--------+ ? ? +------------+
| |
----------------------------------------------------------------+-- I2C
--------------------------------------------------------------+---- Bus
不同于PCI/USB設備,I2C沒有提供硬件上自動枚舉的能力,因此在初始化i2c設備之前需要顯式地指定設備的地址。內核提供了多個顯式初始化一個i2c設備的方法:
1、通過bus number聲明一個i2c設備:
這種情況尤其適用于i2c作為系統總線的嵌入式系統,以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、通過設備樹申明一個i2c設備:
這種方式需要把心建的設備節點掛在master conrollor對應的設備樹上,也就是說它必須是master controller的子節點,如下面的例子所示:
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設備:
ACPI表里能夠申明I2C設備:Documentation/acpi/enumeration.txt。
4、顯式地實例化i2c設備:
這也是通過填充i2c_board_info數據結構,然后調用i2c_new_device()實現的,如下面的代碼所示:
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設備。
5、對一些特殊的設備進行自動探測
有的系統上并沒有提供足夠多的關于i2c設備和拓撲信息,這個時候就需要依賴i2c-core去探測設備,這就要求:
-
-
i2c設備驅動必須實現detect()函數;
-
只有掛載了這種設備的i2c總線能夠且允許被探測。
6、利用/sysfs通過用戶態初始化i2c設備
比如我們需要把位于i2c地址0x50的eerom添加到設備當中去,可以直接操作sysfs實現:
echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
本文轉自存儲之廚51CTO博客,原文鏈接:http://blog.51cto.com/xiamachao/1704679?,如需轉載請自行聯系原作者
總結
以上是生活随笔為你收集整理的I2C从驱动到应用(中篇)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vsftpd环境下的创建本地yum源镜像
- 下一篇: QString::QString 中文乱