学习目标:分析linux内核源码下的i2c总线驱动 drivers/i2c/busses/i2c-s3c2410.c 和 driver/i2c/chips/eeprom.c 设备驱动;


一、i2c驱动框架

在drivers/i2c/目录下查看文件结构可看到:

其中,

1)Busses: I2C总线驱动相关的文件。例如i2c-s3c2410.c.

2)Chips: I2C设备驱动相关文件。例如:eeprom.c、m41t00.c.

3)Algos:i2c通信相关,使能中断、启动传输、i2c寄存器操作等。

4)i2c-core.c:实现了I2C核心的功能,例如:I2C总线的初始化、注册和适配器添加和注销等相关工作以及/proc/bus/i2c*接口。
5)i2c-dev.c:提供了通用的read、 write和ioctl等接口,实现了I2C适配器设备文件的功能。

二、源码分析

简单的结构图:

 第一部分: i2c总线驱动程序 drivers/i2c/busses/i2c-s3c2410.c

 static struct platform_driver s3c2440_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.resume = s3c24xx_i2c_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2440-i2c",
},
}; static int __init i2c_adap_s3c_init(void)
{
int ret;
ret = platform_driver_register(&s3c2410_i2c_driver);//注册平台platform_driver
if (ret == ) {
ret = platform_driver_register(&s3c2440_i2c_driver);
if (ret)
platform_driver_unregister(&s3c2410_i2c_driver);
}
return ret;
}

进入probe函数可以看到:

(1)*设置i2c_adapter适配器结构体,i2c_adapter适配器指向s3c24xx_i2c;

    i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;

其中,s3c24xx_i2c结构体为:

这里.master_xfer  = s3c24xx_i2c_xfe函数:与i2c通信相关,使能中断、启动传输、i2c寄存器操作等功能。==》发送i2c信号函数。

(2)i2c_add_adapter(&i2c->adap);注册adapter适配器。

进入其中的i2c_register_adapter函数:

 static int i2c_register_adapter(struct i2c_adapter *adap)
{ .......
list_add_tail(&adap->list, &adapters);//添加adap到链表中
/* let legacy drivers scan this bus for matching devices */
list_for_each(item,&drivers) {
driver = list_entry(item, struct i2c_driver, list);
if (driver->attach_adapter)
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
}

1)首先将adapter放入i2c_bus_type的adapter链表中

2)然后将所有的i2c设备调出来,执行i2c_driver设备的attach_adapter函数进行匹配;

第二部分:i2c设备驱动,以driver/i2c/chips/eeprom.c 设备驱动为例

利用i2c_add_driver分配eeprom_driver结构体:

进入i2c_add_driver函数,可看到调用的int i2c_register_driver函数:

  int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{  .....................
   list_add_tail(&driver->list,&drivers);
   pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
  /* legacy drivers scan i2c busses directly */
  if (driver->attach_adapter) {
  struct i2c_adapter *adapter;
   list_for_each_entry(adapter, &adapters, list) {
  driver->attach_adapter(adapter);
}

1)首先添加driver到driver链表中

2)然后取出adapters链表中所有的i2c_adapter, 然后调用了i2c_driver->attach_adapter()函数,进行匹配。

接下来会进入eeprom_driver结构体的eeprom_attach_adapter函数:

其中,第1个参数就是i2c_adapter适配器, 参数addr_data存放了I2C设备地址的信息, 调用i2c_probe_address 发出S信号,发出设备地址(来自addr_data),最终会调用到adap->algo->master_xfer函数发信号,如果如果收到ACK信号,就调用eeprom_detect()回调函数来注册i2c_client结构体,该结构体对应真实的物理从设备。

这里的addr_data为 i2c_client_address_data结构体:

 struct i2c_client_address_data {
unsigned short *normal_i2c; //存放正常的设备高7位地址数据
unsigned short *probe; //存放不受*ignore影响的高7位设备地址数据
unsigned short *ignore; //存放*ignore的高7位设备地址数据
unsigned short **forces; //forces表示适配器匹配不了该设备,也要将其放入适配器中 };

例如:DS1374.c设备驱动程序中:

接下来的i2c_probe函数的调用顺序为:

i2c_probe(adapter, &addr_data, eeprom_detect);
       -->i2c_probe_address // 发出S信号,发出设备地址(来自addr_data)
          -->i2c_smbus_xfer
            --> i2c_smbus_xfer_emulated
               --> i2c_transfer
                   -->adap->algo->master_xfer(adap,msgs,num);// s3c24xx_i2c_xfer

其中,在i2c_smbus_xfer_emulated函数中,

 static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data * data)
{
  unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+];
   unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+];
   int num = read_write == I2C_SMBUS_READ?:;
   struct i2c_msg msg[] = { { addr, flags, , msgbuf0 }, ///定义2个i2c_msg结构体,
   { addr, flags | I2C_M_RD, , msgbuf1 }
   };
  int i;
  u8 partial_pec = ;
  msgbuf0[] = command;
  switch(size) {
  case I2C_SMBUS_QUICK:
   msg[].len = ;
   /* Special case: The read/write field is used as data */
  msg[].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:;
  num = ;
  break;
  case I2C_SMBUS_BYTE:
  if (read_write == I2C_SMBUS_READ) {
  /* Special case: only a read! */
  msg[].flags = I2C_M_RD | flags;
  num = ;
  }
32  break;
   ....
   if (i2c_transfer(adapter, msg, num) < ) //将 i2c_msg结构体的内容发送给I2C设备
   return -;
   /* Check PEC if last message is a read */
   if (i && (msg[num-].flags & I2C_M_RD)) {
if (i2c_smbus_check_pec(partial_pec, &msg[num-]) < )
return -;
  }
  .....
}

其中,其中i2c_msg结构体为:

 struct i2c_msg {
__u16 addr;       //I2C从机的设备地址
__u16 flags;    //当flags=0表示写, flags= I2C_M_RD表示读
__u16 len; //传输的数据长度,等于buf数组里的字节数
__u8 *buf; //存放数据的数组
};

进入i2c_transfer函数的master_xfer(adap,msgs,num);即总线驱动适配器指向结构体的s3c24xx_i2c_xfer函数;其中,参数*adap表示通过哪个适配器传输出去,msgs表示I2C消息,num表示msgs的数目。

总而言之,根据以上分析,i2c_driver ->attach_adapter(adapter)函数里主要执行以下几步:

1) 调用 i2c_probe(adap, addr_data 设备地址结构体, 回调函数);

2) 将要发的设备地址结构体打包成i2c_msg,

3) 然后执行i2c_transfer()来调用i2c_adapter->algo->master_xfer()将i2c_msg发出去

4) 若收到ACK回应,便进入回调函数,注册i2c_client从设备,使该设备与适配器联系在一起。

第三部分:通过回调函数eeprom_detect注册和设置i2c_client从设备(即结构体),使用i2c_transfer()来实现与设备进行后续的传输数据了。

eeprom_detect函数的代码如下:

 static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
{
  struct i2c_client *new_client; //分配i2c_client结构体
  struct eeprom_data *data;
  int err = ;
     ....
  new_client = &data->client; //设置i2c_client结构体
  memset(data->data, 0xff, EEPROM_SIZE);
   i2c_set_clientdata(new_client, data);
   new_client->addr = address;
   new_client->adapter = adapter;
   new_client->driver = &eeprom_driver;
   new_client->flags = ;
   .....
    /* Tell the I2C layer a new client has arrived */
  if ((err = i2c_attach_client(new_client))) //注册i2c_client结构体
  goto exit_kfree;
   .....
   exit_detach:
     i2c_detach_client(new_client);
   exit_kfree:
     kfree(data);
}

在函数中主要做的以下工作:1)分配i2c_client结构体;2)设置i2c_client结构体;3)注册i2c_client结构体。之后就可以用i2c_transfer()来传输数据了。

