• 总线设备驱动模型


    一、总线模型(转自国嵌论坛)
      1.随着技术的进步,对热插拔的要求以及可移植性的要求越来越高,从Linux2.4开始虽然有了模型但是正式提出是在Linux2.6。
      2.关键词是总线,驱动,设备
      3.总线能够感知设备的插拔:
        (1)插入新设备的时候知道有设备插入,那么就去总线上已有的驱动里面查找能够处理该新设备的驱动,一旦匹配,该驱动就有了该设备的控制权
        (2)拔出的时候,总线也能感知,并且告诉相应的驱动从而使得驱动能够处理拔出事件
    二、总线
      1、描述结构
        linux内核中总线由bus_type结构描述

    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);
        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 subsys_private *p;
    };

      2、重要成员
        a、 const char        *name;        总线名称
        b、 int (*match)(struct device *dev, struct device_driver *drv);    match函数,用来匹配挂载在总线上的设备与驱动
      3、注册总线
        函数:bus_register    (struct    bus_type*)
        若成功,新总线被添加进系统,可在/sys/bus目录下查看到相应目录
      4、注销总线
        函数:bus_unregister    (struct    bus_type*)

    三、驱动
      1、描述结构
        device_driver

    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;
    };

      2、重要成员
        a、const char        *name;                    驱动名称
        b、struct bus_type        *bus;                要挂载到的总线名称
        c、 int (*probe) (struct device *dev);     当驱动和设备匹配成功时调用这个函数
      3、注册
        函数:driver_register    (struct    bus_type*)
        若成功,新驱动被添加进系统,可在对应总线目录下的drivers查看到相应目录
      4、注销
        函数:driver_unregister    (struct    bus_type*)
    四、设备
        1、描述结构device

    struct device {
        struct device        *parent;
    
        struct device_private    *p;
    
        struct kobject kobj;
        const char        *init_name; /* initial name of the device */
        struct device_type    *type;
    
        struct mutex        mutex;    /* mutex to synchronize calls to
                         * its driver.
                         */
    
        struct bus_type    *bus;        /* type of bus device is on */
        struct device_driver *driver;    /* which driver has allocated this
                           device */
        void        *platform_data;    /* Platform specific data, device
                           core doesn't touch it */
        struct dev_pm_info    power;
        struct dev_power_domain    *pwr_domain;
    
    #ifdef CONFIG_NUMA
        int        numa_node;    /* NUMA node this device is close to */
    #endif
        u64        *dma_mask;    /* dma mask (if dma'able device) */
        u64        coherent_dma_mask;/* Like dma_mask, but for
                             alloc_coherent mappings as
                             not all hardware supports
                             64 bit addresses for consistent
                             allocations such descriptors. */
    
        struct device_dma_parameters *dma_parms;
    
        struct list_head    dma_pools;    /* dma pools (if dma'ble) */
    
        struct dma_coherent_mem    *dma_mem; /* internal for coherent mem
                             override */
        /* arch specific additions */
        struct dev_archdata    archdata;
    
        struct device_node    *of_node; /* associated device tree node */
    
        dev_t            devt;    /* dev_t, creates the sysfs "dev" */
    
        spinlock_t        devres_lock;
        struct list_head    devres_head;
    
        struct klist_node    knode_class;
        struct class        *class;
        const struct attribute_group **groups;    /* optional groups */
    
        void    (*release)(struct device *dev);
    };

      2、重要成员
        a、const char        *init_name;                    设备名称
        b、struct bus_type        *bus;                要挂载到的总线名称
      3、注册
        函数:device_register    (struct    bus_type*)
        若成功,新驱动被添加进系统,可在对应总线目录下的drivers查看到相应目录
      4、注销
        函数:device_unregister    (struct    bus_type*)

    五、实例验证将驱动和设备挂在到总线中并进行匹配
      1、当实际硬件设备和驱动程序进行匹配时会通过设备ID等来完成,我们此处所使用的是一个模拟的硬件,没有设备ID,所以用设备名称和驱动名称来匹配,这就要求我们在初始化device_driver和device时两者名字要一致。

    struct device wsk_dev = 
    {
        .init_name = "my driver",
        .bus = &bus,
    };
    struct device_driver dev = 
    {
        .name = "my driver",
        .bus = &bus,
        .probe = my_probe,
    };

    2、match函数实现

      

    /*设备匹配函数*/
    int my_match(struct device *dev, struct device_driver *drv)
    {
    
        return !strncmp(dev->kobj.name,drv->name,strlen(dev->kobj.name));
    }

      这里将驱动名称和设备名称进行字符串比较,若一致strncmp返回0表示成功,但是match返回值非0才会调用probe函数,所以在前面加上叹号。
      注意这里使用的是dev->kobj.name而不是dev->init_name,原因是在注册设备函数中将dev->init_name赋值给了dev->kobj.name,而将dev->init_name清空,所以此时dev->init_name是一个空指针。以下是内核代码中清空的过程。

      

      所以用 dev->init_name进行匹配时会提示使用空指针从而导致内核崩溃。

    3、符号导出
      定义设备和驱动描述结构时要初始化总线名称这个成员,要用到bus.c文件里的bus符号,所以要将它导出,然后在driver.c和device.c中将它输出

    4、probe函数
      打印一串信息来提示匹配成功

    int my_probe (struct device *dev)
    {
        printk("driver find the device is can handle
    ");
        
        return 0;
    
    }

      关于probe函数在什么时候被谁调用这个问题,引用一篇博文,里面解释得很清楚了
      链接:http://www.cnblogs.com/hoys/archive/2011/04/01/2002299.html

    bus.c:

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/device.h>
    
    /*设备匹配函数*/
    int my_match(struct device *dev, struct device_driver *drv)
    {
    
        return !strncmp(dev->kobj.name,drv->name,strlen(dev->kobj.name));
    }
    
    /*定义总线设备*/
    struct bus_type bus =
    {
        .name = "my bus",
        .match = my_match,
    };
    
    static int bus_init()
    {
        /*注册总线设备驱动*/
        bus_register(&bus);
    
        return 0;
    }
    
    
    void bus_exit()
    {
        /*注销总线设备驱动*/
        bus_unregister(&bus);
    }
    
    MODULE_LICENSE("GPL");
    EXPORT_SYMBOL(bus);
    module_init(bus_init);
    module_exit(bus_exit);

    driver.c:

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/device.h>
    
    extern struct bus_type bus;
    
    int my_probe (struct device *dev)
    {
        printk("driver find the device is can handle
    ");
        
        return 0;
    
    }
    
    struct device_driver dev = 
    {
        .name = "my driver",
        .bus = &bus,
        .probe = my_probe,
    };
    
    static int my_driver_init()
    {
        /*注册驱动*/
        driver_register(&dev);
    
        return 0;
    }
    
    void my_driver_exit()
    {
        /*注销驱动*/
        driver_unregister(&dev);
    }
    
    MODULE_LICENSE("GPL");
    module_init(my_driver_init);
    module_exit(my_driver_exit);

    devices.c:

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/device.h>
    
    extern struct bus_type bus;
    
    struct device wsk_dev = 
    {
        .init_name = "my driver",
        .bus = &bus,
    };
    
    static int my_device_init()
    {
        /*注册设备*/
        device_register(&wsk_dev);
    
        return 0;
    }
    
    void my_device_exit()
    {
        /*注销设备*/
        device_unregister(&wsk_dev);
    }
    
    MODULE_LICENSE("GPL");
    module_init(my_device_init);
    module_exit(my_device_exit);

    如果有疑问或建议,欢迎指出。

  • 相关阅读:
    ElementUI 之 Message,自动弹出,信息不显示问题
    eslint 对下一行不要校验报错
    <input type="file"> accept属性筛选文件类型
    纯 css 控制隔行变色
    本地启动服务,两个进程分别监听两个端口,导致两个 URL 不同
    tap 事件会触发两次问题
    时间宝贵-----
    有些人,得到和失去,你都会后悔!
    前调清新,中调醇厚,后调悠长。
    office 格式定义
  • 原文地址:https://www.cnblogs.com/51qianrushi/p/4294680.html
Copyright © 2020-2023  润新知