20150226 IMX257 总线设备驱动模型编程之驱动篇
2015-02-26 11:42 李海沿
前面我们已经实现了 总线和设备 的驱动程序,接下来我们的任务就是 实现 驱动 了
地址:http://www.cnblogs.com/lihaiyan/p/4301079.html
http://www.cnblogs.com/lihaiyan/p/4301072.html
在实现驱动程序之前,我们来想两个问题:
一、问题分析
1.什么时候驱动程序会在总线上找它可以处理的设备?
在driver_register(&my_driver),驱动注册时,驱动程序会在总线上找它可以处理的设备。
2.为什么说这个驱动可以处理相应的设备?
总线来判断这个驱动是否可以处理相应的设备,在总线中有.match = my_match ,当驱动在总线上找到了设备时,.match 函数就是用来判断这个驱动是否可以处理设备,判断的原则就是,判断设备的dev->bus_id和驱动的driver->name 是否相等,如果相等,则表明这个驱动是可以处理这个设备的。 此时就说明驱动找到了设备,接着,驱动程序就会调用probe这个函数,这就是我们所说的总线设备驱动模型,三者工作作用。
加载总线之后,不管是先加载驱动或者先加载设备都可以,如果先加载驱动的话,在注册设备时就会在总线上寻找驱动,如果先加载设备时,当注册驱动程序时,驱动程序会在总线中寻找有没有相应的设备。
二、程序分析
1.包含总线
和前面的设备程序一样,先包含总线
2.定义驱动结构体
注意 struct 的成员 .name ,因为探测驱动与设备是否匹配就是看这个名字
my_probe 和my_remove 就是分别当驱动程序 和 设备 关联 或者 不关联时会调用的函数
3.定义属性文件的结构体
关于 static DRIVER_ATTR(drv,S_IRUGO,mydriver_show,NULL);这种宏,此处就不再废话了,不懂的可以看linux源码或者前面我们bus篇中的讲解
4.在init函数中 注册驱动 创建属性文件
可以发现又是和前面的一样,不再废话了。
5. 在exit函数中移除驱动
三、编译测试
编译,成功生成 mybus.ko mydev.o mydrv.ko:
加载
方案一 先加载驱动后加载设备 加载顺序 mybus.ko mydrv.ko mydev.ko
可以发现,一旦我们加载设备,便打印出 驱动中的my_probe的代码:告诉我们驱动找到了设备
移除时,发现打印出了驱动中my_remove函数的代码Driver found device unpluged ! 如图所示:
同样我们还发现,mybus已经有俩个使用了 分别是 mydev 和 mydrv
下面我们来试试方案二,看看结果怎么样
方案二 先加载设备后加载驱动 加载顺序 mybus.ko mydev.ko mydrv.ko
可以发现,结果一样,一旦我们加载驱动,便打印出 驱动中的my_probe的代码:告诉我们驱动找到了设备
这里更加证实了我们前面问题二中的答案
下面我们进入 /sys/bus/my_bus/drivers/my_dev 看看下面有什么文件
附上mybus.c 驱动程序
1 #include <linux/device.h> 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 #include <linux/init.h> 5 #include <linux/string.h> 6 7 8 static char *Version = "$LoverXueEr : 1.0 $"; 9 10 //检测驱动是否匹配设备,dev->bus_id 和 driver->name相等的 11 static int my_match(struct device *dev ,struct device_driver *driver){ 12 return !strncmp(dev_name(dev),driver->name,strlen(driver->name)); 13 } 14 15 static void my_bus_release(struct device *dev){ 16 printk("<0>my bus release "); 17 } 18 19 //设置设备的名字 dev_set_name(&dev,"name"); 20 struct device my_bus = { 21 .init_name = "my_bus0", 22 .release = my_bus_release, 23 }; 24 25 struct bus_type my_bus_type = { 26 .name = "my_bus", 27 .match = my_match, 28 }; 29 EXPORT_SYMBOL(my_bus); //导出符号 30 EXPORT_SYMBOL(my_bus_type); 31 32 //显示总线版本号 33 static ssize_t show_bus_version(struct bus_type *bus,char *buf){ 34 return snprintf(buf,PAGE_SIZE,"%s ",Version); 35 } 36 37 //产生后面的 bus_attr_version 结构体 38 static BUS_ATTR(version,S_IRUGO, show_bus_version, NULL); 39 40 static int __init my_bus_init(void){ 41 int ret; 42 /* 注册总线 */ 43 ret = bus_register(&my_bus_type); 44 if(ret) 45 return ret; 46 /* 创建属性文件 */ 47 if(bus_create_file(&my_bus_type, &bus_attr_version)) 48 printk("<0>Fail to create version attribute! "); 49 50 /* 注册总线设备 */ 51 ret = device_register(&my_bus); 52 if(ret) 53 printk("<0>Fail to register device: my_bus"); 54 return ret; 55 } 56 57 static void my_bus_exit(void){ 58 bus_unregister(&my_bus_type); 59 device_unregister(&my_bus); 60 } 61 62 module_init(my_bus_init); 63 module_exit(my_bus_exit); 64 65 66 MODULE_AUTHOR("Lover雪儿"); 67 MODULE_LICENSE("GPL");
附上mydev.c 驱动程序
1 #include <linux/device.h> 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 #include <linux/init.h> 5 #include <linux/string.h> 6 7 //包含总线 8 extern struct device my_bus; 9 extern struct bus_type my_bus_type; 10 11 static void my_dev_release(struct device *dev){ 12 printk("<0>my_dev release ! "); 13 } 14 15 //设置设备的名字 dev_set_name(&dev,"name"); 16 struct device my_dev = { 17 .bus = &my_bus_type, 18 .parent = &my_bus, //父目录为my_bus 19 .release = my_dev_release, 20 }; 21 22 ssize_t mydev_show(struct device *dev,struct device_attribute *attr,char *buf){ 23 return sprintf(buf, "%s ", "This is my device"); 24 } 25 26 //产生后面的 bus_attr_version 结构体 27 static DEVICE_ATTR(dev,S_IRUGO,mydev_show,NULL); 28 29 static int __init my_dev_init(void){ 30 int ret = 0; 31 32 /* 初始化设备 以后看驱动与设备是否匹配就看这个名字 */ 33 dev_set_name(&my_dev,"my_dev"); 34 35 /* 注册设备 */ 36 ret = device_register(&my_dev); 37 if(ret) 38 printk("<0>Fail to register device: my_dev"); 39 /* 创建属性文件 */ 40 if(device_create_file(&my_dev, &dev_attr_dev)) 41 printk("<0>Fail to create device file: my_dev"); 42 43 return ret; 44 } 45 46 static void my_dev_exit(void){ 47 device_remove_file(&my_dev, &dev_attr_dev); 48 device_unregister(&my_dev); 49 } 50 51 module_init(my_dev_init); 52 module_exit(my_dev_exit); 53 54 55 MODULE_AUTHOR("Lover雪儿"); 56 MODULE_LICENSE("GPL");
附上mydrv.c 驱动程序
1 #include <linux/device.h> 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 #include <linux/init.h> 5 #include <linux/string.h> 6 7 //包含总线 8 extern struct device my_bus; 9 extern struct bus_type my_bus_type; 10 11 static int my_probe(struct device *dev){ 12 printk("<0>Driver found device which my driver can handle ! "); 13 return 0; 14 } 15 16 static int my_remove(struct device *dev){ 17 printk("<0>Driver found device unpluged ! "); 18 return 0; 19 } 20 // 驱动结构体 21 struct device_driver my_driver = { 22 .name = "my_dev", //此处声明了 本驱动程序可以处理的设备 名字 23 .bus = &my_bus_type, 24 .probe = my_probe, 25 .remove = my_remove, 26 }; 27 28 ssize_t mydriver_show(struct device_driver *driver,char *buf){ 29 return sprintf(buf, "%s ", "This is my driver"); 30 } 31 32 //产生后面的 driver_attr_drv 结构体 33 static DRIVER_ATTR(drv,S_IRUGO,mydriver_show,NULL); 34 35 static int __init my_driver_init(void){ 36 int ret = 0; 37 38 /* 注册驱动 */ 39 ret = driver_register(&my_driver); 40 if(ret) 41 printk("<0>Fail to register driver: my_driver"); 42 /* 创建属性文件 */ 43 if(driver_create_file(&my_driver, &driver_attr_drv)) 44 printk("<0>Fail to create driver file: my_drv"); 45 46 return ret; 47 } 48 49 static void my_driver_exit(void){ 50 driver_remove_file(&my_driver, &driver_attr_drv); 51 driver_unregister(&my_driver); 52 } 53 54 module_init(my_driver_init); 55 module_exit(my_driver_exit); 56 57 58 MODULE_AUTHOR("Lover雪儿"); 59 MODULE_LICENSE("GPL");
附上makefile程序
1 ifeq ($(KERNELRELEASE),) 2 KERNELDIR ?= /home/study/system/linux-2.6.31 3 PWD := $(shell pwd) 4 modules: 5 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 6 modules_install: 7 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install 8 clean: 9 rm -rf *.o *~ core .depend *.cmd *.ko *.mod.c .tmp_versions *.markers *.order *.symvers 10 11 else 12 obj-m := mybus.o mydev.o mydrv.o 13 endif
好啦,至此,我们的总线-设备-驱动 模型已经实现了,但是并不能说我们已经懂了,这里再次废话一下,很多原理知识虽然乏味,还是要看,光会写程序是没用的,还需要懂为什么。
我也是处于学习阶段,这些都是我的一些简单的经验,能帮助大家快速入门,剩下的还是。。。入门了就会相对跟简单了。
很多人说学习linux驱动很难,那是因为对未知的恐惧,说简单点,就是那么几个结构体,算法和API的使用罢了,不说了,说多了就是欠揍的下场,
任重而道远,加油吧!!!
下面我们的任务就是实现 平台设备驱动程序 platform 的学习了。敬请期待。。。