9.1 IIC驱动源码分析的更多相关文章

  1. (转)Linux设备驱动之HID驱动 源码分析

    //Linux设备驱动之HID驱动 源码分析 http://blog.chinaunix.net/uid-20543183-id-1930836.html HID是Human Interface De ...

  2. Android USB驱动源码分析(-)

    Android USB驱动中,上层应用协议里最重要的一个文件是android/kernel/drivers/usb/gadget/android.c.这个文件实现USB的上层应用协议. 首先包含了一些 ...

  3. imx6ul linux4.1.15 LED驱动配置及heartbeat源码分析【转】

    本文转载自:https://blog.csdn.net/u010444107/article/details/78328807 1)查看内核配置wujun@wj-vBox:~/freescale/li ...

  4. MyBatis源码分析(5)——内置DataSource实现

    @(MyBatis)[DataSource] MyBatis源码分析(5)--内置DataSource实现 MyBatis内置了两个DataSource的实现:UnpooledDataSource,该 ...

  5. 《深入理解Spark:核心思想与源码分析》(第2章)

    <深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...

  6. Linux内核源码分析方法

    一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都 ...

  7. YII框架源码分析(百度PHP大牛创作-原版-无广告无水印)

           YII 框架源码分析    百度联盟事业部——黄银锋 目 录 1. 引言 3 1.1.Yii 简介 3 1.2.本文内容与结构 3 2.组件化与模块化 4 2.1.框架加载和运行流程 4 ...

  8. 高性能网络I/O框架-netmap源码分析

    from:http://blog.chinaunix.net/uid-23629988-id-3594118.html 博主这篇文章写的很好 感觉很有借签意义 值得阅读 高性能网络I/O框架-netm ...

  9. Monkey源码分析之事件注入

    本系列的上一篇文章<Monkey源码分析之事件源>中我们描述了monkey是怎么从事件源取得命令,然后将命令转换成事件放到事件队列里面的,但是到现在位置我们还没有了解monkey里面的事件 ...

随机推荐

  1. matlab练习程序(全景图到穹顶图)

    这个程序我最初是用FreeImage写的,这两天改成了matlab,再不贴上来,我就要忘了. 看到一篇文章有这样的变换,挺有意思的,就拿来试了一下,文章点此. 全景图到穹顶图变换,通俗的说就是将全景图 ...

  2. Azure 9 月新发布

    亲爱的小伙伴们, 我们很高兴向您宣布以下新功能与相关调整,欢迎关注与使用. 1. SQL 数据库弹性池  2. 存储指标更新 3. SQL 数据库 P15 4. Azure 高级存储 5. Wosig ...

  3. MongoDB数据库 备份 还原

    MongoDB数据库 1.备份用        mongodump 2.还原用        mongorestore 1.备份 @echo offecho 正在备份MongoDB数据库SET mon ...

  4. 配置karma支持Chrome浏览器

    准备:项目中已搭建好了karma. 前言:利用vue-cli初始化创建vue项目时,已经搭建好了测试框架 karma+mocha,但是此时karma默认启动的浏览器是 phantomjs,而我想用 C ...

  5. CRM中间件里的发布-订阅者模式

    从事务码SMW01里能观察到一个BDOC可能被发送往不止一个目的site去,比如下图所示的5个site都会收到该site,而高亮显示的SMOF_ERPSITE代表ERP系统QI3的client 504 ...

  6. Thread control block & thread

    https://en.wikipedia.org/wiki/Thread_control_block Thread Control Block (TCB) is a data structure in ...

  7. HDU 4278 卡特兰,区间DP

    题意:每个人有一个DI值,现在有一个小黑屋,这些人的顺序可以利用这个小黑屋调整,调整方式是入栈出栈方式,也就是说,这里的方案是有卡特兰数个方式. 调整后使得 d1*0 + d2*1 + d3*2 + ...

  8. 【转】Android listview与adapter用法

    一个ListView通常有两个职责. (1)将数据填充到布局. (2)处理用户的选择点击等操作. 第一点很好理解,ListView就是实现这个功能的.第二点也不难做到,在后面的学习中读者会发现,这非常 ...

  9. 【luogu P3369 普通平衡树(Treap/SBT)】 模板 Splay

    题目链接:https://www.luogu.org/problemnew/show/P3369 #include <cstdio> #include <algorithm> ...

  10. [LuoguP1111]修复公路

    [LuoguP1111]修复公路 题目描述: A地区在地震过后,链接所有村庄的公路都损坏了,而导致无法通车,政府派人修复这些公路. 给出A地区的N村庄数和M公路数,并且对于每一个公路给出其链接的两个村 ...