Commit fffcb480 authored by Linus Torvalds's avatar Linus Torvalds

Merge master.kernel.org:/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog

* master.kernel.org:/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog:
  [WATCHDOG] Documentation/watchdog update
  [WATCHDOG] convert AT91RM9200 watchdog to platform driver
  [WATCHDOG] add WDIOC_GETTIMELEFT ioctl
  [WATCHDOG] Pre-Timeout flags
parents b4459790 56fb9e53
......@@ -22,78 +22,9 @@
to run the program with an "&" to run it in the background!)
If you want to write a program to be compatible with the PC Watchdog
driver, simply do the following:
-- Snippet of code --
/*
* Watchdog Driver Test Program
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/watchdog.h>
int fd;
/*
* This function simply sends an IOCTL to the driver, which in turn ticks
* the PC Watchdog card to reset its internal timer so it doesn't trigger
* a computer reset.
*/
void keep_alive(void)
{
int dummy;
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
}
/*
* The main program. Run the program with "-d" to disable the card,
* or "-e" to enable the card.
*/
int main(int argc, char *argv[])
{
fd = open("/dev/watchdog", O_WRONLY);
if (fd == -1) {
fprintf(stderr, "Watchdog device not enabled.\n");
fflush(stderr);
exit(-1);
}
if (argc > 1) {
if (!strncasecmp(argv[1], "-d", 2)) {
ioctl(fd, WDIOC_SETOPTIONS, WDIOS_DISABLECARD);
fprintf(stderr, "Watchdog card disabled.\n");
fflush(stderr);
exit(0);
} else if (!strncasecmp(argv[1], "-e", 2)) {
ioctl(fd, WDIOC_SETOPTIONS, WDIOS_ENABLECARD);
fprintf(stderr, "Watchdog card enabled.\n");
fflush(stderr);
exit(0);
} else {
fprintf(stderr, "-d to disable, -e to enable.\n");
fprintf(stderr, "run by itself to tick the card.\n");
fflush(stderr);
exit(0);
}
} else {
fprintf(stderr, "Watchdog Ticking Away!\n");
fflush(stderr);
}
while(1) {
keep_alive();
sleep(1);
}
}
-- End snippet --
driver, simply use of modify the watchdog test program:
Documentation/watchdog/src/watchdog-test.c
Other IOCTL functions include:
......
#include <stdlib.h>
#include <fcntl.h>
int main(int argc, const char *argv[]) {
int fd = open("/dev/watchdog", O_WRONLY);
if (fd == -1) {
perror("watchdog");
exit(1);
}
while (1) {
write(fd, "\0", 1);
fsync(fd);
sleep(10);
}
}
/*
* Watchdog Driver Test Program
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/watchdog.h>
int fd;
/*
* This function simply sends an IOCTL to the driver, which in turn ticks
* the PC Watchdog card to reset its internal timer so it doesn't trigger
* a computer reset.
*/
void keep_alive(void)
{
int dummy;
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
}
/*
* The main program. Run the program with "-d" to disable the card,
* or "-e" to enable the card.
*/
int main(int argc, char *argv[])
{
fd = open("/dev/watchdog", O_WRONLY);
if (fd == -1) {
fprintf(stderr, "Watchdog device not enabled.\n");
fflush(stderr);
exit(-1);
}
if (argc > 1) {
if (!strncasecmp(argv[1], "-d", 2)) {
ioctl(fd, WDIOC_SETOPTIONS, WDIOS_DISABLECARD);
fprintf(stderr, "Watchdog card disabled.\n");
fflush(stderr);
exit(0);
} else if (!strncasecmp(argv[1], "-e", 2)) {
ioctl(fd, WDIOC_SETOPTIONS, WDIOS_ENABLECARD);
fprintf(stderr, "Watchdog card enabled.\n");
fflush(stderr);
exit(0);
} else {
fprintf(stderr, "-d to disable, -e to enable.\n");
fprintf(stderr, "run by itself to tick the card.\n");
fflush(stderr);
exit(0);
}
} else {
fprintf(stderr, "Watchdog Ticking Away!\n");
fflush(stderr);
}
while(1) {
keep_alive();
sleep(1);
}
}
......@@ -34,22 +34,7 @@ activates as soon as /dev/watchdog is opened and will reboot unless
the watchdog is pinged within a certain time, this time is called the
timeout or margin. The simplest way to ping the watchdog is to write
some data to the device. So a very simple watchdog daemon would look
like this:
#include <stdlib.h>
#include <fcntl.h>
int main(int argc, const char *argv[]) {
int fd=open("/dev/watchdog",O_WRONLY);
if (fd==-1) {
perror("watchdog");
exit(1);
}
while(1) {
write(fd, "\0", 1);
sleep(10);
}
}
like this source file: see Documentation/watchdog/src/watchdog-simple.c
A more advanced driver could for example check that a HTTP server is
still responding before doing the write call to ping the watchdog.
......@@ -110,7 +95,40 @@ current timeout using the GETTIMEOUT ioctl.
ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
printf("The timeout was is %d seconds\n", timeout);
Envinronmental monitoring:
Pretimeouts:
Some watchdog timers can be set to have a trigger go off before the
actual time they will reset the system. This can be done with an NMI,
interrupt, or other mechanism. This allows Linux to record useful
information (like panic information and kernel coredumps) before it
resets.
pretimeout = 10;
ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout);
Note that the pretimeout is the number of seconds before the time
when the timeout will go off. It is not the number of seconds until
the pretimeout. So, for instance, if you set the timeout to 60 seconds
and the pretimeout to 10 seconds, the pretimout will go of in 50
seconds. Setting a pretimeout to zero disables it.
There is also a get function for getting the pretimeout:
ioctl(fd, WDIOC_GETPRETIMEOUT, &timeout);
printf("The pretimeout was is %d seconds\n", timeout);
Not all watchdog drivers will support a pretimeout.
Get the number of seconds before reboot:
Some watchdog drivers have the ability to report the remaining time
before the system will reboot. The WDIOC_GETTIMELEFT is the ioctl
that returns the number of seconds before reboot.
ioctl(fd, WDIOC_GETTIMELEFT, &timeleft);
printf("The timeout was is %d seconds\n", timeleft);
Environmental monitoring:
All watchdog drivers are required return more information about the system,
some do temperature, fan and power level monitoring, some can tell you
......@@ -169,6 +187,10 @@ The watchdog saw a keepalive ping since it was last queried.
WDIOF_SETTIMEOUT Can set/get the timeout
The watchdog can do pretimeouts.
WDIOF_PRETIMEOUT Pretimeout (in seconds), get/set
For those drivers that return any bits set in the option field, the
GETSTATUS and GETBOOTSTATUS ioctls can be used to ask for the current
......
......@@ -65,28 +65,7 @@ The external event interfaces on the WDT boards are not currently supported.
Minor numbers are however allocated for it.
Example Watchdog Driver
-----------------------
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
int fd=open("/dev/watchdog",O_WRONLY);
if(fd==-1)
{
perror("watchdog");
exit(1);
}
while(1)
{
write(fd,"\0",1);
fsync(fd);
sleep(10);
}
}
Example Watchdog Driver: see Documentation/watchdog/src/watchdog-simple.c
Contact Information
......
......@@ -17,14 +17,15 @@
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>
#define WDT_DEFAULT_TIME 5 /* 5 seconds */
#define WDT_MAX_TIME 256 /* 256 seconds */
#define WDT_DEFAULT_TIME 5 /* seconds */
#define WDT_MAX_TIME 256 /* seconds */
static int wdt_time = WDT_DEFAULT_TIME;
static int nowayout = WATCHDOG_NOWAYOUT;
......@@ -32,8 +33,10 @@ static int nowayout = WATCHDOG_NOWAYOUT;
module_param(wdt_time, int, 0);
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIME) ")");
#ifdef CONFIG_WATCHDOG_NOWAYOUT
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
#endif
static unsigned long at91wdt_busy;
......@@ -196,27 +199,84 @@ static struct miscdevice at91wdt_miscdev = {
.fops = &at91wdt_fops,
};
static int __init at91_wdt_init(void)
static int __init at91wdt_probe(struct platform_device *pdev)
{
int res;
/* Check that the heartbeat value is within range; if not reset to the default */
if (at91_wdt_settimeout(wdt_time)) {
at91_wdt_settimeout(WDT_DEFAULT_TIME);
printk(KERN_INFO "at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time);
}
if (at91wdt_miscdev.dev)
return -EBUSY;
at91wdt_miscdev.dev = &pdev->dev;
res = misc_register(&at91wdt_miscdev);
if (res)
return res;
printk("AT91 Watchdog Timer enabled (%d seconds, nowayout=%d)\n", wdt_time, nowayout);
printk("AT91 Watchdog Timer enabled (%d seconds%s)\n", wdt_time, nowayout ? ", nowayout" : "");
return 0;
}
static int __exit at91wdt_remove(struct platform_device *pdev)
{
int res;
res = misc_deregister(&at91wdt_miscdev);
if (!res)
at91wdt_miscdev.dev = NULL;
return res;
}
static void at91wdt_shutdown(struct platform_device *pdev)
{
at91_wdt_stop();
}
#ifdef CONFIG_PM
static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message)
{
at91_wdt_stop();
return 0;
}
static int at91wdt_resume(struct platform_device *pdev)
{
if (at91wdt_busy)
at91_wdt_start();
return 0;
}
#else
#define at91wdt_suspend NULL
#define at91wdt_resume NULL
#endif
static struct platform_driver at91wdt_driver = {
.probe = at91wdt_probe,
.remove = __exit_p(at91wdt_remove),
.shutdown = at91wdt_shutdown,
.suspend = at91wdt_suspend,
.resume = at91wdt_resume,
.driver = {
.name = "at91_wdt",
.owner = THIS_MODULE,
},
};
static int __init at91_wdt_init(void)
{
/* Check that the heartbeat value is within range; if not reset to the default */
if (at91_wdt_settimeout(wdt_time)) {
at91_wdt_settimeout(WDT_DEFAULT_TIME);
pr_info("at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time);
}
return platform_driver_register(&at91wdt_driver);
}
static void __exit at91_wdt_exit(void)
{
misc_deregister(&at91wdt_miscdev);
platform_driver_unregister(&at91wdt_driver);
}
module_init(at91_wdt_init);
......
......@@ -205,6 +205,23 @@ static int tco_timer_set_heartbeat (int t)
return 0;
}
static int tco_timer_get_timeleft (int *time_left)
{
unsigned char val;
spin_lock(&tco_lock);
/* read the TCO Timer */
val = inb (TCO1_RLD);
val &= 0x3f;
spin_unlock(&tco_lock);
*time_left = (int)((val * 6) / 10);
return 0;
}
/*
* /dev/watchdog handling
*/
......@@ -272,6 +289,7 @@ static int i8xx_tco_ioctl (struct inode *inode, struct file *file,
{
int new_options, retval = -EINVAL;
int new_heartbeat;
int time_left;
void __user *argp = (void __user *)arg;
int __user *p = argp;
static struct watchdog_info ident = {
......@@ -329,6 +347,14 @@ static int i8xx_tco_ioctl (struct inode *inode, struct file *file,
case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p);
case WDIOC_GETTIMELEFT:
{
if (tco_timer_get_timeleft(&time_left))
return -EINVAL;
return put_user(time_left, p);
}
default:
return -ENOIOCTLCMD;
}
......
......@@ -390,6 +390,24 @@ static int pcipcwd_get_temperature(int *temperature)
return 0;
}
static int pcipcwd_get_timeleft(int *time_left)
{
int msb;
int lsb;
/* Read the time that's left before rebooting */
/* Note: if the board is not yet armed then we will read 0xFFFF */
send_command(CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb);
*time_left = (msb << 8) + lsb;
if (debug >= VERBOSE)
printk(KERN_DEBUG PFX "Time left before next reboot: %d\n",
*time_left);
return 0;
}
/*
* /dev/watchdog handling
*/
......@@ -512,6 +530,16 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file,
case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p);
case WDIOC_GETTIMELEFT:
{
int time_left;
if (pcipcwd_get_timeleft(&time_left))
return -EFAULT;
return put_user(time_left, p);
}
default:
return -ENOIOCTLCMD;
}
......
......@@ -317,6 +317,19 @@ static int usb_pcwd_get_temperature(struct usb_pcwd_private *usb_pcwd, int *temp
return 0;
}
static int usb_pcwd_get_timeleft(struct usb_pcwd_private *usb_pcwd, int *time_left)
{
unsigned char msb, lsb;
/* Read the time that's left before rebooting */
/* Note: if the board is not yet armed then we will read 0xFFFF */
usb_pcwd_send_command(usb_pcwd, CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb);
*time_left = (msb << 8) + lsb;
return 0;
}
/*
* /dev/watchdog handling
*/
......@@ -422,6 +435,16 @@ static int usb_pcwd_ioctl(struct inode *inode, struct file *file,
case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p);
case WDIOC_GETTIMELEFT:
{
int time_left;
if (usb_pcwd_get_timeleft(usb_pcwd_device, &time_left))
return -EFAULT;
return put_user(time_left, p);
}
default:
return -ENOIOCTLCMD;
}
......
......@@ -28,6 +28,9 @@ struct watchdog_info {
#define WDIOC_KEEPALIVE _IOR(WATCHDOG_IOCTL_BASE, 5, int)
#define WDIOC_SETTIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 7, int)
#define WDIOC_SETPRETIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
#define WDIOC_GETPRETIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 9, int)
#define WDIOC_GETTIMELEFT _IOR(WATCHDOG_IOCTL_BASE, 10, int)
#define WDIOF_UNKNOWN -1 /* Unknown flag error */
#define WDIOS_UNKNOWN -1 /* Unknown status error */
......@@ -41,6 +44,7 @@ struct watchdog_info {
#define WDIOF_POWEROVER 0x0040 /* Power over voltage */
#define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */
#define WDIOF_MAGICCLOSE 0x0100 /* Supports magic close char */
#define WDIOF_PRETIMEOUT 0x0200 /* Pretimeout (in seconds), get/set */
#define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */
#define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment