I2C的Legacy model驱动
来源:互联网 发布:锁屏后仍能录像的软件 编辑:程序博客网 时间:2024/05/21 09:46
============================================
作者:yuanlulu
http://blog.csdn.net/yuanlulu
版权没有,但是转载请保留此段声明
============================================
第1章 I2C的Legacy model驱动
Linux下的i2c驱动的编写有两种类型:Legacy model和Standard driver model(new style)。
第一种风格的驱动需要自己创建i2c_client,并且需要驱动作者知道i2c设备的地址。第二种风格的驱动不需要自己创建i2c_client,但是需要填写支持的设备列表或者支持设备的地址列表。
本文档将要讨论i2c设备驱动的编写,不涉及i2c适配器驱动的编写。
1.1 重要的数据结构
i2c设备驱动的主体是围绕i2c_driver进行的,这个结构体的定义如<!--[if supportFields]> REF _Ref283130843 /h<![endif]-->程序清单 1.1<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。
程序清单 <!--[if supportFields]> STYLEREF 1 /s<![endif]-->1<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1<![endif]-->1<!--[if supportFields]><![endif]--> i2c_driver
/* include/linux/i2c.h */
struct i2c_driver {
int id;
unsignedint class;
int(*attach_adapter)(struct i2c_adapter *); <!--[if supportFields]> = 1 /* GB2<![endif]-->⑴<!--[if supportFields]><![endif]-->
int(*detach_adapter)(struct i2c_adapter *);
int(*detach_client)(struct i2c_client *); <!--[if supportFields]> = 2 /* GB2<![endif]-->⑵<!--[if supportFields]><![endif]-->
int(*probe)(struct i2c_client *, const struct i2c_device_id *); <!--[if supportFields]> = 3 /* GB2<![endif]-->⑶<!--[if supportFields]><![endif]-->
int(*remove)(struct i2c_client *);
void(*shutdown)(struct i2c_client *); <!--[if supportFields]> = 4 /* GB2<![endif]-->⑷<!--[if supportFields]><![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]> = 5 /* GB2<![endif]-->⑸<!--[if supportFields]><![endif]-->
structdevice_driver driver;
conststruct i2c_device_id *id_table; <!--[if supportFields]> = 6 /* GB2<![endif]-->⑹<!--[if supportFields]><![endif]-->
int(*detect)(struct i2c_client *, int kind, struct i2c_board_info *);
conststruct i2c_client_address_data *address_data;
structlist_head clients;
};
重要的成员的含义如下:
<!--[if supportFields]> = 1 /* GB2<![endif]-->⑴<!--[if supportFields]><![endif]--> attach_adapter和detach_adapter是Legacy model的驱动需要完成的接口,它们是回调函数,分别在依附和脱离适配器的时候被调用。
<!--[if supportFields]> = 2 /* GB2<![endif]-->⑵<!--[if supportFields]><![endif]-->当某个client被移除的时候,detach_client被调用,给i2c_driver一个机会去做一些清理工作 (LEGACY I2C DRIVERS ONLY) 。
<!--[if supportFields]> = 3 /* GB2<![endif]-->⑶<!--[if supportFields]><![endif]-->绑定和移除设备时被调用的两个函数(NEW STYLE DRIVERS ONLY)。
<!--[if supportFields]> = 4 /* GB2<![endif]-->⑷<!--[if supportFields]><![endif]-->这三个函数分别用于关闭、挂起和恢复设备。用于设备的电源管理。
<!--[if supportFields]> =5 /* GB2<![endif]-->⑸<!--[if supportFields]><![endif]-->一个类似ioctl的函数,不推荐使用。
<!--[if supportFields]> =6 /* GB2<![endif]-->⑹<!--[if supportFields]><![endif]-->驱动能够支持的设备列表。
每一个具体的设备用一个i2c_client表示,其代码如程序清单 1.2<!--[if gte mso 9]><![endif]-->所示。
程序清单 <!--[if supportFields]> STYLEREF 1 /s<![endif]-->1<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1<![endif]-->2<!--[if supportFields]><![endif]--> i2c_client
/* include/linux/i2c.h */
struct i2c_client {
unsignedshort flags; <!--[if supportFields]> = 1 /* GB2<![endif]-->⑴<!--[if supportFields]><![endif]-->
unsignedshort addr; <!--[if supportFields]> = 2 /* GB2<![endif]-->⑵<!--[if supportFields]><![endif]-->
charname[I2C_NAME_SIZE]; <!--[if supportFields]> = 3 /* GB2<![endif]-->⑶<!--[if supportFields]><![endif]-->
structi2c_adapter *adapter; <!--[if supportFields]> = 4 /* GB2<![endif]-->⑷<!--[if supportFields]><![endif]-->
structi2c_driver *driver; <!--[if supportFields]> = 5 /* GB2<![endif]-->⑸<!--[if supportFields]><![endif]-->
structdevice dev;
int irq; <!--[if supportFields]> = 6 /* GB2<![endif]-->⑹<!--[if supportFields]><![endif]-->
structlist_head list;
structlist_head detected;
structcompletion 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]> = 1 /* GB2<![endif]-->⑴<!--[if supportFields]><![endif]-->flag为I2C_CLIENT_TEN表示设备地址为10比特,I2C_CLIENT_PEC表示对SMBus进行错误检查。
<!--[if supportFields]> =2 /* GB2<![endif]-->⑵<!--[if supportFields]><![endif]-->存放代表的设备地址,存储在最低7比特。
<!--[if supportFields]> =3 /* GB2<![endif]-->⑶<!--[if supportFields]><![endif]-->设备的名字。
<!--[if supportFields]> =4 /* GB2<![endif]-->⑷<!--[if supportFields]><![endif]-->设备依附的适配器。
<!--[if supportFields]> =5 /* GB2<![endif]-->⑸<!--[if supportFields]><![endif]-->设备依附的驱动。
<!--[if supportFields]> =6 /* GB2<![endif]-->⑹<!--[if supportFields]><![endif]-->设备使用的中断。
在i2c核心收到的设备的地址,是以i2c_client_address_data的形式传递过去的。这个结构体的定义如<!--[if supportFields]> REF _Ref283133552 /h<![endif]-->程序清单 1.3<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。
程序清单 <!--[if supportFields]> STYLEREF 1 /s<![endif]-->1<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1<![endif]-->3<!--[if supportFields]><![endif]--> i2c_client_address_data
/* include/linux/i2c.h */
struct i2c_client_address_data {
constunsigned short *normal_i2c; <!--[if supportFields]> = 1 /* GB2<![endif]-->⑴<!--[if supportFields]><![endif]-->
constunsigned short *probe; <!--[if supportFields]> = 2 /* GB2<![endif]-->⑵<!--[if supportFields]><![endif]-->
constunsigned short *ignore; <!--[if supportFields]> = 3 /* GB2<![endif]-->⑶<!--[if supportFields]><![endif]-->
constunsigned short * const *forces; <!--[if supportFields]> = 4 /* GB2<![endif]-->⑷<!--[if supportFields]><![endif]-->
};
各个成员的含义如下:
<!--[if supportFields]> =1 /* GB2<![endif]-->⑴<!--[if supportFields]><![endif]-->这个指针指向正常探测的地址数组,将在每一个适配器上探测这个数组的地址。
<!--[if supportFields]> =2 /* GB2<![endif]-->⑵<!--[if supportFields]><![endif]-->这个指针指向的内存每两个元素为一组,前一个代表适配器号,后一个代表地址。使用这个指针可以实现在指定的适配器的指定地址进行探测。
<!--[if supportFields]> =3 /* GB2<![endif]-->⑶<!--[if supportFields]><![endif]-->这个指针指向将被忽略的地址,在对i2c_normal中的地址探测前将首先查看是否存在于这个数组,只有在这个数组中不存在的地址才能探测。这个指针指向的内容也是每个适配器号后紧跟一个地址。
<!--[if supportFields]> =4 /* GB2<![endif]-->⑷<!--[if supportFields]><![endif]-->强制使用的地址,不进行物理探测就使用。物理探测的含义是主机发送信号测试能否收到回应,确定某个地址代表的设备已经连接在总线上了。这个指针指向一个指针数组,每一个成员所指的内容也是每个适配器号后紧跟一个地址。
上面介绍的指针指向的数组都以I2C_CLIENT_END结尾。
1.2 驱动例程
在Linux内核目录的文档documentation/i2c/upgrading-clients中有一个Legacymodel的驱动例程,稍加改动之后如<!--[if supportFields]>REF _Ref283140023 /h <![endif]-->程序清单 1.4<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。
程序清单 <!--[if supportFields]> STYLEREF 1 /s<![endif]-->1<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1<![endif]-->4<!--[if supportFields]><![endif]--> i2c legacy驱动例程
struct example_state { <!--[if supportFields]> = 1 /* GB2<![endif]-->⑴<!--[if supportFields]><![endif]-->
structi2c_client client;
....
};
static struct i2c_driver example_driver;
static unsigned short normal_addr[] = { OUR_ADDR,I2C_CLIENT_END };
I2C_CLIENT_INSMOD; <!--[if supportFields]> = 2 /* GB2<![endif]-->⑵<!--[if supportFields]><![endif]-->
static int example_attach(struct i2c_adapter *adap,int addr, int kind)
{
structexample_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);
returnret;
}
/* restof the initialisation goes here. */
return0;
}
static int __devexit example_detach (struct i2c_client*client)
{
structexample_state *state = i2c_get_clientdata(client);
i2c_detach_client(client);
kfree(state);
return0;
}
static int example_attach_adapter(struct i2c_adapter*adap)
{
returni2c_probe(adap, &addr_data, example_attach);
}
static struct i2c_driver example_driver = { <!--[if supportFields]> = 3 /* GB2<![endif]-->⑶<!--[if supportFields]><![endif]-->
.driver ={
.owner = THIS_MODULE,
.name = "example",
},
.attach_adapter = example_attach_adapter,
.detach_client = example_detach,
};
static int __init example _init(void) <!--[if supportFields]> = 4 /* GB2<![endif]-->⑷<!--[if supportFields]><![endif]-->
{
int res;
if ((res =i2c_add_driver(&example_driver))) {
printk("example: Driver registrationfailed, 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]> =1 /* GB2<![endif]-->⑴<!--[if supportFields]><![endif]-->创建i2c_client结构体。
<!--[if supportFields]> =2 /* GB2<![endif]-->⑵<!--[if supportFields]><![endif]-->填写需要探测的地址。
<!--[if supportFields]> =3 /* GB2<![endif]-->⑶<!--[if supportFields]><![endif]-->创建i2c_driver结构体,并初始化它的成员。
<!--[if supportFields]> =4 /* GB2<![endif]-->⑷<!--[if supportFields]><![endif]-->注册i2c_driver。
1.2.1 探测地址
在程序清单1.4<!--[if gte mso 9]><![endif]-->中的example_attach_adapter函数中使用了一个addr_data作为参数,仔细观察发现上下文并未一定义这个变量,查看i2c_probe函数可以知道addr_data应该是一个i2c_client_address_data类型的变量,它的成员如程序清单1.3<!--[if gte mso 9]><![endif]-->所示。
这个成员其实是I2C_CLIENT_INSMOD这个宏扩展得到的,如<!--[if supportFields]> REF _Ref283625783 /h<![endif]-->程序清单 1.5<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。要想得到一个可用的addr_data,在I2C_CLIENT_INSMOD之前必须定义探测地址的数组。应该定义normal_addr这个数组,这个数组中的地址会在每一个适配器上探测。模块参数数组有三个:probe、ignore和forces。注意名字不能改变。这四个数组分别对应i2c_client_address_data的四个成员。它们都必须以I2C_CLIENT_END结尾。
程序清单 <!--[if supportFields]> STYLEREF 1 /s<![endif]-->1<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1<![endif]-->5<!--[if supportFields]><![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) /
staticunsigned short var[I2C_CLIENT_MAX_OPTS] = I2C_CLIENT_DEFAULTS; /
staticunsigned 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 pairswhich are " /
"unquestionably assumed to containa `" /
# name "' chip")
#define I2C_CLIENT_INSMOD_COMMON /
I2C_CLIENT_MODULE_PARM(probe, "List ofadapter,address pairs to scan " /
"additionally"); /
I2C_CLIENT_MODULE_PARM(ignore, "List ofadapter,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 /
"Listof adapter,address pairs to boldly assume to be present"
/* These are the ones you want to use in your owndrivers. Pick the one
which matchesthe 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]><![endif]-->的分析。
1.2.2 定义并初始化i2c_driver
驱动程序的主要工作就是定义并初始化一个i2c_driver结构体。i2c_driver的成员参考程序清单1.1<!--[if gte mso 9]><![endif]-->。
i2c_driver中的driver成员至少应该初始化它的name成员,owner成员i2核心也会进行初始化。
i2c_driver的函数指针至少应该初始化attach_adapter和detach_ client,另外attach_adapter会使用example_attach函数,这个函数主要是将我们的client注册到系统中。这两个函数指针对应的函数实现比较固定,如<!--[if supportFields]>REF _Ref283140023 /h<![endif]-->程序清单 1.4<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。
另外,i2c_driver的shutdown、suspend、resume这三个函数指针是否初始化是可选的。这三个函数指针分别对应关机、挂起、唤醒。
1.2.3 注册i2c_driver
当所有的准备工作做完以后就可以注册i2c_driver了。一般在模块的初始化函数中注册i2c_driver。
1.4 I2C通信接口
如果已经将i2c驱动正确的编译并插入内核,那么内核中提供了一些接口和设备通信:
extern int i2c_master_send(struct i2c_client *client, constchar* buf, int len);
extern int i2c_master_recv(struct i2c_client * client,char*buf, int len);
这两个函数都是让client对应的适配器以主机的身份和client->addr地址的设备进行通信,返回值是实际读写的字节数。Linux下的i2c适配器不支持从机模式。
上面的两个函数有个弊端,那就是只能完成单方向的通信,如果通信的过程既有发送又有接收而且接收和发送不能分开,那就需要调用另一个函数:
extern inti2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num);
实际上,上面两个函数也是直接调用了i2c_transfer。
i2c_transfer中的参数有三个,第一个是适配器结构体的指针,第二个是消息数组的头指针,第三个是消息的数量。这个函数发送一系列的消息。每个消息可以是读,也可以是写,也可以混合。发送过程是连贯的,在发送中没有停止条件。它的返回值是成功执行的消息数目。
消息的格式定义如<!--[if supportFields]> REF _Ref283227534 /h<![endif]-->程序清单 1.7<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。
程序清单 <!--[if supportFields]> STYLEREF 1 /s<![endif]-->1<!--[if supportFields]><![endif]-->.<!--[if supportFields]> SEQ 程序清单 /* ARABIC /s 1<![endif]-->7<!--[if supportFields]><![endif]--> i2c_msg
struct i2c_msg {
__u16addr; /* slave address */
__u16flags;
#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 */
__u16len; /* msg length */
__u8*buf; /* pointer to msg data */
};
flags各位的含义已经用宏定义好了。如果连续多条消息的话,除了第一条之外,余下的都不需要发送起始条件。
- I2C的Legacy model驱动
- I2C的Standard driver model驱动
- i2c驱动的编写
- linux驱动的i2c驱动
- 基于I2C子系统的I2C驱动编写
- Linux的I2C驱动架构
- Linux的I2C驱动架构
- Linux的i2c驱动详解
- I2C 驱动核心的数据结构
- Linux的i2c驱动详解
- 内核下的I2C驱动
- Linux的I2C驱动架构
- Linux的I2C驱动架构
- I2C设备驱动的编写
- Linux的i2c驱动详解
- bma180 的I2C驱动介绍
- Linux的I2C驱动讲解
- Linux的i2c驱动详解
- 八款开源Android游戏引擎
- In java, range of byte : -128 ----- 127(for SCJP)
- VS2008 下安装 AjaxControlToolkit
- js jQuery mobile
- 量子基金
- I2C的Legacy model驱动
- [转]基于uda34x的ALSA声卡驱动之s3c24xx-pcm.c
- 窗体生命周期所响应的消息
- $.get( url, [data], [callback] )
- 生活感悟
- 做为新人-- 转载一下人家对新人的看法
- [转]基于uda34x的ALSA声卡驱动之s3c24xx-i2s.c
- 数据常用命令,记录下来
- intel inspector xe的VISUAL(EDITOR)变量的设置