6.1 设备驱动模型概述

设备驱动模型是Linux内核中用于管理设备驱动的一种架构,它提供了一种统一的方式来描述设备、驱动及其之间的关系,目的是简化驱动开发,提高代码的可维护性和可移植性。这部分通常以理论介绍为主,较少直接涉及代码。

6.2 kobject

kobject 是 Linux 设备驱动模型的基础数据结构,用于表示内核对象,它包含了对象的引用计数、名称以及与 sysfs 文件系统的关联等信息。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

// 定义一个全局的 kobject 结构体
struct kobject *my_kobj;

// 模块初始化函数
static int __init kobject_init(void) {
    // 创建一个 kobject 实例
    my_kobj = kobject_create_and_add("my_kobject", kernel_kobj);
    if (!my_kobj) {
        // 如果创建失败,打印错误信息
        printk(KERN_ERR "Failed to create kobject\n");
        return -ENOMEM;
    }
    printk(KERN_INFO "kobject created successfully\n");
    return 0;
}

// 模块退出函数
static void __exit kobject_exit(void) {
    if (my_kobj) {
        // 释放 kobject 及其相关资源
        kobject_put(my_kobj);
        printk(KERN_INFO "kobject removed successfully\n");
    }
}

module_init(kobject_init);
module_exit(kobject_exit);
MODULE_LICENSE("GPL");

6.3 kset

kset 是一组 kobject 的集合,它提供了一种将相关 kobject 组织在一起的方式,并且可以作为一个整体在 sysfs 中呈现。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/kset.h>

// 定义一个全局的 kset 结构体
struct kset *my_kset;

// 模块初始化函数
static int __init kset_init(void) {
    // 创建一个 kset 实例,该 kset 位于 kernel_kobj 下
    my_kset = kset_create_and_add("my_kset", NULL, kernel_kobj);
    if (!my_kset) {
        // 如果创建失败,打印错误信息
        printk(KERN_ERR "Failed to create kset\n");
        return -ENOMEM;
    }
    printk(KERN_INFO "kset created successfully\n");
    return 0;
}

// 模块退出函数
static void __exit kset_exit(void) {
    if (my_kset) {
        // 释放 kset 及其相关资源
        kset_unregister(my_kset);
        printk(KERN_INFO "kset removed successfully\n");
    }
}

module_init(kset_init);
module_exit(kset_exit);
MODULE_LICENSE("GPL");

6.4 ktype

ktype 定义了 kobject 的操作方法,包括释放函数、sysfs 文件系统的操作方法等,它描述了一类 kobject 的行为。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

// 定义 kobject 的释放函数
static void my_kobj_release(struct kobject *kobj) {
    // 这里可以进行一些资源清理操作
    printk(KERN_INFO "my_kobj is being released\n");
}

// 定义 ktype 结构体
static struct kobj_type my_ktype = {
 .release = my_kobj_release,
};

// 模块初始化函数
static int __init ktype_init(void) {
    struct kobject *kobj;
    // 创建一个 kobject 实例,并关联到 my_ktype
    kobj = kobject_create();
    if (!kobj) {
        printk(KERN_ERR "Failed to create kobject\n");
        return -ENOMEM;
    }
    kobj->ktype = &my_ktype;
    kobject_set_name(kobj, "my_kobject");
    // 将 kobject 添加到 sysfs 中
    if (kobject_add(kobj, kernel_kobj, NULL)) {
        kobject_put(kobj);
        printk(KERN_ERR "Failed to add kobject to sysfs\n");
        return -ENOMEM;
    }
    printk(KERN_INFO "kobject with ktype created and added successfully\n");
    return 0;
}

// 模块退出函数
static void __exit ktype_exit(void) {
    struct kobject *kobj = kobject_find(kernel_kobj, "my_kobject");
    if (kobj) {
        kobject_del(kobj);
        kobject_put(kobj);
        printk(KERN_INFO "kobject with ktype removed successfully\n");
    }
}

module_init(ktype_init);
module_exit(ktype_exit);
MODULE_LICENSE("GPL");

6.5 bus、device 和 driver

  • bus:表示设备所连接的总线类型,如 PCI、USB 等。它管理着总线上的设备和驱动,并负责设备和驱动的匹配。
  • device:代表具体的硬件设备,它包含了设备的属性和与驱动的关联。
  • driver:是控制设备的软件代码,它实现了对设备的操作方法。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/platform_device.h>

