今天我们的任务是简单的入门linux内核下i2c设备驱动分离的四种写法.
一.一个简单的i2c驱动
和以前的驱动程序不同,i2c驱动分为drv驱动和dev设备驱动两个文件,不懂的可以参考我以前写的<20150313 驱动模块分离概念>以及总线设备驱动模型这些博文
地址:http://www.cnblogs.com/lihaiyan/p/4336165.html
1.首先是drv驱动的编写at24cxx_drv_1.c:
在drv驱动中,其实很简单,就是实现一个i2c_driver结构体,然后在init函数中注册i2c_driver结构体,最后自然是在exit函数中卸载i2c_driver结构体.
①定义i2c_driver结构体以及实现相应的函数
1 //probe函数
2 static int __devinit at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id){
3 printk('%s, %s, %dnn',__FILE__,__FUNCTION__,__LINE__);
4 return 0;
5 }
6 //remove函数
7 static int __devexit at24cxx_remove(struct i2c_client *client)
8 {
9 printk('%s, %s, %dnn',__FILE__,__FUNCTION__,__LINE__);
10 return 0;
11 }
12 static const struct i2c_device_id at24cxx_id_table[] = {
13 {'at24c08', 0}, //记录了设备的名字,用于去顶后面的驱动是否匹配
14 {}
15 };
16 static struct i2c_driver at24cxx_driver = {
17 .driver = {
18 .name = 'LoverXueEr',
19 .owner = THIS_MODULE,
20 },
21 .probe = at24cxx_probe, //探测函数
22 .remove = at24cxx_remove, //卸载函数
23 .id_table = at24cxx_id_table,
24 };
可以发现,在i2c_driver结构体中总共实现了探测函数probe,用于探测匹配的设备,id_table定义了设备的名字用于匹配合适的驱动.
②在init和exit对i2c_driver分别注册和卸载
1 static int at24cxx_drv_init(void)
2 {
3 /* 2.注册i2c_driver*/
4 i2c_add_driver(&at24cxx_driver);
5 return 0;
6 }
7 static void at24cxx_drv_exit(void)
8 {
9 i2c_del_driver(&at24cxx_driver);
10 }
2.设备驱动程序编写at24cxx_dev_1.c
在设备驱动中主要是定义了一些设备的具体的信息,比如设备地址啊等等的信息.
在设备驱动中,主要就是I2C控制器(也称适配器)的使用了.
在init函数中首先创建一个i2c_adapter控制器结构体,
接着通过格泰i2c_get_adapter获取内核存在的i2c控制器号,这个在/sys/class/i2c-adapter下可以查看,
接着使用i2c_new_device直接创建一个设备,不管设备存在与否,该函数都会强制认为该设备存在.
而如果使用i2c_new_probed_device的话:它和前面不同的是,它一定要对于能够'已经识别出来的设备'(probed_device),才会去创建,否则无法创建.(见下面方法二)
理论描述总是太抽象了,我们来看看程序就懂了.
1 //单板结构体,用于存放设备的硬件信息
2 static struct i2c_board_info at24cxx_info = {
3 I2C_BOARD_INFO('at24c08',0x50), //注意这个名字 1010000
4 };
5 static struct i2c_client *at24cxx_client;
6
7 /* 1.分配/设置i2c_driver */
8 static int at24cxx_dev_init(void)
9 {
10 struct i2c_adapter *i2c_adapt;
11 printk('%s, %s, %dnn',__FILE__,__FUNCTION__,__LINE__);
12 //创建i2c适配器
13 //直接创建设备
14 i2c_adapt = i2c_get_adapter(1); //获得第0个适配器
15 if (!i2c_adapt) {
16 printk('can't get i2c adapter %dn',1);
17 return -EIO;
18 }
19 at24cxx_client = i2c_new_device(i2c_adapt,&at24cxx_info); //在总线下面创建一个i2c_adapt,强制认为设备存在
20 //i2c_new_probed_device: 对于能够'已经识别出来的设备'(probed_device),才会去创建
21 i2c_put_adapter(i2c_adapt); //释放i2c_adapt
22 return 0;
23 }
24
25 static void at24cxx_dev_exit(void)
26 {
27 if(at24cxx_client)
28 i2c_unregister_device(at24cxx_client);
29 }
3.编译测试
结果如图所示:
附上drv驱动程序: at24cxx_drv_1.c
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7
8
9 //probe函数
10 static int __devinit at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id){
11 printk('%s, %s, %dnn',__FILE__,__FUNCTION__,__LINE__);
12 return 0;
13 }
14
15 //remove函数
16 static int __devexit at24cxx_remove(struct i2c_client *client)
17 {
18 printk('%s, %s, %dnn',__FILE__,__FUNCTION__,__LINE__);
19 return 0;
20 }
21
22
23 static const struct i2c_device_id at24cxx_id_table[] = {
24 {'at24c08', 0},
25 {}
26 };
27
28 static struct i2c_driver at24cxx_driver = {
29 .driver = {
30 .name = 'LoverXueEr',
31 .owner = THIS_MODULE,
32 },
33 .probe = at24cxx_probe,
34 .remove = at24cxx_remove,
35 .id_table = at24cxx_id_table,
36 };
37
38
39 /* 1.分配/设置i2c_driver */
40
41 static int at24cxx_drv_init(void)
42 {
43 /* 2.注册i2c_driver*/
44 i2c_add_driver(&at24cxx_driver);
45 return 0;
46 }
47
48 static void at24cxx_drv_exit(void)
49 {
50 i2c_del_driver(&at24cxx_driver);
51 }
52
53 module_init(at24cxx_drv_init);
54 module_exit(at24cxx_drv_exit);
55 MODULE_LICENSE('GPL');
56
57 /*
58 1.左边注册一个设备 i2c_client
59 2.右边注册一个驱动 i2c_driver
60 3.比较他们的名字,如果相同,则调用probe函数
61 4.在probe函数里,register_chrdev
62 */
附上dev驱动程序: at24cxx_dev_1.c
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9
10 //单板结构体,用于存放设备的硬件信息
11 static struct i2c_board_info at24cxx_info = {
12 I2C_BOARD_INFO('at24c08',0x50), //注意这个名字 1010000
13 };
14 static struct i2c_client *at24cxx_client;
15
16 /* 1.分配/设置i2c_driver */
17 static int at24cxx_dev_init(void)
18 {
19 struct i2c_adapter *i2c_adapt;
20 printk('%s, %s, %dnn',__FILE__,__FUNCTION__,__LINE__);
21 //创建i2c适配器
22 //直接创建设备
23 i2c_adapt = i2c_get_adapter(1); //获得第0个适配器
24 if (!i2c_adapt) {
25 printk('can't get i2c adapter %dn',1);
26 return -EIO;
27 }
28 at24cxx_client = i2c_new_device(i2c_adapt,&at24cxx_info); //在总线下面创建一个i2c_adapt,强制认为设备存在
29 //i2c_new_probed_device: 对于能够'已经识别出来的设备'(probed_device),才会去创建
30 i2c_put_adapter(i2c_adapt); //释放i2c_adapt
31 return 0;
32 }
33
34 static void at24cxx_dev_exit(void)
35 {
36 if(at24cxx_client)
37 i2c_unregister_device(at24cxx_client);
38 }
39
40 module_init(at24cxx_dev_init);
41 module_exit(at24cxx_dev_exit);
42 MODULE_LICENSE('GPL');
43
44 /*
45 1.左边注册一个设备 i2c_client
46 2.右边注册一个驱动 i2c_driver
47 3.比较他们的名字,如果相同,则调用probe函数
48 4.在probe函数里,register_chrdev
49 */
二.创建已经能被识别的设备
前面我们已经涉及到了,和i2c_new_device不同,使用i2c_new_probed_device的话,它会线查看设备是否存在,如果存在的话,才会创建设备.现在我们第二种方法就是使用它来实现.
再前面的程序基础上修改at24cxx_dev_1.c设备驱动程序.drv驱动不用修改.
1.定义一些用于探测的id数组
前面我们说了,它会探测id的设备是否存在,所以,意味着我们这里可以有多个id,并且id号也可以是不存在的,为了保存这些id,自然就需要一个数组来装咯.
程序如下所示:
1 static struct i2c_client *at24cxx_client;
2 //一些用于试探是否存在的id
3 static const unsigned short addr_list[] = {0x60,0x50,0x70,NULL};
2.在init函数中
和前面第一种方法不同的是,
①我们此处是在init函数中动态的创建i2c_board_info结构体.
②接着就是初始化i2c_board_info结构体,分配设备的名字用于匹配合适的总线.
③创建适配器,获取适配器,和前面一样
④使用i2c_new_probed_device来创建设备,再i2c_new_probed_device中,首先会遍历id数组,线测试id设备是否存在,接着就是调用i2c_new_device来创建设备.
⑤动态是否i2c_adapt控制器.
程序如下:
1 /* 1.分配/设置i2c_driver */
2 static int at24cxx_dev_init(void)
3 {
4 struct i2c_adapter *i2c_adapt;
5 struct i2c_board_info at24cxx_info;
6
7 printk('%s, %s, %dnn',__FILE__,__FUNCTION__,__LINE__);
8
9 //初始化i2c_board_info 结构体
10 memset(&at24cxx_info, 0, sizeof(struct i2c_board_info));
11 strlcpy(at24cxx_info.type,'at24c08',20);
12
13 //创建i2c适配器 //直接创建设备
14 i2c_adapt = i2c_get_adapter(1); //获得第1个i2c_bus总线
15 if (!i2c_adapt) {
16 printk('can't get i2c adapter %dn',1);
17 return -EIO;
18 }
19 //在总线下面创建一个i2c_adapt,强制认为设备存在
20 //at24cxx_client = i2c_new_device(i2c_adapt,&at24cxx_info);
21 //对于addr_list能够'已经识别出来的设备'(probed_device),才会去创建,i2c_new_device之前,先测试addr_List中的设备是否存在
22 at24cxx_client = i2c_new_probed_device(i2c_adapt, &at24cxx_info, addr_list);
23 if(!at24cxx_client)
24 return -ENODEV;
25 if(i2c_adapt)
26 i2c_put_adapter(i2c_adapt); //释放i2c_adapt
27 return 0;
28 }
29
30 static void at24cxx_dev_exit(void)
31 {
32 if(at24cxx_client)
33 i2c_unregister_device(at24cxx_client);
34 }
3.编译测试
附上drv驱动程序: at24cxx_drv_2.c
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7
8
9 //probe函数
10 static int __devinit at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id){