Linux的 i2c 驱动框架分析
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 驱动框架分析相关推荐
- Linux i2c驱动框架分析 (二)
Linux i2c驱动框架分析 (一) Linux i2c驱动框架分析 (二) Linux i2c驱动框架分析 (三) 通用i2c设备驱动分析 i2c core i2c核心(drivers/i2c/i ...
- I2C驱动框架分析(3):DW_I2C驱动分析
I2C驱动框架分析(1):I2C重要概念与数据结构 I2C驱动框架分析(2):I2C框架源码分析 I2C驱动框架分析(3):DW_I2C驱动分析 第三章:DW_I2C驱动 其驱动文件在drivers/ ...
- Linux下I2C驱动框架全面解析
I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...
- linux i2c核心,总线与设备驱动,Linux2.6.37 I2C驱动框架分析(一)
最近工作中又使用到了I2C,所以借S3C2440开发板GT2440为硬件平台温习一遍I2C驱动体系. linux内核中IIC驱动的体系框架 linux内核中IIC部分驱动代码位于:/drivers/i ...
- Linux下USB驱动框架分析【转】
转自:http://blog.csdn.net/brucexu1978/article/details/17583407 版权声明:本文为博主原创文章,未经博主允许不得转载. http://www.c ...
- Linux下USB驱动框架分析
http://www.cnblogs.com/general001/articles/2319552.html http://blog.csdn.net/uruita/article/details/ ...
- Linux eth phy驱动框架分析
Linux中,对于eth phy的驱动,是依附于 mdiobus的,物理上,soc内部会有 mac控制器,要想实现以太网数据收发,需要外挂一颗 phy芯片,然后 soc内部的mac控制器驱动,一般都由 ...
- linux下I2C驱动发送IO时序,I2C驱动情景分析——怎样控制I2C时序
内核版本:linux-3.4.2 源程序: linux-3.4.2\drivers\i2c\busses\I2c-s3c2410.c 这次要解决的问题是:如何配置soc的I2C模块,输出想要的时 ...
- 【linux驱动开发】i2c驱动框架之温湿度传感器htu21d
文章目录 一.linux内核i2c驱动框架 1.1 i2c适配器 1.2 i2c设备驱动 1.3 i2c设备驱动匹配过程 二.温湿度传感器htu21d 三.htu21d设备驱动编写 3.1 修改设备树 ...
最新文章
- 关于事务的传播特性和隔离级别的问题
- 搭建 springMVC 框架
- java怎么输出点,Java实现控制台输出两点间距离
- 在控制台输出九九乘法表
- android studio1.5 for mac,适用于Mac的Android Studio 1.5.x随机崩溃
- Javascript的数组对象
- Spark 和 Hadoop MapReduce 对比
- HttpUtility.UrlEncode、HttpUtility.UrlDecode、Server.UrlEncode、Server.UrlDecode的区分与应用
- centos7显示时间的时区修改
- Win2D 官方文章系列翻译 - DPI (每英寸点数)和 DIPs(设备独立像素)
- Oracle可视化工具plsqldev8.0安装详细步骤
- c语言程序设混分,若要补位上单怎么选?混分选白起,求稳选吕布,想C位直接选他!...
- android极光推送tag,Android极光推送设置别名和标签
- linux基础教程 黑鹰基地Linux运维特训班
- 民生银行java面试_民生银行面试题目 是什么?
- 最小二乘法拟合圆心和半径 python实现
- mysql 的capi预处理
- 交通大数据应用场景——高速落地检
- Linux 安装红帽 RHEL 8 详细图文教程
- ubuntu20.04安装opencv4.7