20150503 imx257下实现I2C驱动的四种方法
2015-05-3 Lover雪儿
时间过得好快,转眼间五一假期就即将结束了,假期期间,大家都潇洒的去玩了,徒留辛辛苦苦的程序员还是窝在宿舍乖乖的敲着代码...
好啦,开开玩笑,辛酸史每家都有一大本,还是要积极的面对生活!!!
今天我们的任务是简单的入门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, %d\n\n",__FILE__,__FUNCTION__,__LINE__); 4 return 0; 5 } 6 //remove函数 7 static int __devexit at24cxx_remove(struct i2c_client *client) 8 { 9 printk("%s, %s, %d\n\n",__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, %d\n\n",__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 %d\n",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 <linux/kernel.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 #include <linux/i2c.h> 5 #include <linux/err.h> 6 #include <linux/slab.h> 7 8 9 //probe函数 10 static int __devinit at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id){ 11 printk("%s, %s, %d\n\n",__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, %d\n\n",__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 <linux/kernel.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 #include <linux/i2c.h> 5 #include <linux/i2c-dev.h> 6 #include <mach/i2c.h> 7 #include <linux/err.h> 8 #include <linux/slab.h> 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, %d\n\n",__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 %d\n",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, %d\n\n",__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 %d\n",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.编译测试
1 #include <linux/kernel.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 #include <linux/i2c.h> 5 #include <linux/err.h> 6 #include <linux/slab.h> 7 8 9 //probe函数 10 static int __devinit at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id){ 11 printk("%s, %s, %d\n\n",__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, %d\n\n",__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 */
1 #include <linux/kernel.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 #include <linux/i2c.h> 5 #include <linux/i2c-dev.h> 6 #include <mach/i2c.h> 7 #include <linux/err.h> 8 #include <linux/slab.h> 9 10 11 static struct i2c_client *at24cxx_client; 12 //一些用于试探是否存在的id 13 static const unsigned short addr_list[] = {0x60,0x50,0x70,NULL}; 14 15 /* 1.分配/设置i2c_driver */ 16 static int at24cxx_dev_init(void) 17 { 18 struct i2c_adapter *i2c_adapt; 19 struct i2c_board_info at24cxx_info; 20 21 printk("%s, %s, %d\n\n",__FILE__,__FUNCTION__,__LINE__); 22 23 //初始化i2c_board_info 结构体 24 memset(&at24cxx_info, 0, sizeof(struct i2c_board_info)); 25 strlcpy(at24cxx_info.type,"at24c08",20); 26 27 //创建i2c适配器 //直接创建设备 28 i2c_adapt = i2c_get_adapter(1); //获得第1个i2c_bus总线 29 if (!i2c_adapt) { 30 printk("can't get i2c adapter %d\n",1); 31 return -EIO; 32 } 33 //在总线下面创建一个i2c_adapt,强制认为设备存在 34 //at24cxx_client = i2c_new_device(i2c_adapt,&at24cxx_info); 35 //对于addr_list能够"已经识别出来的设备"(probed_device),才会去创建,i2c_new_device之前,先测试addr_List中的设备是否存在 36 at24cxx_client = i2c_new_probed_device(i2c_adapt, &at24cxx_info, addr_list); 37 if(!at24cxx_client) 38 return -ENODEV; 39 if(i2c_adapt) 40 i2c_put_adapter(i2c_adapt); //释放i2c_adapt 41 return 0; 42 } 43 44 static void at24cxx_dev_exit(void) 45 { 46 if(at24cxx_client) 47 i2c_unregister_device(at24cxx_client); 48 } 49 50 module_init(at24cxx_dev_init); 51 module_exit(at24cxx_dev_exit); 52 MODULE_LICENSE("GPL"); 53 54 /* 55 1.左边注册一个设备 i2c_client 56 2.右边注册一个驱动 i2c_driver 57 3.比较他们的名字,如果相同,则调用probe函数 58 4.在probe函数里,register_chrdev 59 */
三.创建设备的第三种方法,用户空间创建
相对于前面两种方法来说,这第三种方法却是相对简单,并且不需要写设备程序,只需要加载drv驱动程序即可.
使用/sys/class/i2c-adapter/i2c-1/new_device来创建设备
使用/sys/class/i2c-adapter/i2c-1/delete_device来删除设备
从用户空间创建设备:
创建设备: echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-1/new_device 删除设备 echo 0x50 > /sys/class/i2c-adapter/i2c-1/delete_device |
操作过程如下:
root@EasyARM-iMX257 /# cd /sys/class/i2c-adapter/i2c-1 root@EasyARM-iMX257 /sys/class/i2c-adapter/i2c-1# ls 1-0051 power uevent new_device delete_device i2c-dev subsystem root@EasyARM-iMX257 /sys/class/i2c-adapter/i2c-1# ############################################## 创建设备: echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-1/new_device 删除设备 echo 0x50 > /sys/class/i2c-adapter/i2c-1/delete_device ##############################################
root@EasyARM-iMX257 /sys/class/i2c-adapter/i2c-1# cd /mnt/nfs/module/42_I2C/ 加载驱动: root@EasyARM-iMX257 /mnt/nfs/module/42_I2C# insmod at24cxx_drv.ko root@EasyARM-iMX257 /mnt/nfs/module/42_I2C# 创建设备: 导致i2c_device_probe被调用 root@EasyARM-iMX257 /mnt/nfs/module/42_I2C# echo at24c08 0x50 > /sys/class/i2c-a dapter/i2c-1/new_device i2c-adapter i2c-1: The new_device interface is still experimental and may change in a near future /home/study/nfs_home/module/42_I2C/at24cxx_drv.c, at24cxx_probe, 11
i2c-adapter i2c-1: new_device: Instantiated device at24c08 at 0x50 删除设备: root@EasyARM-iMX257 /mnt/nfs/module/42_I2C# echo 0x50 > /sys/class/i2c-adapter/i 2c-1/delete_device i2c-adapter i2c-1: delete_device: Deleting device at24c08 at 0x50 /home/study/nfs_home/module/42_I2C/at24cxx_drv.c, at24cxx_remove, 18
root@EasyARM-iMX257 /mnt/nfs/module/42_I2C#
############################################## 原因:在drivers/i2c/i2c-core.c中 static struct device_attribute i2c_adapter_attrs[] = { __ATTR(name, S_IRUGO, show_adapter_name, NULL), __ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device), __ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device), { }, }; 当写入new_device 时,导致 i2c_sysfs_new_device被调用,最后知道导致i2c_new_device被调用 当写入delete_device 时,导致 i2c_sysfs_delete_device 被调用,最后知道导致i2c_unregister_device被调用 |
结果图如下所示:
四.第四种方法detech函数实现进一步确认设备存在与否
第四种方法,其实我们曾经些过了,都差不多,
博文地址:http://www.cnblogs.com/lihaiyan/p/4452875.html
也可以把这种方法成为旧方法,它是再2.6内核以前使用的,几乎被淘汰了,编写程序也特别复杂,初学者很难懂,之所以在次提及,因为它还是有独特之处的.
前面三种方法,都有一个前提,必须提前指定i2c控制器,定义在/sys/class/i2c-adapter/中.
但是,如果我的设备有多个控制器,事先并不知道i2c控制器再哪个适配器上,此时我们该如何写驱动程序呢?
答:去class的adapter上去查询
沿用前面第二种方法的驱动程序,我们在at24cxx_drv_2.c中修改.
写程序是我们可以参考/drivers/hwmon/lm77.c.
1.定义以及实现i2c_driver结构体
和前面的有些不同,此处我们的i2c结构体却增加了三个成员.
.class: 由于事先未确定控制器,此参数用于确定设备在哪一类i2c控制器上去找能够支持的设备.
.detect: 在i2c_new_probed_device这个函数进行id设备的过滤后,这个函数用来进一步的检测是否能够找到设备,检测设备的类型.最是在的方法就是使用i2c的读写方法向设备进行读写,若是成功则返回0,接着就调用probe函数.
.address_data: 这个参数用于存放我们要探测的设备地址
1 参考lm90.c,/drivers/hwmon/lm77.c lm90.c 2 static const unsigned short normal_i2c[] = {0x50,0x60,0x70,I2C_CLIENT_END}; 3 4 I2C_CLIENT_INSMOD_1(at24c08); //调用normal_i2c动态创建addr_data 5 6 static struct i2c_driver lm90_driver = { 7 .class = I2C_CLASS_HWMON, //去哪一类I2C控制器查找能支持的设备 8 .driver = { 9 .name = "lm90", 10 }, 11 .probe = lm90_probe, 12 .remove = lm90_remove, 13 .id_table = lm90_id, 14 .detect = lm90_detect, //用这个函数来检测能否找打设备 15 .address_data = &addr_data, //这些设备的地址 16 };
总结一下:去class表示的这一类i2c控制器,用detect函数来确定能否找到addr_list里面的设备,如果能够找到则调用i2c_new_device注册i2c_client,这会和i2c_driver的id_table来比较,如果匹配则调用probe函数.
我们的程序如下所示:
1 static const unsigned short normal_i2c[] = {0x50,0x60,0x70,I2C_CLIENT_END}; 2 3 I2C_CLIENT_INSMOD_1(at24c08); //调用normal_i2c动态创建addr_data 4 5 static int at24cxx_detect(struct i2c_client *client, int kind,struct i2c_board_info *info) 6 { 7 /* 能运行到这里,标识该addr的设备是存在的,但是有些设备单凭地址无法分辨 8 (设备地址可能完全一样,还需进一步读写i2c来分辨是哪一款芯片) 9 detect就是用来进一步分辨芯片设备是哪一款并且设置info->type 10 */ 11 printk("at24cxx_detect: addr = 0x%x\n",client->addr); 12 /* 本来应该发送i2c命令,确定设备确实存在,此处我们简单实现一下 */ 13 /* 进一步确定是哪一款设备 */ 14 15 if(client->addr == 0x50){ 16 strlcpy(info->type, "at24c08", 20); 17 return 0; 18 }else 19 return -ENODEV; 20 } 21 22 //probe函数 23 static int __devinit at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id){ 24 //当确定设备存在,切确定了设备类型之后,就会调用这个函数 25 printk("%s, %s, %d\n\n",__FILE__,__FUNCTION__,__LINE__); 26 return 0; 27 } 28 29 //remove函数 30 static int __devexit at24cxx_remove(struct i2c_client *client) 31 { 32 printk("%s, %s, %d\n\n",__FILE__,__FUNCTION__,__LINE__); 33 return 0; 34 } 35 36 37 static const struct i2c_device_id at24cxx_id_table[] = { 38 {"at24c08", 0}, 39 {} 40 }; 41 42 43 /*static const struct i2c_client_address_data addr_data = { 44 .normal_i2c = normal_i2c, 45 }; 46 */ 47 static struct i2c_driver at24cxx_driver = { 48 .class = I2C_CLASS_HWMON, //去哪一类I2C控制器查找能支持的设备 49 .driver = { 50 .name = "LoverXueEr", 51 .owner = THIS_MODULE, 52 }, 53 .probe = at24cxx_probe, 54 .remove = at24cxx_remove, 55 .id_table = at24cxx_id_table, 56 .detect = at24cxx_detect, //用这个函数来检测能否找到设备 57 .address_data = &addr_data, //这些设备的地址 58 };
2.在init函数中注册结构体
1 static int at24cxx_drv_init(void) 2 { 3 /* 2.注册i2c_driver*/ 4 i2c_add_driver(&at24cxx_driver); 5 return 0; 6 } 7 8 static void at24cxx_drv_exit(void) 9 { 10 i2c_del_driver(&at24cxx_driver); 11 }
3.编译测试
从上图中,我们得知注册驱动的步骤:
①at24cxx_driver放入i2c_bus_type的drv链表,并且从dev链表里取出能够匹配的i2c_client并调用probe,driver_register
②对于每一个适配器,调用__process_new_driver
对于每一个适配器,调用它的函数确定address_list里的设备是否存在
如果存在,再调用detect进一步确定,设置,然后i2c_new_device
前三种都是比较简单的,最后一种很复杂,内核的文档中也告诉我们,尽量使用前三种方法去实现,最后一种方法不是是万不得已尽量不要用.
附上drv驱动程序: at24cxx_drv_4.c
1 #include <linux/kernel.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 #include <linux/i2c.h> 5 #include <linux/err.h> 6 #include <linux/slab.h> 7 #include <linux/init.h> 8 #include <linux/jiffies.h> 9 #include <linux/hwmon.h> 10 #include <linux/hwmon-sysfs.h> 11 #include <linux/mutex.h> 12 13 14 static const unsigned short normal_i2c[] = {0x50,0x60,0x70,I2C_CLIENT_END}; 15 16 I2C_CLIENT_INSMOD_1(at24c08); //调用normal_i2c动态创建addr_data 17 18 static int at24cxx_detect(struct i2c_client *client, int kind,struct i2c_board_info *info) 19 { 20 /* 能运行到这里,标识该addr的设备是存在的,但是有些设备单凭地址无法分辨 21 (设备地址可能完全一样,还需进一步读写i2c来分辨是哪一款芯片) 22 detect就是用来进一步分辨芯片设备是哪一款并且设置info->type 23 */ 24 printk("at24cxx_detect: addr = 0x%x\n",client->addr); 25 /* 本来应该发送i2c命令,确定设备确实存在,此处我们简单实现一下 */ 26 /* 进一步确定是哪一款设备 */ 27 28 if(client->addr == 0x50){ 29 strlcpy(info->type, "at24c08", 20); 30 return 0; 31 }else 32 return -ENODEV; 33 } 34 35 //probe函数 36 static int __devinit at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id){ 37 //当确定设备存在,切确定了设备类型之后,就会调用这个函数 38 printk("%s, %s, %d\n\n",__FILE__,__FUNCTION__,__LINE__); 39 return 0; 40 } 41 42 //remove函数 43 static int __devexit at24cxx_remove(struct i2c_client *client) 44 { 45 printk("%s, %s, %d\n\n",__FILE__,__FUNCTION__,__LINE__); 46 return 0; 47 } 48 49 50 static const struct i2c_device_id at24cxx_id_table[] = { 51 {"at24c08", 0}, 52 {} 53 }; 54 55 56 /*static const struct i2c_client_address_data addr_data = { 57 .normal_i2c = normal_i2c, 58 }; 59 */ 60 static struct i2c_driver at24cxx_driver = { 61 .class = I2C_CLASS_HWMON, //去哪一类I2C控制器查找能支持的设备 62 .driver = { 63 .name = "LoverXueEr", 64 .owner = THIS_MODULE, 65 }, 66 .probe = at24cxx_probe, 67 .remove = at24cxx_remove, 68 .id_table = at24cxx_id_table, 69 .detect = at24cxx_detect, //用这个函数来检测能否找到设备 70 .address_data = &addr_data, //这些设备的地址 71 }; 72 73 74 /* 1.分配/设置i2c_driver */ 75 76 static int at24cxx_drv_init(void) 77 { 78 /* 2.注册i2c_driver*/ 79 i2c_add_driver(&at24cxx_driver); 80 return 0; 81 } 82 83 static void at24cxx_drv_exit(void) 84 { 85 i2c_del_driver(&at24cxx_driver); 86 } 87 88 module_init(at24cxx_drv_init); 89 module_exit(at24cxx_drv_exit); 90 MODULE_LICENSE("GPL"); 91 92 /* 93 1.左边注册一个设备 i2c_client 94 2.右边注册一个驱动 i2c_driver 95 3.比较他们的名字,如果相同,则调用probe函数 96 4.在probe函数里,register_chrdev 97 */
附上dev驱动程序: at24cxx_dev_4.c
1 #include <linux/kernel.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 #include <linux/i2c.h> 5 #include <linux/i2c-dev.h> 6 #include <mach/i2c.h> 7 #include <linux/err.h> 8 #include <linux/slab.h> 9 10 11 static struct i2c_client *at24cxx_client; 12 //一些用于试探是否存在的id 13 static const unsigned short addr_list[] = {0x60,0x50,0x70,NULL}; 14 15 /* 1.分配/设置i2c_driver */ 16 static int at24cxx_dev_init(void) 17 { 18 struct i2c_adapter *i2c_adapt; 19 struct i2c_board_info at24cxx_info; 20 21 printk("%s, %s, %d\n\n",__FILE__,__FUNCTION__,__LINE__); 22 23 //初始化i2c_board_info 结构体 24 memset(&at24cxx_info, 0, sizeof(struct i2c_board_info)); 25 strlcpy(at24cxx_info.type,"at24c08",20); 26 27 //创建i2c适配器 //直接创建设备 28 i2c_adapt = i2c_get_adapter(1); //获得第1个i2c_bus总线 看 /sys/class/i2c-adapter 29 if (!i2c_adapt) { 30 printk("can't get i2c adapter %d\n",1); 31 return -EIO; 32 } 33 //在总线下面创建一个i2c_adapt,强制认为设备存在 34 //at24cxx_client = i2c_new_device(i2c_adapt,&at24cxx_info); 35 //对于addr_list能够"已经识别出来的设备"(probed_device),才会去创建,i2c_new_device之前,先测试addr_List中的设备是否存在 36 at24cxx_client = i2c_new_probed_device(i2c_adapt, &at24cxx_info, addr_list); 37 if(!at24cxx_client) 38 return -ENODEV; 39 if(i2c_adapt) 40 i2c_put_adapter(i2c_adapt); //释放i2c_adapt 41 return 0; 42 } 43 44 static void at24cxx_dev_exit(void) 45 { 46 if(at24cxx_client) 47 i2c_unregister_device(at24cxx_client); 48 } 49 50 module_init(at24cxx_dev_init); 51 module_exit(at24cxx_dev_exit); 52 MODULE_LICENSE("GPL"); 53 54 /* 55 1.左边注册一个设备 i2c_client 56 2.右边注册一个驱动 i2c_driver 57 3.比较他们的名字,如果相同,则调用probe函数 58 4.在probe函数里,register_chrdev 59 */