1.基本概念

总线设备驱动模型,是Linux 内核的一个基础,基本理论可以说按照大企业的分工原则,每个人只要负责自己的事情,向其他部门给出标准的接口调用,后勤部就负责后勤工作,厨房有可能跟后勤部产生工作上的沟通,不能一个厨师炒菜就去找后勤部的某个人员拿一根大白菜,而是由厨房统一申请,由后勤部门去采购再给回厨房,写代码很多时候跟生活中相识,需要遵守一定的规则,如果喜欢打擦边球,绕过规则的程序员,也很像那些走机动车道的开电动车的人们,有时候都能达到目的,但是存在车祸的风险。

总线

Linux 内核里面的总线很多,总线的工作主要是管理设备和驱动的,可以是驱动和设备耦合的媒婆,没有总线,设备找不到驱动,驱动也找不到设备,所以如果一个设备,首先要确定它是属于什么总线的,就是一个单身狗,他是想找哪个地方的妹子,需要向总线注册,告诉总线我的name是什么,对于驱动也是一样,这个驱动也要告诉总线,我是什么类型的驱动,实现了哪些接口,我的name是什么?

设备在注册的时候,向总线的设备链表添加一个设备,然后通过name,后面还有一个table_id,来查找这个总线上有没有已经实现了这个设备的驱动,如果有了,就执行这个驱动的probe函数。

驱动在注册的时候也是一样,通过name 和 table_id 匹配来查找设备,找到了就执行驱动对应的probe函数来做一系列事情。一个驱动是可以对应多个设备的,但是一个设备只能对应一个驱动。

总线设备

设备对应描述的是一个硬件,原来老的Linux 内核使用板级文件来描述设备,新的Linux 内核使用dts来描述设备,实际上是一个东西,都是用来描述设备,dts更能显示面向对象思想,也更能题先程序员的能力,比如一个I2C设备,需要描述I2C地址,I2C gpio端口,设备挂载在哪路I2C总线上等等。

总线驱动

一个驱动程序,总是要有依赖的,既然是对应的总线设备,就需要对应的总线驱动来驱动它,让硬件设备能够正常工作起来, 驱动也就是操作设备的方式和操作设备的流程还有接口。

2.I2C传输协议

3.Linux下I2C驱动程序的体系结构


Linux下的i2c框架,分为了3个子模块

1、I2C核心

I2C核心主要是i2c-core.c,里面涉及的adapter都是和i2c核心进行耦合的,设备和驱动不需要关心adapter部分。

2、I2C总线驱动

I2C总线驱动是对I2C硬件体系结构中适配器(i2c-adapter)端的实现,这部分主要是产生I2C协议的波形,操作硬件完成开始信号,数据传输,停止信号,设备应答检测等等,还是看上面那个经典的图片,adapter就是往下走的,所以就是操作到平台cpu的部分了。

3、I2C设备驱动

I2C设备驱动(i2c-client)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。

重要的文件说明

\kernel\drivers\i2c\i2c-core.c

这个文件实现了 I2C 核心的功能以及/proc/bus/i2c*接口。同时对I2C底层的收发函数进行封装。会调用i2c_transfer ,里面实现了adap->algo->master_xfer(adap, msgs, num)

kernel\drivers\i2c\i2c-dev.c

该函数注册了一个设备文件的功能,也就是注册了一个字符设备驱动程序,可以通过/dev/i2c-0(i2c-0, i2c-1,…, i2c-10,…)找到具体的I2C适配器,这个I2C设备的主设备号为89,次设备号0~255。通过访问这个接口,可以通过open()、 write()、 read()、 ioctl()和 close()等来访问这个设备。

kernel\drivers\i2c\busses\i2c-rk30.c

跟平台相关的i2c-adapter,通过这些接口,达到控制cpu的I2C控制器寄存器,然后可以在i2c总线上产生i2c信号,驱动i2c设备。

比较重要的结构体和调用过程

i2c_driver

对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。

i2c_client

对应于真实的物理设备,每个 I2C 设备都需要一个 i2c_client 来描述。i2c_client 一般被包含在 I2C 字符设备的私有信息结构体中。

i2c_adpater

用来匹配i2c_driver与i2c_client。即 i2c_client 依附于 i2c_adpater。由于一个适配器上可以连接多个 I2C 设备, 所以一个 i2c_adpater 也可以被多个 i2c_client 依附, i2c_adpater 中包括依附于它的 i2c_client 的链表 。

i2c_algorithm

i2c_adapter对应与物理上的一个适配器,而i2c_algorithm对应一套通信方法,一个i2c适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用i2c_algorithm的指针。

i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的start stop ack信号,以i2c_msg(即i2c消息)为单位发送和接收通信数据。

i2c_msg也非常关键,调用驱动中的发送接收函数需要填充该结构体

我们在驱动里面调用

这个i2c_transfer继续往下看

会跑到i2c_core.c里面的__i2c_transfer,里面会调用一个指针adap->algo->master_xfer

这个是一个指针,那我们就要找到这个指针的初始化位置

这个位置在 i2c_rk29.c里面

所以这个才是最终的产生I2C波形的位置。

贴出最终的代码

static int rk29_xfer_msg(struct i2c_adapter *adap,    struct i2c_msg *msg, int start, int stop)
{   struct rk29_i2c_data *i2c = (struct rk29_i2c_data *)adap->algo_data;    int ret = 0;   if(msg->len == 0)  {   ret = -EINVAL; i2c_err(i2c->dev, "<error>msg->len = %d\n", msg->len);    goto exit;  }   if(msg->flags & I2C_M_NEED_DELAY)    i2c->udelay = msg->udelay;   else    i2c->udelay = 0;    if((ret = rk29_send_address(i2c, msg, start))!= 0)    {   rk29_set_nak(i2c);  i2c_err(i2c->dev, "<error>rk29_send_address timeout\n"); goto exit;  }   if(msg->flags & I2C_M_RD)    {   if(msg->flags & I2C_M_REG8_DIRECT)   {   struct i2c_msg msg1 = *msg;    struct i2c_msg msg2 = *msg;    msg1.len = 1;  msg2.len = msg->len - 1;    msg2.buf = msg->buf + 1;   if((ret = rk29_i2c_send_msg(i2c, &msg1)) != 0)    i2c_err(i2c->dev, "<error>rk29_i2c_send_msg timeout\n"); if((ret = rk29_i2c_recv_msg(i2c, &msg2)) != 0)    {   i2c_err(i2c->dev, "<error>rk29_i2c_recv_msg timeout\n"); goto exit;  }   }   else if((ret = rk29_i2c_recv_msg(i2c, msg)) != 0) {   i2c_err(i2c->dev, "<error>rk29_i2c_recv_msg timeout\n"); goto exit;  }   }   else    {   if((ret = rk29_i2c_send_msg(i2c, msg)) != 0)  {   rk29_set_nak(i2c);  i2c_err(i2c->dev, "<error>rk29_i2c_send_msg timeout\n"); goto exit;  }   }   exit:   if(stop || ret < 0)  {   rk29_i2c_stop(i2c);         }   return ret; }

4.总结

相信大家学Linux 之前都做过单片机吧,不管什么总线协议,最终都是要发信号出去的,不发信号出去的总线协议框架是没有任何意义的,所以我们在这里吹牛这么多,最终还是要回到代码里面去fucking the code的。不要我说了一大堆,你还是没有知道怎么看代码,那也是没有任何作用的。

也就是device与driver同时向i2c总线上注册。当注册在总线上时,可以通过id_table进行匹配,匹配上之后会调用driver的probe函数。对于一般的I2C设备,可以在probe函数中注册一个字符设备驱动,从而应用层可以通过open函数打开/dev/i2c-0等设备节点对I2C设备进行读写操作。

I2C只是驱动的一部分,我们需要把I2C糅合到Input子系统里面,糅合到摄像头V4L2里面等等,这些都是需要去看代码了解里面的脉络的,我很多时候总是担心自己太水讲了大家也不懂,然后就把图贴上来,i2c到最后还是通过writel readl等函数读取IO部分,只有你有个代码,跟进去看看就知道了。

好了,就说这么多,使劲评论。

扫码或长按关注

回复「 加群 」进入技术群聊

Linux的 i2c 驱动框架分析相关推荐

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

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

  2. I2C驱动框架分析(3):DW_I2C驱动分析

    I2C驱动框架分析(1):I2C重要概念与数据结构 I2C驱动框架分析(2):I2C框架源码分析 I2C驱动框架分析(3):DW_I2C驱动分析 第三章:DW_I2C驱动 其驱动文件在drivers/ ...

  3. Linux下I2C驱动框架全面解析

    I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...

  4. linux i2c核心,总线与设备驱动,Linux2.6.37 I2C驱动框架分析(一)

    最近工作中又使用到了I2C,所以借S3C2440开发板GT2440为硬件平台温习一遍I2C驱动体系. linux内核中IIC驱动的体系框架 linux内核中IIC部分驱动代码位于:/drivers/i ...

  5. Linux下USB驱动框架分析【转】

    转自:http://blog.csdn.net/brucexu1978/article/details/17583407 版权声明:本文为博主原创文章,未经博主允许不得转载. http://www.c ...

  6. Linux下USB驱动框架分析

    http://www.cnblogs.com/general001/articles/2319552.html http://blog.csdn.net/uruita/article/details/ ...

  7. Linux eth phy驱动框架分析

    Linux中,对于eth phy的驱动,是依附于 mdiobus的,物理上,soc内部会有 mac控制器,要想实现以太网数据收发,需要外挂一颗 phy芯片,然后 soc内部的mac控制器驱动,一般都由 ...

  8. linux下I2C驱动发送IO时序,I2C驱动情景分析——怎样控制I2C时序

    内核版本:linux-3.4.2 源程序:    linux-3.4.2\drivers\i2c\busses\I2c-s3c2410.c 这次要解决的问题是:如何配置soc的I2C模块,输出想要的时 ...

  9. 【linux驱动开发】i2c驱动框架之温湿度传感器htu21d

    文章目录 一.linux内核i2c驱动框架 1.1 i2c适配器 1.2 i2c设备驱动 1.3 i2c设备驱动匹配过程 二.温湿度传感器htu21d 三.htu21d设备驱动编写 3.1 修改设备树 ...

最新文章

  1. 关于事务的传播特性和隔离级别的问题
  2. 搭建 springMVC 框架
  3. java怎么输出点,Java实现控制台输出两点间距离
  4. 在控制台输出九九乘法表
  5. android studio1.5 for mac,适用于Mac的Android Studio 1.5.x随机崩溃
  6. Javascript的数组对象
  7. Spark 和 Hadoop MapReduce 对比
  8. HttpUtility.UrlEncode、HttpUtility.UrlDecode、Server.UrlEncode、Server.UrlDecode的区分与应用
  9. centos7显示时间的时区修改
  10. Win2D 官方文章系列翻译 - DPI (每英寸点数)和 DIPs(设备独立像素)
  11. Oracle可视化工具plsqldev8.0安装详细步骤
  12. c语言程序设混分,若要补位上单怎么选?混分选白起,求稳选吕布,想C位直接选他!...
  13. android极光推送tag,Android极光推送设置别名和标签
  14. linux基础教程 黑鹰基地Linux运维特训班
  15. 民生银行java面试_民生银行面试题目 是什么?
  16. 最小二乘法拟合圆心和半径 python实现
  17. mysql 的capi预处理
  18. 交通大数据应用场景——高速落地检
  19. Linux 安装红帽 RHEL 8 详细图文教程
  20. ubuntu20.04安装opencv4.7

热门文章

  1. ajax请求模拟登录
  2. 关于shell脚本编程一些有用资源的小结
  3. 不同技术团队的配合问题及DevOps
  4. 加载一张图片到ImageView到底占据多少内存
  5. laraval如何使用tdd
  6. ContextLoaderListener介绍
  7. python几种括号表示的类型
  8. g开头的C语言编程软件,C语言函数大全(g开头)
  9. n皇后问题java_经典n皇后问题java代码实现
  10. 深度学习之 RPN(RegionProposal Network)- 区域候选网络