diff -urN linux-2.4.26/Documentation/Configure.help linux-2.4.26-nsawdt-0.1/Documentation/Configure.help --- linux-2.4.26/Documentation/Configure.help Fri Jun 4 13:06:12 2004 +++ linux-2.4.26-nsawdt-0.1/Documentation/Configure.help Fri Aug 6 13:14:35 2004 @@ -20346,6 +20346,20 @@ computer, say `Y' here to support its built-in watchdog timer feature. See the help for CONFIG_WATCHDOG for discussion. +NexGate's NSA onboard Watchdog Timer +CONFIG_NSA_WDT + If you are configuring a Linux kernel for the NexGate Network Security + appliances (NSA), and wish to use the onboard watchdog timer to reboot + the system in case of system crash, say 'Y' here. This driver can also + be compiled as a module by saying 'M'. + + The hardware knows 7 timeout values : 1, 2, 4, 8, 16, 32 and 64 seconds. + The default is set to a reasonable value (16 seconds) which should allow + even a heavily loaded system to still run reliably, and a standard + system to poweroff cleanly. + + See the help for CONFIG_WATCHDOG for discussion. + ALi M7101 Watchdog Timer CONFIG_ALIM7101_WDT This is the driver for the hardware watchdog on the ALi M7101 PMU diff -urN linux-2.4.26/drivers/char/Config.in linux-2.4.26-nsawdt-0.1/drivers/char/Config.in --- linux-2.4.26/drivers/char/Config.in Fri Jun 4 13:06:24 2004 +++ linux-2.4.26-nsawdt-0.1/drivers/char/Config.in Thu Aug 5 23:42:30 2004 @@ -277,6 +277,7 @@ if [ "$CONFIG_8xx" = "y" ]; then tristate ' MPC8xx Watchdog Timer' CONFIG_8xx_WDT fi + tristate ' NexGate NSA onboard Watchdog timer' CONFIG_NSA_WDT fi endmenu diff -urN linux-2.4.26/drivers/char/Makefile linux-2.4.26-nsawdt-0.1/drivers/char/Makefile --- linux-2.4.26/drivers/char/Makefile Fri Jun 4 13:06:28 2004 +++ linux-2.4.26-nsawdt-0.1/drivers/char/Makefile Thu Aug 5 23:32:50 2004 @@ -323,6 +323,7 @@ obj-$(CONFIG_AMD7XX_TCO) += amd7xx_tco.o obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o +obj-$(CONFIG_NSA_WDT) += nsawdt.o subdir-$(CONFIG_MWAVE) += mwave ifeq ($(CONFIG_MWAVE),y) diff -urN linux-2.4.26/drivers/char/nsawdt.c linux-2.4.26-nsawdt-0.1/drivers/char/nsawdt.c --- linux-2.4.26/drivers/char/nsawdt.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.26-nsawdt-0.1/drivers/char/nsawdt.c Fri Aug 6 13:13:54 2004 @@ -0,0 +1,381 @@ +/* + * Watchdog Timer driver for NexCom/NexGate's Network Security Appliances + * for Linux 2.4. + * + * Portions copyright 2004 EXOSEC + * Contact: http://www.exosec.fr/ + * Updates: http://kernel.exosec.net/ + * + * Large parts copied from wdt.c, whose copyright follows : + * + * (c) Copyright 1996-1997 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * 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. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned long nsawdt_is_open; +static int expect_close; + +#define WD_IO_PORT 0xF2 +#define WD_EN_BYPASS 0x10 +#define WD_EN_RESET 0x08 +#define WD_TIMER_MASK 0x07 +#define WD_READ_MASK 0xE0 + +/* time-out is set to 2^margin seconds */ +static int margin = 4; /* 16 seconds by default */ + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int nowayout = 1; +#else +static int nowayout = 0; +#endif + +MODULE_PARM(nowayout,"i"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + +MODULE_PARM(margin, "i"); +MODULE_PARM_DESC(margin, "WD timeout from 0 to 6 (1 to 64 seconds). Default=4 (16 sec)."); + +#ifndef MODULE + +/** + * nsawdt_setup: + * @str: command line string + * + */ + +static int __init nsawdt_setup(char *str) +{ + int ints[2]; + + str = get_options (str, ARRAY_SIZE(ints), ints); + + if (ints[0] > 0) { + margin = ints[1]; + } + + return 1; +} + +__setup("nsawdt=", nsawdt_setup); + +#endif /* !MODULE */ + +/** + * nsawdt_ping: + * + * Reload counter one with the watchdog timeout. A simple read is enough. + */ + +static void nsawdt_ping(void) +{ + inb_p(WD_IO_PORT); +} + +/** + * nsawdt_write: + * @file: file handle to the watchdog + * @buf: buffer to write (unused as data does not matter here + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. + */ + +static ssize_t nsawdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } + } + nsawdt_ping(); + return 1; + } + return 0; +} + +/** + * nsawdt_ioctl: + * @inode: inode of the device + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. + */ + +static int nsawdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int new_margin; + + static struct watchdog_info ident = { + WDIOF_SETTIMEOUT|WDIOF_MAGICCLOSE, + 1, /* firmware version */ + "NSAWDT" + }; + + switch(cmd) { + default: + return -ENOTTY; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0; + + case WDIOC_KEEPALIVE: + nsawdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int *)arg)) + return -EFAULT; + /* Arbitrary, can't find the card's limits */ + if ((new_margin < 0) || (new_margin > 6)) + return -EINVAL; + margin = new_margin; + nsawdt_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(margin, (int *)arg); + } +} + + +/** + * nsawdt_open: + * @inode: inode of device + * @file: file handle to device + * + * One of our two misc devices has been opened. The watchdog device is + * single open and on opening we load the counters. The counter is + * decreased twice a second and strikes at 0. + */ + +static int nsawdt_open(struct inode *inode, struct file *file) +{ + int val; + + switch (MINOR(inode->i_rdev)) { + case WATCHDOG_MINOR: + if (test_and_set_bit(0, &nsawdt_is_open)) + return -EBUSY; + /* + * Activate + */ + + nsawdt_is_open=1; + val = inb_p(WD_IO_PORT) & WD_READ_MASK; + val |= (6 - margin) & WD_TIMER_MASK; + val |= WD_EN_RESET; /* reset when counter reaches 0 */ + outb_p(val, WD_IO_PORT); + return 0; + default: + return -ENODEV; + } +} + + +/** + * nsawdt_release: + * @inode: inode to board + * @file: file handle to board + * + * The watchdog has a configurable API. There is a religious dispute + * between people who want their watchdog to be able to shut down and + * those who want to be sure if the watchdog manager dies the machine + * reboots. In the former case we disable the counters, in the latter + * case you have to open it again very soon. + */ + +static int nsawdt_release(struct inode *inode, struct file *file) +{ + if (MINOR(inode->i_rdev)==WATCHDOG_MINOR) { + if (expect_close) { + /* disable the watchdog */ + outb_p(inb_p(WD_IO_PORT) & WD_READ_MASK, WD_IO_PORT); + } else { + printk(KERN_CRIT "nsawdt: Watchdog device closed unexpectedly. Timer will not stop!\n"); + } + clear_bit(0, &nsawdt_is_open); + } + return 0; +} + +/** + * notify_sys: + * @this: our notifier block + * @code: the event being reported + * @unused: unused + * + * Our notifier is called on system shutdowns. We want to turn the card + * off at reboot otherwise the machine will reboot again during memory + * test or worse yet during the following fsck. This would suck, in fact + * trust me - if it happens it does suck. + */ + +static int nsawdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) { + /* In case of reboot, Set the timer to the longest time-out so + * that it does not interfere with system reboot or halt, but + * still can reboot if the system hangs during shutdown (bios + * or anything else). In case of halt, we'll simply disable it, + * because even if the machine hangs, it will be halted anyway. + */ + + int val; + + val = inb_p(WD_IO_PORT) & WD_READ_MASK; + if (code == SYS_DOWN) { + val |= (6 - margin) & WD_TIMER_MASK; + val |= WD_EN_RESET; /* reset when counter reaches 0 */ + } + outb_p(val, WD_IO_PORT); + } + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static struct file_operations nsawdt_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + write: nsawdt_write, + ioctl: nsawdt_ioctl, + open: nsawdt_open, + release: nsawdt_release, +}; + +static struct miscdevice nsawdt_miscdev= +{ + WATCHDOG_MINOR, + "watchdog", + &nsawdt_fops +}; + +/* + * The WD card needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block nsawdt_notifier= +{ + nsawdt_notify_sys, + NULL, + 0 +}; + +/** + * cleanup_module: + * + * Unload the watchdog. You cannot do this with any file handles open. + * If your watchdog is set to continue ticking on close and you unload + * it, well it keeps ticking. You just have to load a new module before + * the timeout expires, or reboot. + */ + +static void __exit nsawdt_exit(void) +{ + misc_deregister(&nsawdt_miscdev); + unregister_reboot_notifier(&nsawdt_notifier); + //release_region(WD_IO_PORT, 1); +} + +/** + * nsawdt_init: + * + * Set up the WD watchdog board. All we have to do is grab the + * resources we require and bitch if anyone beat us to them. + * The open() function will actually kick the board off. + */ + +static int __init nsawdt_init(void) +{ + int ret; + + ret = misc_register(&nsawdt_miscdev); + + if (ret) { + printk(KERN_ERR "nsawdt: can't misc_register on minor=%d\n", WATCHDOG_MINOR); + goto out; + } + //if (!request_region(WD_IO_PORT, 1, "nsawdt")) { + // printk(KERN_ERR "nsawdt: IO %X is not free.\n", WD_IO_PORT); + // ret = -EBUSY; + // goto outmisc; + //} + ret = register_reboot_notifier(&nsawdt_notifier); + if(ret) { + printk(KERN_ERR "nsawdt: can't register reboot notifier (err=%d)\n", ret); + goto outreg; + } + + ret = 0; + printk(KERN_INFO "NexGate's NSA onboard Watchdog Timer driver 0.1 at 0x%X\n", WD_IO_PORT); + out: + return ret; + + outreg: + //release_region(WD_IO_PORT, 1); + //outmisc: + misc_deregister(&nsawdt_miscdev); + goto out; +} + +module_init(nsawdt_init); +module_exit(nsawdt_exit); + +MODULE_AUTHOR("Willy Tarreau"); +MODULE_DESCRIPTION("Driver for NexGate's NSA onboard watchdog"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS;