简介
这篇文章是针对 Documentation/i2c/instantiating-devices 的解释,部分增加自己的理解。为什么要将它解释下呢?因为他很好的说明了 linux3.4.xxx
内核中实例化设备的流程和方法。
具体内容
1. 根据 bus 号声明 i2c 的设备们
每一个i2c 总线的号之前知道,因此可以提前声明 i2c 设备们,方法是通过声明一个 struct i2c_borad_info结构体,并用 i2c_register_board_info() 注册。
例子(来自 omap2 h4):
static struct i2c_board_info __initdata h4_i2c_board_info[] = {
{
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));
(...)
}
上边的代码声明了bus1上的3个设备,包括他们各自的地址和他们私有数据。当被提到的i2c bus注册,i2c-core会自动实例化i2c device。
当i2c_bus被移除时,i2c_device也会unbond。
这里可以看到,这种方法一般用在 平台文件 中
2. 明确的实例化一个设备:
提前不知道i2c bus 号,用此方法。此方法通过 i2c的 adapter 创建设备。
例子 (来自 the sfe4001 network driver):
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 设备,这个i2c设备挂载 i2c bus ,bus上对应相应的adapter。根据的是adapter来创建的设备。
例子:
at24cxx_dev.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>
static struct i2c_board_info at24cxx_info = {
I2C_BOARD_INFO("at24c08", 0x50),
};
static struct i2c_client *at24cxx_client;
static int at24cxx_dev_init(void)
{
struct i2c_adapter *i2c_adap;
i2c_adap = i2c_get_adapter(0); // 0对应的是 dev/i2c-0
at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info);
i2c_put_adapter(i2c_adap);
return 0;
}
static void at24cxx_dev_exit(void)
{
i2c_unregister_device(at24cxx_client);
}
module_init(at24cxx_dev_init);
module_exit(at24cxx_dev_exit);
MODULE_LICENSE("GPL");
at24cxx_drv.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>
static int __devinit at24cxx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int __devexit at24cxx_remove(struct i2c_client *client)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static const struct i2c_device_id at24cxx_id_table[] = {
{ "at24c08", 0 },
{}
};
/* 1. ·ÖÅä/ÉèÖÃi2c_driver */
static struct i2c_driver at24cxx_driver = {
.driver = {
.name = "100ask",
.owner = THIS_MODULE,
},
.probe = at24cxx_probe,
.remove = __devexit_p(at24cxx_remove),
.id_table = at24cxx_id_table,
};
static int at24cxx_drv_init(void)
{
/* 2. ×¢²ái2c_driver */
i2c_add_driver(&at24cxx_driver);
return 0;
}
static void at24cxx_drv_exit(void)
{
i2c_del_driver(&at24cxx_driver);
}
module_init(at24cxx_drv_init);
module_exit(at24cxx_drv_exit);
MODULE_LICENSE("GPL");
Makefile
KERN_DIR = /work/system/linux-3.4.2
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += at24cxx_dev.o
obj-m += at24cxx_drv.o
一个变体,当你不知道一个设备是不是挂在总线上,或者地址发生了变化,可以用 i2c_new_probed_device()
代替 i2c_new_device()
Example (from the nxp OHCI driver):
static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev)
{
(...)
struct i2c_adapter *i2c_adap;
struct i2c_board_info i2c_info;
(...)
i2c_adap = i2c_get_adapter(2);
memset(&i2c_info, 0, sizeof(struct i2c_board_info));
strlcpy(i2c_info.type, "isp1301_nxp", I2C_NAME_SIZE);
isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
normal_i2c, NULL);
i2c_put_adapter(i2c_adap);
(...)
}
上边的代码实例化一个在 OHCI adapter上的i2c bus上的一个设备。他首先尝试地址 0x2c,如果没有回复,则尝试0x2d,如果都没发现,放弃。
例子:
at24cxx_dev.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>
static struct i2c_client *at24cxx_client;
static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };
static int at24cxx_dev_init(void)
{
struct i2c_adapter *i2c_adap;
struct i2c_board_info at24cxx_info;
memset(&at24cxx_info, 0, sizeof(struct i2c_board_info));
strlcpy(at24cxx_info.type, "at24c08", I2C_NAME_SIZE);
i2c_adap = i2c_get_adapter(0);
at24cxx_client = i2c_new_probed_device(i2c_adap, &at24cxx_info, addr_list, NULL);
i2c_put_adapter(i2c_adap);
if (at24cxx_client)
return 0;
else
return -ENODEV;
}
static void at24cxx_dev_exit(void)
{
i2c_unregister_device(at24cxx_client);
}
module_init(at24cxx_dev_init);
module_exit(at24cxx_dev_exit);
MODULE_LICENSE("GPL");
at24cxx_drv.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>
static int __devinit at24cxx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int __devexit at24cxx_remove(struct i2c_client *client)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static const struct i2c_device_id at24cxx_id_table[] = {
{ "at24c08", 0 },
{}
};
static struct i2c_driver at24cxx_driver = {
.driver = {
.name = "100ask", // 匹配不用这个
.owner = THIS_MODULE,
},
.probe = at24cxx_probe,
.remove = __devexit_p(at24cxx_remove),
.id_table = at24cxx_id_table,
};
static int at24cxx_drv_init(void)
{
i2c_add_driver(&at24cxx_driver);
return 0;
}
static void at24cxx_drv_exit(void)
{
i2c_del_driver(&at24cxx_driver);
}
module_init(at24cxx_drv_init);
module_exit(at24cxx_drv_exit);
MODULE_LICENSE("GPL");
Makefile
KERN_DIR = /work/system/linux-3.4.2
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += at24cxx_dev.o
obj-m += at24cxx_drv.o
#obj-m += i2c_bus_s3c2440.o
3. Probe一个针对于每一个设备的 i2c bus
有时,没有 i2c device 的足够的信息,甚至不能调用 i2c_new_probed_device(),典型的事例是 PC 主板上的硬件监控芯片。有很多模型,这些模型可以有25个不同的地址。考虑到有大量的主板,要构建一个详尽的硬件列表几乎是不可能的,幸运的是,大部分这类芯片都有 manufacturer (制造商)和 device ID(设备 ID)寄存器,以供我们可以识别。
既然那样,I2C devices即不声明,也不明确实例化。相反的,一旦 i2c driver 加载,i2c-core将探测这些设备,如果找到,I2C device 会自动实例化。为了阻止这个机制中的一些错误情况,有一下 的一些限制:
- i2c 的设备驱动必须实现 detect() 方法,用这个方法来读设备的专有寄存器。
- 只有可能含有支持设备,并同意 probe 的 bus 们才被用来 probe。
Example:
See lm90_driver and lm90_detect() in drivers/hwmon/lm90.c
当成功 probe 到 i2c device会自动实例化,当驱动检测到他们被移除了,device自动销毁,或者依赖的 i2c bus 自己销毁了。
再一次,方法3尽量避免使用,方法1和2更安全更快。
4. 从用户空间实例化
通常,内核应该知道哪些设备连接和他们的地址。然而,在某些情况,却不知道,所以一个 sysfs 接口被添加来让用户提供信息。这个接口由2个属性文件组成,new_device 和 delete_device ,两个文件都是只写的,你必须写正确的参数以实例化或者删除 i2c device
文件 new_device 需要2个参数:i2c设备的名字(字符串),一个i2c设备的地址(不标记是10进制,0x开头是16进制)
文件 delete_device 需要一个参数:地址
Example:
# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
然而这个接口应该在kernel 设备声明了,但是没有成功执行的时候使用,有一下各种各样情况可能这个接口有帮助:
- 用方法 4.3 detects 设备,但是你设备依赖的 bus segment 没有合适的类的位的设置,这样检测不能被触发
- i2c 驱动通常 detects 设备,但是地址设置的不对。
- i2c 驱动通常 detects 设备,但是你的设备因为检测程序太严格,而没有检测到,或者你的设备还不是官方支持的,但是你知道,他能够使用。
- 你在开发一个驱动在一个测试的板子上。