Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1078415
  • 博文数量: 146
  • 博客积分: 190
  • 博客等级: 入伍新兵
  • 技术积分: 5225
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-06 08:24
个人简介

慢行者

文章分类

全部博文(146)

文章存档

2013年(145)

2012年(1)

分类: LINUX

2013-05-10 13:55:51

目录
  1. 作者yuanlulu httpblogcsdnnetyuanlulu版权没有但是转载请保留此段声明

============================================
作者:yuanlulu
http://blog.csdn.net/yuanlulu

版权没有,但是转载请保留此段声明

============================================

 

 

第1章 I2CStandard driver model驱动


第一章开头已经说明过i2c驱动的两种模式,这一张主要说明第二种模式的驱动如何编写。和上一章一样,本章内容只涉及设备驱动,不涉及适配器驱动的编写。

1.1     i2c板级配置信息

I2CStandard driver model驱动中不需要创建i2c_client,可以选择支持内核配置的i2c设备。

那么设备的地址的信息是怎么得到的呢?这些信息保存在硬件相关的内核源文件中。对于smartarm3250来说,这个源文件是arc/arm/mach-lpc3250/board-smartarm3250.c。在这个文件中i2c相关的代码如程序清单 2.1所示。

程序清单 2.1 i2c板级信息

/* arc/arm/mach-lpc3250/board-smartarm3250.c */

#if defined(CONFIG_RTC_DRV_PCF8563)

static struct i2c_board_info __initdatasmartarm3250_i2c_board_info [] = {

         {

                   I2C_BOARD_INFO("rtc-pcf8563",0x51),

         },

};

#endif

·····················

void __init smartarm3250_board_init(void)

{

·····················

#if defined(CONFIG_RTC_DRV_PCF8563)

         /* I2Cbased RTC device on I2C2 */

         i2c_register_board_info(1,smartarm3250_i2c_board_info,

                                     ARRAY_SIZE(smartarm3250_i2c_board_info));

#endif

····················

}

从这段程序我们可以看出smartarm3250的板子默认支持的i2c设备只有一个,那就是RTC芯片,但是支持RTC芯片需要配置内核的时候选上CONFIG_RTC_DRV_PCF8563相关的选项。

程序清单 2.1中定义的i2c_board_info数组填充了设备的名字和地址。i2c_board_info的定义如程序清单 2.2所示。

程序清单 2.2 i2c_board_info

/* include/linux/i2c.h */

struct i2c_board_info {

         char            type[I2C_NAME_SIZE];

         unsignedshort     flags;

         unsignedshort     addr;

         void            *platform_data;

         int               irq;

};

其中的type成员其实是设备的名字,flags用来初始化i2c_client.flagsaddr是设备的地址,platform_data用来初始化i2c_client.dev.platform_datairq是设备用到的中断。初始化i2c_board_info变量至少应该初始化名字和地址两个成员。

1.2     驱动例程

Standard driver model类型的驱动的特征是i2c_client不再需要自己创建,但是仍然可细分为两种类型。第一种就是使用内核中配置的i2c设备信息,第二种就是使用自己定义的设备地址。这两种驱动并不冲突,但是为了简单起见,我们将这两个功能分开讲。

1.2.1   使用内核配置的I2C设备

Linux内核目录的文档documentation/i2c/upgrading-clients中有一个Standard driver model的驱动例程,支持内核配置信息提供的设备,如程序清单 2.3所示。

程序清单 2.3 Standard driver model驱动例程

struct example_state {

         structi2c_client   *client;

         ....

};

static int example_probe(struct i2c_client *client,

                                const struct i2c_device_id *id)

{

         structexample_state *state;

         structdevice *dev = &client->dev;

         state =kzalloc(sizeof(struct example_state), GFP_KERNEL);

         if(state == NULL) {

                   dev_err(dev,"failed to create our state/n");

                   return-ENOMEM;

         }

         state->client= client;

         i2c_set_clientdata(client,state);

         /* restof the initialisation goes here. */

         dev_info(dev,"example client created/n");

         return0;

}

static int __devexit example_remove(struct i2c_client*client)

{

         structexample_state *state = i2c_get_clientdata(client);

         kfree(state);

         return0;

}

static struct i2c_device_id example_idtable[] = {

         {"example", 0 },

         { }

};

MODULE_DEVICE_TABLE(i2c, example_idtable);

static struct i2c_driver example_driver = {

       .driver                 ={

                   .owner                = THIS_MODULE,

                   .name                  = "example",

         },

         .id_table             =example_idtable,

         .probe                  = example_probe,

         .remove               = __devexit_p(example_remove),

};

程序清单1.4比较本程序主要有三点不同:

(1)     驱动中不再需要创建i2c_client结构体,它是由i2c内核创建的。

(2)     驱动中不需要定义设备的地址,取而代之的是i2c_device_id,用来保存支持的设备类型。这里面保存的设备名将会和程序清单 2.1中的i2c_board_info的名字进行比较,在i2c_device_id中存在的名字才能依附于本驱动。

(3)     i2c_driver函数指针成员只需要初始化proberemove就就够了,不需要程序清单 1.4中定义的函数。其它的函数都是可选的。

特别需要注意的是,如果同时初始化两种模式需要用到的i2c_driver的成员,那么会报错,因为i2c内核无法判断是哪种模式的驱动。i2c_driver中的proberemovedetect任何一个被初始化意味着这是一个Standard driver model模式的驱动,attach_adapterdetach_adapter绝对不可以初始化。

1.2.2  使用自己定义的地址

上一小节的驱动用来支持内核中已经配置的设备,主要定义了proberemove两个函数接口。如果想要i2c核心探测自己模块中定义的地址,需要初始化i2c_driver中的detectaddress_data成员。将上一小节的例程修一下,如程序清单2.4所示。

程序清单 2.4 使用自定义地址的standard driver

#define CHIP_NAME example

static struct i2c_driver example_driver;

static unsigned short normal_addr[] = { OUR_ADDR,I2C_CLIENT_END };

I2C_CLIENT_INSMOD;

int example_detect(structi2c_client *client, int kind, struct i2c_board_info *info)

{

         if(client->adapter==example_driver->adapter && info->addr== OUR_ADDR) {

                   strlcpy(info->type,CHIP_NAME, I2C_NAME_SIZE);

                   return 0;

         }

         else

                   return–ENODEV;

}

static struct i2c_driver example_driver = {

       .driver                 ={

                   .owner                = THIS_MODULE,

                   .name                  = CHIP_NAME,

         },

         .detect                 = example_detect,

         . address_data    = &addr_data,

};

程序清单2.4可以实现和程序清单 1.4同样的功能,但是代码更少。因此没有特殊情况我们应该使用这种风格的驱动。

需要注意的地方有三点:

(1)     example_detect的参数client并不是最终要和example_driver建立联系的client,所以不能保存这个指针以备之后使用,应该在i2c_driver注册完毕之后遍历它的client链表得到每一个和它建立连接的设备client。但是最终注册的clientdriver成员不指向任何驱动。

(2)     client参数只有adapteraddr成员可用,info只有addr成员可用。example_detect函数应该判断携带infoclient是否能被支持,能的话则至少填充info->typeinfo的其它成员也可以填充,但不应该修改addr

(3)     example_detect返回任何0-ENODEV之外的值,都将终止对当前适配器的探测

1.3     i2c核心如何实现

       程序清单 2.1中我们发现,smartarm3250_i2c_board_info被注册为板级配置信息,每一个i2c_adapter注册的时候都会探测板级信息中注册的设备地址,探测成功的则建立client注册进i2c核心。当i2c_driverdriver成员注册的时候,I2C核心会将已经注册的client名字和该驱动的id_table进行名字的比较,比较成功的则建立i2c_driveri2c_client之间的附属关系。这就是为什么程序清单 2.3中定义example_idtable的原因。为了支持多个设备,可以给example_idtable多加几个成员。


程序清单2.4的功能又是如何实现的呢?首先程序清单2.4中的example_driver的注册流程和程序清单1.4中是类似的。区别在于第二类Standard driver model驱动注册的时候i2c核心会在__attach_adapter中调用i2c_detect函数,这个函数可以探测i2c_driver. address_data中的地址,为探测成功的设备创建client并注册。

现在总结一下i2c_adapteri2c_driveri2c_client的关系。


首先i2c_adapter代表具体的适配器,拥有控制I2c总线的方法。具体的通信任务就是由它完成的。

i2c_client代表具体的设备,它包含了设备使用的地址、名字等资源,也包含了设备依附的驱动和适配器的指针,还包含了发送消息时的配置信息在flags成员中。一个client注册进某个i2c_adapter实例,其它的设备就不能在同一个适配器上占用同一个地址了。i2c_adapter也保存了依附它的client链表,这样它就能知道哪些地址被占用了。

i2c_driver是一个辅助性的结构体。一般由驱动开发人员自己创建,它的主要任务是提供需要探测的地址和支持的设备名单,还要判定是否愿意支持一个特定clienti2c_driver更像一个用户,提供需求和产品检验。

阅读(1517) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~