Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2846239
  • 博文数量: 523
  • 博客积分: 11908
  • 博客等级: 上将
  • 技术积分: 5475
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-03 15:50
文章分类

全部博文(523)

文章存档

2019年(3)

2013年(4)

2012年(71)

2011年(78)

2010年(57)

2009年(310)

分类: LINUX

2012-01-31 14:39:12

Standard driver model驱动

第一章开头已经说明过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 __initdata smartarm3250_i2c_board_info [] = {

         {

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

         },

};

#endif

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

void __init smartarm3250_board_init(void)

{

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

#if defined(CONFIG_RTC_DRV_PCF8563)

         /* I2C based 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];

         unsigned short     flags;

         unsigned short     addr;

         void            *platform_data;

         int               irq;

};

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

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

I2C设备

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

2.3  Standard driver model驱动例程

struct example_state {

         struct i2c_client   *client;

         ....

};

 

static int example_probe(struct i2c_client *client,

                                  const struct i2c_device_id *id)

{

         struct example_state *state;

         struct device *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);

 

         /* rest of the initialisation goes here. */

 

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

 

         return 0;

}

 

static int __devexit example_remove(struct i2c_client *client)

{

         struct example_state *state = i2c_get_clientdata(client);

 

         kfree(state);

         return 0;

}

 

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绝对不可以初始化。

上一小节的驱动用来支持内核中已经配置的设备,主要定义了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(struct i2c_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之外的值,都将终止对当前适配器的探测

核心如何实现

       程序清单 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更像一个用户,提供需求和产品检验。

FROM:http://blog.csdn.net/yuanlulu/article/details/6161698

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