// 定义一个虚拟的平台设备结构体
static struct platform_device my_platform_device = {
 .name = "my_platform_device",
 .id = -1,
};

// 定义一个虚拟的平台驱动结构体
static struct platform_driver my_platform_driver = {
 .probe = my_probe,
 .remove = my_remove,
 .driver = {
     .name = "my_platform_driver",
    },
};

// 平台驱动的 probe 函数,当设备和驱动匹配时被调用
static int my_probe(struct platform_device *pdev) {
    printk(KERN_INFO "my_platform_driver: Probing device %s\n", pdev->name);
    return 0;
}

// 平台驱动的 remove 函数,当设备从系统移除时被调用
static int my_remove(struct platform_device *pdev) {
    printk(KERN_INFO "my_platform_driver: Removing device %s\n", pdev->name);
    return 0;
}

// 模块初始化函数
static int __init bus_device_driver_init(void) {
    int ret;
    // 注册平台设备
    ret = platform_device_register(&my_platform_device);
    if (ret) {
        printk(KERN_ERR "Failed to register platform device\n");
        return ret;
    }
    // 注册平台驱动
    ret = platform_driver_register(&my_platform_driver);
    if (ret) {
        platform_device_unregister(&my_platform_device);
        printk(KERN_ERR "Failed to register platform driver\n");
        return ret;
    }
    printk(KERN_INFO "Platform device and driver registered successfully\n");
    return 0;
}

// 模块退出函数
static void __exit bus_device_driver_exit(void) {
    platform_driver_unregister(&my_platform_driver);
    platform_device_unregister(&my_platform_device);
    printk(KERN_INFO "Platform device and driver unregistered successfully\n");
}

module_init(bus_device_driver_init);
module_exit(bus_device_driver_exit);
MODULE_LICENSE("GPL");

6.6 设备驱动模型中的 sysfs 文件系统

sysfs 是一种基于内存的文件系统,它将内核中的对象以文件和目录的形式呈现给用户空间,方便用户查看和操作设备的属性。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

// 定义一个全局的 kobject 结构体
struct kobject *my_kobj;

// 定义一个用于 sysfs 文件操作的 show 函数
static ssize_t my_attribute_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
    return sprintf(buf, "This is a sample attribute value\n");
}

// 定义一个用于 sysfs 文件操作的 store 函数
static ssize_t my_attribute_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
    printk(KERN_INFO "Received data: %.*s\n", (int)count, buf);
    return count;
}

// 定义一个 kobj_attribute 结构体
static struct kobj_attribute my_attribute = __ATTR(my_attr, 0644, my_attribute_show, my_attribute_store);

// 模块初始化函数
static int __init sysfs_init(void) {
    int ret;
    // 创建一个 kobject 实例
    my_kobj = kobject_create_and_add("my_kobject", kernel_kobj);
    if (!my_kobj) {
        printk(KERN_ERR "Failed to create kobject\n");
        return -ENOMEM;
    }
    // 在 kobject 下创建一个 sysfs 文件
    ret = sysfs_create_file(my_kobj, &my_attribute.attr);
    if (ret) {
        kobject_put(my_kobj);
        printk(KERN_ERR "Failed to create sysfs file\n");
        return ret;
    }
    printk(KERN_INFO "sysfs file created successfully\n");
    return 0;
}

// 模块退出函数
static void __exit sysfs_exit(void) {
    if (my_kobj) {
        // 删除 sysfs 文件
        sysfs_remove_file(my_kobj, &my_attribute.attr);
        kobject_put(my_kobj);
        printk(KERN_INFO "sysfs file and kobject removed successfully\n");
    }
}

module_init(sysfs_init);
module_exit(sysfs_exit);
MODULE_LICENSE("GPL");

6.7 设备驱动模型中的热插拔

热插拔是指在系统运行时,设备可以动态地插入或移除。内核通过一些机制来处理热插拔事件,例如在设备插入时自动加载驱动,在设备移除时卸载驱动。以下是一个简单的示意代码,展示如何注册一个热插拔操作回调函数(实际的热插拔处理涉及更多底层机制和硬件交互)。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/pm.h>

// 热插拔操作回调函数
static int my_hotplug(struct device *dev, enum hotplug_type type, unsigned int *hotplug_cmds) {
    if (type == HOTPLUG_EVENT_ADD) {
        printk(KERN_INFO "Device %s added\n", dev->kobj.name);
    } else if (type == HOTPLUG_EVENT_REMOVE) {
        printk(KERN_INFO "Device %s removed\n", dev->kobj.name);
    }
    return 0;
}

// 模块初始化函数
static int __init hotplug_init(void) {
    struct device *dev;
    // 获取一个设备指针(这里假设已有一个设备,实际可能需要通过其他方式获取)
    dev = get_device(...); 
    if (!dev) {
        printk(KERN_ERR "Failed to get device\n");
        return -ENODEV;
    }
    // 注册热插拔操作回调函数
    register_hotplug_notifier(dev, my_hotplug);
    printk(KERN_INFO "Hotplug notifier registered successfully\n");
    return 0;
}

// 模块退出函数
static void __exit hotplug_exit(void) {
    struct device *dev;
    dev = get_device(...); 
    if (dev) {
        // 注销热插拔操作回调函数
        unregister_hotplug_notifier(dev, my_hotplug);
        printk(KERN_INFO "Hotplug notifier unregistered successfully\n");
    }
}

module_init(hotplug_init);
module_exit(hotplug_exit);
MODULE_LICENSE("GPL");

6.8 platform 设备驱动模型

platform 设备驱动模型是一种用于连接系统设备和驱动的抽象模型,常用于嵌入式系统中,设备和驱动通过平台总线进行匹配。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/platform_device.h>

// 定义一个平台设备资源结构体
static struct resource my_platform_device_resources[] = {
    [0] = {
     .start = 0x10000000,
     .end = 0x10000fff,
     .flags = IORESOURCE_MEM,
    },
    [1] = {
     .start = IRQ_NUM, // 假设已定义 IRQ_NUM
     .end = IRQ_NUM,
     .flags = IORESOURCE_IRQ,
    },
};

// 定义一个虚拟的平台设备结构体
static struct platform_device my_platform_device = {
 .name = "my_platform_device",
 .id = -1,
 .num_resources = ARRAY_SIZE(my_platform_device_resources),
 .resource = my_platform_device_resources,
};

// 定义一个平台驱动结构体
static struct platform_driver my_platform_driver = {
 .probe = my_probe,
 .remove = my_remove,
 .driver = {
     .name = "my_platform_driver",
    },
};

// 平台驱动的 probe 函数,当设备和驱动匹配时被调用
static int my_probe(struct platform_device *pdev) {
    struct resource *res;
    // 获取设备的内存资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        printk(KERN_ERR "Failed to get memory resource\n");
        return -ENXIO;
    }
    // 获取设备的中断资源
    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (!res) {
        printk(KERN_ERR "Failed to get IRQ resource\n");
        return -ENXIO;
    }
    printk(KERN_INFO "my_platform_driver: Probing device %s\n", pdev->name);
    return 0;
}

// 平台驱动的 remove 函数,当设备从系统移除时被调用
static int my_remove(struct platform_device *pdev) {
    printk(KERN_INFO "my_platform_driver: Removing device %s\n", pdev->name);
    return 0;
}

// 模块初始化函数
static int __init platform_driver_init(void) {
    int ret;
    // 注册平台设备
    ret = platform_device_register(&my_platform_device);
    if (ret) {
        printk(KERN_ERR "Failed to register platform device\n");
        return ret;
    }
    // 注册平台驱动
    ret = platform_driver_register(&my_platform_driver);
    if (ret) {
        platform_device_unregister(&my_platform_device);
        printk(KERN_ERR "Failed to register platform driver\n");
        return ret;
    }
    printk(KERN_INFO "Platform device and driver registered successfully\n");
    return 0;
}

// 模块退出函数
static void __exit platform_driver_exit(void) {
    platform_driver_unregister(&my_platform_driver);
    platform_device_unregister(&my_platform_device);
    printk(KERN_INFO "Platform device and driver unregistered successfully\n");
}

module_init(platform_driver_init);
module_exit(platform_driver_exit);
MODULE_LICENSE("GPL");

以上代码详细展示了 Linux 设备驱动模型各个部分的基本实现和使用方法,实际应用中会根据具体需求进行更复杂的扩展和优化。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