I2C的Standard driver model驱动

<!--[if !supportLists]-->第1章 <!--[endif]-->I2CStandard driver model驱动

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

<!--[if !supportLists]-->1.1 <!--[endif]-->i2c板级配置信息

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

那么设备的地址的信息是怎么得到的呢?这些信息保存在硬件相关的内核源文件中。对于smartarm3250来说,这个源文件是arc/arm/mach-lpc3250/board-smartarm3250.c。在这个文件中i2c相关的代码如<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283198609 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 2.1<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100390038003600300039000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->所示。

程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283198609'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283198609'><span lang=EN-US><span style='mso-spacerun:yes'>&nbsp;</span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->2<!--[if supportFields]><span style='mso-bookmark: _Ref283198609'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283198609'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283198609'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283198609'><span style='font-family:黑体;mso-ascii-font-family: Arial'>程序清单</span><span lang=EN-US> \* ARABIC \s 1 <span style='mso-element: field-separator'></span></span></span><![endif]-->1<!--[if supportFields]><span style='mso-bookmark:_Ref283198609'></span><span style='mso-element:field-end'></span><![endif]--> 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相关的选项。

<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'>&nbsp;</span>REF _Ref283198609 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 2.1<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100390038003600300039000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->中定义的i2c_board_info数组填充了设备的名字和地址。i2c_board_info的定义如<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283199118 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 2.2<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100390039003100310038000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->所示。

程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283199118'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283199118'><span lang=EN-US><span style='mso-spacerun:yes'>&nbsp;</span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->2<!--[if supportFields]><span style='mso-bookmark: _Ref283199118'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283199118'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283199118'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283199118'><span style='font-family:黑体;mso-ascii-font-family: Arial'>程序清单</span><span lang=EN-US> \* ARABIC \s 1 <span style='mso-element: field-separator'></span></span></span><![endif]-->2<!--[if supportFields]><span style='mso-bookmark:_Ref283199118'></span><span style='mso-element:field-end'></span><![endif]--> 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变量至少应该初始化名字和地址两个成员。

<!--[if !supportLists]-->1.2 <!--[endif]-->驱动例程

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

<!--[if !supportLists]-->1.2.1 <!--[endif]-->使用内核配置的I2C设备

Linux内核目录的文档documentation/i2c/upgrading-clients中有一个Standard driver model的驱动例程,支持内核配置信息提供的设备,如<!--[if supportFields]><span lang=EN-US style='mso-bidi-font-size:9.0pt'><span style='mso-element:field-begin'></span> REF _Ref283217044 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 2.3<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003200310037003000340034000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US style='mso-bidi-font-size:9.0pt'><span style='mso-element:field-end'></span></span><![endif]-->所示。

程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283217044'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283217044'><span lang=EN-US><span style='mso-spacerun:yes'>&nbsp;</span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->2<!--[if supportFields]><span style='mso-bookmark: _Ref283217044'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283217044'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283217044'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283217044'><span style='font-family:黑体;mso-ascii-font-family: Arial'>程序清单</span><span lang=EN-US> \* ARABIC \s 1 <span style='mso-element: field-separator'></span></span></span><![endif]-->3<!--[if supportFields]><span style='mso-bookmark:_Ref283217044'></span><span style='mso-element:field-end'></span><![endif]--> 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<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100340030003000320033000000</w:data> </xml><![endif]-->比较本程序主要有三点不同:

<!--[if !supportLists]-->(1) <!--[endif]-->驱动中不再需要创建i2c_client结构体,它是由i2c内核创建的。

<!--[if !supportLists]-->(2) <!--[endif]-->驱动中不需要定义设备的地址,取而代之的是i2c_device_id,用来保存支持的设备类型。这里面保存的设备名将会和<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283198609 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 2.1<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100390038003600300039000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->中的i2c_board_info的名字进行比较,在i2c_device_id中存在的名字才能依附于本驱动。

<!--[if !supportLists]-->(3) <!--[endif]-->i2c_driver函数指针成员只需要初始化proberemove就就够了,不需要<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283140023 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 1.4<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100340030003000320033000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->中定义的函数。其它的函数都是可选的。

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

<!--[if !supportLists]-->1.2.2 <!--[endif]-->使用自己定义的地址

上一小节的驱动用来支持内核中已经配置的设备,主要定义了proberemove两个函数接口。如果想要i2c核心探测自己模块中定义的地址,需要初始化i2c_driver中的detectaddress_data成员。将上一小节的例程修一下,如程序清单 2.4<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003200310039003900340030000000</w:data> </xml><![endif]-->所示。

程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283219940'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283219940'><span lang=EN-US><span style='mso-spacerun:yes'>&nbsp;</span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->2<!--[if supportFields]><span style='mso-bookmark: _Ref283219940'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283219940'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283219940'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283219940'><span style='font-family:黑体;mso-ascii-font-family: Arial'>程序清单</span><span lang=EN-US> \* ARABIC \s 1 <span style='mso-element: field-separator'></span></span></span><![endif]-->4<!--[if supportFields]><span style='mso-bookmark:_Ref283219940'></span><span style='mso-element:field-end'></span><![endif]--> 使用自定义地址的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<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003200310039003900340030000000</w:data> </xml><![endif]-->可以实现和<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283140023 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 1.4<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100340030003000320033000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->同样的功能,但是代码更少。因此没有特殊情况我们应该使用这种风格的驱动。

需要注意的地方有三点:

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

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

<!--[if !supportLists]-->(3) <!--[endif]-->example_detect返回任何0-ENODEV之外的值,都将终止对当前适配器的探测。

<!--[if !supportLists]-->1.3 <!--[endif]-->i2c核心如何实现

在<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283198609 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 2.1<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100390038003600300039000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->中我们发现,smartarm3250_i2c_board_info被注册为板级配置信息,每一个i2c_adapter注册的时候都会探测板级信息中注册的设备地址,探测成功的则建立client注册进i2c核心。当i2c_driverdriver成员注册的时候,I2C核心会将已经注册的client名字和该驱动的id_table进行名字的比较,比较成功的则建立i2c_driveri2c_client之间的附属关系。这就是为什么<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283217044 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 2.3<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003200310037003000340034000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->中定义example_idtable的原因。为了支持多个设备,可以给example_idtable多加几个成员。


程序清单 2.4<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003200310039003900340030000000</w:data> </xml><![endif]-->的功能又是如何实现的呢?首先程序清单 2.4<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003200310039003900340030000000</w:data> </xml><![endif]-->中的example_driver的注册流程和程序清单 1.4<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100340030003000320033000000</w:data> </xml><![endif]-->中是类似的。区别在于第二类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更像一个用户,提供需求和产品检验。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值