接触和学习Linux已一年有余了. 对 Linux 的设备模型(bus+device+driver+sysfs+uevent)理解尚未深刻.所以,写份简单的代码强化理解.
我自己写的 bus, 和在 bus 上注册 device 和 driver. 并让 device 和 driver 成功的match. 还未实现 uevent 时间.正在抽空了解 netlink.
/* my_bus.c */
- #include <linux/module.h>
- #include <linux/device.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/string.h>
- #include "my_bus.h"
- MODULE_LICENSE("Dual BSD/GPL");
- #define MYBUS "mybus: "
- #define PRINT(x...) printk(KERN_ALERT MYBUS x);
- static char *Version = "$Revision: 1.9 $";
- static ssize_t show_bus_version(struct bus_type *bus, char *buf)
- {
- PRINT("%s\n", __func__);
- return snprintf(buf, PAGE_SIZE, "my_bus: %s\n", Version);
- }
- static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
- static void my_bus_release(struct device *dev)
- {
- PRINT("%s\n", __func__);
- }
- static int my_bus_match(struct device *dev, struct device_driver *drv)
- {
- struct my_device *device = to_my_device(dev);
- PRINT("%s\n", __func__);
- return !strncmp(device->name, drv->name, strlen(drv->name));
- }
- static int my_bus_hotplug(struct device *dev, struct kobj_uevent_env *env){
- PRINT("%s\n", __func__);
- return 0;
- }
- struct bus_type my_bus_type = {
- .name = "my_bus",
- .match = my_bus_match,
- .uevent = my_bus_hotplug
- };
- static struct device my_bus = {
- .init_name = "my_bus0",
- .release = my_bus_release
- };
- /* interface to device. */
- int register_my_device(struct my_device *device)
- {
- PRINT("%s\n", __func__);
- device->device.bus = &my_bus_type;
- device->device.parent = &my_bus;
- device->device.release = my_bus_release;
- //strncpy(device->device.bus_id, device->name, BUS_ID_SIZE);
- return device_register(&device->device);
- }
- EXPORT_SYMBOL(register_my_device);
- void unregister_my_device(struct my_device *device)
- {
- PRINT("%s\n", __func__);
- device_unregister(&device->device);
- }
- EXPORT_SYMBOL(unregister_my_device);
- static ssize_t show_version(struct device_driver *driver, char *buf)
- {
- struct my_driver *drv = to_my_driver(driver);
- PRINT("%s\n", __func__);
- sprintf(buf, "%s\n", drv->version);
- return strlen(buf);
- }
- /* interface to driver. */
- int register_my_driver(struct my_driver *driver)
- {
- int ret = 0;
-
- PRINT("%s\n", __func__);
- driver->driver.bus = &my_bus_type;
- ret = driver_register(&driver->driver);
- if (ret) {
- PRINT("%s, driver_register %s failed!!!\n", __func__, driver->driver.name);
- return ret;
- }
- driver->version_attr.attr.name = "version";
- //driver->version_attr.attr.owner = driver->module;
- driver->version_attr.attr.mode = S_IRUGO;
- driver->version_attr.show = show_version;
- return driver_create_file(&driver->driver, &driver->version_attr);
- }
- EXPORT_SYMBOL(register_my_driver);
- void unregister_my_driver(struct my_driver *driver)
- {
- PRINT("%s\n", __func__);
- driver_unregister(&driver->driver);
- }
- EXPORT_SYMBOL(unregister_my_driver);
- static int __init my_bus_init(void)
- {
- int ret = 0;
- ret = bus_register(&my_bus_type);
- if (ret) {
- PRINT("%s, bus_register failed!\n", __func__);
- goto bus_register_failed;
- }
- ret = bus_create_file(&my_bus_type, &bus_attr_version);
- if (ret) {
- PRINT("%s, bus_create_file failure...!\n", __func__);
- goto bus_create_file_failed;
- }
- ret = device_register(&my_bus);
- if (ret) {
- PRINT("%s, device_register failure...!\n", __func__);
- goto device_register_failed;
- }
- PRINT("%s, bus & device register succeed!\n", __func__);
- return 0;
-
- device_register_failed:
- bus_create_file_failed:
- bus_unregister(&my_bus_type);
- bus_register_failed:
- return ret;
- }
- static void __exit my_bus_exit(void)
- {
- PRINT("%s!\n", __func__);
- device_unregister(&my_bus);
- bus_unregister(&my_bus_type);
- }
- module_init(my_bus_init);
- 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. 作为总线.
- 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也是一种设备.
- static struct device my_bus = {
- .init_name = "my_bus0", //my_bus设备的名称.在device_add函数里会将init_name复制到kobject中.
- .release = my_bus_release //取消注册的设备的时候要调用的函数.
- };
3. 调用 bus_register(&my_bus_type) 将 my_bus_type 这个总线类型注册到系统.
4. 调用 device_register(&my_bus) 将总线设备注册到系统.
(步骤3和4都在my_bus_init函数中.)
- static int __init my_bus_init(void)
- {
- int ret = 0;
- ret = bus_register(&my_bus_type); //注册my_bus_type到Linux设备驱动模型中.
- if (ret) {
- PRINT("%s, bus_register failed!\n", __func__);
- goto bus_register_failed;
- }
- ret = bus_create_file(&my_bus_type, &bus_attr_version);//创建my_bus的一个属性.在sysfs中体现.
- if (ret) {
- PRINT("%s, bus_create_file failure...!\n", __func__);
- goto bus_create_file_failed;
- }
- ret = device_register(&my_bus); //注册my_bus到Linux设备驱动模型中.
- if (ret) {
- PRINT("%s, device_register failure...!\n", __func__);
- goto device_register_failed;
- }
- PRINT("%s, bus & device register succeed!\n", __func__);
- return 0;
-
- device_register_failed:
- bus_create_file_failed:
- bus_unregister(&my_bus_type);
- bus_register_failed:
- return ret;
- }
上面四个步骤, 就将一条总线注册到 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 函数中.
- static void __exit my_bus_exit(void)
- {
- PRINT("%s!\n", __func__);
- device_unregister(&my_bus);
- bus_unregister(&my_bus_type);
- }
驱动的加载和卸载都是 module_init 和 module_exit 来实现的.
- module_init(my_bus_init);
- module_exit(my_bus_exit);
总线上的驱动和设备的匹配函数如下. 就是通过简单的驱动的名称和设备的名称一致即可.(Linux内核很多都是这样实现的.)
- static int my_bus_match(struct device *dev, struct device_driver *drv)
- {
- struct my_device *device = to_my_device(dev);
- PRINT("%s\n", __func__);
- return !strncmp(device->name, drv->name, strlen(drv->name));
- }
注册与卸载设备:
在 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 设备驱动模型的管理中.
- int register_my_device(struct my_device *device)
- {
- PRINT("%s\n", __func__);
- device->device.bus = &my_bus_type;
- device->device.parent = &my_bus;
- device->device.release = my_bus_release;
- //strncpy(device->device.bus_id, device->name, BUS_ID_SIZE);
- return device_register(&device->device);
- }
- EXPORT_SYMBOL(register_my_device);
卸载 my_bus 上的 my_device 过程:
1. 调用 device_unregister 函数将 my_device 中的 device 结构从 Linux 设备驱动模型中移除.
- void unregister_my_device(struct my_device *device)
- {
- PRINT("%s\n", __func__);
- device_unregister(&device->device);
- }
- EXPORT_SYMBOL(unregister_my_device);
注册与卸载驱动:
在my_bus总线上注册驱动的过程, 也就是对 my_driver 中 device_driver 的处理过程.
1. 设置 device_driver 的成员 bus 为 my_bus_type.
2. 调用 driver_register 函数, 将 device_driver 添加到 Linux 设备驱动模型中管理.
- int register_my_driver(struct my_driver *driver)
- {
- int ret = 0;
-
- PRINT("%s\n", __func__);
- driver->driver.bus = &my_bus_type;
- ret = driver_register(&driver->driver);
- if (ret) {
- PRINT("%s, driver_register %s failed!!!\n", __func__, driver->driver.name);
- return ret;
- }
- driver->version_attr.attr.name = "version";
- //driver->version_attr.attr.owner = driver->module;
- driver->version_attr.attr.mode = S_IRUGO;
- driver->version_attr.show = show_version;
- return driver_create_file(&driver->driver, &driver->version_attr);
- }
- EXPORT_SYMBOL(register_my_driver);
在my_bus总线上卸载驱动的过程, 也是对 my_driver 中 device_driver 的处理过程.
1. 调用driver_unregister函数将 device_driver 从 Linux 设备驱动模型中移除.
- void unregister_my_driver(struct my_driver *driver)
- {
- PRINT("%s\n", __func__);
- driver_unregister(&driver->driver);
- }
- 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