zoukankan      html  css  js  c++  java
  • 总线设备驱动模型

    总线设备驱动模型(举足轻重:这个模型运用到众多驱动中)
    1.总线模型概述
    如usb总线,总线上有鼠标驱动,网卡驱动,键盘驱动。现在往总
    线上插入一个设备,这个设备是usb网卡。首先总线会感知到有一
    个设备插上来了,那么这个设备到底使用的是哪一个设备,那么这
    个时候总线就会将总线上挂载的驱动一一和这个设备来匹配。匹配
    的规则,不同的设备匹配的规则是不一样的。usb总线就会将控制
    权交给网卡。接下来就会由网卡驱动来处理这个设备。拔出设备与
    这个过程一样。

    2.总线
    2.1描述结构<linux/device.h>
    struct bus_type {
    const char *name;//总线名称
    struct bus_attribute *bus_attrs;
    struct device_attribute *dev_attrs;
    struct driver_attribute *drv_attrs;
    int (*match)(struct device *dev, struct device_driver
    *drv);//驱动与设备的匹配函数,*match:函数指针
    int (*uevent)(struct device *dev, struct kobj_uevent_env
    *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);
    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);
    const struct dev_pm_ops *pm;
    struct iommu_ops *iommu_ops;
    struct subsys_private *p;

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

    2.3注销
    void bus_unregister(struct bus_type *bus)//总线注销。

    接下我们来创建一条总线:
    touch bus.c
    chmod 777 bus.c
    #include<linux/init.h>
    #include<linux/module.h>
    #include<linux/kernel.h>
    #include<linux/device.h>

    int my_match(struct device *dev, struct device_driver
    *drv)
    {
    return !strncmp(dev->kobj.name,drv->name,strlen(drv-
    >name));//返回0表示匹配不上。
    }


    struct bus_type my_bus_type =
    {
    .name = "my_bus",
    .match = my_match,

    }

    EXPORT_SYMBOL(my_bus_type);//导出符号,那么在另外一个模块
    中才能使用。


    int my_bus_init()
    {
    int ret;
    ret = bus_register(&my_bus_type);//总线注册

    return ret;

    }

    void my_bus_exit()
    {
    bus_unregister(&my_bus_type);

    }

    module_init(my_bus_init);
    module_exit(my_bus_exit);
    MODULE_LICEDSE("GPL");
    安装模块的时候会提示:bus_register but_unregister未定义符
    号。因为模块没有遵循GPL协议。
    cd /sys/bus/在里面可以看到linux系统所支持的总线。
    接下来要做的就是往总线中添加驱动

    3.驱动
    3.1.描述结构
    struct device_driver {
    const char *name;//驱动名称
    struct bus_type *bus;//驱动程序所在的总线
    struct module *owner;
    const char *mod_name; /* used for
    built-in modules */
    bool suppress_bind_attrs; /* disables bind/unbind
    via sysfs */
    const struct of_device_id *of_match_table;
    int (*probe) (struct device *dev);//由总线来调用,当找到驱
    动程序能够去处理设备的时候被调用,用来初始化设备
    int (*remove) (struct device *dev);
    void (*shutdown) (struct device *dev);
    int (*suspend) (struct device *dev, pm_message_t state);
    int (*resume) (struct device *dev);
    const struct attribute_group **groups;
    const struct dev_pm_ops *pm;
    struct driver_private *p;
    }

    3.2.注册
    int driver_register(struct device_driver *drv)//驱动注册

    3.3.注销
    void driver_unregister(struct device_driver *drv)//驱动注
    销。
    下面在总线上挂载驱动:
    touch driver.c
    chmod 777 driver.c
    vim makefile +drivre.o
    vim driver.c
    3include<linux/init.h>
    #include<linux/module.h>
    #inlclude<linux/kernel.h>
    #include<linux/device.h>

    MODULE_LICEDSE("GPL");
    extern struct bus_type my_bus_type;//使用导出符号

    int (*probe) (struct device *dev)
    {
    printk("driver found the device is can handle! ");
    return 0;

    }

    //初始化驱动
    struct device_driver my_driver = {
    .name = "my_dev",//非常重要
    .bus = &my_bus_type,//因为这里引用了外部符号,所以要取地
    址。
    .probe = my_probe,

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

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

    module_init(my_driver_init);
    module_exit(my_driver_exit);

    insmod driver.ko
    发现符号没有定义,因为之前加载的模块没有导出符号,需要重新
    安装两个模块。
    insmod bus.ko
    insmod driver.ko
    cd /sys/bus/my_bus:发现有两个目录,一个是device ,另一个是
    driver,分别保存了总线上的设备和驱动。
    cd driver

    4.设备
    4.1.描述结构
    struct device {
    struct device *parent;
    struct device_private *p;
    struct kobject kobj;
    const char *init_name; /* initial name of the
    device */设备的名字
    const struct device_type *type;
    struct mutex mutex; /* mutex to synchronize
    struct bus_type *bus;/* type of bus 设备所在的总线
    device is on */
    struct device_driver *driver; /* which driver has
    device */
    void *platform_data; /*
    }
    4.2.设备注册
    nt device_register(struct device *dev)

    4.3.设备注销
    void device_unregister(struct device *dev)
    下面在总线上挂载一个设备:
    touch device.c
    chmod 777 device.c
    vim Makefile +device.o

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

    MODULE_LICENSE("GPL");

    extern struct bus_type my_bus_type;

    struct device my_device= {
    .init_name = "my_dev",//与设备驱动的名字一样
    .bus = my_bus_type,
    };


    int my_device_init()
    {
    int ret;
    ret = device_register(&my_device);
    return ret;

    }

    void my_device_exit()
    {
    device_unregister(&my_device);
    }
    module_init(my_device_init);
    module_exit(my_device_exit);

    驱动与设备进行匹配,如果硬件,硬件会有一个对应的id,如果是
    虚拟的设备,那么就会给设备定义一个名字,然后再给驱动定义一
    个名字,如果设备的名字与驱动的名字相同,那么设备和驱动就能
    够匹配了。
    如果设备加载成功,那么在probe函数中就会打印出一条信息。
    但事与愿违,出现了内核异常,出现了null pointer
    在这里需要学会看反馈的错误信息,这里的错误信息是反着来的最
    上面的是出错的地方。
    最终发现init_name出现了空指针?我们找到内核代码在
    device_add这个函数中我们发现这么几行代码:
    if (dev->init_name) {
    dev_set_name(dev, "%s", dev->init_name);
    dev->init_name = NULL;
    }
    这里直接把init_name指针清空了。实际上dev的name被赋值到了
    dev->kobj.name里面来了。
    还有一点内核异常之后通常都是需要重新启动的。
    insmod bus.ko
    insmod driver.ko
    insmod device.ko
    结果成功打印出一行文字。设备与驱动加载顺序可颠倒。很好的适
    应热插拔。提高了驱动的可移植性。驱动与设备的名字需保持一致

  • 相关阅读:
    Unity3D性能优化--- 收集整理的一堆
    Unity5.3官方VR教程重磅登场-系列7 优化VR体验
    VR沉浸体验的要求
    Unity5中叹为观止的实时GI效果
    浅谈控制反转与依赖注入[转]
    unity 使用unityaction 需要注意的问题[转]
    c# orm框架 sqlsugar
    unity Instantiate设置父级物体bug
    宝塔面板 使用mongodb注意事项
    unity中gameObject.SetActive()方法的注意事项。
  • 原文地址:https://www.cnblogs.com/defen/p/4824046.html
Copyright © 2011-2022 走看看