驱动开发硬核特训 · Day 21(上篇加强版):深入理解子系统机制与实战初探

发布于:2025-05-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

📅 日期:2025-04-27
📚 技术平台:嵌入式Jerry(B站)


1. 为什么要有子系统?(深度版)

在 Linux 内核发展早期,设备管理较为混乱,每种设备(如网卡、硬盘、LED)各自为政,驱动开发非常困难,系统维护复杂、扩展困难。

在这里插入图片描述

1.1 难点总结

  • 接口不统一:每类设备定义自己的操作方式
  • 用户访问混乱:无法通过统一路径管理设备
  • 内核膨胀严重:各种 if-else、switch-case 代码横行

1.2 子系统带来的变化

✅ 为每一类功能设备定义 统一接口规范
✅ 在 /sys/class/ 下暴露出 统一访问路径
✅ 驱动开发者只需遵循子系统接口,避免重复设计
✅ 设备热插拔、动态加载、统一管理变得简单。


2. 子系统本质是什么?

一句通俗的话:

子系统是内核根据设备功能,把一大堆设备分类管理的机制。

技术上:

  • 基于 kobject/kset/ktype 的设备模型机制
  • 在 sysfs 文件系统中形成 class 目录
  • 提供标准的注册/注销流程

2.1 子系统 VS 设备模型

项目 设备模型 (device model) 子系统 (subsystem)
定义 Linux 内核通用框架 特定类型设备的组织分类
基础 kobject/kset/ktype 基于设备模型构建
目的 支撑总线/驱动/设备统一管理 便于管理一类设备
示例 platform bus, device net, block, regulator

3. 子系统的基本结构和机制

一个子系统通常包括:

  • Class:对应 sysfs 的 /sys/class/,逻辑分组
  • Device:每个实例(一个 LED、一个 BUCK)对应一个 device
  • Driver:驱动设备,通常与特定 bus 匹配

结构图理解:

class_create() -> 创建类 (Class)
device_create() -> 创建设备 (Device)

用户空间访问:
/sys/class/<class>/<device>/

4. 子系统创建与使用详细流程

4.1 创建子系统

内核代码核心步骤:

// 1. 创建一个 Class(子系统)
struct class *my_class;

my_class = class_create(THIS_MODULE, "mychar_class");
if (IS_ERR(my_class))
    return PTR_ERR(my_class);

对应生成 /sys/class/mychar_class/


4.2 注册设备到子系统

// 2. 在子系统下挂一个设备
struct device *dev;

dev = device_create(my_class, NULL, devt, NULL, "mychar");
if (IS_ERR(dev))
    return PTR_ERR(dev);

对应生成 /sys/class/mychar_class/mychar 节点。

同时,udev等工具可自动生成 /dev/mychar 设备文件。


4.3 销毁流程

// 卸载时销毁
device_destroy(my_class, devt);
class_destroy(my_class);

确保模块退出时资源清理,防止内核内存泄漏。


5. 详细实例:基于 i.MX8MP EVK 控制 LED

5.1 设备树节点

imx8mp-evk.dts 中已有:

gpio-leds {
    compatible = "gpio-leds";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_gpio_led>;

    status {
        label = "yellow:status";
        gpios = <&gpio3 16 GPIO_ACTIVE_HIGH>;
        default-state = "on";
    };
};

说明:

  • GPIO3_16 = 32 * 2 + 16 = 80号 GPIO
  • 默认状态点亮

5.2 驱动代码示例(增加丰富注释版)

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>

#define LED_GPIO 80  // gpio3_16

static dev_t devt;
static struct cdev led_cdev;
static struct class *led_class;
static struct device *led_device;

// 写接口,实现开关LED
static ssize_t led_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
    char kbuf[4] = {0};
    if (copy_from_user(kbuf, buf, len))
        return -EFAULT;

    if (kbuf[0] == '1')
        gpio_set_value(LED_GPIO, 1); // 点亮
    else
        gpio_set_value(LED_GPIO, 0); // 熄灭

    return len;
}

// 文件操作结构
static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .write = led_write,
};

static int __init led_init(void)
{
    int ret;

    // 申请GPIO
    ret = gpio_request(LED_GPIO, "led_gpio");
    if (ret)
        return ret;

    gpio_direction_output(LED_GPIO, 1);

    // 分配设备号
    alloc_chrdev_region(&devt, 0, 1, "ledchar");
    cdev_init(&led_cdev, &led_fops);
    cdev_add(&led_cdev, devt, 1);

    // 创建子系统(class)
    led_class = class_create(THIS_MODULE, "led_class");
    led_device = device_create(led_class, NULL, devt, NULL, "ledchar");

    pr_info("ledchar device initialized\n");
    return 0;
}

static void __exit led_exit(void)
{
    gpio_set_value(LED_GPIO, 0);
    gpio_free(LED_GPIO);

    device_destroy(led_class, devt);
    class_destroy(led_class);
    cdev_del(&led_cdev);
    unregister_chrdev_region(devt, 1);

    pr_info("ledchar device removed\n");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

5.3 加载模块效果

# 加载模块
insmod ledchar.ko

# 查看/sys目录
ls /sys/class/led_class/ledchar/

# 控制LED
echo 1 > /dev/ledchar
echo 0 > /dev/ledchar

✅ 通过字符设备 /dev/ledchar 写操作,控制真正硬件上的 GPIO 灯亮灭。
✅ 同时在 /sys/class/led_class/ledchar/ 中可查看设备属性。


6. 深度讲解:子系统、字符设备、设备模型关系总结

类型 作用 示例 本质
字符设备 (cdev) 提供read/write/ioctl接口 /dev/ledchar 用来读写硬件
子系统 (class) 统一组织管理设备 /sys/class/led_class/ 便于分类管理
设备模型 (device model) 内核统一抽象 bus/device/driver 子系统基于它

7. 为什么字符设备要挂子系统?

  • 如果直接裸 cdev:仅 /dev 有节点,sysfs 中无管理
  • 如果挂到子系统
    • /dev 节点
    • /sys/class 路径
    • 更易于热插拔、用户空间管理
    • 可以扩展 attribute 节点(如调节亮度)

所以,规范写法:

字符设备 + 子系统结合,才是完整专业驱动。


8. 小结与今日提炼

✅ 子系统统一管理同类设备,提升 Linux 内核规范性。
✅ 子系统本质上是设备模型的一个应用。
✅ 字符设备可直接使用,也可以挂到子系统,推荐挂载管理。
✅ 电源子系统(regulator)、LED子系统、网卡子系统都是应用范例。


9. 后续学习计划(已升级)

阶段 主题 备注
✅ 阶段一 设备模型 + 字符设备 已完成
✅ 阶段二 子系统概念与应用 今日完成
🔜 阶段三 电源子系统(regulator)深度学习 明日开始

10. 技术交流

💬 更多 Linux 驱动开发教学,请关注 B 站:【嵌入式Jerry