四联光电智能照明论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 2481|回复: 0
打印 上一主题 下一主题

Linux设备驱动模型之我理解

[复制链接]
  • TA的每日心情
    开心
    2022-6-10 09:59
  • 366

    主题

    741

    帖子

    9649

    积分

    超级版主

    Rank: 8Rank: 8

    积分
    9649
    跳转到指定楼层
    楼主
    发表于 2016-11-15 14:49:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

    原文地址:http://blog.chinaunix.net/uid-25627207-id-3343854.html 作者:hk2305621
    接触和学习Linux已一年有余了. 对 Linux 的设备模型(bus+device+driver+sysfs+uevent)理解尚未深刻.所以,写份简单的代码强化理解.

    我自己写的 bus, 和在 bus 上注册 device 和 driver. 并让 device 和 driver 成功的match. 还未实现 uevent 时间.正在抽空了解 netlink.

    Linux 版本 2.6.38-8(Ubuntu 11.04), 参考LDD3.(正在编译 3.0.42的内核,抽空过来补全这个文章^_^)

    我的总线:


    1. /* my_bus.h */

    2. extern struct bus_type my_bus_type;

    3. struct my_driver {
    4.     char *version;
    5.     struct module *module;
    6.     struct device_driver driver;
    7.     struct driver_attribute version_attr;
    8. };

    9. #define to_my_driver(drv) \
    10.     container_of(drv, struct my_driver, driver)
    11.    
    12. struct my_device {
    13.     char *name;
    14.     struct my_driver *driver;
    15.     struct device device;
    16. };

    17. #define to_my_device(dev) \
    18.     container_of(dev, struct my_device, device)

    19. /* interface to device. */
    20. extern int register_my_device(struct my_device *);
    21. extern void unregister_my_device(struct my_device *);

    22. /* interface to driver. */
    23. extern int register_my_driver(struct my_driver *);
    24. extern void unregister_my_driver(struct my_driver *);
    复制代码

    C语言中,习惯性将一些导出符号放在.h的头文件中. 我这里也不例外. 对于LInux设备驱动模型, 所有的设备和驱动都是挂接在总线上的.所以,既然有总线了,也就得声明要挂在这个总线上的设备的数据结构,和驱动的数据结构. 和对应的注册函数吧.^_^. 这就是所谓的接口.

    my_driver数据结构相关介绍:


    1. /* 我的驱动 */
    2. struct my_driver {
    3.     char *version; //my_driver的版本.用来输出到sysfs文件系统的.
    4.     struct module *module;//基本每个数据结构都要有的.
    5.     struct device_driver driver;//Linux设备驱动模型中,用来管理驱动的.
    6.     struct driver_attribute version_attr;//my_driver的一个属性.用来输出到sysfs.
    7. };

    8. //通过 device_driver 得到 my_driver.
    9. #define to_my_driver(drv) \
    10.     container_of(drv, struct my_driver, driver)

    11. /* interface to driver. */
    12. extern int register_my_driver(struct my_driver *); //将my_driver注册到my_bus上.
    13. extern void unregister_my_driver(struct my_driver *);//将my_driver从my_bus上取消注册.
    复制代码

    同样, 和 my_device 相关的部分介绍如下:


    1. /* 我的设备 */
    2. struct my_device {
    3.     char *name; //my_device的名称. 用来在 my_driver 匹配时用.
    4.     struct my_driver *driver; //my_device绑定的my_driver指针.
    5.     struct device device; //Linux设备驱动模型中,用来管理设备.
    6. };

    7. /* 通过device得到my_device结构.*/
    8. #define to_my_device(dev) \
    9.     container_of(dev, struct my_device, device)

    10. /* interface to device. */
    11. extern int register_my_device(struct my_device *); //将my_device注册到my_bus上.
    12. extern void unregister_my_device(struct my_device *);//将my_device从my_bus上取消注册.
    13. my_bus.c
    14. [code]
    15.   /* my_bus.c   */
    16. #include <linux/module.h>
    17. #include <linux/device.h>
    18. #include <linux/kernel.h>
    19. #include <linux/init.h>
    20. #include <linux/string.h>

    21. #include "my_bus.h"

    22. MODULE_LICENSE("Dual BSD/GPL");

    23. #define MYBUS "mybus: "
    24. #define PRINT(x...) printk(KERN_ALERT MYBUS x);

    25. static char *Version = "$Revision: 1.9 $";

    26. static ssize_t show_bus_version(struct bus_type *bus, char *buf)
    27. {
    28.     PRINT("%s\n", __func__);
    29.     return snprintf(buf, PAGE_SIZE, "my_bus: %s\n", Version);
    30. }

    31. static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

    32. static void my_bus_release(struct device *dev)
    33. {
    34.     PRINT("%s\n", __func__);
    35. }

    36. static int my_bus_match(struct device *dev, struct device_driver *drv)
    37. {
    38.     struct my_device *device = to_my_device(dev);
    39.     PRINT("%s\n", __func__);

    40.     return !strncmp(device->name, drv->name, strlen(drv->name));
    41. }

    42. static int my_bus_hotplug(struct device *dev, struct kobj_uevent_env *env){
    43.     PRINT("%s\n", __func__);
    44.     return 0;
    45. }

    46. struct bus_type my_bus_type = {
    47.     .name = "my_bus",
    48.     .match = my_bus_match,
    49.     .uevent = my_bus_hotplug
    50. };

    51. static struct device my_bus = {
    52.     .init_name = "my_bus0",
    53.     .release = my_bus_release
    54. };

    55. /* interface to device. */
    56. int register_my_device(struct my_device *device)
    57. {
    58.     PRINT("%s\n", __func__);
    59.     device->device.bus = &my_bus_type;
    60.     device->device.parent = &my_bus;
    61.     device->device.release = my_bus_release;

    62.     //strncpy(device->device.bus_id, device->name, BUS_ID_SIZE);

    63.     return device_register(&device->device);
    64. }
    65. EXPORT_SYMBOL(register_my_device);

    66. void unregister_my_device(struct my_device *device)
    67. {
    68.     PRINT("%s\n", __func__);
    69.     device_unregister(&device->device);
    70. }
    71. EXPORT_SYMBOL(unregister_my_device);

    72. static ssize_t show_version(struct device_driver *driver, char *buf)
    73. {
    74.     struct my_driver *drv = to_my_driver(driver);

    75.     PRINT("%s\n", __func__);
    76.     sprintf(buf, "%s\n", drv->version);

    77.     return strlen(buf);
    78. }

    79. /* interface to driver. */
    80. int register_my_driver(struct my_driver *driver)
    81. {
    82.     int ret = 0;
    83.    
    84.     PRINT("%s\n", __func__);

    85.     driver->driver.bus = &my_bus_type;
    86.     ret = driver_register(&driver->driver);
    87.     if (ret) {
    88.         PRINT("%s, driver_register %s failed!!!\n", __func__, driver->driver.name);
    89.         return ret;
    90.     }

    91.     driver->version_attr.attr.name = "version";
    92.     //driver->version_attr.attr.owner = driver->module;
    93.     driver->version_attr.attr.mode = S_IRUGO;
    94.     driver->version_attr.show = show_version;

    95.     return driver_create_file(&driver->driver, &driver->version_attr);
    96. }
    97. EXPORT_SYMBOL(register_my_driver);

    98. void unregister_my_driver(struct my_driver *driver)
    99. {
    100.     PRINT("%s\n", __func__);
    101.     driver_unregister(&driver->driver);
    102. }
    103. EXPORT_SYMBOL(unregister_my_driver);


    104. static int __init my_bus_init(void)
    105. {
    106.     int ret = 0;

    107.     ret = bus_register(&my_bus_type);
    108.     if (ret) {
    109.         PRINT("%s, bus_register failed!\n", __func__);
    110.         goto bus_register_failed;
    111.     }

    112.     ret = bus_create_file(&my_bus_type, &bus_attr_version);
    113.     if (ret) {
    114.         PRINT("%s, bus_create_file failure...!\n", __func__);
    115.         goto bus_create_file_failed;
    116.     }

    117.     ret = device_register(&my_bus);
    118.     if (ret) {
    119.         PRINT("%s, device_register failure...!\n", __func__);
    120.         goto device_register_failed;
    121.     }

    122.     PRINT("%s, bus & device register succeed!\n", __func__);
    123.     return 0;
    124.    
    125. device_register_failed:
    126. bus_create_file_failed:
    127.     bus_unregister(&my_bus_type);
    128. bus_register_failed:
    129.     return ret;
    130. }

    131. static void __exit my_bus_exit(void)
    132. {
    133.     PRINT("%s!\n", __func__);
    134.     device_unregister(&my_bus);
    135.     bus_unregister(&my_bus_type);
    136. }

    137. module_init(my_bus_init);
    138. module_exit(my_bus_exit);
    复制代码

    my_bus.c 是实现 my_bus 的主要文件. 其实一条总线, 在 Linux 设备驱动模型中, 它也是一个设备. 所以, 需要声明 my_bus_type 的同时, 还要声明 my_bus 这个设备.(注:C语言中,非导出符号在文件中声明时用static标明.所以, 在my_bus.c中, 你将看到大多数的函数都是static.)

    注册 my_bus 的步骤.
    1. 声明my_bus_type, 类型为 bus_type. 作为总线.


    1. struct bus_type my_bus_type = {
    2.     .name    = "my_bus",      //总线名称.
    3.     .match   = my_bus_match,  //用来匹配设备和驱动的函数.
    4.     .uevent  = my_bus_hotplug //用来发送uevent到用户空间.
    5. };
    复制代码

    2. 声明my_bus,类型为 struct device, 因为my_bus也是一种设备.


    1. static struct device my_bus = {
    2.     .init_name = "my_bus0", //my_bus设备的名称.在device_add函数里会将init_name复制到kobject中.
    3.     .release = my_bus_release //取消注册的设备的时候要调用的函数.
    4. };
    复制代码

    3. 调用 bus_register(&my_bus_type) 将 my_bus_type 这个总线类型注册到系统.
    4. 调用 device_register(&my_bus) 将总线设备注册到系统.
    (步骤3和4都在my_bus_init函数中.)


    1. static int __init my_bus_init(void)
    2. {
    3.     int ret = 0;

    4.     ret = bus_register(&my_bus_type); //注册my_bus_type到Linux设备驱动模型中.
    5.     if (ret) {
    6.         PRINT("%s, bus_register failed!\n", __func__);
    7.         goto bus_register_failed;
    8.     }

    9.     ret = bus_create_file(&my_bus_type, &bus_attr_version);//创建my_bus的一个属性.在sysfs中体现.
    10.     if (ret) {
    11.         PRINT("%s, bus_create_file failure...!\n", __func__);
    12.         goto bus_create_file_failed;
    13.     }

    14.     ret = device_register(&my_bus); //注册my_bus到Linux设备驱动模型中.
    15.     if (ret) {
    16.         PRINT("%s, device_register failure...!\n", __func__);
    17.         goto device_register_failed;
    18.     }

    19.     PRINT("%s, bus & device register succeed!\n", __func__);
    20.     return 0;
    21.    
    22. device_register_failed:
    23. bus_create_file_failed:
    24.     bus_unregister(&my_bus_type);
    25. bus_register_failed:
    26.     return ret;
    27. }
    复制代码

    上面四个步骤, 就将一条总线注册到 Linux设备驱动模型中.

    编译并将 my_bus.ko 插入到到系统后,
    可以通过命令 ls /sys/bus/  会看到 my_bus 文件夹.
    通过命令 ls /sys/device/ 会看到 my_bus0 文件夹.
    <感慨下, 把自己知道的东西写出来,比写代码难多了.>

    其实, 对于 Linux 设备驱动模型而言, 它管理的, 是 bus_type, device 和 device_driver 三大主体. 譬如我这里的my_bus, my_device, my_driver 都是 bus_type, device, device_driver 的拓展, 都离不开 bus_type, device, device_driver 这三个主体. 所以, 这也就是为什么对应的数据结构中间都要包含它们.(my_bus_type 本身就是 bus_type 类型.)

    取消 my_bus 这条总线, 有两个步骤:
    1. 取消 my_bus 设备的注册.
    2. 取消 my_bus_type 的注册.

    步骤1和2都是在 my_bus_exit 函数中.


    1. static void __exit my_bus_exit(void)
    2. {
    3.     PRINT("%s!\n", __func__);
    4.     device_unregister(&my_bus);
    5.     bus_unregister(&my_bus_type);
    6. }
    复制代码

    驱动的加载和卸载都是 module_init 和 module_exit 来实现的.

    module_init(my_bus_init);
    module_exit(my_bus_exit);
    总线上的驱动和设备的匹配函数如下. 就是通过简单的驱动的名称和设备的名称一致即可.(Linux内核很多都是这样实现的.)


    1. static int my_bus_match(struct device *dev, struct device_driver *drv)
    2. {
    3.     struct my_device *device = to_my_device(dev);
    4.     PRINT("%s\n", __func__);

    5.     return !strncmp(device->name, drv->name, strlen(drv->name));
    6. }
    复制代码

    注册与卸载设备:
    在 my_bus 上注册设备的过程, 就是将 my_device 结构体中 device 结构体进行赋值:
    1. bus_type 成员设置为 my_bus_type
    2. parent 设置为 my_bus.
    3. release 函数设置为 my_bus_release.
    然后调用 device_register 函数将 my_device 中的 device 结构加入到 Linux 设备驱动模型的管理中.


    1. int register_my_device(struct my_device *device)
    2. {
    3.     PRINT("%s\n", __func__);
    4.     device->device.bus = &my_bus_type;
    5.     device->device.parent = &my_bus;
    6.     device->device.release = my_bus_release;

    7.     //strncpy(device->device.bus_id, device->name, BUS_ID_SIZE);

    8.     return device_register(&device->device);
    9. }
    10. EXPORT_SYMBOL(register_my_device);
    复制代码

    卸载 my_bus 上的 my_device 过程:
    1. 调用 device_unregister 函数将 my_device 中的 device 结构从 Linux 设备驱动模型中移除.


    1. void unregister_my_device(struct my_device *device)
    2. {
    3.     PRINT("%s\n", __func__);
    4.     device_unregister(&device->device);
    5. }
    6. EXPORT_SYMBOL(unregister_my_device);
    复制代码

    注册与卸载驱动:
    在my_bus总线上注册驱动的过程, 也就是对 my_driver 中 device_driver 的处理过程.
    1. 设置 device_driver 的成员 bus 为 my_bus_type.
    2. 调用 driver_register 函数, 将 device_driver 添加到 Linux 设备驱动模型中管理.


    1. int register_my_driver(struct my_driver *driver)
    2. {
    3.     int ret = 0;
    4.    
    5.     PRINT("%s\n", __func__);

    6.     driver->driver.bus = &my_bus_type;
    7.     ret = driver_register(&driver->driver);
    8.     if (ret) {
    9.         PRINT("%s, driver_register %s failed!!!\n", __func__, driver->driver.name);
    10.         return ret;
    11.     }

    12.     driver->version_attr.attr.name = "version";
    13.     //driver->version_attr.attr.owner = driver->module;
    14.     driver->version_attr.attr.mode = S_IRUGO;
    15.     driver->version_attr.show = show_version;

    16.     return driver_create_file(&driver->driver, &driver->version_attr);
    17. }
    18. EXPORT_SYMBOL(register_my_driver);
    复制代码

    在my_bus总线上卸载驱动的过程, 也是对 my_driver 中 device_driver 的处理过程.
    1. 调用driver_unregister函数将 device_driver 从 Linux 设备驱动模型中移除.


    1. void unregister_my_driver(struct my_driver *driver)
    2. {
    3.     PRINT("%s\n", __func__);
    4.     driver_unregister(&driver->driver);
    5. }
    6. EXPORT_SYMBOL(unregister_my_driver);
    复制代码

    EXPORT_SYMBOL 是用来导出符号的. 用 EXPORT_SYMBOL 导出的符号, 其他模块也可以使用.

    属性(Attribute)
    在 Linux 设备驱动模型中, 总线, 设备, 驱动的特性, 都是通过属性这一工具来呈现在 sysfs 文件系统中. 导出到用户空间的.

    1. 声明一个属性 bus_attr_version.
    static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
    在内核源码中, 会看到, BUS_ATTR 是一个宏, 基于 __ATTR 这个宏而来的, 只是在 __ATTR 宏的基础上, 赋予一些初值.BUS_ATTR 宏如下:
    #define BUS_ATTR(_name, _mode, _show, _store)    \
    struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
    其中:
    _mode 是属性在 sysfs 中对应文件的访问权限.
    _show 是读取 sysfs 中对应文件时调用的方法.
    _store 是写 sysfs 中对应的文件触发的方法.

    2. 将属性和bus_type挂接. 那么, Linux 设备驱动模型会在 sysfs 文件系统下生成对应的文件.
    __________________________________________________________________________________________
    下面是测试 my_bus 总线使用的 驱动和设备的代码.

    我的设备 my_device.c

    1. #include <linux/module.h>
    2. #include <linux/init.h>
    3. #include <linux/string.h>
    4. #include <linux/kernel.h>
    5. #include <linux/device.h>

    6. #include "my_bus.h"

    7. #define MYDEVICE "my-device: "
    8. #define PRINT(x...) printk(KERN_ALERT MYDEVICE x);

    9. MODULE_LICENSE("Dual BSD/GPL");

    10. static struct my_device device =
    11. {
    12.     .name = "hunk_device",
    13.     .device = {
    14.         .init_name = "my_device",
    15.     }
    16. };

    17. static int __init my_device_init(void)
    18. {
    19.     int ret = 0;
    20.    
    21.     PRINT("%s\n", __func__);
    22.     ret = register_my_device(&device);
    23.     if (ret) {
    24.         PRINT("%s failure..!\n", __func__);
    25.         return ret;
    26.     }
    27.    
    28.     return 0;
    29. }

    30. static void __exit my_device_exit(void)
    31. {
    32.     PRINT("%s\n", __func__);
    33.     unregister_my_device(&device);
    34. }

    35. module_init(my_device_init);
    36. module_exit(my_device_exit);
    复制代码

    我的驱动 my_driver.c

    1. #include <linux/module.h>
    2. #include <linux/init.h>
    3. #include <linux/string.h>
    4. #include <linux/kernel.h>
    5. #include <linux/device.h>

    6. #include "my_bus.h"

    7. #define MYDRIVER "my-driver: "
    8. #define PRINT(x...) printk(KERN_ALERT MYDRIVER x);

    9. MODULE_LICENSE("Dual BSD/GPL");

    10. static struct my_driver driver =
    11. {
    12.     .module = THIS_MODULE,
    13.     .driver = {
    14.         .name = "hunk_device",
    15.         .owner = THIS_MODULE
    16.     }
    17. };

    18. static int __init my_driver_init(void)
    19. {
    20.     int ret = 0;
    21.    
    22.     PRINT("%s\n", __func__);
    23.     ret = register_my_driver(&device);
    24.     if (ret) {
    25.         PRINT("%s failure..!\n", __func__);
    26.         return ret;
    27.     }
    28.    
    29.     return 0;
    30. }

    31. static void __exit my_driver_exit(void)
    32. {
    33.     PRINT("%s\n", __func__);
    34.     unregister_my_driver(&device);
    35. }

    36. module_init(my_driver_init);
    37. module_exit(my_driver_exit);
    复制代码
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|Silian Lighting+ ( 蜀ICP备14004521号-1 )

    GMT+8, 2024-5-14 23:24 , Processed in 1.078125 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表