patch-2.0.11 linux/drivers/char/pcwd.c
Next file: linux/drivers/char/pcxx.c
Previous file: linux/drivers/char/mem.c
Back to the patch index
Back to the overall index
- Lines: 332
- Date:
Thu Aug 1 16:12:26 1996
- Orig file:
v2.0.10/linux/drivers/char/pcwd.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v2.0.10/linux/drivers/char/pcwd.c linux/drivers/char/pcwd.c
@@ -0,0 +1,331 @@
+/*
+ * PC Watchdog Driver
+ * by Ken Hollis (khollis@bitgate.com)
+ *
+ * Permission granted from Simon Machell (73244.1270@compuserve.com)
+ * Written for the Linux Kernel, and GPLed by Ken Hollis
+ *
+ * 960107 Added request_region routines, modulized the whole thing.
+ * 960108 Fixed end-of-file pointer (Thanks to Dan Hollis), added
+ * WD_TIMEOUT define.
+ * 960216 Added eof marker on the file, and changed verbose messages.
+ * 960716 Made functional and cosmetic changes to the source for
+ * inclusion in Linux 2.0.x kernels, thanks to Alan Cox.
+ * 960717 Removed read/seek routines, replaced with ioctl. Also, added
+ * check_region command due to Alan's suggestion.
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/pcwd.h>
+
+#include <asm/io.h>
+
+#define WD_VER "0.41 (07/17/96)"
+#define WD_MINOR 130 /* Minor device number */
+
+#define WD_TIMEOUT 3 /* 1 1/2 seconds for a timeout */
+
+#define WD_TIMERRESET_PORT1 0x270 /* Reset port - first choice */
+#define WD_TIMERRESET_PORT2 0x370 /* Reset port - second choice */
+#define WD_CTLSTAT_PORT1 0x271 /* Control port - first choice */
+#define WD_CTLSTAT_PORT2 0x371 /* Control port - second choice */
+#define WD_PORT_EXTENT 2 /* Takes up two addresses */
+
+#define WD_WDRST 0x01 /* Previously reset state */
+#define WD_T110 0x02 /* Temperature overheat sense */
+#define WD_HRTBT 0x04 /* Heartbeat sense */
+#define WD_RLY2 0x08 /* External relay triggered */
+#define WD_SRLY2 0x80 /* Software external relay triggered */
+
+static int current_ctlport, current_readport;
+static int is_open, is_eof;
+
+int pcwd_checkcard(void)
+{
+ int card_dat, prev_card_dat, found = 0, count = 0, done = 0;
+
+ /* As suggested by Alan Cox */
+ if (check_region(current_ctlport, WD_PORT_EXTENT)) {
+ printk("pcwd: Port 0x%x unavailable.\n", current_ctlport);
+ return 0;
+ }
+
+ card_dat = 0x00;
+ prev_card_dat = 0x00;
+
+ prev_card_dat = inb(current_readport);
+
+ while(count < WD_TIMEOUT) {
+#ifdef DEBUG
+ printk("pcwd: Run #%d on port 0x%03x\n", count, current_readport);
+#endif
+
+ /* Read the raw card data from the port, and strip off the
+ first 4 bits */
+
+ card_dat = inb_p(current_readport);
+ card_dat &= 0x000F;
+
+ /* Sleep 1/2 second (or 500000 microseconds :) */
+
+ udelay(500000L);
+ done = 0;
+
+ /* 0x0F usually means that no card data is present, or the card
+ is not installed on this port. If 0x0F is present here, it's
+ normally safe to assume there's no card at that base address. */
+
+ if (card_dat == 0x0F) {
+ count++;
+ done = 1;
+
+#ifdef DEBUG
+ printk("pcwd: I show nothing on this port.\n");
+#endif
+ }
+
+ /* If there's a heart beat in both instances, then this means we
+ found our card. This also means that either the card was
+ previously reset, or the computer was power-cycled. */
+
+ if ((card_dat & WD_HRTBT) && (prev_card_dat & WD_HRTBT) &&
+ (!done)) {
+ found = 1;
+ done = 1;
+#ifdef DEBUG
+ printk("pcwd: I show alternate heart beats. Card detected.\n");
+#endif
+ break;
+ }
+
+ /* If the card data is exactly the same as the previous card data,
+ it's safe to assume that we should check again. The manual says
+ that the heart beat will change every second (or the bit will
+ toggle), and this can be used to see if the card is there. If
+ the card was powered up with a cold boot, then the card will
+ not start blinking until 2.5 minutes after a reboot, so this
+ bit will stay at 1. */
+
+ if ((card_dat == prev_card_dat) && (!done)) {
+ count++;
+#ifdef DEBUG
+ printk("pcwd: The card data is exactly the same (possibility).\n");
+#endif
+ done = 1;
+ }
+
+ /* If the card data is toggling any bits, this means that the heart
+ beat was detected, or something else about the card is set. */
+
+ if ((card_dat != prev_card_dat) && (!done)) {
+ done = 1;
+ found = 1;
+#ifdef DEBUG
+ printk("pcwd: I show alternate heart beats. Card detected.\n");
+#endif
+ break;
+ }
+
+ /* Otherwise something else strange happened. */
+
+ if (!done)
+ count++;
+ }
+
+ return((found) ? 1 : 0);
+}
+
+void pcwd_showprevstate(void)
+{
+ int card_status = 0x0000;
+
+ card_status = inb(current_readport);
+
+ if (card_status & WD_WDRST)
+ printk("pcwd: Previous reboot was caused by the card.\n");
+
+ if (card_status & WD_T110)
+ printk("pcwd: CPU overheat sense\n");
+}
+
+static int pcwd_return_data(void)
+{
+ return(inb(current_readport));
+}
+
+static int pcwd_write(struct inode *inode, struct file *file, const char *data,
+ int len)
+{
+ int wdrst_stat;
+
+ if (!is_open)
+ return -EIO;
+
+#ifdef DEBUG
+ printk("pcwd: write request\n");
+#endif
+
+ wdrst_stat = inb_p(current_readport);
+ wdrst_stat &= 0x0F;
+
+ wdrst_stat |= WD_WDRST;
+
+ outb_p(wdrst_stat, current_ctlport);
+
+ return(1);
+}
+
+static int pcwd_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int i, cdat, rv;
+
+ switch(cmd) {
+ default:
+ return -ENOIOCTLCMD;
+
+ case PCWD_GETSTAT:
+ i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(int));
+ if (i)
+ return i;
+ else {
+ cdat = pcwd_return_data();
+ rv = 0;
+
+ if (cdat & WD_WDRST)
+ rv |= 0x01;
+
+ if (cdat & WD_T110)
+ rv |= 0x02;
+
+ put_user(rv, (int *) arg);
+ return 0;
+ }
+ break;
+
+ case PCWD_PING:
+ pcwd_write(NULL, NULL, NULL, 1); /* Is this legal? */
+ break;
+ }
+
+ return 0;
+}
+
+static int pcwd_open(struct inode *ino, struct file *filep)
+{
+#ifdef DEBUG
+ printk("pcwd: open request\n");
+#endif
+
+ MOD_INC_USE_COUNT;
+ is_eof = 0;
+ return(0);
+}
+
+static void pcwd_close(struct inode *ino, struct file *filep)
+{
+#ifdef DEBUG
+ printk("pcwd: close request\n");
+#endif
+
+ MOD_DEC_USE_COUNT;
+}
+
+struct file_operations pcwd_fops = {
+ NULL, /* Seek */
+ NULL, /* Read */
+ pcwd_write, /* Write */
+ NULL, /* Readdir */
+ NULL, /* Select */
+ pcwd_ioctl, /* IOctl */
+ NULL, /* MMAP */
+ pcwd_open, /* Open */
+ pcwd_close, /* Close */
+ NULL
+};
+
+static struct miscdevice pcwd_miscdev = {
+ WD_MINOR,
+ "pcwatchdog",
+ &pcwd_fops
+};
+
+#ifdef MODULE
+int init_module(void)
+#else
+int pcwatchdog_init(void)
+#endif
+{
+#ifdef DEBUG
+ printk("pcwd: Success.\n");
+#endif
+ printk("pcwd: v%s Ken Hollis (khollis@bitgate.com)\n", WD_VER);
+
+#ifdef DEBUG
+ printk("pcwd: About to perform card autosense loop.\n");
+#endif
+
+ is_eof = 0;
+ is_open = 0;
+
+ current_ctlport = WD_TIMERRESET_PORT1;
+ current_readport = WD_CTLSTAT_PORT1;
+
+ if (!pcwd_checkcard()) {
+#ifdef DEBUG
+ printk("pcwd: Trying port 0x370.\n");
+#endif
+
+ current_ctlport = WD_TIMERRESET_PORT2;
+ current_readport = WD_CTLSTAT_PORT2;
+
+ if (!pcwd_checkcard()) {
+ printk("pcwd: No card detected.\n");
+ return(-EIO);
+ } else
+ printk("pcwd: Port available at 0x370.\n");
+ } else
+ printk("pcwd: Port available at 0x270.\n");
+
+ pcwd_showprevstate();
+
+#ifdef DEBUG
+ printk("pcwd: Requesting region entry\n");
+#endif
+
+ request_region(current_ctlport, WD_PORT_EXTENT, "PCWD (Berkshire)");
+
+#ifdef DEBUG
+ printk("pcwd: character device creation.\n");
+#endif
+
+ misc_register(&pcwd_miscdev);
+
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ misc_deregister(&pcwd_miscdev);
+ release_region(current_ctlport, 2);
+#ifdef DEBUG
+ printk("pcwd: Cleanup successful.\n");
+#endif
+}
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov