diff -urN linux.orig/Documentation/Configure.help linux/Documentation/Configure.help
--- linux.orig/Documentation/Configure.help	Fri Dec  6 09:11:24 2002
+++ linux/Documentation/Configure.help	Fri Dec  6 09:12:27 2002
@@ -26242,6 +26242,31 @@
 
   If unsure, say N.
 
+IPMI top-level message handler
+CONFIG_IPMI_HANDLER
+  This enables the central IPMI message handler, required for IPMI
+  to work.  Note that you must have this enabled to do any other IPMI
+  things.  See IPMI.txt for more details.
+
+Generate a panic event to all BMCs on a panic
+CONFIG_IPMI_PANIC_EVENT
+  When a panic occurs, this will cause the IPMI message handler to
+  generate an IPMI event describing the panic to each interface
+  registered with the message handler.
+
+Device interface for IPMI
+CONFIG_IPMI_DEVICE_INTERFACE
+  This provides an IOCTL interface to the IPMI message handler so
+  userland processes may use IPMI.  It supports poll() and select().
+
+IPMI KCS handler
+CONFIG_IPMI_KCS
+  Provides a driver for a KCS-style interface to a BMC.
+
+IPMI Watchdog Timer
+CONFIG_IPMI_WATCHDOG
+  This enables the IPMI watchdog timer.
+
 #
 # A couple of things I keep forgetting:
 #   capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet,
diff -urN linux.orig/Documentation/IPMI.txt linux/Documentation/IPMI.txt
--- linux.orig/Documentation/IPMI.txt	Wed Dec 31 18:00:00 1969
+++ linux/Documentation/IPMI.txt	Tue Dec 31 13:00:01 2002
@@ -0,0 +1,352 @@
+
+                          The Linux IPMI Driver
+			  ---------------------
+			      Corey Minyard
+			  <minyard@mvista.com>
+			    <minyard@acm.org>
+
+This document describes how to use the IPMI driver for Linux.  If you
+are not familiar with IPMI itself, see the web site at
+http://www.intel.com/design/servers/ipmi/index.htm.  IPMI is a big
+subject and I can't cover it all here!
+
+Basic Design
+------------
+
+The Linux IPMI driver is designed to be very modular and flexible, you
+only need to take the pieces you need and you can use it in many
+different ways.  Because of that, it's broken into many chunks of
+code.  These chunks are:
+
+ipmi_msghandler - This is the central piece of software for the IPMI
+system.  It handles all messages, message timing, and responses.  The
+IPMI users tie into this, and the IPMI physical interfaces (called
+System Management Interfaces, or SMIs) also tie in here.  This
+provides the kernelland interface for IPMI, but does not provide an
+interface for use by application processes.
+
+ipmi_devintf - This provides a userland IOCTL interface for the IPMI
+driver, each open file for this device ties in to the message handler
+as an IPMI user.
+
+ipmi_kcs_drv - A driver for the KCS SMI.  Most system have a KCS
+interface for IPMI.
+
+
+Much documentation for the interface is in the include files.  The
+IPMI include files are:
+
+ipmi.h - Contains the user interface and IOCTL interface for IPMI.
+
+ipmi_smi.h - Contains the interface for SMI drivers to use.
+
+ipmi_msgdefs.h - General definitions for base IPMI messaging.
+
+
+Addressing
+----------
+
+The IPMI addressing works much like IP addresses, you have an overlay
+to handle the different address types.  The overlay is:
+
+  struct ipmi_addr
+  {
+	int   addr_type;
+	short channel;
+	char  data[IPMI_MAX_ADDR_SIZE];
+  };
+
+The addr_type determines what the address really is.  The driver
+currently understands two different types of addresses.
+
+"System Interface" addresses are defined as:
+
+  struct ipmi_system_interface_addr
+  {
+	int   addr_type;
+	short channel;
+  };
+
+and the type is IPMI_SYSTEM_INTERFACE_ADDR_TYPE.  This is used for talking
+straight to the BMC on the current card.  The channel must be
+IPMI_BMC_CHANNEL.
+
+Messages that are destined to go out on the IPMB bus use the
+IPMI_IPMB_ADDR_TYPE address type.  The format is
+
+  struct ipmi_ipmb_addr
+  {
+	int           addr_type;
+	short         channel;
+	unsigned char slave_addr;
+	unsigned char lun;
+  };
+
+The "channel" here is generally zero, but some devices support more
+than one channel, it corresponds to the channel as defined in the IPMI
+spec.
+
+
+Messages
+--------
+
+Messages are defined as:
+
+struct ipmi_msg
+{
+	unsigned char netfn;
+	unsigned char lun;
+	unsigned char cmd;
+	unsigned char *data;
+	int           data_len;
+};
+
+The driver takes care of adding/stripping the header information.  The
+data portion is just the data to be send (do NOT put addressing info
+here) or the response.  Note that the completion code of a response is
+the first item in "data", it is not stripped out because that is how
+all the messages are defined in the spec (and thus makes counting the
+offsets a little easier :-).
+
+When using the IOCTL interface from userland, you must provide a block
+of data for "data", fill it, and set data_len to the length of the
+block of data, even when receiving messages.  Otherwise the driver
+will have no place to put the message.
+
+Messages coming up from the message handler in kernelland will come in
+as:
+
+  struct ipmi_recv_msg
+  {
+	struct list_head link;
+
+	/* The type of message as defined in the "Receive Types"
+           defines above. */
+	int         recv_type;
+
+	ipmi_user_t      *user;
+	struct ipmi_addr addr;
+	long             msgid;
+	struct ipmi_msg  msg;
+
+	/* Call this when done with the message.  It will presumably free
+	   the message and do any other necessary cleanup. */
+	void (*done)(struct ipmi_recv_msg *msg);
+
+	/* Place-holder for the data, don't make any assumptions about
+	   the size or existence of this, since it may change. */
+	unsigned char   msg_data[IPMI_MAX_MSG_LENGTH];
+  };
+
+You should look at the receive type and handle the message
+appropriately.
+
+
+The Upper Layer Interface (Message Handler)
+-------------------------------------------
+
+The upper layer of the interface provides the users with a consistent
+view of the IPMI interfaces.  It allows multiple SMI interfaces to be
+addressed (because some boards actually have multiple BMCs on them)
+and the user should not have to care what type of SMI is below them.
+
+
+Creating the User
+
+To user the message handler, you must first create a user using
+ipmi_create_user.  The interface number specifies which SMI you want
+to connect to, and you must supply callback functions to be called
+when data comes in.  The callback function can run at interrupt level,
+so be careful using the callbacks.  This also allows to you pass in a
+piece of data, the handler_data, that will be passed back to you on
+all calls.
+
+Once you are done, call ipmi_destroy_user() to get rid of the user.
+
+From userland, opening the device automatically creates a user, and
+closing the device automatically destroys the user.
+
+
+Messaging
+
+To send a message from kernel-land, the ipmi_request() call does
+pretty much all message handling.  Most of the parameter are
+self-explanatory.  However, it takes a "msgid" parameter.  This is NOT
+the sequence number of messages.  It is simply a long value that is
+passed back when the response for the message is returned.  You may
+use it for anything you like.
+
+Responses come back in the function pointed to by the ipmi_recv_hndl
+field of the "handler" that you passed in to ipmi_create_user().
+Remember again, these may be running at interrupt level.  Remember to
+look at the receive type, too.
+
+From userland, you fill out an ipmi_req_t structure and use the
+IPMICTL_SEND_COMMAND ioctl.  For incoming stuff, you can use select()
+or poll() to wait for messages to come in.  However, you cannot use
+read() to get them, you must call the IPMICTL_RECEIVE_MSG with the
+ipmi_recv_t structure to actually get the message.  Remember that you
+must supply a pointer to a block of data in the msg.data field, and
+you must fill in the msg.data_len field with the size of the data.
+This gives the receiver a place to actually put the message.
+
+If the message cannot fit into the data you provide, you will get an
+EMSGSIZE error and the driver will leave the data in the receive
+queue.  If you want to get it and have it truncate the message, us
+the IPMICTL_RECEIVE_MSG_TRUNC ioctl.
+
+When you send a command (which is defined by the lowest-order bit of
+the netfn per the IPMI spec) on the IPMB bus, the driver will
+automatically assign the sequence number to the command and save the
+command.  If the response is not receive in the IPMI-specified 5
+seconds, it will generate a response automatically saying the command
+timed out.  If an unsolicited response comes in (if it was after 5
+seconds, for instance), that response will be ignored.
+
+In kernelland, after you receive a message and are done with it, you
+MUST call ipmi_free_recv_msg() on it, or you will leak messages.  Note
+that you should NEVER mess with the "done" field of a message, that is
+required to properly clean up the message.
+
+Note that when sending, there is an ipmi_request_supply_msgs() call
+that lets you supply the smi and receive message.  This is useful for
+pieces of code that need to work even if the system is out of buffers
+(the watchdog timer uses this, for instance).  You supply your own
+buffer and own free routines.  This is not recommended for normal use,
+though, since it is tricky to manage your own buffers.
+
+
+Events and Incoming Commands
+
+The driver takes care of polling for IPMI events and receiving
+commands (commands are messages that are not responses, they are
+commands that other things on the IPMB bus have sent you).  To receive
+these, you must register for them, they will not automatically be sent
+to you.
+
+To receive events, you must call ipmi_set_gets_events() and set the
+"val" to non-zero.  Any events that have been received by the driver
+since startup will immediately be delivered to the first user that
+registers for events.  After that, if multiple users are registered
+for events, they will all receive all events that come in.
+
+For receiving commands, you have to individually register commands you
+want to receive.  Call ipmi_register_for_cmd() and supply the netfn
+and command name for each command you want to receive.  Only one user
+may be registered for each netfn/cmd, but different users may register
+for different commands.
+
+From userland, equivalent IOCTLs are provided to do these functions.
+
+
+The Lower Layer (SMI) Interface
+-------------------------------
+
+As mentioned before, multiple SMI interfaces may be registered to the
+message handler, each of these is assigned an interface number when
+they register with the message handler.  They are generally assigned
+in the order they register, although if an SMI unregisters and then
+another one registers, all bets are off.
+
+The ipmi_smi.h defines the interface for SMIs, see that for more
+details.
+
+
+The KCS Driver
+--------------
+
+The KCS driver allows up to 4 KCS interfaces to be configured in the
+system.  By default, the driver will register one KCS interface at the
+spec-specified I/O port 0xca2 without interrupts.  You can change this
+at module load time (for a module) with:
+
+  insmod ipmi_kcs_drv.o kcs_ports=<port1>,<port2>... kcs_addrs=<addr1>,<addr2>
+       kcs_irqs=<irq1>,<irq2>... kcs_trydefaults=[0|1]
+
+The KCS driver supports two types of interfaces, ports (for I/O port
+based KCS interfaces) and memory addresses (for KCS interfaces in
+memory).  The driver will support both of them simultaneously, setting
+the port to zero (or just not specifying it) will allow the memory
+address to be used.  The port will override the memory address if it
+is specified and non-zero.  kcs_trydefaults sets whether the standard
+IPMI interface at 0xca2 and any interfaces specified by ACPE are
+tried.  By default, the driver tries it, set this value to zero to
+turn this off.
+
+When compiled into the kernel, the addresses can be specified on the
+kernel command line as:
+
+  ipmi_kcs=<bmc1>:<irq1>,<bmc2>:<irq2>....,[nodefault]
+
+The <bmcx> values is either "p<port>" or "m<addr>" for port or memory
+addresses.  So for instance, a KCS interface at port 0xca2 using
+interrupt 9 and a memory interface at address 0xf9827341 with no
+interrupt would be specified "ipmi_kcs=p0xca2:9,m0xf9827341".
+If you specify zero for in irq or don't specify it, the driver will
+run polled unless the software can detect the interrupt to use in the
+ACPI tables.
+
+By default, the driver will attempt to detect a KCS device at the
+spec-specified 0xca2 address and any address specified by ACPI.  If
+you want to turn this off, use the "nodefault" option.
+
+If you have high-res timers compiled into the kernel, the driver will
+use them to provide much better performance.  Note that if you do not
+have high-res timers enabled in the kernel and you don't have
+interrupts enabled, the driver will run VERY slowly.  Don't blame me,
+the KCS interface sucks.
+
+
+Other Pieces
+------------
+
+Watchdog
+
+A watchdog timer is provided that implements the Linux-standard
+watchdog timer interface.  It has three module parameters that can be
+used to control it:
+
+  insmod ipmi_watchdog timeout=<t> pretimeout=<t> action=<action type>
+      preaction=<preaction type> preop=<preop type>
+
+The timeout is the number of seconds to the action, and the pretimeout
+is the amount of seconds before the reset that the pre-timeout panic will
+occur (if pretimeout is zero, then pretimeout will not be enabled).
+
+The action may be "reset", "power_cycle", or "power_off", and
+specifies what to do when the timer times out, and defaults to
+"reset".
+
+The preaction may be "pre_smi" for an indication through the SMI
+interface, "pre_int" for an indication through the SMI with an
+interrupts, and "pre_nmi" for a NMI on a preaction.  This is how
+the driver is informed of the pretimeout.
+
+The preop may be set to "preop_none" for no operation on a pretimeout,
+"preop_panic" to set the preoperation to panic, or "preop_give_data"
+to provide data to read from the watchdog device when the pretimeout
+occurs.  A "pre_nmi" setting CANNOT be used with "preop_give_data"
+because you can't do data operations from an NMI.
+
+When preop is set to "preop_give_data", one byte comes ready to read
+on the device when the pretimeout occurs.  Select and fasync work on
+the device, as well.
+
+When compiled into the kernel, the kernel command line is available
+for configuring the watchdog:
+
+  ipmi_wdog=<timeout>[,<pretimeout>[,<option>[,<options>....]]]
+
+The options are the actions and preaction above (if an option
+controlling the same thing is specified twice, the last is taken).  An
+options "start_now" is also there, if included, the watchdog will
+start running immediately when all the drivers are ready, it doesn't
+have to have a user hooked up to start it.
+
+The watchdog will panic and start a 120 second reset timeout if it
+gets a pre-action.  During a panic or a reboot, the watchdog will
+start a 120 timer if it is running to make sure the reboot occurs.
+
+Note that if you use the NMI preaction for the watchdog, you MUST
+NOT use nmi watchdog mode 1.  If you use the NMI watchdog, you
+must use mode 2.
diff -urN linux.orig/drivers/char/Config.in linux/drivers/char/Config.in
--- linux.orig/drivers/char/Config.in	Fri Dec  6 09:11:37 2002
+++ linux/drivers/char/Config.in	Fri Dec  6 09:12:38 2002
@@ -190,6 +190,12 @@
    fi
 fi
 
+tristate 'IPMI top-level message handler' CONFIG_IPMI_HANDLER
+dep_mbool '  Generate a panic event to all BMCs on a panic' CONFIG_IPMI_PANIC_EVENT $CONFIG_IPMI_HANDLER
+dep_tristate '  Device interface for IPMI' CONFIG_IPMI_DEVICE_INTERFACE $CONFIG_IPMI_HANDLER
+dep_tristate '  IPMI KCS handler' CONFIG_IPMI_KCS $CONFIG_IPMI_HANDLER
+dep_tristate '  IPMI Watchdog Timer' CONFIG_IPMI_WATCHDOG $CONFIG_IPMI_HANDLER
+
 mainmenu_option next_comment
 comment 'Watchdog Cards'
 bool 'Watchdog Timer Support'	CONFIG_WATCHDOG
diff -urN linux.orig/drivers/char/Makefile linux/drivers/char/Makefile
--- linux.orig/drivers/char/Makefile	Fri Dec  6 09:11:37 2002
+++ linux/drivers/char/Makefile	Fri Dec  6 09:12:38 2002
@@ -289,6 +289,11 @@
   obj-y += mwave/mwave.o
 endif
 
+subdir-$(CONFIG_IPMI_HANDLER) += ipmi
+ifeq ($(CONFIG_IPMI_HANDLER),y)
+  obj-y += ipmi/ipmi.o
+endif
+
 include $(TOPDIR)/Rules.make
 
 fastdep:
diff -urN linux.orig/drivers/char/ipmi/Makefile linux/drivers/char/ipmi/Makefile
--- linux.orig/drivers/char/ipmi/Makefile	Wed Dec 31 18:00:00 1969
+++ linux/drivers/char/ipmi/Makefile	Thu Aug 22 16:58:48 2002
@@ -0,0 +1,20 @@
+#
+# Makefile for the ipmi drivers.
+#
+
+O_TARGET	:= ipmi.o
+
+export-objs	:= ipmi_msghandler.o ipmi_watchdog.o
+
+list-multi := ipmi_kcs_drv.o
+ipmi_kcs_drv-objs := ipmi_kcs_sm.o ipmi_kcs_intf.o
+
+obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
+obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
+obj-$(CONFIG_IPMI_KCS) += ipmi_kcs_drv.o
+obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
+
+include $(TOPDIR)/Rules.make
+
+ipmi_kcs_drv.o:	$(ipmi_kcs_drv-objs)
+	$(LD) -r -o $@ $(ipmi_kcs_drv-objs) 
diff -urN linux.orig/drivers/char/ipmi/ipmi_devintf.c linux/drivers/char/ipmi/ipmi_devintf.c
--- linux.orig/drivers/char/ipmi/ipmi_devintf.c	Wed Dec 31 18:00:00 1969
+++ linux/drivers/char/ipmi/ipmi_devintf.c	Mon Nov 18 20:43:45 2002
@@ -0,0 +1,532 @@
+/*
+ * ipmi_devintf.c
+ *
+ * Linux device interface for the IPMI message handler.
+ *
+ * Author: MontaVista Software, Inc.
+ *         Corey Minyard <minyard@mvista.com>
+ *         source@mvista.com
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ *  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.
+ *
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/ipmi.h>
+#include <asm/semaphore.h>
+#include <linux/init.h>
+
+struct ipmi_file_private
+{
+	ipmi_user_t          user;
+	spinlock_t           recv_msg_lock;
+	struct list_head     recv_msgs;
+	struct file          *file;
+	struct fasync_struct *fasync_queue;
+	wait_queue_head_t    wait;
+	struct semaphore     recv_sem;
+};
+
+static void file_receive_handler(struct ipmi_recv_msg *msg,
+				 void                 *handler_data)
+{
+	struct ipmi_file_private *priv = handler_data;
+	int                      was_empty;
+	unsigned long            flags;
+
+	spin_lock_irqsave(&(priv->recv_msg_lock), flags);
+
+	was_empty = list_empty(&(priv->recv_msgs));
+	list_add_tail(&(msg->link), &(priv->recv_msgs));
+
+	if (was_empty) {
+		wake_up_interruptible(&priv->wait);
+		kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN);
+	}
+
+	spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
+}
+
+static unsigned int ipmi_poll(struct file *file, poll_table *wait)
+{
+	struct ipmi_file_private *priv = file->private_data;
+	unsigned int             mask = 0;
+	unsigned long            flags;
+
+	spin_lock_irqsave(&priv->recv_msg_lock, flags);
+
+	poll_wait(file, &priv->wait, wait);
+
+	if (! list_empty(&(priv->recv_msgs)))
+		mask |= (POLLIN | POLLRDNORM);
+
+	spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
+
+	return mask;
+}
+
+static int ipmi_fasync(int fd, struct file *file, int on)
+{
+	struct ipmi_file_private *priv = file->private_data;
+	int                      result;
+
+	result = fasync_helper(fd, file, on, &priv->fasync_queue);
+
+	return (result);
+}
+
+static struct ipmi_user_hndl ipmi_hndlrs =
+{
+	ipmi_recv_hndl : file_receive_handler
+};
+
+static int ipmi_open(struct inode *inode, struct file *file)
+{
+	int                      if_num = minor(inode->i_rdev);
+	int                      rv;
+	struct ipmi_file_private *priv;
+
+
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->file = file;
+
+	rv = ipmi_create_user(if_num,
+			      &ipmi_hndlrs,
+			      priv,
+			      &(priv->user));
+	if (rv) {
+		kfree(priv);
+		return rv;
+	}
+
+	file->private_data = priv;
+
+	spin_lock_init(&(priv->recv_msg_lock));
+	INIT_LIST_HEAD(&(priv->recv_msgs));
+	init_waitqueue_head(&priv->wait);
+	priv->fasync_queue = NULL;
+	sema_init(&(priv->recv_sem), 1);
+
+	return 0;
+}
+
+static int ipmi_release(struct inode *inode, struct file *file)
+{
+	struct ipmi_file_private *priv = file->private_data;
+	int                      rv;
+
+	rv = ipmi_destroy_user(priv->user);
+	if (rv)
+		return rv;
+
+	ipmi_fasync (-1, file, 0);
+
+	/* FIXME - free the messages in the list. */
+	kfree(priv);
+
+	return 0;
+}
+
+static int ipmi_ioctl(struct inode  *inode,
+		      struct file   *file,
+		      unsigned int  cmd,
+		      unsigned long data)
+{
+	int                      rv = -EINVAL;
+	struct ipmi_file_private *priv = file->private_data;
+
+	switch (cmd) 
+	{
+	case IPMICTL_SEND_COMMAND:
+	{
+		struct ipmi_req    req;
+		struct ipmi_addr   addr;
+		unsigned char msgdata[IPMI_MAX_MSG_LENGTH];
+
+		if (copy_from_user(&req, (void *) data, sizeof(req))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		if (req.addr_len > sizeof(struct ipmi_addr))
+		{
+			rv = -EINVAL;
+			break;
+		}
+
+		if (copy_from_user(&addr, req.addr, req.addr_len)) {
+			rv = -EFAULT;
+			break;
+		}
+
+		rv = ipmi_validate_addr(&addr, req.addr_len);
+		if (rv)
+			break;
+
+		if (req.msg.data != NULL) {
+			if (req.msg.data_len > IPMI_MAX_MSG_LENGTH) {
+				rv = -EMSGSIZE;
+				break;
+			}
+
+			if (copy_from_user(&msgdata,
+					   req.msg.data,
+					   req.msg.data_len))
+			{
+				rv = -EFAULT;
+				break;
+			}
+		} else {
+			req.msg.data_len = 0;
+		}
+
+		req.msg.data = msgdata;
+
+		rv = ipmi_request(priv->user,
+				  &addr,
+				  req.msgid,
+				  &(req.msg),
+				  0);
+		break;
+	}
+
+	case IPMICTL_RECEIVE_MSG:
+	case IPMICTL_RECEIVE_MSG_TRUNC:
+	{
+		struct ipmi_recv      rsp;
+		int              addr_len;
+		struct list_head *entry;
+		struct ipmi_recv_msg  *msg;
+		unsigned long    flags;
+		
+
+		rv = 0;
+		if (copy_from_user(&rsp, (void *) data, sizeof(rsp))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		/* We claim a semaphore because we don't want two
+                   users getting something from the queue at a time.
+                   Since we have to release the spinlock before we can
+                   copy the data to the user, it's possible another
+                   user will grab something from the queue, too.  Then
+                   the messages might get out of order if something
+                   fails and the message gets put back onto the
+                   queue.  This semaphore prevents that problem. */
+		down(&(priv->recv_sem));
+
+		/* Grab the message off the list. */
+		spin_lock_irqsave(&(priv->recv_msg_lock), flags);
+		if (list_empty(&(priv->recv_msgs))) {
+			spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
+			rv = -EAGAIN;
+			goto recv_err;
+		}
+		entry = priv->recv_msgs.next;
+		msg = list_entry(entry, struct ipmi_recv_msg, link);
+		list_del(entry);
+		spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
+
+		addr_len = ipmi_addr_length(msg->addr.addr_type);
+		if (rsp.addr_len < addr_len)
+		{
+			rv = -EINVAL;
+			goto recv_putback_on_err;
+		}
+
+		if (copy_to_user(rsp.addr, &(msg->addr), addr_len)) {
+			rv = -EFAULT;
+			goto recv_putback_on_err;
+		}
+		rsp.addr_len = addr_len;
+
+		rsp.recv_type = msg->recv_type;
+		rsp.msgid = msg->msgid;
+		rsp.msg.netfn = msg->msg.netfn;
+		rsp.msg.cmd = msg->msg.cmd;
+
+		if (msg->msg.data_len > 0) {
+			if (rsp.msg.data_len < msg->msg.data_len) {
+				rv = -EMSGSIZE;
+				if (cmd == IPMICTL_RECEIVE_MSG_TRUNC) {
+					msg->msg.data_len = rsp.msg.data_len;
+				} else {
+					goto recv_putback_on_err;
+				}
+			}
+
+			if (copy_to_user(rsp.msg.data,
+					 msg->msg.data,
+					 msg->msg.data_len))
+			{
+				rv = -EFAULT;
+				goto recv_putback_on_err;
+			}
+			rsp.msg.data_len = msg->msg.data_len;
+		} else {
+			rsp.msg.data_len = 0;
+		}
+
+		if (copy_to_user((void *) data, &rsp, sizeof(rsp))) {
+			rv = -EFAULT;
+			goto recv_putback_on_err;
+		}
+
+		up(&(priv->recv_sem));
+		ipmi_free_recv_msg(msg);
+		break;
+
+	recv_putback_on_err:
+		/* If we got an error, put the message back onto
+		   the head of the queue. */
+		spin_lock_irqsave(&(priv->recv_msg_lock), flags);
+		list_add(entry, &(priv->recv_msgs));
+		spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
+		up(&(priv->recv_sem));
+		break;
+
+	recv_err:
+		up(&(priv->recv_sem));
+		break;
+	}
+
+	case IPMICTL_REGISTER_FOR_CMD:
+	{
+		struct ipmi_cmdspec val;
+
+		if (copy_from_user(&val, (void *) data, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd);
+		break;
+	}
+
+	case IPMICTL_UNREGISTER_FOR_CMD:
+	{
+		struct ipmi_cmdspec   val;
+
+		if (copy_from_user(&val, (void *) data, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd);
+		break;
+	}
+
+	case IPMICTL_SET_GETS_EVENTS_CMD:
+	{
+		int val;
+
+		if (copy_from_user(&val, (void *) data, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		rv = ipmi_set_gets_events(priv->user, val);
+		break;
+	}
+
+	case IPMICTL_SET_MY_ADDRESS_CMD:
+	{
+		unsigned int val;
+
+		if (copy_from_user(&val, (void *) data, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		ipmi_set_my_address(priv->user, val);
+		rv = 0;
+		break;
+	}
+
+	case IPMICTL_GET_MY_ADDRESS_CMD:
+	{
+		unsigned int val;
+
+		val = ipmi_get_my_address(priv->user);
+
+		if (copy_to_user((void *) data, &val, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+		rv = 0;
+		break;
+	}
+
+	case IPMICTL_SET_MY_LUN_CMD:
+	{
+		unsigned int val;
+
+		if (copy_from_user(&val, (void *) data, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		ipmi_set_my_LUN(priv->user, val);
+		rv = 0;
+		break;
+	}
+
+	case IPMICTL_GET_MY_LUN_CMD:
+	{
+		unsigned int val;
+
+		val = ipmi_get_my_LUN(priv->user);
+
+		if (copy_to_user((void *) data, &val, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+		rv = 0;
+		break;
+	}
+
+	}
+  
+	return rv;
+}
+
+
+static struct file_operations ipmi_fops = {
+	owner:   THIS_MODULE,
+	ioctl:   ipmi_ioctl,
+	open:    ipmi_open,
+	release: ipmi_release,
+	fasync:  ipmi_fasync,
+	poll:    ipmi_poll
+};
+
+#define DEVICE_NAME     "ipmidev"
+
+static int ipmi_major = 0;
+MODULE_PARM(ipmi_major, "i");
+
+static devfs_handle_t devfs_handle;
+
+#define MAX_DEVICES 10
+static devfs_handle_t handles[MAX_DEVICES];
+
+static void ipmi_new_smi(int if_num)
+{
+	char name[2];
+
+	if (if_num > MAX_DEVICES)
+		return;
+
+	name[0] = if_num + '0';
+	name[1] = '\0';
+
+	handles[if_num] = devfs_register(devfs_handle, name, DEVFS_FL_NONE,
+					 ipmi_major, if_num,
+					 S_IFCHR | S_IRUSR | S_IWUSR,
+					 &ipmi_fops, NULL);
+}
+
+static void ipmi_smi_gone(int if_num)
+{
+	if (if_num > MAX_DEVICES)
+		return;
+
+	devfs_unregister(handles[if_num]);
+}
+
+static struct ipmi_smi_watcher smi_watcher =
+{
+	new_smi  : ipmi_new_smi,
+	smi_gone : ipmi_smi_gone
+};
+
+static __init int init_ipmi_devintf(void)
+{
+	int rv;
+
+	if (ipmi_major < 0)
+		return -EINVAL;
+
+	rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
+	if (rv < 0) {
+		printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major);
+		return rv;
+	}
+
+	if (ipmi_major == 0) {
+		ipmi_major = rv;
+	}
+
+	devfs_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);
+
+	rv = ipmi_smi_watcher_register(&smi_watcher);
+	if (rv) {
+		unregister_chrdev(ipmi_major, DEVICE_NAME);
+		printk(KERN_WARNING "ipmi: can't register smi watcher");
+		return rv;
+	}
+
+	printk(KERN_INFO "ipmi: device interface at char major %d\n",
+	       ipmi_major);
+
+	return 0;
+}
+module_init(init_ipmi_devintf);
+
+static __exit void cleanup_ipmi(void)
+{
+	ipmi_smi_watcher_unregister(&smi_watcher);
+	devfs_unregister(devfs_handle);
+	unregister_chrdev(ipmi_major, DEVICE_NAME);
+}
+module_exit(cleanup_ipmi);
+#ifndef MODULE
+static __init int ipmi_setup (char *str)
+{
+	int x;
+
+	if (get_option (&str, &x)) {
+		/* ipmi=x sets the major number to x. */
+		ipmi_major = x;
+	} else if (!strcmp(str, "off")) {
+		ipmi_major = -1;
+	}
+
+	return 1;
+}
+#endif
+
+__setup("ipmi=", ipmi_setup);
+MODULE_LICENSE("GPL");
diff -urN linux.orig/drivers/char/ipmi/ipmi_kcs_intf.c linux/drivers/char/ipmi/ipmi_kcs_intf.c
--- linux.orig/drivers/char/ipmi/ipmi_kcs_intf.c	Wed Dec 31 18:00:00 1969
+++ linux/drivers/char/ipmi/ipmi_kcs_intf.c	Mon Jan  6 10:20:27 2003
@@ -0,0 +1,1243 @@
+/*
+ * ipmi_kcs_intf.c
+ *
+ * The interface to the IPMI driver for the KCS.
+ *
+ * Author: MontaVista Software, Inc.
+ *         Corey Minyard <minyard@mvista.com>
+ *         source@mvista.com
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ *  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.
+ *
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file holds the "policy" for the interface to the KCS state
+ * machine.  It does the configuration, handles timers and interrupts,
+ * and drives the real KCS state machine.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/ioport.h>
+#ifdef CONFIG_HIGH_RES_TIMERS
+#include <linux/hrtime.h>
+#endif
+#include <linux/interrupt.h>
+#include <linux/ipmi_smi.h>
+#include <asm/io.h>
+#include "ipmi_kcs_sm.h"
+#include <linux/init.h>
+
+/* Measure times between events in the driver. */
+#undef DEBUG_TIMING
+
+#ifdef CONFIG_IPMI_KCS
+/* This forces a dependency to the config file for this option. */
+#endif
+
+enum kcs_intf_state {
+	KCS_NORMAL,
+	KCS_GETTING_FLAGS,
+	KCS_GETTING_EVENTS,
+	KCS_CLEARING_FLAGS,
+	KCS_CLEARING_FLAGS_THEN_SET_IRQ,
+	KCS_GETTING_MESSAGES,
+	KCS_ENABLE_INTERRUPTS1,
+	KCS_ENABLE_INTERRUPTS2
+	/* FIXME - add watchdog stuff. */
+};
+
+struct kcs_info
+{
+	ipmi_smi_t          intf;
+	struct kcs_data     *kcs_sm;
+	spinlock_t          kcs_lock;
+	spinlock_t          msg_lock;
+	struct list_head    xmit_msgs;
+	struct list_head    hp_xmit_msgs;
+	struct ipmi_smi_msg *curr_msg;
+	enum kcs_intf_state kcs_state;
+
+	/* Flags from the last GET_MSG_FLAGS command, used when an ATTN
+	   is set to hold the flags until we are done handling everything
+	   from the flags. */
+#define RECEIVE_MSG_AVAIL	0x01
+#define EVENT_MSG_BUFFER_FULL	0x02
+#define WDT_PRE_TIMEOUT_INT	0x08
+	unsigned char       msg_flags;
+
+	/* If set to true, this will request events the next time the
+	   state machine is idle. */
+	atomic_t            req_events;
+
+	/* If true, run the state machine to completion on every send
+	   call.  Generally used after a panic to make sure stuff goes
+	   out. */
+	int                 run_to_completion;
+
+	/* The I/O port of a KCS interface. */
+	int                 port;
+
+	/* zero if no irq; */
+	int                 irq;
+
+	/* The physical and remapped memory addresses of a KCS interface. */
+	unsigned long	    physaddr;
+	unsigned char	    *addr;
+
+	/* The timer for this kcs. */
+	struct timer_list   kcs_timer;
+
+	/* The time (in jiffies) the last timeout occurred at. */
+	unsigned long       last_timeout_jiffies;
+
+	/* Used to gracefully stop the timer without race conditions. */
+	volatile int        stop_operation;
+	volatile int        timer_stopped;
+
+	/* The driver will disable interrupts when it gets into a
+	   situation where it cannot handle messages due to lack of
+	   memory.  Once that situation clears up, it will re-enable
+	   interupts. */
+	int                 interrupt_disabled;
+};
+
+static void deliver_recv_msg(struct kcs_info *kcs_info, struct ipmi_smi_msg *msg)
+{
+	/* Deliver the message to the upper layer with the lock
+           released. */
+	spin_unlock(&(kcs_info->kcs_lock));
+	ipmi_smi_msg_received(kcs_info->intf, msg);
+	spin_lock(&(kcs_info->kcs_lock));
+}
+
+static void return_hosed_msg(struct kcs_info *kcs_info)
+{
+	struct ipmi_smi_msg *msg = kcs_info->curr_msg;
+
+	/* Make it a reponse */
+	msg->rsp[0] = msg->data[0] | 4;
+	msg->rsp[1] = msg->data[1];
+	msg->rsp[2] = 0xFF; /* Unknown error. */
+	msg->rsp_size = 3;
+			
+	kcs_info->curr_msg = NULL;
+	deliver_recv_msg(kcs_info, msg);
+}
+
+static enum kcs_result start_next_msg(struct kcs_info *kcs_info)
+{
+	int              rv;
+	struct list_head *entry = NULL;
+#ifdef DEBUG_TIMING
+	struct timeval t;
+#endif
+
+	/* No need to save flags, we aleady have interrupts off and we
+	   already hold the KCS lock. */
+	spin_lock(&(kcs_info->msg_lock));
+	
+	/* Pick the high priority queue first. */
+	if (! list_empty(&(kcs_info->hp_xmit_msgs))) {
+		entry = kcs_info->hp_xmit_msgs.next;
+	} else if (! list_empty(&(kcs_info->xmit_msgs))) {
+		entry = kcs_info->xmit_msgs.next;
+	}
+
+	if (!entry) {
+		kcs_info->curr_msg = NULL;
+		rv = KCS_SM_IDLE;
+	} else {
+		int err;
+
+		list_del(entry);
+		kcs_info->curr_msg = list_entry(entry,
+						struct ipmi_smi_msg,
+						link);
+#ifdef DEBUG_TIMING
+		do_gettimeofday(&t);
+		printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec);
+#endif
+		err = start_kcs_transaction(kcs_info->kcs_sm,
+					   kcs_info->curr_msg->data,
+					   kcs_info->curr_msg->data_size);
+		if (err) {
+			return_hosed_msg(kcs_info);
+		}
+
+		rv = KCS_CALL_WITHOUT_DELAY;
+	}
+	spin_unlock(&(kcs_info->msg_lock));
+
+	return rv;
+}
+
+static void start_enable_irq(struct kcs_info *kcs_info)
+{
+	unsigned char msg[2];
+
+	/* If we are enabling interrupts, we have to tell the
+	   BMC to use them. */
+	msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
+	msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
+
+	start_kcs_transaction(kcs_info->kcs_sm, msg, 2);
+	kcs_info->kcs_state = KCS_ENABLE_INTERRUPTS1;
+}
+
+static void start_clear_flags(struct kcs_info *kcs_info)
+{
+	unsigned char msg[3];
+
+	/* Make sure the watchdog pre-timeout flag is not set at startup. */
+	msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
+	msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD;
+	msg[2] = WDT_PRE_TIMEOUT_INT;
+
+	start_kcs_transaction(kcs_info->kcs_sm, msg, 3);
+	kcs_info->kcs_state = KCS_CLEARING_FLAGS;
+}
+
+/* When we have a situtaion where we run out of memory and cannot
+   allocate messages, we just leave them in the BMC and run the system
+   polled until we can allocate some memory.  Once we have some
+   memory, we will re-enable the interrupt. */
+static inline void disable_kcs_irq(struct kcs_info *kcs_info)
+{
+	if ((kcs_info->irq) && (!kcs_info->interrupt_disabled)) {
+		disable_irq_nosync(kcs_info->irq);
+		kcs_info->interrupt_disabled = 1;
+	}
+}
+
+static inline void enable_kcs_irq(struct kcs_info *kcs_info)
+{
+	if ((kcs_info->irq) && (kcs_info->interrupt_disabled)) {
+		enable_irq(kcs_info->irq);
+		kcs_info->interrupt_disabled = 0;
+	}
+}
+
+static void handle_flags(struct kcs_info *kcs_info)
+{
+	if (kcs_info->msg_flags & WDT_PRE_TIMEOUT_INT) {
+		/* Watchdog pre-timeout */
+		start_clear_flags(kcs_info);
+		kcs_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT;
+		spin_unlock(&(kcs_info->kcs_lock));
+		ipmi_smi_watchdog_pretimeout(kcs_info->intf);
+		spin_lock(&(kcs_info->kcs_lock));
+	} else if (kcs_info->msg_flags & RECEIVE_MSG_AVAIL) {
+		/* Messages available. */
+		kcs_info->curr_msg = ipmi_alloc_smi_msg();
+		if (!kcs_info->curr_msg) {
+			disable_kcs_irq(kcs_info);
+			kcs_info->kcs_state = KCS_NORMAL;
+			return;
+		}
+		enable_kcs_irq(kcs_info);
+
+		kcs_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
+		kcs_info->curr_msg->data[1] = IPMI_GET_MSG_CMD;
+		kcs_info->curr_msg->data_size = 2;
+
+		start_kcs_transaction(kcs_info->kcs_sm,
+				      kcs_info->curr_msg->data,
+				      kcs_info->curr_msg->data_size);
+		kcs_info->kcs_state = KCS_GETTING_MESSAGES;
+	} else if (kcs_info->msg_flags & EVENT_MSG_BUFFER_FULL) {
+		/* Events available. */
+		kcs_info->curr_msg = ipmi_alloc_smi_msg();
+		if (!kcs_info->curr_msg) {
+			disable_kcs_irq(kcs_info);
+			kcs_info->kcs_state = KCS_NORMAL;
+			return;
+		}
+		enable_kcs_irq(kcs_info);
+
+		kcs_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
+		kcs_info->curr_msg->data[1] = IPMI_READ_EVENT_MSG_BUFFER_CMD;
+		kcs_info->curr_msg->data_size = 2;
+
+		start_kcs_transaction(kcs_info->kcs_sm,
+				      kcs_info->curr_msg->data,
+				      kcs_info->curr_msg->data_size);
+		kcs_info->kcs_state = KCS_GETTING_EVENTS;
+	} else {
+		kcs_info->kcs_state = KCS_NORMAL;
+	}
+}
+
+static void handle_transaction_done(struct kcs_info *kcs_info)
+{
+	struct ipmi_smi_msg *msg;
+#ifdef DEBUG_TIMING
+	struct timeval t;
+
+	do_gettimeofday(&t);
+	printk("**Done: %d.%9.9d\n", t.tv_sec, t.tv_usec);
+#endif
+	switch (kcs_info->kcs_state) {
+	case KCS_NORMAL:
+		kcs_info->curr_msg->rsp_size
+			= kcs_get_result(kcs_info->kcs_sm,
+					 kcs_info->curr_msg->rsp,
+					 IPMI_MAX_MSG_LENGTH);
+		
+		/* Do this here becase deliver_recv_msg() releases the
+		   lock, and a new message can be put in during the
+		   time the lock is released. */
+		msg = kcs_info->curr_msg;
+		kcs_info->curr_msg = NULL;
+		deliver_recv_msg(kcs_info, msg);
+		break;
+		
+	case KCS_GETTING_FLAGS:
+	{
+		unsigned char msg[4];
+		unsigned int  len;
+
+		/* We got the flags from the KCS, now handle them. */
+		len = kcs_get_result(kcs_info->kcs_sm, msg, 4);
+		if (msg[2] != 0) {
+			/* Error fetching flags, just give up for
+			   now. */
+			kcs_info->kcs_state = KCS_NORMAL;
+		} else if (len < 3) {
+			/* Hmm, no flags.  That's technically illegal, but
+			   don't use uninitialized data. */
+			kcs_info->kcs_state = KCS_NORMAL;
+		} else {
+			kcs_info->msg_flags = msg[3];
+			handle_flags(kcs_info);
+		}
+		break;
+	}
+
+	case KCS_CLEARING_FLAGS:
+	case KCS_CLEARING_FLAGS_THEN_SET_IRQ:
+	{
+		unsigned char msg[3];
+
+		/* We cleared the flags. */
+		kcs_get_result(kcs_info->kcs_sm, msg, 3);
+		if (msg[2] != 0) {
+			/* Error clearing flags */
+			printk(KERN_WARNING
+			       "ipmi_kcs: Error clearing flags: %2.2x\n",
+			       msg[2]);
+		}
+		if (kcs_info->kcs_state == KCS_CLEARING_FLAGS_THEN_SET_IRQ)
+			start_enable_irq(kcs_info);
+		else
+			kcs_info->kcs_state = KCS_NORMAL;
+		break;
+	}
+
+	case KCS_GETTING_EVENTS:
+	{
+		kcs_info->curr_msg->rsp_size
+			= kcs_get_result(kcs_info->kcs_sm,
+					 kcs_info->curr_msg->rsp,
+					 IPMI_MAX_MSG_LENGTH);
+
+		/* Do this here becase deliver_recv_msg() releases the
+		   lock, and a new message can be put in during the
+		   time the lock is released. */
+		msg = kcs_info->curr_msg;
+		kcs_info->curr_msg = NULL;
+		if (msg->rsp[2] != 0) {
+			/* Error getting event, probably done. */
+			msg->done(msg);
+
+			/* Take off the event flag. */
+			kcs_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL;
+		} else {
+			deliver_recv_msg(kcs_info, msg);
+		}
+		handle_flags(kcs_info);
+		break;
+	}
+
+	case KCS_GETTING_MESSAGES:
+	{
+		kcs_info->curr_msg->rsp_size
+			= kcs_get_result(kcs_info->kcs_sm,
+					 kcs_info->curr_msg->rsp,
+					 IPMI_MAX_MSG_LENGTH);
+
+		/* Do this here becase deliver_recv_msg() releases the
+		   lock, and a new message can be put in during the
+		   time the lock is released. */
+		msg = kcs_info->curr_msg;
+		kcs_info->curr_msg = NULL;
+		if (msg->rsp[2] != 0) {
+			/* Error getting event, probably done. */
+			msg->done(msg);
+
+			/* Take off the msg flag. */
+			kcs_info->msg_flags &= ~RECEIVE_MSG_AVAIL;
+		} else {
+			deliver_recv_msg(kcs_info, msg);
+		}
+		handle_flags(kcs_info);
+		break;
+	}
+
+	case KCS_ENABLE_INTERRUPTS1:
+	{
+		unsigned char msg[4];
+
+		/* We got the flags from the KCS, now handle them. */
+		kcs_get_result(kcs_info->kcs_sm, msg, 4);
+		if (msg[2] != 0) {
+			printk(KERN_WARNING
+			       "ipmi_kcs: Could not enable interrupts"
+			       ", failed get, using polled mode.\n");
+			kcs_info->kcs_state = KCS_NORMAL;
+		} else {
+			msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
+			msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
+			msg[2] = msg[3] | 1; /* enable msg queue int */
+			start_kcs_transaction(kcs_info->kcs_sm, msg,3);
+			kcs_info->kcs_state = KCS_ENABLE_INTERRUPTS2;
+		}
+		break;
+	}
+
+	case KCS_ENABLE_INTERRUPTS2:
+	{
+		unsigned char msg[4];
+
+		/* We got the flags from the KCS, now handle them. */
+		kcs_get_result(kcs_info->kcs_sm, msg, 4);
+		if (msg[2] != 0) {
+			printk(KERN_WARNING
+			       "ipmi_kcs: Could not enable interrupts"
+			       ", failed set, using polled mode.\n");
+		}
+		kcs_info->kcs_state = KCS_NORMAL;
+		break;
+	}
+	}
+}
+
+/* Called on timeouts and events.  Timeouts should pass the elapsed
+   time, interrupts should pass in zero. */
+static enum kcs_result kcs_event_handler(struct kcs_info *kcs_info, int time)
+{
+	enum kcs_result kcs_result;
+
+ restart:
+	/* There used to be a loop here that waited a little while
+	   (around 25us) before giving up.  That turned out to be
+	   pointless, the minimum delays I was seeing were in the 300us
+	   range, which is far too long to wait in an interrupt.  So
+	   we just run until the state machine tells us something
+	   happened or it needs a delay. */
+	kcs_result = kcs_event(kcs_info->kcs_sm, time);
+	time = 0;
+	while (kcs_result == KCS_CALL_WITHOUT_DELAY)
+	{
+		kcs_result = kcs_event(kcs_info->kcs_sm, 0);
+	}
+
+	if (kcs_result == KCS_TRANSACTION_COMPLETE)
+	{
+		handle_transaction_done(kcs_info);
+		kcs_result = kcs_event(kcs_info->kcs_sm, 0);
+	}
+	else if (kcs_result == KCS_SM_HOSED)
+	{
+		if (kcs_info->curr_msg != NULL) {
+			/* If we were handling a user message, format
+                           a response to send to the upper layer to
+                           tell it about the error. */
+			return_hosed_msg(kcs_info);
+		}
+		kcs_result = kcs_event(kcs_info->kcs_sm, 0);
+		kcs_info->kcs_state = KCS_NORMAL;
+	}
+
+	/* We prefer handling attn over new messages. */
+	if (kcs_result == KCS_ATTN)
+	{
+		unsigned char msg[2];
+
+		/* Got a attn, send down a get message flags to see
+                   what's causing it.  It would be better to handle
+                   this in the upper layer, but due to the way
+                   interrupts work with the KCS, that's not really
+                   possible. */
+		msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
+		msg[1] = IPMI_GET_MSG_FLAGS_CMD;
+
+		start_kcs_transaction(kcs_info->kcs_sm, msg, 2);
+		kcs_info->kcs_state = KCS_GETTING_FLAGS;
+		goto restart;
+	}
+
+	/* If we are currently idle, try to start the next message. */
+	if (kcs_result == KCS_SM_IDLE) {
+		kcs_result = start_next_msg(kcs_info);
+		if (kcs_result != KCS_SM_IDLE)
+			goto restart;
+        }
+
+	if ((kcs_result == KCS_SM_IDLE)
+	    && (atomic_read(&kcs_info->req_events)))
+	{
+		/* We are idle and the upper layer requested that I fetch
+		   events, so do so. */
+		unsigned char msg[2];
+
+		atomic_set(&kcs_info->req_events, 0);
+		msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
+		msg[1] = IPMI_GET_MSG_FLAGS_CMD;
+
+		start_kcs_transaction(kcs_info->kcs_sm, msg, 2);
+		kcs_info->kcs_state = KCS_GETTING_FLAGS;
+		goto restart;
+	}
+
+	return kcs_result;
+}
+
+static void sender(void                *send_info,
+		   struct ipmi_smi_msg *msg,
+		   int                 priority)
+{
+	struct kcs_info *kcs_info = (struct kcs_info *) send_info;
+	enum kcs_result result;
+	unsigned long   flags;
+#ifdef DEBUG_TIMING
+	struct timeval t;
+#endif
+
+	spin_lock_irqsave(&(kcs_info->msg_lock), flags);
+#ifdef DEBUG_TIMING
+	do_gettimeofday(&t);
+	printk("**Enqueue: %d.%9.9d\n", t.tv_sec, t.tv_usec);
+#endif
+
+	if (kcs_info->run_to_completion) {
+		/* If we are running to completion, then throw it in
+		   the list and run transactions until everything is
+		   clear.  Priority doesn't matter here. */
+		list_add_tail(&(msg->link), &(kcs_info->xmit_msgs));
+
+		/* We have to release the msg lock and claim the kcs
+		   lock in this case, because of race conditions. */
+		spin_unlock_irqrestore(&(kcs_info->msg_lock), flags);
+
+		spin_lock_irqsave(&(kcs_info->kcs_lock), flags);
+		result = kcs_event_handler(kcs_info, 0);
+		while (result != KCS_SM_IDLE) {
+			udelay(500);
+			result = kcs_event_handler(kcs_info, 500);
+		}
+		spin_unlock_irqrestore(&(kcs_info->kcs_lock), flags);
+		return;
+	} else {
+		if (priority > 0) {
+			list_add_tail(&(msg->link), &(kcs_info->hp_xmit_msgs));
+		} else {
+			list_add_tail(&(msg->link), &(kcs_info->xmit_msgs));
+		}
+	}
+	spin_unlock_irqrestore(&(kcs_info->msg_lock), flags);
+
+	spin_lock_irqsave(&(kcs_info->kcs_lock), flags);
+	if ((kcs_info->kcs_state == KCS_NORMAL)
+	    && (kcs_info->curr_msg == NULL))
+	{
+		start_next_msg(kcs_info);
+	}
+	spin_unlock_irqrestore(&(kcs_info->kcs_lock), flags);
+}
+
+static void set_run_to_completion(void *send_info, int i_run_to_completion)
+{
+	struct kcs_info *kcs_info = (struct kcs_info *) send_info;
+	enum kcs_result result;
+	unsigned long   flags;
+
+	spin_lock_irqsave(&(kcs_info->kcs_lock), flags);
+
+	kcs_info->run_to_completion = i_run_to_completion;
+	if (i_run_to_completion) {
+		result = kcs_event_handler(kcs_info, 0);
+		while (result != KCS_SM_IDLE) {
+			udelay(500);
+			result = kcs_event_handler(kcs_info, 500);
+		}
+	}
+
+	spin_unlock_irqrestore(&(kcs_info->kcs_lock), flags);
+}
+
+static void request_events(void *send_info)
+{
+	struct kcs_info *kcs_info = (struct kcs_info *) send_info;
+
+	atomic_set(&kcs_info->req_events, 1);
+}
+
+static int new_user(void *send_info)
+{
+	if (!try_inc_mod_count(THIS_MODULE))
+		return -EBUSY;
+	return 0;
+}
+
+static void user_left(void *send_info)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+/* Call every 10 ms. */
+#define KCS_TIMEOUT_TIME_USEC	10000
+#define KCS_USEC_PER_JIFFY	(1000000/HZ)
+#define KCS_TIMEOUT_JIFFIES	(KCS_TIMEOUT_TIME_USEC/KCS_USEC_PER_JIFFY)
+#define KCS_SHORT_TIMEOUT_USEC  500 /* .5ms when the SM request a
+                                       short timeout */
+static int initialized = 0;
+
+static void kcs_timeout(unsigned long data)
+{
+	struct kcs_info *kcs_info = (struct kcs_info *) data;
+	enum kcs_result kcs_result;
+	unsigned long   flags;
+	unsigned long   jiffies_now;
+	unsigned long   time_diff;
+#ifdef DEBUG_TIMING
+	struct timeval t;
+#endif
+
+	if (kcs_info->stop_operation) {
+		kcs_info->timer_stopped = 1;
+		return;
+	}
+
+	spin_lock_irqsave(&(kcs_info->kcs_lock), flags);
+#ifdef DEBUG_TIMING
+	do_gettimeofday(&t);
+	printk("**Timer: %d.%9.9d\n", t.tv_sec, t.tv_usec);
+#endif
+	jiffies_now = jiffies;
+	time_diff = ((jiffies_now - kcs_info->last_timeout_jiffies)
+		     * KCS_USEC_PER_JIFFY);
+	kcs_result = kcs_event_handler(kcs_info, time_diff);
+
+	spin_unlock_irqrestore(&(kcs_info->kcs_lock), flags);
+
+	kcs_info->last_timeout_jiffies = jiffies_now;
+
+	if ((kcs_info->irq) && (! kcs_info->interrupt_disabled)) {
+		/* Running with interrupts, only do long timeouts. */
+		kcs_info->kcs_timer.expires = jiffies + KCS_TIMEOUT_JIFFIES;
+		goto do_add_timer;
+	}
+
+	/* If the state machine asks for a short delay, then shorten
+           the timer timeout. */
+#ifdef CONFIG_HIGH_RES_TIMERS
+	if (kcs_result == KCS_CALL_WITH_DELAY) {
+		kcs_info->kcs_timer.sub_expires
+			+= usec_to_arch_cycles(KCS_SHORT_TIMEOUT_USEC);
+		while (kcs_info->kcs_timer.sub_expires >= cycles_per_jiffies) {
+			kcs_info->kcs_timer.expires++;
+			kcs_info->kcs_timer.sub_expires -= cycles_per_jiffies;
+		}
+	} else {
+		kcs_info->kcs_timer.expires = jiffies + KCS_TIMEOUT_JIFFIES;
+	}
+#else
+	/* If requested, take the shortest delay possible */
+	if (kcs_result == KCS_CALL_WITH_DELAY) {
+		kcs_info->kcs_timer.expires = jiffies + 1;
+	} else {
+		kcs_info->kcs_timer.expires = jiffies + KCS_TIMEOUT_JIFFIES;
+	}
+#endif
+
+ do_add_timer:
+	add_timer(&(kcs_info->kcs_timer));
+}
+
+static void kcs_irq_handler(int irq, void *data, struct pt_regs *regs)
+{
+	struct kcs_info *kcs_info = (struct kcs_info *) data;
+	unsigned long   flags;
+#ifdef DEBUG_TIMING
+	struct timeval t;
+#endif
+
+	spin_lock_irqsave(&(kcs_info->kcs_lock), flags);
+	if (kcs_info->stop_operation)
+		goto out;
+
+#ifdef DEBUG_TIMING
+	do_gettimeofday(&t);
+	printk("**Interrupt: %d.%9.9d\n", t.tv_sec, t.tv_usec);
+#endif
+	kcs_event_handler(kcs_info, 0);
+ out:
+	spin_unlock_irqrestore(&(kcs_info->kcs_lock), flags);
+}
+
+static struct ipmi_smi_handlers handlers =
+{
+	sender:		       sender,
+	request_events:        request_events,
+	new_user:	       new_user,
+	user_left:	       user_left,
+	set_run_to_completion: set_run_to_completion
+};
+
+static unsigned char ipmi_kcs_dev_rev;
+static unsigned char ipmi_kcs_fw_rev_major;
+static unsigned char ipmi_kcs_fw_rev_minor;
+static unsigned char ipmi_version_major;
+static unsigned char ipmi_version_minor;
+
+extern int kcs_dbg;
+static int ipmi_kcs_detect_hardware(unsigned int port,
+				    unsigned char *addr,
+				    struct kcs_data *data)
+{
+	unsigned char   msg[2];
+	unsigned char   resp[IPMI_MAX_MSG_LENGTH];
+	unsigned long   resp_len;
+	enum kcs_result kcs_result;
+
+	/* It's impossible for the KCS status register to be all 1's,
+	   (assuming a properly functioning, self-initialized BMC)
+	   but that's what you get from reading a bogus address, so we
+	   test that first. */
+
+	if (port) {
+		if (inb(port+1) == 0xff) return -ENODEV; 
+	} else { 
+		if (readb(addr+1) == 0xff) return -ENODEV; 
+	}
+
+	/* Do a Get Device ID command, since it comes back with some
+	   useful info. */
+	msg[0] = IPMI_NETFN_APP_REQUEST << 2;
+	msg[1] = IPMI_GET_DEVICE_ID_CMD;
+	start_kcs_transaction(data, msg, 2);
+	
+	kcs_result = kcs_event(data, 0);
+	for (;;)
+	{
+		if (kcs_result == KCS_CALL_WITH_DELAY) {
+			udelay(100);
+			kcs_result = kcs_event(data, 100);
+		}
+		else if (kcs_result == KCS_CALL_WITHOUT_DELAY)
+		{
+			kcs_result = kcs_event(data, 0);
+		}
+		else
+			break;
+	}
+	if (kcs_result == KCS_SM_HOSED) {
+		/* We couldn't get the state machine to run, so whatever's at
+		   the port is probably not an IPMI KCS interface. */
+		return -ENODEV;
+	}
+	/* Otherwise, we got some data. */
+	resp_len = kcs_get_result(data, resp, IPMI_MAX_MSG_LENGTH);
+	if (resp_len < 6)
+		/* That's odd, it should be longer. */
+		return -EINVAL;
+	
+	if ((resp[1] != IPMI_GET_DEVICE_ID_CMD) || (resp[2] != 0))
+		/* That's odd, it shouldn't be able to fail. */
+		return -EINVAL;
+	
+	ipmi_kcs_dev_rev = resp[4] & 0xf;
+	ipmi_kcs_fw_rev_major = resp[5] & 0x7f;
+	ipmi_kcs_fw_rev_minor = resp[6];
+	ipmi_version_major = resp[7] & 0xf;
+	ipmi_version_minor = resp[7] >> 4;
+
+	return 0;
+}
+
+/* There can be 4 IO ports passed in (with or without IRQs), 4 addresses,
+   a default IO port, and 1 ACPI/SPMI address.  That sets KCS_MAX_DRIVERS */
+
+#define KCS_MAX_PARMS 4
+#define KCS_MAX_DRIVERS ((KCS_MAX_PARMS * 2) + 2)
+static struct kcs_info *kcs_infos[KCS_MAX_DRIVERS] =
+{ NULL, NULL, NULL, NULL };
+
+#define DEVICE_NAME "ipmi_kcs"
+
+#define DEFAULT_IO_PORT 0xca2
+
+static int kcs_trydefaults = 1;
+static unsigned long kcs_addrs[KCS_MAX_PARMS] = { 0, 0, 0, 0 };
+static int kcs_ports[KCS_MAX_PARMS] = { 0, 0, 0, 0 };
+static int kcs_irqs[KCS_MAX_PARMS] = { 0, 0, 0, 0 };
+
+MODULE_PARM(kcs_trydefaults, "i");
+MODULE_PARM(kcs_addrs, "1-4l");
+MODULE_PARM(kcs_irqs, "1-4i");
+MODULE_PARM(kcs_ports, "1-4i");
+
+/* Returns 0 if initialized, or negative on an error. */
+static int init_one_kcs(int kcs_port, 
+			int irq, 
+			unsigned long kcs_physaddr,
+			struct kcs_info **kcs)
+{
+	int		rv;
+	struct kcs_info *new_kcs;
+
+	/* Did anything get passed in at all?  Both == zero disables the
+	   driver. */
+
+	if (!(kcs_port || kcs_physaddr)) 
+		return -ENODEV;
+	
+	/* Only initialize a port OR a physical address on this call.
+	   Also, IRQs can go with either ports or addresses. */
+
+	if (kcs_port && kcs_physaddr)
+		return -EINVAL;
+
+	new_kcs = kmalloc(kcs_size(), GFP_KERNEL);
+	if (!new_kcs) {
+		printk(KERN_ERR "ipmi_kcs: out of memory\n");
+		return -ENOMEM;
+	}
+
+	/* So we know not to free it unless we have allocated one. */
+	new_kcs->kcs_sm = NULL;
+
+	new_kcs->addr = NULL;
+	new_kcs->physaddr = kcs_physaddr;
+	new_kcs->port = kcs_port;
+
+	if (kcs_port) {
+		if (request_region(kcs_port, 2, DEVICE_NAME) == NULL) {
+			kfree(new_kcs);
+			printk(KERN_ERR 
+			       "ipmi_kcs: can't reserve port @ 0x%4.4x\n",
+		       	       kcs_port);
+			return -EIO;
+		}
+	} else {
+		if (request_mem_region(kcs_physaddr, 2, DEVICE_NAME) == NULL) {
+			kfree(new_kcs);
+			printk(KERN_ERR 
+			       "ipmi_kcs: can't reserve memory @ 0x%lx\n",
+		       	       kcs_physaddr);
+			return -EIO;
+		}
+		if ((new_kcs->addr = ioremap(kcs_physaddr, 2)) == NULL) {
+			kfree(new_kcs);
+			printk(KERN_ERR 
+			       "ipmi_kcs: can't remap memory at 0x%lx\n",
+		       	       kcs_physaddr);
+			return -EIO;
+		}
+	}
+
+	new_kcs->kcs_sm = kmalloc(kcs_size(), GFP_KERNEL);
+	if (!new_kcs->kcs_sm) {
+		printk(KERN_ERR "ipmi_kcs: out of memory\n");
+		rv = -ENOMEM;
+		goto out_err;
+	}
+	init_kcs_data(new_kcs->kcs_sm, kcs_port, new_kcs->addr);
+	spin_lock_init(&(new_kcs->kcs_lock));
+	spin_lock_init(&(new_kcs->msg_lock));
+
+	rv = ipmi_kcs_detect_hardware(kcs_port, new_kcs->addr, new_kcs->kcs_sm);
+	if (rv) {
+		if (kcs_port) 
+			printk(KERN_ERR 
+			       "ipmi_kcs: No KCS @ port 0x%4.4x\n", 
+			       kcs_port);
+		else
+			printk(KERN_ERR 
+			       "ipmi_kcs: No KCS @ addr 0x%lx\n", 
+			       kcs_physaddr);
+		goto out_err;
+	}
+
+	if (irq != 0) {
+		rv = request_irq(irq,
+				 kcs_irq_handler,
+				 SA_INTERRUPT,
+				 DEVICE_NAME,
+				 new_kcs);
+		if (rv) {
+			printk(KERN_WARNING
+			       "ipmi_kcs: %s unable to claim interrupt %d,"
+			       " running polled\n",
+			       DEVICE_NAME, irq);
+			irq = 0;
+		}
+	}
+	new_kcs->irq = irq;
+
+	INIT_LIST_HEAD(&(new_kcs->xmit_msgs));
+	INIT_LIST_HEAD(&(new_kcs->hp_xmit_msgs));
+	new_kcs->curr_msg = NULL;
+	atomic_set(&new_kcs->req_events, 0);
+	new_kcs->run_to_completion = 0;
+
+	start_clear_flags(new_kcs);
+
+	if (irq) {
+		new_kcs->kcs_state = KCS_CLEARING_FLAGS_THEN_SET_IRQ;
+
+		printk(KERN_INFO 
+		       "ipmi_kcs: Acquiring BMC @ port=0x%x irq=%d\n",
+		       kcs_port, irq);
+
+	} else {
+		if (kcs_port)
+			printk(KERN_INFO 
+			       "ipmi_kcs: Acquiring BMC @ port=0x%x\n",
+		       	       kcs_port);
+		else
+			printk(KERN_INFO 
+			       "ipmi_kcs: Acquiring BMC @ addr=0x%lx\n",
+		       	       kcs_physaddr);
+	}
+
+	rv = ipmi_register_smi(&handlers,
+			       new_kcs,
+			       ipmi_version_major,
+			       ipmi_version_minor,
+			       &(new_kcs->intf));
+	if (rv) {
+		free_irq(irq, new_kcs);
+		printk(KERN_ERR 
+		       "ipmi_kcs: Unable to register device: error %d\n",
+		       rv);
+		goto out_err;
+	}
+
+	new_kcs->interrupt_disabled = 0;
+	new_kcs->timer_stopped = 0;
+	new_kcs->stop_operation = 0;
+
+	init_timer(&(new_kcs->kcs_timer));
+	new_kcs->kcs_timer.data = (long) new_kcs;
+	new_kcs->kcs_timer.function = kcs_timeout;
+	new_kcs->last_timeout_jiffies = jiffies;
+	new_kcs->kcs_timer.expires = jiffies + KCS_TIMEOUT_JIFFIES;
+	add_timer(&(new_kcs->kcs_timer));
+
+	*kcs = new_kcs;
+
+	return 0;
+
+ out_err:
+	if (kcs_port) 
+		release_region (kcs_port, 2);
+	if (new_kcs->addr) 
+		iounmap(new_kcs->addr);
+	if (kcs_physaddr) 
+		release_mem_region(kcs_physaddr, 2);
+	if (new_kcs->kcs_sm)
+		kfree(new_kcs->kcs_sm);
+	kfree(new_kcs);
+	return rv;
+}
+
+#ifdef CONFIG_ACPI
+
+/* Retrieve the base physical address from ACPI tables.  Originally
+   from Hewlett-Packard simple bmc.c, a GPL KCS driver. */
+
+#include <linux/acpi.h>
+/* A real hack, but everything's not there yet in 2.4. */
+#define COMPILER_DEPENDENT_UINT64 unsigned long
+#include <../drivers/acpi/include/acpi.h>
+#include <../drivers/acpi/include/actypes.h>
+
+struct SPMITable {
+	s8	Signature[4];
+	u32	Length;
+	u8	Revision;
+	u8	Checksum;
+	s8	OEMID[6];
+	s8	OEMTableID[8];
+	s8	OEMRevision[4];
+	s8	CreatorID[4];
+	s8	CreatorRevision[4];
+	s16	InterfaceType;
+	s16	SpecificationRevision;
+	u8	InterruptType;
+	u8	GPE;
+	s16	Reserved;
+	u64	GlobalSystemInterrupt;
+	u8	BaseAddress[12];
+	u8	UID[4];
+} __attribute__ ((packed));
+
+static unsigned long acpi_find_bmc(void)
+{
+	acpi_status       status;
+	acpi_table_header *spmi;
+	static unsigned long io_base = 0;
+
+	if (io_base != 0)
+		return io_base;
+
+	status = acpi_get_firmware_table("SPMI", 1,
+			ACPI_LOGICAL_ADDRESSING, &spmi);
+
+	if (status != AE_OK) {
+		printk(KERN_ERR "ipmi_kcs: SPMI table not found.\n");
+		return 0;
+	}
+
+	memcpy(&io_base, ((struct SPMITable *)spmi)->BaseAddress,
+			sizeof(io_base));
+	
+	return io_base;
+}
+#endif
+
+static __init int init_ipmi_kcs(void)
+{
+	int		rv = 0;
+	int		pos = 0;
+	int		i = 0;
+#ifdef CONFIG_ACPI
+	unsigned long	physaddr = 0;
+#endif
+
+	if (initialized)
+		return 0;
+	initialized = 1;
+
+	/* First do the "command-line" parameters */
+
+	for (i=0; i < KCS_MAX_PARMS; i++) {
+		rv = init_one_kcs(kcs_ports[i], 
+				  kcs_irqs[i], 
+				  0, 
+				  &(kcs_infos[pos]));
+		if (rv == 0)
+			pos++;
+
+		rv = init_one_kcs(0, 
+				  kcs_irqs[i], 
+				  kcs_addrs[i], 
+				  &(kcs_infos[pos]));
+		if (rv == 0)
+			pos++;
+	}
+
+	/* Only try the defaults if enabled and resources are available
+	   (because they weren't already specified above). */
+
+	if (kcs_trydefaults) {
+#ifdef CONFIG_ACPI
+		if ((physaddr = acpi_find_bmc())) {
+			if (!check_mem_region(physaddr, 2)) {
+				rv = init_one_kcs(0, 
+						  0, 
+						  physaddr, 
+						  &(kcs_infos[pos]));
+				if (rv == 0)
+					pos++;
+			}
+		}
+#endif
+		if (!check_region(DEFAULT_IO_PORT, 2)) {
+			rv = init_one_kcs(DEFAULT_IO_PORT, 
+					  0, 
+					  0, 
+					  &(kcs_infos[pos]));
+			if (rv == 0)
+				pos++;
+		}
+	}
+
+	if (kcs_infos[0] == NULL) {
+		printk("ipmi_kcs: Unable to find any KCS interfaces\n");
+		return -ENODEV;
+	} 
+
+	return 0;
+}
+module_init(init_ipmi_kcs);
+
+#ifdef MODULE
+void __exit cleanup_one_kcs(struct kcs_info *to_clean)
+{
+	int           rv;
+	unsigned long flags;
+
+	if (! to_clean)
+		return;
+
+	/* Tell the timer and interrupt handlers that we are shutting
+	   down. */
+	spin_lock_irqsave(&(to_clean->kcs_lock), flags);
+	spin_lock(&(to_clean->msg_lock));
+
+	to_clean->stop_operation = 1;
+
+	if (to_clean->irq != 0)
+		free_irq(to_clean->irq, to_clean);
+	if (to_clean->port) {
+		printk(KERN_INFO 
+		       "ipmi_kcs: Releasing BMC @ port=0x%x\n",
+		       to_clean->port);
+		release_region (to_clean->port, 2);
+	}
+	if (to_clean->addr) {
+		printk(KERN_INFO 
+		       "ipmi_kcs: Releasing BMC @ addr=0x%lx\n",
+		       to_clean->physaddr);
+		iounmap(to_clean->addr);
+		release_mem_region(to_clean->physaddr, 2);
+	}
+
+	spin_unlock(&(to_clean->msg_lock));
+	spin_unlock_irqrestore(&(to_clean->kcs_lock), flags);
+
+	/* Wait for the timer to stop.  This avoids problems with race
+	   conditions removing the timer here.  Hopefully this will be
+	   long enough to avoid problems with interrupts still
+	   running. */
+	schedule_timeout(2);
+	while (!to_clean->timer_stopped) {
+		schedule_timeout(1);
+	}
+
+	rv = ipmi_unregister_smi(to_clean->intf);
+	if (rv) {
+		printk(KERN_ERR 
+		       "ipmi_kcs: Unable to unregister device: errno=%d\n",
+		       rv);
+	}
+
+	initialized = 0;
+
+	kfree(to_clean->kcs_sm);
+	kfree(to_clean);
+}
+
+static __exit void cleanup_ipmi_kcs(void)
+{
+	int i;
+
+	if (!initialized)
+		return;
+
+	for (i=0; i<KCS_MAX_DRIVERS; i++) {
+		cleanup_one_kcs(kcs_infos[i]);
+	}
+}
+module_exit(cleanup_ipmi_kcs);
+#else
+
+/* Unfortunately, cmdline::get_options() only returns integers, not
+   longs.  Since we need ulongs (64-bit physical addresses) parse the 
+   comma-separated list manually.  Arguments can be one of these forms:
+   m0xaabbccddeeff	A physical memory address without an IRQ
+   m0xaabbccddeeff:cc	A physical memory address with an IRQ
+   p0xaabb		An IO port without an IRQ
+   p0xaabb:cc		An IO port with an IRQ
+   nodefaults		Suppress trying the default IO port or ACPI address 
+
+   For example, to pass one IO port with an IRQ, one address, and 
+   suppress the use of the default IO port and ACPI address,
+   use this option string: ipmi_kcs=p0xCA2:5,m0xFF5B0022,nodefaults
+
+   Remember, ipmi_kcs_setup() is passed the string after the equal sign. */
+
+static int __init ipmi_kcs_setup(char *str)
+{
+	unsigned long val;
+	char *cur, *colon;
+	int pos;
+
+	pos = 0;
+	
+	cur = strsep(&str, ",");
+	while ((cur) && (*cur) && (pos < KCS_MAX_PARMS)) {
+		switch (*cur) {
+		case 'n':
+			if (strcmp(cur, "nodefaults") == 0)
+				kcs_trydefaults = 0;
+			else
+				printk(KERN_INFO 
+				       "ipmi_kcs: bad parameter value %s\n",
+				       cur);
+			break;
+		
+		case 'm':
+		case 'p':
+			val = simple_strtoul(cur + 1,
+					     &colon,
+					     0);
+			if (*cur == 'p')
+				kcs_ports[pos] = val;
+			else
+				kcs_addrs[pos] = val;
+			if (*colon == ':') {
+				val = simple_strtoul(colon + 1,
+						     &colon,
+						     0);
+				kcs_irqs[pos] = val;
+			}
+			pos++;
+			break;
+
+		default:
+			printk(KERN_INFO 
+			       "ipmi_kcs: bad parameter value %s\n",
+			       cur);
+		}
+		cur = strsep(&str, ",");
+	}
+
+	return 1;
+}
+__setup("ipmi_kcs=", ipmi_kcs_setup);
+#endif
+
+MODULE_LICENSE("GPL");
diff -urN linux.orig/drivers/char/ipmi/ipmi_kcs_sm.c linux/drivers/char/ipmi/ipmi_kcs_sm.c
--- linux.orig/drivers/char/ipmi/ipmi_kcs_sm.c	Wed Dec 31 18:00:00 1969
+++ linux/drivers/char/ipmi/ipmi_kcs_sm.c	Fri Jan 10 21:00:19 2003
@@ -0,0 +1,485 @@
+/*
+ * ipmi_kcs_sm.c
+ *
+ * State machine for handling IPMI KCS interfaces.
+ *
+ * Author: MontaVista Software, Inc.
+ *         Corey Minyard <minyard@mvista.com>
+ *         source@mvista.com
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ *  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.
+ *
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This state machine is taken from the state machine in the IPMI spec,
+ * pretty much verbatim.  If you have questions about the states, see
+ * that document.
+ */
+
+#include <asm/io.h>
+#include <asm/string.h>		/* Gets rid of memcpy warning */
+
+#include "ipmi_kcs_sm.h"
+
+/* Set this if you want a printout of why the state machine was hosed
+   when it gets hosed. */
+#define DEBUG_HOSED_REASON
+
+/* Print the state machine state on entry every time. */
+#undef DEBUG_STATE
+
+/* The states the KCS driver may be in. */
+enum kcs_states {
+	KCS_IDLE,		/* The KCS interface is currently
+                                   doing nothing. */
+	KCS_START_OP,		/* We are starting an operation.  The
+				   data is in the output buffer, but
+				   nothing has been done to the
+				   interface yet.  This was added to
+				   the state machine in the spec to
+				   wait for the initial IBF. */
+	KCS_WAIT_WRITE_START,	/* We have written a write cmd to the
+				   interface. */
+	KCS_WAIT_WRITE,		/* We are writing bytes to the
+                                   interface. */
+	KCS_WAIT_WRITE_END,	/* We have written the write end cmd
+                                   to the interface, and still need to
+                                   write the last byte. */
+	KCS_WAIT_READ,		/* We are waiting to read data from
+				   the interface. */
+	KCS_ERROR0,		/* State to transition to the error
+				   handler, this was added to the
+				   state machine in the spec to be
+				   sure IBF was there. */
+	KCS_ERROR1,		/* First stage error handler, wait for
+                                   the interface to respond. */
+	KCS_ERROR2,		/* The abort cmd has been written,
+				   wait for the interface to
+				   respond. */
+	KCS_ERROR3,		/* We wrote some data to the
+				   interface, wait for it to switch to
+				   read mode. */
+	KCS_HOSED		/* The hardware failed to follow the
+				   state machine. */
+};
+
+#define MAX_KCS_READ_SIZE 80
+#define MAX_KCS_WRITE_SIZE 80
+
+/* Timeouts in microseconds. */
+#define IBF_RETRY_TIMEOUT 1000000
+#define OBF_RETRY_TIMEOUT 1000000
+#define MAX_ERROR_RETRIES 10
+
+#define IPMI_ERR_MSG_TRUNCATED	0xc6
+#define IPMI_ERR_UNSPECIFIED	0xff
+
+struct kcs_data
+{
+	enum kcs_states state;
+	unsigned int    port;
+	unsigned char	*addr;
+	unsigned char   write_data[MAX_KCS_WRITE_SIZE];
+	int             write_pos;
+	int             write_count;
+	int             orig_write_count;
+	unsigned char   read_data[MAX_KCS_READ_SIZE];
+	int             read_pos;
+	int	        truncated;
+
+	unsigned int  error_retries;
+	long          ibf_timeout;
+	long          obf_timeout;
+};
+
+void init_kcs_data(struct kcs_data *kcs, unsigned int port, unsigned char *addr)
+{
+	kcs->state = KCS_IDLE;
+	kcs->port = port;
+	kcs->addr = addr;
+	kcs->write_pos = 0;
+	kcs->write_count = 0;
+	kcs->orig_write_count = 0;
+	kcs->read_pos = 0;
+	kcs->error_retries = 0;
+	kcs->truncated = 0;
+	kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
+	kcs->obf_timeout = OBF_RETRY_TIMEOUT;
+}
+
+/* Remember, init_one_kcs() insured port and addr can't both be set */
+
+static inline unsigned char read_status(struct kcs_data *kcs)
+{
+        if (kcs->port)
+		return inb(kcs->port + 1);
+        else
+		return readb(kcs->addr + 1);
+}
+
+static inline unsigned char read_data(struct kcs_data *kcs)
+{
+        if (kcs->port)
+		return inb(kcs->port + 0);
+        else
+		return readb(kcs->addr + 0);
+}
+
+static inline void write_cmd(struct kcs_data *kcs, unsigned char data)
+{
+        if (kcs->port)
+		outb(data, kcs->port + 1);
+        else
+		writeb(data, kcs->addr + 1);
+}
+
+static inline void write_data(struct kcs_data *kcs, unsigned char data)
+{
+        if (kcs->port)
+		outb(data, kcs->port + 0);
+        else
+		writeb(data, kcs->addr + 0);
+}
+
+/* Control codes. */
+#define KCS_GET_STATUS_ABORT	0x60
+#define KCS_WRITE_START		0x61
+#define KCS_WRITE_END		0x62
+#define KCS_READ_BYTE		0x68
+
+/* Status bits. */
+#define GET_STATUS_STATE(status) (((status) >> 6) & 0x03)
+#define KCS_IDLE_STATE	0
+#define KCS_READ_STATE	1
+#define KCS_WRITE_STATE	2
+#define KCS_ERROR_STATE	3
+#define GET_STATUS_ATN(status) ((status) & 0x04)
+#define GET_STATUS_IBF(status) ((status) & 0x02)
+#define GET_STATUS_OBF(status) ((status) & 0x01)
+
+
+static inline void write_next_byte(struct kcs_data *kcs)
+{
+	write_data(kcs, kcs->write_data[kcs->write_pos]);
+	(kcs->write_pos)++;
+	(kcs->write_count)--;
+}
+
+static inline void start_error_recovery(struct kcs_data *kcs, char *reason)
+{
+	(kcs->error_retries)++;
+	if (kcs->error_retries > MAX_ERROR_RETRIES) {
+#ifdef DEBUG_HOSED_REASON
+		printk("ipmi_kcs_sm: kcs hosed: %s\n", reason);
+#endif
+		kcs->state = KCS_HOSED;
+	} else {
+		kcs->state = KCS_ERROR0;
+	}
+}
+
+static inline void read_next_byte(struct kcs_data *kcs)
+{
+	if (kcs->read_pos >= MAX_KCS_READ_SIZE) {
+		/* Throw the data away and mark it truncated. */
+		read_data(kcs);
+		kcs->truncated = 1;
+	} else {
+		kcs->read_data[kcs->read_pos] = read_data(kcs);
+		(kcs->read_pos)++;
+	}
+	write_data(kcs, KCS_READ_BYTE);
+}
+
+static inline int check_ibf(struct kcs_data *kcs,
+			    unsigned char   status,
+			    long            time)
+{
+	if (GET_STATUS_IBF(status)) {
+		kcs->ibf_timeout -= time;
+		if (kcs->ibf_timeout < 0) {
+			start_error_recovery(kcs, "IBF not ready in time");
+			kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
+			return 1;
+		}
+		return 0;
+	}
+	kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
+	return 1;
+}
+
+static inline int check_obf(struct kcs_data *kcs,
+			    unsigned char   status,
+			    long            time)
+{
+	if (! GET_STATUS_OBF(status)) {
+		kcs->obf_timeout -= time;
+		if (kcs->obf_timeout < 0) {
+		    start_error_recovery(kcs, "OBF not ready in time");
+		    return 1;
+		}
+		return 0;
+	}
+	kcs->obf_timeout = OBF_RETRY_TIMEOUT;
+	return 1;
+}
+
+static void clear_obf(struct kcs_data *kcs, unsigned char status)
+{
+	if (GET_STATUS_OBF(status))
+		read_data(kcs);
+}
+
+static void restart_kcs_transaction(struct kcs_data *kcs)
+{
+	kcs->write_count = kcs->orig_write_count;
+	kcs->write_pos = 0;
+	kcs->read_pos = 0;
+	kcs->state = KCS_WAIT_WRITE_START;
+	kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
+	kcs->obf_timeout = OBF_RETRY_TIMEOUT;
+	write_cmd(kcs, KCS_WRITE_START);
+}
+
+int start_kcs_transaction(struct kcs_data *kcs, char *data, unsigned int size)
+{
+	if ((size < 2) || (size > MAX_KCS_WRITE_SIZE)) {
+		return -1;
+	}
+
+	if ((kcs->state != KCS_IDLE) && (kcs->state != KCS_HOSED)) {
+		return -2;
+	}
+
+	kcs->error_retries = 0;
+	memcpy(kcs->write_data, data, size);
+	kcs->write_count = size;
+	kcs->orig_write_count = size;
+	kcs->write_pos = 0;
+	kcs->read_pos = 0;
+	kcs->state = KCS_START_OP;
+	kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
+	kcs->obf_timeout = OBF_RETRY_TIMEOUT;
+	return 0;
+}
+
+int kcs_get_result(struct kcs_data *kcs, unsigned char *data, int length)
+{
+	if (length < kcs->read_pos) {
+		kcs->read_pos = length;
+		kcs->truncated = 1;
+	}
+
+	memcpy(data, kcs->read_data, kcs->read_pos);
+
+	if ((length >= 3) && (kcs->read_pos < 3)) {
+		/* Guarantee that we return at least 3 bytes, with an
+		   error in the third byte if it is too short. */
+		data[2] = IPMI_ERR_UNSPECIFIED;
+		kcs->read_pos = 3;
+	}
+	if (kcs->truncated) {
+		/* Report a truncated error.  We might overwrite
+		   another error, but that's too bad, the user needs
+		   to know it was truncated. */
+		data[2] = IPMI_ERR_MSG_TRUNCATED;
+		kcs->truncated = 0;
+	}
+
+	return kcs->read_pos;
+}
+
+/* This implements the state machine defined in the IPMI manual, see
+   that for details on how this works.  Divide that flowchart into
+   sections delimited by "Wait for IBF" and this will become clear. */
+enum kcs_result kcs_event(struct kcs_data *kcs, long time)
+{
+	unsigned char status;
+	unsigned char state;
+
+	status = read_status(kcs);
+
+#ifdef DEBUG_STATE
+	printk("  State = %d, %x\n", kcs->state, status);
+#endif
+	/* All states wait for ibf, so just do it here. */
+	if (!check_ibf(kcs, status, time))
+		return KCS_CALL_WITH_DELAY;
+
+	/* Just about everything looks at the KCS state, so grab that, too. */
+	state = GET_STATUS_STATE(status);
+
+	switch (kcs->state) {
+	case KCS_IDLE:
+		/* If there's and interrupt source, turn it off. */
+		clear_obf(kcs, status);
+
+		if (GET_STATUS_ATN(status))
+			return KCS_ATTN;
+		else
+			return KCS_SM_IDLE;
+
+	case KCS_START_OP:
+		if (state != KCS_IDLE) {
+			start_error_recovery(kcs,
+					     "State machine not idle at start");
+			break;
+		}
+
+		clear_obf(kcs, status);
+		write_cmd(kcs, KCS_WRITE_START);
+		kcs->state = KCS_WAIT_WRITE_START;
+		break;
+
+	case KCS_WAIT_WRITE_START:
+		if (state != KCS_WRITE_STATE) {
+			start_error_recovery(
+				kcs,
+				"Not in write state at write start");
+			break;
+		}
+		read_data(kcs);
+		if (kcs->write_count == 1) {
+			write_cmd(kcs, KCS_WRITE_END);
+			kcs->state = KCS_WAIT_WRITE_END;
+		} else {
+			write_next_byte(kcs);
+			kcs->state = KCS_WAIT_WRITE;
+		}
+		break;
+
+	case KCS_WAIT_WRITE:
+		if (state != KCS_WRITE_STATE) {
+			start_error_recovery(kcs,
+					     "Not in write state for write");
+			break;
+		}
+		clear_obf(kcs, status);
+		if (kcs->write_count == 1) {
+			write_cmd(kcs, KCS_WRITE_END);
+			kcs->state = KCS_WAIT_WRITE_END;
+		} else {
+			write_next_byte(kcs);
+		}
+		break;
+		
+	case KCS_WAIT_WRITE_END:
+		if (state != KCS_WRITE_STATE) {
+			start_error_recovery(kcs,
+					     "Not in write state for write end");
+			break;
+		}
+		clear_obf(kcs, status);
+		write_next_byte(kcs);
+		kcs->state = KCS_WAIT_READ;
+		break;
+
+	case KCS_WAIT_READ:
+		if ((state != KCS_READ_STATE) && (state != KCS_IDLE_STATE)) {
+			start_error_recovery(
+				kcs,
+				"Not in read or idle in read state");
+			break;
+		}
+
+		if (state == KCS_READ_STATE) {
+			if (! check_obf(kcs, status, time))
+				return KCS_CALL_WITH_DELAY;
+			read_next_byte(kcs);
+		} else {
+			/* We don't implement this exactly like the state
+			   machine in the spec.  Some broken hardware
+			   does not write the final dummy byte to the
+			   read register.  Thus obf will never go high
+			   here.  We just go straight to idle, and we
+			   handle clearing out obf in idle state if it
+			   happens to come in. */
+			clear_obf(kcs, status);
+			kcs->orig_write_count = 0;
+			kcs->state = KCS_IDLE;
+			return KCS_TRANSACTION_COMPLETE;
+		}
+		break;
+
+	case KCS_ERROR0:
+		clear_obf(kcs, status);
+		write_cmd(kcs, KCS_GET_STATUS_ABORT);
+		kcs->state = KCS_ERROR1;
+		break;
+
+	case KCS_ERROR1:
+		clear_obf(kcs, status);
+		write_data(kcs, 0);
+		kcs->state = KCS_ERROR2;
+		break;
+		
+	case KCS_ERROR2:
+		if (state != KCS_READ_STATE) {
+			start_error_recovery(kcs,
+					     "Not in read state for error2");
+			break;
+		}
+		if (! check_obf(kcs, status, time))
+			return KCS_CALL_WITH_DELAY;
+
+		clear_obf(kcs, status);
+		write_data(kcs, KCS_READ_BYTE);
+		kcs->state = KCS_ERROR3;
+		break;
+		
+	case KCS_ERROR3:
+		if (state != KCS_IDLE_STATE) {
+			start_error_recovery(kcs,
+					     "Not in idle state for error3");
+			break;
+		}
+
+		if (! check_obf(kcs, status, time))
+			return KCS_CALL_WITH_DELAY;
+
+		clear_obf(kcs, status);
+		if (kcs->orig_write_count) {
+			restart_kcs_transaction(kcs);
+		} else {
+			kcs->state = KCS_IDLE;
+			return KCS_TRANSACTION_COMPLETE;
+		}
+		break;
+			
+	case KCS_HOSED:
+		return KCS_SM_HOSED;
+	}
+
+	if (kcs->state == KCS_HOSED) {
+		init_kcs_data(kcs, kcs->port, kcs->addr);
+		return KCS_SM_HOSED;
+	}
+
+	return KCS_CALL_WITHOUT_DELAY;
+}
+
+int kcs_size(void)
+{
+	return sizeof(struct kcs_data);
+}
diff -urN linux.orig/drivers/char/ipmi/ipmi_kcs_sm.h linux/drivers/char/ipmi/ipmi_kcs_sm.h
--- linux.orig/drivers/char/ipmi/ipmi_kcs_sm.h	Wed Dec 31 18:00:00 1969
+++ linux/drivers/char/ipmi/ipmi_kcs_sm.h	Thu Oct 31 17:26:33 2002
@@ -0,0 +1,70 @@
+/*
+ * ipmi_kcs_sm.h
+ *
+ * State machine for handling IPMI KCS interfaces.
+ *
+ * Author: MontaVista Software, Inc.
+ *         Corey Minyard <minyard@mvista.com>
+ *         source@mvista.com
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ *  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.
+ *
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+struct kcs_data;
+
+void init_kcs_data(struct kcs_data *kcs,
+		   unsigned int    port,
+		   unsigned char   *addr);
+
+/* Start a new transaction in the state machine.  This will return -2
+   if the state machine is not idle, -1 if the size is invalid (to
+   large or too small), or 0 if the transaction is successfully
+   completed. */
+int start_kcs_transaction(struct kcs_data *kcs, char *data, unsigned int size);
+
+/* Return the results after the transaction.  This will return -1 if
+   the buffer is too small, zero if no transaction is present, or the
+   actual length of the result data. */
+int kcs_get_result(struct kcs_data *kcs, unsigned char *data, int length);
+
+enum kcs_result
+{
+	KCS_CALL_WITHOUT_DELAY, /* Call the driver again immediately */
+	KCS_CALL_WITH_DELAY,	/* Delay some before calling again. */
+	KCS_TRANSACTION_COMPLETE, /* A transaction is finished. */
+	KCS_SM_IDLE,		/* The SM is in idle state. */
+	KCS_SM_HOSED,		/* The hardware violated the state machine. */
+	KCS_ATTN		/* The hardware is asserting attn and the
+				   state machine is idle. */
+};
+
+/* Call this periodically (for a polled interface) or upon receiving
+   an interrupt (for a interrupt-driven interface).  If interrupt
+   driven, you should probably poll this periodically when not in idle
+   state.  This should be called with the time that passed since the
+   last call, if it is significant.  Time is in microseconds. */
+enum kcs_result kcs_event(struct kcs_data *kcs, long time);
+
+/* Return the size of the KCS structure in bytes. */
+int kcs_size(void);
diff -urN linux.orig/drivers/char/ipmi/ipmi_msghandler.c linux/drivers/char/ipmi/ipmi_msghandler.c
--- linux.orig/drivers/char/ipmi/ipmi_msghandler.c	Wed Dec 31 18:00:00 1969
+++ linux/drivers/char/ipmi/ipmi_msghandler.c	Mon Jan 13 12:01:34 2003
@@ -0,0 +1,1984 @@
+/*
+ * ipmi_msghandler.c
+ *
+ * Incoming and outgoing message routing for an IPMI interface.
+ *
+ * Author: MontaVista Software, Inc.
+ *         Corey Minyard <minyard@mvista.com>
+ *         source@mvista.com
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ *  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.
+ *
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+#include <linux/ipmi.h>
+#include <linux/ipmi_smi.h>
+#include <linux/notifier.h>
+#include <linux/init.h>
+
+struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
+static int ipmi_init_msghandler(void);
+
+static int initialized = 0;
+
+#define MAX_EVENTS_IN_QUEUE	25
+
+/* Don't let a message sit in a queue forever, always time it with at lest
+   the max message timer. */
+#define MAX_MSG_TIMEOUT		60000
+
+struct ipmi_user
+{
+	struct list_head link;
+
+	/* The upper layer that handles receive messages. */
+	struct ipmi_user_hndl *handler;
+	void             *handler_data;
+
+	/* The interface this user is bound to. */
+	ipmi_smi_t intf;
+
+	/* Does this interface receive IPMI events? */
+	int gets_events;
+};
+
+struct cmd_rcvr
+{
+	struct list_head link;
+
+	ipmi_user_t   user;
+	unsigned char netfn;
+	unsigned char cmd;
+};
+
+struct seq_table
+{
+	int                  inuse : 1;
+
+	unsigned long        timeout;
+	unsigned long        orig_timeout;
+	unsigned int         retries_left;
+
+	/* To verify on an incoming send message response that this is
+           the message that the response is for, we keep a sequence id
+           and increment it every time we send a message. */
+	long                 seqid;
+
+	/* This is held so we can properly respond to the message on a
+           timeout, and it is used to hold the temporary data for
+           retransmission, too. */
+	struct ipmi_recv_msg *recv_msg;
+};
+
+/* Store the information in a msgid (long) to allow us to find a
+   sequence table entry from the msgid. */
+#define STORE_SEQ_IN_MSGID(seq, seqid) (((seq&0xff)<<26) | (seqid&0x3ffffff))
+
+#define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \
+	do {								\
+		seq = ((msgid >> 26) & 0x3f);				\
+		seqid = (msgid & 0x3fffff);				\
+        } while(0)
+
+#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3fffff)
+
+
+#define IPMI_IPMB_NUM_SEQ	64
+struct ipmi_smi
+{
+	/* The list of upper layers that are using me.  We read-lock
+           this when delivering messages to the upper layer to keep
+           the user from going away while we are processing the
+           message.  This means that you cannot add or delete a user
+           from the receive callback. */
+	rwlock_t                users_lock;
+	struct list_head        users;
+
+	/* The IPMI version of the BMC on the other end. */
+	unsigned char       version_major;
+	unsigned char       version_minor;
+
+	/* This is the lower-layer's sender routine. */
+	struct ipmi_smi_handlers *handlers;
+	void                     *send_info;
+
+	/* A table of sequence numbers for this interface.  We use the
+           sequence numbers for IPMB messages that go out of the
+           interface to match them up with their responses.  A routine
+           is called periodically to time the items in this list. */
+	spinlock_t       seq_lock;
+	struct seq_table seq_table[IPMI_IPMB_NUM_SEQ];
+	int curr_seq;
+
+	/* Messages that were delayed for some reason (out of memory,
+           for instance), will go in here to be processed later in a
+           periodic timer interrupt. */
+	spinlock_t       waiting_msgs_lock;
+	struct list_head waiting_msgs;
+
+	/* The list of command receivers that are registered for commands
+	   on this interface. */
+	rwlock_t	 cmd_rcvr_lock;
+	struct list_head cmd_rcvrs;
+
+	/* Events that were queues because no one was there to receive
+           them. */
+	spinlock_t       events_lock; /* For dealing with event stuff. */
+	struct list_head waiting_events;
+	unsigned int     waiting_events_count; /* How many events in queue? */
+
+	/* This will be non-null if someone registers to receive all
+	   IPMI commands (this is for interface emulation).  There
+	   may not be any things in the cmd_rcvrs list above when
+	   this is registered. */
+	ipmi_user_t all_cmd_rcvr;
+
+	/* My slave address.  This is initialized to IPMI_BMC_SLAVE_ADDR,
+	   but may be changed by the user. */
+	unsigned char my_address;
+
+	/* My LUN.  This should generally stay the SMS LUN, but just in
+	   case... */
+	unsigned char my_lun;
+};
+
+int
+ipmi_register_all_cmd_rcvr(ipmi_user_t user)
+{
+	int flags;
+	int rv = -EBUSY;
+
+	write_lock_irqsave(&(user->intf->users_lock), flags);
+	write_lock(&(user->intf->cmd_rcvr_lock));
+	if ((user->intf->all_cmd_rcvr == NULL)
+	    && (list_empty(&(user->intf->cmd_rcvrs))))
+	{
+		user->intf->all_cmd_rcvr = user;
+		rv = 0;
+	}
+	write_unlock(&(user->intf->cmd_rcvr_lock));
+	write_unlock_irqrestore(&(user->intf->users_lock), flags);
+	return rv;
+}
+
+int
+ipmi_unregister_all_cmd_rcvr(ipmi_user_t user)
+{
+	int flags;
+	int rv = -EINVAL;
+
+	write_lock_irqsave(&(user->intf->users_lock), flags);
+	write_lock(&(user->intf->cmd_rcvr_lock));
+	if (user->intf->all_cmd_rcvr == user)
+	{
+		user->intf->all_cmd_rcvr = NULL;
+		rv = 0;
+	}
+	write_unlock(&(user->intf->cmd_rcvr_lock));
+	write_unlock_irqrestore(&(user->intf->users_lock), flags);
+	return rv;
+}
+
+
+#define MAX_IPMI_INTERFACES 4
+static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES];
+
+/* Used to keep interfaces from going away while operations are
+   operating on interfaces.  Grab read if you are not modifying the
+   interfaces, write if you are. */
+static DECLARE_RWSEM(interfaces_sem);
+
+/* Directly protects the ipmi_interfaces data structure.  This is
+   claimed in the timer interrupt. */
+static spinlock_t interfaces_lock = SPIN_LOCK_UNLOCKED;
+
+/* List of watchers that want to know when smi's are added and
+   deleted. */
+static struct list_head smi_watchers = LIST_HEAD_INIT(smi_watchers);
+static DECLARE_RWSEM(smi_watchers_sem);
+
+int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
+{
+	int i;
+
+	down_read(&interfaces_sem);
+	down_write(&smi_watchers_sem);
+	list_add(&(watcher->link), &smi_watchers);
+	for (i=0; i<MAX_IPMI_INTERFACES; i++) {
+		if (ipmi_interfaces[i] != NULL) {
+			watcher->new_smi(i);
+		}
+	}
+	up_write(&smi_watchers_sem);
+	up_read(&interfaces_sem);
+	return 0;
+}
+
+int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher)
+{
+	down_write(&smi_watchers_sem);
+	list_del(&(watcher->link));
+	up_write(&smi_watchers_sem);
+	return 0;
+}
+
+int
+ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2)
+{
+	if (addr1->addr_type != addr2->addr_type)
+		return 0;
+
+	if (addr1->channel != addr2->channel)
+		return 0;
+
+	if (addr1->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
+		struct ipmi_system_interface_addr *smi_addr1
+		    = (struct ipmi_system_interface_addr *) addr1;
+		struct ipmi_system_interface_addr *smi_addr2
+		    = (struct ipmi_system_interface_addr *) addr2;
+		return (smi_addr1->lun == smi_addr2->lun);
+	}
+
+	if ((addr1->addr_type == IPMI_IPMB_ADDR_TYPE)
+	    || (addr1->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
+	{
+		struct ipmi_ipmb_addr *ipmb_addr1
+		    = (struct ipmi_ipmb_addr *) addr1;
+		struct ipmi_ipmb_addr *ipmb_addr2
+		    = (struct ipmi_ipmb_addr *) addr2;
+
+		return ((ipmb_addr1->slave_addr == ipmb_addr2->slave_addr)
+			&& (ipmb_addr1->lun == ipmb_addr2->lun));
+	}
+
+	return 1;
+}
+
+int ipmi_validate_addr(struct ipmi_addr *addr, int len)
+{
+	if (len < sizeof(struct ipmi_system_interface_addr)) {
+		return -EINVAL;
+	}
+
+	if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
+		if (addr->channel != IPMI_BMC_CHANNEL)
+			return -EINVAL;
+		return 0;
+	}
+
+	if ((addr->channel == IPMI_BMC_CHANNEL)
+	    || (addr->channel >= IPMI_NUM_CHANNELS)
+	    || (addr->channel < 0))
+		return -EINVAL;
+
+	if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE)
+	    || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
+	{
+		if (len < sizeof(struct ipmi_ipmb_addr)) {
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+unsigned int ipmi_addr_length(int addr_type)
+{
+	if (addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
+		return sizeof(struct ipmi_system_interface_addr);
+
+	if ((addr_type == IPMI_IPMB_ADDR_TYPE)
+	    || (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
+	{
+		return sizeof(struct ipmi_ipmb_addr);
+	}
+
+	return 0;
+}
+
+static void deliver_response(struct ipmi_recv_msg *msg)
+{
+    msg->user->handler->ipmi_recv_hndl(msg, msg->user->handler_data);
+}
+
+/* Find the next sequence number not being used and add the given
+   message with the given timeout to the sequence table.  This must be
+   called with the interface's seq_lock held. */
+static int intf_next_seq(ipmi_smi_t           intf,
+			 struct ipmi_recv_msg *recv_msg,
+			 unsigned long        timeout,
+			 int                  retries,
+			 unsigned char        *seq,
+			 long                 *seqid)
+{
+	int          rv = 0;
+	unsigned int i;
+
+	for (i=intf->curr_seq;
+	     i!=(intf->curr_seq-1);
+	     i=(i+1)%IPMI_IPMB_NUM_SEQ)
+	{
+		if (! intf->seq_table[i].inuse)
+			break;
+	}
+
+	if (! intf->seq_table[i].inuse) {
+		intf->seq_table[i].recv_msg = recv_msg;
+
+		/* Start with the maximum timeout, when the send response
+		   comes in we will start the real timer. */
+		intf->seq_table[i].timeout = MAX_MSG_TIMEOUT;
+		intf->seq_table[i].orig_timeout = timeout;
+		intf->seq_table[i].retries_left = retries;
+		intf->seq_table[i].inuse = 1;
+		intf->seq_table[i].seqid = NEXT_SEQID(intf->seq_table[i].seqid);
+		*seq = i;
+		*seqid = intf->seq_table[i].seqid;
+		intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ;
+	} else {
+		rv = -EAGAIN;
+	}
+	
+	return rv;
+}
+
+/* Return the receive message for the given sequence number and
+   release the sequence number so it can be reused.  Some other data
+   is passed in to be sure the message matches up correctly (to help
+   guard against message coming in after their timeout and the
+   sequence number being reused). */
+static int intf_find_seq(ipmi_smi_t           intf,
+			 unsigned char        seq,
+			 short                channel,
+			 unsigned char        cmd,
+			 unsigned char        netfn,
+			 struct ipmi_addr     *addr,
+			 struct ipmi_recv_msg **recv_msg)
+{
+	int           rv = -ENODEV;
+	unsigned long flags;
+
+	if (seq >= IPMI_IPMB_NUM_SEQ)
+		return -EINVAL;
+
+	spin_lock_irqsave(&(intf->seq_lock), flags);
+	if (intf->seq_table[seq].inuse) {
+		struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg;
+
+		if ((msg->addr.channel == channel)
+		    && (msg->msg.cmd == cmd)
+		    && (msg->msg.netfn == netfn)
+		    && (ipmi_addr_equal(addr, &(msg->addr))))
+		{
+			*recv_msg = msg;
+			intf->seq_table[seq].inuse = 0;
+			rv = 0;
+		}
+	}
+	spin_unlock_irqrestore(&(intf->seq_lock), flags);
+
+	return rv;
+}
+
+
+/* Start the timer for a specific sequence table entry. */
+static int intf_start_seq_timer(ipmi_smi_t           intf,
+				long                 msgid)
+{
+	int           rv = -ENODEV;
+	unsigned long flags;
+	unsigned char seq;
+	unsigned long seqid;
+
+
+	GET_SEQ_FROM_MSGID(msgid, seq, seqid);
+
+	spin_lock_irqsave(&(intf->seq_lock), flags);
+	/* We do this verification because the user can be deleted
+           while a message is outstanding. */
+	if ((intf->seq_table[seq].inuse)
+	    && (intf->seq_table[seq].seqid == seqid))
+	{
+		struct seq_table *ent = &(intf->seq_table[seq]);
+		ent->timeout = ent->orig_timeout;
+	}
+	spin_unlock_irqrestore(&(intf->seq_lock), flags);
+
+	return rv;
+}
+
+
+int ipmi_create_user(unsigned int          if_num,
+		     struct ipmi_user_hndl *handler,
+		     void                  *handler_data,
+		     ipmi_user_t           *user)
+{
+	unsigned long flags;
+	ipmi_user_t   new_user;
+	int           rv = 0;
+
+	/* There is no module usecount here, because it's not
+           required.  Since this can only be used by and called from
+           other modules, they will implicitly use this module, and
+           thus this can't be removed unless the other modules are
+           removed. */
+
+	if (handler == NULL)
+		return -EINVAL;
+
+	/* Make sure the driver is actually initialized, this handles
+	   problems with initialization order. */
+	if (!initialized) {
+		rv = ipmi_init_msghandler();
+		if (rv)
+			return rv;
+
+		/* The init code doesn't return an error if it was turned
+		   off, but it won't initialize.  Check that. */
+		if (!initialized)
+			return -ENODEV;
+	}
+
+	new_user = kmalloc(sizeof(*new_user), GFP_KERNEL);
+	if (! new_user)
+		return -ENOMEM;
+
+	down_read(&interfaces_sem);
+	if ((if_num > MAX_IPMI_INTERFACES) || ipmi_interfaces[if_num] == NULL)
+	{
+		rv = -EINVAL;
+		goto out_unlock;
+	}
+
+	new_user->handler = handler;
+	new_user->handler_data = handler_data;
+	new_user->intf = ipmi_interfaces[if_num];
+	new_user->gets_events = 0;
+
+	rv = new_user->intf->handlers->new_user(new_user->intf->send_info);
+	if (rv)
+		goto out_unlock;
+
+	write_lock_irqsave(&(new_user->intf->users_lock), flags);
+	list_add_tail(&(new_user->link), &(new_user->intf->users));
+	write_unlock_irqrestore(&(new_user->intf->users_lock), flags);
+
+ out_unlock:	
+	if (rv) {
+		kfree(new_user);
+	} else {
+		*user = new_user;
+	}
+
+	up_read(&interfaces_sem);
+	return rv;
+}
+
+static int ipmi_destroy_user_nolock(ipmi_user_t user)
+{
+	int              rv = -ENODEV;
+	ipmi_user_t      t_user;
+	struct list_head *entry, *entry2;
+	int              i;
+	unsigned long    flags;
+
+	/* Find the user and delete them from the list. */
+	list_for_each(entry, &(user->intf->users)) {
+		t_user = list_entry(entry, struct ipmi_user, link);
+		if (t_user == user) {
+			list_del(entry);
+			rv = 0;
+			break;
+		}
+	}
+
+	if (rv) {
+		goto out_unlock;
+	}
+
+	/* Remove the user from the interfaces sequence table. */
+	spin_lock_irqsave(&(user->intf->seq_lock), flags);
+	for (i=0; i<IPMI_IPMB_NUM_SEQ; i++) {
+		if (user->intf->seq_table[i].inuse
+		    && (user->intf->seq_table[i].recv_msg->user == user))
+		{
+			user->intf->seq_table[i].inuse = 0;
+		}
+	}
+	spin_unlock_irqrestore(&(user->intf->seq_lock), flags);
+
+	/* Remove the user from the command receiver's table. */
+	write_lock_irqsave(&(user->intf->cmd_rcvr_lock), flags);
+	list_for_each_safe(entry, entry2, &(user->intf->cmd_rcvrs)) {
+		struct cmd_rcvr *rcvr;
+		rcvr = list_entry(entry, struct cmd_rcvr, link);
+		if (rcvr->user == user) {
+			list_del(entry);
+			kfree(rcvr);
+		}
+	}
+	write_unlock_irqrestore(&(user->intf->cmd_rcvr_lock), flags);
+
+	kfree(user);
+
+ out_unlock:
+
+	return rv;
+}
+
+int ipmi_destroy_user(ipmi_user_t user)
+{
+	int           rv;
+	ipmi_smi_t    intf = user->intf;
+	unsigned long flags;
+
+	down_read(&interfaces_sem);
+	write_lock_irqsave(&(intf->users_lock), flags);
+	rv = ipmi_destroy_user_nolock(user);
+	if (!rv)
+		intf->handlers->user_left(intf->send_info);
+		
+	write_unlock_irqrestore(&(intf->users_lock), flags);
+	up_read(&interfaces_sem);
+	return rv;
+}
+
+void ipmi_get_version(ipmi_user_t   user,
+		      unsigned char *major,
+		      unsigned char *minor)
+{
+	*major = user->intf->version_major;
+	*minor = user->intf->version_minor;
+}
+
+void ipmi_set_my_address(ipmi_user_t   user,
+			 unsigned char address)
+{
+	user->intf->my_address = address;
+}
+
+unsigned char ipmi_get_my_address(ipmi_user_t user)
+{
+	return user->intf->my_address;
+}
+
+void ipmi_set_my_LUN(ipmi_user_t   user,
+		     unsigned char LUN)
+{
+	user->intf->my_lun = LUN & 0x3;
+}
+
+unsigned char ipmi_get_my_LUN(ipmi_user_t user)
+{
+	return user->intf->my_lun;
+}
+
+int ipmi_set_gets_events(ipmi_user_t user, int val)
+{
+	unsigned long         flags;
+	struct list_head      *e, *e2;
+	struct ipmi_recv_msg  *msg;
+
+	read_lock(&(user->intf->users_lock));
+	spin_lock_irqsave(&(user->intf->events_lock), flags);
+	user->gets_events = val;
+
+	if (val) {
+		/* Deliver any queued events. */
+		list_for_each_safe(e, e2, &(user->intf->waiting_events)) {
+			msg = list_entry(e, struct ipmi_recv_msg, link);
+			list_del(e);
+			msg->user = user;
+			deliver_response(msg);
+		}
+	}
+	
+	spin_unlock_irqrestore(&(user->intf->events_lock), flags);
+	read_unlock(&(user->intf->users_lock));
+
+	return 0;
+}
+
+int ipmi_register_for_cmd(ipmi_user_t   user,
+			  unsigned char netfn,
+			  unsigned char cmd)
+{
+	struct list_head *entry;
+	unsigned long    flags;
+	struct cmd_rcvr  *rcvr;
+	int              rv = 0;
+
+
+	rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL);
+	if (! rcvr)
+		return -ENOMEM;
+
+	read_lock(&(user->intf->users_lock));
+	write_lock_irqsave(&(user->intf->cmd_rcvr_lock), flags);
+	if (user->intf->all_cmd_rcvr != NULL) {
+		rv = -EBUSY;
+		goto out_unlock;
+	}
+
+	/* Make sure the command/netfn is not already registered. */
+	list_for_each(entry, &(user->intf->cmd_rcvrs)) {
+		struct cmd_rcvr *cmp;
+		cmp = list_entry(entry, struct cmd_rcvr, link);
+		if ((cmp->netfn == netfn) && (cmp->cmd == cmd)) {
+			rv = -EBUSY;
+			break;
+		}
+	}
+
+	if (! rv) {
+		rcvr->cmd = cmd;
+		rcvr->netfn = netfn;
+		rcvr->user = user;
+		list_add_tail(&(rcvr->link), &(user->intf->cmd_rcvrs));
+	}
+ out_unlock:
+	write_unlock_irqrestore(&(user->intf->cmd_rcvr_lock), flags);
+	read_unlock(&(user->intf->users_lock));
+
+	if (rv)
+		kfree(rcvr);
+
+	return rv;
+}
+
+int ipmi_unregister_for_cmd(ipmi_user_t   user,
+			    unsigned char netfn,
+			    unsigned char cmd)
+{
+	struct list_head *entry;
+	unsigned long    flags;
+	struct cmd_rcvr  *rcvr;
+	int              rv = -ENOENT;
+
+	read_lock(&(user->intf->users_lock));
+	write_lock_irqsave(&(user->intf->cmd_rcvr_lock), flags);
+	/* Make sure the command/netfn is not already registered. */
+	list_for_each(entry, &(user->intf->cmd_rcvrs)) {
+		rcvr = list_entry(entry, struct cmd_rcvr, link);
+		if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)) {
+			rv = 0;
+			list_del(entry);
+			kfree(rcvr);
+			break;
+		}
+	}
+	write_unlock_irqrestore(&(user->intf->cmd_rcvr_lock), flags);
+	read_unlock(&(user->intf->users_lock));
+
+	return rv;
+}
+
+static unsigned char
+ipmb_checksum(unsigned char *data, int size)
+{
+	unsigned char csum = 0;
+	
+	for (; size > 0; size--, data++)
+		csum += *data;
+
+	return -csum;
+}
+
+static inline void format_ipmb_msg(struct ipmi_smi_msg   *smi_msg,
+				   struct ipmi_msg       *msg,
+				   struct ipmi_ipmb_addr *ipmb_addr,
+				   long                  msgid,
+				   unsigned char         ipmb_seq,
+				   int                   broadcast,
+				   unsigned char         source_address,
+				   unsigned char         source_lun)
+{
+	int i = broadcast;
+
+	/* Format the IPMB header data. */
+	smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
+	smi_msg->data[1] = IPMI_SEND_MSG_CMD;
+	smi_msg->data[2] = ipmb_addr->channel;
+	if (broadcast)
+		smi_msg->data[3] = 0;
+	smi_msg->data[i+3] = ipmb_addr->slave_addr;
+	smi_msg->data[i+4] = (msg->netfn << 2) | (ipmb_addr->lun & 0x3);
+	smi_msg->data[i+5] = ipmb_checksum(&(smi_msg->data[i+3]), 2);
+	smi_msg->data[i+6] = source_address;
+	smi_msg->data[i+7] = (ipmb_seq << 2) | source_lun;
+	smi_msg->data[i+8] = msg->cmd;
+
+	/* Now tack on the data to the message. */
+	if (msg->data_len > 0)
+		memcpy(&(smi_msg->data[i+9]), msg->data,
+		       msg->data_len);
+	smi_msg->data_size = msg->data_len + 9;
+
+	/* Now calculate the checksum and tack it on. */
+	smi_msg->data[i+smi_msg->data_size]
+		= ipmb_checksum(&(smi_msg->data[i+6]),
+				smi_msg->data_size-6);
+
+	/* Add on the checksum size and the offset from the
+	   broadcast. */
+	smi_msg->data_size += 1 + i;
+
+	smi_msg->msgid = msgid;
+}
+
+/* Separate from ipmi_request so that the user does not have to be
+   supplied in certain circumstances (mainly at panic time).  If
+   messages are supplied, they will be freed, even if an error
+   occurs. */
+static inline int i_ipmi_request(ipmi_user_t          user,
+				 ipmi_smi_t           intf,
+				 struct ipmi_addr     *addr,
+				 long                 msgid,
+				 struct ipmi_msg      *msg,
+				 void                 *supplied_smi,
+				 struct ipmi_recv_msg *supplied_recv,
+				 int                  priority,
+				 unsigned char        source_address,
+				 unsigned char        source_lun)
+{
+	int                  rv = 0;
+	struct ipmi_smi_msg  *smi_msg;
+	struct ipmi_recv_msg *recv_msg;
+	unsigned long        flags;
+
+
+	if (supplied_recv) {
+		recv_msg = supplied_recv;
+	} else {
+		recv_msg = ipmi_alloc_recv_msg();
+		if (recv_msg == NULL) {
+			return -ENOMEM;
+		}
+	}
+
+	if (supplied_smi) {
+		smi_msg = (struct ipmi_smi_msg *) supplied_smi;
+	} else {
+		smi_msg = ipmi_alloc_smi_msg();
+		if (smi_msg == NULL) {
+			ipmi_free_recv_msg(recv_msg);
+			return -ENOMEM;
+		}
+	}
+
+	if (addr->channel > IPMI_NUM_CHANNELS) {
+	    rv = -EINVAL;
+	    goto out_err;
+	}
+
+	recv_msg->user = user;
+	recv_msg->msgid = msgid;
+	/* Store the message to send in the receive message so timeout
+	   responses can get the proper response data. */
+	recv_msg->msg = *msg;
+
+	if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
+		struct ipmi_system_interface_addr *smi_addr;
+
+
+		smi_addr = (struct ipmi_system_interface_addr *) addr;
+		if (smi_addr->lun > 3)
+			return -EINVAL;
+
+		memcpy(&recv_msg->addr, smi_addr, sizeof(*smi_addr));
+
+		if ((msg->netfn == IPMI_NETFN_APP_REQUEST)
+		    && ((msg->cmd == IPMI_SEND_MSG_CMD)
+			|| (msg->cmd == IPMI_GET_MSG_CMD)
+			|| (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD)))
+		{
+			/* We don't let the user do these, since we manage
+			   the sequence numbers. */
+			rv = -EINVAL;
+			goto out_err;
+		}
+
+		if ((msg->data_len + 2) > IPMI_MAX_MSG_LENGTH) {
+			rv = -EMSGSIZE;
+			goto out_err;
+		}
+
+		smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3);
+		smi_msg->data[1] = msg->cmd;
+		smi_msg->msgid = msgid;
+		smi_msg->user_data = recv_msg;
+		if (msg->data_len > 0)
+			memcpy(&(smi_msg->data[2]), msg->data, msg->data_len);
+		smi_msg->data_size = msg->data_len + 2;
+	} else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE)
+		   || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
+	{
+		struct ipmi_ipmb_addr *ipmb_addr;
+		unsigned char         ipmb_seq;
+		long                  seqid;
+		int                   broadcast;
+		int                   retries;
+
+		if (addr == NULL) {
+			rv = -EINVAL;
+			goto out_err;
+		}
+
+		if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) {
+		    /* Broadcasts add a zero at the beginning of the
+		       message, but otherwise is the same as an IPMB
+		       address. */
+		    addr->addr_type = IPMI_IPMB_ADDR_TYPE;
+		    broadcast = 1;
+		    retries = 0; /* Don't retry broadcasts. */
+		} else {
+		    broadcast = 0;
+		    retries = 4;
+		}
+
+		/* 9 for the header and 1 for the checksum, plus
+                   possibly one for the broadcast. */
+		if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) {
+			rv = -EMSGSIZE;
+			goto out_err;
+		}
+
+		ipmb_addr = (struct ipmi_ipmb_addr *) addr;
+		if (ipmb_addr->lun > 3) {
+			rv = -EINVAL;
+			goto out_err;
+		}
+
+		memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr));
+
+		if (recv_msg->msg.netfn & 0x1) {
+			/* It's a response, so use the user's sequence
+                           from msgid. */
+			format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid,
+					msgid, broadcast,
+					source_address, source_lun);
+		} else {
+			/* It's a command, so get a sequence for it. */
+
+			spin_lock_irqsave(&(intf->seq_lock), flags);
+
+			/* Create a sequence number with a 1 second
+                           timeout and 4 retries. */
+			/* FIXME - magic number for the timeout. */
+			rv = intf_next_seq(intf,
+					   recv_msg,
+					   1000,
+					   retries,
+					   &ipmb_seq,
+					   &seqid);
+			if (rv) {
+				/* We have used up all the sequence numbers,
+				   probably, so abort. */
+				spin_unlock_irqrestore(&(intf->seq_lock),
+						       flags);
+				ipmi_free_recv_msg(recv_msg);
+				ipmi_free_smi_msg(smi_msg);
+				goto out_err;
+			}
+
+			/* Store the sequence number in the message,
+                           so that when the send message response
+                           comes back we can start the timer. */
+			format_ipmb_msg(smi_msg, msg, ipmb_addr,
+					STORE_SEQ_IN_MSGID(ipmb_seq, seqid),
+					ipmb_seq, broadcast,
+					source_address, source_lun);
+
+			/* Copy the message into the recv message data, so we
+			   can retransmit it later if necessary. */
+			memcpy(recv_msg->msg_data, smi_msg->data,
+			       smi_msg->data_size);
+			recv_msg->msg.data = recv_msg->msg_data;
+			recv_msg->msg.data_len = smi_msg->data_size;
+
+			/* We don't unlock until here, because we need
+                           to copy the completed message into the
+                           recv_msg before we release the lock.
+                           Otherwise, race conditions may bite us.  I
+                           know that's pretty paranoid, but I prefer
+                           to be correct. */
+			spin_unlock_irqrestore(&(intf->seq_lock), flags);
+		}
+	} else {
+	    /* Unknown address type. */
+	    rv = -EINVAL;
+	    goto out_err;
+	}
+
+#if DEBUG_MSGING
+	{
+	    int m;
+	    for (m=0; m<smi_msg->data_size; m++)
+		printk(" %2.2x", smi_msg->data[m]);
+	    printk("\n");
+	}
+#endif
+	intf->handlers->sender(intf->send_info, smi_msg, priority);
+
+	return 0;
+
+ out_err:
+	ipmi_free_smi_msg(smi_msg);
+	ipmi_free_recv_msg(recv_msg);
+	return rv;
+}
+
+int ipmi_request(ipmi_user_t      user,
+		 struct ipmi_addr *addr,
+		 long             msgid,
+		 struct ipmi_msg  *msg,
+		 int              priority)
+{
+	return i_ipmi_request(user,
+			      user->intf,
+			      addr,
+			      msgid,
+			      msg,
+			      NULL, NULL,
+			      priority,
+			      user->intf->my_address,
+			      user->intf->my_lun);
+}
+
+int ipmi_request_supply_msgs(ipmi_user_t          user,
+			     struct ipmi_addr     *addr,
+			     long                 msgid,
+			     struct ipmi_msg      *msg,
+			     void                 *supplied_smi,
+			     struct ipmi_recv_msg *supplied_recv,
+			     int                  priority)
+{
+	return i_ipmi_request(user,
+			      user->intf,
+			      addr,
+			      msgid,
+			      msg,
+			      supplied_smi,
+			      supplied_recv,
+			      priority,
+			      user->intf->my_address,
+			      user->intf->my_lun);
+}
+
+int ipmi_request_with_source(ipmi_user_t      user,
+			     struct ipmi_addr *addr,
+			     long             msgid,
+			     struct ipmi_msg  *msg,
+			     int              priority,
+			     unsigned char    source_address,
+			     unsigned char    source_lun)
+{
+	return i_ipmi_request(user,
+			      user->intf,
+			      addr,
+			      msgid,
+			      msg,
+			      NULL, NULL,
+			      priority,
+			      source_address,
+			      source_lun);
+}
+
+int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
+		      void		       *send_info,
+		      unsigned char            version_major,
+		      unsigned char            version_minor,
+		      ipmi_smi_t               *intf)
+{
+	int              i, j;
+	int              rv;
+	ipmi_smi_t       new_intf;
+	struct list_head *entry;
+	unsigned int     flags;
+
+
+	/* Make sure the driver is actually initialized, this handles
+	   problems with initialization order. */
+	if (!initialized) {
+		rv = ipmi_init_msghandler();
+		if (rv)
+			return rv;
+		/* The init code doesn't return an error if it was turned
+		   off, but it won't initialize.  Check that. */
+		if (!initialized)
+			return -ENODEV;
+	}
+
+	new_intf = kmalloc(sizeof(*new_intf), GFP_KERNEL);
+	if (!new_intf)
+		return -ENOMEM;
+
+	rv = -ENOMEM;
+
+	down_write(&interfaces_sem);
+	for (i=0; i<MAX_IPMI_INTERFACES; i++) {
+		if (ipmi_interfaces[i] == NULL) {
+			new_intf->version_major = version_major;
+			new_intf->version_minor = version_minor;
+			new_intf->my_address = IPMI_BMC_SLAVE_ADDR;
+			new_intf->my_lun = 2;  /* the SMS LUN. */
+			rwlock_init(&(new_intf->users_lock));
+			INIT_LIST_HEAD(&(new_intf->users));
+			new_intf->handlers = handlers;
+			new_intf->send_info = send_info;
+			spin_lock_init(&(new_intf->seq_lock));
+			for (j=0; j<IPMI_IPMB_NUM_SEQ; j++) {
+				new_intf->seq_table[j].inuse = 0;
+				new_intf->seq_table[j].seqid = 0;
+			}
+			new_intf->curr_seq = 0;
+			spin_lock_init(&(new_intf->waiting_msgs_lock));
+			INIT_LIST_HEAD(&(new_intf->waiting_msgs));
+			spin_lock_init(&(new_intf->events_lock));
+			INIT_LIST_HEAD(&(new_intf->waiting_events));
+			new_intf->waiting_events_count = 0;
+			rwlock_init(&(new_intf->cmd_rcvr_lock));
+			INIT_LIST_HEAD(&(new_intf->cmd_rcvrs));
+			new_intf->all_cmd_rcvr = NULL;
+
+			spin_lock_irqsave(&interfaces_lock, flags);
+			ipmi_interfaces[i] = new_intf;
+			spin_unlock_irqrestore(&interfaces_lock, flags);
+
+			rv = 0;
+			*intf = new_intf;
+			break;
+		}
+	}
+
+	/* We convert to a read semaphore here.  It's possible the
+	   interface was removed between the calls, we have to recheck
+	   afterwards. */
+	up_write(&interfaces_sem);
+	down_read(&interfaces_sem);
+
+	if (ipmi_interfaces[i] != new_intf)
+		/* Well, it went away.  Just return. */
+		goto out;
+
+	if (rv == 0) {
+		/* Call all the watcher interfaces to tell them that a
+		   new interface is available. */
+		down_read(&smi_watchers_sem);
+		list_for_each(entry, &smi_watchers) {
+			struct ipmi_smi_watcher *w;
+			w = list_entry(entry, struct ipmi_smi_watcher, link);
+			w->new_smi(i);
+		}
+		up_read(&smi_watchers_sem);
+	}
+
+ out:
+	up_read(&interfaces_sem);
+
+	if (rv)
+		kfree(new_intf);
+
+	return rv;
+}
+
+static void free_recv_msg_list(struct list_head *q)
+{
+	struct list_head     *entry, *entry2;
+	struct ipmi_recv_msg *msg;
+
+	list_for_each_safe(entry, entry2, q) {
+		msg = list_entry(entry, struct ipmi_recv_msg, link);
+		list_del(entry);
+		ipmi_free_recv_msg(msg);
+	}
+}
+
+static void free_cmd_rcvr_list(struct list_head *q)
+{
+	struct list_head *entry, *entry2;
+	struct cmd_rcvr  *rcvr;
+
+	list_for_each_safe(entry, entry2, q) {
+		rcvr = list_entry(entry, struct cmd_rcvr, link);
+		list_del(entry);
+		kfree(rcvr);
+	}
+}
+
+static void clean_up_interface_data(ipmi_smi_t intf)
+{
+	int i;
+
+	free_recv_msg_list(&(intf->waiting_msgs));
+	free_recv_msg_list(&(intf->waiting_events));
+	free_cmd_rcvr_list(&(intf->cmd_rcvrs));
+
+	for (i=0; i<IPMI_IPMB_NUM_SEQ; i++) {
+		if ((intf->seq_table[i].inuse)
+		    && (intf->seq_table[i].recv_msg))
+		{
+			ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
+		}	
+	}
+}
+
+int ipmi_unregister_smi(ipmi_smi_t intf)
+{
+	int              rv = -ENODEV;
+	int              i;
+	struct list_head *entry;
+	unsigned int     flags;
+
+	down_write(&interfaces_sem);
+	if (list_empty(&(intf->users)))
+	{
+		for (i=0; i<MAX_IPMI_INTERFACES; i++) {
+			if (ipmi_interfaces[i] == intf) {
+				spin_lock_irqsave(&interfaces_lock, flags);
+				ipmi_interfaces[i] = NULL;
+				clean_up_interface_data(intf);
+				spin_unlock_irqrestore(&interfaces_lock,flags);
+				kfree(intf);
+				rv = 0;
+				goto out_call_watcher;
+			}
+		}
+	} else {
+		rv = -EBUSY;
+	}
+	up_write(&interfaces_sem);
+
+	return rv;
+
+ out_call_watcher:
+	/* Convert to a read semaphore so callbacks don't bite us. */
+	up_write(&interfaces_sem);
+	down_read(&interfaces_sem);
+
+	/* Call all the watcher interfaces to tell them that
+	   an interface is gone. */
+	down_read(&smi_watchers_sem);
+	list_for_each(entry, &smi_watchers) {
+		struct ipmi_smi_watcher *w;
+		w = list_entry(entry,
+			       struct ipmi_smi_watcher,
+			       link);
+		w->smi_gone(i);
+	}
+	up_read(&smi_watchers_sem);
+	up_read(&interfaces_sem);
+	return 0;
+}
+
+static int handle_get_msg_rsp(ipmi_smi_t          intf,
+			      struct ipmi_smi_msg *msg)
+{
+	struct ipmi_ipmb_addr ipmb_addr;
+	struct ipmi_recv_msg  *recv_msg;
+
+	
+	if (msg->rsp_size < 11)
+		/* Message not big enough, just ignore it. */
+		return 0;
+
+	if (msg->rsp[2] != 0)
+		/* An error getting the response, just ignore it. */
+		return 0;
+
+	ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE;
+	ipmb_addr.slave_addr = msg->rsp[6];
+	ipmb_addr.channel = msg->rsp[3] & 0x0f;
+	ipmb_addr.lun = msg->rsp[7] & 3;
+
+	/* It's a response from a remote entity.  Look up the sequence
+	   number and handle the response. */
+	if (intf_find_seq(intf,
+			  msg->rsp[7] >> 2,
+			  msg->rsp[3] & 0x0f,
+			  msg->rsp[8],
+			  (msg->rsp[4] >> 2) & (~1),
+			  (struct ipmi_addr *) &(ipmb_addr),
+			  &recv_msg))
+	{
+		/* We were unable to find the sequence number,
+		   so just nuke the message. */
+		return 0;
+	}
+
+	memcpy(recv_msg->msg_data,
+	       &(msg->rsp[9]),
+	       msg->rsp_size - 9);
+	/* THe other fields matched, so no need to set them, except
+           for netfn, which needs to be the response that was
+           returned, not the request value. */
+	recv_msg->msg.netfn = msg->rsp[4] >> 2;
+	recv_msg->msg.data = recv_msg->msg_data;
+	recv_msg->msg.data_len = msg->rsp_size - 10;
+	recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
+	deliver_response(recv_msg);
+
+	return 0;
+}
+
+static int handle_get_msg_cmd(ipmi_smi_t          intf,
+			      struct ipmi_smi_msg *msg)
+{
+	struct list_head *entry;
+	struct cmd_rcvr       *rcvr;
+	int              rv = 0;
+	unsigned char    netfn;
+	unsigned char    cmd;
+	ipmi_user_t      user = NULL;
+	struct ipmi_ipmb_addr *ipmb_addr;
+	struct ipmi_recv_msg  *recv_msg;
+
+	if (msg->rsp_size < 10)
+		/* Message not big enough, just ignore it. */
+		return 0;
+
+	if (msg->rsp[2] != 0) {
+		/* An error getting the response, just ignore it. */
+		return 0;
+	}
+
+	netfn = msg->rsp[4] >> 2;
+	cmd = msg->rsp[8];
+
+	read_lock(&(intf->cmd_rcvr_lock));
+	
+	if (intf->all_cmd_rcvr) {
+		user = intf->all_cmd_rcvr;
+	} else {
+		/* Find the command/netfn. */
+		list_for_each(entry, &(intf->cmd_rcvrs)) {
+			rcvr = list_entry(entry, struct cmd_rcvr, link);
+			if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)) {
+				user = rcvr->user;
+				break;
+			}
+		}
+	}
+	read_unlock(&(intf->cmd_rcvr_lock));
+
+	if (user == NULL) {
+		/* We didn't find a user, deliver an error response. */
+		msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
+		msg->data[1] = IPMI_SEND_MSG_CMD;
+		msg->data[2] = msg->rsp[3];
+		msg->data[3] = msg->rsp[6];
+                msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3);
+		msg->data[5] = ipmb_checksum(&(msg->data[3]), 2);
+		msg->data[6] = intf->my_address;
+                /* rqseq/lun */
+                msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3);
+		msg->data[8] = msg->rsp[8]; /* cmd */
+		msg->data[9] = IPMI_INVALID_CMD_COMPLETION_CODE;
+		msg->data[10] = ipmb_checksum(&(msg->data[6]), 4);
+		msg->data_size = 11;
+
+		intf->handlers->sender(intf->send_info, msg, 0);
+
+		rv = -1; /* We used the message, so return the value that
+			    causes it to not be freed or queued. */
+	} else {
+		/* Deliver the message to the user. */
+		recv_msg = ipmi_alloc_recv_msg();
+		if (! recv_msg) {
+			/* We couldn't allocate memory for the
+                           message, so requeue it for handling
+                           later. */
+			rv = 1;
+		} else {
+			ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr;
+			ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
+			ipmb_addr->slave_addr = msg->rsp[6];
+			ipmb_addr->lun = msg->rsp[7] & 3;
+			ipmb_addr->channel = msg->rsp[3];
+
+			recv_msg->user = user;
+			recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
+			recv_msg->msgid = msg->rsp[7] >> 2;
+			recv_msg->msg.netfn = msg->rsp[4] >> 2;
+			recv_msg->msg.cmd = msg->rsp[8];
+			recv_msg->msg.data = recv_msg->msg_data;
+			recv_msg->msg.data_len = msg->rsp_size - 10;
+			memcpy(recv_msg->msg_data,
+			       &(msg->rsp[9]),
+			       msg->rsp_size - 10);
+			deliver_response(recv_msg);
+		}
+	}
+
+	return rv;
+}
+
+static void copy_event_into_recv_msg(struct ipmi_recv_msg *recv_msg,
+				     struct ipmi_smi_msg  *msg)
+{
+	struct ipmi_system_interface_addr *smi_addr;
+	
+	recv_msg->msgid = 0;
+	smi_addr = (struct ipmi_system_interface_addr *) &(recv_msg->addr);
+	smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	smi_addr->channel = IPMI_BMC_CHANNEL;
+	smi_addr->lun = msg->rsp[0] & 3;
+	recv_msg->recv_type = IPMI_ASYNC_EVENT_RECV_TYPE;
+	recv_msg->msg.netfn = msg->rsp[0] >> 2;
+	recv_msg->msg.cmd = msg->rsp[1];
+	memcpy(recv_msg->msg_data, &(msg->rsp[3]), msg->rsp_size - 3);
+	recv_msg->msg.data = recv_msg->msg_data;
+	recv_msg->msg.data_len = msg->rsp_size - 3;
+}
+
+/* This will be called with the intf->users_lock read-locked, so no need
+   to do that here. */
+static int handle_read_event_rsp(ipmi_smi_t          intf,
+				 struct ipmi_smi_msg *msg)
+{
+	struct ipmi_recv_msg *recv_msg;
+	struct list_head     msgs;
+	struct list_head     *entry, *entry2;
+	ipmi_user_t          user;
+	int                  rv = 0;
+	int                  deliver_count = 0;
+	unsigned long        flags;
+
+	if (msg->rsp_size < 19) {
+		/* Message is too small to be an IPMB event. */
+		return 0;
+	}
+
+	if (msg->rsp[2] != 0) {
+		/* An error getting the event, just ignore it. */
+		return 0;
+	}
+
+	INIT_LIST_HEAD(&msgs);
+
+	spin_lock_irqsave(&(intf->events_lock), flags);
+
+	/* Allocate and fill in one message for every user that is getting
+	   events. */
+	list_for_each(entry, &(intf->users)) {
+		user = list_entry(entry, struct ipmi_user, link);
+
+		if (! user->gets_events)
+			continue;
+
+		recv_msg = ipmi_alloc_recv_msg();
+		if (! recv_msg) {
+			list_for_each_safe(entry, entry2, &msgs) {
+				recv_msg = list_entry(entry,
+						      struct ipmi_recv_msg,
+						      link);
+				list_del(entry);
+				ipmi_free_recv_msg(recv_msg);
+			}
+			/* We couldn't allocate memory for the
+                           message, so requeue it for handling
+                           later. */
+			rv = 1;
+			goto out;
+		}
+
+		deliver_count++;
+
+		copy_event_into_recv_msg(recv_msg, msg);
+		recv_msg->user = user;
+		list_add_tail(&(recv_msg->link), &msgs);
+	}
+
+	if (deliver_count) {
+		/* Now deliver all the messages. */
+		list_for_each_safe(entry, entry2, &msgs) {
+			recv_msg = list_entry(entry,
+					      struct ipmi_recv_msg,
+					      link);
+			list_del(entry);
+			deliver_response(recv_msg);
+		}
+	} else if (intf->waiting_events_count < MAX_EVENTS_IN_QUEUE) {
+		/* No one to receive the message, put it in queue if there's
+		   not already too many things in the queue. */
+		recv_msg = ipmi_alloc_recv_msg();
+		if (! recv_msg) {
+			/* We couldn't allocate memory for the
+                           message, so requeue it for handling
+                           later. */
+			rv = 1;
+			goto out;
+		}
+
+		copy_event_into_recv_msg(recv_msg, msg);
+		list_add_tail(&(recv_msg->link), &(intf->waiting_events));
+	} else {
+		/* There's too many things in the queue, discard this
+		   message. */
+		printk(KERN_WARNING "ipmi: Event queue full, discarding an"
+		       " incoming event\n");
+	}
+
+ out:
+	spin_unlock_irqrestore(&(intf->events_lock), flags);
+
+	return rv;
+}
+
+static int handle_bmc_rsp(ipmi_smi_t          intf,
+			  struct ipmi_smi_msg *msg)
+{
+	struct ipmi_recv_msg *recv_msg;
+	int                  found = 0;
+	struct list_head     *entry;
+
+	recv_msg = (struct ipmi_recv_msg *) msg->user_data;
+
+	/* Make sure the user still exists. */
+	list_for_each(entry, &(intf->users)) {
+		if (list_entry(entry, struct ipmi_user, link)
+		    == recv_msg->user)
+		{
+			/* Found it, so we can deliver it */
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		/* The user for the message went away, so give up. */
+		ipmi_free_recv_msg(recv_msg);
+	} else {
+		struct ipmi_system_interface_addr *smi_addr;
+
+		recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
+		recv_msg->msgid = msg->msgid;
+		smi_addr = ((struct ipmi_system_interface_addr *)
+			    &(recv_msg->addr));
+		smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+		smi_addr->channel = IPMI_BMC_CHANNEL;
+		smi_addr->lun = msg->rsp[0] & 3;
+		recv_msg->msg.netfn = msg->rsp[0] >> 2;
+		recv_msg->msg.cmd = msg->rsp[1];
+		memcpy(recv_msg->msg_data,
+		       &(msg->rsp[2]),
+		       msg->rsp_size - 2);
+		recv_msg->msg.data = recv_msg->msg_data;
+		recv_msg->msg.data_len = msg->rsp_size - 2;
+		deliver_response(recv_msg);
+	}
+
+	return 0;
+}
+
+/* Handle a new message.  Return 1 if the message should be requeued,
+   0 if the message should be freed, or -1 if the message should not
+   be freed or requeued. */
+static int handle_new_recv_msg(ipmi_smi_t          intf,
+			       struct ipmi_smi_msg *msg)
+{
+	int requeue;
+
+	if (msg->rsp_size < 2) {
+		/* Message is too small to be correct. */
+		requeue = 0;
+	} else if (msg->rsp[1] == IPMI_GET_MSG_CMD) {
+#if DEBUG_MSGING
+		int m;
+		printk("Response:");
+		for (m=0; m<msg->rsp_size; m++)
+			printk(" %2.2x", msg->rsp[m]);
+		printk("\n");
+#endif
+		/* It's from the receive queue. */
+		if (msg->rsp[4] & 0x04) {
+			/* It's a response, so find the
+			   requesting message and send it up. */
+			requeue = handle_get_msg_rsp(intf, msg);
+		} else {
+			/* It's a command to the SMS from some other
+			   entity.  Handle that. */
+			requeue = handle_get_msg_cmd(intf, msg);
+		}
+	} else if (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD) {
+		/* It's an asyncronous event. */
+		requeue = handle_read_event_rsp(intf, msg);
+	} else {
+		/* It's a response from the local BMC. */
+		requeue = handle_bmc_rsp(intf, msg);
+	}
+
+	return requeue;
+}
+
+/* Handle a new message from the lower layer. */
+void ipmi_smi_msg_received(ipmi_smi_t          intf,
+			   struct ipmi_smi_msg *msg)
+{
+	unsigned long flags;
+	int           rv;
+
+
+	/* Lock the user lock so the user can't go away while we are
+	   working on it. */
+	read_lock(&(intf->users_lock));
+
+	if ((msg->data_size >= 2) && (msg->data[1] == IPMI_SEND_MSG_CMD)) {
+		/* This is the local response to a send, start the
+                   timer for these. */
+		intf_start_seq_timer(intf, msg->msgid);
+		ipmi_free_smi_msg(msg);
+		goto out_unlock;
+	}
+
+	/* To preserve message order, if the list is not empty, we
+           tack this message onto the end of the list. */
+	spin_lock_irqsave(&(intf->waiting_msgs_lock), flags);
+	if (!list_empty(&(intf->waiting_msgs))) {
+		list_add_tail(&(msg->link), &(intf->waiting_msgs));
+		spin_unlock(&(intf->waiting_msgs_lock));
+		goto out_unlock;
+	}
+	spin_unlock_irqrestore(&(intf->waiting_msgs_lock), flags);
+		
+	rv = handle_new_recv_msg(intf, msg);
+	if (rv > 0) {
+		/* Could not handle the message now, just add it to a
+                   list to handle later. */
+		spin_lock(&(intf->waiting_msgs_lock));
+		list_add_tail(&(msg->link), &(intf->waiting_msgs));
+		spin_unlock(&(intf->waiting_msgs_lock));
+	} else if (rv == 0) {
+		ipmi_free_smi_msg(msg);
+	}
+
+ out_unlock:
+	read_unlock(&(intf->users_lock));
+}
+
+void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf)
+{
+	struct list_head *entry;
+	ipmi_user_t      user;
+
+	read_lock(&(intf->users_lock));
+	list_for_each(entry, &(intf->users)) {
+		user = list_entry(entry, struct ipmi_user, link);
+
+		if (! user->handler->ipmi_watchdog_pretimeout)
+			continue;
+
+		user->handler->ipmi_watchdog_pretimeout(user->handler_data);
+	}
+	read_unlock(&(intf->users_lock));
+}
+
+static void
+handle_msg_timeout(struct ipmi_recv_msg *msg)
+{
+	msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
+	msg->msg_data[0] = IPMI_TIMEOUT_COMPLETION_CODE;
+	msg->msg.netfn |= 1; /* Convert to a response. */
+	msg->msg.data_len = 1;
+	msg->msg.data = msg->msg_data;
+	deliver_response(msg);
+}
+
+static void
+send_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg,
+		   struct ipmi_smi_msg *smi_msg,
+		   unsigned char seq, long seqid)
+{
+	if (!smi_msg)
+		smi_msg = ipmi_alloc_smi_msg();
+	if (!smi_msg)
+		/* If we can't allocate the message, then just return, we
+		   get 4 retries, so this should be ok. */
+		return;
+
+	memcpy(smi_msg->data, recv_msg->msg.data, recv_msg->msg.data_len);
+	smi_msg->data_size = recv_msg->msg.data_len;
+	smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid);
+		
+	/* Send the new message.  We send with a zero priority.  It
+	   timed out, I doubt time is that critical now, and high
+	   priority messages are really only for messages to the local
+	   MC, which don't get resent. */
+	intf->handlers->sender(intf->send_info, smi_msg, 0);
+
+#if DEBUG_MSGING
+	{
+		int m;
+		printk("Resend: ");
+		for (m=0; m<smi_msg->data_size; m++)
+			printk(" %2.2x", smi_msg->data[m]);
+		printk("\n");
+	}
+#endif
+}
+
+static void
+ipmi_timeout_handler(long timeout_period)
+{
+	ipmi_smi_t           intf;
+	struct list_head     timeouts;
+	struct ipmi_recv_msg *msg;
+	struct ipmi_smi_msg  *smi_msg;
+	unsigned long        flags;
+	struct list_head     *entry, *entry2;
+	int                  i, j;
+
+	INIT_LIST_HEAD(&timeouts);
+
+	spin_lock(&interfaces_lock);
+	for (i=0; i<MAX_IPMI_INTERFACES; i++) {
+		intf = ipmi_interfaces[i];
+		if (intf == NULL)
+			continue;
+
+		read_lock(&(intf->users_lock));
+
+		/* See if any waiting messages need to be processed. */
+		spin_lock_irqsave(&(intf->waiting_msgs_lock), flags);
+		list_for_each_safe(entry, entry2, &(intf->waiting_msgs)) {
+			smi_msg = list_entry(entry, struct ipmi_smi_msg, link);
+			if (! handle_new_recv_msg(intf, smi_msg)) {
+				list_del(entry);
+				ipmi_free_smi_msg(smi_msg);
+			} else {
+				/* To preserve message order, quit if we
+				   can't handle a message. */
+				break;
+			}
+		}
+		spin_unlock_irqrestore(&(intf->waiting_msgs_lock), flags);
+
+		/* Go through the seq table and find any messages that
+		   have timed out, putting them in the timeouts
+		   list. */
+		spin_lock_irqsave(&(intf->seq_lock), flags);
+		for (j=0; j<IPMI_IPMB_NUM_SEQ; j++) {
+			struct seq_table *ent = &(intf->seq_table[j]);
+			if (!ent->inuse)
+				continue;
+
+			ent->timeout -= timeout_period;
+			if (ent->timeout > 0)
+				continue;
+
+			if (ent->retries_left == 0) {
+				/* The message has used all its retries. */
+				ent->inuse = 0;
+				msg = ent->recv_msg;
+				list_add_tail(&(msg->link), &timeouts);
+			} else {
+				/* More retries, send again. */
+
+				/* Start with the max timer, set to normal
+				   timer after the message is sent. */
+				ent->timeout = MAX_MSG_TIMEOUT;
+				ent->retries_left--;
+				send_from_recv_msg(intf, ent->recv_msg, NULL,
+						   j, ent->seqid);
+			}
+		}
+		spin_unlock_irqrestore(&(intf->seq_lock), flags);
+
+		list_for_each_safe(entry, entry2, &timeouts) {
+			msg = list_entry(entry, struct ipmi_recv_msg, link);
+			handle_msg_timeout(msg);
+		}
+
+		read_unlock(&(intf->users_lock));
+	}
+	spin_unlock(&interfaces_lock);
+}
+
+static void ipmi_request_event(void)
+{
+	ipmi_smi_t intf;
+	int        i;
+
+	spin_lock(&interfaces_lock);
+	for (i=0; i<MAX_IPMI_INTERFACES; i++) {
+		intf = ipmi_interfaces[i];
+		if (intf == NULL)
+			continue;
+
+		intf->handlers->request_events(intf->send_info);
+	}
+	spin_unlock(&interfaces_lock);
+}
+
+static struct timer_list ipmi_timer;
+
+/* Call every 100 ms. */
+#define IPMI_TIMEOUT_TIME	100
+#define IPMI_TIMEOUT_JIFFIES	(IPMI_TIMEOUT_TIME/(1000/HZ))
+
+/* Request events from the queue every second.  Hopefully, in the
+   future, IPMI will add a way to know immediately if an event is
+   in the queue. */
+#define IPMI_REQUEST_EV_TIME	(1000 / (IPMI_TIMEOUT_TIME))
+
+static volatile int stop_operation = 0;
+static volatile int timer_stopped = 0;
+static unsigned int ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
+
+static void ipmi_timeout(unsigned long data)
+{
+	if (stop_operation) {
+		timer_stopped = 1;
+		return;
+	}
+
+	ticks_to_req_ev--;
+	if (ticks_to_req_ev == 0) {
+		ipmi_request_event();
+		ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
+	}
+
+	ipmi_timeout_handler(IPMI_TIMEOUT_TIME);
+
+	ipmi_timer.expires += IPMI_TIMEOUT_JIFFIES;
+	add_timer(&ipmi_timer);
+}
+
+
+/* FIXME - convert these to slabs. */
+static void free_smi_msg(struct ipmi_smi_msg *msg)
+{
+	kfree(msg);
+}
+
+struct ipmi_smi_msg *ipmi_alloc_smi_msg(void)
+{
+	struct ipmi_smi_msg *rv;
+	rv = kmalloc(sizeof(struct ipmi_smi_msg), GFP_ATOMIC);
+	if (rv)
+		rv->done = free_smi_msg;
+	return rv;
+}
+
+static void free_recv_msg(struct ipmi_recv_msg *msg)
+{
+	kfree(msg);
+}
+
+struct ipmi_recv_msg *ipmi_alloc_recv_msg(void)
+{
+	struct ipmi_recv_msg *rv;
+
+	rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC);
+	if (rv)
+		rv->done = free_recv_msg;
+	return rv;
+}
+
+#ifdef CONFIG_IPMI_PANIC_EVENT
+
+static void dummy_smi_done_handler(struct ipmi_smi_msg *msg)
+{
+}
+
+static void dummy_recv_done_handler(struct ipmi_recv_msg *msg)
+{
+}
+
+static void send_panic_events(void)
+{
+	struct ipmi_msg                   msg;
+	ipmi_smi_t                        intf;
+	unsigned char                     data[8];
+	int                               i;
+	struct ipmi_system_interface_addr addr;
+	struct ipmi_smi_msg               smi_msg;
+	struct ipmi_recv_msg              recv_msg;
+
+	addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	addr.channel = IPMI_BMC_CHANNEL;
+
+	/* Fill in an event telling that we have failed. */
+	msg.netfn = 0x04; /* Sensor or Event. */
+	msg.cmd = 2; /* Platform event command. */
+	msg.data = data;
+	msg.data_len = 8;
+	data[0] = 0x21; /* Kernel generator ID, IPMI table 5-4 */
+	data[1] = 0x03; /* This is for IPMI 1.0. */
+	data[2] = 0x20; /* OS Critical Stop, IPMI table 36-3 */
+	data[4] = 0x6f; /* Sensor specific, IPMI table 36-1 */
+	data[5] = 0xa1; /* Runtime stop OEM bytes 2 & 3. */
+
+	/* These used to have the first three bytes of the panic string,
+	   but not only is that not terribly useful, it's not available
+	   any more. */
+	data[3] = 0;
+	data[6] = 0;
+	data[7] = 0;
+
+	smi_msg.done = dummy_smi_done_handler;
+	recv_msg.done = dummy_recv_done_handler;
+
+	/* For every registered interface, send the event. */
+	for (i=0; i<MAX_IPMI_INTERFACES; i++) {
+		intf = ipmi_interfaces[i];
+		if (intf == NULL)
+			continue;
+
+		intf->handlers->set_run_to_completion(intf->send_info, 1);
+		i_ipmi_request(NULL,
+			       intf,
+			       (struct ipmi_addr *) &addr,
+			       0,
+			       &msg,
+			       &smi_msg,
+			       &recv_msg,
+			       0,
+			       intf->my_address,
+			       intf->my_lun);
+	}
+}
+#endif /* CONFIG_IPMI_PANIC_EVENT */
+
+static int has_paniced = 0;
+
+static int panic_event(struct notifier_block *this,
+		       unsigned long         event,
+                       void                  *ptr)
+{
+	int        i;
+	ipmi_smi_t intf;
+
+	if (has_paniced)
+		return NOTIFY_DONE;
+	has_paniced = 1;
+
+	/* For every registered interface, set it to run to completion. */
+	for (i=0; i<MAX_IPMI_INTERFACES; i++) {
+		intf = ipmi_interfaces[i];
+		if (intf == NULL)
+			continue;
+
+		intf->handlers->set_run_to_completion(intf->send_info, 1);
+	}
+
+#ifdef CONFIG_IPMI_PANIC_EVENT
+	send_panic_events();
+#endif
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_block = {
+	panic_event,
+	NULL,
+	200   /* priority: INT_MAX >= x >= 0 */
+};
+
+
+static __init int ipmi_init_msghandler(void)
+{
+	int i;
+
+	if (initialized)
+		return 0;
+
+	for (i=0; i<MAX_IPMI_INTERFACES; i++) {
+		ipmi_interfaces[i] = NULL;
+	}
+
+	init_timer(&ipmi_timer);
+	ipmi_timer.data = 0;
+	ipmi_timer.function = ipmi_timeout;
+	ipmi_timer.expires = jiffies + IPMI_TIMEOUT_JIFFIES;
+	add_timer(&ipmi_timer);
+
+	notifier_chain_register(&panic_notifier_list, &panic_block);
+
+	initialized = 1;
+
+	printk(KERN_INFO "ipmi: message handler initialized\n");
+
+	return 0;
+}
+
+static __exit void cleanup_ipmi(void)
+{
+	if (!initialized)
+		return;
+
+	notifier_chain_unregister(&panic_notifier_list, &panic_block);
+
+	/* This can't be called if any interfaces exist, so no worry about
+	   shutting down the interfaces. */
+
+	/* Tell the timer to stop, then wait for it to stop.  This avoids
+	   problems with race conditions removing the timer here. */
+	stop_operation = 1;
+	while (!timer_stopped) {
+		schedule_timeout(1);
+	}
+
+	initialized = 0;
+}
+module_exit(cleanup_ipmi);
+
+module_init(ipmi_init_msghandler);
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ipmi_alloc_recv_msg);
+EXPORT_SYMBOL(ipmi_create_user);
+EXPORT_SYMBOL(ipmi_destroy_user);
+EXPORT_SYMBOL(ipmi_get_version);
+EXPORT_SYMBOL(ipmi_request);
+EXPORT_SYMBOL(ipmi_request_supply_msgs);
+EXPORT_SYMBOL(ipmi_request_with_source);
+EXPORT_SYMBOL(ipmi_register_smi);
+EXPORT_SYMBOL(ipmi_unregister_smi);
+EXPORT_SYMBOL(ipmi_register_for_cmd);
+EXPORT_SYMBOL(ipmi_unregister_for_cmd);
+EXPORT_SYMBOL(ipmi_smi_msg_received);
+EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout);
+EXPORT_SYMBOL(ipmi_alloc_smi_msg);
+EXPORT_SYMBOL(ipmi_register_all_cmd_rcvr);
+EXPORT_SYMBOL(ipmi_unregister_all_cmd_rcvr);
+EXPORT_SYMBOL(ipmi_addr_length);
+EXPORT_SYMBOL(ipmi_validate_addr);
+EXPORT_SYMBOL(ipmi_set_gets_events);
+EXPORT_SYMBOL(ipmi_addr_equal);
+EXPORT_SYMBOL(ipmi_smi_watcher_register);
+EXPORT_SYMBOL(ipmi_smi_watcher_unregister);
+EXPORT_SYMBOL(ipmi_set_my_address);
+EXPORT_SYMBOL(ipmi_get_my_address);
+EXPORT_SYMBOL(ipmi_set_my_LUN);
+EXPORT_SYMBOL(ipmi_get_my_LUN);
diff -urN linux.orig/drivers/char/ipmi/ipmi_watchdog.c linux/drivers/char/ipmi/ipmi_watchdog.c
--- linux.orig/drivers/char/ipmi/ipmi_watchdog.c	Wed Dec 31 18:00:00 1969
+++ linux/drivers/char/ipmi/ipmi_watchdog.c	Thu Jan  2 17:14:49 2003
@@ -0,0 +1,1113 @@
+/*
+ * ipmi_watchdog.c
+ *
+ * A watchdog timer based upon the IPMI interface.
+ *
+ * Author: MontaVista Software, Inc.
+ *         Corey Minyard <minyard@mvista.com>
+ *         source@mvista.com
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ *  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.
+ *
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/ipmi.h>
+#include <linux/ipmi_smi.h>
+#include <linux/watchdog.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/rwsem.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <linux/notifier.h>
+#include <linux/nmi.h>
+#include <linux/reboot.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#ifdef CONFIG_X86_LOCAL_APIC
+#include <asm/apic.h>
+#endif
+
+/*
+ * The IPMI command/response information for the watchdog timer.
+ */
+
+/* values for byte 1 of the set command, byte 2 of the get response. */
+#define WDOG_DONT_LOG		(1 << 7)
+#define WDOG_DONT_STOP_ON_SET	(1 << 6)
+#define WDOG_SET_TIMER_USE(byte, use) \
+	byte = ((byte) & 0xf8) | ((use) & 0x7)
+#define WDOG_GET_TIMER_USE(byte) ((byte) & 0x7)
+#define WDOG_TIMER_USE_BIOS_FRB2	1
+#define WDOG_TIMER_USE_BIOS_POST	2
+#define WDOG_TIMER_USE_OS_LOAD		3
+#define WDOG_TIMER_USE_SMS_OS		4
+#define WDOG_TIMER_USE_OEM		5
+
+/* values for byte 2 of the set command, byte 3 of the get response. */
+#define WDOG_SET_PRETIMEOUT_ACT(byte, use) \
+	byte = ((byte) & 0x8f) | (((use) & 0x7) << 4)
+#define WDOG_GET_PRETIMEOUT_ACT(byte) (((byte) >> 4) & 0x7)
+#define WDOG_PRETIMEOUT_NONE		0
+#define WDOG_PRETIMEOUT_SMI		1
+#define WDOG_PRETIMEOUT_NMI		2
+#define WDOG_PRETIMEOUT_MSG_INT		3
+
+/* Operations that can be performed on a pretimout. */
+#define WDOG_PREOP_NONE		0
+#define WDOG_PREOP_PANIC	1
+#define WDOG_PREOP_GIVE_DATA	2 /* Cause data to be available to
+                                     read.  Doesn't work in NMI
+                                     mode. */
+
+/* Actions to perform on a full timeout. */
+#define WDOG_SET_TIMEOUT_ACT(byte, use) \
+	byte = ((byte) & 0xf8) | ((use) & 0x7)
+#define WDOG_GET_TIMEOUT_ACT(byte) ((byte) & 0x7)
+#define WDOG_TIMEOUT_NONE		0
+#define WDOG_TIMEOUT_RESET		1
+#define WDOG_TIMEOUT_POWER_DOWN		2
+#define WDOG_TIMEOUT_POWER_CYCLE	3
+
+/* Byte 3 of the get command, byte 4 of the get response is the
+   pre-timeout in seconds. */
+
+/* Bits for setting byte 4 of the set command, byte 5 of the get response. */
+#define WDOG_EXPIRE_CLEAR_BIOS_FRB2	(1 << 1)
+#define WDOG_EXPIRE_CLEAR_BIOS_POST	(1 << 2)
+#define WDOG_EXPIRE_CLEAR_OS_LOAD	(1 << 3)
+#define WDOG_EXPIRE_CLEAR_SMS_OS	(1 << 4)
+#define WDOG_EXPIRE_CLEAR_OEM		(1 << 5)
+
+/* Setting/getting the watchdog timer value.  This is for bytes 5 and
+   6 (the timeout time) of the set command, and bytes 6 and 7 (the
+   timeout time) and 8 and 9 (the current countdown value) of the
+   response.  The timeout value is given in seconds (in the command it
+   is 100ms intervals). */
+#define WDOG_SET_TIMEOUT(byte1, byte2, val) \
+	(byte1) = (((val) * 10) & 0xff), (byte2) = (((val) * 10) >> 8)
+#define WDOG_GET_TIMEOUT(byte1, byte2) \
+	(((byte1) | ((byte2) << 8)) / 10)
+
+#define IPMI_WDOG_RESET_TIMER		0x22
+#define IPMI_WDOG_SET_TIMER		0x24
+#define IPMI_WDOG_GET_TIMER		0x25
+
+/* These are here until the real ones get into the watchdog.h interface. */
+#ifndef WDIOC_GETTIMEOUT
+#define	WDIOC_GETTIMEOUT        _IOW(WATCHDOG_IOCTL_BASE, 20, int)
+#endif
+#ifndef WDIOC_SET_PRETIMEOUT
+#define	WDIOC_SET_PRETIMEOUT     _IOW(WATCHDOG_IOCTL_BASE, 21, int)
+#endif
+#ifndef WDIOC_GET_PRETIMEOUT
+#define	WDIOC_GET_PRETIMEOUT     _IOW(WATCHDOG_IOCTL_BASE, 22, int)
+#endif
+
+static ipmi_user_t watchdog_user = NULL;
+
+/* Default the timeout to 10 seconds. */
+static int timeout = 10;
+
+/* The pre-timeout is disabled by default. */
+static int pretimeout = 0;
+
+/* Default action is to reset the board on a timeout. */
+static unsigned char action_val = WDOG_TIMEOUT_RESET;
+
+static char *action = "reset";
+
+static unsigned char preaction_val = WDOG_PRETIMEOUT_NONE;
+
+static char *preaction = "pre_none";
+
+static unsigned char preop_val = WDOG_PREOP_NONE;
+
+static char *preop = "preop_none";
+static spinlock_t ipmi_read_lock = SPIN_LOCK_UNLOCKED;
+static char data_to_read = 0;
+static DECLARE_WAIT_QUEUE_HEAD(read_q);
+static struct fasync_struct *fasync_q = NULL;
+static char pretimeout_since_last_heartbeat = 0;
+
+MODULE_PARM(timeout, "i");
+MODULE_PARM(pretimeout, "i");
+MODULE_PARM(action, "s");
+MODULE_PARM(preaction, "s");
+MODULE_PARM(preop, "s");
+
+/* Default state of the timer. */
+static unsigned char ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
+
+/* If shutting down via IPMI, we ignore the heartbeat. */
+static int ipmi_ignore_heartbeat = 0;
+
+/* Is someone using the watchdog?  Only one user is allowed. */
+static int ipmi_wdog_open = 0;
+
+/* If true, the driver will start running as soon as it is configured
+   and ready. */
+static int start_now = 0;
+
+/* If set to 1, the heartbeat command will set the state to reset and
+   start the timer.  The timer doesn't normally run when the driver is
+   first opened until the heartbeat is set the first time, this
+   variable is used to accomplish this. */
+static int ipmi_start_timer_on_heartbeat = 0;
+
+/* IPMI version of the BMC. */
+static unsigned char ipmi_version_major;
+static unsigned char ipmi_version_minor;
+
+
+static int ipmi_heartbeat(void);
+static void panic_halt_ipmi_heartbeat(void);
+
+
+/* We use a semaphore to make sure that only one thing can send a set
+   timeout at one time, because we only have one copy of the data.
+   The semaphore is claimed when the set_timeout is sent and freed
+   when both messages are free. */
+static atomic_t set_timeout_tofree = ATOMIC_INIT(0);
+static DECLARE_MUTEX(set_timeout_lock);
+static void set_timeout_free_smi(struct ipmi_smi_msg *msg)
+{
+    if (atomic_dec_and_test(&set_timeout_tofree))
+	    up(&set_timeout_lock);
+}
+static void set_timeout_free_recv(struct ipmi_recv_msg *msg)
+{
+    if (atomic_dec_and_test(&set_timeout_tofree))
+	    up(&set_timeout_lock);
+}
+static struct ipmi_smi_msg set_timeout_smi_msg =
+{
+	.done = set_timeout_free_smi
+};
+static struct ipmi_recv_msg set_timeout_recv_msg =
+{
+	.done = set_timeout_free_recv
+};
+ 
+static int i_ipmi_set_timeout(struct ipmi_smi_msg  *smi_msg,
+			      struct ipmi_recv_msg *recv_msg,
+			      int                  *send_heartbeat_now)
+{
+	struct ipmi_msg    msg;
+	unsigned char      data[6];
+	int                rv;
+	struct ipmi_system_interface_addr addr;
+
+
+	*send_heartbeat_now = 0;
+	data[0] = 0;
+	WDOG_SET_TIMER_USE(data[0], WDOG_TIMER_USE_SMS_OS);
+
+	if ((ipmi_version_major > 1)
+	    || ((ipmi_version_major == 1) && (ipmi_version_minor >= 5)))
+	{
+		/* This is an IPMI 1.5-only feature. */
+		data[0] |= WDOG_DONT_STOP_ON_SET;
+	} else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) {
+		/* In ipmi 1.0, setting the timer stops the watchdog, we
+		   need to start it back up again. */
+		*send_heartbeat_now = 1;
+	}
+
+	data[1] = 0;
+	WDOG_SET_TIMEOUT_ACT(data[1], ipmi_watchdog_state);
+	if (pretimeout > 0) {
+	    WDOG_SET_PRETIMEOUT_ACT(data[1], preaction_val);
+	    data[2] = pretimeout;
+	} else {
+	    WDOG_SET_PRETIMEOUT_ACT(data[1], WDOG_PRETIMEOUT_NONE);
+	    data[2] = 0; /* No pretimeout. */
+	}
+	data[3] = 0;
+	WDOG_SET_TIMEOUT(data[4], data[5], timeout);
+
+	addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	addr.channel = IPMI_BMC_CHANNEL;
+	addr.lun = 0;
+
+	msg.netfn = 0x06;
+	msg.cmd = IPMI_WDOG_SET_TIMER;
+	msg.data = data;
+	msg.data_len = sizeof(data);
+	rv = ipmi_request_supply_msgs(watchdog_user,
+				      (struct ipmi_addr *) &addr,
+				      0,
+				      &msg,
+				      smi_msg,
+				      recv_msg,
+				      1);
+	if (rv) {
+		printk(KERN_WARNING "IPMI Watchdog, set timeout error: %d\n",
+		       rv);
+	}
+
+	return rv;
+}
+
+static int ipmi_set_timeout(void)
+{
+	int send_heartbeat_now;
+	int rv;
+
+
+	/* We can only send one of these at a time. */
+	down(&set_timeout_lock);
+
+	atomic_set(&set_timeout_tofree, 2);
+
+	rv = i_ipmi_set_timeout(&set_timeout_smi_msg,
+				&set_timeout_recv_msg,
+				&send_heartbeat_now);
+	if (rv) {
+		up(&set_timeout_lock);
+	} else {
+		if (send_heartbeat_now)
+			rv = ipmi_heartbeat();
+	}
+
+	return rv;
+}
+
+static void dummy_smi_free(struct ipmi_smi_msg *msg)
+{
+}
+static void dummy_recv_free(struct ipmi_recv_msg *msg)
+{
+}
+static struct ipmi_smi_msg panic_halt_smi_msg =
+{
+	.done = dummy_smi_free
+};
+static struct ipmi_recv_msg panic_halt_recv_msg =
+{
+	.done = dummy_recv_free
+};
+
+/* Special call, doesn't claim any locks.  This is only to be called
+   at panic or halt time, in run-to-completion mode, when the caller
+   is the only CPU and the only thing that will be going IPMI
+   calls. */
+static void panic_halt_ipmi_set_timeout(void)
+{
+	int send_heartbeat_now;
+	int rv;
+
+	rv = i_ipmi_set_timeout(&panic_halt_smi_msg,
+				&panic_halt_recv_msg,
+				&send_heartbeat_now);
+	if (!rv) {
+		if (send_heartbeat_now)
+			panic_halt_ipmi_heartbeat();
+	}
+}
+
+/* Do a delayed shutdown, with the delay in milliseconds.  If power_off is
+   false, do a reset.  If power_off is true, do a power down.  This is
+   primarily for the IMB code's shutdown. */
+void ipmi_delayed_shutdown(long delay, int power_off)
+{
+	ipmi_ignore_heartbeat = 1;
+	if (power_off) 
+		ipmi_watchdog_state = WDOG_TIMEOUT_POWER_DOWN;
+	else
+		ipmi_watchdog_state = WDOG_TIMEOUT_RESET;
+	timeout = delay;
+	ipmi_set_timeout();
+}
+
+/* We use a semaphore to make sure that only one thing can send a
+   heartbeat at one time, because we only have one copy of the data.
+   The semaphore is claimed when the set_timeout is sent and freed
+   when both messages are free. */
+static atomic_t heartbeat_tofree = ATOMIC_INIT(0);
+static DECLARE_MUTEX(heartbeat_lock);
+static DECLARE_MUTEX_LOCKED(heartbeat_wait_lock);
+static void heartbeat_free_smi(struct ipmi_smi_msg *msg)
+{
+    if (atomic_dec_and_test(&heartbeat_tofree))
+	    up(&heartbeat_wait_lock);
+}
+static void heartbeat_free_recv(struct ipmi_recv_msg *msg)
+{
+    if (atomic_dec_and_test(&heartbeat_tofree))
+	    up(&heartbeat_wait_lock);
+}
+static struct ipmi_smi_msg heartbeat_smi_msg =
+{
+	.done = heartbeat_free_smi
+};
+static struct ipmi_recv_msg heartbeat_recv_msg =
+{
+	.done = heartbeat_free_recv
+};
+ 
+static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg =
+{
+	.done = dummy_smi_free
+};
+static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg =
+{
+	.done = dummy_recv_free
+};
+ 
+static int ipmi_heartbeat(void)
+{
+	struct ipmi_msg                   msg;
+	int                               rv;
+	struct ipmi_system_interface_addr addr;
+
+	if (ipmi_ignore_heartbeat) {
+		return 0;
+	}
+
+	if (ipmi_start_timer_on_heartbeat) {
+		ipmi_start_timer_on_heartbeat = 0;
+		ipmi_watchdog_state = action_val;
+		return ipmi_set_timeout();
+	}
+
+	if (pretimeout_since_last_heartbeat) {
+		/* A pretimeout occurred, make sure we set the timeout.
+		   We don't want to set the action, though, we want to
+		   leave that alone (thus it can't be combined with the
+		   above operation. */
+		pretimeout_since_last_heartbeat = 0;
+		return ipmi_set_timeout();
+	}
+
+	down(&heartbeat_lock);
+
+	atomic_set(&heartbeat_tofree, 2);
+
+	/* Don't reset the timer if we have the timer turned off, that
+           re-enables the watchdog. */
+	if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) {
+		up(&heartbeat_lock);
+		return 0;
+	}
+
+	addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	addr.channel = IPMI_BMC_CHANNEL;
+	addr.lun = 0;
+
+	msg.netfn = 0x06;
+	msg.cmd = IPMI_WDOG_RESET_TIMER;
+	msg.data = NULL;
+	msg.data_len = 0;
+	rv = ipmi_request_supply_msgs(watchdog_user,
+				      (struct ipmi_addr *) &addr,
+				      0,
+				      &msg,
+				      &heartbeat_smi_msg,
+				      &heartbeat_recv_msg,
+				      1);
+	if (rv) {
+		up(&heartbeat_lock);
+		printk(KERN_WARNING "IPMI Watchdog, heartbeat failure: %d\n",
+		       rv);
+		return rv;
+	}
+
+	/* Wait for the heartbeat to be sent. */
+	down(&heartbeat_wait_lock);
+
+	if (heartbeat_recv_msg.msg.data[0] != 0) {
+	    /* Got an error in the heartbeat response.  It was already
+	       reported in ipmi_wdog_msg_handler, but we should return
+	       an error here. */
+	    rv = -EINVAL;
+	}
+
+	up(&heartbeat_lock);
+
+	return rv;
+}
+
+static void panic_halt_ipmi_heartbeat(void)
+{
+	struct ipmi_msg                   msg;
+	struct ipmi_system_interface_addr addr;
+
+
+	/* Don't reset the timer if we have the timer turned off, that
+           re-enables the watchdog. */
+	if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE)
+		return;
+
+	addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	addr.channel = IPMI_BMC_CHANNEL;
+	addr.lun = 0;
+
+	msg.netfn = 0x06;
+	msg.cmd = IPMI_WDOG_RESET_TIMER;
+	msg.data = NULL;
+	msg.data_len = 0;
+	ipmi_request_supply_msgs(watchdog_user,
+				 (struct ipmi_addr *) &addr,
+				 0,
+				 &msg,
+				 &panic_halt_heartbeat_smi_msg,
+				 &panic_halt_heartbeat_recv_msg,
+				 1);
+}
+
+static struct watchdog_info ident=
+{
+	0, /* WDIOF_SETTIMEOUT, */
+	1,
+	"IPMI"
+};
+
+static int ipmi_ioctl(struct inode *inode, struct file *file,
+		      unsigned int cmd, unsigned long arg)
+{
+	int i;
+	int val;
+
+	switch(cmd) {
+	case WDIOC_GETSUPPORT:
+		i = copy_to_user((void*)arg, &ident, sizeof(ident));
+		return i ? -EFAULT : 0;
+
+	case WDIOC_SETTIMEOUT:
+		i = copy_from_user(&val, (void *) arg, sizeof(int));
+		if (i)
+			return -EFAULT;
+		timeout = val;
+		return ipmi_set_timeout();
+
+	case WDIOC_GETTIMEOUT:
+		i = copy_to_user((void *) arg,
+				 &timeout,
+				 sizeof(timeout));
+		if (i)
+			return -EFAULT;
+		return 0;
+
+	case WDIOC_SET_PRETIMEOUT:
+		i = copy_from_user(&val, (void *) arg, sizeof(int));
+		if (i)
+			return -EFAULT;
+		pretimeout = val;
+		return ipmi_set_timeout();
+
+	case WDIOC_GET_PRETIMEOUT:
+		i = copy_to_user((void *) arg,
+				 &pretimeout,
+				 sizeof(pretimeout));
+		if (i)
+			return -EFAULT;
+		return 0;
+
+	case WDIOC_KEEPALIVE:
+		return ipmi_heartbeat();
+
+	case WDIOC_SETOPTIONS:
+		i = copy_from_user(&val, (void *) arg, sizeof(int));
+		if (i)
+			return -EFAULT;
+		if (val & WDIOS_DISABLECARD)
+		{
+			ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
+			ipmi_set_timeout();
+			ipmi_start_timer_on_heartbeat = 0;
+		}
+
+		if (val & WDIOS_ENABLECARD)
+		{
+			ipmi_watchdog_state = action_val;
+			ipmi_set_timeout();
+		}
+		return 0;
+
+	case WDIOC_GETSTATUS:
+		val = 0;
+		return copy_to_user((void *) arg, &val, sizeof(val));
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static ssize_t ipmi_write(struct file *file,
+			  const char  *buf,
+			  size_t      len,
+			  loff_t      *ppos)
+{
+	int rv;
+
+	/*  Can't seek (pwrite) on this device  */
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	if (len) {
+		rv = ipmi_heartbeat();
+		if (rv)
+			return rv;
+		return 1;
+	}
+	return 0;
+}
+
+static ssize_t ipmi_read(struct file *file,
+			 char        *buf,
+			 size_t      count,
+			 loff_t      *ppos)
+{
+	int          rv = 0;
+	wait_queue_t wait;
+
+	/*  Can't seek (pread) on this device  */
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	if (count <= 0)
+		return 0;
+
+	/* Reading returns if the pretimeout has gone off, and it only does
+	   it once per pretimeout. */
+	spin_lock(&ipmi_read_lock);
+	if (!data_to_read) {
+		if (file->f_flags & O_NONBLOCK) {
+			rv = -EAGAIN;
+			goto out;
+		}
+		
+		init_waitqueue_entry(&wait, current);
+		add_wait_queue(&read_q, &wait);
+		while (!data_to_read) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			spin_unlock(&ipmi_read_lock);
+			schedule();
+			spin_lock(&ipmi_read_lock);
+		}
+		remove_wait_queue(&read_q, &wait);
+	    
+		if (signal_pending(current)) {
+			rv = -ERESTARTSYS;
+			goto out;
+		}
+	}
+	data_to_read = 0;
+
+ out:
+	spin_unlock(&ipmi_read_lock);
+
+	if (rv == 0) {
+		if (copy_to_user(buf, &data_to_read, 1))
+			rv = -EFAULT;
+		else
+			rv = 1;
+	}
+
+	return rv;
+}
+
+static int ipmi_open(struct inode *ino, struct file *filep)
+{
+        switch (minor(ino->i_rdev))
+        {
+                case WATCHDOG_MINOR:
+                    if (ipmi_wdog_open)
+                        return -EBUSY;
+
+                    ipmi_wdog_open = 1;
+
+		    /* Don't start the timer now, let it start on the
+		       first heartbeat. */
+		    ipmi_start_timer_on_heartbeat = 1;
+                    return(0);
+
+                default:
+                    return (-ENODEV);
+        }
+}
+
+static unsigned int ipmi_poll(struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+	
+	poll_wait(file, &read_q, wait);
+
+	spin_lock(&ipmi_read_lock);
+	if (data_to_read)
+		mask |= (POLLIN | POLLRDNORM);
+	spin_unlock(&ipmi_read_lock);
+
+	return mask;
+}
+
+static int ipmi_fasync(int fd, struct file *file, int on)
+{
+	int result;
+
+	result = fasync_helper(fd, file, on, &fasync_q);
+
+	return (result);
+}
+
+static int ipmi_close(struct inode *ino, struct file *filep)
+{
+	if (minor(ino->i_rdev)==WATCHDOG_MINOR)
+	{
+#ifndef CONFIG_WATCHDOG_NOWAYOUT	
+		ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
+		ipmi_set_timeout();
+#endif		
+	        ipmi_wdog_open = 0;
+	}
+
+	ipmi_fasync (-1, filep, 0);
+
+	return 0;
+}
+
+static struct file_operations ipmi_wdog_fops = {
+	.owner   = THIS_MODULE,
+	.read    = ipmi_read,
+	.poll    = ipmi_poll,
+	.write   = ipmi_write,
+	.ioctl   = ipmi_ioctl,
+	.open    = ipmi_open,
+	.release = ipmi_close,
+	.fasync  = ipmi_fasync,
+};
+
+static struct miscdevice ipmi_wdog_miscdev = {
+	WATCHDOG_MINOR,
+	"watchdog",
+	&ipmi_wdog_fops
+};
+
+static DECLARE_RWSEM(register_sem);
+
+static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg,
+				  void                 *handler_data)
+{
+	if (msg->msg.data[0] != 0) {
+		printk(KERN_ERR "IPMI Watchdog response: Error %x on cmd %x\n",
+		       msg->msg.data[0],
+		       msg->msg.cmd);
+	}
+	
+	ipmi_free_recv_msg(msg);
+}
+
+static void ipmi_wdog_pretimeout_handler(void *handler_data)
+{
+	if (preaction_val != WDOG_PRETIMEOUT_NONE) {
+		if (preop_val == WDOG_PREOP_PANIC)
+			panic("Watchdog pre-timeout");
+		else if (preop_val == WDOG_PREOP_GIVE_DATA) {
+			spin_lock(&ipmi_read_lock);
+			data_to_read = 1;
+			wake_up_interruptible(&read_q);
+			kill_fasync(&fasync_q, SIGIO, POLL_IN);
+
+			/* On some machines, the heartbeat will give
+			   an error and not work unless we re-enable
+			   the timer.   So do so. */
+			pretimeout_since_last_heartbeat = 1;
+
+			spin_unlock(&ipmi_read_lock);
+		}
+	}
+}
+
+static struct ipmi_user_hndl ipmi_hndlrs =
+{
+	.ipmi_recv_hndl           = ipmi_wdog_msg_handler,
+	.ipmi_watchdog_pretimeout = ipmi_wdog_pretimeout_handler
+};
+
+static void ipmi_register_watchdog(int ipmi_intf)
+{
+	int rv = -EBUSY;
+
+	down_read(&register_sem);
+	if (watchdog_user)
+		goto out;
+
+	rv = ipmi_create_user(ipmi_intf, &ipmi_hndlrs, NULL, &watchdog_user);
+	if (rv < 0) {
+		printk("IPMI watchdog: Unable to register with ipmi\n");
+		goto out;
+	}
+
+	ipmi_get_version(watchdog_user,
+			 &ipmi_version_major,
+			 &ipmi_version_minor);
+
+	rv = misc_register(&ipmi_wdog_miscdev);
+	if (rv < 0) {
+		ipmi_destroy_user(watchdog_user);
+		watchdog_user = NULL;
+		printk("IPMI watchdog: Unable to register misc device\n");
+	}
+
+ out:
+	up_write(&register_sem);
+
+	if ((start_now) && (rv == 0)) {
+		/* Run from startup, so start the timer now. */
+		start_now = 0; /* Disable this function after first startup. */
+		ipmi_watchdog_state = action_val;
+		ipmi_set_timeout();
+		printk("Starting IPMI Watchdog now!\n");
+	}
+}
+
+#ifdef HAVE_NMI_HANDLER
+static int
+ipmi_nmi(void *dev_id, struct pt_regs *regs, int cpu, int handled)
+{
+	/* If no one else handled the NMI, we assume it was the IPMI
+           watchdog. */
+	if ((!handled) && (preop_val == WDOG_PREOP_PANIC))
+		panic("IPMI watchdog pre-timeout");
+	return NOTIFY_DONE;
+}
+
+static struct nmi_handler ipmi_nmi_handler =
+{
+	.dev_name = "ipmi_watchdog",
+	.dev_id   = NULL,
+	.handler  = ipmi_nmi,
+	.priority = 0, /* Call us last. */
+};
+#endif
+
+static int wdog_reboot_handler(struct notifier_block *this,
+			       unsigned long         code,
+			       void                  *unused)
+{
+	static int reboot_event_handled = 0;
+
+	if ((watchdog_user) && (!reboot_event_handled)) {
+		/* Make sure we only do this once. */
+		reboot_event_handled = 1;
+
+		if (code == SYS_DOWN || code == SYS_HALT) {
+			/* Disable the WDT if we are shutting down. */
+			ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
+			panic_halt_ipmi_set_timeout();
+		} else {
+			/* Set a long timer to let the reboot happens, but
+			   reboot if it hangs. */
+			timeout = 120;
+			pretimeout = 0;
+			ipmi_watchdog_state = WDOG_TIMEOUT_RESET;
+			panic_halt_ipmi_set_timeout();
+		}
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block wdog_reboot_notifier = {
+	wdog_reboot_handler,
+	NULL,
+	0
+};
+
+extern int panic_timeout; /* Why isn't this defined anywhere? */
+
+static int wdog_panic_handler(struct notifier_block *this,
+			      unsigned long         event,
+			      void                  *unused)
+{
+	static int panic_event_handled = 0;
+
+	/* On a panic, if we have a panic timeout, make sure that the thing
+	   reboots, even if it hangs during that panic. */
+	if (watchdog_user && !panic_event_handled && (panic_timeout > 0)) {
+		/* Make sure the panic doesn't hang, and make sure we
+		   do this only once. */
+		panic_event_handled = 1;
+	    
+		timeout = panic_timeout + 120;
+		if (timeout > 255)
+			timeout = 255;
+		pretimeout = 0;
+		ipmi_watchdog_state = WDOG_TIMEOUT_RESET;
+		panic_halt_ipmi_set_timeout();
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block wdog_panic_notifier = {
+	wdog_panic_handler,
+	NULL,
+	150   /* priority: INT_MAX >= x >= 0 */
+};
+
+
+static void ipmi_new_smi(int if_num)
+{
+	ipmi_register_watchdog(if_num);
+}
+
+static void ipmi_smi_gone(int if_num)
+{
+	/* This can never be called, because once the watchdog is
+	   registered, the interface can't go away until the watchdog
+	   is unregistered. */
+}
+
+static struct ipmi_smi_watcher smi_watcher =
+{
+	.new_smi  = ipmi_new_smi,
+	.smi_gone = ipmi_smi_gone
+};
+
+static int __init ipmi_wdog_init(void)
+{
+	int rv;
+
+	if (strcmp(action, "reset") == 0) {
+		action_val = WDOG_TIMEOUT_RESET;
+	} else if (strcmp(action, "none") == 0) {
+		action_val = WDOG_TIMEOUT_NONE;
+	} else if (strcmp(action, "power_cycle") == 0) {
+		action_val = WDOG_TIMEOUT_POWER_CYCLE;
+	} else if (strcmp(action, "power_off") == 0) {
+		action_val = WDOG_TIMEOUT_POWER_DOWN;
+	} else {
+		action_val = WDOG_TIMEOUT_RESET;
+		printk("ipmi_watchdog: Unknown action '%s', defaulting to"
+		       " reset\n", action);
+	}
+
+	if (strcmp(preaction, "pre_none") == 0) {
+		preaction_val = WDOG_PRETIMEOUT_NONE;
+	} else if (strcmp(preaction, "pre_smi") == 0) {
+		preaction_val = WDOG_PRETIMEOUT_SMI;
+#ifdef HAVE_NMI_HANDLER
+	} else if (strcmp(preaction, "pre_nmi") == 0) {
+		preaction_val = WDOG_PRETIMEOUT_NMI;
+#endif
+	} else if (strcmp(preaction, "pre_int") == 0) {
+		preaction_val = WDOG_PRETIMEOUT_MSG_INT;
+	} else {
+		action_val = WDOG_PRETIMEOUT_NONE;
+		printk("ipmi_watchdog: Unknown preaction '%s', defaulting to"
+		       " none\n", preaction);
+	}
+
+	if (strcmp(preop, "preop_none") == 0) {
+		preop_val = WDOG_PREOP_NONE;
+	} else if (strcmp(preop, "preop_panic") == 0) {
+		preop_val = WDOG_PREOP_PANIC;
+	} else if (strcmp(preop, "preop_give_data") == 0) {
+		preop_val = WDOG_PREOP_GIVE_DATA;
+	} else {
+		action_val = WDOG_PREOP_NONE;
+		printk("ipmi_watchdog: Unknown preop '%s', defaulting to"
+		       " none\n", preop);
+	}
+
+#ifdef HAVE_NMI_HANDLER
+	if (preaction_val == WDOG_PRETIMEOUT_NMI) {
+		if (preop_val == WDOG_PREOP_GIVE_DATA) {
+			printk(KERN_WARNING
+			       "ipmi_watchdog: Pretimeout op is to give data"
+			       " but NMI pretimeout is enabled, setting"
+			       " pretimeout op to none\n");
+			preop_val = WDOG_PREOP_NONE;
+		}
+#ifdef CONFIG_X86_LOCAL_APIC
+		if (nmi_watchdog == NMI_IO_APIC) {
+			printk(KERN_WARNING
+			       "ipmi_watchdog: nmi_watchdog is set to IO APIC"
+			       " mode (value is %d), that is incompatible"
+			       " with using NMI in the IPMI watchdog."
+			       " Disabling IPMI nmi pretimeout.\n",
+			       nmi_watchdog);
+			preaction_val = WDOG_PRETIMEOUT_NONE;
+		} else {
+#endif
+		rv = request_nmi(&ipmi_nmi_handler);
+		if (rv) {
+			printk(KERN_WARNING
+			       "ipmi_watchdog: Can't register nmi handler\n");
+			return rv;
+		}
+#ifdef CONFIG_X86_LOCAL_APIC
+		}
+#endif
+	}
+#endif
+
+	rv = ipmi_smi_watcher_register(&smi_watcher);
+	if (rv) {
+#ifdef HAVE_NMI_HANDLER
+		if (preaction_val == WDOG_PRETIMEOUT_NMI)
+			release_nmi(&ipmi_nmi_handler);
+#endif
+		printk(KERN_WARNING
+		       "ipmi_watchdog: can't register smi watcher\n");
+		return rv;
+	}
+
+	register_reboot_notifier(&wdog_reboot_notifier);
+	notifier_chain_register(&panic_notifier_list, &wdog_panic_notifier);
+
+	printk(KERN_INFO "IPMI watchdog by "
+	       "Corey Minyard (minyard@mvista.com)\n");
+
+	return 0;
+}
+
+#ifdef MODULE
+static void ipmi_unregister_watchdog(void)
+{
+	int rv;
+
+	down_write(&register_sem);
+
+#ifdef HAVE_NMI_HANDLER
+	if (preaction_val == WDOG_PRETIMEOUT_NMI)
+		release_nmi(&ipmi_nmi_handler);
+#endif
+
+	notifier_chain_unregister(&panic_notifier_list, &wdog_panic_notifier);
+	unregister_reboot_notifier(&wdog_reboot_notifier);
+
+	if (! watchdog_user)
+		goto out;
+
+	/* Make sure no one can call us any more. */
+	misc_deregister(&ipmi_wdog_miscdev);
+
+	/*  Disable the timer. */
+	ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
+	ipmi_set_timeout();
+
+	/* Wait to make sure the message makes it out.  The lower layer has
+	   pointers to our buffers, we want to make sure they are done before
+	   we release our memory. */
+	while (atomic_read(&set_timeout_tofree)) {
+		schedule_timeout(1);
+	}
+
+	/* Disconnect from IPMI. */
+	rv = ipmi_destroy_user(watchdog_user);
+	if (rv) {
+		printk(KERN_WARNING
+		       "IPMI Watchdog, error unlinking from IPMI: %d\n",
+		       rv);
+	}
+	watchdog_user = NULL;
+
+ out:
+	up_write(&register_sem);
+}
+
+static void __exit ipmi_wdog_exit(void)
+{
+	ipmi_smi_watcher_unregister(&smi_watcher);
+	ipmi_unregister_watchdog();
+}
+module_exit(ipmi_wdog_exit);
+#else
+static int __init ipmi_wdog_setup(char *str)
+{
+	int  val;
+	int  rv;
+	char *option;
+
+	rv = get_option(&str, &val);
+	if (rv == 0)
+		return 1;
+	if (val > 0)
+		timeout = val;
+	if (rv == 1)
+		return 1;
+
+	rv = get_option(&str, &val);
+	if (rv == 0)
+		return 1;
+	if (val >= 0)
+		pretimeout = val;
+	if (rv == 1)
+		return 1;
+
+	while ((option = strsep(&str, ",")) != NULL) {
+		if (strcmp(option, "reset") == 0) {
+			action = "reset";
+		}
+		else if (strcmp(option, "none") == 0) {
+			action = "none";
+		}
+		else if (strcmp(option, "power_cycle") == 0) {
+			action = "power_cycle";
+		}
+		else if (strcmp(option, "power_off") == 0) {
+			action = "power_off";
+		}
+		else if (strcmp(option, "pre_none") == 0) {
+			preaction = "pre_none";
+		}
+		else if (strcmp(option, "pre_smi") == 0) {
+			preaction = "pre_smi";
+		}
+#ifdef HAVE_NMI_HANDLER
+		else if (strcmp(option, "pre_nmi") == 0) {
+			preaction = "pre_nmi";
+		}
+#endif
+		else if (strcmp(option, "pre_int") == 0) {
+			preaction = "pre_int";
+		}
+		else if (strcmp(option, "start_now") == 0) {
+			start_now = 1;
+		}
+		else if (strcmp(option, "preop_none") == 0) {
+			preop = "preop_none";
+		}
+		else if (strcmp(option, "preop_panic") == 0) {
+			preop = "preop_panic";
+		}
+		else if (strcmp(option, "preop_none") == 0) {
+			preop = "preop_give_data";
+		} else {
+		    printk("Unknown IPMI watchdog option: '%s'\n", option);
+		}
+	}
+
+	return 1;
+}
+__setup("ipmi_wdog=", ipmi_wdog_setup);
+#endif
+
+EXPORT_SYMBOL(ipmi_delayed_shutdown);
+
+module_init(ipmi_wdog_init);
+MODULE_LICENSE("GPL");
diff -urN linux.orig/include/linux/ipmi.h linux/include/linux/ipmi.h
--- linux.orig/include/linux/ipmi.h	Wed Dec 31 18:00:00 1969
+++ linux/include/linux/ipmi.h	Mon Jan 13 09:46:54 2003
@@ -0,0 +1,516 @@
+/*
+ * ipmi.h
+ *
+ * MontaVista IPMI interface
+ *
+ * Author: MontaVista Software, Inc.
+ *         Corey Minyard <minyard@mvista.com>
+ *         source@mvista.com
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ *  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.
+ *
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_IPMI_H
+#define __LINUX_IPMI_H
+
+#include <linux/ipmi_msgdefs.h>
+
+/*
+ * This file describes an interface to an IPMI driver.  You have to
+ * have a fairly good understanding of IPMI to use this, so go read
+ * the specs first before actually trying to do anything.
+ *
+ * With that said, this driver provides a multi-user interface to the
+ * IPMI driver, and it allows multiple IPMI physical interfaces below
+ * the driver.  The physical interfaces bind as a lower layer on the
+ * driver.  They appear as interfaces to the application using this
+ * interface.
+ *
+ * Multi-user means that multiple applications may use the driver,
+ * send commands, receive responses, etc.  The driver keeps track of
+ * commands the user sends and tracks the responses.  The responses
+ * will go back to the application that send the command.  If the
+ * response doesn't come back in time, the driver will return a
+ * timeout error response to the application.  Asynchronous events
+ * from the BMC event queue will go to all users bound to the driver.
+ * The incoming event queue in the BMC will automatically be flushed
+ * if it becomes full and it is queried once a second to see if
+ * anything is in it.  Incoming commands to the driver will get
+ * delivered as commands.
+ *
+ * This driver provides two main interfaces: one for in-kernel
+ * applications and another for userland applications.  The
+ * capabilities are basically the same for both interface, although
+ * the interfaces are somewhat different.  The stuff in the
+ * #ifdef KERNEL below is the in-kernel interface.  The userland
+ * interface is defined later in the file.  */
+
+
+
+/*
+ * This is an overlay for all the address types, so it's easy to
+ * determine the actual address type.  This is kind of like addresses
+ * work for sockets.
+ */
+#define IPMI_MAX_ADDR_SIZE 32
+struct ipmi_addr
+{
+	 /* Try to take these from the "Channel Medium Type" table
+	    in section 6.5 of the IPMI 1.5 manual. */
+	int   addr_type;
+	short channel;
+	char  data[IPMI_MAX_ADDR_SIZE];
+};
+
+/*
+ * When the address is not used, the type will be set to this value.
+ * The channel is the BMC's channel number for the channel (usually
+ * 0), or IPMC_BMC_CHANNEL if communicating directly with the BMC.
+ */
+#define IPMI_SYSTEM_INTERFACE_ADDR_TYPE	0x0c
+struct ipmi_system_interface_addr
+{
+	int           addr_type;
+	short         channel;
+	unsigned char lun;
+};
+
+/* An IPMB Address. */
+#define IPMI_IPMB_ADDR_TYPE		0x01
+/* Used for broadcast get device id as described in section 17.9 of the
+   IPMI 1.5 manual. */ 
+#define IPMI_IPMB_BROADCAST_ADDR_TYPE	0x41
+struct ipmi_ipmb_addr
+{
+	int           addr_type;
+	short         channel;
+	unsigned char slave_addr;
+	unsigned char lun;
+};
+
+
+/*
+ * Channel for talking directly with the BMC.  When using this
+ * channel, This is for the system interface address type only.  FIXME
+ * - is this right, or should we use -1?
+ */
+#define IPMI_BMC_CHANNEL  0xf
+#define IPMI_NUM_CHANNELS 0x10
+
+
+/*
+ * A raw IPMI message without any addressing.  This covers both
+ * commands and responses.  The completion code is always the first
+ * byte of data in the response (as the spec shows the messages laid
+ * out).
+ */
+struct ipmi_msg
+{
+	unsigned char  netfn;
+	unsigned char  cmd;
+	unsigned short data_len;
+	unsigned char  *data;
+};
+
+/*
+ * Various defines that are useful for IPMI applications.
+ */
+#define IPMI_INVALID_CMD_COMPLETION_CODE	0xC1
+#define IPMI_TIMEOUT_COMPLETION_CODE		0xC3
+#define IPMI_UNKNOWN_ERR_COMPLETION_CODE	0xff
+
+
+/*
+ * Receive types for messages coming from the receive interface.  This
+ * is used for the receive in-kernel interface and in the receive
+ * IOCTL.
+ */
+#define IPMI_RESPONSE_RECV_TYPE		1 /* A response to a command */
+#define IPMI_ASYNC_EVENT_RECV_TYPE	2 /* Something from the event queue */
+#define IPMI_CMD_RECV_TYPE		3 /* A command from somewhere else */
+/* Note that async events and received commands do not have a completion
+   code as the first byte of the incoming data, unlike a response. */
+
+
+
+#ifdef __KERNEL__
+
+/*
+ * The in-kernel interface.
+ */
+#include <linux/list.h>
+
+/* Opaque type for a IPMI message user.  One of these is needed to
+   send and receive messages. */
+typedef struct ipmi_user *ipmi_user_t;
+
+/*
+ * Stuff coming from the recieve interface comes as one of these.
+ * They are allocated, the receiver must free them with
+ * ipmi_free_recv_msg() when done with the message.  The link is not
+ * used after the message is delivered, so the upper layer may use the
+ * link to build a linked list, if it likes.
+ */
+struct ipmi_recv_msg
+{
+	struct list_head link;
+
+	/* The type of message as defined in the "Receive Types"
+           defines above. */
+	int              recv_type;
+
+	ipmi_user_t      user;
+	struct ipmi_addr addr;
+	long             msgid;
+	struct ipmi_msg  msg;
+
+	/* Call this when done with the message.  It will presumably free
+	   the message and do any other necessary cleanup. */
+	void (*done)(struct ipmi_recv_msg *msg);
+
+	/* Place-holder for the data, don't make any assumptions about
+	   the size or existance of this, since it may change. */
+	unsigned char   msg_data[IPMI_MAX_MSG_LENGTH];
+};
+
+/* Allocate and free the receive message. */
+static inline void ipmi_free_recv_msg(struct ipmi_recv_msg *msg)
+{
+	msg->done(msg);
+}
+struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
+
+struct ipmi_user_hndl
+{
+        /* Routine type to call when a message needs to be routed to
+	   the upper layer.  This will be called with some locks held,
+	   the only IPMI routines that can be called are ipmi_request
+	   and the alloc/free operations. */
+	void (*ipmi_recv_hndl)(struct ipmi_recv_msg *msg,
+			       void                 *handler_data);
+
+	/* Called when the interface detects a watchdog pre-timeout.  If
+	   this is NULL, it will be ignored for the user. */
+	void (*ipmi_watchdog_pretimeout)(void *handler_data);
+};
+
+/* Create a new user of the IPMI layer on the given interface number. */
+int ipmi_create_user(unsigned int          if_num,
+		     struct ipmi_user_hndl *handler,
+		     void                  *handler_data,
+		     ipmi_user_t           *user);
+
+/* Destroy the given user of the IPMI layer. */
+int ipmi_destroy_user(ipmi_user_t user);
+
+/* Get the IPMI version of the BMC we are talking to. */
+void ipmi_get_version(ipmi_user_t   user,
+		      unsigned char *major,
+		      unsigned char *minor);
+
+/* Set and get the slave address and LUN that we will use for our
+   source messages.  Note that this affects the interface, not just
+   this user, so it will affect all users of this interface.  This is
+   so some initialization code can come in and do the OEM-specific
+   things it takes to determine your address (if not the BMC) and set
+   it for everyone else. */
+void ipmi_set_my_address(ipmi_user_t   user,
+			 unsigned char address);
+unsigned char ipmi_get_my_address(ipmi_user_t user);
+void ipmi_set_my_LUN(ipmi_user_t   user,
+		     unsigned char LUN);
+unsigned char ipmi_get_my_LUN(ipmi_user_t user);
+
+/*
+ * Send a command request from the given user.  The address is the
+ * proper address for the channel type.  If this is a command, then
+ * the message response comes back, the receive handler for this user
+ * will be called with the given msgid value in the recv msg.  If this
+ * is a response to a command, then the msgid will be used as the
+ * sequence number for the response (truncated if necessary), so when
+ * sending a response you should use the sequence number you received
+ * in the msgid field of the received command.  If the priority is >
+ * 0, the message will go into a high-priority queue and be sent
+ * first.  Otherwise, it goes into a normal-priority queue.
+ */
+int ipmi_request(ipmi_user_t      user,
+		 struct ipmi_addr *addr,
+		 long             msgid,
+		 struct ipmi_msg  *msg,
+		 int              priority);
+
+/*
+ * Like ipmi_request, but lets you specify the slave return address.
+ */
+int ipmi_request_with_source(ipmi_user_t      user,
+			     struct ipmi_addr *addr,
+			     long             msgid,
+			     struct ipmi_msg  *msg,
+			     int              priority,
+			     unsigned char    source_address,
+			     unsigned char    source_lun);
+
+/*
+ * Like ipmi_request, but with messages supplied.  This will not
+ * allocate any memory, and the messages may be statically allocated
+ * (just make sure to do the "done" handling on them).  Note that this
+ * is primarily for the watchdog timer, since it should be able to
+ * send messages even if no memory is available.  This is subject to
+ * change as the system changes, so don't use it unless you REALLY
+ * have to.
+ */
+int ipmi_request_supply_msgs(ipmi_user_t          user,
+			     struct ipmi_addr     *addr,
+			     long                 msgid,
+			     struct ipmi_msg      *msg,
+			     void                 *supplied_smi,
+			     struct ipmi_recv_msg *supplied_recv,
+			     int                  priority);
+
+/*
+ * When commands come in to the SMS, the user can register to receive
+ * them.  Only one user can be listening on a specific netfn/cmd pair
+ * at a time, you will get an EBUSY error if the command is already
+ * registered.  If a command is received that does not have a user
+ * registered, the driver will automatically return the proper
+ * error.
+ */
+int ipmi_register_for_cmd(ipmi_user_t   user,
+			  unsigned char netfn,
+			  unsigned char cmd);
+int ipmi_unregister_for_cmd(ipmi_user_t   user,
+			    unsigned char netfn,
+			    unsigned char cmd);
+
+/*
+ * When the user is created, it will not receive IPMI events by
+ * default.  The user must set this to TRUE to get incoming events.
+ * The first user that sets this to TRUE will receive all events that
+ * have been queued while no one was waiting for events.
+ */
+int ipmi_set_gets_events(ipmi_user_t user, int val);
+
+/*
+ * Register the given user to handle all received IPMI commands.  This
+ * will fail if anyone is registered as a command receiver or if
+ * another is already registered to receive all commands.  NOTE THAT
+ * THIS IS FOR EMULATION USERS ONLY, DO NOT USER THIS FOR NORMAL
+ * STUFF.
+ */
+int ipmi_register_all_cmd_rcvr(ipmi_user_t user);
+int ipmi_unregister_all_cmd_rcvr(ipmi_user_t user);
+
+
+/*
+ * Called when a new SMI is registered.  This will also be called on
+ * every existing interface when a new watcher is registered with
+ * ipmi_smi_watcher_register().
+ */
+struct ipmi_smi_watcher
+{
+	struct list_head link;
+
+	/* These two are called with read locks held for the interface
+	   the watcher list.  So you can add and remove users from the
+	   IPMI interface, send messages, etc., but you cannot add
+	   or remove SMI watchers or SMI interfaces. */
+	void (*new_smi)(int if_num);
+	void (*smi_gone)(int if_num);
+};
+
+int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher);
+int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher);
+
+/* The following are various helper functions for dealing with IPMI
+   addresses. */
+
+/* Return the maximum length of an IPMI address given it's type. */
+unsigned int ipmi_addr_length(int addr_type);
+
+/* Validate that the given IPMI address is valid. */
+int ipmi_validate_addr(struct ipmi_addr *addr, int len);
+
+/* Return 1 if the given addresses are equal, 0 if not. */
+int ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2);
+
+#endif /* __KERNEL__ */
+
+
+/*
+ * The userland interface
+ */
+
+/*
+ * The userland interface for the IPMI driver is a standard character
+ * device, with each instance of an interface registered as a minor
+ * number under the major character device.
+ *
+ * The read and write calls do not work, to get messages in and out
+ * requires ioctl calls because of the complexity of the data.  select
+ * and poll do work, so you can wait for input using the file
+ * descriptor, you just can use read to get it.
+ *
+ * In general, you send a command down to the interface and receive
+ * responses back.  You can use the msgid value to correlate commands
+ * and responses, the driver will take care of figuring out which
+ * incoming messages are for which command and find the proper msgid
+ * value to report.  You will only receive reponses for commands you
+ * send.  Asynchronous events, however, go to all open users, so you
+ * must be ready to handle these (or ignore them if you don't care).
+ *
+ * The address type depends upon the channel type.  When talking
+ * directly to the BMC (IPMC_BMC_CHANNEL), the address is ignored
+ * (IPMI_UNUSED_ADDR_TYPE).  When talking to an IPMB channel, you must
+ * supply a valid IPMB address with the addr_type set properly.
+ *
+ * When talking to normal channels, the driver takes care of the
+ * details of formatting and sending messages on that channel.  You do
+ * not, for instance, have to format a send command, you just send
+ * whatever command you want to the channel, the driver will create
+ * the send command, automatically issue receive command and get even
+ * commands, and pass those up to the proper user.
+ */
+
+
+/* The magic IOCTL value for this interface. */
+#define IPMI_IOC_MAGIC 'i'
+
+
+/* Messages sent to the interface are this format. */
+struct ipmi_req
+{
+	unsigned char *addr; /* Address to send the message to. */
+	unsigned int  addr_len;
+
+	long    msgid; /* The sequence number for the message.  This
+			  exact value will be reported back in the
+			  response to this request if it is a command.
+			  If it is a response, this will be used as
+			  the sequence value for the response.  */
+
+	struct ipmi_msg msg;
+};
+/*
+ * Send a message to the interfaces.  error values are:
+ *   - EFAULT - an address supplied was invalid.
+ *   - EINVAL - The address supplied was not valid, or the command
+ *              was not allowed.
+ *   - EMSGSIZE - The message to was too large.
+ *   - ENOMEM - Buffers could not be allocated for the command.
+ */
+#define IPMICTL_SEND_COMMAND		_IOR(IPMI_IOC_MAGIC, 13,	\
+					     struct ipmi_req)
+
+/* Messages received from the interface are this format. */
+struct ipmi_recv
+{
+	int     recv_type; /* Is this a command, response or an
+			      asyncronous event. */
+
+	unsigned char *addr;    /* Address the message was from is put
+				   here.  The caller must supply the
+				   memory. */
+	unsigned int  addr_len; /* The size of the address buffer.
+				   The caller supplies the full buffer
+				   length, this value is updated to
+				   the actual message length when the
+				   message is received. */
+
+	long    msgid; /* The sequence number specified in the request
+			  if this is a response.  If this is a command,
+			  this will be the sequence number from the
+			  command. */
+
+	struct ipmi_msg msg; /* The data field must point to a buffer.
+				The data_size field must be set to the
+				size of the message buffer.  The
+				caller supplies the full buffer
+				length, this value is updated to the
+				actual message length when the message
+				is received. */
+};
+
+/*
+ * Receive a message.  error values:
+ *  - EAGAIN - no messages in the queue.
+ *  - EFAULT - an address supplied was invalid.
+ *  - EINVAL - The address supplied was not valid.
+ *  - EMSGSIZE - The message to was too large to fit into the message buffer,
+ *               the message will be left in the buffer. */
+#define IPMICTL_RECEIVE_MSG		_IOWR(IPMI_IOC_MAGIC, 12,	\
+					      struct ipmi_recv)
+
+/*
+ * Like RECEIVE_MSG, but if the message won't fit in the buffer, it
+ * will truncate the contents instead of leaving the data in the
+ * buffer.
+ */
+#define IPMICTL_RECEIVE_MSG_TRUNC	_IOWR(IPMI_IOC_MAGIC, 11,	\
+					      struct ipmi_recv)
+
+/* Register to get commands from other entities on this interface. */
+struct ipmi_cmdspec
+{
+	unsigned char netfn;
+	unsigned char cmd;
+};
+
+/* 
+ * Register to receive a specific command.  error values:
+ *   - EFAULT - an address supplied was invalid.
+ *   - EBUSY - The netfn/cmd supplied was already in use.
+ *   - ENOMEM - could not allocate memory for the entry.
+ */
+#define IPMICTL_REGISTER_FOR_CMD	_IOR(IPMI_IOC_MAGIC, 14,	\
+					     struct ipmi_cmdspec)
+/*
+ * Unregister a regsitered command.  error values:
+ *  - EFAULT - an address supplied was invalid.
+ *  - ENOENT - The netfn/cmd was not found registered for this user.
+ */
+#define IPMICTL_UNREGISTER_FOR_CMD	_IOR(IPMI_IOC_MAGIC, 15,	\
+					     struct ipmi_cmdspec)
+
+/* 
+ * Set whether this interface receives events.  Note that the first
+ * user registered for events will get all pending events for the
+ * interface.  error values:
+ *  - EFAULT - an address supplied was invalid.
+ */
+#define IPMICTL_SET_GETS_EVENTS_CMD	_IOR(IPMI_IOC_MAGIC, 16, int)
+
+/*
+ * Set and get the slave address and LUN that we will use for our
+ * source messages.  Note that this affects the interface, not just
+ * this user, so it will affect all users of this interface.  This is
+ * so some initialization code can come in and do the OEM-specific
+ * things it takes to determine your address (if not the BMC) and set
+ * it for everyone else.  You should probably leave the LUN alone.
+ */
+#define IPMICTL_SET_MY_ADDRESS_CMD	_IOR(IPMI_IOC_MAGIC, 17, unsigned int)
+#define IPMICTL_GET_MY_ADDRESS_CMD	_IOR(IPMI_IOC_MAGIC, 18, unsigned int)
+#define IPMICTL_SET_MY_LUN_CMD		_IOR(IPMI_IOC_MAGIC, 19, unsigned int)
+#define IPMICTL_GET_MY_LUN_CMD		_IOR(IPMI_IOC_MAGIC, 20, unsigned int)
+
+#endif /* __LINUX_IPMI_H */
diff -urN linux.orig/include/linux/ipmi_msgdefs.h linux/include/linux/ipmi_msgdefs.h
--- linux.orig/include/linux/ipmi_msgdefs.h	Wed Dec 31 18:00:00 1969
+++ linux/include/linux/ipmi_msgdefs.h	Thu Aug 22 16:56:10 2002
@@ -0,0 +1,58 @@
+/*
+ * ipmi_smi.h
+ *
+ * MontaVista IPMI system management interface
+ *
+ * Author: MontaVista Software, Inc.
+ *         Corey Minyard <minyard@mvista.com>
+ *         source@mvista.com
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ *  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.
+ *
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_IPMI_MSGDEFS_H
+#define __LINUX_IPMI_MSGDEFS_H
+
+/* Various definitions for IPMI messages used by almost everything in
+   the IPMI stack. */
+
+#define IPMI_NETFN_APP_REQUEST	0x06
+#define IPMI_NETFN_APP_RESPONSE	0x07
+
+#define IPMI_BMC_SLAVE_ADDR	0x20
+
+#define IPMI_GET_DEVICE_ID_CMD		0x01
+
+#define IPMI_CLEAR_MSG_FLAGS_CMD	0x30
+#define IPMI_GET_MSG_FLAGS_CMD		0x31
+#define IPMI_SEND_MSG_CMD		0x34
+#define IPMI_GET_MSG_CMD		0x33
+
+#define IPMI_SET_BMC_GLOBAL_ENABLES_CMD	0x2e
+#define IPMI_GET_BMC_GLOBAL_ENABLES_CMD	0x2f
+#define IPMI_READ_EVENT_MSG_BUFFER_CMD	0x35
+
+#define IPMI_MAX_MSG_LENGTH	80
+
+#endif /* __LINUX_IPMI_MSGDEFS_H */
diff -urN linux.orig/include/linux/ipmi_smi.h linux/include/linux/ipmi_smi.h
--- linux.orig/include/linux/ipmi_smi.h	Wed Dec 31 18:00:00 1969
+++ linux/include/linux/ipmi_smi.h	Mon Jan 13 10:23:45 2003
@@ -0,0 +1,144 @@
+/*
+ * ipmi_smi.h
+ *
+ * MontaVista IPMI system management interface
+ *
+ * Author: MontaVista Software, Inc.
+ *         Corey Minyard <minyard@mvista.com>
+ *         source@mvista.com
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ *  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.
+ *
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_IPMI_SMI_H
+#define __LINUX_IPMI_SMI_H
+
+#include <linux/ipmi_msgdefs.h>
+
+/* This files describes the interface for IPMI system management interface
+   drivers to bind into the IPMI message handler. */
+
+/* Structure for the low-level drivers. */
+typedef struct ipmi_smi *ipmi_smi_t;
+
+/*
+ * Messages to/from the lower layer.  The smi interface will take one
+ * of these to send. After the send has occurred and a response has
+ * been received, it will report this same data structure back up to
+ * the upper layer.  If an error occurs, it should fill in the
+ * response with an error code in the completion code location. When
+ * asyncronous data is received, one of these is allocated, the
+ * data_size is set to zero and the response holds the data from the
+ * get message or get event command that the interface initiated.
+ * Note that it is the interfaces responsibility to detect
+ * asynchronous data and messages and request them from the
+ * interface.
+ */
+struct ipmi_smi_msg
+{
+	struct list_head link;
+
+	long    msgid;
+	void    *user_data;
+
+	/* If 0, add to the end of the queue.  If 1, add to the beginning. */
+	int     prio;
+
+	int           data_size;
+	unsigned char data[IPMI_MAX_MSG_LENGTH];
+
+	int           rsp_size;
+	unsigned char rsp[IPMI_MAX_MSG_LENGTH];
+
+	/* Will be called when the system is done with the message
+           (presumably to free it). */
+	void (*done)(struct ipmi_smi_msg *msg);
+};
+
+struct ipmi_smi_handlers
+{
+	/* Called to enqueue an SMI message to be sent.  This
+	   operation is not allowed to fail.  If an error occurs, it
+	   should report back the error in a received message.  It may
+	   do this in the current call context, since no write locks
+	   are held when this is run.  If the priority is > 0, the
+	   message will go into a high-priority queue and be sent
+	   first.  Otherwise, it goes into a normal-priority queue. */
+	void (*sender)(void                *send_info,
+		       struct ipmi_smi_msg *msg,
+		       int                 priority);
+
+	/* Called by the upper layer to request that we try to get
+	   events from the BMC we are attached to. */
+	void (*request_events)(void *send_info);
+
+	/* Called when someone is using the interface, so the module can
+	   adjust it's use count.  Return zero if successful, or an
+	   errno if not. */
+	int (*new_user)(void *send_info);
+
+	/* Called when someone is no longer using the interface, so the
+	   module can adjust it's use count. */
+	void (*user_left)(void *send_info);
+
+	/* Called when the interface should go into "run to
+	   completion" mode.  If this call sets the value to true, the
+	   interface should make sure that all messages are flushed
+	   out and that none are pending, and any new requests are run
+	   to completion immediately. */
+	void (*set_run_to_completion)(void *send_info, int run_to_completion);
+};
+
+/* Add a low-level interface to the IPMI driver. */
+int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
+		      void                     *send_info,
+		      unsigned char            version_major,
+		      unsigned char            version_minor,
+		      ipmi_smi_t               *intf);
+
+/*
+ * Remove a low-level interface from the IPMI driver.  This will
+ * return an error if the interface is still in use by a user.
+ */
+int ipmi_unregister_smi(ipmi_smi_t intf);
+
+/*
+ * The lower layer reports received messages through this interface.
+ * The data_size should be zero if this is an asyncronous message.  If
+ * the lower layer gets an error sending a message, it should format
+ * an error response in the message response.
+ */
+void ipmi_smi_msg_received(ipmi_smi_t          intf,
+			   struct ipmi_smi_msg *msg);
+
+/* The lower layer received a watchdog pre-timeout on interface. */
+void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf);
+
+struct ipmi_smi_msg *ipmi_alloc_smi_msg(void);
+static inline void ipmi_free_smi_msg(struct ipmi_smi_msg *msg)
+{
+	msg->done(msg);
+}
+
+#endif /* __LINUX_IPMI_SMI_H */
--- linux.orig/kernel/ksyms.c	Fri Dec  6 09:12:07 2002
+++ linux/kernel/ksyms.c	Fri Dec  6 09:13:01 2002
@@ -65,6 +65,8 @@
 extern int request_dma(unsigned int dmanr, char * deviceID);
 extern void free_dma(unsigned int dmanr);
 extern spinlock_t dma_spin_lock;
+extern int panic_timeout;
+
 
 #ifdef CONFIG_MODVERSIONS
 const struct module_symbol __export_Using_Versions
@@ -467,6 +469,8 @@
 
 /* misc */
 EXPORT_SYMBOL(panic);
+EXPORT_SYMBOL(panic_notifier_list);
+EXPORT_SYMBOL(panic_timeout);
 EXPORT_SYMBOL(__out_of_line_bug);
 EXPORT_SYMBOL(sprintf);
 EXPORT_SYMBOL(snprintf);