<!--[if !supportLists]-->第1章 <!--[endif]-->I2C的Legacy model驱动
Linux下的i2c驱动的编写有两种类型:Legacy model和Standard driver model(new style)。
第一种风格的驱动需要自己创建i2c_client,并且需要驱动作者知道i2c设备的地址。第二种风格的驱动不需要自己创建i2c_client,但是需要填写支持的设备列表或者支持设备的地址列表。
本文档将要讨论i2c设备驱动的编写,不涉及i2c适配器驱动的编写。
<!--[if !supportLists]-->1.1 <!--[endif]-->重要的数据结构
i2c设备驱动的主体是围绕i2c_driver进行的,这个结构体的定义如<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283130843 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 1.1<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100330030003800340033000000</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:_Ref283130843'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283130843'><span lang=EN-US><span style='mso-spacerun:yes'> </span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->1<!--[if supportFields]><span style='mso-bookmark: _Ref283130843'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283130843'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283130843'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283130843'><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:_Ref283130843'></span><span style='mso-element:field-end'></span><![endif]--> i2c_driver
/* include/linux/i2c.h */
struct i2c_driver {
int id;
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *); <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 1 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑴<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
int (*detach_adapter)(struct i2c_adapter *);
int (*detach_client)(struct i2c_client *); <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 2 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑵<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
int (*probe)(struct i2c_client *, const struct i2c_device_id *); <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 3 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑶<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
int (*remove)(struct i2c_client *);
void (*shutdown)(struct i2c_client *); <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 4 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑷<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 5 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑸<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
struct device_driver driver;
const struct i2c_device_id *id_table; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 6 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑹<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);
const struct i2c_client_address_data *address_data;
struct list_head clients;
};
重要的成员的含义如下:
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span></span><span lang=EN-US style='mso-ansi-language:DE'><span style='mso-spacerun:yes'> </span></span><span lang=DE style='mso-ansi-language:DE'>= 1 \* GB2 </span><span lang=EN-US><span style='mso-element:field-separator'></span></span><![endif]-->⑴<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]--> attach_adapter和detach_adapter是Legacy model的驱动需要完成的接口,它们是回调函数,分别在依附和脱离适配器的时候被调用。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span></span><span lang=EN-US style='mso-ansi-language:DE'><span style='mso-spacerun:yes'> </span></span><span lang=DE style='mso-ansi-language:DE'>= 2 \* GB2 </span><span lang=EN-US><span style='mso-element:field-separator'></span></span><![endif]-->⑵<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->当某个client被移除的时候,detach_client被调用,给i2c_driver一个机会去做一些清理工作 (LEGACY I2C DRIVERS ONLY) 。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span></span><span lang=EN-US style='mso-ansi-language:DE'><span style='mso-spacerun:yes'> </span></span><span lang=DE style='mso-ansi-language:DE'>= 3 \* GB2 </span><span lang=EN-US><span style='mso-element:field-separator'></span></span><![endif]-->⑶<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->绑定和移除设备时被调用的两个函数(NEW STYLE DRIVERS ONLY)。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span></span><span lang=EN-US style='mso-ansi-language:DE'><span style='mso-spacerun:yes'> </span></span><span lang=DE style='mso-ansi-language:DE'>= 4 \* GB2 </span><span lang=EN-US><span style='mso-element:field-separator'></span></span><![endif]-->⑷<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->这三个函数分别用于关闭、挂起和恢复设备。用于设备的电源管理。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 5 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑸<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->一个类似ioctl的函数,不推荐使用。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 6 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑹<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->驱动能够支持的设备列表。
每一个具体的设备用一个i2c_client表示,其代码如程序清单 1.2<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100330032003500330030000000</w:data> </xml><![endif]-->所示。
程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283132530'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283132530'><span lang=EN-US><span style='mso-spacerun:yes'> </span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->1<!--[if supportFields]><span style='mso-bookmark: _Ref283132530'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283132530'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283132530'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283132530'><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:_Ref283132530'></span><span style='mso-element:field-end'></span><![endif]--> i2c_client
/* include/linux/i2c.h */
struct i2c_client {
unsigned short flags; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 1 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑴<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
unsigned short addr; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 2 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑵<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
char name[I2C_NAME_SIZE]; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 3 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑶<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
struct i2c_adapter *adapter; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 4 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑷<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
struct i2c_driver *driver; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 5 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑸<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
struct device dev;
int irq; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 6 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑹<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
struct list_head list;
struct list_head detected;
struct completion released;
};
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */
#define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */
重要的成员的含义如下:
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span></span><span lang=EN-US style='mso-ansi-language:DE'><span style='mso-spacerun:yes'> </span></span><span lang=DE style='mso-ansi-language:DE'>= 1 \* GB2 </span><span lang=EN-US><span style='mso-element:field-separator'></span></span><![endif]-->⑴<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->flag为I2C_CLIENT_TEN表示设备地址为10比特,I2C_CLIENT_PEC表示对SMBus进行错误检查。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 2 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑵<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->存放代表的设备地址,存储在最低7比特。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 3 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑶<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->设备的名字。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 4 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑷<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->设备依附的适配器。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 5 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑸<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->设备依附的驱动。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 6 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑹<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->设备使用的中断。
在i2c核心收到的设备的地址,是以i2c_client_address_data的形式传递过去的。这个结构体的定义如<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283133552 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 1.3<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100330033003500350032000000</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:_Ref283133552'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283133552'><span lang=EN-US><span style='mso-spacerun:yes'> </span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->1<!--[if supportFields]><span style='mso-bookmark: _Ref283133552'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283133552'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283133552'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283133552'><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:_Ref283133552'></span><span style='mso-element:field-end'></span><![endif]--> i2c_client_address_data
/* include/linux/i2c.h */
struct i2c_client_address_data {
const unsigned short *normal_i2c; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 1 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑴<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
const unsigned short *probe; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 2 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑵<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
const unsigned short *ignore; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 3 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑶<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
const unsigned short * const *forces; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 4 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑷<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
};
各个成员的含义如下:
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 1 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑴<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->这个指针指向正常探测的地址数组,将在每一个适配器上探测这个数组的地址。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 2 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑵<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->这个指针指向的内存每两个元素为一组,前一个代表适配器号,后一个代表地址。使用这个指针可以实现在指定的适配器的指定地址进行探测。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 3 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑶<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->这个指针指向将被忽略的地址,在对i2c_normal中的地址探测前将首先查看是否存在于这个数组,只有在这个数组中不存在的地址才能探测。这个指针指向的内容也是每个适配器号后紧跟一个地址。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 4 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑷<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->强制使用的地址,不进行物理探测就使用。物理探测的含义是主机发送信号测试能否收到回应,确定某个地址代表的设备已经连接在总线上了。这个指针指向一个指针数组,每一个成员所指的内容也是每个适配器号后紧跟一个地址。
上面介绍的指针指向的数组都以I2C_CLIENT_END结尾。
<!--[if !supportLists]-->1.2 <!--[endif]-->驱动例程
在Linux内核目录的文档documentation/i2c/upgrading-clients中有一个Legacy model的驱动例程,稍加改动之后如<!--[if supportFields]><span lang=EN-US style='mso-bidi-font-size:9.0pt'><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 style='mso-bidi-font-size:9.0pt'><span style='mso-element:field-end'></span></span><![endif]-->所示。
程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283140023'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283140023'><span lang=EN-US><span style='mso-spacerun:yes'> </span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->1<!--[if supportFields]><span style='mso-bookmark: _Ref283140023'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283140023'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283140023'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283140023'><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:_Ref283140023'></span><span style='mso-element:field-end'></span><![endif]--> i2c legacy驱动例程
struct example_state { <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 1 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑴<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
struct i2c_client client;
....
};
static struct i2c_driver example_driver;
static unsigned short normal_addr[] = { OUR_ADDR, I2C_CLIENT_END };
I2C_CLIENT_INSMOD; <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 2 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑵<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
static int example_attach(struct i2c_adapter *adap, int addr, int kind)
{
struct example_state *state;
int ret;
state = kzalloc(sizeof(struct example_state), GFP_KERNEL);
if (state == NULL) {
dev_err(dev, "failed to create our state\n");
return -ENOMEM;
}
state ->client.addr = addr;
state ->client.flags = 0;
state ->client.adapter = adap;
state ->driver = &example_driver;
i2c_set_clientdata(&state->client, state);
strlcpy(state -> client.name, "example", I2C_NAME_SIZE);
ret = i2c_attach_client(&state->client);
if (ret < 0) {
dev_err(dev, "failed to attach client\n");
kfree(state);
return ret;
}
/* rest of the initialisation goes here. */
return 0;
}
static int __devexit example_detach (struct i2c_client *client)
{
struct example_state *state = i2c_get_clientdata(client);
i2c_detach_client(client);
kfree(state);
return 0;
}
static int example_attach_adapter(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, example_attach);
}
static struct i2c_driver example_driver = { <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 3 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑶<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
.driver = {
.owner = THIS_MODULE,
.name = "example",
},
.attach_adapter = example_attach_adapter,
.detach_ client = example_detach,
};
static int __init example _init(void) <!--[if supportFields]><span lang=DE><span style='mso-element:field-begin'></span> = 4 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑷<!--[if supportFields]><span lang=DE><span style='mso-element:field-end'></span></span><![endif]-->
{
int res;
if ((res = i2c_add_driver(&example_driver))) {
printk("example: Driver registration failed, module not inserted.\n");
return res;
}
return 0;
}
static void __exit example_cleanup(void)
{
i2c_del_driver(&example_driver);
}
module_init(foo_init);
module_exit(foo_cleanup);
这个函数中总共做了四件事:
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 1 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑴<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->创建i2c_client结构体。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 2 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑵<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->填写需要探测的地址。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 3 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑶<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->创建i2c_driver结构体,并初始化它的成员。
<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>= 4 \* GB2 <span style='mso-element:field-separator'></span></span><![endif]-->⑷<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->注册i2c_driver。
<!--[if !supportLists]-->1.2.1 <!--[endif]-->探测地址
在程序清单 1.4<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100340030003000320033000000</w:data> </xml><![endif]-->中的example_attach_adapter函数中使用了一个addr_data作为参数,仔细观察发现上下文并未一定义这个变量,查看i2c_probe函数可以知道addr_data应该是一个i2c_client_address_data类型的变量,它的成员如程序清单 1.3<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100330033003500350032000000</w:data> </xml><![endif]-->所示。
这个成员其实是I2C_CLIENT_INSMOD这个宏扩展得到的,如<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283625783 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 1.5<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003600320035003700380033000000</w:data> </xml><![endif]--><!--[if supportFields]><span lang=EN-US><span style='mso-element:field-end'></span></span><![endif]-->所示。要想得到一个可用的addr_data,在I2C_CLIENT_INSMOD之前必须定义探测地址的数组。应该定义normal_addr这个数组,这个数组中的地址会在每一个适配器上探测。模块参数数组有三个:probe、ignore和forces。注意名字不能改变。这四个数组分别对应i2c_client_address_data的四个成员。它们都必须以I2C_CLIENT_END结尾。
程序清单 <!--[if supportFields]><span style='mso-bookmark:_Ref283625783'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283625783'><span lang=EN-US><span style='mso-spacerun:yes'> </span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->1<!--[if supportFields]><span style='mso-bookmark: _Ref283625783'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283625783'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283625783'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283625783'><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]-->5<!--[if supportFields]><span style='mso-bookmark:_Ref283625783'></span><span style='mso-element:field-end'></span><![endif]--> I2C_CLIENT_INSMOD
#define I2C_CLIENT_MAX_OPTS 48
/* Default fill of many variables */
#define I2C_CLIENT_DEFAULTS {I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \
·················································
I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END}
/* I2C_CLIENT_MODULE_PARM creates a module parameter, and puts it in the
module header */
#define I2C_CLIENT_MODULE_PARM(var,desc) \
static unsigned short var[I2C_CLIENT_MAX_OPTS] = I2C_CLIENT_DEFAULTS; \
static unsigned int var##_num; \
module_param_array(var, short, &var##_num, 0); \
MODULE_PARM_DESC(var,desc)
#define I2C_CLIENT_MODULE_PARM_FORCE(name) \
I2C_CLIENT_MODULE_PARM(force_##name, \
"List of adapter,address pairs which are " \
"unquestionably assumed to contain a `" \
# name "' chip")
#define I2C_CLIENT_INSMOD_COMMON \
I2C_CLIENT_MODULE_PARM(probe, "List of adapter,address pairs to scan " \
"additionally"); \
I2C_CLIENT_MODULE_PARM(ignore, "List of adapter,address pairs not to " \
"scan"); \
static const struct i2c_client_address_data addr_data = { \
.normal_i2c = normal_i2c, \
.probe = probe, \
.ignore = ignore, \
.forces = forces, \
}
#define I2C_CLIENT_FORCE_TEXT \
"List of adapter,address pairs to boldly assume to be present"
/* These are the ones you want to use in your own drivers. Pick the one
which matches the number of devices the driver differenciates between. */
#define I2C_CLIENT_INSMOD \
I2C_CLIENT_MODULE_PARM(force, I2C_CLIENT_FORCE_TEXT); \
static const unsigned short * const forces[] = { force, NULL }; \
I2C_CLIENT_INSMOD_COMMON
I2C_CLIENT_INSMOD首先调用I2C_CLIENT_MODULE_PARM创建了一个模块参数force数组,其容量大小为I2C_CLIENT_MAX_OPTS(48),并用I2C_CLIENT_END初始化所有成员,然后将force数组声明为模块参数。注意模块参数是force不是forces。
然后用I2C_CLIENT_INSMOD_COMMON定义了一个指针数组forces[],并定义probe和ignore数组(容量大小48),用I2C_CLIENT_END初始化所有成员之后声明为模块参数。最后声明一个i2c_client_address_data型变量addr_data并使用之前定义的初始化它的成员。
可以看到I2C_CLIENT_MODULE_PARM直接或者间接的被调用声明并初始化了force、probe、ignore三个数组,还将它们声明为模块参数。所以如果自己定义这三个数组会引起编译错误。这三个数组只能在插入模块的时候初始化。
另外三个数组的更多解释和要求参考程序清单 1.3<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100330033003500350032000000</w:data> </xml><![endif]-->的分析。
<!--[if !supportLists]-->1.2.2 <!--[endif]-->定义并初始化i2c_driver
驱动程序的主要工作就是定义并初始化一个i2c_driver结构体。i2c_driver的成员参考程序清单 1.1<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003100330030003800340033000000</w:data> </xml><![endif]-->。
i2c_driver中的driver成员至少应该初始化它的name成员,owner成员i2核心也会进行初始化。
i2c_driver的函数指针至少应该初始化attach_adapter和detach_ client,另外attach_adapter会使用example_attach函数,这个函数主要是将我们的client注册到系统中。这两个函数指针对应的函数实现比较固定,如<!--[if supportFields]><span lang=EN-US style='mso-bidi-font-size:9.0pt'><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 style='mso-bidi-font-size:9.0pt'><span style='mso-element:field-end'></span></span><![endif]-->所示。
另外,i2c_driver的shutdown、suspend、resume这三个函数指针是否初始化是可选的。这三个函数指针分别对应关机、挂起、唤醒。
<!--[if !supportLists]-->1.2.3 <!--[endif]-->注册i2c_driver
当所有的准备工作做完以后就可以注册i2c_driver了。一般在模块的初始化函数中注册i2c_driver。
<!--[if !supportLists]-->1.4 <!--[endif]-->I2C通信接口
如果已经将i2c驱动正确的编译并插入内核,那么内核中提供了一些接口和设备通信:
extern int i2c_master_send(struct i2c_client *client, const char* buf, int len);
extern int i2c_master_recv(struct i2c_client * client,char* buf, int len);
这两个函数都是让client对应的适配器以主机的身份和client->addr地址的设备进行通信,返回值是实际读写的字节数。Linux下的i2c适配器不支持从机模式。
上面的两个函数有个弊端,那就是只能完成单方向的通信,如果通信的过程既有发送又有接收而且接收和发送不能分开,那就需要调用另一个函数:
extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num);
实际上,上面两个函数也是直接调用了i2c_transfer。
i2c_transfer中的参数有三个,第一个是适配器结构体的指针,第二个是消息数组的头指针,第三个是消息的数量。这个函数发送一系列的消息。每个消息可以是读,也可以是写,也可以混合。发送过程是连贯的,在发送中没有停止条件。它的返回值是成功执行的消息数目。
消息的格式定义如<!--[if supportFields]><span lang=EN-US><span style='mso-element:field-begin'></span> REF _Ref283227534 \h <span style='mso-element:field-separator'></span></span><![endif]-->程序清单 1.7<!--[if gte mso 9]><xml> <w:data>08D0C9EA79F9BACE118C8200AA004BA90B02000000080000000E0000005F005200650066003200380033003200320037003500330034000000</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:_Ref283227534'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283227534'><span lang=EN-US><span style='mso-spacerun:yes'> </span>STYLEREF 1 \s <span style='mso-element:field-separator'></span></span></span><![endif]-->1<!--[if supportFields]><span style='mso-bookmark: _Ref283227534'></span><span style='mso-element:field-end'></span><![endif]-->.<!--[if supportFields]><span style='mso-bookmark:_Ref283227534'></span><span style='mso-element:field-begin'></span><span style='mso-bookmark:_Ref283227534'><span lang=EN-US> SEQ </span></span><span style='mso-bookmark:_Ref283227534'><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]-->7<!--[if supportFields]><span style='mso-bookmark:_Ref283227534'></span><span style='mso-element:field-end'></span><![endif]--> i2c_msg
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* 10bit地址 */
#define I2C_M_RD 0x0001 /*读取数据标志,清零表示写 */
#define I2C_M_NOSTART x4000 /* 不发送起始位 */
#define I2C_M_REV_DIR_ADDR 0x2000 /* 反转读写标志 */
#define I2C_M_IGNORE_NAK x1000 /*忽略I2C器件的ack和nack信号 */
#define I2C_M_NO_RD_ACK 0x0800 /*读操作时不去ACK */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
flags各位的含义已经用宏定义好了。如果连续多条消息的话,除了第一条之外,余下的都不需要发送起始条件。