This artical is reprinted from the URL below:
http://embeddedfreak.wordpress.com/2010/08/23/howto-use-linux-watchdog/
Howto Use Linux Watchdog
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:
- 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.
- 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:
- Write character ‘V’ into ‘/dev/watchdog’ to prevent stopping the watchdog accidentally
- 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:
- 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)
- 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] ", app_name); fprintf(stream, " -h --help Display this usage information. " " -d --dev <device_file> Use <device_file> as watchdog device file. " " The default device file is '/dev/watchdog' " " -i --interval <interval> Change the watchdog interval time "); 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 ", strerror(errno)); exit(EXIT_FAILURE); } /* If user wants to change the watchdog interval */ if (interval != 0) { fprintf(stdout, "Set watchdog interval to %d ", interval); if (ioctl(fd, WDIOC_SETTIMEOUT, &interval) != 0) { fprintf(stderr, "Error: Set watchdog interval failed "); exit(EXIT_FAILURE); } } /* Display current watchdog interval */ if (ioctl(fd, WDIOC_GETTIMEOUT, &interval) == 0) { fprintf(stdout, "Current watchdog interval is %d ", interval); } else { fprintf(stderr, "Error: Cannot read watchdog interval "); 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 ", (bootstatus != 0) ? "Watchdog" : "Power-On-Reset"); } else { fprintf(stderr, "Error: Cannot read watchdog status "); 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: " " <w> to kick through writing over device file " " <i> to kick through IOCTL " " <x> to exit the program "); do { kick_watchdog = getchar(); switch (kick_watchdog) { case 'w': write(fd, "w", 1); fprintf(stdout, "Kick watchdog through writing over device file "); break; case 'i': ioctl(fd, WDIOC_KEEPALIVE, NULL); fprintf(stdout, "Kick watchdog through IOCTL "); break; case 'x': fprintf(stdout, "Goodbye ! "); break; default: fprintf(stdout, "Unknown command "); 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