权力的游戏无删百度云百度云

11273人阅读
内核接口(3)
1、通信接口
i2c发送或者接收一次数据都以数据包&struct i2c_msg&封装
struct i2c_msg {
// 从机地址
#define I2C_M_TEN
// 十位地址标志
#define I2C_M_RD
// 接收数据标志
// 数据长度
// 数据指针
其中addr为从机地址;flags则是这次通信的标志,发送数据为0,接收数据则为 I2C_M_RD;len为此次通信的数据字节数;buf 为发送或接收数据的指针。在设备驱动中我们通常调用 i2c-core 定义的接口&i2c_master_send 和&i2c_master_recv 来发送或接收一次数据。
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
struct i2c_adapter *adap=client-&
// 获取adapter信息
struct i2c_
// 定义一个临时的数据包
msg.addr = client-&
// 将从机地址写入数据包
msg.flags = client-&flags & I2C_M_TEN;
// 将从机标志并入数据包
// 将此次发送的数据字节数写入数据包
msg.buf = (char *)
// 将发送数据指针写入数据包
ret = i2c_transfer(adap, &msg, 1);
// 调用平台接口发送数据
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count :
// 如果发送成功就返回字节数
EXPORT_SYMBOL(i2c_master_send);
i2c_master_send 接口的三个参数:client 为此次与主机通信的从机,buf 为发送的数据指针,count 为发送数据的字节数。
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
struct i2c_adapter *adap=client-&
// 获取adapter信息
struct i2c_
// 定义一个临时的数据包
msg.addr = client-&
// 将从机地址写入数据包
msg.flags = client-&flags & I2C_M_TEN;
// 将从机标志并入数据包
msg.flags |= I2C_M_RD;
// 将此次通信的标志并入数据包
// 将此次接收的数据字节数写入数据包
ret = i2c_transfer(adap, &msg, 1);
// 调用平台接口接收数据
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count :
// 如果接收成功就返回字节数
EXPORT_SYMBOL(i2c_master_recv);
i2c_master_recv&接口的三个参数:client 为此次与主机通信的从机,buf 为接收的数据指针,count 为接收数据的字节数。我们看一下&i2c_transfer&接口的参数说明:
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);其中 adap 为此次主机与从机通信的适配器;msgs 为通信的数据包,这里可以是单个或多个数据包;num 用于指定数据包的个数,如果大于1则表明将进行不止一次的通信。通信一次就需要寻址一次,如果需要多次通信就需要多次寻址,前面2个接口都是进行一次通信,所以 num 为1;有的情况下我们要读一个寄存器的值,就需要先向从机发送一个寄存器地址然后再接收数据,这样如果想自己封装一个接口就需要将 num 设置为2。接口的返回值如果失败则为负数,如果成功则返回传输的数据包个数。比如读一个寄存器的接口可以按照如下方式封装:
static int read_reg(struct i2c_client *client, unsigned char reg, unsigned char *data)
struct i2c_msg msgs[] = {
.addr = client-&addr,
.flags = 0,
.buf = &reg,
// 寄存器地址
.addr = client-&addr,
.flags = I2C_M_RD,
.buf = data,
// 寄存器的值
ret = i2c_transfer(client-&adapter, msgs, 2);
// 这里 num = 2,通信成功 ret = 2
if (ret & 0)
tp_err(&%s error: %d\n&, __func__, ret);
还可调用前面所述的接口封装:
static unsigned char read_reg(struct i2c_client *client, unsigned char reg)
i2c_master_send(client, &reg, 1);
// 发送寄存器地址
i2c_master_recv(client, &buf, 1);
// 接收寄存器的值
}2、reset 接口
最近因为平台的i2c总线经常发生死锁,用逻辑分析仪检测发现通常为SDA和SCL都被拉低,于是在i2c-core中加入了reset机制,总体思路如下:
(1)在i2c.driver和i2c.adapter的结构中加入reset接口,即每一个i2c设备都可以注册reset函数,每条i2c总线都有相应的reset接口
(2)当发生死锁时,首先根据i2c-timeout的信息获取当前通信的设备地址和总线编号,然后依次执行当前总线下所有i2c设备的reset函数,再尝试发送是否成功;如果总线仍然处于死锁状态则执行i2c.adapter的reset函数;如果总线还是处于死锁状态就重启机器;总共3层reset机制
(3)i2c.driver的reset函数一般操作设备的reset pin或者电源(需要根据硬件设计进行相应操作)
(4)i2c.adapter的reset函数首选进行SCL的模拟解锁方案,然后再是操作整个总线上设备的电源(需要根据硬件设计进行相应操作)
(5)重启是最后的一层机制,此时无法恢复设备的正常使用就只能重启了
因为i2c.adapter层的需要,在i2c-core中加入了遍历当前总线所有设备并执行设备reset函数的接口i2c_reset_device:
* i2c_reset_device - reset I2C device when bus dead
* @adapter: the adapter being reset
* @addr: the device address
static int __i2c_reset_device(struct device *dev, void *addrp)
struct i2c_client *client = to_i2c_client(dev);
int addr = *(int *)
if (client && client-&driver && client-&driver-&reset)
return client-&driver-&reset();
int i2c_reset_device(struct i2c_adapter *adapter, int addr)
return device_for_each_child(&adapter-&dev, &addr, __i2c_reset_device);
EXPORT_SYMBOL(i2c_reset_device);
需要注意的是i2c.driver的reset函数返回值需要为0,不然device_for_each_child不会继续后面的遍历。用GPIO模拟SCL解锁的参考代码如下:
static int i2c_reset_adapter(void)
int counter = 0;
gpio_request(I2C_BUS_DATA, &gpioxx&);
gpio_request(I2C_BUS_CLK, &gpioxx&);
/* try to recover I2C bus */
gpio_direction_input(I2C_BUS_DATA);
if (!__gpio_get_value(I2C_BUS_DATA)) {
while((!__gpio_get_value(I2C_BUS_DATA)) && ++counter & 10)
udelay(5);
gpio_direction_output(I2C_BUS_CLK, 1);
udelay(5);
gpio_direction_output(I2C_BUS_CLK, 0);
i2c_err(&try to recover i2c bus, retry times are %d\n&,counter);
if (counter & 10) {
udelay(5);
gpio_direction_output(I2C_BUS_DATA, 0);
udelay(5);
gpio_direction_output(I2C_BUS_CLK, 1);
udelay(5);
gpio_direction_output(I2C_BUS_DATA, 1);
msleep(10);
i2c_err(&try to recover i2c bus failed!\n&);
gpio_free(I2C_BUS_DATA);
gpio_free(I2C_BUS_CLK);
3、i2c_detect 接口
最近在看一个 wifi 驱动的时候发现了另外一种注册 i2c 设备的方式,其实也是属于动态注册的一种,不过内核支持得更好,可以用内核提供的宏来实现。该方法以&struct i2c_client_address_data 结构为基础,我们先来看下这个结构的组成:
struct i2c_client_address_data {
const unsigned short *normal_i2c;
/* 常规探测地址 */
const unsigned short *
/* 常规的通道和地址 */
const unsigned short *
/* 过滤的通道和地址 */
const unsigned short * const *
/* 强制的通道和地址 */
};这个结构定义了 4 个指向不同数组的指针,数组元素都以 I2C_CLIENT_END 结尾,其中 normal_i2c 指向的数组保存的是常规探测地址,而且该数组中的地址如果和 ignore 数组中的地址相同则会被过滤掉,常见定义如下:
static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
/* 该数组定义了3个可能的设备地址 */
其他 3 个数组的元素依次是通道和地址成对的组合,而且元素个数最好定义为偶数个即最后两个 I2C_CLIENT_END。在探测过程中,i2c_detect 函数会遍历系统中所有的 i2c 通道,在每个通道中依次探测地址,顺序是&forces -& probe -& normal_i2c,比如在探测 forces 里面的通道和地址时,会先比较当前通道和数组里面定义的通道是否一致,不一致则不会继续探测该对地址,如果定义的是&ANY_I2C_BUS&则任意通道都会探测该对地址,接着探测地址会调用
driver-&detect 接口,在该接口中一般会去读取设备的某个寄存器值以判断设备是否存在,如果设备存在则返回 0 值,如果不存在则返回&-ENODEV,无论返回 0 值或者 -ENODEV 都会继续后面的探测,如果返回其他错误值则会结束整个探测过程。一个常见的例子如下:
static const struct i2c_device_id wifi_core_i2c_id[] = {{WIFI_CORE_I2C_DEVNAME, 0}, {}};
MODULE_DEVICE_TABLE(i2c, wifi_core_i2c_id);
static unsigned short wifi_core_force[] = {ANY_I2C_BUS, WIFI_CORE_ADDR, I2C_CLIENT_END, I2C_CLIENT_END};
static const unsigned short * const wifi_core_forces[] = {wifi_core_force, NULL};
static struct i2c_client_address_data wifi_core_addr_data = {
= wifi_core_forces,
static int wifi_core_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
strcpy(info-&type, WIFI_CORE_I2C_DEVNAME);
/* 如果探测成功则在detect接口中至少要设置设备名称 */
static struct i2c_driver wifi_core_driver =
.probe = wifi_core_probe,
.remove = wifi_core_remove,
.detect = wifi_core_detect,
.shutdown = wifi_shutdown,
.driver.name = WIFI_CORE_I2C_DEVNAME,
.id_table = wifi_core_i2c_id,
.address_data = &wifi_core_addr_data,
i2c_add_driver(&wifi_core_driver)
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:279174次
积分:2943
积分:2943
排名:第9398名
原创:39篇
评论:77条
(3)(1)(1)(1)(6)(1)(1)(5)(1)(5)(4)(7)(2)(1)每个时代都有一群爱“玩”的人,比如瓦特玩出了蒸汽机,……
近年来,以智能手机、智能硬件为代表的电子产品技术飞速……
也许我们有听过NASA、ESA、Jaxa这些高大上的宇航机构,但……
艾迈斯半导体中文原名奥地利微电子,2014年才正式更名。……
演讲人:李东咸, 张乃千时间: 10:00:00
演讲人:彭煜歆时间: 10:00:00
演讲人:杜复旦时间: 10:00:00
预算:¥10,000-¥50,000预算:小于¥5,000
浅谈I2C总线工作原理与应用
[导读]一.简介  I2C(Inter-Integrated Circuit)总线是一种由Philips公司开发的两线式串行总线,用于连接微控制器及其外围设备。I2C总线产生于在80年代,最初为音频和视频设备开发。由于其简单性,如今方泛用于微控制器
  I2C(Inter-Integrated Circuit)总线是一种由Philips公司开发的两线式串行总线,用于连接微控制器及其外围设备。I2C总线产生于在80年代,最初为音频和视频设备开发。由于其简单性,如今方泛用于微控制器与各种功能模块的连接,可以说是学单片机的人,入门之后,必定要涉及到的。
  I2C 总线实际上已经成为一个国际标准在超过100 种不同的IC 上实现,而且得到超过50 家公司的许可,正因为其简单和应用广泛,因此其功能也越来不满足人们的要求,其速度也从原来的100Kbit/S,增加了快速模式,其速度达400Kbit/S,再后来也增加了高速模式,其速度更达3.4Mbit/S。
  二.功能和特点
  I2C总线是一种用于IC器件之间连接的双向二线制总线,所谓总线它上面可以挂多少器件,并且通个两根线连接,占用空间非常的小,总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件。它的另一优点是多主控,只要能够进行接收和发送的设备都可以成为主控制器,当然多个主控不能同一时间工作。
  I2C总线有两根信号线,一根为SDA(数据线),一根为SCL(时钟线)。任何时候时钟信号都是由主控器件产生。
  I2C总线在传送数据的过程中,主要有三种控制信号:起始信号,结不信号,应答信号
  起始信号:当SCL为高电平时,SDA由高电平转为低电平时,开始传送数据
  结束信号:当SCL为高电平时,SDA由低电平转为高电平时,结束数据传送
  应答信号:接收数据的器件在接收到8bit数据后,向发送数据的器件发出低电平信号,表示已收到数据。这个信号可以是主控器件发出,也可以是从动器件发出。总之由接收数据的器件发出。
  这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。
  三.基本操作
  下面我们以ATMEL公司的AT24C02来介绍I2C的基本操作
  AT24C02是美国ATMEL公司的低功耗CMOS串行EEPROM,它是内含256&8bit存储空间,具有工作电压宽(2.5~5.5V)、擦写次数多(大于10000次)、写入速度快(小于10ms)等特点。他在系统中始终为从动器件。
  对AT24C02的操作主要有:字节读,字节写,页面读,页面写
  首先发送起始信号,如下图,起始信号后必须是控制字,
&&& 控制字格式如下,其中高四位为器件类型识别符(不同的芯片类型有不同的定义,EEPROM一般应为1010),接着三位为片选,也就是三个地址位,最后一位为读写控制位,当为1(Input)时为读操作,为0(Output)时为写操作。
  控制字后就是相应的操作,读或写,一定不要结束,因为这个操作还没有完成,如果结束就等于放弃操作。
  先来看写操作,写操作分为字节写和页面写两种操作,对于页面写根据芯片的一次装载的字节不同有所不同,AT24C02为8字节,每写一个字节后,地址自动加1。关于页面写的地址、应答和数据传送的时序参见图3,字节写可以看成是只有一个字节的页面写,也就是写一个数据后停止。注意:写一次需要一定时间,一般为10ms,要等侍这个操作完成。时序如下图:
  说明:对于AT24C02,在控制字后还必须写入地址,这个地址是以后读写的起始地址。
  读操作有三种基本操作:当前地址读、随机读和顺序读。三种操作方法类似,只是读的数据个数不同,可连续读8个字节,图4给出的是顺序读的时序图,图中共读了四个数据,需要注意的是当前的地址,如果不是想要的,可以用写操作,重新写入地址。非常重要的是,每读一个数据后,必须置低SDA,作为应答,否则,只能读一个数据,后面的数据,因为收到不应答信号,AT24C02就会认为出错,停止操作。特别提醒的是,当SCL为低电平时,数据是可变的,因些只有SCL为高电平时,才能读数。
&&& 四.例程序(51汇编,测试单片机为AT89C51,12M晶振)
  电路连接如图5,其中A0,A1,A2为地址线,本例中全部接地,因此全部为0。由于SCL和SDA为漏极开路输出,所以在使用时,需加上拉电阻。
  五&. 结束语
  在I2C总线的应用中应注意的事项总结为以下几点 :
  1) 严格按照时序图的要求进行操作,
  2) 若与口线上带内部上拉电阻的单片机接口连接,可以不外加上拉电阻。
  3) 程序中为配合相应的传输速率,在对口线操作的指令后可用NOP指令加一定的延时。
  4) 为了减少意外的干扰信号将EEPROM内的数据改写可用外部写保护引脚(如果有),或者在EEPROM内部没有用的空间写入标志字,每次上电时或复位时做一次检测,判断EEPROM是否被意外改写。
安立公司发布了高速串行总线接收器测试解决方案,该解决方案使用其信号质量分析仪MP1800A系列以及Granite River
Labs(GRL)开发的GRL-PCIE4-BASE-RXA 校准/接收器测试软件。GRL-PCIE4-BAS......关键字:
我 要 评 论
热门关键词I2C驱动架构
I2C驱动架构
  I2C是philips提出的外设总线.
  I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线。
  因此,I2C总线被非常广泛地应用在EEPROM,实时钟,小型LCD等设备与CPU的接口中。
linux下的驱动思路
  在linux系统下编写I2C驱动,目前主要有两种方法,一种是把I2C设备当作一个普通的字符设备来处理,另一种是利用linux下I2C驱动体系结构来完成。下面比较下这两种方法:  第一种方法:    优点:思路比较直接,不需要花很多时间去了解linux中复杂的I2C子系统的操作方法。    缺点:&&    &要求工程师不仅要对I2C设备的操作熟悉,而且要熟悉I2C的适配器(I2C控制器)操作。&&    &要求工程师对I2C的设备器及I2C的设备操作方法都比较熟悉,最重要的是写出的程序可以移植性差。&&    &对内核的资源无法直接使用,因为内核提供的所有I2C设备器以及设备驱动都是基于I2C子系统的格式。
  第一种方法的优点就是第二种方法的缺点,  第一种方法的缺点就是第二种方法的优点。
I2C架构概述
  Linux的I2C体系结构分为3个组成部分:
  I2C核心:I2C核心提供了I2C总线驱动和设备驱动的注册,注销方法,I2C通信方法(”algorithm”)上层的,与具体适配器无关的代码以及探测设备,检测设备地址的上层代码等。
  I2C总线驱动:I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。
  I2C设备驱动:I2C设备驱动(也称为客户驱动)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。
linux驱动中i2c驱动架构
  上图完整的描述了linux i2c驱动架构,虽然I2C硬件体系结构比较简单,但是i2c体系结构在linux中的实现却相当复杂。
  那么我们如何编写特定i2c接口器件的驱动程序?就是说上述架构中的那些部分需要我们完成,而哪些是linux内核已经完善的或者是芯片提供商已经提供的?
架构层次分类
  第一层:提供i2c adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号),驱动soc控制的i2c adapter在硬件上产生信号(start、stop、ack)以及处理i2c中断。覆盖图中的硬件实现层
  第二层:提供i2c adapter的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。覆盖图中的访问抽象层、i2c核心层
  第三层:实现i2c设备驱动中的i2c_driver接口,用具体的i2c device设备的attach_adapter()、detach_adapter()方法赋值给i2c_driver的成员函数指针。实现设备device与总线(或者叫adapter)的挂接。覆盖图中的driver驱动层
  第四层:实现i2c设备所对
