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

at24cxx_3_detach.c

四、创建I2C的字符设备,并且创建字符设备节点

前面我们虽然写好了i2c的最简单驱动,但是我们却无法访问,所以,为了便于我们访问i2c设备,此处我们将i2c设备注册为一个字符设备,以便读写。

创建字符设备大概的流程想必大家已经都很了解,此处不再赘述,就讲一下大概的步骤。

①定义主设备号,以及字符设备的file_operation结构体,为了自动创建设备节点,还需定义一个class结构体,如下图所示:

②实现file_operation结构体中的read、write函数。

③在at24cxx_detect函数中申请字符设备,创建类,然后自动创建设备节点,如下图所示:

④最后自然就是在at24cxx_detach_adapter函数中注销字符设备咯

附上驱动程序4:

  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
 10
 11 static unsigned short ignore[]         = {I2C_CLIENT_END};
 12 static unsigned short normal_addr[] = {0X50, I2C_CLIENT_END};    //设备地址:01010000(0x50) 七位
 13                                     /* 改为0x60的话,由于不存在设备地址为0x60的设备,at24cxx_detect不被调用 */
 14
 15 /*static unsigned short force_addr[2][3]  = { {ANY_I2C_BUS, 0x60, I2C_CLIENT_END}, // 强制调用at24cxx_detect
 16                                             {ANY_I2C_BUS, 0x60, I2C_CLIENT_END},
 17                                             };*/
 18 static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
 19 static unsigned short * forces[] = {force_addr,NULL};
 20
 21
 22 static struct i2c_client_address_data addr_data = {
 23     .normal_i2c    = normal_addr,     //要发出地址信号,并且得到ACK信号,才能确定是否存在这个设备
 24     .probe        = ignore,
 25     .ignore        = ignore,
 26     .forces        = forces,    //在probe函数中查找,强制认为存在这个设备,从会调用at24cxx_detect
 27 };
 28
 29 static struct i2c_driver at24cxx_i2c_driver;
 30
 31
 32 static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size,loff_t * offset)
 33 {
 34     return 0;
 35 }
 36
 37 static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
 38 {
 39     return 0;
 40 }
 41
 42 //定义主设备号
 43 static int major;
 44 //定义字符设备结构体
 45 static struct file_operations at24cxx_fops = {
 46     .owner = THIS_MODULE,
 47     .read = at24cxx_read,
 48     .write = at24cxx_write,
 49 };
 50 //自动创建设备节点
 51 static struct class *cls;
 52
 53 static struct i2c_client *at24cxx_client;
 54
 55 static int at24cxx_detect(struct  i2c_adapter *adapter, int address, int kind)
 56 {
 57     printk("enter at24cxx_detect \n");
 58
 59     /* ④构造一个i2c_client结构体:以后收发数据靠它,里面有.address .adapter  .driver */
 60     at24cxx_client = kzalloc(sizeof(struct i2c_client),GFP_KERNEL);
 61     at24cxx_client->addr     = address;
 62     at24cxx_client->adapter = adapter;
 63     at24cxx_client->driver  = &at24cxx_i2c_driver;
 64     strcpy(at24cxx_client->name, "at24cxx");
 65     i2c_attach_client(at24cxx_client);  //等要卸载驱动时,会调用at24cxx_detach_adapter函数
 66
 67     //申请字符设备主设备号
 68     major = register_chrdev(0,"at24cxx",&at24cxx_fops);
 69     //创建一个类,然后再类下面创建一个设备
 70     cls = class_create(THIS_MODULE,"at24cxx");
 71     //device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); // /dev/at24cxx
 72     class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx");
 73
 74     return 0;
 75 }
 76
 77 //探测时调用函数
 78 static int at24cxx_attach_adapter(struct  i2c_adapter *adapter)
 79 {
 80     return i2c_probe(adapter, &addr_data, at24cxx_detect);
 81 }
 82
 83 //卸载函数
 84 static int at24cxx_detach_adapter(struct i2c_client *client)
 85 {
 86     printk("enter at24cxx_detach_adapter \n");
 87
 88     device_destroy(cls, MKDEV(major, 0));        //销毁设备
 89     class_destroy(cls);                            //销毁类
 90     unregister_chrdev(major, "at24cxx");        //注销字符设备
 91     i2c_detach_client(client);                    //client结构体
 92     kfree(i2c_get_clientdata(client));            //释放client的内存
 93     return 0;
 94 }
 95
 96 //定义i2c_driver结构体
 97 static struct i2c_driver at24cxx_i2c_driver = {
 98     .driver = {
 99         .name = "at24cxx",
100     },
101     .attach_adapter = at24cxx_attach_adapter,
102     .detach_client = at24cxx_detach_adapter,
103 };
104
105 static int at24cxx_init(void)
106 {
107     /* 1.分配一个i2c_driver结构体 */
108     /* 2.设置 */
109     i2c_add_driver(&at24cxx_i2c_driver);    //注册i2c驱动
110
111     return 0;
112 }
113
114 void at24cxx_exit(void)
115 {
116     i2c_del_driver(&at24cxx_i2c_driver);
117 }
118
119 module_init(at24cxx_init);
120 module_exit(at24cxx_exit);
121 MODULE_LICENSE("GPL");
122
123
124 /*
125
126 1.在I2C中总共有两条线,SDA,SCL,分别是负责数据的发送,时钟脉冲线
127 起始信号,SCL变为低电平,SDA由低电平变为高电平
128 ------------------------------------------------------------------------------
129 | 起始位S | 7位设备地址 | R/W | 8B数据 | ACK (注8b数据和ACK可以周而复始) | P |
130 ------------------------------------------------------------------------------
131 设备地址最后一位表示的是读还是写
132
133     刚开始前8个clk里SDA由主机驱动,发送从机设备地址,当从机发现设备地址为自己的时,
134     便会在第9个时钟里SDA由从机驱动,I2C主机释放SDA,由从机驱动SDA
135 写数据:前8个CLK,SDA由主机驱动
136         第9个CLK,SDA由从机驱动
137 写数据:前8个CLK,SDA由从机驱动
138         第9个CLK,SDA由主机驱动
139 停止信号,SCL变为高电平,SDA由低电平变为高电平
140
141 2.i2c驱动框架
142 ----------------------------------------------------------------------
143 APP:  open,read,write
144 ----------------------------------------------------------------------
145 I2c设备驱动程序:drv_open,drv_read,drv_write,     知道数据的含义
146 ----------------------------------------------------------------------
147 I2c总线驱动程序:①识别②提供读写函数,            知道如何收发数据
148 ----------------------------------------------------------------------
149
150 drivers/chip/                            //设备驱动程序
151 drivers/i2c/busses/i2c-s3c2410.c        //总线驱动程序
152 drivers/i2c/algos                        //算法
153 ----------------------------------------------------------------------------------------------
154                             BUS
155             i2c_add    /                    \ i2c_add_diver
156        i2c_adapter /                      \    i2c_driver    .id 表示能支持哪些设备
157                   /    插槽(适配器)          \设备驱动程序    .attach_adapter 接到适配器上去
158 ----------------------------------------------------------------------------------------------
159 i2c总线驱动程序:
160 1.分配一个i2c_adapter    插槽
161 2.设置,algo结构体,核心算法,.master_xfer,
162 3.注册i2c_add_adapter     .master_xfer:发I2C信号的函数
163 ----------------------------------------------------------------------------------------------
164 i2c_add_adapter    ①把结构体放入链表
165                 ②从driver链表取出每一项,得到其ID(设备地址)
166                 ③使用master_xfer函数发start信号,发设备地址
167                 ④如果收到ACK则发现了一个设备client
168 ---------------------------------------------------------------------------------------------
169 i2c设备驱动程序:
170 i2c_add_driver: ①把I2C_driver放入链表
171                 ②使用从adapter链表取出”适配器“,使用它的mater_xfer函数法start信号,发设备地址(.id里面)
172                 如果能收到ACK信号,则表示发现了一个设备client
173 --------------------------------------------------------------------------------------------
174
175 写I2C的driver驱动程序:
176 ①分配一个i2c_driver结构体
177 ②设置
178     attach_adapter  //它直接调用i2c_probe(adap,设备地址,发现这个设备后要调用的函数)
179     detach_client    //卸载驱动后,如果之前有能支持的,则调用它来清理
180 ③注册
181
182
183
184 4.测试1th
185 insmod at24cxx.ko   观察输出信息
186 修改normal_addr为0x60,加载观察输出信息。
187
188
189 */

at24cxx_4_chrdev

五、实现字符设备的读写函数

前面在第四节中我们已经实现了注册i2c的字符设备,接下来最后一步就是实现读写函数了。

1.at24cxx_write(含详细注释)

 1 static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size,loff_t *offset )
 2 {
 3     unsigned char val[2] ;        //定义val用于保存数据
 4     struct i2c_msg msg[1];        //定义一个i2c_msg用于发送msg
 5     int ret;                    //用于保存返回值
 6
 7     /* 写 addr = buf[0]   data = buf[1]*/
 8     if(size != 2)                //如果数据的大小不等于2则退出
 9         return -EINVAL;
10
11     if(copy_from_user(val,buf,2));        //读取应用程序要发送的数据
12
13     //构造I2C消息,数据传输三要素:源,目的,长度
14     msg[0].addr = at24cxx_client->addr;    //目的:从设备地址
15     msg[0].buf  = val;                    //源,目的,长度
16     msg[0].len    = 2;                    //长度:地址+数据=2byte
17     msg[0].flags = 0;                    //0:表示写数据
18
19     //传输消息
20     ret = i2c_transfer(at24cxx_client->adapter, msg, 1);    //发送i2c消息
21
22     if(ret == 1)
23         return size;                    //若是发送成功则返回发送数据的大小
24     else
25         return -EIO;
26 }

2. at24cxx_read(含详细注释)

 1 static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size,loff_t * offset)
 2 {
 3     unsigned char address;            //定义要读取数据的地址
 4     unsigned char data;                //保存数据的内容
 5     struct i2c_msg msg[1];            //定义i2c_msg结构体用于读取数据
 6     int ret;
 7
 8     /* 读 addr = buf[0]   data = buf[1]*/
 9     if(size != 1)                    //如果大小不为1则退出
10         return -EINVAL;
11
12     if(copy_from_user(&address,buf,1));
13
14     //构造I2C消息,数据传输三要素:源,目的,长度]
15     /* 读at24cxx时,要先把要读的存储空间发给它 */
16     msg[0].addr = at24cxx_client->addr;    //目的:从设备地址
17     msg[0].buf  = &address;                //源,
18     msg[0].len    = 1;                    //长度:地址+数据=2byte
19     msg[0].flags = 0;                    //0:表示写数据
20
21     //然后启动读操作
22     msg[1].addr = at24cxx_client->addr;    //源:从设备地址
23     msg[1].buf  = &data;                //目的,
24     msg[1].len    = 1;                    //长度:地址+数据=1byte
25     msg[1].flags = 0;                    //1:表示读数据
26
27     //传输消息
28     ret = i2c_transfer(at24cxx_client->adapter, msg, 2);    //开始从适配器读取消息到msg
29
30     if(ret == 2){
31         if(copy_to_user(&buf[0], &data, 1));
32         return size;
33     }else{
34         return -EIO;
35     }
36 }

3.i2c读写测试函数

 1 /* i2c测试程序 */
 2
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <string.h>
 6 #include <sys/types.h>
 7 #include <sys/stat.h>
 8 #include <fcntl.h>
 9
10 /* i2c_test r addr
11  * i2c_test w addr val
12  */
13 int main(int argc, char **argv)
14 {
15     int fd;
16     unsigned char buf[2];
17
18     if((argc != 3) && (argc != 4)){
19         printf("%s r addr \n",argv[0]);
20         printf("%s w addr val \n",argv[0]);
21         return -1;
22     }
23
24     fd = open("/dev/at24cxx", O_RDWR);
25     if(fd < 0){
26         printf("can‘t open /dev/at24cxx \n");
27         return -1;
28     }
29
30     if(strcmp(argv[1], "r") == 0){            //读
31         buf[0] = srtoul(argv[2], NULL, 0);    //将字符串转换成无符号长整型数
32         read(fd, buff, 1);
33         printf("data: %c, %d, 0x%2x\n",buff[0],buf[0],buf[0]);
34
35     }else if((strcmp(argv[1], "w") == 0) && (argc == 4)) {    //写
36         buf[0] = srtoul(argv[2],NULL, 0);    //读取地址,将字符串转换成无符号长整型数
37         buf[1] = srtoul(argv[3],NULL, 0);    //将字符串转换成无符号长整型数
38         if( write(fd, buf, 2) !== 2)
39             printf("write err, addr = 0x%02x, data = 0x%02x\n", buf[0], buf[1]);
40
41     }else{                                    //出错返回
42         printf("%s r addr \n",argv[0]);
43         printf("%s w addr val \n",argv[0]);
44         return -1;
45     }
46
47     return 0;
48 }
49
50 /*
51 测试:
52
53 i2c_test r 0
54 i2c_test w 0 0x61
55 i2c_test r 0
56
57 */

附上驱动程序5:

  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 struct i2c_driver at24cxx_i2c_driver;
 31 static struct i2c_client *at24cxx_client;
 32
 33 static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size,loff_t * offset)
 34 {
 35     unsigned char address;            //定义要读取数据的地址
 36     unsigned char data;                //保存数据的内容
 37     struct i2c_msg msg[1];            //定义i2c_msg结构体用于读取数据
 38     int ret;
 39
 40     /* 读 addr = buf[0]   data = buf[1]*/
 41     if(size != 1)                    //如果大小不为1则退出
 42         return -EINVAL;
 43
 44     if(copy_from_user(&address,buf,1));
 45
 46     //构造I2C消息,数据传输三要素:源,目的,长度]
 47     /* 读at24cxx时,要先把要读的存储空间发给它 */
 48     msg[0].addr = at24cxx_client->addr;    //目的:从设备地址
 49     msg[0].buf  = &address;                //源,
 50     msg[0].len    = 1;                    //长度:地址+数据=2byte
 51     msg[0].flags = 0;                    //0:表示写数据
 52
 53     //然后启动读操作
 54     msg[1].addr = at24cxx_client->addr;    //源:从设备地址
 55     msg[1].buf  = &data;                //目的,
 56     msg[1].len    = 1;                    //长度:地址+数据=1byte
 57     msg[1].flags = 0;                    //1:表示读数据
 58
 59     //传输消息
 60     ret = i2c_transfer(at24cxx_client->adapter, msg, 2);    //开始从适配器读取消息到msg
 61
 62     if(ret == 2){
 63         if(copy_to_user(&buf[0], &data, 1));
 64         return size;
 65     }else{
 66         return -EIO;
 67     }
 68 }
 69
 70 static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size,loff_t *offset )
 71 {
 72     unsigned char val[2] ;        //定义val用于保存数据
 73     struct i2c_msg msg[1];        //定义一个i2c_msg用于发送msg
 74     int ret;                    //用于保存返回值
 75
 76     /* 写 addr = buf[0]   data = buf[1]*/
 77     if(size != 2)                //如果数据的大小不等于2则退出
 78         return -EINVAL;
 79
 80     if(copy_from_user(val,buf,2));        //读取应用程序要发送的数据
 81
 82     //构造I2C消息,数据传输三要素:源,目的,长度
 83     msg[0].addr = at24cxx_client->addr;    //目的:从设备地址
 84     msg[0].buf  = val;                    //源,目的,长度
 85     msg[0].len    = 2;                    //长度:地址+数据=2byte
 86     msg[0].flags = 0;                    //0:表示写数据
 87
 88     //传输消息
 89     ret = i2c_transfer(at24cxx_client->adapter, msg, 1);    //发送i2c消息
 90
 91     if(ret == 1)
 92         return size;                    //若是发送成功则返回发送数据的大小
 93     else
 94         return -EIO;
 95 }
 96
 97
 98
 99 //定义主设备号
100 static int major;
101 //定义字符设备结构体
102 static struct file_operations at24cxx_fops = {
103     .owner = THIS_MODULE,
104     .read = at24cxx_read,
105     .write = at24cxx_write,
106 };
107 //自动创建设备节点
108 static struct class *cls;
109
110
111
112 static int at24cxx_detect(struct  i2c_adapter *adapter, int address, int kind)
113 {
114     printk("enter at24cxx_detect \n");
115
116     /* ④构造一个i2c_client结构体:以后收发数据靠它,里面有.address .adapter  .driver */
117     at24cxx_client = kzalloc(sizeof(struct i2c_client),GFP_KERNEL);
118     at24cxx_client->addr     = address;
119     at24cxx_client->adapter = adapter;
120     at24cxx_client->driver  = &at24cxx_i2c_driver;
121     strcpy(at24cxx_client->name, "at24cxx");
122     i2c_attach_client(at24cxx_client);  //等要卸载驱动时,会调用at24cxx_detach_adapter函数
123
124     //申请字符设备主设备号
125     major = register_chrdev(0,"at24cxx",&at24cxx_fops);
126     //创建一个类,然后再类下面创建一个设备
127     cls = class_create(THIS_MODULE,"at24cxx");
128     class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); // /dev/at24cxx
129
130     return 0;
131 }
132
133 //探测时调用函数
134 static int at24cxx_attach_adapter(struct  i2c_adapter *adapter)
135 {
136     return i2c_probe(adapter, &addr_data, at24cxx_detect);
137 }
138
139 //卸载函数
140 static int at24cxx_detach_adapter(struct i2c_client *client)
141 {
142     printk("enter at24cxx_detach_adapter \n");
143     if(major){
144         device_destroy(cls, MKDEV(major, 0));        //销毁设备
145         class_destroy(cls);                            //销毁类
146         unregister_chrdev(major, "at24cxx");        //注销字符设备
147     }
148     if(client){
149         i2c_detach_client(client);                    //client结构体
150         kfree(i2c_get_clientdata(client));            //释放client的内存
151     }
152     return 0;
153 }
154
155 //定义i2c_driver结构体
156 static struct i2c_driver at24cxx_i2c_driver = {
157     .driver = {
158         .name = "at24cxx",
159     },
160     .attach_adapter = at24cxx_attach_adapter,
161     .detach_client = at24cxx_detach_adapter,
162 };
163
164 static int at24cxx_init(void)
165 {
166     /* 1.分配一个i2c_driver结构体 */
167     /* 2.设置 */
168     i2c_add_driver(&at24cxx_i2c_driver);    //注册i2c驱动
169
170     return 0;
171 }
172
173 void at24cxx_exit(void)
174 {
175     i2c_del_driver(&at24cxx_i2c_driver);
176 }
177
178 module_init(at24cxx_init);
179 module_exit(at24cxx_exit);
180 MODULE_LICENSE("GPL");
181
182
183 /*
184
185 1.在I2C中总共有两条线,SDA,SCL,分别是负责数据的发送,时钟脉冲线
186 起始信号,SCL变为低电平,SDA由低电平变为高电平
187 ------------------------------------------------------------------------------
188 | 起始位S | 7位设备地址 | R/W | 8B数据 | ACK (注8b数据和ACK可以周而复始) | P |
189 ------------------------------------------------------------------------------
190 设备地址最后一位表示的是读还是写
191
192     刚开始前8个clk里SDA由主机驱动,发送从机设备地址,当从机发现设备地址为自己的时,
193     便会在第9个时钟里SDA由从机驱动,I2C主机释放SDA,由从机驱动SDA
194 写数据:前8个CLK,SDA由主机驱动
195         第9个CLK,SDA由从机驱动
196 写数据:前8个CLK,SDA由从机驱动
197         第9个CLK,SDA由主机驱动
198 停止信号,SCL变为高电平,SDA由低电平变为高电平
199
200 2.i2c驱动框架
201 ----------------------------------------------------------------------
202 APP:  open,read,write
203 ----------------------------------------------------------------------
204 I2c设备驱动程序:drv_open,drv_read,drv_write,     知道数据的含义
205 ----------------------------------------------------------------------
206 I2c总线驱动程序:①识别②提供读写函数,            知道如何收发数据
207 ----------------------------------------------------------------------
208
209 drivers/chip/                            //设备驱动程序
210 drivers/i2c/busses/i2c-s3c2410.c        //总线驱动程序
211 drivers/i2c/algos                        //算法
212 ----------------------------------------------------------------------------------------------
213                             BUS
214             i2c_add    /                    \ i2c_add_diver
215        i2c_adapter /                      \    i2c_driver    .id 表示能支持哪些设备
216                   /    插槽(适配器)          \设备驱动程序    .attach_adapter 接到适配器上去
217 ----------------------------------------------------------------------------------------------
218 i2c总线驱动程序:
219 1.分配一个i2c_adapter    插槽
220 2.设置,algo结构体,核心算法,.master_xfer,
221 3.注册i2c_add_adapter     .master_xfer:发I2C信号的函数
222 ----------------------------------------------------------------------------------------------
223 i2c_add_adapter    ①把结构体放入链表
224                 ②从driver链表取出每一项,得到其ID(设备地址)
225                 ③使用master_xfer函数发start信号,发设备地址
226                 ④如果收到ACK则发现了一个设备client
227 ---------------------------------------------------------------------------------------------
228 i2c设备驱动程序:
229 i2c_add_driver: ①把I2C_driver放入链表
230                 ②使用从adapter链表取出”适配器“,使用它的mater_xfer函数法start信号,发设备地址(.id里面)
231                 如果能收到ACK信号,则表示发现了一个设备client
232 --------------------------------------------------------------------------------------------
233
234 写I2C的driver驱动程序:
235 ①分配一个i2c_driver结构体
236 ②设置
237     attach_adapter  //它直接调用i2c_probe(adap,设备地址,发现这个设备后要调用的函数)
238     detach_client    //卸载驱动后,如果之前有能支持的,则调用它来清理
239 ③注册
240
241
242
243 4.测试1th
244 insmod at24cxx.ko   观察输出信息
245 修改normal_addr为0x60,加载观察输出信息。
246
247
248 */

at24cxx_5_readwrite.c

附上测试程序5:

 1 /* i2c测试程序 */
 2
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <string.h>
 6 #include <sys/types.h>
 7 #include <sys/stat.h>
 8 #include <fcntl.h>
 9
10 /* i2c_test r addr
11  * i2c_test w addr val
12  */
13 int main(int argc, char **argv)
14 {
15     int fd;
16     unsigned char buf[2];
17
18     if((argc != 3) && (argc != 4)){
19         printf("%s r addr \n",argv[0]);
20         printf("%s w addr val \n",argv[0]);
21         return -1;
22     }
23
24     fd = open("/dev/at24cxx", O_RDWR);
25     if(fd < 0){
26         printf("can‘t open /dev/at24cxx \n");
27         return -1;
28     }
29
30     if(strcmp(argv[1], "r") == 0){            //读
31         buf[0] = srtoul(argv[2], NULL, 0);    //将字符串转换成无符号长整型数
32         read(fd, buff, 1);
33         printf("data: %c, %d, 0x%2x\n",buff[0],buf[0],buf[0]);
34
35     }else if((strcmp(argv[1], "w") == 0) && (argc == 4)) {    //写
36         buf[0] = srtoul(argv[2],NULL, 0);    //读取地址,将字符串转换成无符号长整型数
37         buf[1] = srtoul(argv[3],NULL, 0);    //将字符串转换成无符号长整型数
38         if( write(fd, buf, 2) !== 2)
39             printf("write err, addr = 0x%02x, data = 0x%02x\n", buf[0], buf[1]);
40
41     }else{                                    //出错返回
42         printf("%s r addr \n",argv[0]);
43         printf("%s w addr val \n",argv[0]);
44         return -1;
45     }
46
47     return 0;
48 }
49
50 /*
51 测试:
52
53 i2c_test r 0
54 i2c_test w 0 0x61
55 i2c_test r 0
56
57 */

at24cxx_5_test.c

时间: 2024-10-08 05:00:56

20150424 adapter实现i2c驱动程序编写的相关文章

i2c驱动程序全面分析,从adapter驱动程序到设备驱动程序

开发板    :mini2440 内核版本:linux2.6.32.2 驱动程序参考:韦东山老师毕业班i2c 内容概括: 1.adapter client 简介 2.adapter 驱动框架 2.1 设备侧 2.2 驱动侧 2.2.1 probe 函数 2.2.1.1 注册adapter new_device del_device board_info i2c_detect i2c_new_device 3.i2c 设备驱动框架 3.1 i2c_bus_type 3.2 i2c_driver 3

ok6410之led驱动程序编写

led驱动程序编写 本文主要包含三部分,led驱动程序led.c编写,编译驱动程序的makefile的编写,以及使用驱动程序的应用程序led_app的编写 一.led.c编写 1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/cdev.h> 4 #include <linux/fs.h> 5 #include <linux/io.h> 6 #includ

20150409 IMX257 USB鼠标驱动程序编写

20150409 IMX257 USB鼠标驱动程序编写 2015-03-14 Lover雪儿 USB驱动程序包括为USB总线驱动程序以及USB设备驱动程序. USB总线驱动程序的功能是: 1.识别 2.找到匹配的设备驱动程序 3.提供USB读写函数(不知道数据的含义) USB设备驱动程序功能是: 分析USB的数据,上报相应的事件 今天,我们来实现一个USB鼠标的设备驱动程序,让鼠标的左键模拟为键盘的L键,鼠标的右键模拟为键盘的S键,鼠标的中键模拟为键盘的ENTER键,接下来我们先来实现一个简单程

20150429 S3C实现DMA驱动程序编写

20150429 S3C实现DMA驱动程序编写 2015-04-29 Lover雪儿 在IMX257上只有SDMA,SDMA比DMA的功能更加强大,但是为了学习的目的,如果直接学习SDMA,可能会不能消化, 所以,此处,我们从简单到复杂,从S3C2440的DMA驱动程序开始学习,等学懂它之后,我们再进军IMX257的SDMA. 一.一个简单的程序框架 1.定义一些指针 1 static int major = 0; 2 3 #define MEM_CPY_NO_DMA 0 4 #define M

转:Linux网卡驱动程序编写

Linux网卡驱动程序编写 [摘自 LinuxAID] 工作需要写了我们公司一块网卡的Linux驱动程序.经历一个从无到有的过程,深感技术交流的重要.Linux作为挑战微软垄断的强有力武器,日益受到大家的喜爱.真希望她能在中国迅速成长.把程序文档贴出来,希望和大家探讨Linux技术和应用,促进Linux在中国的普及. Linux操作系统网络驱动程序编写 一.Linux系统设备驱动程序概述 1.1 Linux设备驱动程序分类 1.2 编写驱动程序的一些基本概念 二.Linux系统网络设备驱动程序

Linux I2C驱动编写要点

继续上一篇博文没讲完的内容“针对 RepStart 型i2c设备的驱动模型”,其中涉及的内容有:i2c_client 的注册.i2c_driver 的注册.驱动程序的编写. 一.i2c 设备的注册分析:在新版本内核的i2c驱动模型中,支持多种方式来注册 i2c 设备,在Documentation/i2c/instantiating-devices文件中有讲到,在内核中对应的抽象数据结构就是 struct i2c_client. (1)Declare the I2C devices by bus

I2C驱动程序

i2c_add_driver i2c_register_driver driver->driver.bus = &i2c_bus_type; driver_register(&driver->driver); list_for_each_entry(adapter, &adapters, list) { driver->attach_adapter(adapter); i2c_probe(adapter, &addr_data, eeprom_detect

DM9000C网卡驱动程序编写与测试

一般网卡驱动程序厂商会给我们提供一份模板驱动,我们的工作就是需要根据自己的需要更改这个模板驱动 1.DM9000C的硬件连接 硬件连接图如下所示:它接在S3C2440的BANK4内存控制器上,它只占用8个字节的长度,并且是16bit的位宽. 下面介绍一下DM9000C的主要引脚的功能:SD0-SD15位16bit的数据引脚接口:IOR为读使能信号,低电平有效:IOW为写使能信号,低电平有效:CS为片选信号,低电平有效:CMD为数据与索引选择信号,高电平表数据,低电平表索引,它连接到S3C2440

Linux中断处理驱动程序编写

本章节我们一起来探讨一下Linux中的中断: http://blog.csdn.net/gotosola/article/details/7422072 中断处理 http://www.cnblogs.com/tianshuai11/archive/2012/04/20/2477168.html 里边有自动探测中断号: http://blog.csdn.net/en_wang/article/details/6726421 当目标设备有能力告知驱动它要使用的中断号时,自动探测中断号只是意味着探测