• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

20150424adapter实现i2c驱动程序编写

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

20150424 adapter实现i2c驱动程序编写

2015-04-24 Lover雪儿

    i2c的驱动程序可以由旧探测方法(adapt)和新探测方法(probe)这两种方法实现.

    在i2c_driver中,adapt老方法使用的是attach_adapter作为探测函数,而在新方法中使用的是probe作为探测函数。

 1 I2C设备驱动结构:
 2 struct i2c_driver {
 3     unsigned int class;
 4 
 5     /* 添加和卸载I2C设备,老版本*/
 6     int (*attach_adapter)(struct i2c_adapter *) __deprecated;
 7     int (*detach_adapter)(struct i2c_adapter *) __deprecated;
 8 
 9     /* 添加和卸载I2C设备,新版本*/
10     int (*probe)(struct i2c_client *, const struct i2c_device_id *);
11     int (*remove)(struct i2c_client *);
12 
13     /*休眠和唤醒 */
14     void (*shutdown)(struct i2c_client *);
15     int (*suspend)(struct i2c_client *, pm_message_t mesg);
16     int (*resume)(struct i2c_client *);
17     void (*alert)(struct i2c_client *, unsigned int data);
18     int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
19     struct device_driver driver;
20     const struct i2c_device_id *id_table;
21 
22     /* Device detection callback for automatic device creation */
23     int (*detect)(struct i2c_client *, struct i2c_board_info *);
24     const unsigned short *address_list;
25     struct list_head clients;
26 };

 

    从简单开始学习,我们今天从旧探测方法adapt开始学习实现存储芯片at24cxx的i2c驱动程序,注意本试验的实现必须在linux-2.6.22.6或者更低版本的内核上编译,原因是linux-2.6.30以后的内核i2c.c文件中已经取消了统一的i2c_probe()这个函数了,所以本实验再高版本的内核中是无法实现的.

一、一个简单的I2C驱动

1.定义i2c_client_address_data结构体用于设置设备地址

    i2c_client_address_data是在include/linux/i2c.h中定义或者自己定义的一个结构体(如上图所示)。它的一个最主要的作用就是定义设备地址。

    在设备地址数组中,I2C_CLIENT_END是指设备地址结束,一旦系统检测到这个值,就会停止扫描。

    如上图中所示:

    在normal_addr[]数组中填入我们的设备地址(注意设备地址为7位),然后使用I2C_CLIENT_END结束。

    如果自己未定义,则会使用i2c.h中默认定义的结构体。

1 struct i2c_client_address_data {
2     unsigned short *normal_i2c;
3     unsigned short *probe;
4     unsigned short *ignore;
5     unsigned short **forces;
6 };

    一般来说,采用默认定义的是不会工作的,因为i2c-core是不可能知道设备地址的,i2c-probe探测函数也是不可能找到设备地址,i2c-probe如果能传入地址的话,是很容易导致系统混乱的从而一起未知的错误。

2.定义i2c_driver结构体

如图所示:

在i2c_driver结构体中定义了i2c设备的名字,探测函数attach_adapter,卸载函数detach_adapter这三个属性。

3.完善探测函数等

如图所示:分别实现了前面i2c_driver结构体中定义的三个属性。

在探测函数中调用i2c.h(linux2.6.22内核以前)中的的i2c_probe函数进行匹配探测。

4.在入口函数中注册i2c驱动

    I2C的设备驱动是通过i2c_add_driver(&at24cxx_i2c_driver)向i2c-core注册的,my_driver中的核心是detach和attach函数,在attach中通过i2c_probe探测到总线上的设备并把设备和驱动建立连接以完成设备的初始化。

static int my_attach(struct i2c_adapter *adapter){

return i2c_probe(adapter, &addr_data, my_probe);

}

5.在出口函数中删除i2c-driver驱动。

6.在linux下编译结果

 

7.总结i2c程序的工作流程

简单的来说i2c的实现驱动就分为三个步骤:定义设备地址i2c_client_address_data、定义i2c_driver结构体实现探测、在入口函数中注册i2c_driver结构体。

接下来就细细讲述一下工作流程吧。

①首先,在i2c_client_address_data的normal_i2c属性中定义好我们设备的设备地址。

②接下来,i2c_driver就出场了,它的功能是定义i2c设备的名字,探测函数,卸载函数三个属性。

③当程序在入口函数中注册i2c-driver驱动之后,系统就会根据我们第一步中定义的设备地址,调用attach_adapter函数进行匹配设备地址是否支持,在attach_adapter函数中主要的功能是在调用i2c_probe函数,当系统检测到设备地址匹配时,就会进入i2c_probe函数中干一些重要的事,接着就进入i2c-probe传入的at24cxx_detect函数中实现我们自己的事。

其实总结一下就下面一个流程:at24cxx_attach_adapter -> i2c_probe -> at24cxx_detect

④当我们卸载设备时,会自动调用i2c_driver中定义的卸载函数at24cxx_detach_adapter进行卸载设备。

⑤最后一步自然就是在出口函数中卸载i2c-driver驱动拉。

 

附上驱动程序1:

  1 #include <linux/kernel.h>
  2 #include <linux/init.h>
  3 #include <linux/module.h>
  4 #include <linux/slab.h>
  5 #include <linux/jiffies.h>
  6 #include <linux/mutex.h>
  7 #include <linux/i2c.h>
  8 #include <linux/fs.h>
  9 #include <asm/uaccess.h>
 10 #include <linux/i2c-id.h>
 11 #include <linux/device.h>    /* for struct device */
 12 #include <linux/sched.h>    /* for completion */
 13 
 14 #include <linux/ctype.h>
 15 #include <linux/types.h>
 16 #include <linux/delay.h>
 17 
 18 
 19 
 20 static unsigned short ignore[]         = {I2C_CLIENT_END};
 21 static unsigned short normal_addr[] = {0X50, I2C_CLIENT_END};    //设备地址:01010000(0x50) 七位
 22 
 23 static struct i2c_client_address_data addr_data = {
 24     .normal_i2c    = normal_addr,     //要发出地址信号,并且得到ACK信号,才能确定是否存在这个设备
 25     .probe        = ignore,
 26     .ignore        = ignore,
 27     //.forces                    //强制认为存在这个设备
 28 };
 29 
 30 
 31 static int at24cxx_detect(struct  i2c_adapter *adapter, int address, int kind)
 32 {
 33     printk("enter at24cxx_detect \n");
 34     return 0;
 35 }
 36 
 37 //探测时调用函数
 38 static int at24cxx_attach_adapter(struct i2c_adapter *adapter)
 39 {
 40     return  i2c_probe(adapter, &addr_data, at24cxx_detect);
 41 }
 42 
 43 //卸载函数
 44 static int at24cxx_detach_adapter(struct i2c_adapter *client)
 45 {
 46     printk("enter at24cxx_detach_adapter \n");
 47     return 0;
 48 }
 49 
 50 //定义i2c_driver结构体
 51 static struct i2c_driver at24cxx_i2c_driver = {
 52     .driver = {
 53         .name = "at24cxx",
 54     },
 55     .attach_adapter = at24cxx_attach_adapter,
 56     .detach_adapter = at24cxx_detach_adapter,
 57 };
 58 
 59 static int at24cxx_init(void)
 60 {
 61     /* 1.分配一个i2c_driver结构体 */
 62     /* 2.设置 */
 63     i2c_add_driver(&at24cxx_i2c_driver);    //注册i2c驱动
 64     
 65     return 0;
 66 }
 67 
 68 void at24cxx_exit(void)
 69 {
 70     i2c_del_driver(&at24cxx_i2c_driver);
 71 }
 72 
 73 module_init(at24cxx_init);
 74 module_exit(at24cxx_exit);
 75 MODULE_LICENSE("GPL");
 76 
 77 
 78 /*
 79 
 80 1.在I2C中总共有两条线,SDA,SCL,分别是负责数据的发送,时钟脉冲线
 81 起始信号,SCL变为低电平,SDA由低电平变为高电平
 82 ------------------------------------------------------------------------------
 83 | 起始位S | 7位设备地址 | R/W | 8B数据 | ACK (注8b数据和ACK可以周而复始) | P |
 84 ------------------------------------------------------------------------------
 85 设备地址最后一位表示的是读还是写
 86 
 87     刚开始前8个clk里SDA由主机驱动,发送从机设备地址,当从机发现设备地址为自己的时,
 88     便会在第9个时钟里SDA由从机驱动,I2C主机释放SDA,由从机驱动SDA
 89 写数据:前8个CLK,SDA由主机驱动
 90         第9个CLK,SDA由从机驱动
 91 写数据:前8个CLK,SDA由从机驱动
 92         第9个CLK,SDA由主机驱动
 93 停止信号,SCL变为高电平,SDA由低电平变为高电平
 94 
 95 2.i2c驱动框架
 96 ----------------------------------------------------------------------
 97 APP:  open,read,write
 98 ----------------------------------------------------------------------
 99 I2c设备驱动程序:drv_open,drv_read,drv_write,     知道数据的含义
100 ----------------------------------------------------------------------
101 I2c总线驱动程序:①识别②提供读写函数,            知道如何收发数据
102 ----------------------------------------------------------------------
103 
104 drivers/chip/                            //设备驱动程序
105 drivers/i2c/busses/i2c-s3c2410.c        //总线驱动程序
106 drivers/i2c/algos                        //算法
107 ----------------------------------------------------------------------------------------------
108                             BUS
109             i2c_add    /                    \ i2c_add_diver
110        i2c_adapter /                      \    i2c_driver    .id 表示能支持哪些设备
111                   /    插槽(适配器)          \设备驱动程序    .attach_adapter 接到适配器上去
112 ----------------------------------------------------------------------------------------------
113 i2c总线驱动程序:
114 1.分配一个i2c_adapter    插槽
115 2.设置,algo结构体,核心算法,.master_xfer,
116 3.注册i2c_add_adapter     .master_xfer:发I2C信号的函数
117 ----------------------------------------------------------------------------------------------
118 i2c_add_adapter    ①把结构体放入链表
119                 ②从driver链表取出每一项,得到其ID(设备地址)
120                 ③使用master_xfer函数发start信号,发设备地址
121                 ④如果收到ACK则发现了一个设备client
122 ---------------------------------------------------------------------------------------------                        
123 i2c设备驱动程序:
124 i2c_add_driver: ①把I2C_driver放入链表
125                 ②使用从adapter链表取出”适配器“,使用它的mater_xfer函数法start信号,发设备地址(.id里面)
126                 如果能收到ACK信号,则表示发现了一个设备client
127 --------------------------------------------------------------------------------------------
128 
129 写I2C的driver驱动程序:
130 ①分配一个i2c_driver结构体
131 ②设置
132     attach_adapter  //它直接调用i2c_probe(adap,设备地址,发现这个设备后要调用的函数)
133     detach_client    //卸载驱动后,如果之前有能支持的,则调用它来清理
134 ③注册
135 
136 
137 
138 4.测试1th
139 insmod at24cxx.ko   观察输出信息
140 修改normal_addr为0x60,加载观察输出信息。
141 
142 
143 */
at24cxx_1_detect.c

 

二、增加I2C设备地址的force属性

    不知道大家注意到没有,在i2c_client_address_data结构体中还有一个我们没有使用到的属性.forces,它是用于强制找到设备地址的,现在我们就在前面已经实现好的驱动程序中实现forces属性。

    当我们设备不存在时,可以使用.froces属性强制系统认为存在设备,进入probe函数中进行匹配,从而调用at24cxx_detect函数。

编写forces数组

由于.forces属性对应的数据类型为**,即指针的指针,所以此处我们的forces结构体就必须定义为二维数组了,

如图所示,首先我们定义一个force_addr数组,数组中三个元素的意思分别为,

ANY_I2C_BUS:适用于任何总线。

0x60:我们伪造的设备地址。

I2C_CLIENT_END:设备地址结束。

接下来,用unsigned shor类型的指针数组进行封装一下,赋值给.forces属性。

编译加载,可以发现我们的probe函数被正常调用了,说明系统已经强制认识这个设备了。

 

附上驱动程序2:

  1 #include <linux/kernel.h>
  2 #include <linux/init.h>
  3 #include <linux/module.h>
  4 #include <linux/slab.h>
  5 #include <linux/jiffies.h>
  6 #include <linux/mutex.h>
  7 #include <linux/i2c.h>
  8 #include <linux/fs.h>
  9 #include <asm/uaccess.h>
 10 
 11 
 12 static unsigned short ignore[]         = {I2C_CLIENT_END};
 13 static unsigned short normal_addr[] = {0X50, I2C_CLIENT_END};    //设备地址:01010000(0x50) 七位
 14                                     /* 改为0x60的话,由于不存在设备地址为0x60的设备,at24cxx_detect不被调用 */
 15                                     
 16 /*static unsigned short force_addr[2][3]  = { {ANY_I2C_BUS, 0x60, I2C_CLIENT_END}, // 强制调用at24cxx_detect
 17                                             {ANY_I2C_BUS, 0x60, I2C_CLIENT_END},
 18                                             };*/
 19 static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
 20 static unsigned short * forces[] = {force_addr,NULL};
 21 
 22 
 23 static struct i2c_client_address_data addr_data = {
 24     .normal_i2c    = normal_addr,     //要发出地址信号,并且得到ACK信号,才能确定是否存在这个设备
 25     .probe        = ignore,
 26     .ignore        = ignore,
 27     .forces        = forces,    //在probe函数中查找,强制认为存在这个设备,从会调用at24cxx_detect
 28 };
 29 
 30 static int at24cxx_detect(struct  i2c_adapter *adapter, int address, int kind)
 31 {
 32     printk("enter at24cxx_detect \n");
 33     return 0;
 34 }
 35 
 36 //探测时调用函数
 37 static int at24cxx_attach_adapter(struct  i2c_adapter *adapter)
 38 {
 39     return i2c_probe(adapter, &addr_data, at24cxx_detect);
 40 }
 41 
 42 //卸载函数
 43 static int at24cxx_detach_adapter(struct i2c_client *client)
 44 {
 45     printk("enter at24cxx_detach_adapter \n");
 46     return 0;
 47 }
 48 
 49 //定义i2c_driver结构体
 50 static struct i2c_driver at24cxx_i2c_driver = {
 51     .driver = {
 52         .name = "at24cxx",
 53     },
 54     .attach_adapter = at24cxx_attach_adapter,
 55     .detach_client = at24cxx_detach_adapter,
 56 };
 57 
 58 static int at24cxx_init(void)
 59 {
 60     /* 1.分配一个i2c_driver结构体 */
 61     /* 2.设置 */
 62     i2c_add_driver(&at24cxx_i2c_driver);    //注册i2c驱动
 63     
 64     return 0;
 65 }
 66 
 67 void at24cxx_exit(void)
 68 {
 69     i2c_del_driver(&at24cxx_i2c_driver);
 70 }
 71 
 72 module_init(at24cxx_init);
 73 module_exit(at24cxx_exit);
 74 MODULE_LICENSE("GPL");
 75 
 76 
 77 /*
 78 
 79 1.在I2C中总共有两条线,SDA,SCL,分别是负责数据的发送,时钟脉冲线
 80 起始信号,SCL变为低电平,SDA由低电平变为高电平
 81 ------------------------------------------------------------------------------
 82 | 起始位S | 7位设备地址 | R/W | 8B数据 | ACK (注8b数据和ACK可以周而复始) | P |
 83 ------------------------------------------------------------------------------
 84 设备地址最后一位表示的是读还是写
 85 
 86     刚开始前8个clk里SDA由主机驱动,发送从机设备地址,当从机发现设备地址为自己的时,
 87     便会在第9个时钟里SDA由从机驱动,I2C主机释放SDA,由从机驱动SDA
 88 写数据:前8个CLK,SDA由主机驱动
 89         第9个CLK,SDA由从机驱动
 90 写数据:前8个CLK,SDA由从机驱动
 91         第9个CLK,SDA由主机驱动
 92 停止信号,SCL变为高电平,SDA由低电平变为高电平
 93 
 94 2.i2c驱动框架
 95 ----------------------------------------------------------------------
 96 APP:  open,read,write
 97 ----------------------------------------------------------------------
 98 I2c设备驱动程序:drv_open,drv_read,drv_write,     知道数据的含义
 99 ----------------------------------------------------------------------
100 I2c总线驱动程序:①识别②提供读写函数,            知道如何收发数据
101 ----------------------------------------------------------------------
102 
103 drivers/chip/                            //设备驱动程序
104 drivers/i2c/busses/i2c-s3c2410.c        //总线驱动程序
105 drivers/i2c/algos                        //算法
106 ----------------------------------------------------------------------------------------------
107                             BUS
108             i2c_add    /                    \ i2c_add_diver
109        i2c_adapter /                      \    i2c_driver    .id 表示能支持哪些设备
110                   /    插槽(适配器)          \设备驱动程序    .attach_adapter 接到适配器上去
111 ----------------------------------------------------------------------------------------------
112 i2c总线驱动程序:
113 1.分配一个i2c_adapter    插槽
114 2.设置,algo结构体,核心算法,.master_xfer,
115 3.注册i2c_add_adapter     .master_xfer:发I2C信号的函数
116 ----------------------------------------------------------------------------------------------
117 i2c_add_adapter    ①把结构体放入链表
118                 ②从driver链表取出每一项,得到其ID(设备地址)
119                 ③使用master_xfer函数发start信号,发设备地址
120                 ④如果收到ACK则发现了一个设备client
121 ---------------------------------------------------------------------------------------------                        
122 i2c设备驱动程序:
123 i2c_add_driver: ①把I2C_driver放入链表
124                 ②使用从adapter链表取出”适配器“,使用它的mater_xfer函数法start信号,发设备地址(.id里面)
125                 如果能收到ACK信号,则表示发现了一个设备client
126 --------------------------------------------------------------------------------------------
127 
128 写I2C的driver驱动程序:
129 ①分配一个i2c_driver结构体
130 ②设置
131     attach_adapter  //它直接调用i2c_probe(adap,设备地址,发现这个设备后要调用的函数)
132     detach_client    //卸载驱动后,如果之前有能支持的,则调用它来清理
133 ③注册
134 
135 
136 
137 4.测试1th
138 insmod at24cxx.ko   观察输出信息
139 修改normal_addr为0x60,加载观察输出信息。
140 
141 
142 */
at24cxx_2_forces.c

 

三、完善i2c_probe函数里的参数at24cxx_detect函数

在at24cxx_detect函数中,它的主要功能就是构建一个i2c_client结构体

 1 struct i2c_client {
 2     unsigned short flags;
 3     unsigned short addr;
 4     char name[I2C_NAME_SIZE];
 5     struct i2c_adapter *adapter; //定义adapter适配器
 6     struct i2c_driver *driver;
 7     struct device dev;
 8     int irq;
 9     char driver_name[KOBJ_NAME_LEN];
10     struct list_head list;
11     struct completion released;
12 };
13 
14 struct i2c_client代表一个挂载到i2c总线上的i2c从设备,该设备所需要的数据结构,其中包括
15 该i2c从设备所依附的i2c主设备 struct i2c_adapter *adapter
16 该i2c从设备的驱动程序struct i2c_driver *driver
17 作为i2c从设备所通用的成员变量,比如addr, name等
18 该i2c从设备驱动所特有的数据,依附于dev->driver_data下

如图所示。

首先定义一个全局的i2c_client结构体,然后再at24cxx_detect函数中对其进行设置。

61行:分配一个i2c_client

62行:设置设备地址

63行:设置I2C适配器

64行:设置连接i2c_driver结构体

65行:设置client的名字

66行:加载client结构体,当要卸载驱动时,便会调用detach函数,如下图所示:

在卸载函数中,首先卸载client结构体,然后再释放client结构体的内存。

附上驱动程序3:

  1 #include <linux/kernel.h>
  2 #include <linux/init.h>
  3 #include <linux/module.h>
  4 #include <linux/slab.h>
  5 #include <linux/jiffies.h>
  6 #include <linux/mutex.h>
  7 #include <linux/i2c.h>
  8 
  9 
 10 static unsigned short ignore[]         = {I2C_CLIENT_END};
 11 static unsigned short normal_addr[] = {0X50, I2C_CLIENT_END};    //设备地址:01010000(0x50) 七位
 12                                     /* 改为0x60的话,由于不存在设备地址为0x60的设备,at24cxx_detect不被调用 */
 13                                     
 14 /*static unsigned short force_addr[2][3]  = { {ANY_I2C_BUS, 0x60, I2C_CLIENT_END}, // 强制调用at24cxx_detect
 15  
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#推送RocketMQ信息发布时间:2022-07-13
下一篇:
java/php/c#版rsa签名以及java验签实现--转发布时间:2022-07-13
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap