linux i2c子系统代码分析2 ---操作函数i2c_init介绍
来源:互联网 发布:人工智能理论 编辑:程序博客网 时间:2024/06/06 10:46
二.i2c子系统操作函数,i2c-core.c
上面介绍了i2c子系统重要的数据结构,下面介绍i2c子系统的适配器、算法、设备、设备驱动注册、注销以及重要的数据处理函数
在kernel/driver/linux/i2c/i2c-core.c文件中
static int __init i2c_init(void)
{
int retval;
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
return 0;
class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
bus_unregister(&i2c_bus_type);
return retval;
}
i2c_init 是i2c子系统的初始化函数,i2c总线注册,以及虚拟驱动的添加。
步骤:
1.bus_register(&i2c_bus_type); i2c总线注册,这样在/sys/bus/下就会增加i2c的节点
2.i2c_add_driver(&dummy_driver); 虚拟驱动的注册,用来创建注册i2c适配器
详细步骤:
1.1
bus_register(&i2c_bus_type);
kernel/driver/linux/i2c/i2c-core.c
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
上面是i2c总线的实例化
.match= i2c_device_match, i2c总线驱动和设备匹配函数
.probe= i2c_device_probe, i2c总线设备探测函数,最终会调用驱动中的probe函数
1.1.1
kernel/driver/linux/i2c/i2c-core.c
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client*client = i2c_verify_client(dev);
struct i2c_driver*driver;
if (!client)
return 0;
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
i2c_device_match 设备和驱动匹配函数。
struct i2c_client*client = i2c_verify_client(dev); 首先判断该设备是否是i2c设备
kernel/driver/linux/i2c/i2c-core.c
struct i2c_client *i2c_verify_client(struct device *dev)
{
return (dev->type == &i2c_client_type)
? to_i2c_client(dev)
: NULL;
}
kernel/driver/linux/i2c/i2c-core.c
static struct device_type i2c_client_type = {
.groups = i2c_dev_attr_groups,
.uevent = i2c_device_uevent,
.release = i2c_client_dev_release,
};
of_driver_match_device(dev, drv) 再看设备驱动模型中设备和驱动是否匹配
kernel/include/linux/of_device.h
/**
* of_driver_match_device - Tell if a driver's of_match_table matches a device.
* @drv: the device_driver structure to test
* @dev: the device structure to match against
*/
static inline int of_driver_match_device(struct device *dev,
const struct device_driver *drv)
{
return of_match_device(drv->of_match_table, dev) != NULL;
}
kernel/driver/of/device.c
/**
* of_match_device - Tell if a struct device matches an of_device_id list
* @ids: array of of device match structures to search in
* @dev: the of device structure to match against
*
* Used by a driver to check whether an platform_device present in the
* system is in its list of supported devices.
*/
const struct of_device_id *of_match_device(const struct of_device_id *matches,
const struct device *dev)
{
if ((!matches) || (!dev->of_node))
return NULL;
return of_match_node(matches, dev->of_node);
}
kernel/driver/of/base.c
/**
* of_match_node - Tell if an device_node has a matching of_match structure
* @matches: array of of device match structures to search in
* @node: the of device structure to match against
*
* Low level utility function used by device matching.
*/
const struct of_device_id *of_match_node(const struct of_device_id *matches,
const struct device_node *node)
{
if (!matches)
return NULL;
while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
int match = 1;
if (matches->name[0])
match &= node->name
&& !strcmp(matches->name, node->name);
if (matches->type[0])
match &= node->type
&& !strcmp(matches->type, node->type);
if (matches->compatible[0])
match &= of_device_is_compatible(node,
matches->compatible);
if (match)
return matches;
matches++;
}
return NULL;
}
/*
* Struct used for matching a device
*/
struct of_device_id
{
char name[32];
char type[32];
char compatible[128];
#ifdef __KERNEL__
void *data;
#else
kernel_ulong_t data;
#endif
};
struct device_node {
const char *name;
const char *type;
phandle phandle;
char *full_name;
struct property *properties;
struct property *deadprops; /* removed properties */
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
struct device_node *next; /* next device of same type */
struct device_node *allnext; /* next in list of all nodes */
struct proc_dir_entry *pde; /* this node's proc directory */
struct kref kref;
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
of_match_node判断设备匹配表和设备节点是否匹配,其实就是看名称类型兼容性等是否匹配。
内核中of是openFirmware的缩写,是设备模型利用其建立设备树的代码,
小结:of_driver_match_device->of_match_device->of_match_node
driver = to_i2c_driver(drv);通过drv获取i2c驱动指针
i2c_match_id(driver->id_table, client) i2c驱动匹配表匹配i2c设备
kernel/driver/linux/i2c/i2c-core.c
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client)
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
kernel/include/linux/mod_devicetable.h
struct i2c_device_id {
char name[I2C_NAME_SIZE];
kernel_ulong_t driver_data/* Data private to the driver */
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
该函数就是比较 i2c_device_id i2c驱动设备表中是否有i2c设备同名的
总结: i2c_device_match有设备驱动模型中匹配以及i2c子系统设备驱动匹配两部分
i2c_device_match->of_driver_match_device
i2c_match_id(driver->id_table, client)
1.1.2 i2c_device_probe i2c总线探测设备函数
kernel/driver/linux/i2c/i2c-core.c
static int i2c_device_probe(struct device *dev)
{
struct i2c_client*client = i2c_verify_client(dev);
struct i2c_driver*driver;
int status;
if (!client)
return 0;
driver = to_i2c_driver(dev->driver);
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));
if (status) {
client->driver = NULL;
i2c_set_clientdata(client, NULL);
}
return status;
}
i2c_verify_client(dev); 先判断是否是i2c设备
driver = to_i2c_driver(dev->driver); 获取i2c驱动指针
if (!driver->probe || !driver->id_table) 如果i2c驱动没有probe函数或者没有设备匹配表就返回错误,所以i2c驱动必须有probe,它是驱动的入口
device_can_wakeup(&client->dev) 设备电源管理的操作
driver->probe(client, i2c_match_id(driver->id_table, client)) 执行i2c驱动中的probe操作
总结: i2c_device_probe -》i2c_verify_client
to_i2c_driver
device_can_wakeup
driver->probe
2
i2c_add_driver(&dummy_driver); 注册虚拟驱动
kernel/include/linux/i2c.h
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}
其是一个封装,直接调用i2c_register_driver
kernel/driver/linux/i2c/i2c-core.c
/*
* An i2c_driver is used with one or more i2c_client (device) nodes to access
* i2c slave chips, on a bus instance associated with some i2c_adapter.
*/
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
/* 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;
/* Drivers should switch to dev_pm_ops instead. */
if (driver->suspend)
pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
driver->driver.name);
if (driver->resume)
pr_warn("i2c-core: driver [%s] using legacy resume method\n",
driver->driver.name);
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
return 0;
}
i2c_register_driver i2c驱动注册函数,i2c驱动和i2c适配器关联
if (unlikely(WARN_ON(!i2c_bus_type.p))) 检查驱动设备模型是否初始化好
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type; i2c驱动的驱动模型赋值
driver_register(&driver->driver); 设备驱动模型驱动注册,建立设备驱动模型
INIT_LIST_HEAD(&driver->clients); 初始化驱动的i2c设备链表
i2c_for_each_dev(driver, __process_new_driver); 遍历i2c总线适配器,动态添加i2c设备
kernel/driver/linux/i2c/i2c-core.c
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;
}
mutex_lock(&core_lock);加锁
bus_for_each_dev(&i2c_bus_type, NULL, data, fn); 遍历i2c总线上的适配器设备,该设备是i2c设备驱动模型下的代表,调用fn函数并传入data参数
kernel/driver/base/bus.c
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)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->p->knode_bus : NULL)); 初始化klist_iter节点
next_device(&i) 通过klist_iter节点获取设备指针
__process_new_driver 添加新驱动后依据适配器动态探测i2c设备并实例化
kernel/driver/linux/i2c/i2c-core.c
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));
}
dev->type != &i2c_adapter_type判断设备类型是否是适配器设备
kernel/driver/linux/i2c/i2c-core.c
i2c_do_add_adapter(data, to_i2c_adapter(dev)); i2c驱动和适配器探测添加i2c设备
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);
/* Let legacy drivers scan this bus for matching devices */
if (driver->attach_adapter) {
dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
driver->driver.name);
dev_warn(&adap->dev, "Please use another way to instantiate "
"your i2c_client\n");
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
return 0;
}
i2c_detect(adap, driver); i2c驱动和适配器来探测i2c设备并实例化,这里i2c驱动和i2c适配器类型必须是同一类才行adapter->class & driver->class。
kernel/driver/linux/i2c/i2c-core.c
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{
const unsigned short *address_list;
struct i2c_client *temp_client;
int i, err = 0;
int adap_id = i2c_adapter_id(adapter);
address_list = driver->address_list;
if (!driver->detect || !address_list)
return 0;
/* Stop here if the classes do not match */
if (!(adapter->class & driver->class))
return 0;
/* Set up a temporary client to help detect callback */
temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (!temp_client)
return -ENOMEM;
temp_client->adapter = adapter;
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
"addr 0x%02x\n", adap_id, address_list[i]);
temp_client->addr = address_list[i];
err = i2c_detect_address(temp_client, driver);
if (unlikely(err))
break;
}
kfree(temp_client);
return err;
}
i2c_adapter_id(adapter);获取适配器号
address_list = driver->address_list; 获取驱动支持地址链表
if (!(adapter->class & driver->class)) 判断i2c驱动和适配器是否匹配,不匹配则不进行设备探测
temp_client->adapter = adapter; 客户端初始化适配器
temp_client->addr = address_list[i]; 客户端初始化地址
i2c_detect_address(temp_client, driver); i2c驱动根据设备地址探测设备函数
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;
/* Make sure the address is valid */
err = i2c_check_addr_validity(addr);
if (err) {
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
addr);
return err;
}
/* Skip if already in use */
if (i2c_check_addr_busy(adapter, addr))
return 0;
/* Make sure there is something at this address */
if (!i2c_default_probe(adapter, addr))
return 0;
/* Finally call the custom detection function */
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
err = driver->detect(temp_client, &info);
if (err) {
/* -ENODEV is returned if the detection fails. We catch it
here as this isn't an error. */
return err == -ENODEV ? 0 : err;
}
/* Consistency check */
if (info.type[0] == '\0') {
dev_err(&adapter->dev, "%s detection function provided "
"no name for 0x%x\n", driver->driver.name,
addr);
} else {
struct i2c_client *client;
/* Detection succeeded, instantiate the device */
dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
info.type, info.addr);
client = i2c_new_device(adapter, &info);
if (client)
list_add_tail(&client->detected, &driver->clients);
else
dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
info.type, info.addr);
}
return 0;
}
i2c_check_addr_validity(addr);检查地址有效性
i2c_check_addr_busy(adapter, addr)) 检查该地址是否被其他设备使用,具体过程就是先获取该适配器父节点,然后再遍历父节点适配器的子设备链表,看是否有适配器和本适配器地址冲突
i2c_default_probe(adapter, addr) 用smbus通信算法
driver->detect(temp_client, &info); 调用i2c驱动的i2c设备探测函数
i2c_new_device(adapter, &info); i2c适配器增加新i2c设备,根据 i2c_board_info 静态设备声明信息
kernel/driver/linux/i2c/i2c-core.c
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client*client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap;
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
/* Check for address validity */
status = i2c_check_client_addr_validity(client);
if (status) {
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
goto out_err_silent;
}
/* Check for address business */
status = i2c_check_addr_busy(adap, client->addr);
if (status)
goto out_err;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
client->addr);
status = device_register(&client->dev);
if (status)
goto out_err;
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
return client;
out_err:
dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, status);
out_err_silent:
kfree(client);
return NULL;
}
client->adapter = adap; 给i2c设备设置适配器
client->dev.platform_data = info->platform_data;
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
上面是利用i2c板级信息给i2c设备赋值
strlcpy(client->name, info->type, sizeof(client->name)); 设置设备名称
i2c_check_client_addr_validity(client); 检查地址有效性
i2c_check_addr_busy(adap, client->addr); 检查设备地址是否和其他冲突
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
上面是给i2c设备驱动模型的设备赋值
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),client->addr); 给设备驱动模型设备设置名称,名称是总线号以及设备地址组合
device_register(&client->dev); 注册设备到设备驱动模型
小结:i2c_detect-》i2c_detect_address-》i2c_check_addr_validity
i2c_check_addr_busy
i2c_default_probe
driver->detect
i2c_new_device -》 device_register(&client->dev);
driver->attach_adapter(adap); i2c驱动绑定适配器,此函数已经被移除,这是为了保持兼容性
小结:__process_new_driver -》i2c_do_add_adapter -》i2c_detect
driver->attach_adapter
总结:i2c_add_driver -》 i2c_register_driver -》driver_register
INIT_LIST_HEAD
i2c_for_each_dev -》bus_for_each_dev -》klist_iter_init_node
next_device -》 -》__process_new_driver
- linux i2c子系统代码分析2 ---操作函数i2c_init介绍
- linux i2c子系统代码分析3 ---操作函数i2c_add_adapter i2c_add_numbered_adapter介绍
- linux i2c子系统代码分析4 ---操作函数i2c_add_driver i2c_register_driver介绍
- linux i2c子系统代码分析5 ---操作函数i2c_new_device i2c_new_probed_device i2c_register_board_info介绍
- linux i2c子系统代码分析6 ---操作函数i2c数据处理函数
- linux i2c子系统代码分析8 ---i2c子系统内核目录介绍
- Linux I2C 子系统分析
- Linux I2C子系统分析
- i2c子系统之i2c bus初始化——i2c_init()
- I2C子系统之I2C bus初始化——I2C_init()
- Linux I2C子系统分析-I2C设备驱动 2
- linux i2c子系统代码分析1 ---概述以及主要数据结构
- Linux I2C子系统分析整理
- Linux I2C子系统分析-I2C总线驱动
- Linux I2C子系统分析-I2C设备驱动
- Linux I2C子系统分析-I2C总线驱动
- Linux I2C子系统分析-I2C总线驱动
- Linux I2C子系统分析-I2C总线驱动
- 千里之行,始于足下。从今天开始记录android移动开发的点点滴滴
- 更加简洁且通用的ViewHolder写法
- 【机房】机房收费系统总览
- 16位汇编 int 10h和int 21h 显示字符串实例
- vs2008环境的objectArx开发-32位升级64位问题整理
- linux i2c子系统代码分析2 ---操作函数i2c_init介绍
- 视图动画:RotateAnimation、TranslateAnimation、ScaleAnimation、AlphaAnimation
- 关于python java C++ 变量机制的思考
- Handler
- iOS - 解决Warning: Attempt to present <UIImagePickerController: 0x7f9ba106a000>which is already presen
- 类似微信查看群所有成员的动态伸展GridView,ScrollView做父控件(最后增加一个可添加的图片)
- android Studio 关于SVN的问题
- vijos p1191(递推)
- 深入源码理解Android Touch事件分发机制(上篇)