您的位置:首页 > 运维架构 > Linux

linux i2c子系统代码分析4 ---操作函数i2c_add_driver i2c_register_driver介绍

2016-09-05 10:32 633 查看
下面介绍 i2c 驱动相关操作函数

i2c_add_driver

kernel/include/linux/i2c.h

static inline int i2c_add_driver(struct i2c_driver *driver)

{
return i2c_register_driver(THIS_MODULE, driver);

}

 i2c_add_driver   添加一个i2c驱动,其就是封装了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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: