学习目标:分析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. C# winform 跨线程修改界面

    我们可以使用invoke和bengininvoke invoke同步执行一个委托 begininvoke异步执行一个委托

  2. 15分钟完成基于Azure公有云搭建远程测试环境

  3. Flask入门 flask结构 url_for 重定向(一)

    Flask入门(一) 1 安装虚拟环境Mac,linux sudo pip install virtualenv ​ ubuntu系统 sudo apt-get install python-virt ...

  4. 【Leetcode】【Easy】Merge Sorted Array

    Given two sorted integer arrays A and B, merge B into A as one sorted array. Note:You may assume tha ...

  5. 如何将BSP应用配置成Fiori Launchpad上的一个tile

    当我们通过WebIDE或者Eclipse的插件Team Provider把一个本地开发好的UI5应用部署到了ABAP Netweaver服务器上之后,我们可以将该UI5应用配置成Fiori launc ...

  6. 每日linux命令之kill

    1.命令格式: kill[参数][进程号] 2.命令功能: 发送指定的信号到相应进程.不指定型号将发送SIGTERM(15)终止指定进程.如果任无法终止该程序可用“-KILL” 参数,其发送的信号为S ...

  7. hiho 第155周 任务分配

    最小路径覆盖会超时: 贪心思路: 按照开始时间排序,然后根据结束时间,维护一个以结束时间的单调递增的队列,每次与最快结束的任务进行比较即可: /* #include <cstdio> #i ...

  8. [19/03/24-星期日] 容器_Collection(集合、容器)之List(表,有顺序可重复)

    一. 概念&方法 Collection 表示一组对象,它是集中.收集的意思.Collection接口的两个子接口是List.Set接口. 由于List.Set是Collection的子接口,意 ...

  9. python-文件基本操作(二)

    在上一篇文章中,简单介绍了打开文件的方法以及关于读.写.追加的操作,点击此处查看. 在此篇文章中,继续介绍另外一种打开文件的方法和几种同时读写的模式. 一.打开文件方法:with 使用file()或o ...

  10. 搜索自动提示的简单模拟JQuery

    使用jQuery实现类似于百度搜索时的自动完成功能,界面效果 所示. 首先在输入框上注册keyup事件,然后在事件中通过AJAX获取JSON对象.取得数据后,每一项数据创建一个li标签,在标签上注册c ...