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

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

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

删除 file_operations 结构

旧的驱动程序为 open()、write() 等操作定义自己的 file_operations。这些现在由框架处理,并且仅在需要时调用驱动程序。因此,通常,“file_operations”结构和相关的函数都可以删除。只有极少数特定于驱动程序的细节必须移动到其他函数。以下是函数和可能需要的操作的概述

  • open:所有处理资源管理(文件打开检查、魔术关闭准备)的内容都可以直接删除。特定于设备的内容需要转到驱动程序特定的启动函数。请注意,对于某些驱动程序,启动函数也充当 ping 函数。如果情况如此,并且你需要启动/停止平衡(时钟!),你最好重构一个单独的启动函数。

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

  • write:可以直接删除,所有定义的行为都由框架处理,即写入时 ping 和魔术字符(‘V’)处理。

  • ioctl:虽然允许驱动程序扩展 IOCTL 接口,但最常见的 IOCTL 由框架处理,并在驱动程序的一些协助下支持

    WDIOC_GETSUPPORT

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

    WDIOC_GETSTATUS

    需要定义状态回调,否则返回 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

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

转换示例

-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_dev_register() 调用 watchdog_register_device() 时创建它

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

删除过时的 include 和 define

由于简化,现在可能未使用一些 define。删除它们。也可以删除 include。例如

- #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。通常,旧的驱动程序使用静态变量自行记录诸如 bootstatus 和 timeout 之类的内容。这些必须转换为使用 watchdog_device 中的成员。请注意,timeout 值是无符号整数。某些驱动程序使用有符号整数,因此也必须进行转换。

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

+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

  • 选择 WATCHDOG_CORE

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

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