二.i2c子系统操作函数,i2c-core.c

1.

kernel/driver/linux/i2c/i2c-core.c

/**
 * i2c_add_adapter - declare i2c adapter, use dynamic bus number
 * @adapter: the adapter to add
 * Context: can sleep
 *
 * This routine is used to declare an I2C adapter when its bus number
 * doesn't matter.  Examples: for I2C adapters dynamically added by
 * USB links or PCI plugin cards.
 *
 * When this returns zero, a new bus number was allocated and stored
 * in adap->nr, and the specified adapter became available for clients.
 * Otherwise, a negative errno value is returned.
 */
int i2c_add_adapter(struct i2c_adapter *adapter)
{
int id, res = 0;

retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
return -ENOMEM;

mutex_lock(&core_lock);
/* "above" here means "above or equal to", sigh */
res = idr_get_new_above(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, &id);
mutex_unlock(&core_lock);

if (res < 0) {
if (res == -EAGAIN)
goto retry;
return res;
}

adapter->nr = id;
return i2c_register_adapter(adapter);
}

i2c_add_adapter  注册i2c适配器到系统,主要功能是首先通过ird机制给i2c适配器分配指针,然后注册该i2c适配器

idr_pre_get           预分配i2c适配器指针

idr_get_new_above  给i2c适配器分配指针并和id关联

i2c_register_adapter  i2c注册适配器

kernel/driver/linux/i2c/i2c-core.c

static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0;

/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
res = -EAGAIN;
goto out_list;
}

/* Sanity checks */
if (unlikely(adap->name[0] == '\0')) {
pr_err("i2c-core: Attempt to register an adapter with "
      "no name!\n");
return -EINVAL;
}
if (unlikely(!adap->algo)) {
pr_err("i2c-core: Attempt to register adapter '%s' with "
      "no algo!\n", adap->name);
return -EINVAL;
}

rt_mutex_init(&adap->bus_lock);
mutex_init(&adap->userspace_clients_lock);
INIT_LIST_HEAD(&adap->userspace_clients);

/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;

dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev);
if (res)
goto out_list;

dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

#ifdef CONFIG_I2C_COMPAT
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
      adap->dev.parent);
if (res)
dev_warn(&adap->dev,
"Failed to create compatibility class link\n");
#endif

/* create pre-declared device nodes */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);

/* Notify drivers */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);

return 0;

out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);
return res;
}

i2c_register_adapter  注册适配器  
unlikely(WARN_ON(!i2c_bus_type.p   检查总线是否初始化完成

unlikely(adap->name[0] == '\0')             检查适配器名称是否为空

unlikely(!adap->algo)                             检查适配器算法是否为空

rt_mutex_init(&adap->bus_lock);                         初始化实时互斥锁
mutex_init(&adap->userspace_clients_lock);       初始化用户设备锁
INIT_LIST_HEAD(&adap->userspace_clients);    初始化i2c设备链表

dev_set_name(&adap->dev, "i2c-%d", adap->nr);  设置适配器设备驱动模型设备名称

adap->dev.bus = &i2c_bus_type;                            初始化适配器设备驱动模型设备的总线类型
adap->dev.type = &i2c_adapter_type;                    初始化适配器设备驱动模型设备类型

device_register(&adap->dev);                                注册适配器设备到设备驱动模型

i2c_scan_static_board_info(adap);                        扫描静态声明的i2c设备信息,该方法也对应i2c设备静态声明注册

kernel/driver/linux/i2c/i2c-core.c

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo*devinfo;

down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}

kernel/driver/linux/i2c/i2c-core.h
struct i2c_devinfo {
struct list_headlist;
int busnum;
struct i2c_board_infoboard_info;
};

上面结构体封装了i2c_board_info和链表,因此可以通过链表遍历i2c板信息。

down_read    读信号量

list_for_each_entry  遍历i2c板信息

i2c_new_device(adapter,&devinfo->board_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_scan_static_board_info 扫描静态声明的i2c设备信息 -》down_read读信号量

list_for_each_entry遍历i2c板信息   -》i2c_new_devicei2c适配器增加新i2c设备,根据 i2c_board_info 静态设备声明信息-》device_register注册设备到设备驱动模型

bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);    遍历总线上的i2c驱动,动态探测添加i2c设备,该方法主要为了适配器动态加载时比i2c驱动晚注册的情况。

kernel/driver/base/bus.c

int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
    void *data, int (*fn)(struct device_driver *, void *))
{
struct klist_iter i;
struct device_driver *drv;
int error = 0;

if (!bus)
return -EINVAL;

klist_iter_init_node(&bus->p->klist_drivers, &i,
    start ? &start->p->knode_bus : NULL);
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
klist_iter_exit(&i);
return error;
}

klist_iter_init_node(&bus->p->klist_drivers, &i, start ? &start->p->knode_bus : NULL);    初始化内核链表遍历节点

next_driver(&i)    获取下一个设备驱动指针

fn(drv, data);     调用处理函数即__process_new_adapter

__process_new_adapter                              添加新适配器处理函数,结合设备驱动动态探测i2c设备

kernel/driver/linux/i2c/i2c-core.c

static int __process_new_adapter(struct device_driver *d, void *data)
{
return i2c_do_add_adapter(to_i2c_driver(d), data);
}

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设备并实例化

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驱动绑定适配器,此函数已经被移除,这是为了保持兼容性

小结:bus_for_each_drv遍历总线上的i2c驱动,动态添加i2c设备-》next_driver获取下一个设备驱动指针-》__process_new_adapter添加新适配器处理函数,结合设备驱动动态探测i2c设备-》i2c_detect i2c驱动和适配器来探测i2c设备并实例化

总结:

i2c_add_adapter  注册i2c适配器到系统-》idr_pre_get预分配i2c适配器指针

idr_get_new_above  给i2c适配器分配指针并和id关联

i2c_register_adapter  i2c注册适配器-》device_register注册适配器设备到设备驱动模型

i2c_scan_static_board_info 扫描静态声明的i2c设备信息

bus_for_each_drv遍历总线上的i2c驱动,动态添加i2c设备

2. i2c_add_numbered_adapter  注册具有静态总线号的i2c适配器的函数

kernel/driver/linux/i2c/i2c-core.c

int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
 int id;
 int status;

if (adap->nr & ~MAX_ID_MASK)
  return -EINVAL;

retry:
 if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
  return -ENOMEM;

mutex_lock(&core_lock);
 /* "above" here means "above or equal to", sigh;
  * we need the "equal to" result to force the result
  */
 status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
 if (status == 0 && id != adap->nr) {
  status = -EBUSY;
  idr_remove(&i2c_adapter_idr, id);
 }
 mutex_unlock(&core_lock);
 if (status == -EAGAIN)
  goto retry;

if (status == 0)
  status = i2c_register_adapter(adap);
 return status;
}

if (adap->nr & ~MAX_ID_MASK)    判断适配器号是否超过最大值

idr_pre_get(&i2c_adapter_idr, GFP_KERNEL)     idr机制预分配指针

idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);    idr机制整数id与指针建立关系

i2c_register_adapter(adap);                注册i2c适配器

总结: i2c_add_numbered_adapter  注册具有静态总线号的i2c适配器的函数 ->idr_pre_get  idr机制预分配指针

idr_get_new_aboveidr机制整数id与指针建立关系

i2c_register_adapter注册i2c适配器

linux i2c子系统代码分析3 ---操作函数i2c_add_adapter i2c_add_numbered_adapter介绍相关推荐

  1. Linux i2c子系统

    一.前言 因为自己在研发过程中经常要涉及到tp驱动程序,而tp驱动就涉及到i2c驱动.经常可以看到在驱动程序中会定义一个struct i2c_driver的数据结构,并实现里面的某些成员,比如prob ...

  2. LINUX 路由子系统流程分析

    title: LINUX 路由子系统流程分析 date: 2020-11-28 categories: Linux tags: Linux Routing-Subsystem 上次分析了Linux协议 ...

  3. Linux i2c驱动框架分析 (二)

    Linux i2c驱动框架分析 (一) Linux i2c驱动框架分析 (二) Linux i2c驱动框架分析 (三) 通用i2c设备驱动分析 i2c core i2c核心(drivers/i2c/i ...

  4. Linux内核汇编代码分析

    Linux内核汇编代码分析 1.vmlinux.lds.S文件分析 1.2 vmlinux.lds.S文件总体框架 1.3 代码段 1.4 只读数据段 1.5 init段 1.6 数据段 1.7 未初 ...

  5. Linux I2C子系统分析之(一) ----- 用GPIO模拟I2C总线

    在drivers/i2c/busses下包含各种I2C总线驱动,如S3C2440的I2C总线驱动i2c-s3c2410.c,使用GPIO模拟I2C总线的驱动i2c-gpio.c,这里只分析i2c-gp ...

  6. Linux I2C子系统分析-I2C总线驱动

    在drivers/i2c/busses下包含各种I2C总线驱动,如S3C2440的I2C总线驱动i2c-s3c2410.c,使用GPIO模拟I2C总线的驱动i2c-gpio.c,这里只分析i2c-gp ...

  7. Linux I2C子系统分析-I2C设备驱动

    接下来以一个实际的例子来看I2C设备驱动,就以drivers/i2c/i2c-dev.c为例. 先看它的初始化和注销函数 [cpp] view plaincopy static int __init  ...

  8. linux i2c子系统看不懂啊,Linux 下的I2C子系统

    Linux 下的I2C子系统 2013.7.16 本文分为两部分,一.设备模型 二.平台相关 . ================================================ 第一 ...

  9. 第九讲 Linux I2C子系统及mma8653重力传感器驱动编写

    2.I2C子系统 设备.总线.驱动模型: subsystem bus dev drv platform bus_type platform_device platform_driver I2C bus ...

最新文章

  1. 自己动手编写tomcat服务器(三)
  2. 转: ubuntu apt-get 与 aptitude 用法与区别
  3. 【学习笔记】SAP 成本对象控制
  4. 使用模板有什么缺点?如何避免?
  5. 根据ABAP类方法的形式参数名,反查是哪个方法定义了该形式参数
  6. 【转】细说.NET中的多线程 (六 使用MemoryBarrier,Volatile进行同步)
  7. 2020年考证时间表汇总!这些证书值得拥有!
  8. LG卖楼进行时:价值87.7亿元的双子座大厦将出手
  9. 微信支付v2开发(6) 发货通知
  10. 台式计算机开机风扇不转,台式机开机风扇转一下停一下
  11. 【如何选】校园卡购买必读,移动联通电信校园卡套餐对比及购买策略(1预热)...
  12. win7管理员取得所有权
  13. 手游开发攻防——二、基础篇
  14. rust怎么拆除墙壁指令_腐蚀RUST指令大全
  15. eclipse安装WindousBuilder为什么在项目里不显示
  16. 阿里天池大赛脱敏多标签文本分类初赛20名方案分享
  17. 百度语音合成模型Deep Voice3
  18. vue VNode如何使用,是什么东西?
  19. java斜椭圆_JAVA 任意椭圆方向画法
  20. Mybatis遇到的脑残问题

热门文章

  1. 华为欧拉系统安装docker和数据库MySQL
  2. 论文学习——基于优化DTW算法的水文要素时间序列数据相似性分析
  3. 软件测试---概念篇
  4. 我身边幽默风趣的程序猿
  5. day7 - 日志和作业
  6. JAVA入门教程:(十五)安装mysql
  7. MongoDB User Asserts数量很大
  8. win10无法设置移动热点
  9. 根据身份证号码判断是否是未成年人
  10. 测试爬虫 爬取百度贴吧 爬取百度搜图图片