zoukankan      html  css  js  c++  java
  • 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!
    ");
         //如果是实际应用的驱动,这里会做很多的硬件初始化操作
        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 }

    编译运行:

      

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

  • 相关阅读:
    老男孩九期全栈Python之基础一
    为善如挽逆水之舟,才放手便下流
    对自己的表现打分
    anki
    解决推送数据平台
    己所独知,尽是方便;人所不见,尽是自由
    常与权
    为什么会一直刷视频而停不下来
    准备换个房子
    UDEC 1
  • 原文地址:https://www.cnblogs.com/wmx-learn/p/5367815.html
Copyright © 2011-2022 走看看