38、Linux内核中的NVMEM框架与看门狗设备驱动解析

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

Linux内核中的NVMEM框架与看门狗设备驱动解析

1. NVMEM框架概述

NVMEM(Non-Volatile Memory)框架在Linux内核中扮演着重要角色,它涉及到生产者和消费者两个方面。以下是一段使用NVMEM框架读取校准值和温度等级值的代码示例:

int ret; 
u32 val;
ret = nvmem_cell_read_u32(&pdev->dev, "calib", &val);
if (ret)
    return ret;
ret = imx_init_calib(pdev, val);
if (ret)
    return ret;
ret = nvmem_cell_read_u32(&pdev->dev, "temp_grade", &val);
if (ret)
    return ret;
imx_init_temp_grade(pdev, val);
return 0;

此代码首先读取校准值,调用 imx_init_calib 函数进行校准初始化;接着读取温度等级值,调用 imx_init_temp_grade 函数进行温度等级初始化。

1.1 NVMEM在用户空间的使用

NVMEM用户空间接口依赖于sysfs,与大多数内核框架类似。每个注册到系统的NVMEM设备都会在 /sys/bus/nvmem/devices 下创建一个目录项,并在该目录下创建一个 nvmem 二进制文件,代表设备的内存。其完整路径模式为 /sys/bus/nvmem/devices/<dev-name>X/nvmem ,其中 <dev-name> 由生产者驱动提供的 nvmem_config.name 指定。以下代码展示了NVMEM核心如何构建 <dev-name>X 模式:

int rval;
rval = ida_simple_get(&nvmem_ida, 0, 0, GFP_KERNEL);
nvmem->id = rval;
if (config->id == -1 && config->name) {
    dev_set_name(&nvmem->dev, "%s", config->name);
} else {
    dev_set_name(&nvmem->dev, "%s%d", config->name ? : "nvmem",
                 config->name ? config->id : nvmem->id);
}

不同情况下的命名规则如下:
| 条件 | 命名规则 |
| ---- | ---- |
| nvmem_config->id == -1 | 仅使用 nvmem_config->name 命名sysfs目录项 |
| nvmem_config->id != -1 nvmem_config->name 已设置 | 使用 nvmem_config->name nvmem_config->id 命名 |
| nvmem_config->name 未设置 | 使用 nvmem 和生成的ID命名 |

需要注意的是,无论定义了哪些单元,NVMEM框架通过NVMEM二进制文件暴露完整的寄存器空间,而非单元。从用户空间访问单元需要事先知道其偏移量和大小。用户可以使用 hexdump cat 命令读取NVMEM内容,例如:

cat /sys/bus/nvmem/devices/2-00550/nvmem
echo "foo" > /sys/bus/nvmem/devices/2-00550/nvmem
cat /sys/bus/nvmem/devices/2-00550/nvmem

2. 看门狗设备驱动简介

看门狗是一种硬件(有时由软件模拟)设备,旨在确保给定系统的可用性。它通常是一个具有合理超时时间的定时器,运行在受监控系统上的软件应定期刷新该定时器。如果软件在定时器到期前停止或未能刷新它(且未明确关闭它),将触发整个系统的(硬件)复位。这种机制甚至有助于从内核崩溃中恢复。

2.1 技术要求

在学习看门狗设备驱动之前,需要具备以下条件:
- C编程技能
- 基本电子知识
- Linux内核v4.19.X源码,可从 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/refs/tags 获取

2.2 看门狗数据结构和API

2.2.1 struct watchdog_device

这是Linux内核中看门狗设备的表示,包含了关于该设备的所有信息,定义在 include/linux/watchdog.h 中:

struct watchdog_device {
    int id;
    struct device *parent;
    const struct watchdog_info *info;
    const struct watchdog_ops *ops;
    const struct watchdog_governor *gov;
    unsigned int bootstatus;
    unsigned int timeout;
    unsigned int pretimeout;
    unsigned int min_timeout;
    struct watchdog_core_data *wd_data;
    unsigned long status;
    [...]
};

各字段含义如下:
| 字段 | 含义 |
| ---- | ---- |
| id | 内核在设备注册期间分配的看门狗ID |
| parent | 该设备的父设备 |
| info | 指向 struct watchdog_info 结构的指针,提供看门狗定时器的额外信息 |
| ops | 指向看门狗操作列表的指针 |
| gov | 指向看门狗预超时调节器的指针 |
| bootstatus | 看门狗设备在启动时的状态,是触发系统复位原因的位掩码 |
| timeout | 看门狗设备的超时值(秒) |
| pretimeout | 预超时事件触发的时间间隔(秒) |
| min_timeout max_timeout | 看门狗设备的最小和最大超时值(秒) |
| wd_data | 指向看门狗核心内部数据的指针 |
| status | 包含设备内部状态位的字段 |

2.2.2 struct watchdog_info

定义在 include/uapi/linux/watchdog.h 中,是用户空间API的一部分,也是 WDIOC_GETSUPPORT ioctl成功时返回给用户空间的结构:

struct watchdog_info {
    u32 options;
    u32 firmware_version;
    u8 identity[32];
};

各字段含义如下:
| 字段 | 含义 |
| ---- | ---- |
| options | 卡/驱动支持的功能,是看门狗设备/驱动支持功能的位掩码 |
| firmware_version | 卡的固件版本 |
| identity | 描述设备的字符串 |

2.2.3 struct watchdog_ops

包含了允许对看门狗设备执行的操作列表:

struct watchdog_ops { 
    struct module *owner;
    /* mandatory operations */
    int (*start)(struct watchdog_device *);
    int (*stop)(struct watchdog_device *);
    /* optional operations */
    int (*ping)(struct watchdog_device *);
    unsigned int (*status)(struct watchdog_device *);
    int (*set_timeout)(struct watchdog_device *, unsigned int);
    int (*set_pretimeout)(struct watchdog_device *, unsigned int);
    unsigned int (*get_timeleft)(struct watchdog_device *);
    int (*restart)(struct watchdog_device *, unsigned long, void *);
    long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
};

各操作含义如下:
| 操作 | 含义 |
| ---- | ---- |
| start stop | 分别启动和停止看门狗的强制操作 |
| ping | 向看门狗发送保持活动信号的回调函数,可选 |
| status | 返回看门狗设备状态的可选例程 |
| set_timeout | 设置看门狗超时值(秒)的回调函数 |
| set_pretimeout | 设置预超时的回调函数 |
| get_timeleft | 返回复位前剩余秒数的可选操作 |
| restart | 重启机器的例程 |
| ioctl | 处理额外/非标准ioctl命令的回调函数,可选 |

2.3 看门狗设备的注册与注销

看门狗框架提供了两个基本函数来注册和注销看门狗设备:

int watchdog_register_device(struct watchdog_device *wdd)
void watchdog_unregister_device(struct watchdog_device *wdd)

watchdog_register_device 在成功时返回零,失败时返回负错误码; watchdog_unregister_device 执行相反的操作。为了避免手动注销,可以使用托管版本的函数 devm_watchdog_register_device

int devm_watchdog_register_device(struct device *dev, struct watchdog_device *wdd)

注册方法会检查 wdd->ops->restart 函数是否提供,并将其注册为重启处理程序。因此,在向系统注册看门狗设备之前,驱动程序应使用 watchdog_set_restart_priority 设置重启优先级,优先级值的指导原则如下:
| 优先级值 | 含义 |
| ---- | ---- |
| 0 | 最低优先级,将看门狗的重启函数作为最后手段 |
| 128 | 默认优先级,若没有其他处理程序可用,则默认使用该重启处理程序 |
| 255 | 最高优先级,抢占所有其他处理程序 |

以下是一个将 watchdog_device 结构嵌入更大结构的示例:

struct imx2_wdt_device {
    struct clk *clk;
    struct regmap *regmap;
    struct watchdog_device wdog;
    bool ext_reset;
};

以下是初始化和注册看门狗设备的示例代码:

static int init imx2_wdt_probe(struct platform_device *pdev)
{
    struct imx2_wdt_device *wdev;
    struct watchdog_device *wdog; 
    int ret;
    wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
    if (!wdev)
        return -ENOMEM;
    wdog = &wdev->wdog;
    if (imx2_wdt_is_running(wdev)) {
        imx2_wdt_set_timeout(wdog, wdog->timeout); 
        set_bit(WDOG_HW_RUNNING, &wdog->status);
    }
    ret = watchdog_register_device(wdog);
    if (ret) {
        dev_err(&pdev->dev, "cannot register watchdog device\n");
    }
    return 0;
}

以下是注销看门狗设备的示例代码:

static int exit imx2_wdt_remove(struct platform_device *pdev)
{
    struct watchdog_device *wdog = platform_get_drvdata(pdev);
    struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
    watchdog_unregister_device(wdog);
    if (imx2_wdt_is_running(wdev)) {
      imx2_wdt_ping(wdog);
      dev_crit(&pdev->dev, "Device removed: Expect reboot!\n");
    }
    return 0;
}

通过以上内容,我们对NVMEM框架和看门狗设备驱动有了较为全面的了解。NVMEM框架方便了非易失性内存的管理和使用,而看门狗设备驱动则为系统的可靠性提供了保障。在实际应用中,我们可以根据具体需求合理运用这些技术。

3. 看门狗设备驱动的深入理解

3.1 看门狗状态位详解

struct watchdog_device 中的 status 字段包含了设备的内部状态位,以下是各状态位的详细解释:
| 状态位 | 含义 |
| ---- | ---- |
| WDOG_ACTIVE | 表示看门狗是否正在运行/激活 |
| WDOG_NO_WAY_OUT | 指示是否设置了“无退出”功能,可以使用 watchdog_set_nowayout 函数设置 |
| WDOG_STOP_ON_REBOOT | 表示在重启时应停止看门狗 |
| WDOG_HW_RUNNING | 表示硬件看门狗正在运行,可以使用 watchdog_hw_running 函数检查该标志是否设置 |
| WDOG_STOP_ON_UNREGISTER | 表示在注销时应停止看门狗,可以使用 watchdog_stop_on_unregister 函数设置该标志 |

3.2 看门狗功能标志分析

struct watchdog_info 中的 options 字段是一个位掩码,代表了看门狗设备/驱动支持的功能。以下是各标志的详细说明:
| 标志 | 含义 |
| ---- | ---- |
| WDIOF_SETTIMEOUT | 表示看门狗设备可以设置超时值,若设置该标志,则必须定义 set_timeout 回调函数 |
| WDIOF_MAGICCLOSE | 表示驱动支持“魔法关闭”功能,即向看门狗字符设备文件写入 V 字符序列可允许下一次关闭操作关闭看门狗 |
| WDIOF_POWERUNDER | 表示设备可以监测/检测电源故障,在 watchdog_device.bootstatus 中设置该标志表示系统因欠压而复位 |
| WDIOF_POWEROVER | 表示设备可以监测工作电压,在 watchdog_device.bootstatus 中设置该标志表示系统因过压而复位 |
| WDIOF_OVERHEAT | 表示看门狗设备可以监测芯片/SoC 温度,在 watchdog_device.bootstatus 中设置该标志表示系统因过热而复位 |
| WDIOF_FANFAULT | 表示该看门狗设备可以监测风扇,设置该标志表示风扇故障 |
| WDIOF_EXTERN1 WDIOF_EXTERN2 | 表示设备有外部事件输入,在 watchdog_device.bootstatus 中设置该标志表示系统因外部继电器/源 1 或 2 而复位 |
| WDIOF_PRETIMEOUT | 表示该看门狗设备支持预超时功能 |
| WDIOF_KEEPALIVEPING | 表示驱动支持 WDIOC_KEEPALIVE ioctl,在 watchdog_device.bootstatus 中设置该标志表示看门狗自上次查询以来收到了保持活动信号 |
| WDIOF_CARDRESET | 是一个特殊标志,仅出现在 watchdog_device.bootstatus 中,表示系统最后一次重启是由看门狗本身(超时)引起的 |

3.3 看门狗设备驱动开发流程

下面通过 mermaid 流程图展示看门狗设备驱动的开发流程:

graph TD
    A[分配内存空间] --> B[初始化 watchdog_device 结构]
    B --> C[设置 info、ops 和超时相关字段]
    C --> D[设置重启优先级]
    D --> E[注册看门狗设备]
    E --> F[设备正常运行]
    F --> G{是否需要注销?}
    G -- 是 --> H[注销看门狗设备]
    G -- 否 --> F

具体步骤如下:
1. 分配内存空间 :为 watchdog_device 结构分配内存,可以将其封装在更大的驱动特定数据结构中。
2. 初始化 watchdog_device 结构 :设置 id parent info ops 等字段。
3. 设置 info ops 和超时相关字段 :提供有效的 watchdog_info watchdog_ops 数据结构,设置 timeout pretimeout 等字段。
4. 设置重启优先级 :使用 watchdog_set_restart_priority 函数设置重启处理程序的优先级。
5. 注册看门狗设备 :使用 watchdog_register_device devm_watchdog_register_device 函数将设备注册到系统中。
6. 设备正常运行 :在设备运行过程中,根据需要调用 start stop ping 等操作。
7. 注销看门狗设备 :当不再需要设备时,使用 watchdog_unregister_device 函数注销设备。

4. 总结

本文详细介绍了 Linux 内核中的 NVMEM 框架和看门狗设备驱动。NVMEM 框架通过 sysfs 接口方便了用户空间对非易失性内存的访问,用户可以使用 hexdump cat 命令读取和写入 NVMEM 内容。看门狗设备驱动则为系统的可靠性提供了保障,通过定期刷新定时器来防止系统因软件故障而死机。

在开发看门狗设备驱动时,需要熟悉 struct watchdog_device struct watchdog_info struct watchdog_ops 等数据结构,掌握看门狗设备的注册和注销方法,设置合适的重启优先级。同时,要理解看门狗状态位和功能标志的含义,根据设备的实际需求进行配置。

通过合理运用 NVMEM 框架和看门狗设备驱动,可以提高系统的稳定性和可靠性,满足不同应用场景的需求。在实际开发中,还可以根据具体情况进行优化和扩展,以实现更加高效和灵活的系统设计。

内容概要:本文提出了一种基于变分模态分解(VMD)、卷积神经网络(CNN)和双向长短期记忆网络(BiLSTM)的混合深度学习模型,用于轴承故障诊断研究,采用西储大学公开的轴承振动数据集进行实验验证。首先利用VMD对原始振动信号进行自适应分解,得到一系列本征模态分量(IMFs),以有效提取信号的局部时频特征并抑制噪声干扰;随后,通过CNN自动提取各模态分量的空间特征,捕捉局部故障模式;最后,将提取的特征输入BiLSTM网络,充分挖掘序列数据中的长期依赖关系,实现对不同故障类型程度的精准分类。整个方法充分发挥了VMD在信号预处理方面的优势以及深度学习模型在特征提取分类上的强大能力,显著提升了故障识别的准确率鲁棒性。; 适合人群:具备信号处理深度学习基础知识,从事机械故障诊断、智能制造、工业大数据分析等相关领域的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于旋转机械设备的状态监测早期故障预警;②为工业4.0背景下智能运维系统提供核心技术支持;③目标是提升复杂工况下轴承故障诊断的自动化水平准确性,克服传统方法对人工特征依赖性强、泛化能力差的问题; 阅读建议:建议读者结合Matlab代码实现过程,深入理解VMD参数设置、CNN-BiLSTM网络结构设计及模型训练调优的关键步骤,并尝试在不同数据集或实际采样环境中进行迁移验证,以全面掌握该方法的应用技巧改进空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值