[Reprinted] Howto Use Linux Watchdog

This artical is reprinted from the URL below:

http://embeddedfreak.wordpress.com/2010/08/23/howto-use-linux-watchdog/

 

Howto Use Linux Watchdog

Filed under: ARM9, embedded-linux — Tags: ARM9, embedded-linux — kunilkuda @ 4:08 pm

If you’re new with embedded Linux, one of interesting thing that you can start to learn is how to use the watchdog.

Watchdog, like its name, is a kind of peripheral that will boot the system if it doesn’t being ‘fed’ at certain time (In our daily terms: it will bite your system, unless you kick it). Therefore, it will prevent your system from hanging. It’s a nice feature to have, specially if there’s nobody around to press the ‘reset’ button for you =)

Watchdog Driver

To use watchdog peripheral in Linux, you will need:

  1. Watchdog driver: Most of board suppliers will provide you for free (ask their support if you need to). This article is using EA3131 Linux BSP’s provided watchdog driver.
  2. Watchdog device file: Device file is a special kind of file to mark the device node in your root filesystem (so you can access the peripheral like accessing file..’everything is a file’ in UNIX system). Normally it is called ‘/dev/watchdog’

If you don’t have watchdog device file in your target root filesystem, you can recreate it using ‘mknod’

# mknod /dev/watchdog c 10 130

Of course, the ‘mknod’ tool must be present in your root filesystem first before you can execute this. To run the watchdog driver in your system

# insmod your_watchdog_driver.ko

Starting – Stopping Watchdog

The watchdog is automatically started once you open ‘/dev/watchdog’. To stop the watchdog, you will need to:

  1. Write character ‘V’ into ‘/dev/watchdog’ to prevent stopping the watchdog accidentally
  2. Close the ‘/dev/watchdog’ file

An exception on stopping the watchdog by closing the file is when ‘CONFIG_WATCHDOG_NOWAYOUT’ is enabled in your kernel configuration. When this option is enabled, the watchdog cannot be stopped at all. Hence, you will need to feed / kick it all the time or it will reset the system

‘Kicking’ Watchdog

To kick or to feed the watchdog you can do it in two ways:

  1. Write any character into ‘/dev/watchdog’. You can write any character into /dev/watchdog, but, my suggestion, don’t write ‘V’ character (See the ‘Starting-Stopping Watchdog’ point above)
  2. Use IOCTL to insert ‘WDIOC_KEEPALIVE’ value.

Other Things To Do with Watchdog

If you’re bored with the standard start-kick-stop things, you can also try out other watchdog features:

  • Set the watchdog timeout. Use IOCTL with WDIOC_SETTIMEOUT
  • Get the current watchdog timeout. Use IOCTL with WDIOC_GETTIMEOUT
  • Check if the last boot is caused by watchdog or it is power-on-reset. Use IOCTL with WDIOC_GETBOOTSTATUS.

If you are interested to know more about using watchdog in Linux, read the watchdog documentation in Linux source code (Linux-x.y.z/Documentation/watchdog/watchdog-api.txt). Here’s some demo code to test the Linux watchdog:

/*
 * Linux watchdog demo for LPC313x
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <getopt.h>
#include <string.h>
#include <errno.h>

#include <linux/watchdog.h>

#define WATCHDOGDEV "/dev/watchdog"
static const char *const short_options = "hd:i:";
static const struct option long_options[] = {
   {"help", 0, NULL, 'h'},
   {"dev", 1, NULL, 'd'},
   {"interval", 1, NULL, 'i'},
   {NULL, 0, NULL, 0},
};

static void print_usage(FILE * stream, char *app_name, int exit_code)
{
   fprintf(stream, "Usage: %s [options]\n", app_name);
   fprintf(stream,
      " -h  --help                Display this usage information.\n"
      " -d  --dev <device_file>   Use <device_file> as watchdog device file.\n"
      "                           The default device file is '/dev/watchdog'\n"
      " -i  --interval <interval> Change the watchdog interval time\n");

   exit(exit_code);
}

int main(int argc, char **argv)
{
   int fd;         /* File handler for watchdog */
   int interval;      /* Watchdog timeout interval (in secs) */
   int bootstatus;      /* Wathdog last boot status */
   char *dev;      /* Watchdog default device file */

   int next_option;   /* getopt iteration var */
   char kick_watchdog;   /* kick_watchdog options */

   /* Init variables */
   dev = WATCHDOGDEV;
   interval = 0;
   kick_watchdog = 0;

   /* Parse options if any */
   do {
      next_option = getopt_long(argc, argv, short_options,
                 long_options, NULL);
      switch (next_option) {
      case 'h':
         print_usage(stdout, argv[0], EXIT_SUCCESS);
      case 'd':
         dev = optarg;
         break;
      case 'i':
         interval = atoi(optarg);
         break;
      case '?':   /* Invalid options */
         print_usage(stderr, argv[0], EXIT_FAILURE);
      case -1:   /* Done with options */
         break;
      default:   /* Unexpected stuffs */
         abort();
      }
   } while (next_option != -1);

   /* Once the watchdog device file is open, the watchdog will be activated by
      the driver */
   fd = open(dev, O_RDWR);
   if (-1 == fd) {
      fprintf(stderr, "Error: %s\n", strerror(errno));
      exit(EXIT_FAILURE);
   }

   /* If user wants to change the watchdog interval */
   if (interval != 0) {
      fprintf(stdout, "Set watchdog interval to %d\n", interval);
      if (ioctl(fd, WDIOC_SETTIMEOUT, &interval) != 0) {
         fprintf(stderr,
            "Error: Set watchdog interval failed\n");
         exit(EXIT_FAILURE);
      }
   }

   /* Display current watchdog interval */
   if (ioctl(fd, WDIOC_GETTIMEOUT, &interval) == 0) {
      fprintf(stdout, "Current watchdog interval is %d\n", interval);
   } else {
      fprintf(stderr, "Error: Cannot read watchdog interval\n");
      exit(EXIT_FAILURE);
   }

   /* Check if last boot is caused by watchdog */
   if (ioctl(fd, WDIOC_GETBOOTSTATUS, &bootstatus) == 0) {
      fprintf(stdout, "Last boot is caused by : %s\n",
         (bootstatus != 0) ? "Watchdog" : "Power-On-Reset");
   } else {
      fprintf(stderr, "Error: Cannot read watchdog status\n");
      exit(EXIT_FAILURE);
   }

   /* There are two ways to kick the watchdog:
      - by writing any dummy value into watchdog device file, or
      - by using IOCTL WDIOC_KEEPALIVE
    */
   fprintf(stdout,
      "Use:\n"
      " <w> to kick through writing over device file\n"
      " <i> to kick through IOCTL\n" " <x> to exit the program\n");
   do {
      kick_watchdog = getchar();
      switch (kick_watchdog) {
      case 'w':
         write(fd, "w", 1);
         fprintf(stdout,
            "Kick watchdog through writing over device file\n");
         break;
      case 'i':
         ioctl(fd, WDIOC_KEEPALIVE, NULL);
         fprintf(stdout, "Kick watchdog through IOCTL\n");
         break;
      case 'x':
         fprintf(stdout, "Goodbye !\n");
         break;
      default:
         fprintf(stdout, "Unknown command\n");
         break;
      }
   } while (kick_watchdog != 'x');

   /* The 'V' value needs to be written into watchdog device file to indicate
      that we intend to close/stop the watchdog. Otherwise, debug message
      'Watchdog timer closed unexpectedly' will be printed
    */
   write(fd, "V", 1);
   /* Closing the watchdog device will deactivate the watchdog. */
   close(fd);
}

Note1:  The watchdog driver may not implement all of IOCTL that is used in this demo code
Note2: The demo is tested on LPC313x (on EA3131 board). See LPCLinux forum for the details.
Note3: Assuming you’re using CodeSourcery’s GNU/Linux, the command to compile it for ARM926EJ-S is

$ arm-none-linux-gnueabi-gcc -mcpu=arm926ej-s watchdog_demo.c -o watchdog_demo

posted on 2013-08-15 13:20  Shawn X.Y. Bai  阅读(389)  评论(0)    收藏  举报

导航