I2C设备的4种添加方式
分为静态注册、动态注册、用户空间注册及i2c驱动扫描注册
1.静态注册
静态注册就是在构架板级文件或初始化文件中添加i2c设备信息,并注册到特定位置(__i2c_board_list数组)上就可以了,如arm构架下board-xxx-yyy.c文件linux平台总线,x86构架下xxx-yyy-init-zzz.c文件。当系统静态注册i2c控制器(adapter)时,将会去查找这个数组,并实例化i2c设备添加到i2c总线上。注意:一定要赶在i2c控制器注册前将i2c设备信息添加到数组上。
1)定义一个i2c_board_info结构体
必需要有名子和设备地址,其他如中断号、私有数据非必须。
static struct i2c_board_info my_tmp75_info = {
I2C_BOARD_INFO("my_tmp75", 0x48),
};
@my_tmp75是设备名字,用于匹配i2c驱动。
@0x48是i2c设备的基地址。
倘若有多个设备,可以定义成结构链表,一次添加多个设备信息。
2)注册设备
使用i2c_register_board_info函数将i2c设备信息添加到特定数组,函数原型如下
i2c_register_board_info(int busnum, struct i2c_board_info const * info, unsigned n)
{
devinfo->busnum = busnum; /* 组装i2c总线 */
devinfo->board_info = *info; /* 绑定设备信息 */
list_add_tail(&devinfo->list, &__i2c_board_list); /* 将设备信息添加进链表中 */
}
@busnum:哪一条总线,也就是选择哪一个i2c控制器(adapter)
@info:i2c设备信息,就是上面的结构体
@n:info中有几个设备
将在i2c_register_adapter函数中使用到
static int i2c_register_adapter(struct i2c_adapter *adap)
{
…
if (adap->nr busnum == adapter->nr && !i2c_new_device(adapter, &devinfo->board_info))
dev_err(&adapter->dev,"Can't create device at 0xxn",devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}
而调用i2c_register_adapter函数的有两个地方linux系统装win7,分别是i2c_add_adapter函数和i2c_add_numbered_adapter函数,但i2c_add_adapter函数中是动态分配的总线号,adap->nr一定比__i2c_first_dynamic_bus_num变量大,因而不会步入到i2c_scan_static_board_info函数,所以只有i2c_add_numbered_adapter最终使用到,而这个函数是i2c控制器静态注册时调用的,因而静态注册i2c设备必须赶在i2c控制器注册前添加。
2.动态注册
动态注册i2c设备可以使用两个函数,分别为i2c_new_device函数与i2c_new_probed_device函数linux是什么系统,它们两区别是:
1)使用i2c_new_device注册设备
#include
#include
#include
#include
static struct i2c_board_info my_tmp75_info = {
I2C_BOARD_INFO("my_tmp75", 0x48),//这个名字很重要,用于匹配I2C驱动
};
static struct i2c_client *my_tmp75_client;
static int my_tmp75_init(void)
{
struct i2c_adapter *i2c_adapt;
int ret = 0;
i2c_adapt = i2c_get_adapter(6);
if (i2c_adapt == NULL)
{
printk("get adapter fail!n");
ret = -ENODEV;
}
my_tmp75_client = i2c_new_device(i2c_adapt, &my_tmp75_info);
if (my_tmp75_client == NULL)
{
printk("i2c new fail!n");
ret = -ENODEV;
}
i2c_put_adapter(i2c_adapt);
return ret;
}
static void my_tmp75_exit(void)
{
i2c_unregister_device(my_tmp75_client);
}
module_init(my_tmp75_init);
module_exit(my_tmp75_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("caodongwang");
MODULE_DESCRIPTION("This my i2c device for tmp75");
2)使用i2c_new_probed_device注册设备
#include
#include
#include
#include
static struct i2c_client *my_tmp75_client;
static const unsigned short addr_list[] = { 0x46, 0x48, I2C_CLIENT_END };//必须以I2C_CLIENT_END宏结尾
static int my_i2c_dev_init(void)
{
struct i2c_adapter *i2c_adap;
struct i2c_board_info my_i2c_dev_info;
memset(&my_i2c_dev_info, 0, sizeof(struct i2c_board_info));
strlcpy(my_i2c_dev_info.type, "my_tmp75", I2C_NAME_SIZE);
i2c_adap = i2c_get_adapter(0);
my_tmp75_client = i2c_new_probed_device(i2c_adap, &my_i2c_dev_info, addr_list, NULL);//只会匹配到0x48地址
i2c_put_adapter(i2c_adap);
if (my_tmp75_client)
return 0;
else
return -ENODEV;
}
static void my_i2c_dev_exit(void)
{
i2c_unregister_device(my_tmp75_client);
}
module_init(my_i2c_dev_init);
module_exit(my_i2c_dev_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("caodongwang");
MODULE_DESCRIPTION("This my i2c device for tmp75");
3.用户空间注册
1)创建i2c设备
echo i2c_test 0x48 > /sys/bus/i2c/devices/i2c-6/new_device
使用这些技巧创建的i2c设备会挂在i2c_adapter的数组上,为了便捷用户空间删掉i2c设备!
2)删掉设备
echo 0x48 > /sys/bus/i2c/devices/i2c-6/delete_device
删掉设备只能删掉在用户空间创建的i2c设备!
在i2c控制器注册时,会在/sys/bus/i2c/devices/目录下创建i2c-x设备文件,但是设置它的属性,而new_device和delete_device均是它的属性,写new_device时会调用i2c_sysfs_new_device函数,内部再调用i2c_new_device函数
写delete_device时会调用i2c_sysfs_delete_device函数,内部再调用i2c_unregister_device函数
4.i2c驱动扫描注册
在《i2c设备与驱动匹配过程》中说到,i2c驱动注册时会使用两种匹配方式去找寻i2c设备,代码如下:
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
driver->driver.bus = &i2c_bus_type;//添加总线
res = driver_register(&driver->driver);//驱动注册核心函数,注意只传入了driver成员
/* 遍历所有挂在总线上的iic适配器,用它们去探测driver中指定的iic设备地址列表 */
i2c_for_each_dev(driver, __process_new_driver);
}
driver_register函数已将讲解过,如今来剖析i2c_for_each_dev函数,
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
int res;
mutex_lock(&core_lock);
res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
mutex_unlock(&core_lock);
return res;
}
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus || !bus->p)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->p->knode_bus : NULL));
while (!error && (dev = next_device(&i)))
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
最终调用__process_new_driver函数,使用i2c总线上所有i2c适配器去侦测i2c驱动中的设备地址字段!
static int __process_new_driver(struct device *dev, void *data)
{
if (dev->type != &i2c_adapter_type)
return 0;
return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}
入口先判定传入的设备是不是i2c适配器(i2c控制器),由于在《i2c设备与驱动匹配过程》中说到,i2c适配器和i2c设备一样,就会挂在i2c总线上,它们是通过dev->type项分辨的!
static int i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)
{
/* Detect supported devices on that bus, and instantiate them */
i2c_detect(adap, driver);
…
}
最终调用i2c_detect函数,函数简化后如下:
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{
int adap_id = i2c_adapter_id(adapter);
address_list = driver->address_list;
if (!driver->detect || !address_list)
return 0;
temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
itemp_client->adapter = adapter;
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1)
{
temp_client->addr = address_list[i];
err = i2c_detect_address(temp_client, driver);
if (unlikely(err))
break;
}
}
假如i2c驱动的设备地址字段为空或detect函数不存在,则结束返回,否则临时实例化一个temp_client设备形参adapter为当前i2c控制器,之后在使用该i2c控制器去侦测i2c驱动设备地址字段中的所有地址,关键函数是i2c_detect_address如下(简化后):
static int i2c_detect_address(struct i2c_client *temp_client, struct i2c_driver *driver)
{
struct i2c_board_info info;
struct i2c_adapter *adapter = temp_client->adapter;
int addr = temp_client->addr;
int err;
err = i2c_check_7bit_addr_validity_strict(addr);//检查地址是否有效,即7位有效地址
if (err) {
return err;
}
if (i2c_check_addr_busy(adapter, addr))//跳过已经使用的i2c设备
return 0;
if (!i2c_default_probe(adapter, addr))//检查这个地址是否有回应
return 0;
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
err = driver->detect(temp_client, &info);
if (err) {
return err == -ENODEV ? 0 : err;
}
if (info.type[0] == '')
{
}
else
{
struct i2c_client *client;
client = i2c_new_device(adapter, &info);
if (client)
list_add_tail(&client->detected, &driver->clients);
}
}
首先检测有效性、是否有设备回应、是否被使用linux平台总线,然后初始化了i2c_board_info结构,注意只初始化了地址(实例化设备必须还要名子),之后调用了i2c驱动中的detect函数,假若成功则调用i2c_new_device函数真正实例化i2c设备,而且将i2c设备挂在i2c驱动的数组上!注意:只有这些形式添加的i2c设备才能挂在驱动的数组上!
仔细思索里面能够发觉,i2c驱动中的detect函数必需要填写i2c_board_info结构体中name,i2c_new_device能够实例化i2c设备。
所以,使用i2c驱动扫描注册设备时,须要按如下格式编撰驱动!
#include
#include
#include
#include
static int __devinit my_i2c_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
return 0;
}
static int __devexit my_i2c_drv_remove(struct i2c_client *client)
{
return 0;
}
static const struct i2c_device_id my_dev_id_table[] = {
{ "my_i2c_dev", 0 },
{}
};//这里的名字很重要,驱动第一种匹配设备的方式要用到
static int my_i2c_drv_detect(struct i2c_client *client, struct i2c_board_info *info)
{
/* 能运行到这里, 表示该addr的设备是存在的
* 但是有些设备单凭地址无法分辨(A芯片的地址是0x50, B芯片的地址也是0x50)
* 还需要进一步读写I2C设备来分辨是哪款芯片,自己写方法
* detect就是用来进一步分辨这个芯片是哪一款,并且设置info->type,也就是设备名字
*/
printk("my_i2c_drv_detect: addr = 0x%xn", client->addr);
/* 进一步判断是哪一款 */
strlcpy(info->type, "my_i2c_dev", I2C_NAME_SIZE);
return 0;
}
static const unsigned short addr_list[] = { 0x46, 0x48, I2C_CLIENT_END };//必须使用I2C_CLIENT_END宏结尾
/* 1. 分配/设置i2c_driver */
static struct i2c_driver my_i2c_driver = {
.class = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备,不是对应类将不会调用匹配 */
.driver = {
.name = "my_i2c_dev",
.owner = THIS_MODULE,
},
.probe = my_i2c_drv_probe,
.remove = __devexit_p(my_i2c_drv_remove),
.id_table = my_dev_id_table,
.detect = my_i2c_drv_detect, /* 用这个函数来检测设备确实存在 ,并填充设备名字*/
.address_list = addr_list, /* 这些设备的地址 */
};
static int my_i2c_drv_init(void)
{
/* 2. 注册i2c_driver */
i2c_add_driver(&my_i2c_driver);
return 0;
}
static void my_i2c_drv_exit(void)
{
i2c_del_driver(&my_i2cc_driver);
}
module_init(my_i2c_drv_init);
module_exit(my_i2c_drv_exit);
MODULE_LICENSE("GPL");