驱动使用smbus提供的IIC读写函数可以参考smbus-protocol.txt文档;应用层直接使用IIC读写函数读写IIC设备,应用层读写函数是由i2c-tools这个库提供的(编译的使用和应用程序放在同一目录就可以了),这个库调用的底层实际上是有驱动中i2c-dev.c提供的open、ioctl函数,其需要编译进内核或者模块,见下面b. 驱动的写法3,这种方式和前面那种方式不能一起使用,在驱动中有判断的(根据链表上的信息)

driver层驱动可以参考:max8925-i2c.c文件

driver步骤:

1、 分配/设置i2c_driver结构体

2、注册i2c_driver//可以通过module_i2c_driver()宏定义和i2c_add_driver,前者的作用和后者一样

新版本的IIC驱动框架:i2c_bus_type总线,左右两边调用总线的match函数比较右边idtable里面的名字和左右在i2c_board_info中声明的名字,如果名字相等就调用driver的probe函数

1、左边注册一个设备:i2c_client使用i2c_new_device来构造注册

(参看内核Documentation\i2c目录下的instantiating-devices文件,介绍了四种构造设备的方法)

2、右边注册一个驱动:i2c_driver使用i2c_add_driver来构造注册

1. 框架
1.1 硬件协议简介
1.2 驱动框架
1.3 bus-drv-dev模型及写程序(代码可以参考instantiating-devices)
a. 设备的4种构建方法(构造i2c_client)
a.1 定义一个i2c_board_info, 里面有:名字, 设备地址(mach_mini2440.c就是用的这种方法),根据提供的信息来构造i2c_client结构体
  然后i2c_register_board_info(busnum, i2c_board_info结构体变量...) (把它们放入__i2c_board_list链表)(busnum表示哪一个I2c总线,“0”表示第一个)
      list_add_tail(&devinfo->list, &__i2c_board_list);

  链表何时使用:(链表在i2c_scan_static_board_info 函数中被使用)
  i2c_register_adapter > i2c_scan_static_board_info > i2c_new_device(构造i2c_client)

  使用限制:必须在 i2c_register_adapter 之前 i2c_register_board_info(因为前者要使用__i2c_board_list链表里面的参数来构造i2c_client)
  所以:不适合我们动态加载insmod(程序中没有例子)

a.2 直接i2c_new_device, i2c_new_probed_device
a.2.1 i2c_new_device : 认为设备肯定存在,不管设备是不是存在,只要name相同都会调用driver的probe函数(其有个i2c_adapter(适配器)参数,适配器提供传输函数,2440只有一个适配器)
a.2.2 i2c_new_probed_device :对于"已经识别出来的设备"(probed_device),才会创建(执行i2c_new_device"new"),其有个传参函数就是用来识别设备的,可以是NULL,有默认的probe函数
i2c_new_probed_device
  probe(adap, addr_list[i]) /* 确定设备是否真实存在 */
  info->addr = addr_list[i];
  i2c_new_device(adap, info);

(上面两种方法对应的例子是第一个和第二个,driver在注册的时候因为其没有detect函数和address_list,在进入i2c_detect后就返回了,没有在探测设备创建client,client已经在左边创建了

i2c_detect

  address_list = driver->address_list;
  if (!driver->detect || !address_list)
  return 0;

)

a.3 从用户空间创建设备
创建设备
echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-0/new_device

在内核中搜索new_device,可以发现在i2c-core.c中有个宏定义,表示在操作new_device文件时会调用i2c_sysfs_new_device函数

导致i2c_new_device被调用,如果匹配driver,会调用右边driver 的probe函数

删除设备
echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device

导致i2c_unregister_device

a.4 前面的3种方法都要事先确定适配器(I2C总线,I2C控制器)(驱动框架可以参考drivers/hwmon/lm90.c)(这种方式是为了处理多个adapter的情况下,确定通讯IIC设备)
如果我事先并不知道这个I2C设备在哪个适配器上,怎么办?去class表示的所有的适配器上查找(注意:前面几种方法都是在dev.c中直接调用i2c_new_device,第四种方法是在注册drv的时候调用的)
有上一些I2C设备的地址是一样,怎么继续分配它是哪一款?用detect函数

static struct i2c_driver at24cxx_driver = {
  .class = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备 */
  .driver = {
    .name = "100ask",
    .owner = THIS_MODULE,
  },
  .probe = at24cxx_probe,
  .remove = __devexit_p(at24cxx_remove),
  .id_table = at24cxx_id_table,
  .detect = at24cxx_detect, /* 用这个函数来检测设备确实存在 */
  .address_list = addr_list, /* 这些设备的地址 */
};

(第四种方法对应的是第三个例子,提供设备地址和detect函数,没有左边的i2c_new_device,而是在i2c_add_driver中如果detect设备成功了就通过i2c_new_device创建client)
去"class表示的这一类"I2C适配器里,用"detect函数"来确定能否找到"address_list里的设备",
如果能找到就调用i2c_new_device来注册i2c_client, 这会和i2c_driver的id_table比较,
如果匹配,调用probe

i2c_add_driver
  i2c_register_driver
    a. at24cxx_driver放入i2c_bus_type的drv链表
      并且从dev链表里取出能匹配的i2c_client并调用probe(matching-but-unbound匹配但是没有挂接的设备,否则继续往下走去i2c_new_device)
    driver_register

      //进去分析会发现在调用probe的地方,drv在这里是device_driver结构体,而不是iic_driver结构体,这里应该是调用bus->probe,bus中的probe函数会把通过to_i2c_driver函数根据device_driver取得iic_driver,进而调用我们写的驱动中的probe函数

    b. 对于每一个适配器,调用__process_new_driver(这个链表会从总结左边的链表中取出每个适配器,其实总线左边链表挂接了client和adapter)
      对于每一个适配器,调用它的函数确定address_list里的设备是否存在
      如果存在,再调用detect进一步确定、设置,然后i2c_new_device,在detect中设置i2c_board_info->type,其值会传给新建的device,同时在i2c_new_device的时候与之前i2c_driver中的idtable比较名字,一样则会调用driver中的probe函数
      /* Walk the adapters that are already present */
    i2c_for_each_dev(driver, __process_new_driver);
        __process_new_driver
          i2c_do_add_adapter
            /* Detect supported devices on that bus, and instantiate them */
            i2c_detect(adap, driver);

              if(! driver->detect || !(address_list) return 0//没有detect或者address_list,会直接返回
              for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
                err = i2c_detect_address(temp_client, driver);
                  /* 判断这个设备是否存在:简单的发出S信号确定有ACK */
                  if (!i2c_default_probe(adapter, addr))
                    return 0;

                  memset(&info, 0, sizeof(struct i2c_board_info));
                  info.addr = addr;

                  // 设置info.type
                  err = driver->detect(temp_client, &info);//该函数一般处理当两款IIC设备地址相同的情况,进一步来确认设备

                  i2c_new_device

i2c_new_device

  device_register

    device_add(&udev->dev);
                                                            bus_add_device(dev);/
                                                            bus_probe_device(dev);
                                                                    device_attach(dev);
                                                                           //对所有的驱动,调用__device_attach判断设备与驱动是否匹配
                                                                          bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
                                                                                 driver_match_device(drv, dev)
                                                                                          //调用总线下的match函数来判断设备与驱动时候是否匹配
                                                                                         drv->bus->match ? drv->bus->match(dev, drv) : 1;
                                                                                  driver_probe_device(drv, dev);
                                                                                         really_probe(dev, drv);

b. 驱动的写法

A、左边注册一个设备i2c_client;(client中有adapter)

B、右边注册一个驱动i2c_driver;(调用其probe函数的时候,会传入匹配的client,调用内核的读写函数就可以收发数据,这些函数会传入client)

C、比较他们的名字,如果相同,则调用probe函数;

D、probe函数里,register_chrdev

2. 完善设备驱动程序

3. 不自己写驱动直接访问((或者参考内核中dev-interface文档,里面有参考应用程序,怎么通过访问/dev/i2c-%c设备来直接通过iic-tool工具包访问iic设备,访问的其实是i2c-dev.c中注册的iic驱动程序))

Device Drivers
    I2C support
        <*> I2C device interface

4. 编写"总线(适配器adapter)"驱动(内核只带的适配器驱动是i2c-s3c2410.c,可以通过make menuconfig查看对应的CONFIGXXXX来确定)

Device Drivers
  I2C support
    I2C Hardware Bus support
      < > S3C2410 I2C Driver

nfs 30000000 192.168.1.123:/work/nfs_root/uImage_noi2cbus; bootm 30000000

18.1 IIC驱动程序(基于3.4.2内核)的更多相关文章

  1. Linux系统启动那些事—基于Linux 3.10内核【转】

    转自:https://blog.csdn.net/shichaog/article/details/40218763 Linux系统启动那些事—基于Linux 3.10内核 csdn 我的空间的下载地 ...

  2. [转]Linux芯片级移植与底层驱动(基于3.7.4内核)

      1.   SoC Linux底层驱动的组成和现状 为了让Linux在一个全新的ARM SoC上运行,需要提供大量的底层支撑,如定时器节拍.中断控制器.SMP启动.CPU hotplug以及底层的G ...

  3. 基于x86架构的内核Demo的详细开发文档

    http://hurlex.0xffffff.org/ 这里是hurlex这个基于x86架构的内核Demo的详细开发文档, 包含PDF文档和生成PDF的XeLaTex源码和文档每章节的阶段代码. 你可 ...

  4. 18、IIC总线驱动程序

    i2c_s3c2410.c是内核自带dev层(adapt)驱动程序,知道怎么发收数据,不知道含义 在与i2c_s3c2410.c(在其probe函数中的s3c24xx_i2c_init函数会初始化ii ...

  5. 基于tiny4412的Linux内核移植 -- MMA7660驱动移植(九)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  6. 基于tiny4412的Linux内核移植 -- 设备树的展开

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  7. 基于tiny4412的Linux内核移植 -- MMA7660驱动移植(九-2)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  8. 基于Linux-3.9.4内核的GDB跟踪系统调用实验

    382 + 原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/ 一.实验环境 win10 -> VMware -> Ubuntu1 ...

  9. 基于tiny4412的Linux内核移植 -- 设备树的展开【转】

    转自:https://www.cnblogs.com/pengdonglin137/p/5248114.html#_lab2_3_1 阅读目录(Content) 作者信息 平台简介 摘要 正文 一.根 ...

随机推荐

  1. notepad++go语法高亮文件

    notepad++go语法高亮文件 下载 右键另存为下载后在语言栏中的自定义面板中直接导入,重启即可

  2. python自学群里遇到的小题汇总

    题目一: 请使在3秒内计算出一组的数据,偶数在奇数前(注意不使用for while等循环的方法)格式如下1,2,3,4,5,6,7,8,9,10输出结果是2,1,4,3,6,5,8,7,10,9 解决 ...

  3. 洛谷 P2958 [USACO09OCT]木瓜的丛林Papaya Jungle

    P2958 [USACO09OCT]木瓜的丛林Papaya Jungle 题目描述 Bessie has wandered off the farm into the adjoining farmer ...

  4. poj3244(公式题)

    Difference between Triplets Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 2476   Acce ...

  5. python核心编程五——映像和集合

    1.字典 不同意一个键相应多个值:当有键发生冲突(即.字典键反复赋值),取最后(近期)的赋值. >>> dict1 = {' foo':789, 'foo': 'xyz'}     ...

  6. 正确理解Widget::Widget(QWidget *parent) :QWidget(parent)这句话(初始化列表中无法直接初始化基类的数据成员,所以你需要在列表中指定基类的构造函数)

    最近有点忙,先发一篇我公众号的文章,以下是原文. /********原文********/ 最近很多学习Qt的小伙伴在我的微信公众号私信我,该如何理解下面段代码的第二行QWidget(parent) ...

  7. 2.Xml与多个对象的映射(聚合或组合)及注意事项

    在我们的实际应用中,Xml中的结构往往不止这么简单,一般都会有2,3层.也就是说如果映射成对象就是聚合(组合)的情况 . 就用我们上一章的例子继续来讲,简单我们的Book的author现在不止是一个S ...

  8. BZOJ1396: 识别子串(后缀自动机,线段树)

    Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample I ...

  9. 【Codeforces Round #455 (Div. 2) C】 Python Indentation

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 一个for循环之后. 下一个写代码的地方一是从(x+1,y+1)开始的 然后如果写完了一个simple statement 下次就有 ...

  10. [React] Call setState with null to Avoid Triggering an Update in React 16

    Sometimes it’s desired to decide within an updater function if an update to re-render should be trig ...