找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1902

积分

0

好友

252

主题
发表于 2 小时前 | 查看: 3| 回复: 0

相信很多从事嵌入式或驱动开发的工程师,尤其是新手,都会产生这样一个疑问:当我们的 Linux 系统(例如一块开发板)启动时,板载的 LED 驱动、按键驱动甚至是复杂的触屏驱动,为什么都能自动加载起来,而无需我们手动执行 insmod driver.ko 命令?

更有趣的是,内核是如何知道这些硬件存在的?它怎么找到对应的驱动?又是如何确保驱动加载后立刻去驱动那个硬件呢?

这篇文章将以 Linux 6.1 + Orange Pi 5 Plus(RK3588) 为具体的案例,逐步揭开这层迷雾,带大家理解 Linux 驱动自动加载的完整机制

1. 核心概念与工作流程

1.1 三个关键角色

在驱动自动加载的“故事”里,有三个主要角色,它们共同协作完成了这场“自动化”表演:

1. 设备树(Device Tree)

  • 一个 .dts 文件编译成的 .dtb 二进制文件。
  • 它的核心作用,是向内核描述 Orange Pi 5 Plus 这块板子上有哪些硬件设备、它们连接在哪个总线上、以及各自使用什么资源(如 GPIO 引脚、中断号、时钟等)。
  • 简单说,它负责告诉内核:“看,GPIO3_A6 这个引脚上接了一个蓝色 LED”,“GPIO3_B1 上接了一个绿色 LED”。

2. 驱动程序(Driver)

  • 一个内核模块或内置驱动。
  • 它包含了操作硬件的具体代码逻辑。
  • 更关键的是,它明确声明了自己能驱动什么样的硬件,这是通过 compatible 字段实现的。
  • 例如,GPIO LED 驱动会声明:“我能驱动所有 compatible 属性为 gpio-leds 的硬件”。

3. 内核中的设备驱动框架(Bus、Device、Driver)

  • 这是扮演“中间人”或“红娘”的角色。
  • 它的职责包括扫描硬件、加载驱动、以及最终将合适的“设备”与“驱动”进行配对。
  • 你可以把它想象成一个“婚配服务器”,确保正确的设备找到了正确的驱动。

1.2 自动加载的完整流程(以 Orange Pi 5 Plus 为例)

让我们勾勒一个 Orange Pi 5 Plus 启动时的真实场景:

【系统启动】
    ↓
【设备树被加载并解析】→ 内核扫描 rk3588-orangepi-5-plus.dtb
    ↓
    ├─ Device 1: gpio-leds, compatible = “gpio-leds”
    ├─ Device 2: pwm-fan, compatible = “pwm-fan”
    ├─ Device 3: sfc (SPI Flash), compatible = “jedec,spi-nor”
    ├─ Device 4: remotectl, compatible = “rockchip,remotectl-pwm”
    └─ Device 5: GPIO3, compatible = “rockchip,rk3588-gpio”
    ↓
【内核驱动模块被加载】→ 驱动注册自己到内核
    ↓
    ├─ Driver A: compatible = “gpio-leds”
    ├─ Driver B: compatible = “pwm-fan”
    ├─ Driver C: compatible = “jedec,spi-nor”
    ├─ Driver D: compatible = “rockchip,remotectl-pwm”
    └─ Driver E: compatible = “rockchip,rk3588-gpio”
    ↓
【内核进行配对】→ 匹配 compatible 字符串
    ↓
    ├─ gpio-leds device + gpio-leds driver ✓ 匹配!→ 调用 probe()
    ├─ pwm-fan device + pwm-fan driver ✓ 匹配!→ 调用 probe()
    ├─ spi-nor device + jedec-spi-nor driver ✓ 匹配!→ 调用 probe()
    └─ … 其他设备
    ↓
【驱动 probe() 执行】→ 驱动被激活,硬件被初始化
    ↓
【驱动开始工作】

1.3 这个过程的关键词

整个过程的核心,其实是一场基于字符串的精确匹配

设备树里的硬件节点说:“我的 compatiblegpio-leds”。
驱动程序在注册时说:“我的 of_match_table 里包含 gpio-leds”。
内核的设备驱动框架这个“红娘”一看,字符串完全一致,便拍板决定:“你俩配对成功,开始工作吧!”。

这就是 Linux 驱动框架 的核心设计理念之一:约定优于配置,通过标准的属性字符串来解耦硬件描述与驱动实现。

2. 设备树分析

2.1 设备树顶层结构

我们从 Orange Pi 5 Plus 官方的设备树文件 rk3588-orangepi-5-plus.dts 看起:

// arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts
/dts-v1/;

#include “rk3588-orangepi-5-plus.dtsi”
#include “rk3588-linux.dtsi”
#include “rk3588-orangepi-5-plus-lcd.dtsi”
#include “rk3588-orangepi-5-plus-camera1.dtsi”

/ {
    model = “RK3588 OPi 5 Plus”;
    compatible = “rockchip,rk3588-orangepi-5-plus”, “rockchip,rk3588”; // ← 关键!
};

关键点解析:

  1. 顶级 compatible 字符串
    compatible = “rockchip,rk3588-orangepi-5-plus”, “rockchip,rk3588”;
    这行信息向内核传达了两层意思(优先级从左到右递减):
    • 首选:这是一块 Rockchip 公司的 rk3588-orangepi-5-plus 特定开发板。
    • 备选:如果没有找到上面那个最匹配的专用驱动,那就把它当作一个通用的 RK3588 芯片来处理。
  2. 包含关系
    • rk3588-orangepi-5-plus.dtsi – 定义了 Orange Pi 5 Plus 特有的硬件。
    • rk3588-linux.dtsi – 包含了 Linux 系统下 RK3588 芯片的通用配置。
    • rk3588-orangepi-5-plus-lcd.dtsi – 特定 LCD 显示屏的配置。
    • rk3588-orangepi-5-plus-camera1.dtsi – 摄像头的配置。

2.2 GPIO LED 设备定义

设备树中,LED 设备是这样定义的:

./arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dts
leds: gpio-leds {
    compatible = “gpio-leds”;                    // ← 关键:告诉内核这是 GPIO LED
    pinctrl-names = “default”;
    pinctrl-0 = <&leds_rgb>;
    status = “okay”;                             // ← 启用这个设备

    blue_led@1 {
        gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>; // ← GPIO3 的第 6 号 pin (A 组)
        label = “blue_led”;
        linux,default-trigger = “heartbeat”;       // ← LED 闪烁方式:心跳
        linux,default-trigger-delay-ms = <0>;
    };

    green_led@2 {
        gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>; // ← GPIO3 的第 9 号 pin (B 组)
        label = “green_led”;
        linux,default-trigger = “heartbeat”;
        linux,default-trigger-delay-ms = <0>;
    };
};

设备树中LED与PWM风扇配置代码截图

解析:

  1. compatible = “gpio-leds”
    • 这是 Linux 内核为 GPIO 连接的 LED 设备定义的标准兼容性字符串。
    • 内核源码中已经有一个现成的驱动 drivers/leds/leds-gpio.c 就是用来匹配和驱动它的。
  2. GPIO 引脚编号
    • &gpio3 – 引用了 GPIO3 这个控制器。
    • RK_PA6 – 表示 A 组第 6 号引脚(编号从0开始)。
    • RK_PB1 – 表示 B 组第 1 号引脚。
    • 换算成 Linux 内核的 GPIO 编号(公式:组号*32 + 组内基址 + 引脚号):
      • GPIO3_A6 = 3×32 + 0×8 + 6 = 102
      • GPIO3_B1 = 3×32 + 1×8 + 1 = 105
  3. pinctrl 配置
    pinctrl-0 = <&leds_rgb>;
    这行告诉系统的 pinctrl(引脚控制)子系统,需要把 leds_rgb 这个引脚组配置为 GPIO 输出模式。

2.3 Pinctrl 配置

在设备树的 pinctrl 部分,我们可以找到 leds_rgb 的具体定义:

&pinctrl {
    leds_gpio {
        leds_rgb: leds-rgb {
            rockchip,pins = <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>,
                            <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>;
        };
    };
};

