Chinaunix首页 | 论坛 | 博客
  • 博客访问: 248620
  • 博文数量: 33
  • 博客积分: 246
  • 博客等级: 二等列兵
  • 技术积分: 918
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-31 16:37
文章分类

全部博文(33)

文章存档

2014年(4)

2013年(7)

2012年(22)

分类: LINUX

2012-09-10 21:02:12

接触和学习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上取消注册.
my_bus.c

点击(此处)折叠或打开

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

    6. #include "my_bus.h"

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

    8. #define MYBUS "mybus: "
    9. #define PRINT(x...) printk(KERN_ALERT MYBUS x);

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

    11. static ssize_t show_bus_version(struct bus_type *bus, char *buf)
    12. {
    13.     PRINT("%s\n", __func__);
    14.     return snprintf(buf, PAGE_SIZE, "my_bus: %s\n", Version);
    15. }

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

    17. static void my_bus_release(struct device *dev)
    18. {
    19.     PRINT("%s\n", __func__);
    20. }

    21. static int my_bus_match(struct device *dev, struct device_driver *drv)
    22. {
    23.     struct my_device *device = to_my_device(dev);
    24.     PRINT("%s\n", __func__);

    25.     return !strncmp(device->name, drv->name, strlen(drv->name));
    26. }

    27. static int my_bus_hotplug(struct device *dev, struct kobj_uevent_env *env){
    28.     PRINT("%s\n", __func__);
    29.     return 0;
    30. }

    31. struct bus_type my_bus_type = {
    32.     .name = "my_bus",
    33.     .match = my_bus_match,
    34.     .uevent = my_bus_hotplug
    35. };

    36. static struct device my_bus = {
    37.     .init_name = "my_bus0",
    38.     .release = my_bus_release
    39. };

    40. /* interface to device. */
    41. int register_my_device(struct my_device *device)
    42. {
    43.     PRINT("%s\n", __func__);
    44.     device->device.bus = &my_bus_type;
    45.     device->device.parent = &my_bus;
    46.     device->device.release = my_bus_release;

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

    48.     return device_register(&device->device);
    49. }
    50. EXPORT_SYMBOL(register_my_device);

    51. void unregister_my_device(struct my_device *device)
    52. {
    53.     PRINT("%s\n", __func__);
    54.     device_unregister(&device->device);
    55. }
    56. EXPORT_SYMBOL(unregister_my_device);

    57. static ssize_t show_version(struct device_driver *driver, char *buf)
    58. {
    59.     struct my_driver *drv = to_my_driver(driver);

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

    62.     return strlen(buf);
    63. }

    64. /* interface to driver. */
    65. int register_my_driver(struct my_driver *driver)
    66. {
    67.     int ret = 0;
    68.     
    69.     PRINT("%s\n", __func__);

    70.     driver->driver.bus = &my_bus_type;
    71.     ret = driver_register(&driver->driver);
    72.     if (ret) {
    73.         PRINT("%s, driver_register %s failed!!!\n", __func__, driver->driver.name);
    74.         return ret;
    75.     }

    76.     driver->version_attr.attr.name = "version";
    77.     //driver->version_attr.attr.owner = driver->module;
    78.     driver->version_attr.attr.mode = S_IRUGO;
    79.     driver->version_attr.show = show_version;

    80.     return driver_create_file(&driver->driver, &driver->version_attr);
    81. }
    82. EXPORT_SYMBOL(register_my_driver);

    83. void unregister_my_driver(struct my_driver *driver)
    84. {
    85.     PRINT("%s\n", __func__);
    86.     driver_unregister(&driver->driver);
    87. }
    88. EXPORT_SYMBOL(unregister_my_driver);


    89. static int __init my_bus_init(void)
    90. {
    91.     int ret = 0;

    92.     ret = bus_register(&my_bus_type);
    93.     if (ret) {
    94.         PRINT("%s, bus_register failed!\n", __func__);
    95.         goto bus_register_failed;
    96.     }

    97.     ret = bus_create_file(&my_bus_type, &bus_attr_version);
    98.     if (ret) {
    99.         PRINT("%s, bus_create_file failure...!\n", __func__);
    100.         goto bus_create_file_failed;
    101.     }

    102.     ret = device_register(&my_bus);
    103.     if (ret) {
    104.         PRINT("%s, device_register failure...!\n", __func__);
    105.         goto device_register_failed;
    106.     }

    107.     PRINT("%s, bus & device register succeed!\n", __func__);
    108.     return 0;
    109.     
    110. device_register_failed:
    111. bus_create_file_failed:
    112.     bus_unregister(&my_bus_type);
    113. bus_register_failed:
    114.     return ret;
    115. }

    116. static void __exit my_bus_exit(void)
    117. {
    118.     PRINT("%s!\n", __func__);
    119.     device_unregister(&my_bus);
    120.     bus_unregister(&my_bus_type);
    121. }

    122. module_init(my_bus_init);
    123. 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 = {
          .name    = "my_bus",      //总线名称.
          .match   = my_bus_match,  //用来匹配设备和驱动的函数.
          .uevent  = my_bus_hotplug //用来发送uevent到用户空间.
      };
    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 来实现的.

    1. module_init(my_bus_init);
    2. 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 宏如下:
    1. #define BUS_ATTR(_name, _mode, _show, _store)    \
    2. 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);
    阅读(12639) | 评论(4) | 转发(23) |
    给主人留下些什么吧!~~

    wjlkoorey2582012-09-15 16:34:29

    hk2305621: 今天晚上增加了一些讲解和注释.有空帮我看看是否OK.谢谢!.....
    嗯,很棒。其实我不是做驱动的,不敢妄加评判,呵呵,术业有专攻嘛。期待你更多原创作品

    hk23056212012-09-12 23:56:54

    wjlkoorey258: 好文章,最近刚好看到点儿驱动的资料,很不错。前段时间研究了一下netlink,回头总结出来和大家分享分享。文章中所配的代码如果再能“深入”讲解一下就更棒了[e.....
    今天晚上增加了一些讲解和注释.有空帮我看看是否OK.谢谢!

    hk23056212012-09-11 23:16:21

    wjlkoorey258: 好文章,最近刚好看到点儿驱动的资料,很不错。前段时间研究了一下netlink,回头总结出来和大家分享分享。文章中所配的代码如果再能“深入”讲解一下就更棒了[e.....
    呵呵, 这份代码是我做的实验, 注释和讲解还尚未补全. 而且, 我也自己认为这个文章需要更多的文字来阐述.
    白天工作没办法更新, 只有下班回家才能弄. 改天补上之.

    我看你blog, 你对网络部分应该很精通了吧.膜拜下.今天白天还进你的blog参观了下.呵呵.如果可以的话,交个朋友.^_^

    wjlkoorey2582012-09-11 21:59:09

    好文章,最近刚好看到点儿驱动的资料,很不错。前段时间研究了一下netlink,回头总结出来和大家分享分享。文章中所配的代码如果再能“深入”讲解一下就更棒了