当前位置:   article > 正文

Linux内核4.14版本——watchdog看门狗框架分析_linux kernel watchdog 源码详解

linux kernel watchdog 源码详解

目录

0 简介

1. 设备的注册

1.1 dw_wdt_drv_probe

1.2 watchdog_register_device

1.3 __watchdog_register_device

1.4 最终misc_register注册watchdog_miscdev

2. watchdog_miscdev设备分析

2.1 watchdog_open

2.2 watchdog_write

2.3 watchdog_release

2.4 watchdog_stop

2.5 watchdog_set_timeout

2.6 watchdog_ping

2.7 watchdog_ioctl

3. 使用标准的内核框架wdt driver时,需要特别注意以下两点

3.1 NOWAYOUT(无路可逃)的使用

3.2 magic close特性

4. 应用层代码(app demo)


0 简介

版本:Linux 4.14
用到的文件:
kernel\watchdog.c
drivers\watchdog\dw_wdt.c
drivers\watchdog\watchdog_dev.c
drivers\watchdog\watchdog_core.c

      wdt的驱动挺特别的,linux内核中也对它做了一个封装并归纳处理总结出了一个框架,分为以下三层:统一driver层(watchdog_dev),核心层(watchdog_core),具体的设备层(本文以dw wdt为例)。
      在写wdt的时候会发现,和其他driver不同的是,不需要我们在自己的driver中去创建节点,我们只需要实现ops结构体成员即可,然后去调用wdt核心层的api注册ops即可。
      其实,创建节点的工作在wdt的统一driver层已经实现了,这是因wdt设备在各个soc上是一个高度统一的设备,可以被高度抽象出来,查看代码watchdog_dev.c中就可以看到,这是一个标准的字符设备驱动,该文件中创建”dev/watchdog”节点,同时向上层提供了ioctl接口。
       driver中的各个接口调用都是以函数指针的方式去调用,而这些函数指针在我们每个soc自己的巨头的wdt driver层去初始化然后注册。

1. 设备的注册

1.1 dw_wdt_drv_probe

drivers\watchdog\dw_wdt.c

  1. #ifdef CONFIG_OF
  2. static const struct of_device_id dw_wdt_of_match[] = {
  3.     { .compatible = "snps,dw-wdt", },
  4.     { /* sentinel */ }
  5. };
  6. MODULE_DEVICE_TABLE(of, dw_wdt_of_match);
  7. #endif
  8. static struct platform_driver dw_wdt_driver = {
  9.     .probe        = dw_wdt_drv_probe,
  10.     .remove        = dw_wdt_drv_remove,
  11.     .driver        = {
  12.         .name    = "dw_wdt",
  13.         .of_match_table = of_match_ptr(dw_wdt_of_match),
  14.         .pm    = &dw_wdt_pm_ops,
  15.     },
  16. };

      设备树中比较compatible = "snps,dw-wdt",比较通过后调用dw_wdt_drv_probe函数。

  1. static int dw_wdt_drv_probe(struct platform_device *pdev)
  2. {
  3.     struct device *dev = &pdev->dev;
  4.     struct watchdog_device *wdd;
  5.     struct dw_wdt *dw_wdt;
  6.     struct resource *mem;
  7.     int ret;
  8.     dw_wdt = devm_kzalloc(dev, sizeof(*dw_wdt), GFP_KERNEL);
  9.     if (!dw_wdt)
  10.         return -ENOMEM;
  11.     wdd->info = &dw_wdt_ident;
  12.     wdd->ops = &dw_wdt_ops;  //设置操作函数
  13.     ........
  14.     
  15.     watchdog_set_nowayout(wdd, nowayout);
  16.     
  17.     ........
  18.     platform_set_drvdata(pdev, dw_wdt);
  19.     watchdog_set_restart_priority(wdd, 128);
  20.     ret = watchdog_register_device(wdd);
  21.     if (ret)
  22.         goto out_disable_clk;
  23.     return 0;
  24. out_disable_clk:
  25.     clk_disable_unprepare(dw_wdt->clk);
  26.     return ret;
  27. }
  28. static const struct watchdog_ops dw_wdt_ops = {
  29.     .owner        = THIS_MODULE,
  30.     .start        = dw_wdt_start,
  31.     .stop        = dw_wdt_stop,
  32.     .ping        = dw_wdt_ping,
  33.     .set_timeout    = dw_wdt_set_timeout,
  34.     .get_timeleft    = dw_wdt_get_timeleft,
  35.     .restart    = dw_wdt_restart,
  36. };

      前面一些关于硬件、IO等的设置,这里不做介绍,注意wdt的fops就行。我们主要讲watchdog_register_device函数。

1.2 watchdog_register_device

      该函数drivers\watchdog\watchdog_core.c在这里面,如下:

  1. int watchdog_register_device(struct watchdog_device *wdd)
  2. {
  3.     int ret;
  4.     mutex_lock(&wtd_deferred_reg_mutex);
  5.     if (wtd_deferred_reg_done)
  6.         ret = __watchdog_register_device(wdd);
  7.     else
  8.         ret = watchdog_deferred_registration_add(wdd);
  9.     mutex_unlock(&wtd_deferred_reg_mutex);
  10.     return ret;
  11. }

      wtd_deferred_reg_done全局变量已经定义了,所以走__watchdog_register_device分支。

  1. static int __init watchdog_deferred_registration(void)
  2. {
  3.     mutex_lock(&wtd_deferred_reg_mutex);
  4.     wtd_deferred_reg_done = true;
  5.     while (!list_empty(&wtd_deferred_reg_list)) {
  6.         struct watchdog_device *wdd;
  7.         wdd = list_first_entry(&wtd_deferred_reg_list,
  8.                        struct watchdog_device, deferred);
  9.         list_del(&wdd->deferred);
  10.         __watchdog_register_device(wdd);
  11.     }
  12.     mutex_unlock(&wtd_deferred_reg_mutex);
  13.     return 0;
  14. }
  15. static int __init watchdog_init(void)
  16. {
  17.     int err;
  18.     err = watchdog_dev_init();
  19.     if (err < 0)
  20.         return err;
  21.     watchdog_deferred_registration();
  22.     return 0;
  23. }
  24. subsys_initcall_sync(watchdog_init);

1.3 __watchdog_register_device

  1. static int __watchdog_register_device(struct watchdog_device *wdd)
  2. {
  3.     int ret, id = -1;
  4.     .......
  5.     
  6.     ret = watchdog_dev_register(wdd);
  7.     ....
  8.     return 0;
  9. }

      其他不管,发现调用watchdog_dev_register。

1.4 最终misc_register注册watchdog_miscdev

  1. static int watchdog_cdev_register(struct watchdog_device *wdd)
  2. {
  3.     struct watchdog_core_data *wd_data;
  4.     int err;
  5.     ......
  6.     if (wdd->id == 0) {
  7.         old_wd_data = wd_data;
  8.         watchdog_miscdev.parent = wdd->parent;
  9.         err = misc_register(&watchdog_miscdev);
  10.         if (err != 0) {
  11.             pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n",
  12.                 wdd->info->identity, WATCHDOG_MINOR, err);
  13.             if (err == -EBUSY)
  14.                 pr_err("%s: a legacy watchdog module is probably present.\n",
  15.                     wdd->info->identity);
  16.             old_wd_data = NULL;
  17.             kfree(wd_data);
  18.             return err;
  19.         }
  20.     }
  21.     .......
  22.     return 0;
  23. }

      发现最终注册了一个混杂设备watchdog_miscdev,其定义如下。

  1. static const struct file_operations watchdog_fops = {
  2.     .owner        = THIS_MODULE,
  3.     .write        = watchdog_write,
  4.     .unlocked_ioctl    = watchdog_ioctl,
  5.     .open        = watchdog_open,
  6.     .release    = watchdog_release,
  7. };
  8. static struct miscdevice watchdog_miscdev = {
  9.     .minor        = WATCHDOG_MINOR,
  10.     .name        = "watchdog",
  11.     .fops        = &watchdog_fops,
  12. };

2. watchdog_miscdev设备分析

2.1 watchdog_open

  1. /*
  2.  *    watchdog_open: open the /dev/watchdog* devices.
  3.  *    @inode: inode of device
  4.  *    @file: file handle to device
  5.  *
  6.  *    When the /dev/watchdog* device gets opened, we start the watchdog.
  7.  *    Watch out: the /dev/watchdog device is single open, so we make sure
  8.  *    it can only be opened once.
  9.  */
  10. static int watchdog_open(struct inode *inode, struct file *file)
  11. {
  12.     struct watchdog_core_data *wd_data;
  13.     struct watchdog_device *wdd;
  14.     bool hw_running;
  15.     int err;
  16.     ......
  17.     err = watchdog_start(wdd);
  18.     if (err < 0)
  19.         goto out_mod;
  20.     ......
  21. }

      当打开open("/dev/watchdog")时,最终调用watchdog_open,可以看出最终调用了watchdog_start。

  1. /*
  2.  *    watchdog_start: wrapper to start the watchdog.
  3.  *    @wdd: the watchdog device to start
  4.  *
  5.  *    The caller must hold wd_data->lock.
  6.  *
  7.  *    Start the watchdog if it is not active and mark it active.
  8.  *    This function returns zero on success or a negative errno code for
  9.  *    failure.
  10.  */
  11. static int watchdog_start(struct watchdog_device *wdd)
  12. {
  13.     struct watchdog_core_data *wd_data = wdd->wd_data;
  14.     unsigned long started_at;
  15.     int err;
  16.     .....
  17.     set_bit(_WDOG_KEEPALIVE, &wd_data->status);
  18.     started_at = jiffies;
  19.     if (watchdog_hw_running(wdd) && wdd->ops->ping)
  20.         err = wdd->ops->ping(wdd);
  21.     else
  22.         err = wdd->ops->start(wdd);
  23.     .....
  24.     return err;
  25. }

      查看watchdog_start源码可知,最终调用了wdt fops中的start函数。

2.2 watchdog_write

  1. /*
  2.  *    watchdog_write: writes to the watchdog.
  3.  *    @file: file from VFS
  4.  *    @data: user address of data
  5.  *    @len: length of data
  6.  *    @ppos: pointer to the file offset
  7.  *
  8.  *    A write to a watchdog device is defined as a keepalive ping.
  9.  *    Writing the magic 'V' sequence allows the next close to turn
  10.  *    off the watchdog (if 'nowayout' is not set).
  11.  */
  12. static ssize_t watchdog_write(struct file *file, const char __user *data,
  13.                         size_t len, loff_t *ppos)
  14. {
  15.     struct watchdog_core_data *wd_data = file->private_data;
  16.     struct watchdog_device *wdd;
  17.     int err;
  18.     size_t i;
  19.     char c;
  20.     if (len == 0)
  21.         return 0;
  22.     /*
  23.      * Note: just in case someone wrote the magic character
  24.      * five months ago...
  25.      */
  26.     clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status);
  27.     /* scan to see whether or not we got the magic character */
  28.     for (i = 0; i != len; i++) {
  29.         if (get_user(c, data + i))
  30.             return -EFAULT;
  31.         if (c == 'V')
  32.             set_bit(_WDOG_ALLOW_RELEASE, &wd_data->status);
  33.     }
  34.     /* someone wrote to us, so we send the watchdog a keepalive ping */
  35.     err = -ENODEV;
  36.     mutex_lock(&wd_data->lock);
  37.     wdd = wd_data->wdd;
  38.     if (wdd)
  39.         err = watchdog_ping(wdd);
  40.     mutex_unlock(&wd_data->lock);
  41.     if (err < 0)
  42.         return err;
  43.     return len;
  44. }

      这里的写有一个特殊操作,如果向wdt节点写“V”字符时,则会置位标志为_WDOG_ALLOW_RELEASE,即允许release,当上层去close节点的时候会callback到这一层的release接口,release会去判断_WDOG_ALLOW_RELEASE,如果不被置位则不执行watchdog_stop,即虽然上层关掉了fd,但是底层实际没有执行stop的操作。如果被置位,就执行watchdog_stop,按照上面的分析,watchdog_stop又会判断NOWAYOUT是否被置位。即想要关闭wdt,首先需要disable _WDOG_ALLOW_RELEASE,即去掉该选项。即在close节点前向节点写“V”,然后再clsoe,这样才可以正真调用到具体driver的stop接口

2.3 watchdog_release

  1. /*
  2.  *    watchdog_release: release the watchdog device.
  3.  *    @inode: inode of device
  4.  *    @file: file handle to device
  5.  *
  6.  *    This is the code for when /dev/watchdog gets closed. We will only
  7.  *    stop the watchdog when we have received the magic char (and nowayout
  8.  *    was not set), else the watchdog will keep running.
  9.  */
  10. static int watchdog_release(struct inode *inode, struct file *file)
  11. {
  12.     struct watchdog_core_data *wd_data = file->private_data;
  13.     struct watchdog_device *wdd;
  14.     int err = -EBUSY;
  15.     bool running;
  16.     ......
  17.     /*
  18.      * We only stop the watchdog if we received the magic character
  19.      * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then
  20.      * watchdog_stop will fail.
  21.      */
  22.     if (!test_bit(WDOG_ACTIVE, &wdd->status))
  23.         err = 0;
  24.     else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) ||
  25.          !(wdd->info->options & WDIOF_MAGICCLOSE))
  26.         err = watchdog_stop(wdd);
  27.     /* If the watchdog was not stopped, send a keepalive ping */
  28.     if (err < 0) {
  29.         pr_crit("watchdog%d: watchdog did not stop!\n", wdd->id);
  30.         watchdog_ping(wdd);
  31.     }
  32.     watchdog_update_worker(wdd);
  33.     /* make sure that /dev/watchdog can be re-opened */
  34.     clear_bit(_WDOG_DEV_OPEN, &wd_data->status);
  35.     ......
  36.     return 0;
  37. }

      release 接口,上层做close的时候会调用到。release会去判断_WDOG_ALLOW_RELEASE,而这个标志在write函数中会被置位,(向节点写“V”操作)。如果不被置位则不执行watchdog_stop即虽然上层关掉了fd,但是底层实际没有执行stop的操作。如果被置位,就执行watchdog_stop,按照上面的分析,watchdog_stop又会判断NOWAYOUT是否被置位。即想要关闭wdt,首先需要disable _WDOG_ALLOW_RELEASE,即去掉该选项。然后在close节点前向节点写“V”,然后再clsoe,这样才可以正真调用到具体driver的stop接口。

2.4 watchdog_stop

  1. /*
  2.  *    watchdog_stop: wrapper to stop the watchdog.
  3.  *    @wdd: the watchdog device to stop
  4.  *
  5.  *    The caller must hold wd_data->lock.
  6.  *
  7.  *    Stop the watchdog if it is still active and unmark it active.
  8.  *    This function returns zero on success or a negative errno code for
  9.  *    failure.
  10.  *    If the 'nowayout' feature was set, the watchdog cannot be stopped.
  11.  */
  12. static int watchdog_stop(struct watchdog_device *wdd)
  13. {
  14.     int err = 0;
  15.     if (!watchdog_active(wdd))
  16.         return 0;
  17.     if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) {
  18.         pr_info("watchdog%d: nowayout prevents watchdog being stopped!\n",
  19.             wdd->id);
  20.         return -EBUSY;
  21.     }
  22.     if (wdd->ops->stop) {
  23.         clear_bit(WDOG_HW_RUNNING, &wdd->status);
  24.         err = wdd->ops->stop(wdd);
  25.     } else {
  26.         set_bit(WDOG_HW_RUNNING, &wdd->status);
  27.     }
  28.     if (err == 0) {
  29.         clear_bit(WDOG_ACTIVE, &wdd->status);
  30.         watchdog_update_worker(wdd);
  31.     }
  32.     return err;
  33. }

      WDOG_NO_WAY_OUT,这里有个特殊的一点是上层调用进这个接口想关闭wdt即终止计数功能时,这里会判断status是否被设置为WDOG_NO_WAY_OUT状态,如果设置了,则直接返回不去调用实际driver中的stop函数。也就是说当NOWAYOUT被配置后,无论上层是close wdt节点还是调用统一层stop接口,wdt都是不会关掉的,会一直计数下去,如果不持续喂狗就会reset。

  1. static bool nowayout = WATCHDOG_NOWAYOUT;
  2. #define WATCHDOG_NOWAYOUT        IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
  3. /* Use the following function to set the nowayout feature */
  4. static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool nowayout)
  5. {
  6.     if (nowayout)
  7.         set_bit(WDOG_NO_WAY_OUT, &wdd->status);
  8. }

       由上可以看出如果配置了CONFIG_WATCHDOG_NOWAYOUT项,则watchdog_set_nowayout在dw_wdt_drv_probe就会被执行,WDOG_NO_WAY_OUT状态就会被置起来,当上层通过ioctl调用了wdt同一层的stop接口时,则直接返回。

2.5 watchdog_set_timeout

  1. /*
  2.  *    watchdog_set_timeout: set the watchdog timer timeout
  3.  *    @wdd: the watchdog device to set the timeout for
  4.  *    @timeout: timeout to set in seconds
  5.  *
  6.  *    The caller must hold wd_data->lock.
  7.  */
  8. static int watchdog_set_timeout(struct watchdog_device *wdd,
  9.                             unsigned int timeout)
  10. {
  11.     int err = 0;
  12.     if (!(wdd->info->options & WDIOF_SETTIMEOUT))
  13.         return -EOPNOTSUPP;
  14.     if (watchdog_timeout_invalid(wdd, timeout))
  15.         return -EINVAL;
  16.     if (wdd->ops->set_timeout) {
  17.         err = wdd->ops->set_timeout(wdd, timeout);
  18.     } else {
  19.         wdd->timeout = timeout;
  20.         /* Disable pretimeout if it doesn't fit the new timeout */
  21.         if (wdd->pretimeout >= wdd->timeout)
  22.             wdd->pretimeout = 0;
  23.     }
  24.     watchdog_update_worker(wdd);
  25.     return err;
  26. }

      设置超时时间,一般正常喂狗会下发一个时间,如果上层想主动重启,只需设置时间为0,当然实际的底层driver需要对时间做判断,当时间为0时,就重启系统。

2.6 watchdog_ping

  1. /*
  2.  *    watchdog_ping: ping the watchdog.
  3.  *    @wdd: the watchdog device to ping
  4.  *
  5.  *    The caller must hold wd_data->lock.
  6.  *
  7.  *    If the watchdog has no own ping operation then it needs to be
  8.  *    restarted via the start operation. This wrapper function does
  9.  *    exactly that.
  10.  *    We only ping when the watchdog device is running.
  11.  */
  12. static int watchdog_ping(struct watchdog_device *wdd)
  13. {
  14.     struct watchdog_core_data *wd_data = wdd->wd_data;
  15.     if (!watchdog_active(wdd) && !watchdog_hw_running(wdd))
  16.         return 0;
  17.     set_bit(_WDOG_KEEPALIVE, &wd_data->status);
  18.     wd_data->last_keepalive = jiffies;
  19.     return __watchdog_ping(wdd);
  20. }
  21. static int __watchdog_ping(struct watchdog_device *wdd)
  22. {
  23.     struct watchdog_core_data *wd_data = wdd->wd_data;
  24.     unsigned long earliest_keepalive = wd_data->last_hw_keepalive +
  25.                 msecs_to_jiffies(wdd->min_hw_heartbeat_ms);
  26.     int err;
  27.     if (time_is_after_jiffies(earliest_keepalive)) {
  28.         mod_delayed_work(watchdog_wq, &wd_data->work,
  29.                  earliest_keepalive - jiffies);
  30.         return 0;
  31.     }
  32.     wd_data->last_hw_keepalive = jiffies;
  33.     if (wdd->ops->ping)
  34.         err = wdd->ops->ping(wdd);  /* ping the watchdog */
  35.     else
  36.         err = wdd->ops->start(wdd); /* restart watchdog */
  37.     watchdog_update_worker(wdd);
  38.     return err;
  39. }

2.7 watchdog_ioctl

  1. /*
  2.  *    watchdog_ioctl: handle the different ioctl's for the watchdog device.
  3.  *    @file: file handle to the device
  4.  *    @cmd: watchdog command
  5.  *    @arg: argument pointer
  6.  *
  7.  *    The watchdog API defines a common set of functions for all watchdogs
  8.  *    according to their available features.
  9.  */
  10. static long watchdog_ioctl(struct file *file, unsigned int cmd,
  11.                             unsigned long arg)
  12. {
  13.     struct watchdog_core_data *wd_data = file->private_data;
  14.     void __user *argp = (void __user *)arg;
  15.     struct watchdog_device *wdd;
  16.     int __user *p = argp;
  17.     unsigned int val;
  18.     int err;
  19.     mutex_lock(&wd_data->lock);
  20.     wdd = wd_data->wdd;
  21.     if (!wdd) {
  22.         err = -ENODEV;
  23.         goto out_ioctl;
  24.     }
  25.     err = watchdog_ioctl_op(wdd, cmd, arg);
  26.     if (err != -ENOIOCTLCMD)
  27.         goto out_ioctl;
  28.     switch (cmd) {
  29.     case WDIOC_GETSUPPORT:
  30.         err = copy_to_user(argp, wdd->info,
  31.             sizeof(struct watchdog_info)) ? -EFAULT : 0;
  32.         break;
  33.     case WDIOC_GETSTATUS:
  34.         val = watchdog_get_status(wdd);
  35.         err = put_user(val, p);
  36.         break;
  37.     case WDIOC_GETBOOTSTATUS:
  38.         err = put_user(wdd->bootstatus, p);
  39.         break;
  40.     case WDIOC_SETOPTIONS:
  41.         if (get_user(val, p)) {
  42.             err = -EFAULT;
  43.             break;
  44.         }
  45.         if (val & WDIOS_DISABLECARD) {
  46.             err = watchdog_stop(wdd);
  47.             if (err < 0)
  48.                 break;
  49.         }
  50.         if (val & WDIOS_ENABLECARD)
  51.             err = watchdog_start(wdd);
  52.         break;
  53.     case WDIOC_KEEPALIVE:
  54.         if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) {
  55.             err = -EOPNOTSUPP;
  56.             break;
  57.         }
  58.         err = watchdog_ping(wdd);
  59.         break;
  60.     case WDIOC_SETTIMEOUT:
  61.         if (get_user(val, p)) {
  62.             err = -EFAULT;
  63.             break;
  64.         }
  65.         err = watchdog_set_timeout(wdd, val);
  66.         if (err < 0)
  67.             break;
  68.         /* If the watchdog is active then we send a keepalive ping
  69.          * to make sure that the watchdog keep's running (and if
  70.          * possible that it takes the new timeout) */
  71.         err = watchdog_ping(wdd);
  72.         if (err < 0)
  73.             break;
  74.         /* Fall */
  75.     case WDIOC_GETTIMEOUT:
  76.         /* timeout == 0 means that we don't know the timeout */
  77.         if (wdd->timeout == 0) {
  78.             err = -EOPNOTSUPP;
  79.             break;
  80.         }
  81.         err = put_user(wdd->timeout, p);
  82.         break;
  83.     case WDIOC_GETTIMELEFT:
  84.         err = watchdog_get_timeleft(wdd, &val);
  85.         if (err < 0)
  86.             break;
  87.         err = put_user(val, p);
  88.         break;
  89.     case WDIOC_SETPRETIMEOUT:
  90.         if (get_user(val, p)) {
  91.             err = -EFAULT;
  92.             break;
  93.         }
  94.         err = watchdog_set_pretimeout(wdd, val);
  95.         break;
  96.     case WDIOC_GETPRETIMEOUT:
  97.         err = put_user(wdd->pretimeout, p);
  98.         break;
  99.     default:
  100.         err = -ENOTTY;
  101.         break;
  102.     }
  103. out_ioctl:
  104.     mutex_unlock(&wd_data->lock);
  105.     return err;
  106. }

3. 使用标准的内核框架wdt driver时,需要特别注意以下两点

3.1 NOWAYOUT(无路可逃)的使用

       有的时候我们希望看门狗不被停止,即上层的任何关狗的动作都不予支持,此时就可以使用NOWAYOUT功能,接配置内核的标准配置项:CONFIG_WATCHDOG_NOWAYOUT
使能该项后,要为我们的driver所用需要在具体的wdt driver中对该配置进行支持,即调用watchdog_set_nowayout()去设置WDOG_NO_WAY_OUT:

  1. static bool nowayout = WATCHDOG_NOWAYOUT;
  2. #define WATCHDOG_NOWAYOUT        IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
  3. /* Use the following function to set the nowayout feature */
  4. static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool nowayout)
  5. {
  6.     if (nowayout)
  7.         set_bit(WDOG_NO_WAY_OUT, &wdd->status);
  8. }

      配置后会去执行set_bit(WDOG_NO_WAY_OUT, &wdd->status);置位WDOG_NO_WAY_OUT。在上层调用统一driver层IOCTL去调用watchdog_stop()时,watchdog_stop会去判断是否WDOG_NO_WAY_OUT被置位,如果设置了,则直接返回不去调用实际driver中的stop函数
这样就可以屏蔽所有关wdt的动作,这个操作仍然在统一设备层,具体的wdt driver不用管这个

3.2 magic close特性

      有的时候我们想停掉狗,不想然他reset,如果系统使用了标准的wdt框架,则需要magic close的支持。

      magic close即向wdt节点写字符“V”向wdt节点写“V”字符时,被调用到的接口watchdog_write()会置位标志为_WDOG_ALLOW_RELEASE。当上层close节点时,会调用标准的wdt统一driver层的release接口,release会去判断_WDOG_ALLOW_RELEASE,如果不被置位则不执行watchdog_stop即虽然上层关掉了fd,但是底层实际没有执行stop的操作。如果被置位,就执行watchdog_stop,按照上面的分析,watchdog_stop又会判断NOWAYOUT是否被置位。
       所以想要关闭wdt rest功能结束其计数,首先需要disable _WDOG_ALLOW_RELEASE,即去掉该选项。然后在close节点前向节点写“V”,然后再clsoe,这样才可以正真调用到具体driver的stop接口。

4. 应用层代码(app demo)

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <errno.h>
  6. #include <pthread.h>
  7. #include <sys/ioctl.h>
  8. #include <stdlib.h>
  9. #include <unistd.h>
  10. #include <fcntl.h>
  11. #include <sys/time.h>
  12. #include <unistd.h>
  13. #include <time.h>
  14. #include <getopt.h>
  15. #include <sys/signal.h>
  16. //watchdog 
  17. #define WATCHDOG_IOCTL_BASE     'W'
  18. struct watchdog_info {
  19.     unsigned int options;          /* Options the card/driver supports */
  20.     unsigned int firmware_version; /* Firmware version of the card */
  21.     char identity[32];     /* Identity of the board */
  22. };
  23. #define WDIOC_GETSUPPORT        _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
  24. #define WDIOC_GETSTATUS         _IOR(WATCHDOG_IOCTL_BASE, 1, int)
  25. #define WDIOC_GETBOOTSTATUS     _IOR(WATCHDOG_IOCTL_BASE, 2, int)
  26. #define WDIOC_GETTEMP           _IOR(WATCHDOG_IOCTL_BASE, 3, int)
  27. #define WDIOC_SETOPTIONS        _IOR(WATCHDOG_IOCTL_BASE, 4, int)
  28. #define WDIOC_KEEPALIVE         _IOR(WATCHDOG_IOCTL_BASE, 5, int)
  29. #define WDIOC_SETTIMEOUT        _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
  30. #define WDIOC_GETTIMEOUT        _IOR(WATCHDOG_IOCTL_BASE, 7, int)
  31. #define WDIOC_SETPRETIMEOUT     _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
  32. #define WDIOC_GETPRETIMEOUT     _IOR(WATCHDOG_IOCTL_BASE, 9, int)
  33. #define WDIOC_GETTIMELEFT       _IOR(WATCHDOG_IOCTL_BASE, 10, int)
  34. #define WDIOF_OVERHEAT          0x0001  /* Reset due to CPU overheat */
  35. #define WDIOF_FANFAULT          0x0002  /* Fan failed */
  36. #define WDIOF_EXTERN1           0x0004  /* External relay 1 */
  37. #define WDIOF_EXTERN2           0x0008  /* External relay 2 */
  38. #define WDIOF_POWERUNDER        0x0010  /* Power bad/power fault */
  39. #define WDIOF_CARDRESET         0x0020  /* Card previously reset the CPU */
  40. #define WDIOF_POWEROVER         0x0040  /* Power over voltage */
  41. #define WDIOF_SETTIMEOUT        0x0080  /* Set timeout (in seconds) */
  42. #define WDIOF_MAGICCLOSE        0x0100  /* Supports magic close char */
  43. #define WDIOF_PRETIMEOUT        0x0200  /* Pretimeout (in seconds), get/set */
  44. #define WDIOF_KEEPALIVEPING     0x8000  /* Keep alive ping reply */
  45. #define WDIOS_DISABLECARD       0x0001  /* Turn off the watchdog timer */
  46. #define WDIOS_ENABLECARD        0x0002  /* Turn on the watchdog timer */
  47. #define WDIOS_TEMPPANIC         0x0004  /* Kernel panic on temperature trip */
  48. int wdt_fd;
  49. int time_out = 5;
  50. #define DEFAULT_PING_RATE    1
  51. void stop_signal()
  52. {
  53.     int val = 0 , ret = 0 ;
  54.     val = WDIOS_DISABLECARD ;
  55.     ret = ioctl(wdt_fd, WDIOC_SETOPTIONS, &val) ;
  56.     if (ret < 0)
  57.         printf("ioctl WDIOC_GETSUPPORT failed with %d.\n", ret);
  58.     printf("===watchdow will be closed===\n") ;
  59.     close(wdt_fd) ;
  60.     exit(0);
  61.     
  62. }
  63. int main(int argc, char *argv[])
  64. {
  65.     int ret;
  66.     static int count = 0;
  67.     struct watchdog_info wdt_info;
  68.     unsigned int ping_rate = DEFAULT_PING_RATE;
  69.     signal(SIGINT, stop_signal) ;
  70.     wdt_fd = open("/dev/watchdog0", O_RDWR);
  71.     if(wdt_fd < 0)
  72.     {
  73.         printf("open /dev/watchdog0 failed.\n");
  74.     }
  75.     /* get watchdog infomation struct */
  76.     ret = ioctl(wdt_fd, WDIOC_GETSUPPORT, &wdt_info);
  77.     if (ret < 0)
  78.         printf("ioctl WDIOC_GETSUPPORT failed.\n");
  79.     else
  80.     {
  81.         printf("options = 0x%x,id = %s\n", wdt_info.options, wdt_info.identity);
  82.     }
  83.     ioctl(wdt_fd, WDIOC_SETTIMEOUT, &time_out);
  84.     if (ret < 0)
  85.         printf("ioctl WDIOC_SETTIMEOUT failed.\n");
  86.     
  87.     while(1)
  88.     {
  89.         
  90.         if(count > 10)
  91.         {
  92.             printf("unfood watchdog, count = %d \n",count++);
  93.         }
  94.         else
  95.         {
  96.             ioctl(wdt_fd,WDIOC_KEEPALIVE,NULL);
  97.             printf("food watchdog, count = %d \n",count++);
  98.         }
  99.         sleep(DEFAULT_PING_RATE);
  100.     }    
  101.     close(wdt_fd);
  102.     return 0;
  103. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/713127
推荐阅读
相关标签
  

闽ICP备14008679号