应的具体device的驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,所以要实现具体设备device的
write()、read()、ioctl()等方法,赋值给file_operations,然后注册字符设备(多数是字符设备)。覆盖图中的driver驱动层
  第一层和第二层又叫i2c总线驱动(bus),第三第四属于i2c设备驱动(device driver)。
  在linux驱动架构中,几乎不需要驱动开发人员再添加bus,因为linux内核几乎集成所有总线bus,如usb、pci、i2c等等。并
且总线bus中的(与特定硬件相关的代码)已由芯片提供商编写完成,例如三星的s3c-2440平台i2c总线bus为/drivers/i2c
/buses/i2c-s3c2410.c
  第三第四层与特定device相干的就需要驱动工程师来实现了。
Linux下I2C体系文件构架
  在Linux内核源代码中的driver目录下包含一个i2c目录
  i2c-core.c这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口。  &&i2c-dev.c实
现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访设备时的主设备号都为89,次设备号为0-255。I2c-
dev.c并没有针对特定的设备而设计,只是提供了通用的read(),write(),和ioctl()等接口,应用层可以借用这些接口访问挂接在适配
器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。  busses文件夹这个文件中包含了一些I2C总线的驱动,如针对S3C2410,S3C2440,S3C6410等处理器的I2C控制器驱动为i2c-s3c2410.c.  algos文件夹实现了一些I2C总线适配器的algorithm.
重要的结构体
i2c_driver
1 struct i2c_driver {
2 unsigned int class;
3 int (*attach_adapter)(struct i2c_adapter *);//依附i2c_adapter函数指针
4 int (*detach_adapter)(struct i2c_adapter *);//脱离i2c_adapter函数指针
5 int (*probe)(struct i2c_client *, const struct i2c_device_id *);
6 int (*remove)(struct i2c_client *);
7 void (*shutdown)(struct i2c_client *);
8 int (*suspend)(struct i2c_client *, pm_message_t mesg);
9 int (*resume)(struct i2c_client *);
10 void (*alert)(struct i2c_client *, unsigned int data);
11 int (*command)(struct i2c_client *client, unsigned int cmd, void*arg);//命令列表
12 struct device_
13 const struct i2c_device_id *id_//该驱动所支持的设备ID表
14 int (*detect)(struct i2c_client *, struct i2c_board_info *);
15 const unsigned short *address_
16 struct list_
i2c_client
1 struct i2c_client {
unsigned short flags;//标志
unsigned short //低7位为芯片地址
char name[I2C_NAME_SIZE];//设备名称
struct i2c_adapter *//依附的i2c_adapter
struct i2c_driver *//依附的i2c_driver
struct//设备结构体
int//设备所使用的结构体
struct list_//链表头
i2c_adapter
1 struct i2c_adapter {
struct module *//所属模块
unsigned int//algorithm的类型,定义于i2c-id.h,
unsigned int class;
const struct i2c_algorithm * //总线通信方法结构体指针
void *algo_//algorithm数据
struct rt_mutex bus_//控制并发访问的自旋锁
int//重试次数
struct //适配器设备
char name[48];//适配器名称
struct completion dev_//用于同步
struct list_head userspace_//client链表头
i2c_algorithm
1 struct i2c_algorithm {
2 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);//I2C传输函数指针
3 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union
4 i2c_smbus_data *data);//smbus传输函数指针
5 u32 (*functionality) (struct i2c_adapter *);//返回适配器支持的功能
各结构体的作用与它们之间的关系
i2c_adapter与i2c_algorithm
  i2c_adapter对应与物理上的一个适配器,而i2c_algorithm对应一套通信方法,一个i2c适配器需要i2c_algorithm中提供的(i2c_algorithm中的又是更下层与硬件相关的代码提供)通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用i2c_algorithm的指针。
  i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的start stop ack信号,以i2c_msg(即i2c消息)为单位发送和接收通信数据。
  i2c_msg也非常关键,调用驱动中的发送接收函数需要填充该结构体
1 struct i2c_msg {
__u16 /* slave address
/* msg length
/* pointer to msg data
i2c_driver和i2c_client
  i2c_driver对应一套驱动方法,其主要函数是attach_adapter()和detach_client()
  i2c_client对应真实的i2c物理设备device,每个i2c设备都需要一个i2c_client来描述
  i2c_driver与i2c_client的关系是一对多。一个i2c_driver上可以支持多个同等类型的i2c_client.
i2c_adapter和i2c_client
  i2c_adapter和i2c_client的关系与i2c硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备,所以i2c_adapter中包含依附于它的i2c_client的链表。
  从i2c驱动架构图中可以看出,linux内核对i2c架构抽象了一个叫核心层core的中间件,它分离了设备驱动device driver和硬件控制的实现细节(如操作i2c的寄存器),core层不但为上面的设备驱动提供封装后的内核注册函数,而且还为小面的硬件事件提供注册接口(也就是i2c总线注册接口),可以说core层起到了承上启下的作用。
  先看一下i2c-core为外部提供的核心函数(选取部分),i2c-core对应的源文件为i2c-core.c,位于内核目录/driver/i2c/i2c-core.c
1 EXPORT_SYMBOL(i2c_add_adapter);
2 EXPORT_SYMBOL(i2c_del_adapter);
3 EXPORT_SYMBOL(i2c_del_driver);
4 EXPORT_SYMBOL(i2c_attach_client);
5 EXPORT_SYMBOL(i2c_detach_client);
7 EXPORT_SYMBOL(i2c_transfer);
  i2c_transfer()函
数:i2c_transfer()函数本身并不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的
i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正的驱动硬件流程,代码清单如下,不重要的已删除。
1 int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
if (adap-&algo-&master_xfer) {//如果master_xfer函数存在,则调用,否则返回错误
ret = adap-&algo-&master_xfer(adap,msgs,num);//这个函数在硬件相关的代码中给algorithm赋值
return -ENOSYS;
  当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册。
  相反的,在client被取消关联的时候,sysfs文件和设备也被注销,驱动开发人员在开发i2c设备驱动时,需要调用下列函数。程序清单如下
1 int i2c_attach_client(struct i2c_client *client)
device_register(&client-&dev);
device_create_file(&client-&dev, &dev_attr_client_name);
11 [cpp] view plaincopy
12 int i2c_detach_client(struct i2c_client *client)
device_remove_file(&client-&dev, &dev_attr_client_name);
device_unregister(&client-&dev);
  i2c_add_adapter()函数和i2c_del_adapter()在i2c-davinci.c中有调用,稍后分析
1 int i2c_add_adapter(struct i2c_adapter *adap)
device_register(&adap-&dev);
device_create_file(&adap-&dev, &dev_attr_name);
/* inform drivers of new adapters */
list_for_each(item,&drivers) {
driver = list_entry(item, struct i2c_driver, list);
if (driver-&attach_adapter)
/* We ig if it fails, too bad */
driver-&attach_adapter(adap);
19 int i2c_del_adapter(struct i2c_adapter *adap)
list_for_each(item,&drivers) {
driver = list_entry(item, struct i2c_driver, list);
if (driver-&detach_adapter)
if ((res = driver-&detach_adapter(adap))) {
list_for_each_safe(item, _n, &adap-&clients) {
client = list_entry(item, struct i2c_client, list);
if ((res=client-&driver-&detach_client(client))) {
device_remove_file(&adap-&dev, &dev_attr_name);
device_unregister(&adap-&dev);
  i2c-davinci.c是实现与硬件相关功能的代码集合,这部分是与平台相关的,也叫做i2c总线驱动,这部分代码是这样添加到系统中的
1 static struct platform_driver davinci_i2c_driver = {
= davinci_i2c_probe,
= davinci_i2c_remove,
= "i2c_davinci",
= THIS_MODULE,
10 /* I2C may be needed to bring up other drivers */
11 static int __init davinci_i2c_init_driver(void)
return platform_driver_register(&davinci_i2c_driver);
15 subsys_initcall(davinci_i2c_init_driver);
17 static void __exit davinci_i2c_exit_driver(void)
platform_driver_unregister(&davinci_i2c_driver);
21 module_exit(davinci_i2c_exit_driver);
  并且,i2c适配器控制硬件发送接收数据的函数在这里赋值给i2c-algorithm,i2c_davinci_xfer稍加修改就可以在裸机中控制i2c适配器
1 static struct i2c_algorithm i2c_davinci_algo = {
.master_xfer
= i2c_davinci_xfer,
.functionality
= i2c_davinci_func,
  然后在davinci_i2c_probe函数中,将i2c_davinci_algo添加到添加到algorithm系统中
1 adap-&algo = &i2c_davinci_
适配器驱动程序分析
  在linux系统中,适配器驱动位于linux目录下的\drivers\i2c\busses下,不同的处理器的适配器驱动程序设计有差异,但是总体思路不变。
  在适配器的驱动中,实现两个结构体非常关键,也是整个适配器驱动的灵魂。
  下面以某个适配器的驱动程序为例进行说明:
1 static struct platform_driver tcc_i2c_driver = {
= tcc_i2c_probe,
= tcc_i2c_remove,
= tcc_i2c_suspend_late,
= tcc_i2c_resume_early,
= THIS_MODULE,
= "tcc-i2c",
&  以上说明这个驱动是基于平台总线的,这样实现的目的是与CPU紧紧联系起来。
1 static const struct i2c_algorithm tcc_i2c_algorithm = {
.master_xfer = tcc_i2c_xfer,
.functionality = tcc_i2c_func,
  这个结构体也是非常的关键,这个结构体里面的函数tcc_i2c_xfer是适配器算法的实现,这个函数实现了适配器与I2C CORE的连接。
  tcc_i2c_func是指该适配器所支持的功能。
  tcc_i2c_xfer这个函数实质是实现I2C数据的发送与接收的处理过程。不同的处理器实现的方法不同,主要表现在寄存器的设置与中断的处理方法上。
  把握上面的两点去分析适配器程序就简单多了。
I2C-core驱动程序分析
  在I2C-core.c这个函数中,把握下面的几个关键函数就可以了。
1 //增加/删除i2c_adapter
2 int i2c_add_adapter(struct i2c_adapter *adapter)
3 int i2c_del_adapter(struct i2c_adapter *adap)
5 //增加/删除i2c_driver
6 int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
7 void i2c_del_driver(struct i2c_driver *driver)
9 //i2c_client依附/脱离
10 int i2c_attach_client(struct i2c_client *client)
12 //增加/删除i2c_driver
13 int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
14 void i2c_del_driver(struct i2c_driver *driver)
16 //i2c_client依附/脱离
17 int i2c_attach_client(struct i2c_client *client)
18 int i2c_detach_client(struct i2c_client *client)
20 //I2C传输,发送和接收
21 int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
22 int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
23 int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
  I2c_transfer这个函数实现了core与adapter的联系。
代码调用层次图&
  有时候代码比任何文字描述都来得直接,但是过多的代码展示反而让人觉得枯燥。这个时候,需要一幅图来梳理一下上面的内容
  上面这些代码的展示是告诉我们:linux内核和芯片提供商为我们的的驱动程序提供了 i2c驱动的框架,以及框架底层与硬件相关的代码的实现。
  剩下的就是针对挂载在i2c两线上的i2c设备了device,而编写的即具体设备驱动了,这里的设备就是硬件接口外挂载的设备,而非硬件接口本身(soc硬件接口本身的驱动可以理解为总线驱动)
编写驱动需要完成的工作
  编写具体的I2C驱动时,工程师需要处理的主要工作如下:
  1).提供I2C适配器的硬件驱动,探测,初始化I2C适配器(如申请I2C的I/O地址和中断号),驱动CPU控制的I2C适配器从硬件上产生。
  2).提供I2C控制的algorithm, 用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋给i2c_adapter的algo指针。
  3).实现I2C设备驱动中的i2c_driver接口,用具体yyy的
yyy_probe(),yyy_remove(),yyy_suspend(),yyy_resume()函数指针和i2c_device_id设备
ID表赋给i2c_driver的probe,remove,suspend,resume和id_table指针。
  4).实现I2C设备所对应类型的具体驱动,i2c_driver只是实现设备与总线的挂接。
  上面的工作中前两个属于I2C总线驱动,后面两个属于I2C设备驱动。
发表评论:
TA的最新馆藏[转]&[转]&

我要回帖

更多关于 权力的游戏7无删百度云 的文章

 

随机推荐