将旧的看门狗驱动程序转换为看门狗框架

作者:Wolfram Sang <wsa@kernel.org>

在看门狗框架进入内核之前,每个驱动程序都必须自行实现其 API。现在,由于该框架提取出了通用组件,这些驱动程序可以得到简化,成为框架的用户。本文档将指导您完成此任务。其中描述了必要的步骤以及需要注意的事项。

移除 file_operations 结构体

旧的驱动程序会为 open()、write() 等操作定义自己的 file_operations……这些现在由框架处理,只在需要时调用驱动程序。因此,一般来说,`file_operations` 结构体和相关函数可以移除。只有极少数驱动程序特定的细节需要移动到其他函数中。下面是函数和可能需要的操作的概述:

  • open:所有与资源管理相关的内容(文件打开检查、魔术关闭准备)都可以直接移除。设备特定的内容需要移至驱动程序特定的启动函数。请注意,对于某些驱动程序,启动函数也兼作心跳函数。如果出现这种情况,并且您需要启动/停止功能平衡(例如时钟!),最好重构一个单独的启动函数。

  • close:与 open 相同的提示也适用。

  • write:可以简单移除,所有已定义的行为都由框架处理,即写入时进行心跳操作和魔术字符('V')处理。

  • ioctl:虽然驱动程序允许对 IOCTL 接口进行扩展,但最常见的操作由框架处理,并得到驱动程序的一些协助

    WDIOC_GETSUPPORT

    返回驱动程序中强制性的 watchdog_info 结构体

    WDIOC_GETSTATUS

    需要定义 status-callback,否则返回 0

    WDIOC_GETBOOTSTATUS

    需要正确设置 bootstatus 成员。如果您没有进一步的支持,请确保它为 0!

    WDIOC_SETOPTIONS

    无需准备

    WDIOC_KEEPALIVE

    如果需要,watchdog_info 中的选项需要设置 WDIOF_KEEPALIVEPING

    WDIOC_SETTIMEOUT

    watchdog_info 中的选项需要设置 WDIOF_SETTIMEOUT,并且必须定义 set_timeout 回调。如果看门狗设备中设置了 min_timeout 和 max_timeout,核心也会进行限制检查。所有这些都是可选的。

    WDIOC_GETTIMEOUT

    无需准备

    WDIOC_GETTIMELEFT

    需要定义 get_timeleft() 回调。否则将返回 EOPNOTSUPP

    其他 IOCTLs 可以使用 ioctl 回调来处理。请注意,这主要用于移植旧驱动程序;新驱动程序不应发明私有 IOCTLs。私有 IOCTLs 会首先处理。当回调返回 -ENOIOCTLCMD 时,框架的 IOCTLs 也会被尝试。任何其他错误将直接返回给用户。

转换示例

-static const struct file_operations s3c2410wdt_fops = {
-       .owner          = THIS_MODULE,
-       .write          = s3c2410wdt_write,
-       .unlocked_ioctl = s3c2410wdt_ioctl,
-       .open           = s3c2410wdt_open,
-       .release        = s3c2410wdt_release,
-};

检查函数中是否有设备特定的内容并保留它们以供后续重构。其余的可以移除。

移除 miscdevice

由于 file_operations 已被移除,您也可以移除 `struct miscdevice`。框架将在 watchdog_register_device() 调用 watchdog_dev_register() 时创建它。

-static struct miscdevice s3c2410wdt_miscdev = {
-       .minor          = WATCHDOG_MINOR,
-       .name           = "watchdog",
-       .fops           = &s3c2410wdt_fops,
-};

移除废弃的头文件和定义

由于简化,现在可能有一些定义不再使用。请移除它们。头文件也可以移除。例如

- #include <linux/fs.h>
- #include <linux/miscdevice.h> (if MODULE_ALIAS_MISCDEV is not used)
- #include <linux/uaccess.h> (if no custom IOCTLs are used)

添加看门狗操作

所有可能的回调都在 `struct watchdog_ops` 中定义。您可以在此目录中的《Linux 看门狗定时器驱动程序核心内核 API》中找到其解释。start() 和 owner 必须设置,其余可选。您将在旧驱动程序中轻松找到相应的函数。请注意,现在您将获得一个指向 watchdog_device 的指针作为这些函数的参数,因此您可能需要更改函数头。其他更改很可能不需要,因为这里只是直接进行硬件访问。如果您在上述步骤中留下了设备特定的代码,则应将其重构到这些回调中。

这是一个简单的例子

+static struct watchdog_ops s3c2410wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = s3c2410wdt_start,
+       .stop = s3c2410wdt_stop,
+       .ping = s3c2410wdt_keepalive,
+       .set_timeout = s3c2410wdt_set_heartbeat,
+};

典型的函数头更改如下所示:

-static void s3c2410wdt_keepalive(void)
+static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
 {
...
+
+       return 0;
 }

...

-       s3c2410wdt_keepalive();
+       s3c2410wdt_keepalive(&s3c2410_wdd);

添加看门狗设备

现在我们需要创建一个 `struct watchdog_device` 并用框架所需的信息填充它。该结构体也在此目录中的《Linux 看门狗定时器驱动程序核心内核 API》中详细解释。我们向它传递强制性的 watchdog_info 结构体和新创建的 watchdog_ops。通常,旧驱动程序使用静态变量来记录启动状态和超时等信息。这些必须转换为使用 watchdog_device 中的成员。请注意,超时值是无符号整数。有些驱动程序使用有符号整数,因此也必须进行转换。

这是一个简单的看门狗设备示例

+static struct watchdog_device s3c2410_wdd = {
+       .info = &s3c2410_wdt_ident,
+       .ops = &s3c2410wdt_ops,
+};

处理 'nowayout' 功能

少数驱动程序静态使用 nowayout,即没有模块参数来控制它,只有 CONFIG_WATCHDOG_NOWAYOUT 决定是否使用该功能。这需要通过如下方式初始化 watchdog_device 的状态变量来进行转换:

.status = WATCHDOG_NOWAYOUT_INIT_STATUS,

然而,大多数驱动程序也允许在运行时配置 nowayout,通常是通过添加模块参数。这方面的转换会是这样的:

watchdog_set_nowayout(&s3c2410_wdd, nowayout);

模块参数本身需要保留,但所有其他与 nowayout 相关的内容都可以移除。这可能是一些在 open()、close() 或 write() 中的代码。

注册看门狗设备

将 misc_register(&miscdev) 替换为 watchdog_register_device(&watchdog_dev)。确保检查返回值,并且错误消息(如果存在)仍然适用。同时转换注销情况。

-       ret = misc_register(&s3c2410wdt_miscdev);
+       ret = watchdog_register_device(&s3c2410_wdd);

...

-       misc_deregister(&s3c2410wdt_miscdev);
+       watchdog_unregister_device(&s3c2410_wdd);

更新 Kconfig 条目

现在驱动程序的条目需要选择 WATCHDOG_CORE

  • select WATCHDOG_CORE

创建补丁并将其发送到上游

确保您已理解提交补丁:将代码引入内核的基本指南并将您的补丁发送到linux-watchdog@vger.kernel.org。我们期待您的贡献 :)