\arch\arm\mach-mx6\board-mx6q_sabresd.c

 static struct i2c_board_info i2c_board_info_rtc[] __initdata = {
//isl1208 does not work on the 1st board that's sent to Korea, but we should enbaled it later.
#if 1
{
I2C_BOARD_INFO("isl1208", 0x6F),
//.irq = gpio_to_irq(CYNO_GPIO_R10),
},
#else
//pcf8563 is no longer usned.
{
I2C_BOARD_INFO("pcf8563", 0x51),
//.irq = gpio_to_irq(CYNO_GPIO_R10),
},
#endif
};

 这个busnum号决定了adapter->nr之间的关系,如果相等这把这个总线设备和适配器连接起来

i2c_register_board_info(2, i2c_board_info_rtc, ARRAY_SIZE(i2c_board_info_rtc));

下面我们再来分析下i2c_register_board_info这个文件的定义

在drivers/i2c/i2c-boardinfo.c文件中我们找到这个函数的定义:

 int __init
i2c_register_board_info(int busnum, //这个busnum号决定了adapter->nr之间的关系,如果相等这把这个总线设备和适配器连接起来
struct i2c_board_info const *info, unsigned len)
{
int status; down_write(&__i2c_board_lock); /* dynamic bus numbers will be assigned after the last static one */
if (busnum >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = busnum + ; for (status = ; len; len--, info++) {
struct i2c_devinfo *devinfo; devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
if (!devinfo) {
pr_debug("i2c-core: can't register boardinfo!\n");
status = -ENOMEM;
break;
} devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list);
} up_write(&__i2c_board_lock); return status;
}

在这个函数里面定义了bus_num以及RTC相关信息

下面我们针对这个设备具体分析

我们在/drivers/rtc/rtc-isl1208.c这个文件

static const struct i2c_device_id isl1208_id[] = {
{ "isl1208", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, isl1208_id);

static struct i2c_driver isl1208_driver = {
.driver = {
.name = "rtc-isl1208",
},
.probe = isl1208_probe,
.remove = isl1208_remove,
.id_table = isl1208_id,
};

可以看到isl1208_driver真是这个设备所支持的驱动

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

mutex_init(&adap->bus_lock);    //初始化保护i2c适配器的互斥锁
 mutex_init(&adap->clist_lock);    //初始化保护adap->clients的锁
 INIT_LIST_HEAD(&adap->clients); //初始化i2c适配器上介入的设备(client)链表

mutex_lock(&core_lock);

//初始化adap->dev然后注册该设备
 if (adap->dev.parent == NULL) {
  adap->dev.parent = &platform_bus;
  pr_debug("I2C adapter driver [%s] forgot to specify "
    "physical device\n", adap->name);
 }
 sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
 adap->dev.release = &i2c_adapter_dev_release;
 adap->dev.class = &i2c_adapter_class;
 res = device_register(&adap->dev);
 if (res)
  goto out_list;

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

//适配器驱动注册进内核后,对系统中现有的两种设备进行绑定。

if (adap->nr < __i2c_first_dynamic_bus_num)//__i2c_first_dynamic_bus_num表示第一个动态分配的设备号
  i2c_scan_static_board_info(adap);  //如果适配器号和设备好一致,则会进行设备和适配器的绑定,如果不一致,则会执行下面的函数

 dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, 
     i2c_do_add_adapter);

out_unlock:
 mutex_unlock(&core_lock);
 return res;

out_list:
 idr_remove(&i2c_adapter_idr, adap->nr);
 goto out_unlock;
}

注册适配器后,开始调用  i2c_scan_static_board_info(adap);查找i2c_board_info的相关设备的注册信息

__i2c_board_list中查找 我们在板级文件中以把相关i2c——device的信息注册到这个双向链表中了。。struct i2c_devinfo相对比,如果找到了,就会调用i2c_new_device()把device和适配器绑定在一起,

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

mutex_lock(&__i2c_board_lock);
 list_for_each_entry(devinfo, &__i2c_board_list, list) {
  if (devinfo->busnum == adapter->nr //如果device编号和adapter编号一致,则会调用i2c_new_device,把device和adapter绑定在一起
    && !i2c_new_device(adapter,
      &devinfo->board_info))
   printk(KERN_ERR "i2c-core: can't create i2c%d-x\n",
    i2c_adapter_id(adapter),
    devinfo->board_info.addr);
 }
 mutex_unlock(&__i2c_board_lock);
}

下面来分析这个i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
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; //在这个地方适配器和i2c_device 绑定

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);这个设备被绑定到了总线上面,但是这个设备所支持的drivers呢??????

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;
}

下面分析 dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, i2c_do_add_adapter);这个函数

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); 通过这个函数又会把设配器和设备绑定在一起,这次依靠的是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;
}

在struct i2c_adapter 注册到内核中后,内核时怎样把已经注册进系统中的i2c设备设备与刚注册进内核的适配器进行绑定的。分两种情况一是在板级用 i2c_register_board_info注册的,其二是通过各种struct i2c_driver注册的。其中驱动注册又有两种方法,一种是新的总线式驱动一种是老式的,这里我们对老式的方法不做介绍,老式的方法在内核中也慢慢的消亡。

可能很多人有以后这个该死的adapter什么时候注册,那我现在说先注册顺序(讲解针对板级文件中设备的注册分析)

步骤1:i2c_register_board_info(2, i2c_board_info_rtc, ARRAY_SIZE(i2c_board_info_rtc));

注册这个板级文件后就会把这个device的相关信息存储到__i2c_board_list

步骤2:对于静态的注册adapter这个适配器,这个早就设定好了,所以直接注册,在这个函数i2c_register_adapter的后面几个函数就会决定设配器和device的绑定


    • 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 < __i2c_first_dynamic_bus_num)//__i2c_first_dynamic_bus_num表示第一个动态分配的设备号
  i2c_scan_static_board_info(adap);  //如果适配器号和设备好一致,则会进行设备和适配器的绑定,如果不一致,则会执行下面的函数

 dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, 
     i2c_do_add_adapter);

 步骤3:现在设配器和设备已经绑定了,那设备的驱动什么时候能找到这个设备呢,我们继续分析

static struct i2c_driver isl1208_driver = {
.driver = {
.name = "rtc-isl1208",
},
.probe = isl1208_probe,
.remove = isl1208_remove,
.id_table = isl1208_id,
};

在driver/rtc/rtc-isli1208.c文件中最好几行代码会注册这个驱动,

static int __init
isl1208_init(void)
{
return i2c_add_driver(&isl1208_driver);
}

static void __exit
isl1208_exit(void)
{
i2c_del_driver(&isl1208_driver);
}

我们再来分析这个i2c_add_driver函数,也许会有一点感悟

static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}

/*
* 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. *///这个地方会通过probe函数实现
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_for_each_dev(driver, __process_new_driver);这个地方不太理解,个人认为是那些可插拔设备寻找合适的适配器的方法

那到底设备和驱动是怎么结合到一起的呢,正确的设备是怎么配对正确的驱动的呢,这个最主要的功臣当然非probe函数莫属啦

那我们就来看看.probe = isl1208_probe这个探测函数吧

isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int rc = 0;
struct rtc_device *rtc;

if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))  //检查设备有哪些功能
return -ENODEV;

if (isl1208_i2c_validate_client(client) < 0) //判定这个设备是否是isl1208驱动所支持的设备,如过不是probe探测失败,
return -ENODEV;

dev_info(&client->dev,
"chip found, driver version " DRV_VERSION "\n");

if (client->irq > 0) {
rc = request_threaded_irq(client->irq, NULL,isl1208_rtc_interrupt,IRQF_SHARED,
                                                       isl1208_driver.driver.name, client);//申请中断
if (!rc) {
device_init_wakeup(&client->dev, 1);
enable_irq_wake(client->irq);
} else {
dev_err(&client->dev,
"Unable to request irq %d, no alarm support\n",
client->irq);
client->irq = 0;
}
}

rtc = rtc_device_register(isl1208_driver.driver.name,
&client->dev, &isl1208_rtc_ops,  THIS_MODULE); //关键中的关键终于到来,设备和驱动完美的结合,特备强调isl1208_rtc_ops,我们看看它代表什么。

////////////////////static const struct rtc_class_ops isl1208_rtc_ops = {

proc — 一个虚拟文件系统 
/proc 文件系统是一种内核和内核模块用来向进程 (process) 发送信息的机制 (所以叫做 /proc)。这个伪文件系统让你可以和内核内部数据结构进行交互,获取 有关进程的有用信息,在运行中 (on the fly) 改变设置 (通过改变内核参数)。 与其他文件系统不同,/proc 存在于内存之中而不是硬盘上。
///////////////////      .proc = isl1208_rtc_proc,  这些函数不都是定义在rtc-isl1208.c这个文件里面吗,此处的配置就是为了用户在调用rtc-isl1208这个设备的时候,能够对这个设备进行相应的读写操作
//////////////////       .read_time = isl1208_rtc_read_time,
/////////////////        .set_time = isl1208_rtc_set_time,
////////////////         .read_alarm = isl1208_rtc_read_alarm,

////////                  .set_alarm = isl1208_rtc_set_alarm,

///////////////////////        };

if (IS_ERR(rtc)) {
rc = PTR_ERR(rtc);
goto exit_free_irq;
}

i2c_set_clientdata(client, rtc);

rc = isl1208_i2c_get_sr(client);
if (rc < 0) {
dev_err(&client->dev, "reading status failed\n");
goto exit_unregister;
}

if (rc & ISL1208_REG_SR_RTCF)
dev_warn(&client->dev, "rtc power failure detected, "
"please set clock.\n");

rc = sysfs_create_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
if (rc)
goto exit_unregister;

return 0;

exit_unregister:
rtc_device_unregister(rtc);
exit_free_irq:
if (client->irq)
free_irq(client->irq, client);

return rc;
}

至此整个I2C的体系的分析就告一段落了,下一步,usb-mouse的分析哦

我敢肯定这里有很多不对的地方,如果被某人发现,请给我留言,谢谢 我定会更正

i2c_drivers个人分析的更多相关文章

  1. alias导致virtualenv异常的分析和解法

    title: alias导致virtualenv异常的分析和解法 toc: true comments: true date: 2016-06-27 23:40:56 tags: [OS X, ZSH ...

  2. 火焰图分析openresty性能瓶颈

    注:本文操作基于CentOS 系统 准备工作 用wget从https://sourceware.org/systemtap/ftp/releases/下载最新版的systemtap.tar.gz压缩包 ...

  3. 一起来玩echarts系列(一)------箱线图的分析与绘制

    一.箱线图 Box-plot 箱线图一般被用作显示数据分散情况.具体是计算一组数据的中位数.25%分位数.75%分位数.上边界.下边界,来将数据从大到小排列,直观展示数据整体的分布情况. 大部分正常数 ...

  4. 应用工具 .NET Portability Analyzer 分析迁移dotnet core

    大多数开发人员更喜欢一次性编写好业务逻辑代码,以后再重用这些代码.与构建不同的应用以面向多个平台相比,这种方法更加容易.如果您创建与 .NET Core 兼容的.NET 标准库,那么现在比以往任何时候 ...

  5. UWP中新加的数据绑定方式x:Bind分析总结

    UWP中新加的数据绑定方式x:Bind分析总结 0x00 UWP中的x:Bind 由之前有过WPF开发经验,所以在学习UWP的时候直接省略了XAML.数据绑定等几个看着十分眼熟的主题.学习过程中倒是也 ...

  6. 查看w3wp进程占用的内存及.NET内存泄露,死锁分析

    一 基础知识 在分析之前,先上一张图: 从上面可以看到,这个w3wp进程占用了376M内存,启动了54个线程. 在使用windbg查看之前,看到的进程含有 *32 字样,意思是在64位机器上已32位方 ...

  7. ZIP压缩算法详细分析及解压实例解释

    最近自己实现了一个ZIP压缩数据的解压程序,觉得有必要把ZIP压缩格式进行一下详细总结,数据压缩是一门通信原理和计算机科学都会涉及到的学科,在通信原理中,一般称为信源编码,在计算机科学里,一般称为数据 ...

  8. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  9. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

随机推荐

  1. 爬虫(正则)——爬neihan8

    工具:python3 步骤: 1)定义spider类,将具体功能封装成方法 2)观察html页面,写好正确的正则表达式来找到符合规则的文字 3)写一个方法测试一下,保证每写好一个都是对的 4)加入提示 ...

  2. SPI接口的ETH芯片(ENC28J60/W5500)

    一 ENC28J60:SPI接口.中断.复位.LED指示.可参看野火相应教程.简单的在单片机中实现网页服务器是参考AVRNET项目,复杂的是用LWIP协议栈.telnet用于用PC的TELNET可以远 ...

  3. 单台服务器最大tcp连接

    如果对服务器进行压力测试,常常出现这种情况 tcp连接数过多 netstat   -an    windows查看tcp连接数 那么怎么增加单台服务器的最大连接数呢?       最简单的办法,增加内 ...

  4. XML文件的一些操作

    XML 是被设计用来传输和存储数据的, XML 必须含有且仅有一个 根节点元素(没有根节点会报错) 源码下载 http://pan.baidu.com/s/1ge2lpM7 好了,我们 先看一个 XM ...

  5. 解决Chrome浏览器自动记录用户名和密码的黄色背景问题和该解决方法与tab切换至下一个input冲突的问题。

    哈哈哈,是不是标题很长呀,不逗你们了.其实这么长的标题主要就说了两件事: 第一件:解决Chrome浏览器自动记录用户名和密码的黄色背景问题. 第二件:输入完用户名后按下tab键切换至下一个输入密码in ...

  6. form-data、x-www-form-urlencoded、raw、binary的区别

    1.form-data: 就是http请求中的multipart/form-data,它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开.既可以上传键值对,也可以上传文件.当上传的字段是文件时 ...

  7. php 根据周数获取当周的开始日期与最后日期

    /** * 根据第几周获取当周的开始日期与最后日期 * @param int $year 年份 $weeks = get_week($year) * @param 如获取第18周的开始日期$weeks ...

  8. ESP8266串口WiFi扩展板详解

    产品简介 ESP8266串口WiFi扩展板是深圳四博智联科技有限公司开发的一款基于乐鑫ESP8266的超低功耗的UART-WiFi模块,兼容Arduino UNO.Mega等标准主板,可以方便地进行二 ...

  9. javaSe-hashMap

    package com.java.chap08.sec05; public class Student { private String name; private Integer age; publ ...

  10. hdu 3861 The King’s Problem

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission( ...