I2C的Legacy model驱动

来源:互联网 发布:锁屏后仍能录像的软件 编辑:程序博客网 时间:2024/05/21 09:46

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


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

第1章 I2CLegacy model驱动

Linux下的i2c驱动的编写有两种类型:Legacy modelStandard driver modelnew 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_adapterdetach_adapterLegacy 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]-->flagI2C_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_dataI2C_CLIENT_INSMOD之前必须定义探测地址的数组。应该定义normal_addr这个数组,这个数组中的地址会在每一个适配器上探测。模块参数数组有三个:probeignoreforces。注意名字不能改变。这四个数组分别对应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[],并定义probeignore数组(容量大小48),用I2C_CLIENT_END初始化所有成员之后声明为模块参数。最后声明一个i2c_client_address_data型变量addr_data并使用之前定义的初始化它的成员。

可以看到I2C_CLIENT_MODULE_PARM直接或者间接的被调用声明并初始化了forceprobeignore三个数组,还将它们声明为模块参数。所以如果自己定义这三个数组会引起编译错误。这三个数组只能在插入模块的时候初始化。

另外三个数组的更多解释和要求参考程序清单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_adapterdetach_ client,另外attach_adapter会使用example_attach函数,这个函数主要是将我们的client注册到系统中。这两个函数指针对应的函数实现比较固定,如<!--[if supportFields]>REF _Ref283140023 /h<![endif]-->程序清单 1.4<!--[if gte mso 9]><![endif]--><!--[if supportFields]><![endif]-->所示。

另外,i2c_drivershutdownsuspendresume这三个函数指针是否初始化是可选的。这三个函数指针分别对应关机、挂起、唤醒。

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器件的acknack信号                                 */

#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各位的含义已经用宏定义好了。如果连续多条消息的话,除了第一条之外,余下的都不需要发送起始条件。

原创粉丝点击