解析:

  1. rockchip,pins – 指定要配置的物理引脚。
  2. RK_FUNC_GPIO – 将这些引脚的功能设置为 GPIO(而不是其他复用功能如 I2C、UART等)。
  3. &pcfg_pull_up – 配置为上拉模式。

2.4 PWM 风扇驱动

设备树中还定义了一个 PWM 风扇,这同样是驱动自动加载的绝佳例子:

fan: pwm-fan {
    compatible = “pwm-fan”;                     // ← 标准 PWM 风扇驱动
    #cooling-cells = <2>;
    pwms = <&pwm3 0 50000 0>;                 // ← 使用 PWM3
    cooling-levels = <0 50 100 150 200 255>;    // ← 风扇速度等级
    rockchip,temp-trips = <
        50000   1    // 温度 50°C,风扇速度等级 1
        55000   2    // 温度 55°C,风扇速度等级 2
        60000   3    // 温度 60°C,风扇速度等级 3
        65000   4    // 温度 65°C,风扇速度等级 4
        70000   5    // 温度 70°C,风扇速度等级 5
    >;
    status = “okay”;
};

这个设备同样会被自动驱动:

  1. 内核解析设备树时,发现一个 compatible = “pwm-fan” 的设备。
  2. 在内核驱动中搜索能匹配 “pwm-fan” 的驱动。
  3. 找到 drivers/hwmon/pwm-fan.c 这个驱动。
  4. 调用该驱动的 probe() 函数。
  5. 风扇驱动被激活,开始根据温度监控并自动控制风扇转速。

2.5 红外遥控驱动

设备树中还有一个红外遥控的配置,它展示了一个芯片厂商特定的驱动:

&pwm15 {
    compatible = “rockchip,remotectl-pwm”;      // ← Rockchip 特定驱动
    pinctrl-names = “default”;
    pinctrl-0 = <&pwm15m1_pins>;
    remote_pwm_id = <3>;
    handle_cpu_id = <1>;
    remote_support_psci = <0>;
    status = “okay”;

    ir_key1 {
        rockchip,usercode = <0xfb04>;           // ← 遥控器用户码
        rockchip,key_table = <
            0xa3   KEY_ENTER,
            0xe4   388,
            0xf5   KEY_BACK,
            0xbb   KEY_UP,
            // … 更多按键映射
            0xb2   KEY_POWER,
            // … 等等
        >;
    };
};

这是一个 Rockchip 特定 的远程控制驱动(并非通用的 gpio-ir-receiver 驱动)。它的 compatible 字符串 “rockchip,remotectl-pwm” 会精确匹配 Rockchip 提供的内核驱动。

3. 驱动加载实现

3.1 GPIO LED 驱动源码(drivers/leds/leds-gpio.c)

让我们看看驱动是如何声明自己并响应匹配的。以下是简化版的 GPIO LED 驱动关键代码:

#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/leds.h>

// 1. 驱动能驱动什么硬件 → of_match_table(设备树匹配表)
static const struct of_device_id of_gpio_leds_match[] = {
    { .compatible = “gpio-leds” },    // ← 关键:声明能驱动 “gpio-leds”
    {}
};
MODULE_DEVICE_TABLE(of, of_gpio_leds_match);

// 2. Probe 函数:匹配成功后被内核调用
static int gpio_leds_probe(struct platform_device *pdev) {
    struct device *dev = &pdev->dev;
    struct device_node *np = dev->of_node;  // ← 获取对应的设备树节点
    struct device_node *child;
    int count = 0;

    dev_info(dev, “GPIO LED driver probe called!\n”);

    // 遍历设备树中此节点下的每个子节点(如 blue_led@1, green_led@2)
    for_each_child_of_node(np, child) {
        const char *label;
        int ret;

        // 读取 label 属性:”blue_led” 或 “green_led”
        label = of_get_property(child, “label”, NULL);

        // 获取 GPIO 引脚描述符(Linux 6.1 推荐做法)
        struct gpio_desc *gpiod = fwnode_gpiod_get_index(
            of_fwnode_handle(child), “gpios”, 0, GPIOD_OUT_LOW, label);

        if (IS_ERR(gpiod)) {
            dev_err(dev, “failed to get GPIO for %s\n”, label);
            continue;
        }

        dev_info(dev, “Registered LED %s\n”, label);
        count++;
    }

    dev_info(dev, “GPIO LED driver probed with %d LEDs\n”, count);
    return 0;
}

// 3. Remove 函数
static int gpio_leds_remove(struct platform_device *pdev) {
    dev_info(&pdev->dev, “GPIO LED driver removed\n”);
    return 0;
}

// 4. 驱动结构体:注册驱动到内核
static struct platform_driver gpio_led_driver = {
    .probe  = gpio_leds_probe,
    .remove = gpio_leds_remove,
    .driver = {
        .name = “leds-gpio”,
        .of_match_table = of_gpio_leds_match,    // ← 设备树匹配表,这是核心!
        .owner = THIS_MODULE,
    }
};

// 5. 驱动注册宏
module_platform_driver(gpio_led_driver);

MODULE_AUTHOR(“Kernel LED Authors”);
MODULE_DESCRIPTION(“GPIO LED driver for Linux”);
MODULE_LICENSE(“GPL”);

3.2 系统启动时的实际情况

当 Orange Pi 5 Plus 启动 Linux 6.1 时,幕后发生了这些事情:

第 1 步:U-Boot 加载设备树

U-Boot 从 eMMC/SPI Flash 启动
  ↓
加载 Linux 内核映像
  ↓
加载 rk3588-orangepi-5-plus.dtb 设备树文件
  ↓
将 .dtb 文件在内存中的地址传递给 Linux 内核

第 2 步:Linux 内核解析设备树

Linux 内核初始化
  ↓
设备树解析器读取 .dtb 文件内容
  ↓
在内存中创建设备树节点对象
  ↓
扫描所有节点,为那些拥有 `compatible` 属性的节点创建对应的 platform_device
  ├─ gpio-leds 节点 → 创建 platform_device(“leds”)
  ├─ pwm-fan 节点 → 创建 platform_device(“fan”)
  ├─ pwm15 节点 → 创建 platform_device(“remotectl”)
  └─ … 其他设备

第 3 步:驱动注册和配对

leds-gpio.ko 驱动模块初始化(或被编译进内核)
  ↓
驱动调用 platform_driver_register(&gpio_led_driver)
  ↓
驱动的 of_match_table 告诉内核框架:“我能驱动 compatible=’gpio-leds’ 的设备”
  ↓
内核框架立即搜索当前已注册的设备中是否有 compatible 匹配的
  ↓
找到了!platform_device(“leds”) 的 compatible = “gpio-leds”
  ↓
配对成功!✓
  ↓
内核框架调用驱动的 probe() 函数:
  ├─ gpio_leds_probe(&platform_device(“leds”))
  ├─ 遍历子节点:blue_led@1 和 green_led@2
  ├─ 为每个 LED 获取 GPIO 引脚,并配置为输出
  ├─ 将 LED 注册到内核的 LED 子系统
  └─ 在 /sys/class/leds/ 下创建 blue_led 和 green_led 接口

第 4 步:系统启动完毕

所有驱动加载、配对、初始化完毕
  ↓
蓝色 LED 和绿色 LED 开始按照设备树中定义的 “heartbeat” 方式自动闪烁
  ↓
用户现在可以通过 sysfs 文件系统控制 LED:
  $ echo 1 > /sys/class/leds/blue_led/brightness  # 点亮
  $ echo 0 > /sys/class/leds/green_led/brightness # 熄灭

3.3 内核中的配对逻辑(drivers/of/device.c)

匹配的核心逻辑在内核中,简化后的代码如下:

// 这是内核如何进行 compatible 匹配的简化代码
const struct of_device_id *of_match_device(
    const struct of_device_id *matches,
    struct device *dev) {

    // 如果设备没有关联的设备树节点,则无法进行OF匹配
    if (!dev->of_node)
        return NULL;

    // 获取设备树节点中的 compatible 属性字符串
    const char *compatible = of_get_property(dev->of_node,
                                             “compatible”, NULL);
    if (!compatible)
        return NULL;

    // 逐一检查驱动提供的 of_match_table 数组
    while (matches->name[0] || matches->compatible[0]) {
        // 比较设备与驱动的 compatible 字符串
        if (!of_compat_cmp(compatible, matches->compatible,
                           strlen(matches->compatible))) {
            return matches;  // ✓ 匹配成功!
        }
        matches++;
    }

    return NULL;  // ✗ 没有驱动能驱动这个设备
}

核心就是这一行比较:

if (!of_compat_cmp(compatible, matches->compatible, …))

字符串比较!当设备树节点中的 compatible 属性字符串,与驱动 of_match_table 中声明的某个字符串完全相同时,内核就判定匹配成功,并启动后续的驱动加载流程。这整个机制深刻体现了 操作系统 对硬件资源管理和抽象的统一思想。

4. 实战验证

4.1 验证 LED 驱动是否自动加载

在你的 Orange Pi 5 Plus 上,可以通过以下命令验证整个过程:

# 1. 查看 LED 设备是否被成功识别并创建了用户空间接口
$ ls /sys/class/leds/
blue_led  green_led

# 2. 查看某个 LED 的当前亮度
$ cat /sys/class/leds/blue_led/brightness
0    # 当前亮度值(范围通常是 0-255)

# 3. 查看 LED 当前的触发模式(trigger),方括号[]内是当前激活的模式
$ cat /sys/class/leds/blue_led/trigger
none [heartbeat] timer oneshot transient none

# 4. 查看已加载的内核模块,确认 leds_gpio 驱动已加载
$ lsmod | grep leds
leds_gpio              8192  2

# 5. 查看内核启动日志,寻找驱动初始化的证据
$ dmesg | grep -i “gpio.*led\|leds.*gpio”
[    0.234567] leds-gpio: GPIO LED driver probe called!
[    0.234890] leds-gpio: Registered LED blue_led
[    0.235123] leds-gpio: Registered LED green_led
[    0.235234] leds-gpio: GPIO LED driver probed with 2 LEDs

# 6. 直接从内核映射的设备树信息中查看原始数据
$ cat /proc/device-tree/leds/compatible
gpio-leds

$ cat /proc/device-tree/leds/blue_led@1/label
blue_led

# 7. 手动控制 LED(让蓝色 LED 常亮)
$ echo 1 > /sys/class/leds/blue_led/brightness

# 8. 关闭 LED
$ echo 0 > /sys/class/leds/blue_led/brightness

4.2 验证风扇驱动

同样,可以验证 PWM 风扇驱动:

# 查看系统中的硬件监控(hwmon)设备
$ ls /sys/class/hwmon/
hwmon0  hwmon1  …

# 找出哪个是 pwm-fan
$ cat /sys/class/hwmon/hwmon*/name
pwm-fan  # 找到它了,假设是 hwmon0

# 查看当前的 PWM 风扇速度(通常对应 pwm1)
$ cat /sys/class/hwmon/hwmon0/pwm1
# 输出:一个 0-255 之间的值,代表风扇速度等级

# 查看当前 SoC 温度(单位:毫摄氏度)
$ cat /sys/class/hwmon/hwmon0/temp1_input
45000  # 表示 45.0°C

# 在内核日志中搜索风扇初始化信息
$ dmesg | grep -i pwm-fan
[    0.456789] pwm-fan: PWM fan initialized
[    0.457012] pwm-fan: Cooling device registered

5. 常见问题与调试

问题 1:LED 亮了,但闪烁方式不是我想要的

可能的原因:

  • 设备树中 default-trigger 属性设置错误或不符合预期。
  • 内核中对应的 trigger 驱动(如 leds-trigger-heartbeat)没有被编译或加载。

调试步骤:

# 查看该 LED 所有可用的触发模式
$ cat /sys/class/leds/blue_led/trigger
none [heartbeat] timer oneshot transient none

# 手动切换到 ‘timer’ 定时闪烁模式
$ echo timer > /sys/class/leds/blue_led/trigger

# 设置闪烁参数:亮1秒,灭1秒
$ echo 1000 > /sys/class/leds/blue_led/delay_on
$ echo 1000 > /sys/class/leds/blue_led/delay_off

问题 2:驱动加载了,sysfs 接口也看到了,但 LED 就是不亮

可能的原因:

  1. GPIO 引脚号在设备树中配置错误。
  2. pinctrl 配置错误,引脚功能未设置为 GPIO 输出。
  3. 电平极性搞反(比如设备树中是 GPIO_ACTIVE_HIGH,但实际电路是低电平点亮)。
  4. 硬件问题(LED损坏、限流电阻过大、接线错误等)。

调试步骤:

# 1. 使用 gpio-tools 查看 GPIO 状态
$ sudo apt install gpiod
$ gpioinfo | grep -A2 “gpio3\|PA6\|PB1”  # 查看相关GPIO的状态

# 2. 绕过驱动,直接通过 sysfs GPIO 接口手动测试(需确认GPIO未被占用)
$ echo 102 > /sys/class/gpio/export        # 导出 GPIO3_A6 (编号102)
$ echo out > /sys/class/gpio/gpio102/direction
$ echo 1 > /sys/class/gpio/gpio102/value    # 设置高电平,LED 应该亮
$ echo 0 > /sys/class/gpio/gpio102/value    # 设置低电平,LED 应该灭

# 3. 查看 pinctrl 子系统的调试信息,确认引脚复用配置
$ cat /sys/kernel/debug/pinctrl/pinctrl-rockchip/pingroups | grep leds

问题 3:为什么驱动加载了,但它的 probe() 函数没被调用?

最常见的原因:compatible 字符串不匹配。

# 检查设备树实际传递给内核的 compatible 属性
$ cat /proc/device-tree/leds/compatible
gpio-leds

# 查看内核日志,搜索驱动注册和设备匹配相关信息
$ dmesg | grep “compatible”

# 如果设备树中误写为 “gpio-led” (少了一个s)
# 而驱动中声明的是 “gpio-leds”
# 那么字符串比较就会失败,配对不会发生,probe() 自然不会被调用。

问题 4:如何在不修改驱动代码的情况下,自定义 LED 的行为?

直接修改设备树源文件(.dts)并重新编译即可。例如,改变 LED 的默认触发模式:

blue_led@1 {
    gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>;
    label = “blue_led”;

    // 改为上电常亮模式
    linux,default-trigger = “default-on”;

    // 或者改为定时器闪烁,并设置延迟参数
    // linux,default-trigger = “timer”;
    // linux,default-trigger-delay-ms = <1000>; // 可配合timer使用,但非标准属性,依赖驱动支持
};

总结

Linux 驱动自动加载的机制,其核心思想清晰而优雅:

  1. 设备树定义硬件:通过 compatible 属性给硬件贴上“身份证”。
  2. 驱动声明能力:通过 of_match_table 声明自己能驱动哪类“身份证”的硬件。
  3. 内核框架进行配对:当“身份证” (compatible) 与“能力声明” (of_match_table) 字符串完全匹配时,自动调用驱动的 probe() 函数完成初始化。

整个过程实现了硬件描述与驱动代码的解耦,使得同一份驱动可以用于不同板卡上的相同硬件,而板卡差异仅通过设备树来描述。这正是现代 Linux 内核 支持多样化嵌入式设备的关键机制之一。

希望这篇结合 Orange Pi 5 Plus 实例如深入代码的分析,能帮助你彻底理解 Linux 驱动自动加载的奥秘。如果你对更底层的代码实现感兴趣,可以顺着文中的代码路径,在内核源码中继续探索 platform_driver_registerof_platform_default_populate 等函数的完整调用链。

趣味电池电量表情包


版权说明:本文内容基于 Linux 内核源码及 Orange Pi 官方设备树文件进行分析,旨在技术分享与学习。文中涉及的所有代码示例均来自开源项目。更多深入的嵌入式系统及驱动开发讨论,欢迎关注 云栈社区 的相关板块。




上一篇:FireRed-Image-Edit v1.1发布:攻克ID一致性与复杂融合,4.5秒完成图像编辑
下一篇:OpenClaw插件进阶指南:基于RDS MySQL构建企业级知识库与记忆管理系统
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-11 04:02 , Processed in 0.418360 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

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