--- linux-2.4.18-14orig/Documentation/Configure.help	2003-03-27 15:37:28.000000000 -0500
+++ linux-2.4.18-14/Documentation/Configure.help	2003-03-27 15:32:58.000000000 -0500
@@ -25590,6 +25590,17 @@
 CONFIG_IPMI_WATCHDOG
   This enables the IPMI watchdog timer.
 
+Emulate Radisys IPMI driver
+CONFIG_IPMI_EMULATE_RADISYS
+  This enables emulation of the Radisys IPMI device driver.
+
+Emulate Intel IBM driver
+CONFIG_IPMI_EMULATE_IMB
+  This enables emulation of the Intel IMB device driver.  Note that you
+  MUST have the IPMI watchdog timer enabled to use this.  This code
+  uses some of the watchdog code, but the dependency is not enforced
+  by config.
+
 #
 # A couple of things I keep forgetting:
 #   capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet,
--- linux-2.4.18-14orig/drivers/char/Config.in	2003-03-27 15:37:28.000000000 -0500
+++ linux-2.4.18-14/drivers/char/Config.in	2003-03-27 15:32:58.000000000 -0500
@@ -180,6 +180,8 @@
 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
+dep_tristate '  Emulate Radisys IPMI driver' CONFIG_IPMI_EMULATE_RADISYS $CONFIG_IPMI_HANDLER
+dep_tristate '  Emulate Intel IMB driver' CONFIG_IPMI_EMULATE_IMB $CONFIG_IPMI_WATCHDOG
 
 mainmenu_option next_comment
 comment 'Watchdog Cards'
--- linux-2.4.18-14orig/drivers/char/ipmi/Makefile	2003-03-27 15:37:28.000000000 -0500
+++ linux-2.4.18-14/drivers/char/ipmi/Makefile	2003-03-27 15:32:58.000000000 -0500
@@ -13,6 +13,8 @@
 obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
 obj-$(CONFIG_IPMI_KCS) += ipmi_kcs_drv.o
 obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
+obj-$(CONFIG_IPMI_EMULATE_RADISYS) += ipmi_radisys.o
+obj-$(CONFIG_IPMI_EMULATE_IMB) += ipmi_imb.o
 
 include $(TOPDIR)/Rules.make
 
--- /dev/null	2002-08-30 19:31:37.000000000 -0400
+++ linux-2.4.18-14/include/linux/ipmi_imb.h	2003-03-27 15:33:11.000000000 -0500
@@ -0,0 +1,144 @@
+/*
+ * ipmi_imb.h
+ *
+ * Intels IMB emulation on the 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_IMB_H
+#define __LINUX_IPMI_IMB_H
+
+typedef struct overlapped_s {
+    unsigned long Internal;
+    unsigned long InternalHigh;
+    unsigned long Offset;
+    unsigned long OffsetHigh;
+} overlapped_t;
+
+struct smi {
+    unsigned long smi_VersionNo;
+    unsigned long smi_Reserved1;
+    unsigned long smi_Reserved2;
+    void          *ntstatus;	      /* address of NT status block*/
+    void          *lpvInBuffer;       /* address of buffer for input data*/
+    unsigned long cbInBuffer;         /* size of input buffer*/
+    void          *lpvOutBuffer;      /* address of output buffer*/
+    unsigned long cbOutBuffer;        /* size of output buffer*/
+    unsigned long *lpcbBytesReturned; /* address of actual bytes of output*/
+    overlapped_t  *lpoOverlapped;     /* address of overlapped structure*/
+};
+
+
+#define MAX_IMB_PACKET_SIZE	33
+
+typedef struct {
+	unsigned char rsSa;
+	unsigned char cmd;
+	unsigned char netFn;
+	unsigned char rsLun;
+	unsigned char dataLength;
+	unsigned char data[1];
+} ImbRequest;
+
+typedef struct {
+	unsigned long flags;
+#define	      		NO_RESPONSE_EXPECTED	0x01
+
+	unsigned long timeOut;
+	ImbRequest    req;
+} ImbRequestBuffer;
+
+#define MIN_IMB_REQ_BUF_SIZE	13
+
+
+typedef struct {
+	unsigned char cCode;
+	unsigned char data[1];
+} ImbResponseBuffer;
+
+#define MIN_IMB_RESP_BUF_SIZE	1	// a buffer without any request data
+#define MAX_IMB_RESP_SIZE		(MIN_IMB_RESP_BUF_SIZE + MAX_IMB_RESPONSE_SIZE)
+
+#define MIN_IMB_RESPONSE_SIZE	7
+#define MAX_IMB_RESPONSE_SIZE	MAX_IMB_PACKET_SIZE
+
+typedef struct {
+	unsigned long timeOut;
+	unsigned long lastSeq;
+} ImbAsyncRequest;
+
+typedef struct {
+	unsigned long thisSeq;
+	unsigned char data[1];
+} ImbAsyncResponse;
+
+#define MIN_ASYNC_RESP_SIZE		sizeof(unsigned long)
+#define MAX_ASYNC_RESP_SIZE		(MIN_ASYNC_RESP_SIZE + MAX_IMB_PACKET_SIZE)
+
+#define STATUS_SUCCESS                  (0x00000000U)
+#define IMB_NO_ASYNC_MSG		((unsigned long)0xE0070012L)
+#define IMB_SEND_REQUEST_FAILED		((unsigned long)0xE0070013L)
+#define INVALID_ARGUMENTS		((unsigned long)0xE0070002L)
+
+
+#define FILE_DEVICE_IMB			0x00008010
+#define IOCTL_IMB_BASE			0x00000880
+
+#define CTL_CODE(DeviceType, Function, Method, Access)\
+		_IO(DeviceType & 0x00FF, Function & 0x00FF)
+
+#define FILE_DEVICE_IMB			0x00008010
+#define IOCTL_IMB_BASE			0x00000880
+#define METHOD_BUFFERED                 0
+#define FILE_ANY_ACCESS                 0
+
+
+typedef struct {
+	int	code;
+#define		SD_NO_ACTION				0
+#define		SD_RESET				1
+#define		SD_POWER_OFF				2
+
+	int	delayTime;  /* in units of 100 millisecond */
+} ShutdownCmdBuffer;
+
+
+/* BMC added parentheses around IOCTL_IMB_BASE + 2 */
+#define IOCTL_IMB_SEND_MESSAGE		CTL_CODE(FILE_DEVICE_IMB, (IOCTL_IMB_BASE + 2), METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_IMB_GET_ASYNC_MSG		CTL_CODE(FILE_DEVICE_IMB, (IOCTL_IMB_BASE + 8), METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_IMB_MAP_MEMORY		CTL_CODE(FILE_DEVICE_IMB, (IOCTL_IMB_BASE + 14),METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_IMB_UNMAP_MEMORY		CTL_CODE(FILE_DEVICE_IMB, (IOCTL_IMB_BASE + 16),METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_IMB_SHUTDOWN_CODE		CTL_CODE(FILE_DEVICE_IMB, (IOCTL_IMB_BASE + 18),METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_IMB_REGISTER_ASYNC_OBJ	CTL_CODE(FILE_DEVICE_IMB, (IOCTL_IMB_BASE + 24),METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_IMB_DEREGISTER_ASYNC_OBJ	CTL_CODE(FILE_DEVICE_IMB, (IOCTL_IMB_BASE + 26),METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_IMB_CHECK_EVENT		CTL_CODE(FILE_DEVICE_IMB, (IOCTL_IMB_BASE + 28),METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_IMB_POLL_ASYNC		CTL_CODE(FILE_DEVICE_IMB, (IOCTL_IMB_BASE + 20),METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+
+#endif /* __LINUX_IPMI_IMB_H */
--- /dev/null	2002-08-30 19:31:37.000000000 -0400
+++ linux-2.4.18-14/include/linux/ipmi_radisys.h	2003-03-27 15:33:11.000000000 -0500
@@ -0,0 +1,128 @@
+/*
+ * ipmi_radisys.h
+ *
+ * An emulation of the Radisys IPMI interface on top of the MontaVista
+ * 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_RADISYS_H
+#define __LINUX_IPMI_RADISYS_H
+
+/******************************************************************************
+ * This is the old IPMI interface defined by Radisys.  We are
+ * compliant with that.  Don't use it for new designs, though.
+ */
+#define IOCTL_IPMI_RCV          ( IPMI_IOC_MAGIC<<8 | 1 )
+#define IOCTL_IPMI_SEND         ( IPMI_IOC_MAGIC<<8 | 2 )
+#define IOCTL_IPMI_EVENT        ( IPMI_IOC_MAGIC<<8 | 3 )
+#define IOCTL_IPMI_REGISTER     ( IPMI_IOC_MAGIC<<8 | 4 )
+#define IOCTL_IPMI_UNREGISTER   ( IPMI_IOC_MAGIC<<8 | 5 )
+#define IOCTL_IPMI_CLEAR        ( IPMI_IOC_MAGIC<<8 | 9 )
+
+/* These don't seem to be implemented in the Radisys driver.
+#define IOCTL_IPMI_RESET_BMC    ( IPMI_IOC_MAGIC<<8 | 6 )
+#define IOCTL_IPMI_GET_BMC_ADDR ( IPMI_IOC_MAGIC<<8 | 7 )
+#define IOCTL_IPMI_SET_BMC_ADDR ( IPMI_IOC_MAGIC<<8 | 8 )
+*/
+
+/* 
+ * Network Function Codes 
+ */
+#define IPMI_NETFN_CHASSIS          0x00    /* Chassis - 0x00 << 2 */
+#define IPMI_NETFN_CHASSIS_RESP     0x04    /* Chassis - 0x01 << 2 */
+
+#define IPMI_NETFN_BRIDGE           0x08    /* Bridge - 0x02 << 2 */
+#define IPMI_NETFN_BRIDGE_RESP      0x0c    /* Bridge - 0x03 << 2 */
+
+#define IPMI_NETFN_SENSOR_EVT       0x10    /* Sensor/Event - 0x04 << 2 */
+#define IPMI_NETFN_SENSOR_EVT_RESP  0x14    /* Sensor/Event - 0x05 << 2 */
+
+#define IPMI_NETFN_APP              0x18    /* Application - 0x06 << 2 */
+#define IPMI_NETFN_APP_RESP         0x1c    /* Application - 0x07 << 2 */
+
+#define IPMI_NETFN_FIRMWARE         0x20    /* Firmware - 0x08 << 2 */
+#define IPMI_NETFN_FIRMWARE_RESP    0x24    /* Firmware - 0x09 << 2 */
+
+#define IPMI_NETFN_STORAGE          0x28    /* Storage - 0x0a << 2 */
+#define IPMI_NETFN_STORAGE_RESP     0x2c    /* Storage - 0x0b << 2 */
+
+#define IPMI_NETFN_OEM_1            0xC0    /* Storage - 0x30 << 2 */
+#define IPMI_NETFN_OEM_1_RESP       0xC4    /* Storage - 0x31 << 2 */
+
+/* there are 15 other OEM netfn pairs (OEM - 0x30-0x3f) */
+
+typedef struct _IPMI_LIST_ENTRY {
+   struct _IPMI_LIST_ENTRY * volatile Flink;
+   struct _IPMI_LIST_ENTRY * volatile Blink;
+} IPMI_LIST_ENTRY, *PIPMI_LIST_ENTRY;
+
+typedef struct IPMI_semaphore   IPMI_KSEMAPHORE;
+typedef struct IPMI_semaphore * IPMI_PKSEMAPHORE;
+
+/* IPMI Address structure */
+typedef struct _IPMI_ADDR {
+    unsigned char uchSlave;                /* Slave Address */
+    unsigned char uchLun;                  /* Logical Unit Number */
+} IPMI_ADDR, *PIPMI_ADDR;
+
+#define IPMI_MAX_MSG_SIZE	36
+
+/* IPMI Message Descriptor structure */
+typedef struct _IPMI_MSGDESC {
+                                         /************************************/
+                                         /* Device Driver Specific Elements  */
+                                         /************************************/
+    IPMI_LIST_ENTRY  Entry;                  /* Linked list element */
+    void             *pIRPacket;             /* Pointer to IRP object */
+    IPMI_PKSEMAPHORE pSema;                  /* Semaphore Object */
+    long             lTimeout;               /* Timeout value */
+                                         /************************************/
+                                         /* Shared elements                  */
+                                         /************************************/
+    unsigned char   auchBuffer[IPMI_MAX_MSG_SIZE]; /* Message buffer */
+    unsigned long   ulLength;               /* Length of message in bytes */
+    int             fDefer;                 /* TRUE - Defer I/O
+					       operation, doesn't seem
+					       to be used in the
+					       Radisys driver. */
+    IPMI_ADDR       Dest;                   /* Destination IPM Address */
+    unsigned char   uchNetFn;               /* Network Function */
+    unsigned char   uchCmd;                 /* Command */
+    unsigned char   uchSeq;                 /* Sequence Number */
+    unsigned char   uchComplete;            /* Completion Code */
+} IPMI_MSGDESC, *PIPMI_MSGDESC;
+
+/* Byte return codes for some things. */
+#define LOWLRC_SUCCESS              0x00 /* routine completed successfully */
+#define LOWLRC_ERROR                0xff /* routine did not complete */
+#define LOWLRC_INVALID_PARAMETERS   0xfe /* invalid parameters */
+#define LOWLRC_INVALID_REQUEST_DATA 0xfd /* invalid request data */
+
+#endif /* __LINUX_IPMI_RADISYS_H */
--- /dev/null	2002-08-30 19:31:37.000000000 -0400
+++ linux-2.4.18-14/drivers/char/ipmi/ipmi_imb.c	2003-03-27 15:32:59.000000000 -0500
@@ -0,0 +1,744 @@
+/*
+ * ipmi_imb.c
+ *
+ * Intel IMB emulation for 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/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 <linux/ipmi_imb.h>
+
+
+
+#define MAX_BUFFER_SIZE                 64
+#define BMC_SA				0x20
+
+struct priv_data
+{
+	/* This is for supporting the old Imb interface. */
+	ipmi_user_t       imb_user;
+	spinlock_t        imb_lock;
+
+	unsigned long     curr_msgid;
+
+	/* A list of responses in the queue. */
+	struct list_head  imb_waiting_rsps;
+
+	/* A list of things waiting for responses.  We wake them all up
+	   when a response comes in. */
+	wait_queue_head_t imb_waiting_rsp_rcvrs;
+
+	/* A list of commands that have come in. */
+	struct list_head  imb_waiting_cmds;
+
+	/* A list of thing waiting for commands.  We wake them all up
+	   when a command comes in. */
+	wait_queue_head_t imb_waiting_cmd_rcvrs;
+
+	/* The registered command receiver value.  This only allows someone
+	   with the "magic number" to issue commands. */
+	unsigned long     imb_cmd_receiver;
+
+	/* Is someone already waiting for a command?  The Imb driver
+	   only allows one waiter, this enforces that. */
+	int               imb_cmd_waiting;
+
+	/* A list of IPMI events waiting to be delivered.  (not that
+           the Imb driver calls incoming commands "events", this
+           variable is actual IPMI events, not incoming commands). */
+	struct list_head  imb_waiting_events;
+
+#define IMB_EVENT_QUEUE_LIMIT	16 /* Allow up to 16 events. */
+	/* The number of events in the event queue. */
+	unsigned int      imb_waiting_event_count;
+};
+
+static devfs_handle_t devfs_handle;
+
+
+/* We cheat and use a piece of the address as the timeout. */
+static long *imb_timeout(struct ipmi_recv_msg *msg)
+{
+	char *base = (char *) &(msg->addr);
+	base += sizeof(struct ipmi_ipmb_addr);
+	return (long *) base;
+}
+
+static void imb_msg_recv(struct ipmi_recv_msg *msg,
+			 void                 *data)
+{
+	struct priv_data *priv = data;
+	unsigned long    flags;
+
+	spin_lock_irqsave(&(priv->imb_lock), flags);
+	if (msg->recv_type == IPMI_RESPONSE_RECV_TYPE) {
+		*imb_timeout(msg) = 5000;
+		list_add_tail(&(msg->link), &(priv->imb_waiting_rsps));
+		wake_up_all(&(priv->imb_waiting_rsp_rcvrs));
+	} else if (msg->recv_type == IPMI_CMD_RECV_TYPE) {
+		*imb_timeout(msg) = 5000;
+		list_add_tail(&(msg->link), &(priv->imb_waiting_cmds));
+		wake_up_all(&(priv->imb_waiting_cmd_rcvrs));
+	} else if (msg->recv_type == IPMI_ASYNC_EVENT_RECV_TYPE) {
+		if (priv->imb_waiting_event_count > IMB_EVENT_QUEUE_LIMIT) {
+			ipmi_free_recv_msg(msg);
+		} else {
+			list_add_tail(&(msg->link),&(priv->imb_waiting_events));
+			(priv->imb_waiting_event_count)++;
+		}
+	} else {
+		ipmi_free_recv_msg(msg);
+	}
+	spin_unlock_irqrestore(&(priv->imb_lock), flags);
+}
+
+/* We emulate the event queue in the driver for the imb emulation. */
+static int imb_handle_event_request(struct priv_data     *priv,
+				    struct ipmi_recv_msg **rsp)
+{
+	struct list_head     *entry;
+	unsigned long        flags;
+	struct ipmi_recv_msg *msg = NULL;
+	int                  rv = 0;
+
+	spin_lock_irqsave(&(priv->imb_lock), flags);
+	if (list_empty(&(priv->imb_waiting_events))) {
+		/* Nothing in the event queue, just return an error. */
+		msg = ipmi_alloc_recv_msg();
+		if (msg == NULL) {
+			rv = -EAGAIN;
+			goto out_err;
+		}
+		msg->addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+		msg->addr.channel = IPMI_BMC_CHANNEL;
+		msg->msg.cmd = IPMI_READ_EVENT_MSG_BUFFER_CMD;
+		msg->msgid = 0;
+		msg->recv_type = IPMI_ASYNC_EVENT_RECV_TYPE;
+		msg->msg.netfn = IPMI_NETFN_APP_RESPONSE;
+		msg->msg.data = msg->msg_data;
+		msg->msg.data[0] = 0x80; /* Data not available. */
+		msg->msg.data_len = 1;
+	} else {
+		/* Pull an item from the event queue . */
+		entry = priv->imb_waiting_events.next;
+		list_del(entry);
+		msg = list_entry(entry, struct ipmi_recv_msg, link);
+		(priv->imb_waiting_event_count)--;
+	}
+
+	*rsp = msg;
+
+ out_err:
+	spin_unlock_irqrestore(&(priv->imb_lock), flags);
+	return rv;
+}
+
+static struct priv_data *ipmi_user;
+static unsigned int user_count = 0; /* How many users have this open. */
+static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED;
+
+static int ipmi_imb_open(struct inode *inode, struct file *file)
+{
+	int rv;
+
+	if (user_count == 0) {
+		rv = ipmi_register_all_cmd_rcvr(ipmi_user->imb_user);
+		if (rv)	{
+			return rv;
+		}
+	}
+
+	file->private_data = ipmi_user;
+	spin_lock(&dev_lock);
+	if (user_count == 0)
+		ipmi_set_gets_events(ipmi_user->imb_user, 1);
+	user_count++;
+	spin_unlock(&dev_lock);
+
+	return 0;
+}
+
+static int ipmi_imb_release(struct inode *inode, struct file *file)
+{
+	spin_lock(&dev_lock);
+	user_count--;
+	if (user_count == 0) {
+		ipmi_set_gets_events(ipmi_user->imb_user, 0);
+		ipmi_unregister_all_cmd_rcvr(ipmi_user->imb_user);
+	}
+	spin_unlock(&dev_lock);
+	return 0;
+}
+
+static unsigned char
+ipmb_checksum(unsigned char *data, int size)
+{
+	unsigned char csum = 0;
+	
+	for (; size > 0; size--, data++)
+		csum += *data;
+
+	return -csum;
+}
+
+extern void ipmi_delayed_shutdown(long delay, int power_off);
+
+static int ipmi_imb_ioctl(struct inode  *inode,
+			  struct file   *file,
+			  unsigned int  cmd,
+			  unsigned long data)
+{
+	struct priv_data *priv = file->private_data;
+	int              rv = -EINVAL;
+	struct smi       smi;
+	unsigned long    flags;
+
+	if (copy_from_user((caddr_t)&smi, (caddr_t)data, sizeof(smi))) {
+		return -EFAULT;
+	}
+	
+	switch(cmd) {
+	case IOCTL_IMB_POLL_ASYNC:
+		/*
+		 * No-op for this, the low-level driver polls.
+		 */
+		break;
+
+	case IOCTL_IMB_GET_ASYNC_MSG:
+	{
+		unsigned char        resp[MAX_ASYNC_RESP_SIZE];
+		struct ipmi_recv_msg *msg = NULL;
+		ImbAsyncResponse     *pAsyncResp = (ImbAsyncResponse *) resp;
+		unsigned long        length = 0;
+			
+		if (smi.cbInBuffer < sizeof(ImbAsyncRequest))
+			return -EINVAL;
+		if (smi.cbOutBuffer < MIN_ASYNC_RESP_SIZE)
+			return -EINVAL;
+
+		spin_lock_irqsave(&(priv->imb_lock), flags);
+
+		if (list_empty(&(priv->imb_waiting_cmds))) {
+			/* No command waiting, just return an error. */
+			rv = IMB_NO_ASYNC_MSG;
+		} else {
+			struct list_head *entry;
+
+			/* Got a command, pull it out and handle it. */
+			entry = priv->imb_waiting_cmds.next;
+			list_del(entry);
+			msg = list_entry(entry, struct ipmi_recv_msg, link);
+			rv = STATUS_SUCCESS;
+		}
+		spin_unlock_irqrestore(&(priv->imb_lock), flags);
+
+		if (msg != NULL) {
+			struct ipmi_ipmb_addr *ipmb_addr;
+
+			ipmb_addr = (struct ipmi_ipmb_addr *) &(msg->addr);
+			pAsyncResp->thisSeq = msg->msgid;
+			pAsyncResp->data[0] = IPMI_NETFN_APP_REQUEST << 2;
+			pAsyncResp->data[1] = IPMI_SEND_MSG_CMD;
+			pAsyncResp->data[2] = 0;
+			pAsyncResp->data[3] = msg->addr.channel;
+			pAsyncResp->data[4] = ((msg->msg.netfn << 2)
+					     | 2);
+			pAsyncResp->data[5]
+				= ipmb_checksum(&(pAsyncResp->data[3]),
+						2);
+			pAsyncResp->data[6] = ipmb_addr->slave_addr;
+			pAsyncResp->data[7] = ((msg->msgid << 2)
+					     | ipmb_addr->lun);
+			pAsyncResp->data[8] = msg->msg.cmd;
+
+			memcpy(&(pAsyncResp->data[9]),
+			       &(msg->msg.data[0]),
+			       msg->msg.data_len);
+			
+			length = msg->msg.data_len + MIN_ASYNC_RESP_SIZE;
+
+			ipmi_free_recv_msg(msg);
+
+			if (copy_to_user(smi.lpvOutBuffer, pAsyncResp, length))
+			{
+				return -EFAULT;
+			}
+		}
+
+		if (copy_to_user(smi.lpcbBytesReturned,
+				 &length,
+				 sizeof(length)))
+		{
+			return -EFAULT;
+		}
+		rv = 0;
+		break;
+	}
+		
+	case IOCTL_IMB_SEND_MESSAGE:
+	{
+		unsigned char        imbReqBuffer[MAX_IMB_RESPONSE_SIZE + 8];
+		unsigned char        imbRespBuffer[MAX_IMB_RESPONSE_SIZE + 8];
+		ImbRequestBuffer     *pImbReq=(ImbRequestBuffer *)imbReqBuffer;
+		ImbResponseBuffer    *pImbResp=(ImbResponseBuffer*)imbRespBuffer;
+		struct ipmi_addr     addr;
+		struct ipmi_msg      msg;
+		unsigned long        msgid;
+		struct ipmi_recv_msg *rsp;
+		unsigned long        length;
+		wait_queue_t         wait;
+		struct list_head     *entry;
+
+
+		if ((smi.cbInBuffer < MIN_IMB_REQ_BUF_SIZE)
+		    || (smi.cbOutBuffer < MIN_IMB_RESP_BUF_SIZE))
+		{
+			return -EINVAL;
+		}
+
+		if (smi.cbInBuffer > (MAX_IMB_RESPONSE_SIZE + 8)) {
+			/* Input buffer is too large */
+			return -EINVAL;
+		}
+
+		if (copy_from_user(pImbReq, smi.lpvInBuffer, smi.cbInBuffer)) {
+			return -EFAULT;
+		}
+		if ((pImbReq->req.dataLength + MIN_IMB_REQ_BUF_SIZE)
+		    > smi.cbInBuffer)
+		{
+			return -EINVAL;
+		}
+		if (pImbReq->req.dataLength > MAX_BUFFER_SIZE) {
+			return -EINVAL;
+		}
+
+		if (pImbReq->req.rsSa == BMC_SA) {
+			struct ipmi_system_interface_addr *smi_addr
+				= (struct ipmi_system_interface_addr *) &addr;
+
+			if ((pImbReq->req.netFn
+			     == (IPMI_NETFN_APP_REQUEST << 2))
+			    && (pImbReq->req.cmd
+				== IPMI_READ_EVENT_MSG_BUFFER_CMD))
+			{
+				/* The driver gets event messages
+                                   automatically, so we emulate
+                                   this. */
+				rv = imb_handle_event_request(priv, &rsp);
+				goto copy_resp;
+			} else {
+				smi_addr->addr_type
+				    = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+				smi_addr->channel = IPMI_BMC_CHANNEL;
+				smi_addr->lun = 0;
+			}
+		} else {
+			struct ipmi_ipmb_addr *ipmb_addr =
+				(struct ipmi_ipmb_addr *) &addr;
+			
+			ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
+			ipmb_addr->slave_addr = pImbReq->req.rsSa;
+			ipmb_addr->lun = pImbReq->req.rsLun;
+			ipmb_addr->channel = 0;
+		}
+
+		if (pImbReq->flags & NO_RESPONSE_EXPECTED) {
+			spin_lock(&priv->imb_lock);
+			msgid = priv->curr_msgid;
+			(priv->curr_msgid)++;
+			spin_unlock(&priv->imb_lock);
+		} else {
+			msgid = 0;
+		}
+
+		msg.netfn = pImbReq->req.netFn;
+		msg.cmd = pImbReq->req.cmd;
+		msg.data = pImbReq->req.data;
+		msg.data_len = pImbReq->req.dataLength;
+		rv = ipmi_request(priv->imb_user,
+				  &addr,
+				  msgid,
+				  &msg,
+				  0);
+		if (rv) {
+			rv = IMB_SEND_REQUEST_FAILED;
+			goto copy_resp;
+		}
+
+		if (pImbReq->flags & NO_RESPONSE_EXPECTED)
+			goto no_response;
+
+		/* Now wait for the response to come back. */
+		spin_lock_irqsave(&(priv->imb_lock), flags);
+		init_waitqueue_entry(&wait, current);
+		add_wait_queue(&(priv->imb_waiting_rsp_rcvrs),
+			       &wait);
+		for (;;) {
+			/* Check to see if it's there. */
+			if (!list_empty(&(priv->imb_waiting_rsps))) {
+				entry = priv->imb_waiting_rsps.next;
+				list_del(entry);
+				rsp = list_entry(entry,
+						 struct ipmi_recv_msg,
+						 link);
+				if (rsp->msgid != msgid) {
+					ipmi_free_recv_msg(rsp);
+					rsp = NULL;
+				} else {
+					break;
+				}
+			}
+
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (!signal_pending(current)) {
+				spin_unlock_irqrestore
+					(&(priv->imb_lock), flags);
+				schedule();
+				spin_lock_irqsave
+					(&(priv->imb_lock), flags);
+			} else {
+				rv = -ERESTARTSYS;
+				break;
+			}
+		}
+		remove_wait_queue(&(priv->imb_waiting_rsp_rcvrs),
+				  &wait);
+		spin_unlock_irqrestore(&(priv->imb_lock), flags);
+
+	copy_resp:
+		if (rsp != NULL) {
+			pImbResp->cCode = rsp->msg.data[0];
+			memcpy(pImbResp->data,
+			       rsp->msg.data+1,
+			       rsp->msg.data_len-1);
+			length = (rsp->msg.data_len - 1
+				  + MIN_IMB_RESP_BUF_SIZE);
+
+			if (copy_to_user(smi.lpvOutBuffer, pImbResp, length)) {
+				return -EFAULT;
+			}
+
+			if (copy_to_user(smi.lpcbBytesReturned,
+					 &length,
+					 sizeof(length)))
+			{
+				return -EFAULT;
+			}
+		}
+	no_response:
+		break;
+	}
+
+	case IOCTL_IMB_SHUTDOWN_CODE:
+	{
+		ShutdownCmdBuffer shutdownCmd;
+
+		if (copy_from_user(&shutdownCmd,
+				   smi.lpvInBuffer, 
+				   sizeof(ShutdownCmdBuffer)))
+		{
+			return -EFAULT;
+		}
+
+		if (smi.cbInBuffer < sizeof(ShutdownCmdBuffer))
+		{
+			return -EINVAL;
+		}
+
+		rv = 0;
+		switch (shutdownCmd.code) {
+		case SD_POWER_OFF:
+			ipmi_delayed_shutdown(shutdownCmd.delayTime / 10, 1);
+			break;
+
+		case SD_RESET:
+			ipmi_delayed_shutdown(shutdownCmd.delayTime / 10, 0);
+			break;
+
+		case SD_NO_ACTION:
+			break;
+
+		default:
+			rv = INVALID_ARGUMENTS;
+		}
+
+        }
+
+	case IOCTL_IMB_REGISTER_ASYNC_OBJ:
+		rv = STATUS_SUCCESS;
+		break;
+
+
+	case IOCTL_IMB_CHECK_EVENT:
+	{
+		wait_queue_t wait;
+
+		spin_lock_irqsave(&(priv->imb_lock), flags);
+		init_waitqueue_entry(&wait, current);
+		add_wait_queue(&(priv->imb_waiting_cmd_rcvrs),
+			       &wait);
+		while (!list_empty(&(priv->imb_waiting_cmds))) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (!signal_pending(current)) {
+				spin_unlock_irqrestore
+					(&(priv->imb_lock), flags);
+				schedule();
+				spin_lock_irqsave
+					(&(priv->imb_lock), flags);
+			} else {
+				rv = -ERESTARTSYS;
+				break;
+			}
+		}
+		remove_wait_queue(&(priv->imb_waiting_cmd_rcvrs),
+				  &wait);
+		spin_unlock_irqrestore(&(priv->imb_lock), flags);
+		rv = 0;
+		break;
+	}
+	}
+
+	return rv;
+}
+
+static int ipmi_imb_mmap(struct file *file, struct vm_area_struct *vma)
+{
+    off_t offset = vma->vm_pgoff << PAGE_SHIFT;
+
+    if (offset < 0)
+	    return -EINVAL;
+
+    if (remap_page_range(vma->vm_start, offset, 
+			 vma->vm_end - vma->vm_start,
+			 vma->vm_page_prot))
+    {
+	    return -EAGAIN;
+    }
+
+    /*vma->vm_inode = what_goes_here; */
+
+    return 0;
+
+}
+
+
+static struct file_operations ipmi_fops = {
+	owner:   THIS_MODULE,
+	ioctl:   ipmi_imb_ioctl,
+	open:    ipmi_imb_open,
+	release: ipmi_imb_release,
+	mmap:    ipmi_imb_mmap
+};
+
+static struct timer_list ipmi_imb_timer;
+
+/* Call every 100 ms. */
+#define IPMI_TIMEOUT_TIME	100
+#define IPMI_TIMEOUT_JIFFIES	(IPMI_TIMEOUT_TIME/(1000/HZ))
+
+static volatile int stop_operation = 0;
+static volatile int timer_stopped = 0;
+
+static void ipmi_imb_timeout(unsigned long data)
+{
+	struct list_head     *entry, *entry2;
+	struct priv_data     *priv = (struct priv_data *) data;
+	int                  timeout_period = IPMI_TIMEOUT_TIME;
+	struct ipmi_recv_msg *msg;
+
+	if (stop_operation) {
+		timer_stopped = 1;
+		return;
+	}
+
+	/* Now time out any messages in the Imb message queue. */
+	spin_lock(&(priv->imb_lock));
+	list_for_each_safe(entry, entry2, &(priv->imb_waiting_rsps)) {
+		long *timeout;
+		msg = list_entry(entry, struct ipmi_recv_msg, link);
+		timeout = imb_timeout(msg);
+		*timeout -= timeout_period;
+		if ((*timeout) <= 0) {
+			list_del(entry);
+			ipmi_free_recv_msg(msg);
+		}
+	}
+	list_for_each_safe(entry, entry2, &(priv->imb_waiting_cmds)) {
+		long *timeout;
+		msg = list_entry(entry, struct ipmi_recv_msg, link);
+		timeout = imb_timeout(msg);
+		*timeout -= timeout_period;
+		if ((*timeout) <= 0) {
+			list_del(entry);
+			ipmi_free_recv_msg(msg);
+		}
+	}
+	spin_unlock(&priv->imb_lock);
+
+	ipmi_imb_timer.expires += IPMI_TIMEOUT_JIFFIES;
+	add_timer(&ipmi_imb_timer);
+}
+
+#define DEVICE_NAME     "imb"
+
+static int ipmi_imb_major = 0;
+MODULE_PARM(ipmi_imb_major, "i");
+
+static struct ipmi_user_hndl ipmi_hndlrs =
+{
+	ipmi_recv_hndl : imb_msg_recv
+};
+
+static int init_ipmi_imb(void)
+{
+	int rv;
+
+	if (ipmi_imb_major < 0)
+		return -EINVAL;
+
+	ipmi_user = kmalloc(sizeof(*ipmi_user), GFP_KERNEL);
+	if (!ipmi_user) {
+		return -ENOMEM;
+	}
+
+	/* Create the Imb interface user. */
+	spin_lock_init(&(ipmi_user->imb_lock));
+	INIT_LIST_HEAD(&(ipmi_user->imb_waiting_rsps));
+	init_waitqueue_head(&(ipmi_user->imb_waiting_rsp_rcvrs));
+	INIT_LIST_HEAD(&(ipmi_user->imb_waiting_cmds));
+	init_waitqueue_head(&(ipmi_user->imb_waiting_cmd_rcvrs));
+	ipmi_user->imb_cmd_waiting = 0;
+	INIT_LIST_HEAD(&(ipmi_user->imb_waiting_events));
+
+	rv = ipmi_create_user(0,
+			      &ipmi_hndlrs,
+			      ipmi_user,
+			      &(ipmi_user->imb_user));
+	if (rv) {
+		kfree(ipmi_user);
+		ipmi_user = NULL;
+		return rv;
+	}
+
+	rv = register_chrdev(ipmi_imb_major, DEVICE_NAME, &ipmi_fops);
+	if (rv < 0) 
+	{
+		kfree(ipmi_user);
+		ipmi_user = NULL;
+		printk(KERN_ERR "ipmi: can't get major %d\n",
+		       ipmi_imb_major);
+		return rv;
+	}
+
+	if (ipmi_imb_major == 0) 
+	{
+		ipmi_imb_major = rv;
+	}
+
+	devfs_handle = devfs_register(NULL, DEVICE_NAME, DEVFS_FL_NONE,
+				      ipmi_imb_major, 0,
+				      S_IFCHR | S_IRUSR | S_IWUSR,
+				      &ipmi_fops, NULL);
+
+	ipmi_imb_timer.data = (long) ipmi_user;
+	ipmi_imb_timer.function = ipmi_imb_timeout;
+	ipmi_imb_timer.expires = jiffies + IPMI_TIMEOUT_JIFFIES;
+	add_timer(&ipmi_imb_timer);
+
+	printk(KERN_INFO "ipmi_imb: driver initialized at char major %d\n",
+	       ipmi_imb_major);
+
+	return 0;
+}
+
+#ifdef MODULE
+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 cleanup_ipmi_imb(void)
+{
+	devfs_unregister(devfs_handle);
+
+	/* 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);
+	}
+
+	ipmi_destroy_user(ipmi_user->imb_user);
+
+	free_recv_msg_list(&(ipmi_user->imb_waiting_rsps));
+	free_recv_msg_list(&(ipmi_user->imb_waiting_cmds));
+	free_recv_msg_list(&(ipmi_user->imb_waiting_events));
+
+	kfree(ipmi_user);
+	ipmi_user = NULL;
+
+	unregister_chrdev(ipmi_imb_major, DEVICE_NAME);
+}
+module_exit(cleanup_ipmi_imb);
+#else
+static int __init ipmi_imb_setup (char *str)
+{
+	int x;
+
+	if (get_option (&str, &x)) {
+		/* ipmi=x sets the major number to x. */
+		ipmi_imb_major = x;
+	} else if (!strcmp(str, "off")) {
+		ipmi_imb_major = -1;
+	}
+
+	return 1;
+}
+__setup("ipmi_imb=", ipmi_imb_setup);
+#endif
+
+module_init(init_ipmi_imb);
+MODULE_LICENSE("GPL");
--- /dev/null	2002-08-30 19:31:37.000000000 -0400
+++ linux-2.4.18-14/drivers/char/ipmi/ipmi_radisys.c	2003-03-27 15:32:59.000000000 -0500
@@ -0,0 +1,834 @@
+/*
+ * ipmi_radisys.c
+ *
+ * Radisys emulation for 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/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 <linux/ipmi_radisys.h>
+
+
+struct priv_data
+{
+	/* This is for supporting the old Radisys interface. */
+	ipmi_user_t       rs_user;
+	spinlock_t        rs_lock;
+
+	/* A list of responses in the queue. */
+	struct list_head  rs_waiting_rsps;
+
+	/* A list of things waiting for responses.  We wake them all up
+	   when a response comes in. */
+	wait_queue_head_t rs_waiting_rsp_rcvrs;
+
+	/* A list of commands that have come in. */
+	struct list_head  rs_waiting_cmds;
+
+	/* A list of thing waiting for commands.  We wake them all up
+	   when a command comes in. */
+	wait_queue_head_t rs_waiting_cmd_rcvrs;
+
+	/* The registered command receiver value.  This only allows someone
+	   with the "magic number" to issue commands. */
+	unsigned long     rs_cmd_receiver;
+
+	/* Is someone already waiting for a command?  The Radisys driver
+	   only allows one waiter, this enforces that. */
+	int               rs_cmd_waiting;
+
+	/* A list of IPMI events waiting to be delivered.  (not that
+           the Radisys driver calls incoming commands "events", this
+           variable is actual IPMI events, not incoming commands). */
+	struct list_head  rs_waiting_events;
+
+#define RS_EVENT_QUEUE_LIMIT	16 /* Allow up to 16 events. */
+	/* The number of events in the event queue. */
+	unsigned int      rs_waiting_event_count;
+};
+
+
+static devfs_handle_t devfs_handle;
+
+/* We cheat and use a piece of the address as the timeout. */
+static long *rs_timeout(struct ipmi_recv_msg *msg)
+{
+	char *base = (char *) &(msg->addr);
+	base += sizeof(struct ipmi_ipmb_addr);
+	return (long *) base;
+}
+
+static void rs_msg_recv(struct ipmi_recv_msg *msg,
+			void                 *data)
+{
+	struct priv_data *priv = data;
+	unsigned long    flags;
+
+	spin_lock_irqsave(&(priv->rs_lock), flags);
+	if (msg->recv_type == IPMI_RESPONSE_RECV_TYPE) {
+		*rs_timeout(msg) = 5000;
+		list_add_tail(&(msg->link), &(priv->rs_waiting_rsps));
+		wake_up_all(&(priv->rs_waiting_rsp_rcvrs));
+	} else if (msg->recv_type == IPMI_CMD_RECV_TYPE) {
+		*rs_timeout(msg) = 5000;
+		list_add_tail(&(msg->link), &(priv->rs_waiting_cmds));
+		wake_up_all(&(priv->rs_waiting_cmd_rcvrs));
+	} else if (msg->recv_type == IPMI_ASYNC_EVENT_RECV_TYPE) {
+		if (priv->rs_waiting_event_count > RS_EVENT_QUEUE_LIMIT) {
+			ipmi_free_recv_msg(msg);
+		} else {
+			list_add_tail(&(msg->link),&(priv->rs_waiting_events));
+			(priv->rs_waiting_event_count)++;
+		}
+	} else {
+		ipmi_free_recv_msg(msg);
+	}
+	spin_unlock_irqrestore(&(priv->rs_lock), flags);
+}
+
+/* We emulate the event queue in the driver for the Radisys emulation. */
+static int rs_handle_event_request(struct priv_data *priv)
+{
+	struct list_head     *entry;
+	unsigned long        flags;
+	struct ipmi_recv_msg *msg = NULL;
+	int                  rv = 0;
+
+	spin_lock_irqsave(&(priv->rs_lock), flags);
+	if (list_empty(&(priv->rs_waiting_events))) {
+		/* Nothing in the event queue, just return an error. */
+		msg = ipmi_alloc_recv_msg();
+		if (msg == NULL) {
+			rv = -EAGAIN;
+			goto out_err;
+		}
+		msg->addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+		msg->addr.channel = IPMI_BMC_CHANNEL;
+		msg->msg.cmd = IPMI_READ_EVENT_MSG_BUFFER_CMD;
+		msg->msgid = 0;
+		msg->recv_type = IPMI_ASYNC_EVENT_RECV_TYPE;
+		msg->msg.netfn = IPMI_NETFN_APP_RESPONSE;
+		msg->msg.data = msg->msg_data;
+		msg->msg.data[0] = 0x80; /* Data not available. */
+		msg->msg.data_len = 1;
+	} else {
+		/* Pull an item from the event queue . */
+		entry = priv->rs_waiting_events.next;
+		list_del(entry);
+		msg = list_entry(entry, struct ipmi_recv_msg, link);
+		(priv->rs_waiting_event_count)--;
+	}
+
+	/* Put the response into the list of waiting responses and
+           wake all the response waiters up. */
+	*rs_timeout(msg) = 5000;
+	list_add_tail(&(msg->link), &(priv->rs_waiting_rsps));
+	wake_up_all(&(priv->rs_waiting_rsp_rcvrs));
+
+ out_err:
+	spin_unlock_irqrestore(&(priv->rs_lock), flags);
+	return rv;
+}
+
+static struct ipmi_recv_msg *rs_find_in_list(struct list_head *q,
+					     unsigned char    slave_addr,
+					     unsigned char    lun,
+					     unsigned char    netfn,
+					     unsigned char    cmd,
+					     unsigned char    seq)
+{
+	struct list_head      *entry;
+	struct ipmi_recv_msg  *msg;
+	struct ipmi_addr      addr;
+	unsigned char         msg_seq;
+
+	if (slave_addr == 1) {
+		struct ipmi_system_interface_addr *smi_addr;
+		smi_addr = (struct ipmi_system_interface_addr *) &addr;
+		smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+		smi_addr->lun = lun;
+		/* Slave address 1 means no matching sequence in the
+                   Radisys driver. */
+	} else {
+		struct ipmi_ipmb_addr *ipmb_addr;
+		ipmb_addr = (struct ipmi_ipmb_addr *) &addr;
+		ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
+		ipmb_addr->slave_addr = slave_addr;
+		ipmb_addr->lun = lun;
+	}
+
+	list_for_each(entry, q) {
+		msg = list_entry(entry, struct ipmi_recv_msg, link);
+		if (msg->addr.channel == IPMI_BMC_CHANNEL)
+			msg_seq = 0;
+		else
+			msg_seq = msg->msgid;
+
+		/* We ignore the channel for these comparisons, since the
+		   Radisys driver seems to ignore it. */
+		addr.channel = msg->addr.channel;
+
+		if ((msg_seq == seq)
+		    && (msg->msg.cmd == cmd)
+		    && (msg->msg.netfn == (netfn >> 2))
+		    && ipmi_addr_equal(&addr, &(msg->addr)))
+		{
+			list_del(entry);
+			return msg;
+		}
+	}
+
+	return NULL;
+}
+
+static struct priv_data *ipmi_user;
+static unsigned int user_count = 0; /* How many users have this open. */
+static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED;
+
+static int ipmi_open(struct inode *inode, struct file *file)
+{
+	file->private_data = ipmi_user;
+	spin_lock(&dev_lock);
+	if (user_count == 0)
+		ipmi_set_gets_events(ipmi_user->rs_user, 1);
+	user_count++;
+	spin_unlock(&dev_lock);
+
+	return 0;
+}
+
+static int ipmi_release(struct inode *inode, struct file *file)
+{
+	spin_lock(&dev_lock);
+	user_count--;
+	if (user_count == 0)
+		ipmi_set_gets_events(ipmi_user->rs_user, 0);
+	spin_unlock(&dev_lock);
+	return 0;
+}
+
+static unsigned char
+ipmb_checksum(unsigned char *data, int size)
+{
+	unsigned char csum = 0;
+	
+	for (; size > 0; size--, data++)
+		csum += *data;
+
+	return -csum;
+}
+
+static int ipmi_ioctl(struct inode  *inode,
+		      struct file   *file,
+		      unsigned int  cmd,
+		      unsigned long data)
+{
+	struct priv_data *priv = file->private_data;
+	int              rv = -EINVAL;
+
+	switch(cmd) {
+	case IOCTL_IPMI_RCV:   /* get ipmi message */
+	{
+		IPMI_MSGDESC         rsp;
+		struct ipmi_recv_msg *msg;
+		unsigned long        flags;
+		long                 timeout;
+		wait_queue_t         wait;
+
+		if (copy_from_user(&rsp, (void *) data, sizeof(rsp))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		rv = 0;
+
+		spin_lock_irqsave(&(priv->rs_lock), flags);
+
+		msg = rs_find_in_list(&(priv->rs_waiting_rsps),
+				      rsp.Dest.uchSlave,
+				      rsp.Dest.uchLun,
+				      rsp.uchNetFn,
+				      rsp.uchCmd,
+				      rsp.uchSeq);
+		init_waitqueue_entry(&wait, current);
+		add_wait_queue(&(priv->rs_waiting_rsp_rcvrs),
+			       &wait);
+		timeout = 5000 / (1000 / HZ);
+		while (msg == NULL) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (!signal_pending(current)) {
+				spin_unlock_irqrestore
+					(&(priv->rs_lock), flags);
+				timeout = schedule_timeout(timeout);
+				spin_lock_irqsave
+					(&(priv->rs_lock), flags);
+			} else {
+				rv = -ERESTARTSYS;
+				break;
+			}
+			if (timeout <= 0) {
+				rsp.uchComplete = IPMI_TIMEOUT_COMPLETION_CODE;
+				break;
+			} else {
+				msg = rs_find_in_list
+					(&(priv->rs_waiting_rsps),
+					 rsp.Dest.uchSlave,
+					 rsp.Dest.uchLun,
+					 rsp.uchNetFn,
+					 rsp.uchCmd,
+					 rsp.uchSeq);
+			}
+		}
+		remove_wait_queue(&(priv->rs_waiting_rsp_rcvrs),
+				  &wait);
+		spin_unlock_irqrestore(&(priv->rs_lock), flags);
+
+		if (msg != NULL) {
+			rsp.uchComplete = msg->msg.data[0];
+			/* The Radisys driver expects all the data to
+			   be there in the data, even the stuff we
+			   already have processed for it.  So make is
+			   so. */
+			if (msg->addr.channel == IPMI_BMC_CHANNEL) {
+				struct ipmi_system_interface_addr *smi_addr;
+
+				smi_addr = ((struct ipmi_system_interface_addr *)
+					    &(msg->addr));
+				memcpy(&(rsp.auchBuffer[2]),
+				       &(msg->msg.data[0]),
+				       msg->msg.data_len);
+				rsp.ulLength = msg->msg.data_len+2;
+				rsp.auchBuffer[0] = ((msg->msg.netfn << 2)
+						     | (smi_addr->lun));
+				rsp.auchBuffer[1] = msg->msg.cmd;
+			} else {
+				struct ipmi_ipmb_addr *ipmb_addr;
+
+				ipmb_addr = (struct ipmi_ipmb_addr *) &msg->addr;
+				memcpy(&(rsp.auchBuffer[9]),
+				       &(msg->msg.data[0]),
+				       msg->msg.data_len);
+				rsp.ulLength = msg->msg.data_len+10;
+				rsp.auchBuffer[0] = IPMI_NETFN_APP_REQUEST << 2;
+				rsp.auchBuffer[1] = IPMI_GET_MSG_CMD;
+				rsp.auchBuffer[2] = 0;
+				rsp.auchBuffer[3] = msg->addr.channel;
+				rsp.auchBuffer[4] = ((msg->msg.netfn << 2)
+						     | 2);
+				rsp.auchBuffer[5]
+					= ipmb_checksum(&(rsp.auchBuffer[3]),
+							2);
+				rsp.auchBuffer[6] = ipmb_addr->slave_addr;
+				rsp.auchBuffer[7] = ((msg->msgid << 2)
+						     | ipmb_addr->lun);
+				rsp.auchBuffer[8] = msg->msg.cmd;
+				rsp.auchBuffer[msg->msg.data_len+9]
+					= ipmb_checksum(&(rsp.auchBuffer[6]),
+							msg->msg.data_len+3);
+			}
+			ipmi_free_recv_msg(msg);
+		}
+
+		if (copy_to_user((void *) data, &rsp, sizeof(rsp))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		break;
+	}
+     
+	case IOCTL_IPMI_SEND: /* send ipmi message */
+	{
+		IPMI_MSGDESC     req;
+		struct ipmi_addr addr;
+		struct ipmi_msg  msg;
+		unsigned char    source_address;
+		unsigned char    source_lun;
+		unsigned int     start_offset;
+
+		if (copy_from_user(&req, (void *) data, sizeof(req))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		if (req.Dest.uchSlave == 1) {
+			struct ipmi_system_interface_addr *smi_addr
+				= (struct ipmi_system_interface_addr *) &addr;
+			if ((req.uchNetFn == (IPMI_NETFN_APP_REQUEST << 2))
+			    && (req.uchCmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))
+			{
+				/* The driver gets event messages
+                                   automatically, so we emulate
+                                   this. */
+				rv = rs_handle_event_request(priv);
+				break;
+			}
+
+			smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+			smi_addr->channel = IPMI_BMC_CHANNEL;
+			smi_addr->lun = req.uchNetFn & 0x3;
+			source_address = 0;
+			source_lun = 0;
+			start_offset = 2;
+		} else {
+			struct ipmi_ipmb_addr *ipmb_addr =
+				(struct ipmi_ipmb_addr *) &addr;
+			
+			ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
+			ipmb_addr->channel = 0;
+			ipmb_addr->slave_addr = req.Dest.uchSlave;
+			ipmb_addr->lun = req.Dest.uchLun;
+			source_address = req.auchBuffer[6];
+			source_lun = req.auchBuffer[7] & 0x3;
+			start_offset = 9;
+			req.ulLength--; /* Remove the checksum the userland
+					   process adds. */
+		}
+
+		msg.netfn = req.uchNetFn >> 2;
+		msg.cmd = req.uchCmd;
+		msg.data = req.auchBuffer + start_offset;
+		msg.data_len = req.ulLength - start_offset;
+
+		rv = ipmi_request_with_source(priv->rs_user,
+					      &addr,
+					      req.uchSeq,
+					      &msg,
+					      0,
+					      source_address,
+					      source_lun);
+		if (rv)
+			req.uchComplete = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+		else
+			req.uchComplete = 0;
+		/* The Radisys driver does no error checking here. */
+		copy_to_user((void *) data, &req, sizeof(req));
+		rv = 0;
+		break;
+	}
+
+	case IOCTL_IPMI_EVENT:  /* get an incoming command.  Don't be
+                                   fooled by the name, these are
+                                   commands, not IPMI events. */
+	{
+		IPMI_MSGDESC         rsp;
+		struct ipmi_recv_msg *msg = NULL;
+		struct list_head     *entry;
+		unsigned long        flags;
+		long                 timeout;
+		unsigned long        receiver;
+		wait_queue_t         wait;
+
+		if (copy_from_user(&receiver, (void *) data, sizeof(receiver)))
+		{
+			rv = -EFAULT;
+			break;
+		}
+
+		if (copy_from_user(&timeout,
+				   (void *) (data + sizeof(receiver)),
+				   sizeof(timeout)))
+		{
+			rv = -EFAULT;
+			break;
+		}
+
+		rv = 0;
+
+		spin_lock_irqsave(&(priv->rs_lock), flags);
+
+		/* If someone else is already waiting, the Radisys driver
+		   returns EFAULT, so we do to. */
+		if (priv->rs_cmd_waiting) {
+			spin_unlock_irqrestore(&(priv->rs_lock), flags);
+			rv = -EFAULT;
+			break;
+		}
+
+		/* If the user thread doesn't match up, abort. */
+		if (receiver != priv->rs_cmd_receiver) {
+			spin_unlock_irqrestore(&(priv->rs_lock), flags);
+			rsp.uchComplete = IPMI_INVALID_CMD_COMPLETION_CODE;
+			break;
+		}
+
+		init_waitqueue_entry(&wait, current);
+		add_wait_queue(&(priv->rs_waiting_cmd_rcvrs),
+			       &wait);
+		priv->rs_cmd_waiting = 1;
+		timeout = timeout / (1000 / HZ); /* from ms to jiffies */
+		while ((timeout > 0) &&
+		       list_empty(&(priv->rs_waiting_cmds)))
+		{
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (!signal_pending(current)) {
+				spin_unlock_irqrestore
+					(&(priv->rs_lock), flags);
+				timeout = schedule_timeout(timeout);
+				spin_lock_irqsave
+					(&(priv->rs_lock), flags);
+			} else {
+				rv = -ERESTARTSYS;
+				break;
+			}
+		}
+		if (!list_empty(&(priv->rs_waiting_cmds))) {
+			entry = priv->rs_waiting_cmds.next;
+			list_del(entry);
+			msg = list_entry(entry, struct ipmi_recv_msg, link);
+		}
+		priv->rs_cmd_waiting = 0;
+		remove_wait_queue(&(priv->rs_waiting_cmd_rcvrs),
+				  &wait);
+		spin_unlock_irqrestore(&(priv->rs_lock), flags);
+
+		if (msg != NULL) {
+			/* The Radisys driver expects all the data to
+			   be there in the data, even the stuff we
+			   already have processed for it.  So make is
+			   so. */
+			struct ipmi_ipmb_addr *ipmb_addr;
+
+			ipmb_addr = (struct ipmi_ipmb_addr *) &msg->addr;
+			memcpy(&(rsp.auchBuffer[9]),
+			       &(msg->msg.data[0]),
+			       msg->msg.data_len);
+			rsp.ulLength = msg->msg.data_len+9;
+			rsp.auchBuffer[0] = IPMI_NETFN_APP_REQUEST << 2;
+			rsp.auchBuffer[1] = IPMI_SEND_MSG_CMD;
+			rsp.auchBuffer[2] = 0;
+			rsp.auchBuffer[3] = msg->addr.channel;
+			rsp.auchBuffer[4] = ((msg->msg.netfn << 2)
+					     | 2);
+			rsp.auchBuffer[5]
+				= ipmb_checksum(&(rsp.auchBuffer[3]),
+						2);
+			rsp.auchBuffer[6] = ipmb_addr->slave_addr;
+			rsp.auchBuffer[7] = ((msg->msgid << 2)
+					     | ipmb_addr->lun);
+			rsp.auchBuffer[8] = msg->msg.cmd;
+
+			rsp.uchNetFn = (msg->msg.netfn << 2);
+			rsp.uchCmd = msg->msg.cmd;
+			rsp.uchSeq = msg->msgid;
+			rsp.ulLength = msg->msg.data_len + 9;
+			ipmi_free_recv_msg(msg);
+		} else if (!rv) {
+			/* On a time out, the Radisys driver returns
+			   IPMIRC_ERROR in the completion code, for
+			   some wierd reason. */
+			rsp.uchComplete = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+		}
+
+		/* The Radisys driver does no error checking here. */
+		copy_to_user((void *) data, &rsp, sizeof(rsp));
+		rv = 0;
+		break;
+	}
+    
+	case IOCTL_IPMI_REGISTER: /* register as event receiver */
+	{
+		unsigned long receiver;
+		unsigned char rc = LOWLRC_SUCCESS;
+		unsigned long flags;
+
+		if (copy_from_user(&receiver, (void *) data, sizeof(receiver)))
+		{
+			rv = -EFAULT;
+			break;
+		}
+
+		spin_lock_irqsave(&(priv->rs_lock), flags);
+		if (priv->rs_cmd_receiver == 0) {
+			rv = ipmi_register_all_cmd_rcvr(priv->rs_user);
+			if (rv)	{
+				priv->rs_cmd_receiver = receiver;
+			} else {
+				rc = LOWLRC_ERROR;
+			}
+		} else if (priv->rs_cmd_receiver != receiver) {
+			rc = LOWLRC_ERROR;
+		}
+		spin_unlock_irqrestore(&(priv->rs_lock), flags);
+
+		/* The Radisys driver does no error checking here. */
+		copy_to_user((void *) data, &rc, sizeof(rc));
+		rv = 0;
+		break;
+	}
+    
+	case IOCTL_IPMI_UNREGISTER:   /* unregister as event receiver */
+	{
+		unsigned long receiver;
+		unsigned char rc = LOWLRC_SUCCESS;
+		unsigned long flags;
+
+		if (copy_from_user(&receiver, (void *) data, sizeof(receiver)))
+		{
+			rv = -EFAULT;
+			break;
+		}
+
+		spin_lock_irqsave(&(priv->rs_lock), flags);
+		if (priv->rs_cmd_receiver == receiver) {
+			ipmi_unregister_all_cmd_rcvr(priv->rs_user);
+			priv->rs_cmd_receiver = 0;
+		} else {
+			rc = LOWLRC_ERROR;
+		}
+		spin_unlock_irqrestore(&(priv->rs_lock), flags);
+
+		/* The Radisys driver does no error checking here. */
+		copy_to_user((void *) data, &rc, sizeof(rc));
+		rv = 0;
+		break;
+	}
+
+	case IOCTL_IPMI_CLEAR: /* clear registered event receiver */
+	{
+		unsigned char rc = LOWLRC_SUCCESS;
+		unsigned long flags;
+
+		spin_lock_irqsave(&(priv->rs_lock), flags);
+		ipmi_unregister_all_cmd_rcvr(priv->rs_user);
+		priv->rs_cmd_receiver = 0;
+		spin_unlock_irqrestore(&(priv->rs_lock), flags);
+
+		/* The Radisys driver does no error checking here. */
+		copy_to_user((void *) data, &rc, sizeof(rc));
+		rv = 0;
+		break;
+	}
+	}
+
+	return rv;
+}
+
+static struct file_operations ipmi_fops = {
+	owner:   THIS_MODULE,
+	ioctl:   ipmi_ioctl,
+	open:    ipmi_open,
+	release: ipmi_release
+};
+
+static struct timer_list ipmi_radisys_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 void ipmi_radisys_timeout(unsigned long data)
+{
+	struct list_head     *entry, *entry2;
+	struct priv_data     *priv = (struct priv_data *) data;
+	int                  timeout_period = IPMI_TIMEOUT_TIME;
+	struct ipmi_recv_msg *msg;
+
+	if (stop_operation) {
+		timer_stopped = 1;
+		return;
+	}
+
+	/* Now time out any messages in the Radisys message queue. */
+	spin_lock(&(priv->rs_lock));
+	list_for_each_safe(entry, entry2, &(priv->rs_waiting_rsps)) {
+		long *timeout;
+		msg = list_entry(entry, struct ipmi_recv_msg, link);
+		timeout = rs_timeout(msg);
+		*timeout -= timeout_period;
+		if ((*timeout) <= 0) {
+			list_del(entry);
+			ipmi_free_recv_msg(msg);
+		}
+	}
+	list_for_each_safe(entry, entry2, &(priv->rs_waiting_cmds)) {
+		long *timeout;
+		msg = list_entry(entry, struct ipmi_recv_msg, link);
+		timeout = rs_timeout(msg);
+		*timeout -= timeout_period;
+		if ((*timeout) <= 0) {
+			list_del(entry);
+			ipmi_free_recv_msg(msg);
+		}
+	}
+	spin_unlock(&priv->rs_lock);
+
+	ipmi_radisys_timer.expires += IPMI_TIMEOUT_JIFFIES;
+	add_timer(&ipmi_radisys_timer);
+}
+
+#define DEVICE_NAME     "ipmi_radisys"
+
+static int ipmi_radisys_major = 0;
+MODULE_PARM(ipmi_radisys_major, "i");
+
+static struct ipmi_user_hndl ipmi_hndlrs =
+{
+	ipmi_recv_hndl : rs_msg_recv
+};
+
+
+static int init_ipmi_radisys(void)
+{
+	int rv;
+
+	if (ipmi_radisys_major < 0)
+		return -EINVAL;
+
+	ipmi_user = kmalloc(sizeof(*ipmi_user), GFP_KERNEL);
+	if (!ipmi_user) {
+		printk("ipmi_radisys: Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	/* Create the Radisys interface user. */
+	spin_lock_init(&(ipmi_user->rs_lock));
+	INIT_LIST_HEAD(&(ipmi_user->rs_waiting_rsps));
+	init_waitqueue_head(&(ipmi_user->rs_waiting_rsp_rcvrs));
+	INIT_LIST_HEAD(&(ipmi_user->rs_waiting_cmds));
+	init_waitqueue_head(&(ipmi_user->rs_waiting_cmd_rcvrs));
+	ipmi_user->rs_cmd_waiting = 0;
+	INIT_LIST_HEAD(&(ipmi_user->rs_waiting_events));
+
+	rv = ipmi_create_user(0,
+			      &ipmi_hndlrs,
+			      ipmi_user,
+			      &(ipmi_user->rs_user));
+	if (rv) {
+		printk("ipmi_radisys: Unable to create an IPMI user, probably"
+		       " no physical devices present.\n");
+		kfree(ipmi_user);
+		ipmi_user = NULL;
+		return rv;
+	}
+
+	rv = register_chrdev(ipmi_radisys_major, DEVICE_NAME, &ipmi_fops);
+	if (rv < 0) 
+	{
+		printk("ipmi_radisys: Unable to create the character device\n");
+		kfree(ipmi_user);
+		ipmi_user = NULL;
+		printk(KERN_ERR "ipmi: can't get major %d\n",
+		       ipmi_radisys_major);
+		return rv;
+	}
+
+	if (ipmi_radisys_major == 0) 
+	{
+		ipmi_radisys_major = rv;
+	}
+
+	devfs_handle = devfs_register(NULL, DEVICE_NAME, DEVFS_FL_NONE,
+				      ipmi_radisys_major, 0,
+				      S_IFCHR | S_IRUSR | S_IWUSR,
+				      &ipmi_fops, NULL);
+
+	ipmi_radisys_timer.data = (long) ipmi_user;
+	ipmi_radisys_timer.function = ipmi_radisys_timeout;
+	ipmi_radisys_timer.expires = jiffies + IPMI_TIMEOUT_JIFFIES;
+	add_timer(&ipmi_radisys_timer);
+
+	printk(KERN_INFO "ipmi_radisys: driver initialized at char major %d\n",
+	       ipmi_radisys_major);
+
+	return 0;
+}
+
+#ifdef MODULE
+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 cleanup_ipmi_radisys(void)
+{
+	devfs_unregister(devfs_handle);
+
+	/* 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);
+	}
+
+	ipmi_destroy_user(ipmi_user->rs_user);
+
+	free_recv_msg_list(&(ipmi_user->rs_waiting_rsps));
+	free_recv_msg_list(&(ipmi_user->rs_waiting_cmds));
+	free_recv_msg_list(&(ipmi_user->rs_waiting_events));
+
+	kfree(ipmi_user);
+	ipmi_user = NULL;
+
+	unregister_chrdev(ipmi_radisys_major, DEVICE_NAME);
+}
+module_exit(cleanup_ipmi_radisys);
+#else
+static int __init ipmi_radisys_setup (char *str)
+{
+	int x;
+
+	if (get_option (&str, &x)) {
+		/* ipmi=x sets the major number to x. */
+		ipmi_radisys_major = x;
+	} else if (!strcmp(str, "off")) {
+		ipmi_radisys_major = -1;
+	}
+
+	return 1;
+}
+__setup("ipmi_radisys=", ipmi_radisys_setup);
+#endif
+
+module_init(init_ipmi_radisys);
+MODULE_LICENSE("GPL");
