zoukankan      html  css  js  c++  java
  • linux设备驱动模型二【转】

    本文转载自:http://blog.csdn.net/u013904227/article/details/51167886

     

    目录(?)[+]

     

    1、总线概念

    总线是内核与一个或者多个设备之间的通道。在设备模型中,所有的设备都是通过总线连接

    2、sysfs

    • 作用: 


      一个用于内核对象表述的文件系统 
      sysfs是一个基于ramfs的内存文件系统. 描述内核数据结构,属性以及与它们用户空间的链接关系

    • sys文件夹内含的顶层文件夹(使用ls -l /sys可以查看)

      1. block/
      2. bus/ :包含内核中所有总线类型的描述. 每一个总线文件夹下面都有下面两类文件夹
      3. devices/ :内核中所有注册的设备,内含的设备都是指向sys目录下面devices目录内设备的链接文件
      4. drivers/ :所有与注册设备相匹配的驱动程序 
        总线设备驱动总是会分出设备与驱动,也就是设备、驱动分离分层的概念,在注册的时候总会进行设备与驱动的匹配,内核总线会提供各自的match函数以供此类总线设备驱动调用
      5. class/
      6. devices/ : 设备树的文件系统表述. 映射了内核的设备结构层次,包含内核所有的注册设备 
      7. firmware/
      8. net/
      9. fs/ : 包含一些文件系统的目录.想要表述其属性的文件系统都必须在fs目录下创建自己的层次

    3、kobject

    每个加载到内核的kobject结构体都具体化为一个sys或者其子目录下的一个文件夹

    Kobject

    一个基础结构,用于保持设备模型在一起,Kobject最初是用来简单的引用计数的,但是随着时间的发展已经增加了很多的功能

    • 对象的引用计数 
      当一个内核对象被创建的时候,往往不能够确定这个对象的声明周期,Kobject被用作对象声明周期的跟踪,当对象计数完毕之后且没有内核进行引用,则该对象就可以被删除(正在使用的模块不能够删除应该与此有关)
    • sysfs的表示 
      在sys下面的每一个对象都对应一个Kobject结构,用来创建其可视化的表示
    • 数据结构粘和 
      整体来看, 设备模型是一个极端复杂的由多级组成的数据结构, 各级之间有许多连接。kobject 实现这个结构并且保持它在一起
    • 热拔插事件的处理 
      Kobject负责事件的产生,而事件则负责通知内核用户空间硬件的接入与卸载

    Kobject结构体原型(不同版本的内核原型可能不一样)

    struct kobject {
        const char      * k_name;               //Kobject静态名字
        char            name[KOBJ_NAME_LEN];    //Kobject名字 
        struct kref     kref;
        struct list_head    entry;              //list_head结构体
        struct kobject      * parent;           //父结构体,往往是指向容纳kobject的kset中的kobject结构体
        struct kset     * kset;                 //kset结构体
        struct kobj_type    * ktype;        //ktype,内包含kobject的relase函数指针
        struct dentry       * dentry;
        wait_queue_head_t   poll;               //等待队列的头部
    };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    使用Kobject

    由于C语言没有继承这一特性,所以需要另一种方法实现,就是在结构体里面嵌入另一个结构体来实现对上一个对象的继承,要从嵌入的结构体找到被嵌入的结构体需要使用container_of宏,这个是与list_head一样的原理,详见list_head结构体详解

    • 初始化清零Kobject 
      使用memset函数对Kobject结构体进行初始化清零操作,否则会产生意想不到的程序崩溃
    • 初始化一个Kobject
    void kobject_init(struct kobject *kobj);
    • 1
    • 1
    • 设置Kobject的名字(必须的,需要用在sysfs的入口)
    int kobject_set_name(struct kobject *kobj, const char *fmt, ...);
    va_start(args,fmt); //初始化一个char型指针,指向本函数最后一个参数的结尾处并且32字节对齐
    (void)((args) = (((char *) &(fmt)) + (((sizeof (fmt)) + (31)) & (~(31)))))
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3
    • 设置其他应当设置的 kobject 成员 
      ktype, kset, parent.

    Kobject引用计数

    struct kobject *kobject_get(struct kobject *kobj);  //增加一个计数
    void kobject_put(struct kobject *kobj);             //减掉一个计数
    • 1
    • 2
    • 1
    • 2

    当一个Kobject计数为0时,代表此对象已经到达生命周期的尽头,此时就需要一个Kobject的释放函数,而这个释放函数必须存在到其被调用为止,但是又由于这个Kobject释放函数并不能嵌入kobject结构体本身,所以就有了kobj_type这个结构体,此结构体往往可能会在两个地方给出,一个是kobject内部的kobj_type,另一个是包含该kobject的kset给出,已知该kobject的话,可以使用struct kobj_type *get_ktype(struct kobject *kobj);来获取其kobj_type结构体

    4、kset

    很多情况下,kset被看做是kobj_type的扩展,用来表示被嵌入到同一类型结构的kobject集合。但是kobj_type关注的是特定一个对象,而kset关注的是一整个集合

    struct kset {
        struct kobj_type    * ktype;        //内含的kobj_type,用于指示kobject的kobj_type,优先于kobject自身含有的kobj_type
        struct list_head    list;           //用于串联kset的list_head双向循环链表
        spinlock_t      list_lock;  
        struct kobject      kobj;           //内含的kobject
        struct kset_uevent_ops  * uevent_ops;   //
    };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    增加一个kobject到kset

    此时该objetc计数会增加1,因为这是对kobject的一个引用

    extern int kobject_register(struct kobject *kobj);
    //此函数是kobject_init 和 kobject_add 的结合
    • 1
    • 2
    • 1
    • 2

    从kset删掉一个kobject

    void kobject_del(struct kobject *kobj); //或者 
    extern void kset_unregister(struct kset * k);
    //后者是下述两个函数的结合
    extern void kobject_del(struct kobject *);
    extern void kobject_put(struct kobject *);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 1
    • 2
    • 3
    • 4
    • 5

    kset提供类似于kobject的接口函数

    extern void kset_init(struct kset * k);
    extern int __must_check kset_add(struct kset * k);
    extern int __must_check kset_register(struct kset * k);
    extern void kset_unregister(struct kset * k);
    
    /* 减少一个计数 */
    static inline struct kset * kset_get(struct kset * k)
    {
        return k ? to_kset(kobject_get(&k->kobj)) : NULL;
    }
    
    /* 增加一个计数 */
    static inline void kset_put(struct kset * k)
    {
        kobject_put(&k->kobj);
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    5、低级sysfs操作

    kobjects 的 sysfs 入口一直为目录, 因此一个对 kobject_add 的调用导致在sysfs 中创建一个目录. 常常地, 这个目录包含一个或多个属性

    • 分配给 kobject 的名字( 用 kobject_set_name ) 是给 sysfs 目录使用的名字. 因此, 出现在 sysfs 层次的相同部分的 kobjects必须有唯一的名字. 分配给 kobjects 的名字也应当是合理的文件名字: 它们不能包含斜线字符, 不能为空

    • sysfs 入口位于对应 kobject 的 parent 指针的目录中. 如果 parent 是 NULL 当 kobject_add 被调用时, 它被设置为嵌在新 kobject 的kset 中的 kobject; 因此, sysfs 层级常常匹配使用 kset 创建的内部层次. 如果 parent 和 kset 都是 NULL, sysfs 目录将在顶级被创建

    //每个kobject被创建的时候都会有一个缺省的属性,由kobj_type里面的struct attribute   ** default_attrs;指定
    /* struct attribute原型 */
    struct attribute {
        const char      * name;
        struct module       * owner;
        mode_t          mode;
    };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    //属性类别
    #define S_IRWXUGO   (S_IRWXU|S_IRWXG|S_IRWXO)
    #define S_IALLUGO   (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
    #define S_IRUGO     (S_IRUSR|S_IRGRP|S_IROTH)
    #define S_IWUGO     (S_IWUSR|S_IWGRP|S_IWOTH)
    #define S_IXUGO     (S_IXUSR|S_IXGRP|S_IXOTH)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    /* attrbute只是负责指明属性的类型,而实现这些属性的工作就交给了sysfs_ops */
    struct sysfs_ops {
        ssize_t (*show)(struct kobject *, struct attribute *,char *);
        ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
    };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 1
    • 2
    • 3
    • 4
    • 5
    • 添加一个属性到kobject
    int sysfs_create_file(struct kobject *kobj, struct attribute *attr);
    • 1
    • 1
    • 删除属性
    int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);
    • 1
    • 1
    • 创建一个符号链接
    int sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);
    • 1
    • 1
    • 删除一个符号链接
    void sysfs_remove_link(struct kobject *kobj, char *name);
    • 1
    • 1

    6、mybus的编写实现

    头文件

    #ifndef _MYBUS_H_
    #define _MYBUS_H_
    
    #include <linux/device.h>
    
    /* 定义mybus的设备结构体 */
    struct mybus_device {
        const char  * name;
        struct device   dev;
        struct mybus_driver *drv;
    };
    /* 由mybus设备里面的设备结构体找到该mybus,详见list_head结构体注解 */
    #define to_mybus_device(_dev) container_of(_dev, struct mybus_device, dev)
    
    /* 定义mybus驱动 */
    struct mybus_driver {
        int (*probe)(struct mybus_device *);
        int (*remove)(struct mybus_device *);
        void (*shutdown)(struct mybus_device *);
        int (*suspend)(struct mybus_device *, pm_message_t state);
        int (*resume)(struct mybus_device *);
        struct device_driver driver;
    };
    /* 同上面的 to_mybus_device */
    #define to_mybus_driver(drv) container_of(drv, struct mybus_driver, driver)
    
    extern int register_mybus_device(struct mybus_device *mybusdev);
    extern void unregister_mybus_device(struct mybus_device *mybusdev);
    extern int register_mybus_driver(struct mybus_driver *mybusdrv);
    extern void unregister_mybus_driver(struct mybus_driver *mybusdrv);
    
    #endif /* end _MYBUS_H_ */
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    C文件

    #include <linux/device.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/string.h>
    #include "/work/testfile/ARM9_Pro/mybus/mybus.h"
    
    /* mybus卸载时候会调用到 */
    static void mybus_relase(struct device * dev)
    {
        printk("mybus relase 
    ");
    }
    
    /* 总线设备 */
    struct device my_bus = {
        .bus_id   = "mybus",
        .release  = mybus_relase
    };
    
    /* 设备与设备驱动匹配函数 */
    static int mybus_match(struct device * dev, struct device_driver * drv)
    {
        struct mybus_device *pdev = to_mybus_device(dev);
    
        printk("mybus_match is called
    ");  //输出打印信息,说明此函数被调用
    
        return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
    }
    
    /* mybus总线热拔插事件 */
    static int mybus_event(struct device *dev, char **envp, int num_envp,
            char *buffer, int buffer_size)
    {
        envp[0] = buffer;
        snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s", mybus_version);
    
        return 0;
    }
    
    /* 定义一个总线结构体 */
    struct bus_type mybus_type = {
        .name = "mybus",
        .match = mybus_match,
        .uevent  = mybus_event,
    };
    
    /* mybus 的设备与设备驱动操作函数 */
    /* mybus 设备相关函数 */
    static void mybus_device_release(struct device * dev)
    {
        printk("mybus_device relase 
    ");
    }
    
    int register_mybus_device(struct mybus_device *mybusdev)
    {
        mybusdev->dev.bus = &mybus_type;    //设置该设备的总线为mybus总线类型
        mybusdev->dev.parent = &my_bus;     //设置该设备的父设备为my_bus设备
        mybusdev->dev.release = mybus_device_release;   //设备释放
        strncpy(mybusdev->dev.bus_id, mybusdev->name, BUS_ID_SIZE); //拷贝名字
    
        return device_register(&mybusdev->dev); //注册该设备
    }
    
    EXPORT_SYMBOL(register_mybus_device);
    
    void unregister_mybus_device(struct mybus_device *mybusdev)
    {
        device_unregister(&mybusdev->dev);  //卸载设备
    }
    
    EXPORT_SYMBOL(unregister_mybus_device);
    
    /* mybus设备驱动相关的函数 */
    static int mybus_driver_probe(struct device *_dev)
    {
        struct mybus_driver *drv = to_mybus_driver(_dev->driver);
        struct mybus_device *dev = to_mybus_device(_dev);
    
        return drv->probe(dev);
    }
    
    static int mybus_driver_remove(struct device *_dev)
    {
        struct mybus_driver *drv = to_mybus_driver(_dev->driver);
        struct mybus_device *dev = to_mybus_device(_dev);
    
        return drv->remove(dev);
    }
    
    static void mybus_driver_shutdown(struct device *_dev)
    {
        struct mybus_driver *drv = to_mybus_driver(_dev->driver);
        struct mybus_device *dev = to_mybus_device(_dev);
    
        drv->shutdown(dev);
    }
    
    static int mybus_driver_suspend(struct device *_dev, pm_message_t state)
    {
        struct mybus_driver *drv = to_mybus_driver(_dev->driver);
        struct mybus_device *dev = to_mybus_device(_dev);
    
        return drv->suspend(dev, state);
    }
    
    static int mybus_driver_resume(struct device *_dev)
    {
        struct mybus_driver *drv = to_mybus_driver(_dev->driver);
        struct mybus_device *dev = to_mybus_device(_dev);
    
        return drv->resume(dev);
    }
    
    int register_mybus_driver(struct mybus_driver *mybusdrv)
    {
        if(mybusdrv->probe)
            mybusdrv->driver.probe = mybus_driver_probe;
        if(mybusdrv->remove)
            mybusdrv->driver.remove = mybus_driver_remove;
        if(mybusdrv->shutdown)
            mybusdrv->driver.shutdown = mybus_driver_shutdown;
        if(mybusdrv->suspend)
            mybusdrv->driver.suspend = mybus_driver_suspend;
        if(mybusdrv->resume)
            mybusdrv->driver.resume = mybus_driver_resume;
    
        mybusdrv->driver.bus = &mybus_type; //设置设备驱动的总线为mybus总线
    
        return driver_register(&mybusdrv->driver);  //注册设备驱动
    }
    
    EXPORT_SYMBOL(register_mybus_driver);
    
    void unregister_mybus_driver(struct mybus_driver *mybusdrv)
    {
        driver_unregister(&mybusdrv->driver);   //卸载设备驱动
    }
    
    EXPORT_SYMBOL(unregister_mybus_driver);
    
    /* mybus总线初始化 */
    static int mybus_init(void)
    {
        int error;
    
        error = bus_register(&mybus_type);  //注册mybus总线
        if(error){
            bus_unregister(&mybus_type);
            printk("Can't register mybus_type bus_type
    ");
            return error;
        }
    //  此处简化创建,没有使用属性,创建之后会自动被设置为默认属性
    //  if(bus_create_file(&mybus_type, &mybus_attribute)){
    //      printk("Can't create mybus version attribute
    ");
    //  }
    
        error = device_register(&my_bus);   //mybus设备注册,所有的从属于mybus总线的设备都会被挂入此设备链表
        if(error){
            device_unregister(&my_bus);
            printk("Can't register my_bus device
    ");
            return error;
        }
    
        return 0;
    }
    
    /* mybus总线卸载 */
    static void mybus_exit(void)
    {
        device_unregister(&my_bus);
    //  bus_remove_file(&mybus_type, &mybus_attribute);
    //  此处简化创建,没有使用属性,创建之后会自动被设置为默认属性
        bus_unregister(&mybus_type);
    }
    
    module_init(mybus_init);
    module_exit(mybus_exit);
    MODULE_LICENSE("GPL");
    
    MODULE_AUTHOR("YellowMax");
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180

    8、mybus与驱动的联合测试

    led_dev测试程序

    #include "/work/testfile/ARM9_Pro/mybus/mybus.h"
    
    static void led_dev_release(struct device * dev)
    {
        printk("led_dev relase 
    ");
    }
    
    static struct mybus_device led_dev = {
        .name   =   "by_led",
        .dev    = {
            .release    = led_dev_release,
        },
    };
    
    static int led_dev_init(void)
    {
        int error;
    
        error = register_mybus_device(&led_dev);
        if(error){
            printk("register led_dev failed
    ");
            unregister_mybus_device(&led_dev);
            return error;
        }
        return 0;
    }
    
    static void led_dev_exit(void)
    {
        unregister_mybus_device(&led_dev);
    }
    
    module_init(led_dev_init);
    module_exit(led_dev_exit);
    MODULE_LICENSE("GPL");
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    led_drv测试程序

    #include "/work/testfile/ARM9_Pro/mybus/mybus.h"
    
    static int led_drv_probe(struct platform_device *p_dev)
    {
        /* 输出打印信息,说明两者成功匹配 */
        printk("Found by_led, Connect success
    ");
    
        return 0;
    }
    
    static int led_drv_remove(struct platform_device *p_dev)
    {
        /* 输出打印信息,说明成功卸载设备 */
        printk("Found by_led, Remove success
    ");
    
        return 0;
    }
    
    static struct mybus_driver led_drv = {
        .probe  = led_drv_probe,
        .remove = led_drv_remove,
        .driver = {
            .name   = "by_led",
        },
    };
    
    static int led_drv_init(void)
    {
        register_mybus_driver(&led_drv);
        return 0;
    }
    
    static void led_drv_exit(void)
    {
        unregister_mybus_driver(&led_drv);
    }
    
    module_init(led_drv_init);
    module_exit(led_drv_exit);
    MODULE_LICENSE("GPL");
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    联合测试

    • 装载led_drv.ko查看现象(由于没有注册总线设备,所以会出现函数未定义错误)
    insmod led_drv.ko led_drv: Unknown symbol register_mybus_driver led_drv: Unknown symbol unregister_mybus_driver insmod: cannot insert 'led_drv.ko': Unknown symbol in module (-1): No such file or directory
    • 装载mybus.ko并且查看/sys/bus/
    insmod mybus.ko ls -l /sys/bus/ drwxr-xr-x 4 0 0 0 Jan 1 00:01 mybus
    • 查看设备
    ls -l /sys/devices/ drwxr-xr-x 3 0 0 0 Jan 1 00:01 mybus
    • 再次装载led_drv.ko并查看mybus里面的驱动
    insmod led_drv.ko ls -l /sys/bus/mybus/drivers drwxr-xr-x 2 0 0 0 Jan 1 00:12 by_led
    • 装载led_dev.ko
    insmod led_dev.ko mybus_match is called //调用到了mybus.c里面的相关match函数 Found by_led, Connect success //调用到led_drv.c里面的probe相关函数
    • 装载其他模块
    insmod led_drv1.ko insmod led_drv2.ko insmod led_dev2.ko mybus_match is called mybus_match is called mybus_match is called Found by_led2, Connect success

    由此可见,mybus.c里面的match相关函数会被内核不断地调用,直到找到匹配的设备或者驱动(测试函数同上,只不过修改一下设备以及驱动名字)。我装载了3个驱动,一共找了三次,基本可以说明这些设备是挂载在mybus总线设备上面的,找的时候直接去mybus总线找,

    insmod led_dev1.ko mybus_match is called mybus_match is called Found by_led1, Connect success
    • 全部装载完毕并且查看相关的设备以及设备驱动
    ls -l /sys/bus/mybus/drivers drwxr-xr-x 2 0 0 0 Jan 1 00:12 by_led drwxr-xr-x 2 0 0 0 Jan 1 00:13 by_led1 drwxr-xr-x 2 0 0 0 Jan 1 00:13 by_led2 ls -l /sys/bus/mybus/devices/ lrwxrwxrwx 1 0 0 0 Jan 1 00:15 by_led -> ../../../devices/mybus/by_led lrwxrwxrwx 1 0 0 0 Jan 1 00:15 by_led1 -> ../../../devices/mybus/by_led1 lrwxrwxrwx 1 0 0 0 Jan 1 00:15 by_led2 -> ../../../devices/mybus/by_led2
    • 尝试移除mybus
    rmmod mybus rmmod: mybus: Resource temporarily unavailable rmmod led_dev1 Found by_led, Remove success mybus_device relase rmmod led_dev Found by_led, Remove success mybus_device relase rmmod led_dev2 Found by_led, Remove success mybus_device relase rmmod led_drv rmmod led_drv1 rmmod led_drv2
    • 再次尝试移除mybus,调用到了mybus.c里面的static void mybus_relase(struct device * dev)函数,mybus设备引用计数达到0
    rmmod mybus mybus relase

    9、分析mybus过程

    /* 初始化注册一个bus总线 */
    int bus_register(struct bus_type * bus)
        retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name);  //将总线对应为mybus子系统的Kobject结构
        /* 将Kobject放入一个kset容器,并且注册 */
        subsys_set_kset(bus, bus_subsys);
        retval = subsystem_register(&bus->subsys);
    
        /* 设置一个名字为devices的Kobject,然后将其父结构指向上面构建的mybus的Kobject */
        kobject_set_name(&bus->devices.kobj, "devices");
        bus->devices.kobj.parent = &bus->subsys.kobj;
    
        /* 初始化bus总线的device链表与driver链表,device链表添加get(增加一个设备引用计数),put(减少一个设备引用计数) 
         * 之后在此bus总线里面加入设备以及驱动的时候就可以通过该bus总线的设备链与设备驱动链进行查找了 
         */
        klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
        klist_init(&bus->klist_drivers, NULL, NULL);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    /* 提供该bus总线的设备注册 */
    int register_mybus_device(struct mybus_device *mybusdev)
        /* 结构体的填充 */
        mybusdev->dev.bus = &mybus_type;    //设置该设备的总线为mybus总线类型
        mybusdev->dev.parent = &my_bus;     //设置该设备的父设备为my_bus设备
        mybusdev->dev.release = mybus_device_release;   //设备释放
        strncpy(mybusdev->dev.bus_id, mybusdev->name, BUS_ID_SIZE); //拷贝名字
    
        return device_register(&mybusdev->dev); //注册该设备
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    /* 提供该bus总线的设备驱动注册 */   
    int register_mybus_driver(struct mybus_driver *mybusdrv)
        if(mybusdrv->probe)
            mybusdrv->driver.probe = mybus_driver_probe;
        if(mybusdrv->remove)
            mybusdrv->driver.remove = mybus_driver_remove;
    
        //省略若干
    
        mybusdrv->driver.bus = &mybus_type; //设置设备驱动的总线为mybus总线
    
        return driver_register(&mybusdrv->driver);  //注册设备驱动
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    10、总线设备以及总线设备驱动的注册

    分别由device_registerdriver_register实际完成注册 
    在两者的函数里面都会有试图获得该注册对象的bus的语句,如果获得成功的话,不仅在系统顶层的sys/devices里面创建对象Kobject,而且在对应的bus里面也创建Kobject用来计数以及以文件形式显示出来设备。同一个被注册对象可能会被不同的链表链接,在任意一个链表里面都可以找到该对象。就比如一个总线设备,在其bus总线的设备列表里面可以找得到,在系统的devices链表里面也可以找得到

  • 相关阅读:
    window下安装QT出错解决方案
    wiin10下VS2015+opencv3.4.0-extra_modules+CMake配置
    可重入、线程安全辨析与场景举例
    WPF仿网易云音乐系列(三、播放进度条+控制按钮)
    WPF仿网易云音乐系列(二、歌单创建窗口+登录设置模块)
    WPF仿网易云音乐系列(一、左侧菜单栏:Expander+RadioButton)
    WPF仿网易云音乐系列(序)
    C# WPF仿360安全卫士11
    crontab的语法规则格式(每分钟、每小时、每天、每周、每月、每年定时执行 规则)
    Pymysql 连接 Mysql 数据库及增删改查操作
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/7268308.html
Copyright © 2011-2022 走看看