Linux2.6内核i2c驱动架构
Linux下I2C驱动架构全面分析概要
Linux下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交换数据。
第二层:提供i2c adapter的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter 的algo指针。
Linux下I2C驱动介绍
1、I2C概述I2C是philips公司提供的外设总线,I2C有两条数据线,一条是串行数据线SDA、一条是时钟线SCL,使用SDA和SCL实现了数据的交换,便于布线。
I2C总线方便用在EEPROM、实时钟、小型LCD等与CPU外部的接口上。
2、Linux下的驱动思路Linux系统下编写I2c驱动主要有两种方法:一种是把I2C当做普通字符设备来使用;另一种利用Linux下驱动的体系结构来实现。
第一种方法:优点:思路比较直接,不用花费大量时间去了解Linux系统下I2C体系结构缺点:不仅对I2C设备操作要了解,还有了解I2C的适配器操作不仅对I2C设备器和设备操作需要了解,编写的驱动移植性差,内核提供的I2C设备器都没有用上。
第二种方法:第一种的优点就是第二种的缺点,第一种的缺点就是第二种的优点。
3、I2C框架概述Linux的I2C体系结构分为3部分:1)I2C核心I2C核心提供了I2C总线驱动和设备驱动的注册和注销的方法,I2C 通信方法(algorithm)上层,与具体适配器无关的代码,检测设备上层的代码等。
2)I2C总线驱动I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可以直接受CPU来控制。
3)I2C设备驱动I2C设备驱动是对I2C硬件体系结构中设备端的实现,设备端挂在受CPU控制的适配器上,通过I2C适配器与CPU交换数据。
Linux下的I2C体系结构:1)Linux下的I2C体系结构4、I2C设备驱动编写方法首先让我们明白适配器驱动的作用是让我们能够通过它发出标准的I2C时序,在linux内核源代码中driver/I2C/buss包含一些适配器的驱动,例如s3c2410的驱动I2C-s3c2410.c,适配器被加载到内核中,接下的任务就是实现设备驱动的编写。
编写设备驱动的方法主要分为两种方法:第一种:利用设备提供的I2C-dev.c来实现I2C适配器设备文件,然后通过上层应用程序来操作I2C设备器来控制I2C设备。
i2c_rtc_rx8025驱动总结
ARM : at91-sam9x5ekLinux内核:linux-2.6.39时钟芯片:rx8025 SA/NB第一种方法:Rx8025驱动程序在该linux内核中已经包含,路径为:drivers/rtc/rtc-rx8025.c所以在内核中增加驱动只需要配置即可:1.执行make ARCH=arm menuconfig 进入菜单选项,选择RTC配置,选中EPSONRX-8025SA/NB,同时一定要去掉cpu自带的时钟。
2.i2c support 需要选中I2C Hardware Bus support中GPIO-based bitbanging I2C,否则,即使rtc驱动正确,也不能使用,提示:drivers/rtc/hctosys.c: unable to open rtc device (rtc0)。
问题源自,驱动配置正确,I2C驱动没有加载正确,udevd不能创建rtc0设备节点3.在arch/arm/mach-at91/board-sam9x5ek.c中添加信息,注意此处的name名称要与驱动程序中id_table中的名称保持一致。
而在linux某些匹配机制中,设备名称是与驱动名称相一致。
如果名称不能正确匹配,系统是不会执行probe函数的。
另外需要注意的是i2c地址,手册上给出的地址一般都是带有读写位的,而程序中的地址需要把最后一位的读写位去掉,取前面的7位。
Rx8025的地址为0x64,相应的程序中的地址应该为0x32。
否则会提示对寄存器的操作失败。
4.驱动正确配置后,生成相应的文件/dev/i2c-0/dev/rtc0/sys/bus/i2c/drivers/rtc-rx8025/sys/bus/i2c/drivers/0-0032。
嵌入式Linux 2.6内核下的I2C驱动架构及应用技术分析
嵌入式Linux 2.6内核下的I2C驱动架构及应用技术分析摘要:Linux系统下的设备-核心-总线三层的I2C驱动架构,是合理、高效开发I2C设备驱动程序的重要参考框架。
该文以结构化的视角深入分析了嵌入式Linux 系统下I2C驱动的层次结构、数据结构、驱动流程等,并着重分析了2种设备层驱动方法,对I2C驱动开发具有普遍的适用性。
关键词:嵌入式;Linux;I2C;驱动前言I2C总线是一种双线式总线,由PHILLIPS公司发明,由于其紧凑的尺寸及相对简单的时序,在嵌入式设备中获得了广泛的应用。
但是在Linux系统中,出于支持多设备、多任务的要求,I2C驱动架构变得非常复杂。
在此尝试以一种结构化的视角对嵌入式Linux下的I2C的架构及应用进行详细的阐述。
1 I2C驱动的分层结构1.1 驱动的层次构成Linux驱动按由调用层次可分为3层分别是:I2C设备层驱动、I2C核心层驱动、I2C总线(适配器)层驱动。
这3部分共同配合完成了适用性很强的I2C驱动框架。
I2C总线驱动和设备驱动通过内核驱动联系起来。
与驱动层次相对应的是I2C源码的文件层次结构。
1.2 I2C文件结构内核源码组织:I2C相关的源码位于linux kernel的i2c文件夹下:有i2c_core.c、i2c_dev.c及busses、chips、algorithm 等文件夹。
核心层功能由I2c_core.c实现。
设备层比较特殊,有2种等效的方法:①通过i2c_dev.c实现适配器文件接口(i2c_dev方法),即在应用层调用内核文件I2C_dev.C所创建的主设备节点(相当于适配器)接口函数如read、ioctl 等来访问设备。
这种方法相当于在应用层编写设备的驱动程序;②通过chips文件夹下c文件实现设备驱动文件接口(Dirver方法)。
即在chips下从设备对应的C文件中编写如XXX_command(xxx为自定义的从设备名称)等从功能函数完成设备的访问流程,并在应用层调用此功能函数。
基于嵌入式Linux 2.6内核的I2C总线驱动
基于嵌入式Linux 2.6内核的I2C总线驱动
袁建华;万频
【期刊名称】《现代计算机(专业版)》
【年(卷),期】2008(000)012
【摘要】分析Linux系统中I2C驱动程序的结构,以Intel公司的PXA270芯片为例,详细介绍PXA270微处理器内置的I2C总线的组成、时序、总线驱动,以及如何在嵌入式Linux系统中实现I2C总线适配器及I2C设备驱动.通过其驱动程序分析了整个I2C驱动各个数据结构的错综复杂的关系,对可能存在的问题进行简要的分析并提出解决方案.
【总页数】4页(P157-160)
【作者】袁建华;万频
【作者单位】广东工业大学自动化学院,广州,510006;广东工业大学自动化学院,广州,510006
【正文语种】中文
【相关文献】
1.基于Linux
2.6内核开发MiniGUI的触摸屏驱动 [J], 高洁;罗凌江
2.基于AT91RM9200和嵌入式Linux的I2C总线驱动程序 [J], 张圣华;喻晓峰;石崇林
3.基于BF533的uClinux2.6中I2C总线驱动设计与实现 [J], 牛凡
4.嵌入式Linux2.6内核的CAN驱动设计与实现 [J], 张雪松;王鸿磊;徐钊
5.基于嵌入式Linux2.6的CAN设备驱动程序设计 [J], 陈华
因版权原因,仅展示原文概要,查看原文内容请购买。
实例解析linux内核I2C体系结构(1)
实例解析linux内核I2C体系结构(1)华清远见刘洪涛一、概述谈到在linux系统下编写I2C驱动,目前主要有两种方式,一种是把I2C设备当作一个普通的字符设备来处理,另一种是利用linux I2C驱动体系结构来完成。
下面比较下这两种驱动。
第一种方法的好处(对应第二种方法的劣势)有:●思路比较直接,不需要花时间去了解linux内核中复杂的I2C子系统的操作方法。
第一种方法问题(对应第二种方法的好处)有:●要求工程师不仅要对I2C设备的操作熟悉,而且要熟悉I2C的适配器操作;●要求工程师对I2C的设备器及I2C的设备操作方法都比较熟悉,最重要的是写出的程序可移植性差;●对内核的资源无法直接使用。
因为内核提供的所有I2C设备器及设备驱动都是基于I2C子系统的格式。
I2C适配器的操作简单还好,如果遇到复杂的I2C适配器(如:基于PCI的I2C适配器),工作量就会大很多。
本文针对的对象是熟悉I2C协议,并且想使用linux内核子系统的开发人员。
网络和一些书籍上有介绍I2C子系统的源码结构。
但发现很多开发人员看了这些文章后,还是不清楚自己究竟该做些什么。
究其原因还是没弄清楚I2C子系统为我们做了些什么,以及我们怎样利用I2C子系统。
本文首先要解决是如何利用现有内核支持的I2C适配器,完成对I2C设备的操作,然后再过度到适配器代码的编写。
本文主要从解决问题的角度去写,不会涉及特别详细的代码跟踪。
二、I2C设备驱动程序编写首先要明确适配器驱动的作用是让我们能够通过它发出符合I2C标准协议的时序。
在Linux内核源代码中的drivers/i2c/busses目录下包含着一些适配器的驱动。
如S3C2410的驱动i2c-s3c2410.c。
当适配器加载到内核后,接下来的工作就要针对具体的设备编写设备驱动了。
编写I2C设备驱动也有两种方法。
一种是利用系统给我们提供的i2c-dev.c来实现一个i2c适配器的设备文件。
Linux设备驱动之I2C架构分析
Linux设备驱动之I2C架构分析一:前言I2c是philips提出的外设总线.I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL.正因为这样,它方便了工程人员的布线.另外,I2C是一种多主机控制总线.它和USB总线不同,USB是基于master-slave 机制,任何设备的通信必须由主机发起才可以.而 I2C 是基于multi master机制.一同总线上可允许多个master.关于I2C协议的知识,这里不再赘述.可自行下载spec阅读即可.二:I2C架构概述在linux中,I2C驱动架构如下所示:如上图所示,每一条I2C对应一个adapter.在kernel中,每一个adapter提供了一个描述的结构(struct i2c_adapter),也定义了adapter支持的操作(struct i2c_adapter).再通过i2c core层将i2c设备与i2c adapter关联起来.这个图只是提供了一个大概的框架.在下面的代码分析中,从下至上的来分析这个框架图.以下的代码分析是基于linux 2.6.26.分析的代码基本位于: linux-2.6.26.3/drivers/i2c/位置.三:adapter注册在kernel中提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter().由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为 I2C总线号.这个总线号的PCI中的总线号不同.它和硬件无关,只是软件上便于区分而已.对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分析一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败.分别来看一下这两个函数的代码:int i2c_add_adapter(struct i2c_adapter *adapter){int id, res = 0;retry:if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)return -ENOMEM;mutex_lock(&core_lock);/* "above" here means "above or equal to", sigh */res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);mutex_unlock(&core_lock);if (res < 0) {if (res == -EAGAIN)goto retry;return res;}adapter->nr = id;return i2c_register_adapter(adapter);}在这里涉及到一个idr结构.idr结构本来是为了配合page cache中的radix tree而设计的.在这里我们只需要知道,它是一种高效的搜索树,且这个树预先存放了一些内存.避免在内存不够的时候出现问题.所在,在往idr中插入结构的时候,首先要调用idr_pre_get()为它预留足够的空闲内存,然后再调用idr_get_new_above()将结构插入idr中,该函数以参数的形式返回一个id.以后凭这个id就可以在idr中找到相对应的结构了.对这个数据结构操作不太理解的可以查阅本站<< linux文件系统之文件的读写>>中有关radix tree的分析.注意一下 idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id)的参数的含义,它是将adapter结构插入到i2c_adapter_idr中,存放位置的id必须要大于或者等于__i2c_first_dynamic_bus_num,然后将对应的id号存放在adapter->nr中.调用i2c_register_adapter(adapter)对这个adapter进行进一步注册.看一下另外一人注册函数: i2c_add_numbered_adapter( ),如下所示:int i2c_add_numbered_adapter(struct i2c_adapter *adap){int id;int status;if (adap->nr & ~MAX_ID_MASK)return -EINVAL;retry:if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)return -ENOMEM;mutex_lock(&core_lock);/* "above" here means "above or equal to", sigh;* we need the "equal to" result to force the result*/status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);if (status == 0 && id != adap->nr) {status = -EBUSY;idr_remove(&i2c_adapter_idr, id);}mutex_unlock(&core_lock);if (status == -EAGAIN)goto retry;if (status == 0)status = i2c_register_adapter(adap);return status;}对比一下就知道差别了,在这里它已经指定好了adapter->nr了.如果分配的id不和指定的相等,便返回错误.过一步跟踪i2c_register_adapter().代码如下:static int i2c_register_adapter(struct i2c_adapter *adap){int res = 0, dummy;mutex_init(&adap->bus_lock);mutex_init(&adap->clist_lock);INIT_LIST_HEAD(&adap->clients);mutex_lock(&core_lock);/* Add the adapter to the driver core.* If the parent pointer is not set up,* we add this adapter to the host bus.*/if (adap->dev.parent == NULL) {adap->dev.parent = &platform_bus;pr_debug("I2C adapter driver [%s] forgot to specify ""physical device\n", adap->name);}sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);adap->dev.release = &i2c_adapter_dev_release;adap->dev.class = &i2c_adapter_class;res = device_register(&adap->dev);if (res)goto out_list;dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);/* create pre-declared device nodes for new-style drivers */if (adap->nr < __i2c_first_dynamic_bus_num)i2c_scan_static_board_info(adap);/* let legacy drivers scan this bus for matching devices */dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,i2c_do_add_adapter);out_unlock:mutex_unlock(&core_lock);return res;out_list:idr_remove(&i2c_adapter_idr, adap->nr);goto out_unlock;}首先对adapter和adapter中内嵌的struct device结构进行必须的初始化.之后将adapter内嵌的struct device注册.在这里注意一下adapter->dev的初始化.它的类别为i2c_adapter_class,如果没有父结点,则将其父结点设为platform_bus.adapter->dev的名字为i2c + 总线号.测试一下:[eric@mochow i2c]$ cd /sys/class/i2c-adapter/[eric@mochow i2c-adapter]$ lsi2c-0可以看到,在我的PC上,有一个I2C adapter,看下详细信息:[eric@mochow i2c-adapter]$ tree.`-- i2c-0|-- device -> ../../../devices/pci0000:00/0000:00:1f.3/i2c-0|-- name|-- subsystem -> ../../../class/i2c-adapter`-- uevent3 directories, 2 files可以看到,该adapter是一个PCI设备.继续往下看:之后,在注释中看到,有两种类型的driver,一种是new-style drivers,另外一种是legacy drivers New-style drivers是在2.6近版的kernel加入的.它们最主要的区别是在adapter和i2c driver的匹配上.3.1: new-style 形式的adapter注册对于第一种,也就是new-style drivers,将相关代码再次列出如下:if (adap->nr < __i2c_first_dynamic_bus_num)i2c_scan_static_board_info(adap);如果adap->nr 小于__i2c_first_dynamic_bus_num的话,就会进入到i2c_scan_static_board_info().结合我们之前分析的adapter的两种注册分式: i2c_add_adapter()所分得的总线号肯会不会小于__i2c_first_dynamic_bus_num.只有i2c_add_numbered_adapter()才有可能满足:(adap->nr < __i2c_first_dynamic_bus_num)而且必须要调用i2c_register_board_info()将板子上的I2C设备信息预先注册时才会更改__i2c_first_dynamic_bus_num的值.在x86上只没有使用i2c_register_board_info()的.因此,x86平台上的分析可以忽略掉new-style driver的方式.不过,还是详细分析这种情况下.首先看一下i2c_register_board_info(),如下:int __initi2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len){int status;mutex_lock(&__i2c_board_lock);/* dynamic bus numbers will be assigned after the last static one */if (busnum >= __i2c_first_dynamic_bus_num)__i2c_first_dynamic_bus_num = busnum + 1;for (status = 0; len; len--, info++) {struct i2c_devinfo *devinfo;devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);if (!devinfo) {pr_debug("i2c-core: can't register boardinfo!\n");status = -ENOMEM;break;}devinfo->busnum = busnum;devinfo->board_info = *info;list_add_tail(&devinfo->list, &__i2c_board_list);}mutex_unlock(&__i2c_board_lock);return status;}这个函数比较简单, struct i2c_board_info用来表示I2C设备的一些情况,比如所在的总线.名称,地址,中断号等.最后,这些信息会被存放到__i2c_board_list链表.跟踪i2c_scan_static_board_info():代码如下:static void i2c_scan_static_board_info(struct i2c_adapter *adapter){struct i2c_devinfo *devinfo;mutex_lock(&__i2c_board_lock);list_for_each_entry(devinfo, &__i2c_board_list, list) {if (devinfo->busnum == adapter->nr&& !i2c_new_device(adapter,&devinfo->board_info))printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",i2c_adapter_id(adapter),devinfo->board_info.addr);}mutex_unlock(&__i2c_board_lock);}该函数遍历挂在__i2c_board_list链表上面的i2c设备的信息,也就是我们在启动的时候指出的i2c 设备的信息.如果指定设备是位于adapter所在的I2C总线上,那么,就调用i2c_new_device().代码如下:struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info){struct i2c_client *client;int status;client = kzalloc(sizeof *client, GFP_KERNEL);if (!client)return NULL;client->adapter = adap;client->dev.platform_data = info->platform_data;device_init_wakeup(&client->dev, info->flags & I2C_CLIENT_WAKE);client->flags = info->flags & ~I2C_CLIENT_WAKE;client->addr = info->addr;client->irq = info->irq;strlcpy(client->name, info->type, sizeof(client->name));/* a new style driver may be bound to this device when we* return from this function, or any later moment (e.g. maybe* hotplugging will load the driver module). and the device* refcount model is the standard driver model one.*/status = i2c_attach_client(client);if (status < 0) {kfree(client);client = NULL;}return client;}我们又遇到了一个新的结构:struct i2c_client,不要被这个结构吓倒了,其实它就是一个嵌入struct device的I2C设备的封装.它和我们之前遇到的struct usb_device结构的作用是一样的.首先,在clinet里保存该设备的相关消息.特别的, client->adapter指向了它所在的adapter.特别的,clinet->name为info->name.也是指定好了的.一切初始化完成之后,便会调用i2c_attach_client( ).看这个函数的字面意思,是将clinet关联起来.到底怎么样关联呢?继续往下看:int i2c_attach_client(struct i2c_client *client){struct i2c_adapter *adapter = client->adapter;int res = 0;//初始化client内嵌的dev结构//父结点为所在的adapter,所在bus为i2c_bus_typeclient->dev.parent = &client->adapter->dev;client->dev.bus = &i2c_bus_type;//如果client已经指定了driver,将driver和内嵌的dev关联起来if (client->driver)client->dev.driver = &client->driver->driver;//指定了driver, 但不是newstyle的if (client->driver && !is_newstyle_driver(client->driver)) {client->dev.release = i2c_client_release;client->dev.uevent_suppress = 1;} elseclient->dev.release = i2c_client_dev_release;//clinet->dev的名称snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),"%d-%04x", i2c_adapter_id(adapter), client->addr);//将内嵌的dev注册res = device_register(&client->dev);if (res)goto out_err;//将clinet链到adapter->clients中mutex_lock(&adapter->clist_lock);list_add_tail(&client->list, &adapter->clients);mutex_unlock(&adapter->clist_lock);dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",client->name, client->dev.bus_id);//如果adapter->cleinet_reqister存在,就调用它if (adapter->client_register) {if (adapter->client_register(client)) {dev_dbg(&adapter->dev, "client_register ""failed for client [%s] at 0x%02x\n",client->name, client->addr);}}return 0;out_err:dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x ""(%d)\n", client->name, client->addr, res);return res;}参考上面添加的注释,应该很容易理解这段代码了,就不加详细分析了.这个函数的名字不是i2c_attach_client()么?怎么没看到它的关系过程呢?这是因为:在代码中设置了client->dev所在的bus为i2c_bus_type .以为只需要有bus为i2c_bus_type的driver注册,就会产生probe了.这个过程呆后面分析i2c driver的时候再来详细分析.3.2: legacy形式的adapter注册Legacy形式的adapter注册代码片段如下:dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,i2c_do_add_adapter);这段代码遍历挂在i2c_bus_type上的驱动,然后对每一个驱动和adapter调用i2c_do_add_adapter().代码如下:static int i2c_do_add_adapter(struct device_driver *d, void *data){struct i2c_driver *driver = to_i2c_driver(d);struct i2c_adapter *adap = data;if (driver->attach_adapter) {/* We ignore the return code; if it fails, too bad */driver->attach_adapter(adap);}return 0;}该函数很简单,就是调用driver的attach_adapter()接口.到此为止,adapter的注册已经分析完了.四:i2c driver注册在分析i2c driver的时候,有必要先分析一下i2c架构的初始化代码如下:static int __init i2c_init(void){int retval;retval = bus_register(&i2c_bus_type);if (retval)return retval;retval = class_register(&i2c_adapter_class);if (retval)goto bus_err;retval = i2c_add_driver(&dummy_driver);if (retval)goto class_err;return 0;class_err:class_unregister(&i2c_adapter_class);bus_err:bus_unregister(&i2c_bus_type);return retval;}subsys_initcall(i2c_init);很明显,i2c_init()会在系统初始化的时候被调用.在i2c_init中,先注册了i2c_bus_type的bus,i2c_adapter_class的class.然后再调用i2c_add_driver()注册了一个i2c driver.I2c_bus_type结构如下:static struct bus_type i2c_bus_type = {.name = "i2c",.dev_attrs = i2c_dev_attrs,.match = i2c_device_match,.uevent = i2c_device_uevent,.probe = i2c_device_probe,.remove = i2c_device_remove,.shutdown = i2c_device_shutdown,.suspend = i2c_device_suspend,.resume = i2c_device_resume,};这个结构先放在这里吧,以后还会用到里面的信息的.从上面的初始化函数里也看到了,注册i2c driver的接口为i2c_add_driver().代码如下: static inline int i2c_add_driver(struct i2c_driver *driver){return i2c_register_driver(THIS_MODULE, driver);}继续跟踪:int i2c_register_driver(struct module *owner, struct i2c_driver *driver){int res;/* new style driver methods can't mix with legacy ones *///如果是一个newstyle的driver.但又定义了attach_adapter/detach_adapter.非法 if (is_newstyle_driver(driver)) {if (driver->attach_adapter || driver->detach_adapter|| driver->detach_client) {printk(KERN_WARNING"i2c-core: driver [%s] is confused\n",driver->);return -EINVAL;}}/* add the driver to the list of i2c drivers in the driver core *///关联到i2c_bus_typesdriver->driver.owner = owner;driver->driver.bus = &i2c_bus_type;/* for new style drivers, when registration returns the driver core* will have called probe() for all matching-but-unbound devices.*///注册内嵌的driverres = driver_register(&driver->driver);if (res)return res;mutex_lock(&core_lock);pr_debug("i2c-core: driver [%s] registered\n", driver->);/* legacy drivers scan i2c busses directly *///遍历所有的adapter,对其都调用driver->attach_adapterif (driver->attach_adapter) {struct i2c_adapter *adapter;down(&i2c_adapter_class.sem);list_for_each_entry(adapter, &i2c_adapter_class.devices,dev.node) {driver->attach_adapter(adapter);}up(&i2c_adapter_class.sem);}mutex_unlock(&core_lock);return 0;}这里也有两种形式的区分,对于第一种,只需要将内嵌的driver注册就可以了,对于legacy的情况,对每一个adapter都调用driver->attach_adapter().现在,我们可以将adapter和i2c driver关联起来考虑一下了:1:如果是news style形式的,在注册adapter的时候,将它上面的i2c 设备转换成了struct client.struct client->dev->bus又指定了和i2c driver同一个bus.因为,它们可以发生probe.2:如果是legacy形式,就直接找到对应的对象,调用driver->attach_adapter().五: i2c_bus_type的相关操作I2c_bus_type的操作主要存在于new-style形式的驱动中.接下来分析一下对应的probe过程:5.1:match过程分析Match对应的操作函数为i2c_device_match().代码如下static int i2c_device_match(struct device *dev, struct device_driver *drv){struct i2c_client *client = to_i2c_client(dev);struct i2c_driver *driver = to_i2c_driver(drv);/* make legacy i2c drivers bypass driver model probing entirely;* such drivers scan each i2c adapter/bus themselves.*/if (!is_newstyle_driver(driver))return 0;/* match on an id table if there is one */if (driver->id_table)return i2c_match_id(driver->id_table, client) != NULL;return 0;}如果该驱动不是一个new-style形式的.或者driver没有定义匹配的id_table.都会匹配失败. 继续跟踪进i2c_match_id():static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,const struct i2c_client *client){while (id->name[0]) {if (strcmp(client->name, id->name) == 0)return id;id++;}return NULL;}由此可见.如果client的名字和driver->id_table[]中的名称匹配即为成功.5.2:probe过程分析Probe对应的函数为: i2c_device_probe()static int i2c_device_probe(struct device *dev){struct i2c_client *client = to_i2c_client(dev);struct i2c_driver *driver = to_i2c_driver(dev->driver);const struct i2c_device_id *id;int status;if (!driver->probe)return -ENODEV;client->driver = driver;dev_dbg(dev, "probe\n");if (driver->id_table)id = i2c_match_id(driver->id_table, client);elseid = NULL;status = driver->probe(client, id);if (status)client->driver = NULL;return status;}这个函数也很简单,就是将probe流程回溯到i2c driver的probe()六:其它的扩展分析完adapter和i2c driver的注册之后,好像整个架构也差不多了,其它,扩展的东西还有很多.我们举一个legacy形式的例子,这个例子是在kernel中随便搜索出来的:在linux-2.6.26.3/drivers/hwmon/ad7418.c中,初始化函数为:static int __init ad7418_init(void){return i2c_add_driver(&ad7418_driver);}i2c_driver ad7418_driver结构如下:static struct i2c_driver ad7418_driver = {.driver = {.name = "ad7418",},.attach_adapter = ad7418_attach_adapter,.detach_client = ad7418_detach_client,};该结构中没有probe()函数,可以断定是一个legacy形式的驱动.这类驱动注册的时候,会调用driver 的attach_adapter函数.在这里也就是ad7418_attach_adapter.这个函数代码如下:static int ad7418_attach_adapter(struct i2c_adapter *adapter){if (!(adapter->class & I2C_CLASS_HWMON))return 0;return i2c_probe(adapter, &addr_data, ad7418_detect);}在这里我们又遇到了一个i2c-core中的函数,i2c_probe().在分析这个函数之前,先来看下addr_data 是什么?#define I2C_CLIENT_MODULE_PARM(var,desc) \static unsigned short var[I2C_CLIENT_MAX_OPTS] = I2C_CLIENT_DEFAULTS; \static unsigned int var##_num; \module_param_array(var, short, &var##_num, 0); \MODULE_PARM_DESC(var,desc)#define I2C_CLIENT_MODULE_PARM_FORCE(name) \I2C_CLIENT_MODULE_PARM(force_##name, \"List of adapter,address pairs which are " \"unquestionably assumed to contain a `" \# name "' chip")#define I2C_CLIENT_INSMOD_COMMON \I2C_CLIENT_MODULE_PARM(probe, "List of adapter,address pairs to scan " \"additionally"); \I2C_CLIENT_MODULE_PARM(ignore, "List of adapter,address pairs not to " \"scan"); \static const struct i2c_client_address_data addr_data = { \.normal_i2c = normal_i2c, \.probe = probe, \.ignore = ignore, \.forces = forces, \}#define I2C_CLIENT_FORCE_TEXT \"List of adapter,address pairs to boldly assume to be present"由此可知道,addr_data中的三个成员都是模块参数.在加载模块的时候可以用参数的方式对其赋值.三个模块参数为别为probe,ignore,force.另外需要指出的是normal_i2c不能以模块参数的方式对其赋值,只能在驱动内部静态指定.从模块参数的模述看来, probe是指"List of adapter,address pairs to scan additionally"Ignore是指"List of adapter,address pairs not to scan "Force是指"List of adapter,address pairs to boldly assume to be present"事实上,它们里面的数据都是成对出现的.前面一部份表示所在的总线号,ANY_I2C_BUS表示任一总线.后一部份表示设备的地址.现在可以来跟踪i2c_probe()的代码了.如下:int i2c_probe(struct i2c_adapter *adapter,const struct i2c_client_address_data *address_data,int (*found_proc) (struct i2c_adapter *, int, int)){int i, err;int adap_id = i2c_adapter_id(adapter);/* Force entries are done first, and are not affected by ignoreentries *///先扫描force里面的信息,注意它是一个二级指针.ignore里的信息对它是无效的if (address_data->forces) {const unsigned short * const *forces = address_data->forces;int kind;for (kind = 0; forces[kind]; kind++) {for (i = 0; forces[kind] != I2C_CLIENT_END;i += 2) {if (forces[kind] == adap_id|| forces[kind] == ANY_I2C_BUS) {dev_dbg(&adapter->dev, "found force ""parameter for adapter %d, ""addr 0x%02x, kind %d\n",adap_id, forces[kind][i + 1],kind);err = i2c_probe_address(adapter,forces[kind][i + 1],kind, found_proc);if (err)return err;}}}}/* Stop here if we can't use SMBUS_QUICK *///如果adapter不支持quick.不能够遍历这个adapter上面的设备if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {if (address_data->probe[0] == I2C_CLIENT_END&& address_data->normal_i2c[0] == I2C_CLIENT_END)return 0;dev_warn(&adapter->dev, "SMBus Quick command not supported, ""can't probe for chips\n");return -1;}/* Probe entries are done second, and are not affected by ignoreentries either *///遍历probe上面的信息.ignore上的信息也对它是没有影响的for (i = 0; address_data->probe != I2C_CLIENT_END; i += 2) {if (address_data->probe == adap_id|| address_data->probe == ANY_I2C_BUS) {dev_dbg(&adapter->dev, "found probe parameter for ""adapter %d, addr 0x%02x\n", adap_id,address_data->probe[i + 1]);err = i2c_probe_address(adapter,address_data->probe[i + 1],-1, found_proc);if (err)return err;}}/* Normal entries are done last, unless shadowed by an ignore entry */ //最后遍历normal_i2c上面的信息.它上面的信息不能在ignore中.for (i = 0; address_data->normal_i2c != I2C_CLIENT_END; i += 1) {int j, ignore;ignore = 0;for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;j += 2) {if ((address_data->ignore[j] == adap_id ||address_data->ignore[j] == ANY_I2C_BUS)&& address_data->ignore[j + 1]== address_data->normal_i2c) {dev_dbg(&adapter->dev, "found ignore ""parameter for adapter %d, ""addr 0x%02x\n", adap_id,address_data->ignore[j + 1]);ignore = 1;break;}}if (ignore)continue;dev_dbg(&adapter->dev, "found normal entry for adapter %d, ""addr 0x%02x\n", adap_id,address_data->normal_i2c);err = i2c_probe_address(adapter, address_data->normal_i2c,-1, found_proc);if (err)return err;}return 0;}这段代码很简单,结合代码上面添加的注释应该很好理解.如果匹配成功,则会调用i2c_probe_address ().这个函数代码如下:static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,int (*found_proc) (struct i2c_adapter *, int, int)){int err;/* Make sure the address is valid *///地址小于0x03或者大于0x77都是不合法的if (addr < 0x03 || addr > 0x77) {dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",addr);return -EINVAL;}/* Skip if already in use *///adapter上已经有这个设备了if (i2c_check_addr(adapter, addr))return 0;/* Make sure there is something at this address, unless forced *///如果kind小于0.检查adapter上是否有这个设备if (kind < 0) {if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,I2C_SMBUS_QUICK, NULL) < 0)return 0;/* prevent 24RF08 corruption */if ((addr & ~0x0f) == 0x50)i2c_smbus_xfer(adapter, addr, 0, 0, 0,I2C_SMBUS_QUICK, NULL);}/* Finally call the custom detection function *///调用回调函数err = found_proc(adapter, addr, kind);/* -ENODEV can be returned if there is a chip at the given addressbut it isn't supported by this chip driver. We catch it here asthis isn't an error. */if (err == -ENODEV)err = 0;if (err)dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",addr, err);return err;}首先,对传入的参数进行一系列的合法性检查.另外,如果该adapter上已经有了这个地址的设备了.也会返回失败.所有adapter下面的设备都是以 adapter->dev为父结点的.因此只需要遍历adapter->dev下面的子设备就可以得到当前地址是不是被占用了.如果kind < 0.还得要adapter检查该总线是否有这个地址的设备.方法是向这个地址发送一个Read 的Quick请求.如果该地址有应答,则说明这个地址上有这个设备.另外还有一种情况是在24RF08设备的特例.如果adapter上确实有这个设备,就会调用驱动调用时的回调函数.在上面涉及到了IIC的传输方式,有疑问的可以参考intel ICH5手册的有关smbus部份.跟踪i2c_smbus_xfer().代码如下:s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,char read_write, u8 command, int size,union i2c_smbus_data * data){s32 res;flags &= I2C_M_TEN | I2C_CLIENT_PEC;if (adapter->algo->smbus_xfer) {mutex_lock(&adapter->bus_lock);res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,command,size,data);mutex_unlock(&adapter->bus_lock);} elseres = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data);return res;}如果adapter有smbus_xfer()函数,则直接调用它发送,否则,也就是在adapter不支持smbus协议的情况下,调用i2c_smbus_xfer_emulated()继续处理.跟进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){/* So we need to generate a series of msgs. In the case of writing, weneed to use only one message; when reading, we need two. We initializemost things with sane defaults, to keep the code below somewhatsimpler. *///写操作只会进行一次交互,而读操作,有时会有两次操作.//因为有时候读操作要先写command,再从总线上读数据//在这里为了代码的简洁.使用了两个缓存区,将两种情况统一起来.unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];//一般来说,读操作要交互两次.例外的情况我们在下面会接着分析int num = read_write == I2C_SMBUS_READ?2:1;//与设备交互的数据,一般在msg[0]存放写入设备的信息,在msb[1]里存放接收到的//信息.不过也有例外的//msg[2]的初始化,默认发送缓存区占一个字节,无接收缓存struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },{ addr, flags | I2C_M_RD, 0, msgbuf1 }};int i;u8 partial_pec = 0;//将要发送的信息copy到发送缓存区的第一字节msgbuf0[0] = command;switch(size) {//quick类型的,其它并不传输有效数据,只是将地址写到总线上,等待应答即可//所以将发送缓存区长度置为0 .再根据读/写操作,调整msg[0]的标志位//这类传输只需要一次总线交互case I2C_SMBUS_QUICK:msg[0].len = 0;/* Special case: The read/write field is used as data */msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;num = 1;break;case I2C_SMBUS_BYTE://BYTE类型指一次写和读只有一个字节.这种情况下,读和写都只会交互一次//这种类型的读有例外,它读取出来的数据不是放在msg[1]中的,而是存放在msg[0]if (read_write == I2C_SMBUS_READ) {/* Special case: only a read! */msg[0].flags = I2C_M_RD | flags;num = 1;}break;case I2C_SMBUS_BYTE_DATA://Byte_Data是指命令+数据的传输形式.在这种情况下,写只需要一次交互,读却要两次//第一次将command写到总线上,第二次要转换方向.要将设备地址和read标志写入总线. //应回答之后再进行read操作//写操作占两字节,分别是command+data.读操作的有效数据只有一个字节//交互次数用初始化值就可以了if (read_write == I2C_SMBUS_READ)msg[1].len = 1;else {msg[0].len = 2;msgbuf0[1] = data->byte;}break;case I2C_SMBUS_WORD_DATA://Word_Data是指命令+双字节的形式.这种情况跟Byte_Data的情况类似//两者相比只是交互的数据大小不同if (read_write == I2C_SMBUS_READ)msg[1].len = 2;else {msg[0].len=3;msgbuf0[1] = data->word & 0xff;msgbuf0[2] = data->word >> 8;}break;case I2C_SMBUS_PROC_CALL://Proc_Call的方式与write 的Word_Data相似,只不过写完Word_Data之后,要等待它的应答//应该它需要交互两次,一次写一次读num = 2; /* Special case */read_write = I2C_SMBUS_READ;msg[0].len = 3;msg[1].len = 2;msgbuf0[1] = data->word & 0xff;msgbuf0[2] = data->word >> 8;break;case I2C_SMBUS_BLOCK_DATA://Block_Data:指command+N段数据的情况.//如果是读操作,它首先要写command到总线,然后再读N段数据.要写的command已经//放在msg[0]了.现在只需要将msg[1]的标志置I2C_M_RECV_LEN位,msg[1]有效长度为1字节.因为//adapter驱动会处理好的.现在现在还不知道要传多少段数据.//对于写的情况:msg[1]照例不需要.将要写的数据全部都放到msb[0]中.相应的也要更新 //msg[0]中的缓存区长度if (read_write == I2C_SMBUS_READ) {msg[1].flags |= I2C_M_RECV_LEN;msg[1].len = 1; /* block length will be added bythe underlying bus driver */} else {//data->block[0]表示后面有多少段数据.总长度要加2是因为command+count+N段数据 msg[0].len = data->block[0] + 2;if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {dev_err(&adapter->dev, "smbus_access called with ""invalid block write size (%d)\n",data->block[0]);return -1;}for (i = 1; i < msg[0].len; i++)msgbuf0 = data->block[i-1];}break;case I2C_SMBUS_BLOCK_PROC_CALL://Proc_Call:表示写完Block_Data之后,要等它的应答消息它和Block_Data相比,只是多了一部份应答而已num = 2; /* Another special case */read_write = I2C_SMBUS_READ;if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {dev_err(&adapter->dev, "%s called with invalid ""block proc call size (%d)\n", __func__,data->block[0]);return -1;}msg[0].len = data->block[0] + 2;for (i = 1; i < msg[0].len; i++)msgbuf0 = data->block[i-1];msg[1].flags |= I2C_M_RECV_LEN;msg[1].len = 1; /* block length will be added bythe underlying bus driver */break;case I2C_SMBUS_I2C_BLOCK_DATA://I2c Block_Data与Block_Data相似,只不过read的时候,数据长度是预先定义好了的.另外//与Block_Data相比,中间不需要传输Count字段.(Count表示数据段数目)if (read_write == I2C_SMBUS_READ) {msg[1].len = data->block[0];} else {msg[0].len = data->block[0] + 1;if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {dev_err(&adapter->dev, "i2c_smbus_xfer_emulated called with ""invalid block write size (%d)\n",data->block[0]);return -1;}for (i = 1; i <= data->block[0]; i++)msgbuf0 = data->block;}break;default:dev_err(&adapter->dev, "smbus_access called with invalid size (%d)\n",size);return -1;}//如果启用了PEC.Quick和I2c Block_Data是不支持PEC的i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK&& size != I2C_SMBUS_I2C_BLOCK_DATA);if (i) {/* Compute PEC if first message is a write */。
Linux2.6.25平台下的I2C驱动架构分析
Linux2.6.25平台下的I2C驱动架构分析【摘要】本文以PowerPC+Linux 2.6.25 平台为例,详细分析了I2C总线的驱动架构。
首先介绍了I2C的总体架构,从用户的角度将其分为三个层面,不同的开发者只需要关注相应的层面即可。
然后分析了主要数据结构及其之间的相互关系,接着分析了不同层的具体实现,最后以一款EEPEOM为例讲述了如何在用户空间访问I2C驱动。
对于ARM + Linux平台,只有平台依赖层即总线适配器驱动有差异。
【关键字】PowerPC, I2C, i2c-core, adapter , i2c_algorithm, RTC, EEPROM目录1 I2C概述 32 I2C总体架构 32.1 硬件抽象层 32.2 平台依赖层 32.3 用户接口层 33 主要的数据结构 43.1 Adapter 43.2 I2c_algorithm 53.3 i2c_driver 53.4 Client 64 平台依赖层-总线适配器驱动74.1 platform device 74.2 platform driver 94.3 Adapter及algorithm 125 硬件抽象层-I2C core 135.1 总线初始化135.2 Adapter注册155.3 驱动注册165.4 数据传输176 用户接口层-I2C设备驱动186.1 统一的设备模型186.1.1 关键数据结构186.1.2 初始化196.1.3 Open及release 216.1.4 数据收发226.2 特定的设备驱动266.2.1 关键数据结构266.2.2 初始化276.2.3 数据收发297 驱动访问示例297.1.1 写操作297.1.2 读操作318 参考鸣谢331 I2C概述I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL。
I2C是一种多主机控制总线,同一总线上可允许多个master,即总线上的设备都有主动发起数据传输的可能,依靠线与逻辑来实现无损仲裁。
基于S3C2410的Linux2.6下I 2C驱动研究
Absr c : C. usi s mp ea d us f I i h a s e i l r h t c u eห้องสมุดไป่ตู้ r e s d v l p d t u p r u . s d o h t a t l b S i l n e u _ c p c a c i t r fd i rwa e e o e o s p o tIC b s Ba e n t e wh a e v
a ay i o v r lsr t r fIC rv rb a i gt ee a peo n lss f heo e al tucu eo 2 d ie ,ytk n h x m l f¥3 41 t C2 0’ SI b sa ay ig a d i pe n ai n 2 u ,n lzn n C m lme t t o t eIC sd ie n e ied ie . h bu rv ra d IC d vc rv r Ke o d : yw r sEmb d e n x IC b ; C e ie De ied ie e d dLiu ; usI d vc ; vc rv r
2 I 总线 及其特点 C
2 1 I 工 作 原 理 . C
IC 线 通 过 串 行 数 据 线 (e i l D t Ln s 总 2 S r a a a ie ,
S L及 串行 时钟 线 (e i l C o k L n ,S L 两 线 在 连 接 D) S r a lc ie C ) 到 总 线 上 的 器 件 之 间 传 送 信 息 , 并 根 据 地 址 识 别 每 个 器 件 。在 电 气 特 性 上 ,IC 线 的 设 计 思 想 是 利 用 “ 与 ” 总 线 的 方 法 实 现 总 线 的 冲 突 检 测 , 所 有 IC 备 采 用 o f 设 2 c q的 I0 / ,故 连 接 器 件 时 必 须 要 有 上 拉 电 阻 。数 据 可 以在 主 机 ( 常 是微 处 理 器 )与 被 控 I之 间 、 I 与 I 之 问 进 行 双 向 通 C C C 传 输 ,各 种 被 控 制 电 路 均 并 联 在 总 线 上 , 每 个 电路 或 模 块
linux下i2c设备驱动开发和实现
经验与交流计算机与信息技术·79·Linux下I2C设备驱动开发和实现商凯周轶男(江南计算技术研究所,江苏214083)摘要I2C总线具有结构简单使用方便的特点。
本文描述了linux下I2C驱动的结构,并在此基础上给出了I2C设备驱动和应用的实现。
关键词I2C;驱动;应用1引言I2C(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。
I2C总线最主要的优点是其简单性和有效性。
由于接口直接在组件之上,因此I2C总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。
I2C总线最初为音频和视频设备开发,现已应用于各种服务与管理场合,来实现配置或掌握组件的功能状态,如电源、系统风扇、系统温度等参数,增加了系统的安全性,方便了管理。
2I2C总线概述I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据,每个器件都有一个惟一的地址识别。
I2C 规程运用主/从双向通讯。
器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。
主器件和从器件都可以工作于接收和发送状态。
总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。
SDA线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA 状态的改变被用来表示起始和停止条件。
I2C总线在传送数据过程中共有三种类型信号,它们分别是:开始信号、结束信号和应答信号。
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
结束信号:SCL为低电平时,SDA由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。
CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。
Linux I2C核心、总线与设备驱动
• i2c-core.c
这个文件实现了I2C核心的功能ቤተ መጻሕፍቲ ባይዱ及/proc/bus/i2c*接口。
• i2c-dev.c
实现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访问设备时的主设备号都为89,次设备号为0~255。应用程序通过“i2c-%d” (i2c-0, i2c-1, ..., i2c-10, ...)文件名并使用文件操作接口open()、write()、read()、ioctl()和close()等来访问这个设备。
5 struct i2c_driver *driver; /*依附的i2c_driver */
6 int usage_count; /*访问计数*/
7 struct device dev; /*设备结构体*/
8 struct list_head list; /*链表头*/
9 char name[I2C_NAME_SIZE]; /*设备名称*/
代码清单15.1 i2c_adapter结构体
1 struct i2c_adapter {
2 struct module *owner;/*所属模块*/
3 unsigned int id; /*algorithm的类型,定义于i2c-id.h,以I2C_ALGO_开始*/
4 unsigned int class;
9 int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long); /*类似ioctl*/
10 u32 (*functionality) (struct i2c_adapter *);/*返回适配器支持的功能*/
S3C2410 I 2C总线的Linux2.6驱动程序设计
作者简介 : 徐小庆 (9 2) 男 , 1 8 - , 四川 内江 人 , 南 石油 大 学计 算 机 科 学学 院硕 士 研 究 生 , 究方 向 : 入 式 系统 ; 大 成 西 研 嵌 梅 ( 9 8) 男 , 16 . , 副教授 , 硕士 , 研究方 向 : 嵌入式 系统 。
现 总线 的 冲突检测 , 有 IC设备 采 用 O 所 C门 的 IO, / 故 连接 器件 时必须 要有 上拉 电阻 。
IC总线 空 闲时 , 数据 线 和 时 钟 线 都 处 于 高 阻状
is 司开 发 的两 线 式 串行 总线 标 准 , 于连 接微 控 p公 用
态 。S L线 为高 电平 时 ,D C S A线从 高 电平 变到 低 电平
这是一次写操作 , 否则表示这是一次读请求 。数据传 输的基本单位是 1 个字节 , 而且从最高位开始传送 , 传送的字节 数 不限。发 送完 一个 字 节后 的下 一个
C K周 期 为 A K周期 , L C 发送 端 发 送 完 一 个 字 节 后 会
置 S A为高 电平 , D 即在 A K周 期 开始 时置 S A为 高 C D
( ol eo o p t c ne SuhWet e o u U i r t,C eg u6 00 , h a C lg f m ue Si c , ot s P t l m nv sy hn d 15 0 C i ) e C r e re ei n
Ab ta tT i a e n lzste ac i cueo 2 u r e n e iu ,fr¥ C 41 2 a sa x mpe he d sg sr c : hsp p ra aye h rht tr f1C b sdi ru d rL n x o 3 2 0 IC b sa n e a l ,t ein e v
Linux内核i2c读写操作驱动架构
成员变量解说: 1) addr, flags 分别表示从设备的地址和访问操作标志,最初源头为由开发人员构造和初始
化的 i2c_board_info{}结构 2) adapter 在从设备与适配器匹配后,在后端的 i2c_new_device()中被初始化
struct i2c_msg { __u16 addr; __u16 flags; __u16 len; __u8 *buf;
struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); u32 (*functionality) (struct i2c_adapter *);
};
成员变量解说: 1) master_xfer 启动 i2c 适配器,分流读写操作的总接口 2) functionality 表示当前 i2c 适配器,支持和实现的操作功能
struct i2c_board_info { unsigned short addr; unsigned short flags; ... ...;
};
成员变量解说: 1) addr 将初始化 i2c_client.addr 2) flags 将初始化 i2c_client.flags
struct i2c_client { unsigned short addr; unsigned short flags; struct i2c_adapter *adapter; ... ...;
具体适配器驱动读写
#define I2C_M_TEN
0x0010
#define I2C_M_RD
0x0001
【免费下载】linux内核I2C总线驱动实现
需要特别注意的是:i2c-dev.c 的 read()、write()方法都只适合于如下方式的数据格式 (可查看内核相关源码)
1.2 从芯片的 I2C 的驱动
下面主要分析从芯片的 I2C 驱动(也有 2 种方式,第一个是利用内核提供的 i2c-dev.c 来构建,另一个是自己写) 主要分析第一种方式:
利用系统给我们提供的 i2c-dev.c 来实现一个 i2c 适配器的设备文件。然后通过在应用 层操作 i2c 适配器来控制 i2c 设备。
static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func,
};
另外一方面需要确定为了实现对 AT24C02 及读写访问时序。 ● AT24C02 地址的确定
原理图上将 A2、A1、A0 都接地了,所以地址是 0x50。 ● AT24C02 任意地址字节写的时序
对全部高中资料试卷电气设备,在安装过程中以及安装结束后进行高中资料试卷调整试验;通电检查所有设备高中资料电试力卷保相护互装作置用调与试相技互术关,系电,力根保通据护过生高管产中线工资敷艺料设高试技中卷术资配0料不置试仅技卷可术要以是求解指,决机对吊组电顶在气层进设配行备置继进不电行规保空范护载高高与中中带资资负料料荷试试下卷卷高问总中题体资,配料而置试且时卷可,调保需控障要试各在验类最;管大对路限设习度备题内进到来行位确调。保整在机使管组其路高在敷中正设资常过料工程试况中卷下,安与要全过加,度强并工看且作护尽下关可都于能可管地以路缩正高小常中故工资障作料高;试中对卷资于连料继接试电管卷保口破护处坏进理范行高围整中,核资或对料者定试对值卷某,弯些审扁异核度常与固高校定中对盒资图位料纸置试,.卷保编工护写况层复进防杂行腐设自跨备动接与处地装理线置,弯高尤曲中其半资要径料避标试免高卷错等调误,试高要方中求案资技,料术编试交写5、卷底重电保。要气护管设设装线备备置敷4高、调动设中电试作技资气高,术料课中并中3试、件资且包卷管中料拒含试路调试绝线验敷试卷动槽方设技作、案技术,管以术来架及避等系免多统不项启必方动要式方高,案中为;资解对料决整试高套卷中启突语动然文过停电程机气中。课高因件中此中资,管料电壁试力薄卷高、电中接气资口设料不备试严进卷等行保问调护题试装,工置合作调理并试利且技用进术管行,线过要敷关求设运电技行力术高保。中护线资装缆料置敷试做设卷到原技准则术确:指灵在导活分。。线对对盒于于处调差,试动当过保不程护同中装电高置压中高回资中路料资交试料叉卷试时技卷,术调应问试采题技用,术金作是属为指隔调发板试电进人机行员一隔,变开需压处要器理在组;事在同前发一掌生线握内槽图部内纸故,资障强料时电、,回设需路备要须制进同造行时厂外切家部断出电习具源题高高电中中源资资,料料线试试缆卷卷敷试切设验除完报从毕告而,与采要相用进关高行技中检术资查资料和料试检,卷测并主处且要理了保。解护现装场置设。备高中资料试卷布置情况与有关高中资料试卷电气系统接线等情况,然后根据规范与规程规定,制定设备调试高中资料试卷方案。
LinuxI2C驱动框架
LinuxI2C驱动框架⼀、I2C总线概述I2C是由Philips公司开发的⼀种简单的、双向同步串⾏总线,它只需要两条线即可在连接于总线上的器件之间传送信息,其硬件连接框图如下所⽰:SCL:串⾏时钟线,数据传输过程中⽤于同步的时钟信号,低电平时允许SDA线上数据改变。
SDA:串⾏数据线,在时钟信号作⽤下,数据按位在数据线上进⾏传输。
I2C总线上的设备之间通信都要遵从I2C总线协议,I2C总线由起始信号、停⽌信号、应答信号、⾮应答信号组成。
起始信号:当时钟线SCL为⾼期间,数据线SDA由⾼到低的跳变。
停⽌信号:当时钟线SCL为⾼期间,数据线SDA由低到⾼的跳变。
应答信号(ACK):应答位为低电平时,规定为有效应答位,表⽰接收器已经成功接收到该字节。
⾮应答信号(NACK):应答位为⾼电平时,规定为⾮应答位,⼀般表⽰接收器接收该字节没有成功。
挂接在同⼀条I2C总线上的设备都要⾃⼰的物理地址,I2C主机控制器在和设备通信前需要先发送设备的地址,设备接收到总线上传过来的地址,看是否是⾃⼰的地址,如果是产⽣后续的应答。
主机控制器和设备通信⼀般是由⼀个起始信号开始和⼀个停⽌信号结束,地址信息⼀般是7bit,发送地址的最后⼀位代表数据传输的⽅向,1表⽰是读,0表⽰写操作,其发送时序⼀般如下所⽰:主机发送数据主机读取数据前⾯对I2C总线的⼀些基本概念和I2C协议的做了简单的介绍,下⾯开始来分析Linux内核的I2C驱动框架,看看内核中如何实现对I2C设备的⽀持。
⼆、Linux内核I2C驱动1、⼏个重要对象内核中的I2C驱动框架使⽤了总线设备驱动模型,在分析内核I2C驱动之前,先讨论这⼏个重要的数据结构。
1.1、I2C总线I2C总线是⼀条虚拟的bus总线(同platform总线⼀样,位于/sys/bus⽬录),其在drivers\i2c\i2c-core.c实现,具体内容如下:struct bus_type i2c_bus_type = {.name = "i2c",.dev_attrs = i2c_dev_attrs,.match = i2c_device_match,.uevent = i2c_device_uevent,.probe = i2c_device_probe,.remove = i2c_device_remove,.shutdown = i2c_device_shutdown,.suspend = i2c_device_suspend,.resume = i2c_device_resume,};这个i2c总线结构管理着I2C设备与I2C驱动的匹配、删除等操作。
i2c设备驱动实例分析
i2c设备驱动实例分析刚学习到i2c驱动这一块,在linux内核源码中(我用的是linux-2.6.38.6)有pca9541.c的驱动源码,所以我就拿该实例来学习i2c设备驱动开发。
我在网上找了该设备的相关资料(主要是工作原理和datasheet),下面我把我的分析思路记录下来,作为我以后学习的参考资料。
里面有许多我暂时不理解的地方,欢迎朋友们帮忙解决。
谢谢!/*****************(1)头文件*****************/#include <linux/module.h>#include <linux/init.h>#include <linux/jiffies.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/device.h>#include <linux/i2c.h>#include <linux/i2c-mux.h>#include <linux/i2c/pca954x.h>/******************(2)宏定义******************/#define PCA9541_CONTROL 0x01#define PCA9541_ISTAT 0x02#define PCA9541_CTL_MYBUS (1 << 0)#define PCA9541_CTL_NMYBUS (1 << 1)#define PCA9541_CTL_BUSON (1 << 2)#define PCA9541_CTL_NBUSON (1 << 3)#define PCA9541_CTL_BUSINIT (1 << 4)#define PCA9541_CTL_TESTON (1 << 6)#define PCA9541_CTL_NTESTON (1 << 7)#define PCA9541_ISTAT_INTIN (1 << 0)#define PCA9541_ISTAT_BUSINIT (1 << 1) #define PCA9541_ISTAT_BUSOK (1 << 2) #define PCA9541_ISTAT_BUSLOST (1 << 3)#define PCA9541_ISTAT_MYTEST (1 << 6)#define PCA9541_ISTAT_NMYTEST (1 << 7)#define BUSON (PCA9541_CTL_BUSON | PCA9541_CTL_NBUSON)#define MYBUS (PCA9541_CTL_MYBUS | PCA9541_CTL_NMYBUS)#define mybus(x) (!((x) & MYBUS) || ((x) & MYBUS) == MYBUS)#define busoff(x) (!((x) & BUSON) || ((x) & BUSON) == BUSON)/* arbitration timeouts, in jiffies */#define ARB_TIMEOUT (HZ / 8) /* 125 ms until forcing bus ownership */#define ARB2_TIMEOUT (HZ / 4) /* 250 ms until acquisition failure *//* arbitration retry delays, in us */#define SELECT_DELAY_SHORT 50#define SELECT_DELAY_LONG 1000/********************(3)加载函数********************/static int __init pca9541_init(void){return i2c_add_driver(&pca9541_driver);}首先从加载函数入手,慢慢展开分析。
Linux内核I2C子系统初始化驱动架构
device_driver{ }.name 在 驱 动中指定
i2c_driver{} .driver *id_table *probe ... ...
tv168_probe()
device_driver{} *p *bus *name ... ...
driver_private{} .knode_bus ... ...
I2C 子系统初始化驱动架构
目标:
分析整理 i2c 子系统初始化驱动架构;
本文要点:
1、i2c 重要数据结构及关系图; 2、i2c 子系统初始化驱动架构;
硬件框图:
2012 年 12 月 22 日
scl0 adapter i2c-0
sda0
scl1 adapter i2c-1
sda1
scl2 adapter i2c-2
i2c_register_board_info(busnum, ...)
复制到
i2c_board _info{}
i2c_devinfo{}
.busnum
.list
(等待与适配器匹配)
链入
__i2c_board_list
说明:
i2c_board_info{}
这 里 有 两 个 i2c_board_info{} 结 构 , 一 个 是 前 面 构 造 的 i2c_board_info{} , 另 一 个 在
*archdata
... ...
dev_archdata{} 开发人员自己使用
开发人员自己使用
i2c_board_info{}
i2c_devinfo{} int busnum .board_info struct list_head list
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
三. i2c驱动架构分析 i2c驱动架构分析
2、硬件抽象层 、 i2c-core.h和i2c-core.c为其主体框架代码,提供了 为其主体框架代码, 和 为其主体框架代码 核心数据结构的定义、 适配器驱动和设备驱动的注 核心数据结构的定义、i2c适配器驱动和设备驱动的注 注销管理等API。其为硬件平台无关层,向下屏 册、注销管理等 。其为硬件平台无关层, 蔽了物理总线适配器的差异, 蔽了物理总线适配器的差异,定义了统一的访问策略 和接口;其向上提供了统一的接口,以便I2C设备驱 和接口;其向上提供了统一的接口,以便 设备驱 动通过总线适配器进行数据收发。 动通过总线适配器进行数据收发。 3、用户接口层 、 i2c设备驱动层为用户接口层,其为用户提供了通 设备驱动层为用户接口层, 设备驱动层为用户接口层 总线访问具体设备的接口。 过I2C总线访问具体设备的接口。 I2c设备驱动主要包 总线访问具体设备的接口 设备驱动主要包 含了数据结构i2c_driver和i2c_client,我们需要根据 含了数据结构 和 , 具体设备实现其中的成员函数。 具体设备实现其中的成员函数。
Linux2.6内核i2c驱动架构
目录
一.i2c简介 i2c简介 二.驱动相关知识介绍 三.i2c驱动架构分析 i2c驱动架构分析
一. i2c简介 i2c简介
I2C 协议
I2C协议是有PHILIPS公司在1992年最先提出, PHILIPS公司专利。 I2C协议是有PHILIPS公司在1992年最先提出,乃PHILIPS公司专利。只要购 协议是有PHILIPS公司在1992年最先提出 公司专利 Philips的I2C元件同时传递了一个在Philips的 元件同时传递了一个在Philips 专利下, 买Philips的I2C元件同时传递了一个在Philips的I2C 专利下,在I2C 系统使用 元件使系统符合由Philips定义的I2C规范的许可证。任何使用I2C Philips定义的I2C规范的许可证 I2C的元件都必须 元件使系统符合由Philips定义的I2C规范的许可证。任何使用I2C的元件都必须 得到PHILIPS公司的授权。 PHILIPS公司的授权 得到PHILIPS公司的授权。 I2C总线的特征 I2C总线的特征 只要求两条总线线路一条串行数据线(SDA)一条串行时钟线(SCL)。 1.只要求两条总线线路一条串行数据线(SDA)一条串行时钟线(SCL)。 同时SDL SCL都是双向线路 分别通过上拉电阻连接到正的电源电压。 SDL和 都是双向线路, 同时SDL和SCL都是双向线路,分别通过上拉电阻连接到正的电源电压。 .每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主 2 .每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主 从机关系软件设定地址;主机可以作为主机发送器或主机接收器。 机/从机关系软件设定地址;主机可以作为主机发送器或主机接收器。 3.它是一个真正的多主机总线 它是一个真正的多主机总线, 3.它是一个真正的多主机总线,如果两个或更多主机同时初始化数据传输 可以通过冲突检测和仲裁防止数据被破坏。 可以通过冲突检测和仲裁防止数据被破坏。 串行的8 位双向数据传输位速率在标准模式下可达100kbit/s 100kbit/s。 4. 串行的8 位双向数据传输位速率在标准模式下可达100kbit/s。快速模 式下可达400kbit/s 高速模式下可达3.4Mbit/s 400kbit/s。 3.4Mbit/s。 式下可达400kbit/s。高速模式下可达3.4Mbit/s。 5.片上的滤波器可以滤去总线数据线上的毛刺波, 5.片上的滤波器可以滤去总线数据线上的毛刺波,保证数据完整 片上的滤波器可以滤去总线数据线上的毛刺波
二. 驱动相关知识介绍
二. 驱动相关知识介绍
(2)与用户空间通信 ) 这些通信的操作都是通过sysfs文件系统这个中介来完 这些通信的操作都是通过 文件系统这个中介来完 成的。 文件系统紧密团结在设备模型周围, 成的。 sysfs文件系统紧密团结在设备模型周围,把控制设 文件系统紧密团结在设备模型周围 备的接口(比如设备休眠、唤醒) 备的接口(比如设备休眠、唤醒)通过文件的形式暴露给 用户,一边用户可以通过读写sysfs中文件的方式,读取或 中文件的方式, 用户,一边用户可以通过读写 中文件的方式 改变设备的配置。 改变设备的配置。
二. 驱动相关知识介绍
(4)管理对象生命周期 ) 前面描述的功能,包括支持热插拔和sysfs 前面描述的功能,包括支持热插拔和 使用这个模型相当的复杂。 ,使用这个模型相当的复杂。得有一个好的机 制来实现设备生命周期。比如说, 制来实现设备生命周期。比如说,把USB鼠标 鼠标 拔了之后,全局设备树和sysfs里面的相应文件 拔了之后,全局设备树和 里面的相应文件 必须去掉。 必须去掉。
三. i2c驱动架构分析 i2c驱动架构分析
二. 驱动相关知识介绍
(3)支持热插拔 ) 越来越多的计算机设备可被动态的热插拔了, 越来越多的计算机设备可被动态的热插拔了,也 就是说,外围设备可根据用户的需要安装与卸载。 就是说,外围设备可根据用户的需要安装与卸载。内 核中的热插拔机制可以处理热插拔设备, 核中的热插拔机制可以处理热插拔设备,特别是能够 与用户空间进行关于插拔设备的通信, 与用户空间进行关于插拔设备的通信,而这种机制也 是通过设备模型管理的。 是通过设备模型管理的。
二. 驱动相关知识介绍
设备树 设备树是一种描述硬件配置的树形数据结构,有且仅有一个 根它包含了有关CPU、物理内存、总线、串口、PHY 以及其他外 围设备信息等。该树继承了OpenFirmware IEEE 1275 设备树的 定义。操作系统能够在启动时对此结构进行语法分析,以此配置 内核,加载相应的驱动。 U-Boot 需要将设备树在内存中的存储地址传给内核。该树 主要由三大部分组成:头(Header)、结构块(Structure block )、字符串块(Strings block)。设备树在内存中的存储布局图1 如下:
二. 驱动相关知识介绍
总线、 总线、设备和驱动程序
总线是处理器与一个或者多个设备之间的通道。在设备模型中,所有的 设备通过总线相连。甚至是那些内部的虚拟“平台”总线。总线可以相互插 入,比如一个USB控制器通常是一个PCI设备。设备模型展示了总线和它们 所控制的设备之间的实际连接。 of_platform总线(平台总线)是2.6 kernel中引入的正对Linux PowerPC CPU SPI I2C 的一种虚拟总线,主要用来管理CPU的片上资源,例如串口、SPI、I2C、网 卡。 设备连接某条物理或者虚拟总线上的对象。可能是真正物理对象,也 可能是虚拟对象。例如U盘就是一个物理对象,pty设备就是一个虚拟的对象 。 驱动是用来和设备通信的软件程序。驱动可以从设备中获取数据,也 可以把相应数据发给设备进行处理。驱动一般都是和具体操作系统以及具体 硬件相关的。
二. 驱动相关知识介绍
设备模型具有以下的功能: 设备模型具有以下的功能: (1)电源管理和系统关机。 )电源管理和系统关机。 想在内核中实现智能的电源管理, 想在内核中实现智能的电源管理,就需要建立表示 系统中设备的拓扑关系的树结构。比如一个USB宿主适 系统中设备的拓扑关系的树结构。比如一个 宿主适 配器, 配器,在处理完所有与其连接的设备前是不能被关闭的 。设备模型使得操作系统能够以正确的顺序遍历系统硬 件。
一. i2c简介 i2c简介
i2c设备连接图 i2c设备连接图
I2cห้องสมุดไป่ตู้配器 SCL
SDA AT24C8 other device
二. 驱动相关知识介绍
设备模型: 设备模型:
在以前的内核中没有独立的数据结构用来让内 核获得系统整体配合的信息。尽管缺乏这些信息, 核获得系统整体配合的信息。尽管缺乏这些信息, 在许多时候还是能工作正常。然而,随着 在许多时候还是能工作正常。然而,随着linux支 支 持的设备越来越多,拓扑结构越来越复杂, 持的设备越来越多,拓扑结构越来越复杂, linux 2.6内核引入了设备模型,目的是为了能够对 内核引入了设备模型, 内核引入了设备模型 设备进行有效管理。其实, 设备进行有效管理。其实,建立设备模型的初衷很 直白——为了省电。即是为了实现一些动态管理电 为了省电。 直白 为了省电 源的功能。 源的功能。
二. 驱动相关知识介绍
三. i2c驱动架构分析 i2c驱动架构分析
内核中, 在2.6的Linux内核中,I2C的驱动架构分为如下三个层 的 内核中 的驱动架构分为如下三个层 硬件抽象层、平台依赖层和用户接口层。 次:硬件抽象层、平台依赖层和用户接口层。 1、平台依赖层 、 i2c总线适配器(adapter)就是一条 总线的控制器 总线适配器( 总线适配器 )就是一条i2c总线的控制器 所谓控制是相对于本CPU来说的),在物理上连接若干 来说的), (所谓控制是相对于本 来说的),在物理上连接若干 i2c设备。在Linux驱动中,每种处理器平台有自己的适配 设备。 驱动中, 设备 驱动中 器驱动,属于平台移植相关层。每一个特定的硬件平台在 器驱动,属于平台移植相关层。 i2c/busses/目录下都有一个 目录下都有一个adapter的实现,PowerPC 880的 的实现, 目录下都有一个 的实现 的 平台驱动在i2c-mpc.c中。其按照核心层定义的接口实现了 平台驱动在 中 i2适配器的 适配器的adapter数据结构 数据结构i2c_adapter,提供了具体的 适配器的 数据结构 , i2c_algorithm数据结构用于和 从机设备进行通信。 数据结构用于和i2c从机设备进行通信 数据结构用于和 从机设备进行通信。