[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20070419190055.GA31632@infomag.infomag.iguana.be>
Date: Thu, 19 Apr 2007 21:00:55 +0200
From: Wim Van Sebroeck <wim@...ana.be>
To: Andrew Morton <akpm@...ux-foundation.org>
Cc: linux-kernel@...r.kernel.org, holger@...oeck.de
Subject: Re: RFC: drivers/char/watchdog/pcwd.c + drivers/char/watchdog/pcwd_pci.c
Hi Andrew,
> > On Tue, 17 Apr 2007 20:58:49 +0200 Wim Van Sebroeck <wim@...ana.be> 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 (khollis@...gate.com)
+ * Berkshire ISA-PC Watchdog Card Driver
+ * by Ken Hollis (khollis@...gate.com)
*
- * Permission granted from Simon Machell (smachell@...kprod.com)
- * Written for the Linux Kernel, and GPLed by Ken Hollis
+ * Permission granted from Simon Machell (smachell@...kprod.com)
+ * 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 <holger@...oeck.de>,
+ * All Rights Reserved.
+ *
+ * http://www.pcwd.de/
+ *
+ * (c) Copyright 2005-2007 Wim Van Sebroeck <wim@...ana.be>.
*
* 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 (kenji@...gate.com)\n", WD_VER);
+ printk(KERN_INFO PFX "v%s by Ken Hollis <kenji@...gate.com>, Holger Eiboeck <holger@...oeck.de> & Wim Van Sebroeck <wim@...ana.be>\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 <kenji@...gate.com>");
-MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
+MODULE_AUTHOR("Ken Hollis <kenji@...gate.com>, Holger Eiboeck <holger@...oeck.de>, Wim Van Sebroeck <wim@...ana.be>");
+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 majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists