Linux I2C驱动分析.docx

上传人:b****8 文档编号:9941412 上传时间:2023-05-22 格式:DOCX 页数:53 大小:53.28KB
下载 相关 举报
Linux I2C驱动分析.docx_第1页
第1页 / 共53页
Linux I2C驱动分析.docx_第2页
第2页 / 共53页
Linux I2C驱动分析.docx_第3页
第3页 / 共53页
Linux I2C驱动分析.docx_第4页
第4页 / 共53页
Linux I2C驱动分析.docx_第5页
第5页 / 共53页
Linux I2C驱动分析.docx_第6页
第6页 / 共53页
Linux I2C驱动分析.docx_第7页
第7页 / 共53页
Linux I2C驱动分析.docx_第8页
第8页 / 共53页
Linux I2C驱动分析.docx_第9页
第9页 / 共53页
Linux I2C驱动分析.docx_第10页
第10页 / 共53页
Linux I2C驱动分析.docx_第11页
第11页 / 共53页
Linux I2C驱动分析.docx_第12页
第12页 / 共53页
Linux I2C驱动分析.docx_第13页
第13页 / 共53页
Linux I2C驱动分析.docx_第14页
第14页 / 共53页
Linux I2C驱动分析.docx_第15页
第15页 / 共53页
Linux I2C驱动分析.docx_第16页
第16页 / 共53页
Linux I2C驱动分析.docx_第17页
第17页 / 共53页
Linux I2C驱动分析.docx_第18页
第18页 / 共53页
Linux I2C驱动分析.docx_第19页
第19页 / 共53页
Linux I2C驱动分析.docx_第20页
第20页 / 共53页
亲,该文档总共53页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

Linux I2C驱动分析.docx

《Linux I2C驱动分析.docx》由会员分享,可在线阅读,更多相关《Linux I2C驱动分析.docx(53页珍藏版)》请在冰点文库上搜索。

Linux I2C驱动分析.docx

LinuxI2C驱动分析

一:

前言

I2c是philips提出的外设总线.I2C只有两条线,一条串行数据线:

SDA,一条是时钟线SCL.正因为这样,它方便了工程人员的布线.另外,I2C是一种多主机控制总线.它和USB总线不同,USB是基于master-slave机制,任何设备的通信必须由主机发起才可以.而I2C是基于multimaster机制.一同总线上可允许多个master.关于I2C协议的知识,这里不再赘述.可自行下载spec阅读即可.

二:

I2C架构概述

在linux中,I2C驱动架构如下所示:

如上图所示,每一条I2C对应一个adapter.在kernel中,每一个adapter提供了一个描述的结构(structi2c_adapter),也定义了adapter支持的操作(structi2c_adapter).再通过i2ccore层将i2c设备与i2cadapter关联起来.

这个图只是提供了一个大概的框架.在下面的代码分析中,从下至上的来分析这个框架图.以下的代码分析是基于linux2.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()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败.

分别来看一下这两个函数的代码:

inti2c_add_adapter(structi2c_adapter*adapter)

{

intid,res=0;

retry:

if(idr_pre_get(&i2c_adapter_idr,GFP_KERNEL)==0)

return-ENOMEM;

mutex_lock(&core_lock);

/*"above"heremeans"aboveorequalto",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)

gotoretry;

returnres;

}

adapter->nr=id;

returni2c_register_adapter(adapter);

}

在这里涉及到一个idr结构.idr结构本来是为了配合pagecache中的radixtree而设计的.在这里我们只需要知道,它是一种高效的搜索树,且这个树预先存放了一些内存.避免在内存不够的时候出现问题.所在,在往idr中插入结构的时候,首先要调用idr_pre_get()为它预留足够的空闲内存,然后再调用idr_get_new_above()将结构插入idr中,该函数以参数的形式返回一个id.以后凭这个id就可以在idr中找到相对应的结构了.对这个数据结构操作不太理解的可以查阅本站<>中有关radixtree的分析.

注意一下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(),如下所示:

inti2c_add_numbered_adapter(structi2c_adapter*adap)

{

intid;

intstatus;

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"heremeans"aboveorequalto",sigh;

*weneedthe"equalto"resulttoforcetheresult

*/

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)

gotoretry;

if(status==0)

status=i2c_register_adapter(adap);

returnstatus;

}

对比一下就知道差别了,在这里它已经指定好了adapter->nr了.如果分配的id不和指定的相等,便返回错误.

过一步跟踪i2c_register_adapter().代码如下:

staticinti2c_register_adapter(structi2c_adapter*adap)

{

intres=0,dummy;

mutex_init(&adap->bus_lock);

mutex_init(&adap->clist_lock);

INIT_LIST_HEAD(&adap->clients);

mutex_lock(&core_lock);

/*Addtheadaptertothedrivercore.

*Iftheparentpointerisnotsetup,

*weaddthisadaptertothehostbus.

*/

if(adap->dev.parent==NULL){

adap->dev.parent=&platform_bus;

pr_debug("I2Cadapterdriver[%s]forgottospecify"

"physicaldevice\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)

gotoout_list;

dev_dbg(&adap->dev,"adapter[%s]registered\n",adap->name);

/*createpre-declareddevicenodesfornew-styledrivers*/

if(adap->nr<__i2c_first_dynamic_bus_num)

i2c_scan_static_board_info(adap);

/*letlegacydriversscanthisbusformatchingdevices*/

dummy=bus_for_each_drv(&i2c_bus_type,NULL,adap,

i2c_do_add_adapter);

out_unlock:

mutex_unlock(&core_lock);

returnres;

out_list:

idr_remove(&i2c_adapter_idr,adap->nr);

gotoout_unlock;

}

首先对adapter和adapter中内嵌的structdevice结构进行必须的初始化.之后将adapter内嵌的structdevice注册.

在这里注意一下adapter->dev的初始化.它的类别为i2c_adapter_class,如果没有父结点,则将其父结点设为platform_bus.adapter->dev的名字为i2c+总线号.

测试一下:

[eric@mochowi2c]$cd/sys/class/i2c-adapter/

[eric@mochowi2c-adapter]$ls

i2c-0

可以看到,在我的PC上,有一个I2Cadapter,看下详细信息:

[eric@mochowi2c-adapter]$tree

.

`--i2c-0

|--device->../../../devices/pci0000:

00/0000:

00:

1f.3/i2c-0

|--name

|--subsystem->../../../class/i2c-adapter

`--uevent

3directories,2files

可以看到,该adapter是一个PCI设备.

继续往下看:

之后,在注释中看到,有两种类型的driver,一种是new-styledrivers,另外一种是legacydrivers

New-styledrivers是在2.6近版的kernel加入的.它们最主要的区别是在adapter和i2cdriver的匹配上.

3.1:

new-style形式的adapter注册

对于第一种,也就是new-styledrivers,将相关代码再次列出如下:

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-styledriver的方式.不过,还是详细分析这种情况下.

首先看一下i2c_register_board_info(),如下:

int__init

i2c_register_board_info(intbusnum,

structi2c_board_infoconst*info,unsignedlen)

{

intstatus;

mutex_lock(&__i2c_board_lock);

/*dynamicbusnumberswillbeassignedafterthelaststaticone*/

if(busnum>=__i2c_first_dynamic_bus_num)

__i2c_first_dynamic_bus_num=busnum+1;

for(status=0;len;len--,info++){

structi2c_devinfo*devinfo;

devinfo=kzalloc(sizeof(*devinfo),GFP_KERNEL);

if(!

devinfo){

pr_debug("i2c-core:

can'tregisterboardinfo!

\n");

status=-ENOMEM;

break;

}

devinfo->busnum=busnum;

devinfo->board_info=*info;

list_add_tail(&devinfo->list,&__i2c_board_list);

}

mutex_unlock(&__i2c_board_lock);

returnstatus;

}

这个函数比较简单,structi2c_board_info用来表示I2C设备的一些情况,比如所在的总线.名称,地址,中断号等.最后,这些信息会被存放到__i2c_board_list链表.

跟踪i2c_scan_static_board_info():

代码如下:

staticvoidi2c_scan_static_board_info(structi2c_adapter*adapter)

{

structi2c_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'tcreatei2c%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().代码如下:

structi2c_client*

i2c_new_device(structi2c_adapter*adap,structi2c_board_infoconst*info)

{

structi2c_client*client;

intstatus;

client=kzalloc(sizeof*client,GFP_KERNEL);

if(!

client)

returnNULL;

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));

/*anewstyledrivermaybeboundtothisdevicewhenwe

*returnfromthisfunction,oranylatermoment(e.g.maybe

*hotpluggingwillloadthedrivermodule).andthedevice

*refcountmodelisthestandarddrivermodelone.

*/

status=i2c_attach_client(client);

if(status<0){

kfree(client);

client=NULL;

}

returnclient;

}

我们又遇到了一个新的结构:

structi2c_client,不要被这个结构吓倒了,其实它就是一个嵌入structdevice的I2C设备的封装.它和我们之前遇到的structusb_device结构的作用是一样的.

首先,在clinet里保存该设备的相关消息.特别的,client->adapter指向了它所在的adapter.

特别的,clinet->name为info->name.也是指定好了的.

一切初始化完成之后,便会调用i2c_attach_client().看这个函数的字面意思,是将clinet关联起来.到底怎么样关联呢?

继续往下看:

inti2c_attach_client(structi2c_client*client)

{

structi2c_adapter*adapter=client->adapter;

intres=0;

//初始化client内嵌的dev结构

//父结点为所在的adapter,所在bus为i2c_bus_type

client->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;

}else

client->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)

gotoout_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]registeredwithbusid%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"

"failedforclient[%s]at0x%02x\n",

client->name,client->addr);

}

}

return0;

out_err:

dev_err(&adapter->dev,"Failedtoattachi2cclient%sat0x%02x"

"(%d)\n",client->name,client->addr,res);

returnres;

}

参考上面添加的注释,应该很容易理解这段代码了,就不加详细分析了.这个函数的名字不是i2c_attach_client()么?

怎么没看到它的关系过程呢?

这是因为:

在代码中设置了client->dev所在的bus为i2c_bus_type.以为只需要有bus为i2c_bus_type的driver注册,就会产生probe了.这个过程呆后面分析i2cdriver的时候再来详细分析.

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().

代码如下:

staticinti2c_do_add_adapter(structdevice_driver*d,void*data)

{

structi2c_driver*driver=to_i2c_driver(d);

structi2c_adapter*adap=data;

if(driver->attach_adapter){

/*Weignorethereturncode;ifitfails,toobad*/

driver->attach_adapter(adap);

}

return0;

}

该函数很简单,就是调用driver的attach_adapter(

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > IT计算机 > 电脑基础知识

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2