Embedded Freaks..

August 23, 2010

Howto Use Linux Watchdog

Filed under: ARM9, embedded-linux — Tags: , — 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

9 Comments »

  1. Great post, very helpful indeed.

    I noticed you don’t handle the CR or LF in your getchar() keyboard input loop, this results in some mildly confusing output. Just something like:

    case 0xA:
    case 0xD:
    break;

    to your switch.

    Comment by owen — December 15, 2011 @ 8:00 am

  2. Great Post.

    However I have a problem. I start /dev/watchdog in my linuxrc script (Initrd), and I’m monitor some PIDs in my watchdog.conf. So even before the CF is mounted which will contain the location the PIDs are pointing to, the watchdog has been started. This results in some annoying error messages in the log “PID file not found”. I have handled it in repair-binary, but the errors still exist.

    Any suggestions?

    Thanks.

    Comment by steve88test — January 31, 2012 @ 12:21 am

  3. Very useful post. One question: How can I be sure that my watchdog driver indeed uses /dev/watchdog?
    Although in my Ubuntu 11.04 watchdog seems to be running, there is no /dev/watchdog and I create it using mknod. However, when I run your program I receive the error “Cannot read watchdog interval”. Any hint?

    Comment by kosbir — March 25, 2012 @ 3:42 am

  4. Actually I figured out that I had to load the softdog module. Upon loading, /dev/watchdog is automatically created and your program works great.

    Comment by kosbir — March 25, 2012 @ 4:34 pm

  5. […] I think there is a way to control watchdog timers with ioctl (ref: EmbeddedFreak: How to use linux watchdog). […]

    Pingback by How can I inspect and change this interface’s watchdog timer? | PHP Developer Resource — May 2, 2012 @ 3:00 am

  6. […] I think there is a way to control watchdog timers with ioctl (ref: EmbeddedFreak: How to use linux watchdog). […]

    Pingback by Ethernet watchdog timer issue | PHP Developer Resource — May 2, 2012 @ 3:39 am

  7. […] I think there is a way to control watchdog timers with ioctl (ref: EmbeddedFreak: How to use linux watchdog). […]

    Pingback by Ethernet watchdog timer issue | MoVn - Linux Ubuntu Center — May 7, 2012 @ 11:24 pm

  8. Thanks for the post, helped a lot.

    I don’t suppose you know of a way to test the watchdog? I’m having trouble simulating a system crash.

    Comment by Richard — May 23, 2012 @ 8:50 pm

  9. Whenever i run the code my machine restarts!
    this is my machine specification:
    http://www.advantech.com/products/PCM-9363/mod_235FB6C7-A605-4230-B8DA-33F5B4F6A1AA.aspx

    Comment by mohsenjamali — July 5, 2012 @ 9:18 pm


RSS feed for comments on this post. TrackBack URI

Leave a comment