i2c设备驱动实例分析.docx
《i2c设备驱动实例分析.docx》由会员分享,可在线阅读,更多相关《i2c设备驱动实例分析.docx(24页珍藏版)》请在冰点文库上搜索。
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)宏定义**
****************/
#definePCA9541_CONTROL0x01
#definePCA9541_ISTAT0x02
#definePCA9541_CTL_MYBUS(1<<0)
#definePCA9541_CTL_NMYBUS(1<<1)
#definePCA9541_CTL_BUSON(1<<2)
#definePCA9541_CTL_NBUSON(1<<3)
#definePCA9541_CTL_BUSINIT(1<<4)
#definePCA9541_CTL_TESTON(1<<6)
#definePCA9541_CTL_NTESTON(1<<7)
#definePCA9541_ISTAT_INTIN(1<<0)
#definePCA9541_ISTAT_BUSINIT(1<<1)
#definePCA9541_ISTAT_BUSOK(1<<2)
#definePCA9541_ISTAT_BUSLOST(1<<3)
#definePCA9541_ISTAT_MYTEST(1<<6)
#definePCA9541_ISTAT_NMYTEST(1<<7)
#defineBUSON(PCA9541_CTL_BUSON|PCA9541_CTL_NBUSON)
#defineMYBUS(PCA9541_CTL_MYBUS|PCA9541_CTL_NMYBUS)
#definemybus(x)(!
((x)&MYBUS)||((x)&MYBUS)==MYBUS)
#definebusoff(x)(!
((x)&BUSON)||((x)&BUSON)==BUSON)
/*arbitrationtimeouts,injiffies*/
#defineARB_TIMEOUT(HZ/8)/*125msuntilforcingbusownership*/
#defineARB2_TIMEOUT(HZ/4)/*250msuntilacquisitionfailure*/
/*arbitrationretrydelays,inus*/
#defineSELECT_DELAY_SHORT50
#defineSELECT_DELAY_LONG1000
/******************
**(3)加载函数**
******************/
staticint__initpca9541_init(void)
{
returni2c_add_driver(&pca9541_driver);
}
首先从加载函数入手,慢慢展开分析。
/*
**注册:
调用i2c_add_driver(structi2c_driver*)函数
**该函数位于linux/i2c.h中
**其原型为:
**staticinlineinti2c_add_driver(structi2c_driver*driver)
{
returni2c_register_driver(THIS_MODULE,driver);
}
**它又调用i2c_register_driver(sructmodule*,structi2c_driver*)
**函数来完成注册,该函数位于driver/i2c/i2c-core.c中
*/
/*i2c_register_driver函数*/
inti2c_register_driver(structmodule*owner,structi2c_driver*driver)
{
intres;/*Can'tregisteruntilafterdrivermodelinit*/
if(unlikely(WARN_ON(!
i2c_bus_type.p)))//警告信息
return-EAGAIN;//#defineEAGAIN11/*Tryagain*//*addthedrivertothelistofi2cdriversinthedrivercore*/
driver->driver.owner=owner;//所属模块
driver->driver.bus=&i2c_bus_type;//总线类型/*Whenregistrationreturns,thedrivercore
*willhavecalledprobe()forallmatching-but-unbounddevices.
*/
res=driver_register(&driver->driver);
if(res)
returnres;/*Driversshouldswitchtodev_pm_opsinstead.*/
if(driver->suspend)
pr_warn("i2c-core:
driver[%s]usinglegacysuspendmethod\n",
driver->driver.name);
if(driver->resume)
pr_warn("i2c-core:
driver[%s]usinglegacyresumemethod\n",
driver->driver.name);pr_debug("i2c-core:
driver[%s]registered\n",driver->driver.name);INIT_LIST_HEAD(&driver->clients);
/*Walktheadaptersthatarealreadypresent*/
mutex_lock(&core_lock);//获取mutex
bus_for_each_dev(&i2c_bus_type,NULL,driver,__process_new_driver);
mutex_unlock(&core_lock);//释放mutexreturn0;
}
/*
**参数说明:
&pca9541_driver是一个i2c_driver的结构体地址
**structi2c_driver结构体的作用:
它对应的设备的驱动方法,不对应具体的设备。
这点暂时先说到这里,待分析到
**i2c设备驱动两大结构体之一的另一大结构体structi2c_client时再具体分析。
**所以pca9541设备驱动中需要定义设备结构体structi2c_driverpca9541_driver
*/
/*pca9541_driver结构体及其初始化*/
staticstructi2c_driverpca9541_driver=
{
driver=
{
.name="pca9541",
.owner=THIS_MODULE,
},
.probe=pca9541_probe,
.remove=pca9541_remove
.id_table=pca9541_id,
};
/*有了这个结构体后,就可以完成pca9541设备驱动的注册和注销。
*/
下面就说一说i2c设备驱动的两大结构体:
structi2c_driver
structi2c_client
/*
(1)structi2c_driver结构体以及它所涉及到的结构体*/structi2c_driver{
unsignedintclass;int(*attach_adapter)(structi2c_adapter*);//依附i2c_adapter的函数指针
int(*detach_adapter)(structi2c_adapter*);//脱离i2c_adapter的指针
/*Standarddrivermodelinterfaces*/
int(*probe)(structi2c_client*,conststructi2c_device_id*);
int(*remove)(structi2c_client*);/*drivermodelinterfacesthatdon'trelatetoenumeration*/
void(*shutdown)(structi2c_client*);
int(*suspend)(structi2c_client*,pm_message_tmesg);
int(*resume)(structi2c_client*);/*Alertcallback,forexamplefortheSMBusalertprotocol.
*Theformatandmeaningofthedatavaluedependsontheprotocol.
*FortheSMBusalertprotocol,thereisasinglebitofdatapassed
*asthealertresponse'slowbit("eventflag").
*/
void(*alert)(structi2c_client*,unsignedintdata);/*aioctllikecommandthatcanbeusedtoperformspecificfunctions
*withthedevice.
*/
int(*command)(structi2c_client*client,unsignedintcmd,void*arg);//类似ioctlstructdevice_driverdriver;//内嵌的device_driver结构体
conststructi2c_device_id*id_table;//id_table主要用来匹配设备与驱动程序的。
//对于I2C驱动程序来说,只是比较它们的名字是否相同
/*Devicedetectioncallbackforautomaticdevicecreation*/
int(*detect)(structi2c_client*,structi2c_board_info*);//i2c_client脱离函数指针constunsignedshort*address_list;
structlist_headclients;//链表头
};
/*attach_adapter,detach_adapter很重要。
后面会说到*/
在这里说一下device_driver这个设备结构体。
位于linux/device.h中
系统中的每个驱动程序由一个device_driver对象描述,对应的数据结构定义为:
structdevice_driver{
char*name;//设备驱动程序的名称
structbus_type*bus;//该驱动所管理的设备挂接的总线类型
structkobjectkobj;//内嵌kobject对象
structlist_headdevices;//该驱动所管理的设备链表头
int(*probe)(structdevice*dev);//指向设备探测函数,用于探测设备是否可以被该驱动程序管理
int(*remove)(structdevice*dev);//用于删除设备的函数
/*somefieldsomitted*/
};
/*
(2)structi2c_client结构体*/
structi2c_client
{
unsignedintflags;//标志
unsignedshortaddr;//低7位为芯片地址
structi2c_adapter*adapter;//依附的i2c_adapter
structi2c_driver*driver//依附的i2c_driver
intusage_count;//访问计数
structdevicedev;//内嵌的设备结构体,这个很重要包括该设备属性信息
structlist_headlist;//链表头
charname[I2C_NAME_SIZE];//设备名称
structcompletionreleased;//用于同步
};
/*
**i2c_client对应具体的i2c实体设备,每一个i2c实体设备都需要一个i2c_client结构体来描述。
**上文中说到i2c_driver这个结构体,当它的attach_adapter()函数被运行时,attach_adapter()函数会探测物理设备,当探测到
**它所支持的设备时,它就会创建一个structi2c_cilent结构体来描述这个设备。
一个i2c_client代表着位于adapter总线上,地址为
**addr,使用driver来驱动的一个设备。
它将总线驱动与设备驱动,以及设备地址绑定在了一起。
一个i2c_client就代表着一个
**I2C设备
*/
/*下面我们分析pca9541_driver这个结构体*/
1).driver:
它是一个内嵌的device_driver结构体。
主要作用是对驱动的描述。
这里pca541_driver初始化,说明了pca9541驱动的
名称:
pca9541
所属模块:
THIS_MODULE
这里介绍一下THIS_MODULE
在linux/module.h中有定义:
externstruct
module__this_module;
#defineTHIS_MODULE(&__this_module)
可见THIS_MODULE是个指针,它指向__this_module.
而__this_module它是一个structmodule结构类型的结构体。
structmodule在该头文件中也有定义。
这里要说明的是__this_module它是一个变量,表示当前模块。
只有当驱动加载到内核后,这个符号才会产生,才会被
赋值。
2).probe:
探测函数指针int
(*probe)(structi2c_client*,conststructi2c_device_id*)
.probe=pca9541_probe
表示该驱动要使用pca9541_probe函数来实现探测。
主要就是去找能使用该驱动的i2c设备,并为其创建i2c_client结体。
3).remove:
注销设备的i2c_client结构体。
与.probe功能相反。
int
(*remove)(structi2c_client*)
.remove=pca9541_remove
表示该驱动将使用pca9541_remove函数来实现注销。
主要就是为注销掉使用该驱动的设备的i2c_client结构体。
4).id_table:
id_table
主要用来匹配设备与驱动程序的。
对于I2C驱动程序来说,只是比较它们的名字是否相同。
const
structi2c_device_id*id_table
.id_table=pca9541_id
表示该驱动要使用pca9541_id来比较设备与驱动程序是否匹配。
/*下面我们要分析pca9541_id,pca9541_remove,pca9541_probe*/
1)pca9541_id
staticconststructi2c_device_idpca9541_id[]=
{
{"pca9541",0},
{}
};
MODULE_DEVICE_TABLE(i2c,pca9541_id);
这里要分析一下i2c_device_id这个结构体。
它位于linux/mod_devicetable.h中。
如果驱动可以支持好几个设备,那么这里面就要包含这些设备的ID。
structi2c_device_id{
charname[I2C_NAME_SIZE];//#defineI2C_NAME_SIZE20表示设备名称
kernel_ulong_tdriver_data/*Dataprivatetothedriver*/
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
2)pca9541_probe()
/*定义结构体pca9541,下面函数会用到*/
structpca9541
{
structi2c_adapter*mux_adap;
unsignedlongselect_timeout;
unsignedlongarb_timeout;
};
staticintpca9541_probe(structi2c_client*client,conststructi2c_device_id*id)//首先传进来一个设备,一个设备ID
{
structi2c_adapter*adap=client->adapter;//定义一个指向i2c_adapter的指针adap,并把赋初值client->adapter
structpca954x_platform_data*pdata=client->dev.platform_data;//定义平台数据信息,并把client的平台信息传给*pdata
structpca9541*data;//这里用到了struct
pca9541这个结构体,所以我们需要定义它。
intforce;
intret=-ENODEV;//ENODEV是系统错误定义,在linux/asm-generic/errno-bash.h中定义。
表示no
suchdevice值为19
//下面函数用到了i2c_check_functionality(struct
i2c_adapter*adap,u32func)函数,所以在这里我们插入说明一下这个函数
//在linux/i2c.h中定义了i2c_check_functionality(struct
i2c_adapter*adap,u32func)函数
/*
Return1ifadaptersupportseverythingweneed,0ifnot.*/
staticinlineinti2c_check_functionality(structi2c_adapter*adap,u32func)
{
return(func&i2c_get_functionality(adap))==func;//这里用到了u32
i2c_get_functionality(structi2c_adapter*adap)函数
//即判断适配器是否完全符合要求,匹配返回1,不匹配返回0
}
/*
Returnthefunctionalitymask*/
staticinlineu32i2c_get_functionality(structi2c_adapter*adap)
{
returnadap->algo->functionality(adap);//即返回适配器支持的功能
}
关于I2C_FUNC_SMBUS_BYTE_DATA定义:
在linux/i2c.h中
#define
I2C_FUNC_SMBUS_BYTE_DATA(I2C_FUNC_SMBUS_READ_BYTE_DATA|\
I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
#define
I2C_FUNC_SMBUS_READ_BYTE_DATA0x00080000
#defineI2C_FUNC_SMBUS_WRITE_BYTE_DATA0x00100000
if(!
i2c_check_functionality(adap,I2C_FUNC_SMBUS_BYTE_DATA))
gotoerr;//如果不匹配
/*这个函数就是原来的两个函数的整合,即原来我们每次申请内存的时候都会这么做,先是用kmalloc()申请空间,然后用memset()来初始化,而现在省事了,一步到位,直接调用kzalloc(),效果等同于原来那两个函数,所有申请的元素都被初始化为0.其实对写驱动的来说,知道现在应该用kzalloc()代替原来的kmalloc()和memset()就可以了,这是内核中内存管理部分做出的改变,确切的说是改进,负责内存管理那部分的兄弟们的目标无非就是让内核跑起来更快一些,而从kmalloc/memset到kzalloc的改变确实也是为了实现这方面的优化*