diff options
Diffstat (limited to 'kern/openipmi-emu-rh80.patch')
-rw-r--r-- | kern/openipmi-emu-rh80.patch | 1904 |
1 files changed, 1904 insertions, 0 deletions
diff --git a/kern/openipmi-emu-rh80.patch b/kern/openipmi-emu-rh80.patch new file mode 100644 index 0000000..a7e46a0 --- /dev/null +++ b/kern/openipmi-emu-rh80.patch @@ -0,0 +1,1904 @@ +--- 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"); |