将旧的看门狗驱动程序转换为看门狗框架¶
作者: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。我们期待它 :)