首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

飞天专有云敏捷版2.0:容器引领新一代敏捷革命,深度学习和区块链路加速业务创新

飞天专有云敏捷版(简称敏捷版)是企业级容器应用平台,可以在用户自有数据中心的物理机和已有IaaS环境中部署。敏捷版包含从容器的创建到运行以及镜像的全生命周期管理,并且提供开放的接口,为客户提供敏捷、弹性、开放的容器云平台。编排框架支持Docker Swarm和Kubernetes,容器引擎支持企业版和社区版,操作系统支持Windows和Linux。借助阿里云在公共云和专有云方面的积累,飞天敏捷版更提供了独特的混合云管理模式,让客户轻松管理云上云下运行环境。 容器是新一代的敏捷革命,将加速企业的创新,通过容器技术助推企业IT现代化。 阿里云容器服务于2015年底推出,已经积累了丰富的容器技

优秀的个人博客,低调大师

linux驱动学习(八) i2c驱动架构(史上最全) davinc dm368 i2c驱动分析【转】

转自:http://blog.csdn.net/ghostyu/article/details/8094049 版权声明:本文为博主原创文章,未经博主允许不得转载。 目录(?)[-] 预备知识 linux设备驱动到底复杂在什么地方 linux驱动中 i2c驱动架构 架构层次分类 具体分析 i2c_driver i2c_client i2c_adapter i2c_algorithm 梳理图 ov2715设备i2c驱动源码分析 预备知识 在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinci.c)。标题党请见谅! 其实i2c接口非常的简单,即使用51单片的gpio来模拟i2c,编写一个e2prom或者其他i2c接口的驱动程序,也不是什么难事,几百行代码就能搞定。 但是Linux的i2c驱动体系结构却有相当的复杂度,不管是叫linux i2c驱动还是单片机i2c驱动,其根本还是操作soc芯片内部的i2c模块(也叫i2c adapter)(读写i2c相关的寄存器)来产生start、stop还有ack信号而已。 linux设备驱动到底复杂在什么地方? 假设soc芯片dm368有两个i2c adapter(368内部真正只有一个i2c模块):i2c_adapter1,i2c_adapter1;然后外部有三个i2c接口的设备i2c_device1,i2c_device2,i2c_device3。 现在要求在裸机下写出他们的驱动函数。那么肯定要写出6个不同的驱动函数: [cpp] view plain copy i2c_adapter1_ReadWrite_i2c_device1(); i2c_adapter1_ReadWrite_i2c_device2() i2c_adapter1_ReadWrite_i2c_device3() i2c_adapter2_ReadWrite_i2c_device1() i2c_adapter2_ReadWrite_i2c_device2() i2c_adapter2_ReadWrite_i2c_device3() 设想一共有m个i2c adapter和n个外设i2c device,那么将需要m*n个驱动。并且这m*n个驱动程序必要会有很大部分重复的代码,而且不利于驱动程序的移植。 如果采用adapter和device分离的思想来写这样的驱动会是怎样呢? 图1 这样分离之后,只需要m+n个驱动,而且Adapter和Device的几乎没有耦合性,增加一个Adapter或者device并不会影响其余的驱动。 这就是分离思想带来的好处。除此之外,linux虽然是C写的,但是大量使用了面向对象的变成方法(可以理解为分层的思想),仅仅分离细想和分层思想的引入,就大大增加了linux设备驱动的复杂度。 linux驱动中 i2c驱动架构 图2 上图完整的描述了linux i2c驱动架构,虽然I2C硬件体系结构比较简单,但是i2c体系结构在linux中的实现却相当复杂。那么我们如何编写特定i2c接口器件(比如,ov2715,需要i2c来配置寄存器)的驱动程序?就是说上述架构中的那些部分需要我们完成,而哪些是linux内核已经完善的或者是芯片提供商(TI davinci平台已经做好的)已经提供的? 架构层次分类 第一层:提供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只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,eeprom和ov2715显然不是同一类的device,所以要实现具体设备device的write()、read()、ioctl()等方法,赋值给file_operations,然后注册字符设备(多数是字符设备)。覆盖图中的driver驱动层 第一层和第二层又叫i2c总线驱动(bus),第三第四属于i2c设备驱动(device driver)。在linux驱动架构中,几乎不需要驱动开发人员再添加bus,因为linux内核几乎集成所有总线bus,如usb、pci、i2c等等。并且总线bus中的【与特定硬件相关的代码】已由芯片提供商编写完成,例如TI davinci平台i2c总线bus与硬件相关的代码在内核目录/drivers/i2c/buses下的i2c-davinci.c源文件中;而三星的s3c-2440平台i2c总线bus为/drivers/i2c/buses/i2c-s3c2410.c 第三第四层又叫设备驱动层与特定device相干的就需要驱动工程师来实现了。 明确了方向后,再来具体分析。 具体分析 i2c_adapter与i2c_client的关系与i2c硬件体系中设配器与设备的关系一致,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备device,所以相应的,i2c_adapter也可以被多个i2c_client依附,在i2c_adapter中包含i2c_client的链表。同一类的i2c设备device对应一个驱动driver。driver与device的关系是一对多的关系。 现在,我们就来看一下这几个重要的结构体,分别是i2c_driver i2c_client i2c_adapter,也可以先忽略他们,待会回过头来看会更容易理解 1、i2c_driver [cpp] view plain copy structi2c_driver{ intid; unsignedintclass; int(*attach_adapter)(structi2c_adapter*); int(*detach_adapter)(structi2c_adapter*); int(*detach_client)(structi2c_client*); int(*command)(structi2c_client*client,unsignedintcmd,void*arg); structdevice_driverdriver; structlist_headlist; }; 2、i2c_client [cpp] view plain copy structi2c_client{ unsignedintflags;/*div.,seebelow*/ unsignedshortaddr;/*chipaddress-NOTE:7bit*/ /*addressesarestoredinthe*/ /*_LOWER_7bits*/ structi2c_adapter*adapter;/*theadapterwesiton*/ structi2c_driver*driver;/*andouraccessroutines*/ intusage_count;/*Howmanyaccessescurrently*/ /*totheclient*/ structdevicedev;/*thedevicestructure*/ structlist_headlist; charname[I2C_NAME_SIZE]; structcompletionreleased; }; 3、i2c_adapter [cpp] view plain copy structi2c_adapter{ structmodule*owner; unsignedintid; unsignedintclass; structi2c_algorithm*algo;/*thealgorithmtoaccessthebus*/ void*algo_data; /*---administrationstuff.*/ int(*client_register)(structi2c_client*); int(*client_unregister)(structi2c_client*); /*datafieldsthatarevalidforalldevices*/ structmutexbus_lock; structmutexclist_lock; inttimeout; intretries; structdevicedev;/*theadapterdevice*/ structclass_deviceclass_dev;/*theclassdevice*/ intnr; structlist_headclients; structlist_headlist; charname[I2C_NAME_SIZE]; structcompletiondev_released; structcompletionclass_dev_released; }; 4、i2c_algorithm [cpp] view plain copy structi2c_algorithm{ int(*master_xfer)(structi2c_adapter*adap,structi2c_msg*msgs, intnum); int(*slave_send)(structi2c_adapter*,char*,int); int(*slave_recv)(structi2c_adapter*,char*,int); u32(*functionality)(structi2c_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也非常关键,调用驱动中的发送接收函数需要填充该结构体 [cpp] view plain copy /* *I2CMessage-usedforpurei2ctransaction,alsofrom/devinterface */ structi2c_msg{ __u16addr;/*slaveaddress*/ __u16flags; __u16len;/*msglength*/ __u8*buf;/*pointertomsgdata*/ }; 【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的链表。 从图1图2中都可以看出,linux内核对i2c架构抽象了一个叫核心层core的中间件,它分离了设备驱动device driver和硬件控制的实现细节(如操作i2c的寄存器),core层不但为上面的设备驱动提供封装后的内核注册函数,而且还为小面的硬件时间提供注册接口(也就是i2c总线注册接口),可以说core层起到了承上启下的作用。 我们先看一下i2c-core为外部提供的核心函数(选取部分),i2c-core对应的源文件为i2c-core.c,位于内核目录/driver/i2c/i2c-core.c [cpp] view plain copy EXPORT_SYMBOL(i2c_add_adapter); EXPORT_SYMBOL(i2c_del_adapter); EXPORT_SYMBOL(i2c_del_driver); EXPORT_SYMBOL(i2c_attach_client); EXPORT_SYMBOL(i2c_detach_client); EXPORT_SYMBOL(i2c_transfer); 如果看过i2c设备驱动程序的人一定对上面几个函数比较熟悉。 i2c_transfer()函数,i2c_transfer()函数本身并不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正的驱动硬件流程,代码清单如下,不重要的已删除。 [cpp] view plain copy inti2c_transfer(structi2c_adapter*adap,structi2c_msg*msgs,intnum) { intret; if(adap->algo->master_xfer){//如果master_xfer函数存在,则调用,否则返回错误 ret=adap->algo->master_xfer(adap,msgs,num);//这个函数在硬件相关的代码中给algorithm赋值 returnret; }else{ return-ENOSYS; } } 当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册。相反的,在client被取消关联的时候,sysfs文件和设备也被注销,驱动开发人员需开发i2c设备驱动时,需要调用下列函数。程序清单如下 [cpp] view plain copy inti2c_attach_client(structi2c_client*client) { ... device_register(&client->dev); device_create_file(&client->dev,&dev_attr_client_name); ... return0; } [cpp] view plain copy inti2c_detach_client(structi2c_client*client) { ... device_remove_file(&client->dev,&dev_attr_client_name); device_unregister(&client->dev); ... returnres; } i2c_add_adapter()函数和i2c_del_adapter()在i2c-davinci.c中有调用,稍后分析 [cpp] view plain copy /*----- *i2c_add_adapteriscalledfromwithinthealgorithmlayer, *whenanewhwadapterregisters.Anewdeviceisregistertobe *availableforclients. */ inti2c_add_adapter(structi2c_adapter*adap) { ... device_register(&adap->dev); device_create_file(&adap->dev,&dev_attr_name); ... /*informdriversofnewadapters*/ list_for_each(item,&drivers){ driver=list_entry(item,structi2c_driver,list); if(driver->attach_adapter) /*Weignorethereturncode;ifitfails,toobad*/ driver->attach_adapter(adap); } ... } [cpp] view plain copy inti2c_del_adapter(structi2c_adapter*adap) { ... list_for_each(item,&drivers){ driver=list_entry(item,structi2c_driver,list); if(driver->detach_adapter) if((res=driver->detach_adapter(adap))){ } } ... list_for_each_safe(item,_n,&adap->clients){ client=list_entry(item,structi2c_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总线驱动,这部分代码是这样添加到系统中的 [cpp] view plain copy staticstructplatform_driverdavinci_i2c_driver={ .probe=davinci_i2c_probe, .remove=davinci_i2c_remove, .driver={ .name="i2c_davinci", .owner=THIS_MODULE, }, }; /*I2Cmaybeneededtobringupotherdrivers*/ staticint__initdavinci_i2c_init_driver(void) { returnplatform_driver_register(&davinci_i2c_driver); } subsys_initcall(davinci_i2c_init_driver); staticvoid__exitdavinci_i2c_exit_driver(void) { platform_driver_unregister(&davinci_i2c_driver); } module_exit(davinci_i2c_exit_driver); 并且,i2c适配器控制硬件发送接收数据的函数在这里赋值给i2c-algorithm,i2c_davinci_xfer稍加修改就可以在裸机中控制i2c适配器 [cpp] view plain copy staticstructi2c_algorithmi2c_davinci_algo={ .master_xfer=i2c_davinci_xfer, .functionality=i2c_davinci_func, }; 然后在davinci_i2c_probe函数中,将i2c_davinci_algo添加到添加到algorithm系统中 [cpp] view plain copy adap->algo=&i2c_davinci_algo; 梳理图 有时候代码比任何文字描述都来得直接,但是过多的代码展示反而让人觉得枯燥。这个时候,需要一幅图来梳理一下上面的内容,请看图3。 图3 好了,上面这些代码的展示是告诉我们,linux内核和芯片提供商为我们的的驱动程序提供了 i2c驱动的框架,以及框架底层与硬件相关的代码的实现。剩下的就是针对挂载在i2c两线上的i2c设备了device,如at24c02,例如ov2715,而编写的具体设备驱动了,这里的设备就是硬件接口外挂载的设备,而非硬件接口本身(soc硬件接口本身的驱动可以理解为总线驱动)。 在理解了i2c驱动架构后,我们接下来再作两方面的分析工作:一是具体的i2c设备ov2715驱动源码分析,二是davinci平台的i2c总线驱动源码。 ov2715设备i2c驱动源码分析 ov2715为200万的CMOS Sensor,芯片的寄存器控制通过i2c接口完成,i2c设备地址为0x6c,寄存器地址为16位两个字节,寄存器值为8位一个字节,可以理解为一般的字符设备。 该驱动程序并非只能用于ov2715,因此源码中存在支持多个设备地址的机制。 该字符设备的用到的结构体有两个,如下 [cpp] view plain copy typedefstruct{ intdevAddr; structi2c_clientclient;//!<Datastructurecontaininggeneralaccessroutines. structi2c_driverdriver;//!<Datastructurecontaininginformationspecifictoeachclient. charname[20]; intnameSize; intusers; }I2C_Obj; [cpp] view plain copy #defineI2C_DEV_MAX_ADDR(0xFF) #defineI2C_TRANSFER_BUF_SIZE_MAX(256) typedefstruct{ structcdevcdev;/*Chardevicestructure*/ intmajor; structsemaphoresemLock; I2C_Obj*pObj[I2C_DEV_MAX_ADDR]; uint8_treg[I2C_TRANSFER_BUF_SIZE_MAX]; uint16_treg16[I2C_TRANSFER_BUF_SIZE_MAX]; uint8_tbuffer[I2C_TRANSFER_BUF_SIZE_MAX*4]; }I2C_Dev; 一个I2C_Obj描述一个设备,devAddr保存该设备的地址,I2C_Obj内嵌到结构体I2C_Dev,I2C_Dev管理该驱动所支持的所有设备,尽管支持多个设备,但i2c适配器只有一个,因此需要一个信号量semLock来保护该共享资源,同时只能向一个设备读写数据。成员变量cdev是我们所熟知的,每个字符设备驱动中几乎总会有一个结构体包含它,major用于保存该驱动的主设备编号,reg数组为寄存器地址为8位的寄存器地址缓冲区,reg16为寄存器地址为16的寄存器地址缓冲区。同时可以读写多个寄存器地址的值。buffer为读写的寄存器值 使用I2C_Dev构建一个全局变量gI2C_dev,在驱动的多个地方均需要它。 下面先从字符设备的基本框架入手,然后深入该驱动的细节部分。 首先是该字符设备的初始化和退出函数 [cpp] view plain copy intI2C_devInit(void) { intresult,i; dev_tdev=0; result=alloc_chrdev_region(&dev,0,1,I2C_DRV_NAME);//分配字符设备空间 for(i=0;i<I2C_DEV_MAX_ADDR;i++) { gI2C_dev.pObj[i]=NULL; } gI2C_dev.major=MAJOR(dev);//保存设备主编号 sema_init(&gI2C_dev.semLock,1);//信号量初始化 cdev_init(&gI2C_dev.cdev,&gI2C_devFileOps);//使用gI2C_devFileOps初始化该字符设备,gI2C_devFileOps见下文 gI2C_dev.cdev.owner=THIS_MODULE;//常规赋值 gI2C_dev.cdev.ops=&gI2C_devFileOps;//常规赋值result=cdev_add(&gI2C_dev.cdev,dev,1);//添加设备到字符设备中returnresult;}voidI2C_devExit(void){dev_tdevno=MKDEV(gI2C_dev.major,0);cdev_del(&gI2C_dev.cdev);//从字符设备中删除该设备unregister_chrdev_region(devno,1);//回收空间} gI2c_devFileOps全局变量,驱动初始化会用到该结构体变量 structfile_operationsgI2C_devFileOps={ .owner=THIS_MODULE, .open=I2C_devOpen, .release=I2C_devRelease, .ioctl=I2C_devIoctl, }; 该驱动只实现了三个函数,open,release和ioctl,对于i2c设备来说,这已经足够了。 在I2C_devOpen和I2C_devOpen中并没有做实际的工作,重要的工作均在I2C_devIoctl这个ioctl中完成。I2C_devIoctl代码展示(将影响结构条理的代码去掉,稍后在做详细分析) [cpp] view plain copy intI2C_devIoctl(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg) { I2C_Obj*pObj; intstatus=0; I2C_TransferPrmtransferPrm; pObj=(I2C_Obj*)filp->private_data; if(!I2C_IOCTL_CMD_IS_VALID(cmd)) return-1; cmd=I2C_IOCTL_CMD_GET(cmd);//cmd命令转换,防止混淆,具体原因参见上一篇文章:ioctl中的cmd down_interruptible(&gI2C_dev.semLock);//信号量down switch(cmd) { caseI2C_CMD_SET_DEV_ADDR://命令1,设置设备地址 filp->private_data=I2C_create(arg); caseI2C_CMD_WRITE://命令2,写寄存器值 status=copy_from_user(&transferPrm,(void*)arg,sizeof(transferPrm)); ... break; caseI2C_CMD_READ://命令3,读寄存器值 status=copy_from_user(&transferPrm,(void*)arg,sizeof(transferPrm)); ... break; default: status=-1; break; } up(&gI2C_dev.semLock);//信号量up returnstatus; } 以上三个命令中最重要最复杂的是第一个I2C_CMD_SET_DEV_ADDR,设置设备地址,之所以重要和复杂,因为在I2C_create()函数中,将通过i2c-core提供的函数把该驱动程序和底层的i2c_adapter联系起来。下面是I2C_create()函数源码 [cpp] view plain copy void*I2C_create(intdevAddr){ intret; structi2c_driver*driver; structi2c_client*client=client; I2C_Obj*pObj; devAddr>>=1; if(devAddr>I2C_DEV_MAX_ADDR)//变量合法性判断 returnNULL; if(gI2C_dev.pObj[devAddr]!=NULL){//变量合法性判断,如果该地址的设备已经创建,则调过,防止上层错误调用 //alreadyallocated,incrementusercount,andreturntheallocatedhandle gI2C_dev.pObj[devAddr]->users++; returngI2C_dev.pObj[devAddr]; } pObj=(void*)kmalloc(sizeof(I2C_Obj),GFP_KERNEL);//为pObj分配空间 gI2C_dev.pObj[devAddr]=pObj;//将分配的空间地址保存在全局变量里 memset(pObj,0,sizeof(I2C_Obj)); pObj->client.adapter=NULL; pObj->users++;//用户基数,初始化为0,当前设为1 pObj->devAddr=devAddr;//保存设备地址 gI2C_curAddr=pObj->devAddr;//gI2C_curAddr为全局的整型变量,用于保存当前的设备地址 driver=&pObj->driver;//将成员变量driver单独抽取出来,因为线面要使用driver来初始化驱动 pObj->nameSize=0;//i2c设备名称,注意,这里不是在/dev下面的设备节点名 pObj->name[pObj->nameSize++]='I'; pObj->name[pObj->nameSize++]='2'; pObj->name[pObj->nameSize++]='C'; pObj->name[pObj->nameSize++]='_'; pObj->name[pObj->nameSize++]='A'+((pObj->devAddr>>0)&0xF); pObj->name[pObj->nameSize++]='B'+((pObj->devAddr>>4)&0xF); pObj->name[pObj->nameSize++]=0; driver->driver.name=pObj->name;//保存刚才设置的name driver->id=I2C_DRIVERID_MISC; driver->attach_adapter=I2C_attachAdapter;//这个很重要,将驱动连接到i2c适配器上,在后面分析 driver->detach_client=I2C_detachClient;//这个很重,在后面分析 if((ret=i2c_add_driver(driver)))//使用i2c-core(i2c_register_driver函数)的接口,注册该驱动,i2c_add_driver实质调用了driver_register() { printk(KERN_ERR"I2C:ERROR:Driverregistrationfailed(address=%x),modulenotinserted.\n",pObj->devAddr); } if(ret<0){ gI2C_dev.pObj[pObj->devAddr]=NULL; kfree(pObj); returnNULL; } returnpObj; } 其他两个命令是I2C_CMD_WRITE和I2C_CMD_READ,这个比较简单,只需设置寄存器地址的大小以及寄存器值的大小,然后通过i2c-core 提供的i2c_transfer()函数发送即可。例如I2C_wirte() [cpp] view plain copy intI2C_write(I2C_Obj*pObj,uint8_t*reg,uint8_t*buffer,uint8_tcount,uint8_tdataSize) { uint8_ti; interr; structi2c_client*client; structi2c_msgmsg[1]; unsignedchardata[8]; if(pObj==NULL) return-ENODEV; client=&pObj->client;//得到client信息 if(!client->adapter) return-ENODEV; if(dataSize<=0||dataSize>4) return-1; for(i=0;i<count;i++){ msg->addr=client->addr;//设置要写的i2c设备地址 msg->flags=0;//一直为0 msg->buf=data;//date为准备i2c通信的缓冲区,这个缓冲区除了不包含设备地址外,要包括要目标寄存器地址,和要写入该寄存器的值 data[0]=reg[i];//寄存器地址赋值 if(dataSize==1){//寄存器值长度为1 data[1]=buffer[i];//寄存器值赋值 msg->len=2;//设置data长度为2 }elseif(dataSize==2){//寄存器值长度为2 data[1]=buffer[2*i+1]; data[2]=buffer[2*i]; msg->len=3; } err=i2c_transfer(client->adapter,msg,1);//调用i2c-core中的i2c_transfer发送i2c数据 if(err<0) returnerr; } return0; } 现在,我们重点分析上一段代码void *I2C_create(int devAddr)函数中的i2c_driver结构体部分的代码,下面的代码是从上面I2C_create抽取出来的 [cpp] view plain copy driver->driver.name=pObj->name; driver->id=I2C_DRIVERID_MISC; driver->attach_adapter=I2C_attachAdapter; driver->detach_client=I2C_detachClient; 在i2c_driver结构体中针对attach_adapter有这样的说明: [cpp] view plain copy /*Notifiesthedriverthatanewbushasappeared.Thisroutine *canbeusedbythedrivertotestifthebusmeetsitsconditions *&seekforthepresenceofthechip(s)itsupports.Iffound,it *registerstheclient(s)thatareonthebustothei2cadmin.via *i2c_attach_client. */ 意思是通知驱动,i2c适配器已经就绪了,这时可以讲device的driver连接到总线bus上。所以I2C_attachAdapter的作用就是检测client,然后将client连接上来。attach_adapter和detach_client由内核驱动自动调用,我们只需在调用的时候实现必要的功能即可,如下代码展示 [cpp] view plain copy intI2C_attachAdapter(structi2c_adapter*adapter) { returnI2C_detectClient(adapter,gI2C_curAddr); } intI2C_detectClient(structi2c_adapter*adapter,intaddress) { I2C_Obj*pObj; structi2c_client*client; interr=0; if(address>I2C_DEV_MAX_ADDR){ printk(KERN_ERR"I2C:ERROR:Invaliddeviceaddress%x\n",address); return-1; } pObj=gI2C_dev.pObj[address]; if(pObj==NULL){ printk(KERN_ERR"I2C:ERROR:Objectnotfoundforaddress%x\n",address); return-1; } client=&pObj->client; if(client->adapter) return-EBUSY;/*ourclientisalreadyattached*/ memset(client,0x00,sizeof(structi2c_client)); client->addr=pObj->devAddr; client->adapter=adapter; client->driver=&pObj->driver; if((err=i2c_attach_client(client))) { printk(KERN_ERR"I2C:ERROR:Couldn'tattach%s(address=%x)\n",pObj->name,pObj->devAddr); client->adapter=NULL; returnerr; } return0; } 最终I2C_detectClient()函数调用了i2c-core中的i2c_attach_client,从名字上就能看出什么意思,连接client设备。 当内核驱动准备删除该驱动时会自动调用i2c_driver的成员函数:detech_client,因此我们需要实现删除client设备的函数然后赋值给改函数指针,detech_client的说明如下: [cpp] view plain copy /*tellsthedriverthataclientisabouttobedeleted&givesit *thechancetoremoveitsprivatedata.Also,iftheclientstruct *hasbeendynamicallyallocatedbythedriverinthefunctionabove, *itmustbefreedhere. */ 下面是detech_client调用的函数代码清单,该函数最终调用了i2c-core提供的i2c_detach_client,用于取消client设备的连接 [cpp] view plain copy intI2C_detachClient(structi2c_client*client) { interr; if(!client->adapter) return-ENODEV;/*ourclientisn'tattached*/ if((err=i2c_detach_client(client))){ printk(KERN_ERR"Clientderegistrationfailed(address=%x),clientnotdetached.\n",client->addr); returnerr; } client->adapter=NULL; return0; } ov2715设备的i2c驱动源码的分析就到这里,至于平台相关的i2c总线驱动分析就放到下一篇文章里分析,因为这部分多数情况下并不需要我们亲自去实现。但是对于理解i2c驱动架构来说,还是有很大帮助的。 【作者】 张昺华 【出处】 http://www.cnblogs.com/sky-heaven/ 【博客园】 http://www.cnblogs.com/sky-heaven/ 【新浪博客】 http://blog.sina.com.cn/u/2049150530 【知乎】 http://www.zhihu.com/people/zhang-bing-hua 【我的作品---旋转倒立摆】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【我的作品---自平衡自动循迹车】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【新浪微博】 张昺华--sky 【twitter】 @sky2030_ 【facebook】 张昺华 zhangbinghua 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册