在实现驱动程序之前,我们来想两个问题:
一、问题分析
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
2 #include
3 #include
4 #include
5 #include
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('
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,'%sn',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('
49
50 /* 注册总线设备 */
51 ret = device_register(&my_bus);
52 if(ret)
53 printk('
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
2 #include
3 #include
4 #include
5 #include
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('
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, '%sn', '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('
39 /* 创建属性文件 */
40 if(device_create_file(&my_dev, &dev_attr_dev))
41 printk('
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
2 #include
3 #include
4 #include
5 #include
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('
13 return 0;
14 }
15
16 static int my_remove(struct device *dev){
17 printk('
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, '%sn', '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('
42 /* 创建属性文件 */
43 if(driver_create_file(&my_driver, &driver_attr_drv))
44 printk('
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 的学习了。敬请期待。。