Re: RFC: drivers/char/watchdog/pcwd.c + drivers/char/watchdog/pcwd_pci.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Andrew,

> > On Tue, 17 Apr 2007 20:58:49 +0200 Wim Van Sebroeck <[email protected]> wrote:
> > Would anyone object if we would merge the "full bells and whistles" drivers for
> > the pcwd isa and pci cards in the kernel tree. (It basically only adds some extra
> > /proc routines). This would make it easier to maintain the "full bells and whistles"
> > driver.
> 
> It's hard to answer that question.  Please send the patches out in the
> usual manner for review and comment.

for the pcwd.c driver the patch would be as follows (see below).
I'll create the patch for pcwd_pci.c later this week.

Greetings,
Wim.

---------------------------------------------------------------------------------------
--- pcwd.c	2007-03-26 20:11:29.000000000 +0000
+++ pcwd.c	2007-04-19 18:40:04.000000000 +0000
@@ -1,9 +1,18 @@
 /*
- * PC Watchdog Driver
- * by Ken Hollis ([email protected])
+ *	Berkshire ISA-PC Watchdog Card Driver
+ *	by Ken Hollis ([email protected])
  *
- * Permission granted from Simon Machell ([email protected])
- * Written for the Linux Kernel, and GPLed by Ken Hollis
+ *	Permission granted from Simon Machell ([email protected])
+ *	Written for the Linux Kernel, and GPLed by Ken Hollis
+ *
+ *	More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/
+ *
+ *	Copyright (c) 1998-2002 Holger Eiboeck  <[email protected]>,
+ *				  All Rights Reserved.
+ *
+ *	http://www.pcwd.de/
+ *
+ *	(c) Copyright 2005-2007 Wim Van Sebroeck <[email protected]>.
  *
  * 960107	Added request_region routines, modulized the whole thing.
  * 960108	Fixed end-of-file pointer (Thanks to Dan Hollis), added
@@ -45,8 +54,7 @@
  */
 
 /*
- *	A bells and whistles driver is available from http://www.pcwd.de/
- *	More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/
+ *      Includes, defines, variables, module parameters, ...
  */
 
 #include <linux/module.h>	/* For module specific items */
@@ -65,13 +73,18 @@
 #include <linux/fs.h>		/* For file operations */
 #include <linux/ioport.h>	/* For io-port access */
 #include <linux/spinlock.h>	/* For spin_lock/spin_unlock/... */
+#include <linux/string.h>	/* For string manipulations */
+#ifdef CONFIG_PROC_FS
+#include <linux/stat.h>		/* For S_IFREG/S_IRUGO/S_IWUSR */
+#include <linux/proc_fs.h>	/* For create_proc_entry/remove_proc_entry/ ... */
+#endif
 
 #include <asm/uaccess.h>	/* For copy_to_user/put_user/... */
 #include <asm/io.h>		/* For inb/outb/... */
 
 /* Module and version information */
-#define WATCHDOG_VERSION "1.18"
-#define WATCHDOG_DATE "21 Jan 2007"
+#define WATCHDOG_VERSION "1.52"
+#define WATCHDOG_DATE "18 Apr 2007"
 #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog"
 #define WATCHDOG_NAME "pcwd"
 #define PFX WATCHDOG_NAME ": "
@@ -155,6 +168,10 @@
 /* We can only use 1 card due to the /dev/watchdog restriction */
 static int cards_found;
 
+/* pcwd_private.card_status */
+#define DISABLED                0
+#define ENABLED                 1
+
 /* internal variables */
 static atomic_t open_allowed = ATOMIC_INIT(1);
 static char expect_close;
@@ -169,9 +186,21 @@
 	spinlock_t io_lock;		/* the lock for io operations */
 	struct timer_list timer;	/* The timer that pings the watchdog */
 	unsigned long next_heartbeat;	/* the next_heartbeat for the timer */
+	int card_status;		/* The card's status */
+	unsigned int ping_counter;	/* A counter to keep track how many times the watchdog was tickled */
+#ifdef CONFIG_PROC_FS
+	spinlock_t proc_lock;		/* the lock for /proc/ operations */
+	struct proc_dir_entry *proc_pcwd;	/* the /proc/ entry */
+#endif
 } pcwd_private;
 
 /* module parameters */
+#define CELSIUS		0
+#define FAHRENHEIT	1
+static int proc_temp_mode = CELSIUS;
+module_param(proc_temp_mode, int, 0);
+MODULE_PARM_DESC(proc_temp_mode, "which temperature mode to use in /proc/ 0=Celsius, 1=Fahrenheit (default=0)");
+
 #define QUIET	0	/* Default */
 #define VERBOSE	1	/* Verbose */
 #define DEBUG	2	/* print fancy stuff too */
@@ -398,6 +427,7 @@
 		}
 	}
 
+	pcwd_private.card_status=ENABLED;
 	if (debug >= VERBOSE)
 		printk(KERN_DEBUG PFX "Watchdog started\n");
 
@@ -426,6 +456,7 @@
 		}
 	}
 
+	pcwd_private.card_status=DISABLED;
 	if (debug >= VERBOSE)
 		printk(KERN_DEBUG PFX "Watchdog stopped\n");
 
@@ -437,6 +468,9 @@
 	/* user land ping */
 	pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
 
+	if (pcwd_private.card_status == ENABLED)
+		pcwd_private.ping_counter++;
+
 	if (debug >= DEBUG)
 		printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n");
 
@@ -533,7 +567,7 @@
 	return 0;
 }
 
-static int pcwd_get_temperature(int *temperature)
+static int pcwd_get_temperature(int *temperature, int temp_mode)
 {
 	/* check that port 0 gives temperature info and no command results */
 	if (pcwd_private.command_mode)
@@ -543,22 +577,358 @@
 	if (!pcwd_private.supports_temp)
 		return -ENODEV;
 
+	spin_lock(&pcwd_private.io_lock);
+	*temperature = inb(pcwd_private.io_addr);
+	spin_unlock(&pcwd_private.io_lock);
+
 	/*
-	 * Convert celsius to fahrenheit, since this was
+	 * Convert celsius to fahrenheit, this is normally
 	 * the decided 'standard' for this return value.
 	 */
-	spin_lock(&pcwd_private.io_lock);
-	*temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32;
-	spin_unlock(&pcwd_private.io_lock);
+	if (temp_mode == FAHRENHEIT)
+		*temperature = (*temperature * 9 / 5) + 32;
 
 	if (debug >= DEBUG) {
-		printk(KERN_DEBUG PFX "temperature is: %d F\n",
-			*temperature);
+		printk(KERN_DEBUG PFX "temperature is: %d %s\n",
+			*temperature, (temp_mode == CELSIUS) ? "C" :"F");
 	}
 
 	return 0;
 }
 
+#ifdef CONFIG_PROC_FS
+static int
+pcwd_reset_pc(void)
+{
+	int rv;
+
+	if (set_command_mode()) {
+		/* Reset PC */
+		printk(KERN_INFO PFX "reseting PC!\n");
+
+		rv = send_isa_command(CMD_ISA_RESET_PC);
+
+		if (!(rv == 0xAA)) {
+			printk(KERN_ERR PFX "Card did not acknowledge reset attempt.\n");
+			unset_command_mode();
+			return 0;
+		}
+
+		unset_command_mode();
+		printk(KERN_INFO PFX "reseting PC ACK!\n");
+
+		pcwd_private.card_status = DISABLED;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+pcwd_set_timeout(char *sub_command)
+{
+	char *new_sub_command = sub_command;
+	int i;
+
+	while (*sub_command == ' ')
+		sub_command++;
+	if (sub_command == new_sub_command) {
+		printk(KERN_ERR PFX "illegal timeout argument '%s'\n", sub_command);
+		return 1;
+	}
+	i = simple_strtoul(sub_command, NULL, 0);
+	if (!pcwd_set_heartbeat(i)) {
+		if (debug >= VERBOSE)
+			printk(KERN_INFO PFX "timeout %d\n", heartbeat);
+	} else {
+		printk(KERN_ERR PFX
+			"illegal timeout argument '%s', should be a number between 2 and 7200\n",
+			sub_command);
+		return 1;
+	}
+	return 0;
+}
+
+static int
+pcwd_set_arm(char *sub_command)
+{
+	char *new_sub_command = sub_command;
+	int i = 0;
+
+	while (*sub_command == ' ')
+		sub_command++;
+	if (sub_command == new_sub_command) {
+		printk(KERN_ERR PFX
+		       "illegal arm argument '%s', should be 0, 30 or 60\n",
+		       sub_command);
+		return 1;
+	}
+
+	set_command_mode();
+
+	if ((strcmp(sub_command, "0") == 0)
+	    || (strcmp(sub_command, "immediately") == 0)) {
+		if ((i = send_isa_command(CMD_ISA_ARM_0)) == 0x87) {
+			if (debug >= VERBOSE)
+				printk(KERN_INFO PFX "arm immediately\n");
+			return 0;
+		}
+	} else if (strcmp(sub_command, "30") == 0) {
+		if ((i = send_isa_command(CMD_ISA_ARM_30)) == 0x88) {
+			if (debug >= VERBOSE)
+				printk(KERN_INFO PFX "arm after 30s more\n");
+			return 0;
+		}
+	} else if (strcmp(sub_command, "60") == 0) {
+		if ((i = send_isa_command(CMD_ISA_ARM_60)) != 0x89) {
+			if (debug >= VERBOSE)
+				printk(KERN_INFO PFX "arm after 60s more\n");
+			return 0;
+		}
+	} else {
+		printk(KERN_ERR PFX
+		       "illegal arm argument '%s', should be 0, 30 or 60\n",
+		       sub_command);
+		return 1;
+	}
+
+	if (i == 0xF5)
+		printk(KERN_INFO PFX "already armed\n");
+
+	unset_command_mode();
+	return 0;
+}
+
+static void
+pcwd_user_help(void)
+{
+	printk(" pcwd proc command interface help:\n");
+	printk("   ping, pong, tickle - tickle the timer\n");
+	printk("   hardreset - reset pc\n");
+	printk("   enable, start - start the watchdog\n");
+	printk("   disable, stop - stop the watchdog\n");
+	printk("   clear - clear trip status and led\n");
+	printk("   verbose, debug, quiet - command reponse\n");
+	printk("   fahrenheit, celsius - display temp in\n");
+	printk(" extended commands:\n");
+	printk("   arm [val] - isa [30,60,90]\n");
+	printk("   timeout [val]\n");
+}
+
+static int
+pcwd_user_command(char *user_command)
+{
+	if ((strcmp(user_command, "ping") == 0)
+	    || (strcmp(user_command, "pong") == 0)
+	    || (strcmp(user_command, "tickle") == 0)) {
+		pcwd_keepalive();
+		if (debug >= VERBOSE)
+			printk(KERN_INFO PFX "ping...\n");
+	} else if ((strcmp(user_command, "enable") == 0)
+	    || (strcmp(user_command, "start") == 0)) {
+		if (!pcwd_start()) {
+			if (debug >= VERBOSE)
+				printk(KERN_INFO PFX "watchdog enabled.\n");
+		}
+	} else if ((strcmp(user_command, "disable") == 0)
+	    || (strcmp(user_command, "stop") == 0)) {
+		if (!pcwd_stop()) {
+			if (debug >= VERBOSE)
+				printk(KERN_INFO PFX "watchdog disabled!\n");
+		}
+	} else if (strcmp(user_command, "clear") == 0) {
+		pcwd_clear_status();
+		if (debug >= VERBOSE)
+			printk(KERN_INFO PFX "cleared trip status\n");
+	} else if (strcmp(user_command, "hardreset") == 0) {
+		pcwd_reset_pc();
+		if (debug >= VERBOSE)
+			printk(KERN_INFO PFX "hardreset PC!\n");
+	} else if (strcmp(user_command, "quiet") == 0) {
+		debug = QUIET;
+	} else if (strcmp(user_command, "verbose") == 0) {
+		debug = VERBOSE;
+		printk(KERN_INFO PFX "verbose mode on.\n");
+	} else if (strcmp(user_command, "debug") == 0) {
+		debug = DEBUG;
+		printk(KERN_INFO PFX "debug mode on.\n");
+	} else if (strcmp(user_command, "celsius") == 0) {
+		proc_temp_mode = CELSIUS;
+		if (debug >= VERBOSE)
+			printk(KERN_INFO PFX "display temp in Celsius.\n");
+	} else if (strcmp(user_command, "fahrenheit") == 0) {
+		proc_temp_mode = FAHRENHEIT;
+		if (debug >= VERBOSE)
+			printk(KERN_INFO PFX "display temp in Fahrenheit.\n");
+	} else if (strcmp(user_command, "help") == 0) {
+		pcwd_user_help();
+	} else if (strncmp(user_command, "arm", 3) == 0) {
+		if ((pcwd_private.fw_ver_str[0] >= '1')
+		    && (pcwd_private.fw_ver_str[2] >= '2')) {
+			if (pcwd_set_arm(&user_command[3])) {
+				printk(KERN_ERR PFX "arm FAILED.\n");
+				return 0;
+			}
+		} else {
+			printk(KERN_ERR
+			       "pcwd: ISA Card and firmware > 1.20 needed.\n");
+			return 0;
+		}
+	} else if (strncmp(user_command, "timeout", 7) == 0) {
+		if (pcwd_set_timeout(&user_command[7])) {
+			printk(KERN_ERR PFX "timeout FAILED.\n");
+			return 0;
+		}
+	} else {
+		printk(KERN_ERR "illegal user command: '%s'\n", user_command);
+		pcwd_user_help();
+		return 0;
+	}
+	return 1;
+}
+
+static int
+pcwd_write_proc(struct file *file, const char *buffer,
+		unsigned long count, void *data)
+{
+	char command_buffer[80];
+	int rc, length;
+
+	/* write only allowed for root users */
+	if (current->euid != 0)
+		return -EACCES;
+
+	if (count > sizeof (command_buffer) - 1)
+		return -EINVAL;
+
+	if (copy_from_user(command_buffer, buffer, count))
+		return -EFAULT;
+
+	command_buffer[count] = '\0';
+	length = strlen(command_buffer);
+	if (command_buffer[length - 1] == '\n')
+		command_buffer[--length] = '\0';
+
+	spin_lock(&pcwd_private.proc_lock);
+	rc = pcwd_user_command(command_buffer);
+	spin_unlock(&pcwd_private.proc_lock);
+	return (rc ? count : -EBUSY);
+}
+
+/* This macro frees the machine specific function from bounds checking and
+ *  * this like that... [from nvram.c]*/
+#define PRINT_PROC(fmt,args...)                         \
+    do {                                                \
+        *len += sprintf( buffer+*len, fmt, ##args );    \
+        if (*begin + *len > offset + size)              \
+            return( 0 );                                \
+        if (*begin + *len < offset) {                   \
+            *begin += *len;                             \
+            *len = 0;                                   \
+        }                                               \
+    } while(0)
+
+static int
+pcwd_proc_info(unsigned char *buf, char *buffer, int *len,
+	       off_t * begin, off_t offset, int size)
+{
+	int sw, i;
+
+	PRINT_PROC("%s\n", DRIVER_VERSION);
+
+	if (pcwd_private.revision == PCWD_REVISION_C) {
+		PRINT_PROC("Firmware     : Version %s\n", pcwd_private.fw_ver_str);
+		PRINT_PROC("Option Switch: Delay Time ");
+		sw = pcwd_get_option_switches();
+		switch (sw & 0x07) {
+		case 0:
+			PRINT_PROC("20 s");
+			break;
+		case 1:
+			PRINT_PROC("40 s");
+			break;
+		case 2:
+			PRINT_PROC("1 min");
+			break;
+		case 3:
+			PRINT_PROC("5 min");
+			break;
+		case 4:
+			PRINT_PROC("10 min");
+			break;
+		case 5:
+			PRINT_PROC("30 min");
+			break;
+		case 6:
+			PRINT_PROC("1 h");
+			break;
+		case 7:
+			PRINT_PROC("2 h");
+			break;
+		}
+		PRINT_PROC((sw & 0x08) ? ", POD on" : ", POD off");
+		PRINT_PROC((sw & 0x10) ? ", TRE on" : ", TRE off");
+		PRINT_PROC((sw & 0x20) ? ", R2M on" : ", R2M off");
+		PRINT_PROC((sw & 0x40) ? ", R1M on" : ", R1M off");
+		PRINT_PROC((sw & 0x80) ? ", RTM on\n" : ", RTM off\n");
+	} else {
+		PRINT_PROC("Firmware ROM not present\n");
+	}
+
+	PRINT_PROC("Temperature  : ");
+	if (pcwd_private.supports_temp && (!pcwd_get_temperature(&i, proc_temp_mode))) {
+		PRINT_PROC("%d ", i);
+		PRINT_PROC((proc_temp_mode == CELSIUS) ? "°C\n" : "°F\n");
+	} else {
+		PRINT_PROC("Card without temp option\n");
+	}
+
+	if (pcwd_private.card_status == ENABLED)
+		PRINT_PROC("Card status  : Timer enabled\n");
+	else
+		PRINT_PROC("Card status  : Timer disabled\n");
+
+	spin_lock(&pcwd_private.io_lock);
+	if (pcwd_private.revision == PCWD_REVISION_A) {
+		sw = inb_p(pcwd_private.io_addr) & WD_WDRST;
+	} else {
+		sw = inb_p(pcwd_private.io_addr + 1) & WD_REVC_WTRP;
+	}
+	spin_unlock(&pcwd_private.io_lock);
+	PRINT_PROC("Reboot status: ");
+	PRINT_PROC(sw ? "Tripped\n" : "Not tripped\n");
+
+	if ((pcwd_private.boot_status & WDIOF_CARDRESET) && (pcwd_private.boot_status & WDIOF_OVERHEAT)) {
+		PRINT_PROC("Boot status  : CPU Overheat  + Watchdog caused reboot\n");
+	} else if (pcwd_private.boot_status & WDIOF_CARDRESET) {
+		PRINT_PROC("Boot status  : Watchdog caused reboot\n");
+	} else if (pcwd_private.boot_status & WDIOF_OVERHEAT) {
+		PRINT_PROC("Boot status  : CPU Overheat\n");
+	} else {
+		PRINT_PROC("Boot status  : Cold boot or reset\n");
+	}
+
+	// PRINT_PROC("Ping Count   : %d\n", ping_counter);
+	return 1;
+}
+
+static int
+pcwd_read_proc(char *buffer, char **start, off_t offset,
+	       int size, int *eof, void *data)
+{
+	int len = 0;
+	off_t begin = 0;
+
+	spin_lock(&pcwd_private.proc_lock);
+	*eof = pcwd_proc_info(NULL, buffer, &len, &begin, offset, size);
+	spin_unlock(&pcwd_private.proc_lock);
+
+	if (offset >= begin + len)
+		return (0);
+	*start = buffer + (offset - begin);	// CORRECTED !!!!
+	return (size < begin + len - offset ? size : begin + len - offset);
+}
+#endif				/* PROC_FS */
+
 /*
  *	/dev/watchdog handling
  */
@@ -598,7 +968,7 @@
 		return put_user(pcwd_private.boot_status, argp);
 
 	case WDIOC_GETTEMP:
-		if (pcwd_get_temperature(&temperature))
+		if (pcwd_get_temperature(&temperature, FAHRENHEIT))
 			return -EFAULT;
 
 		return put_user(temperature, argp);
@@ -711,7 +1081,7 @@
 {
 	int temperature;
 
-	if (pcwd_get_temperature(&temperature))
+	if (pcwd_get_temperature(&temperature, FAHRENHEIT))
 		return -EFAULT;
 
 	if (copy_to_user(buf, &temperature, 1))
@@ -809,7 +1179,8 @@
 
 	cards_found++;
 	if (cards_found == 1)
-		printk(KERN_INFO PFX "v%s Ken Hollis ([email protected])\n", WD_VER);
+		printk(KERN_INFO PFX "v%s by Ken Hollis <[email protected]>, Holger Eiboeck <[email protected]> & Wim Van Sebroeck <[email protected]>\n",
+			WD_VER);
 
 	if (cards_found > 1) {
 		printk(KERN_ERR PFX "This driver only supports 1 device\n");
@@ -898,6 +1269,14 @@
 		return ret;
 	}
 
+#ifdef CONFIG_PROC_FS
+	if ((pcwd_private.proc_pcwd =
+	     create_proc_entry(WATCHDOG_NAME, S_IFREG | S_IRUGO | S_IWUSR, 0))) {
+		pcwd_private.proc_pcwd->read_proc = pcwd_read_proc;
+		pcwd_private.proc_pcwd->write_proc = pcwd_write_proc;
+	}
+#endif
+
 	printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
 		heartbeat, nowayout);
 
@@ -906,6 +1285,11 @@
 
 static void __devexit pcwatchdog_exit(void)
 {
+#ifdef CONFIG_PROC_FS
+	if (pcwd_private.proc_pcwd)
+		remove_proc_entry(WATCHDOG_NAME, 0);
+#endif
+
 	/*  Disable the board  */
 	if (!nowayout)
 		pcwd_stop();
@@ -981,6 +1365,12 @@
 	int i, found = 0;
 
 	spin_lock_init(&pcwd_private.io_lock);
+#ifdef CONFIG_PROC_FS
+	spin_lock_init(&pcwd_private.proc_lock);
+#endif
+
+	if (proc_temp_mode)
+		proc_temp_mode = FAHRENHEIT;
 
 	for (i = 0; pcwd_ioports[i] != 0; i++) {
 		if (pcwd_checkcard(pcwd_ioports[i])) {
@@ -1008,8 +1398,8 @@
 module_init(pcwd_init_module);
 module_exit(pcwd_cleanup_module);
 
-MODULE_AUTHOR("Ken Hollis <[email protected]>");
-MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
+MODULE_AUTHOR("Ken Hollis <[email protected]>, Holger Eiboeck <[email protected]>, Wim Van Sebroeck <[email protected]>");
+MODULE_DESCRIPTION("Driver for the Berkshire ISA-PC watchdog cards");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS_MISCDEV(TEMP_MINOR);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux