16.总线设备驱动模型学习

            总线设备驱动模型学习

  

一、总线概述

  随着技术的不断进步,系统的拓扑结构也越来越复杂,对热插拔,跨平台移植性的要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,从Linux 2.6内核开始提供了全新的设备模型。

  总线:创建一条总线,跟按键一样,首先是描述总线结构,接着是注册总线,注销总线。总线设备,例如usb总线,上面会有很多类型的usb的驱动,例如鼠标、键盘.....等,当我们把之一的usb插上的时候,usb总线会把每个驱动遍历一遍,找到相应的驱动程序执行。

1.1总线描述

  在 Linux 内核中, 总线由 bus_type 结构表示, 定义在 <linux/device.h> 

1 struct bus_type{
2     const char *name; /*总线名称*/
3     int (*match) (struct device *dev, struct
4     device_driver *drv); /*驱动与设备的匹配函数*/
5 6 }

  int (*match)(struct device * dev, struct device_driver * drv)当一个新设备或者新驱动被添加到这个总线时,该函数被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零。

1.2总线注册于注销

总线的注册使用:
  bus_register(struct bus_type *bus)
  若成功,新的总线将被添加进系统,并可在/sys/bus 下看到相应的目录。

总线的注销使用:
  void bus_unregister(struct bus_type *bus)

创建一条总线:

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/kernel.h>
 4 #include <linux/device.h>
 5 MODULE_LICENSE("GPL");
 6 int my_match(struct device *dev,struct device_driver *drv)
 7 {
 8     return 0;
 9 }
10 
11 struct bus_type my_bus_type = {
12     .name = "my_bus",
13     .match = my_match,
14 };
15 
16 EXPORT_SYMBOL(my_bus_type);//变量输出
17 
18 int my_bus_init(void)
19 {
20     int ret;
21     ret = bus_register(&my_bus_type);
22     return ret;
23 }
24 
25 void my_bus_exit(void)
26 {
27     bus_unregister(&my_bus_type);
28 }
29 
30 module_init(my_bus_init);
31 module_exit(my_bus_exit);

控制台运行:

  

二、总线驱动

2.1总线设备驱动描述结构

  在 Linux内核中, 驱动由 device_driver结构表示。

1 struct device_driver{
2 {
3   const char *name; /*驱动名称*/
4   struct bus_type *bus; /*驱动程序所在的总线*/
5   int (*probe) (struct device *dev);
6   …
7 }

  prode函数,当我们有设备加到总线的时候,当设备与总线的某个借口相匹配的时候,系统就会调用prode函数。对我的设备进行相应的初始化。

2.2驱动的注册于注销

驱动的注册:
  int driver_register(struct device_driver *drv)
驱动的注销使用:
  void driver_unregister(struct device_driver *drv)

总线设备驱动程序:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>

MODULE_LICENSE("GPL");
extern struct bus_type my_bus_type;

int my_probe (struct device *dev)
{
    printk(KERN_EMERG"driver found the device it can handle!\n");
     //如果是实际应用的驱动,这里会做很多的硬件初始化操作
    return 0;
}

struct device_driver my_driver = {
    .name = "my_dev",
    .bus = &my_bus_type,   /*驱动是属于哪一条总线的。这里来自总线驱动程序,所以总线的代码
                                要有EXPORT_SYMBOL符号导出标志*/
    .probe = my_probe,
};

int my_driver_init(void)
{
    int ret;
    ret = driver_register(&my_driver);
    return 0;
}

void my_driver_exit(void)
{
    driver_unregister(&my_driver);
}

module_init(my_driver_init);
module_exit(my_driver_exit);

  

  上面的目录/sys/bus/存的是系统总线的各类接口,可以看到了创建的mybus总线,进去,打开驱动drivers的目录,里面有创建的驱动my_dev,驱动挂载成功了。

三、设备

3.1设备描述结构

  在 Linux内核中, 设备由struct device结构表示。

1 struct device{
2 {
3   const char*init_name; /*设备的名字*/
4   struct bus_type *bus; /*设备所在的总线*/
5   …
6 }

3.2设备注销与注册

设备的注册使用如下函数
  int device_register(struct device *dev)
设备的注销使用:
  void device_unregister(struct device *dev)

设备程序如下:

 1 #include <linux/device.h>
 2 #include <linux/module.h>
 3 #include <linux/kernel.h>
 4 #include <linux/init.h>
 5 
 6 MODULE_LICENSE("GPL");
 7 
 8 extern struct bus_type my_bus_type;
 9 
10 struct device my_dev=
11 {
12     .init_name = "my_dev",
13     .bus = &my_bus_type,
14 };
15 
16 int my_device_init(void)
17 {
18     int ret;
19     ret = device_register(&my_dev);
20     return 0;
21 }
22 
23 void my_device_exit(void)
24 {
25     device_unregister(&my_dev);
26 }
27 
28 module_init(my_device_init);
29 module_exit(my_device_exit);

四、编译运行

Makefile文件如下:

obj-m := driver.o bus.o device.o
KDIR :=/home/kernel/kernel/linux-ok6410
all:
    make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
    rm -f *.ko *.o *.order *.symvers *.mod.c *~

将我们的总线程序修改为:

1 int my_match(struct device *dev,struct device_driver *drv)
2 {
3      return !strncmp(dev->init_name,drv->name,strlen(drv->name));   //名字匹配
4 
5 }

运行结果如下:

分析错误提醒:

  出现了空指针:是在strncmp里出现了空指针,这个空指针是init_name;但是我们在我的device.c里已经.init_name="my_dev",为什么还是空指针呢?接下来看内核代码:

首先是找device_register:

  

进入上面的device_add函数:

  

  上面的代码就是把不为空的init_name,赋值给dev_set_name,然后自身的值变为NULL。所以,我们的程序出现空指针的原因。这个值被赋值到了成员kobj.name:

因此我们将bus程序修改为:

1 int my_match(struct device *dev,struct device_driver *drv)
2 {
3     // return !strncmp(dev->init_name,drv->name,strlen(drv->name));   //名字匹配
4     return !strncmp(dev->kobj.name,drv->name,strlen(drv->name));
5 }

编译运行:

  

  当然也可以先执行设备程序在执行驱动程序,但是总线是必须先创建的

posted @ 2016-04-08 13:56  for_learning  阅读(436)  评论(0编辑  收藏  举报