Index: oldkernel/linux/drivers/char/ipmi_kcs.c
diff -u linux/drivers/char/ipmi_kcs.c:1.1 linux/drivers/char/ipmi_kcs.c:1.2
--- linux/drivers/char/ipmi_kcs.c:1.1	Thu Jun  1 17:01:59 2000
+++ linux/drivers/char/ipmi_kcs.c	Mon Jul 10 16:23:53 2000
@@ -1,25 +1,25 @@
 /*
- *	Intelligent Platform Management Interface  driver for Linux 2.1.x
+ *  Intelligent Platform Management Interface driver for Linux 2.2.x
  *
- *	(c) Copyright 1999 San Mehat <nettwerk@valinux.com>,All Rights Reserved.
- *				http://www.valinux.com
+ *  (c) Copyright 2000 San Mehat <nettwerk@valinux.com>, All Rights Reserved.
+ *        http://www.valinux.com
  *
- *	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.
- *	
- *	Neither San Mehat nor VA Linux Systems admit liability nor provide 
- *	warranty for any of this software. This material is provided 
- *	"AS-IS" and at no charge.	
+ *  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.
+ *  
+ *  Neither San Mehat nor VA Linux Systems admit liability nor provide 
+ *  warranty for any of this software. This material is provided 
+ *  "AS-IS" and at no charge.  
  *
- *	(c) Copyright 1999    San Mehat <nettwerk@valinux.com>
+ *  (c) Copyright 1999    San Mehat <nettwerk@valinux.com>
  *
- *	Release 0.04.   - Initial Release
- *	
- *	Release 0.05.   - Fixed ring buffer bugs... better buffer handling
+ *  Release 0.04.   - Initial Release
+ *  
+ *  Release 0.05.   - Fixed ring buffer bugs... better buffer handling
  *
- *	Release 0.06.   - Changed polling freq to 1/10 sec
+ *  Release 0.06.   - Changed polling freq to 1/10 sec
  *
  *  Release 0.07.   - Integrated watchdog commands into IOCTL's and added
  *                    support for blinking front panel LED
@@ -38,6 +38,20 @@
  *
  *  Release 0.10.   - Modified kcs_write routine so once a write is complete
  *                    if the interface isn't in a 'READ STATE' it's okay.
+ *
+ *  Release 0.12.   - Added Intel Nightshade MB fixups since NS boards don't
+ *                    support pre-timeout NMI support
+ *                  - FRU download support added
+ *                  - /proc/ipmi created with fru data and driver status
+ *  Release 0.13.   - Added ioctl for setting asset tag
+ *                  - Fixed bug in /proc
+ *                  - Added asset tag max length field
+ *  Release 1.00    - Added intelligent proc reading so that asset tag is
+ *                    refreshed whenever /proc/ipmi is read
+ *                  - Code cleanup
+ *                  - When asset tag is set with data whoes size is < maximum,
+ *                    pad the rest out with NULLs
+ *                    
  */
 
 #include <linux/config.h>
@@ -61,6 +75,10 @@
 #include <linux/timer.h>
 #include <linux/ipmi_kcs_ioctls.h>
 
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
 /* function prototypes */
 static int kcs_send_message(unsigned char *buf, int length);
 static int kcs_read_message(int *msglen, unsigned char *buf);
@@ -69,7 +87,7 @@
 
 static int add_data_to_ringbuffer(unsigned char *data, int length);
 static int remove_data_from_ringbuffer(unsigned char *data, int length);
-static int watchdog_set(unsigned char args);
+static int watchdog_set(unsigned char args, int disable);
 static int watchdog_ping(void);
 static int panel_set(unsigned char state);
 static int read_sensor(struct sensor_request *sensor);
@@ -83,34 +101,93 @@
 static ssize_t kcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos);
 static long long kcs_llseek(struct file *file, long long offset, int origin);
 static int kcs_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-										 unsigned long arg);
+                     unsigned long arg);
+static int load_fru(void);
+static int refresh_asset(void);
+static void decode_string(unsigned char type,
+                          unsigned char language_code,
+                          unsigned char *source,
+                          char          *target,
+                          int           size);
+static int asset_set(unsigned char *tag, int len);
+static int add_sel_entry(struct sel_entry_request *req);
+static int watchdog_get(void);
+static int get_deviceid(void);
 
+#ifdef CONFIG_PROC_FS
+static int  ipmi_get_info(char *, char **, off_t, int, int);
+#endif
+
 /* static globals */
-static struct timer_list	poll_timer;
-static int 								kcs_is_open=0;
-static struct wait_queue	*wq = NULL;
-static int								head;
-static int								tail;
-static unsigned char			*buffer;
+static struct timer_list  poll_timer;
+static int                 kcs_is_open=0;
+static int                 internal_cmd = 0;
+static struct wait_queue  *wq = NULL;
+static int                head;
+static int                tail;
+static unsigned char      *buffer;
+static unsigned char      *fru_buffer;
+static int                driver_major = 1;
+static int                driver_minor = 0;
+
+#define BOARD_AREA_NUM_FIELDS  4
+#define BOARD_MANUFACTURER   0
+#define BOARD_PRODUCT_NAME   1
+#define BOARD_SERIAL         2
+#define BOARD_PART           3
+static char                board_area[BOARD_AREA_NUM_FIELDS][64];
+
+#define PRODUCT_AREA_NUM_FIELDS  6
+#define PRODUCT_MANUFACTURER   0
+#define PRODUCT_NAME           1
+#define PRODUCT_PART           2
+#define PRODUCT_VERSION        3
+#define PRODUCT_SERIAL         4
+#define PRODUCT_ASSET          5
+static char               product_area[PRODUCT_AREA_NUM_FIELDS][64];
+
+static int                    asset_offset = -1; 
+static int                    asset_length = 0;
+static char                   asset_lang = 0;
+
+static time_t                 watchdog_last_pet;
+static int                    watchdog_active = 0;
+static GET_WATCHDOG_RESPONSE  wd_status;
+static DEVICE_ID_RESPONSE     dev_id;
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry ipmi_proc_entry = {
+  0,
+  4,
+  "ipmi",
+  S_IFREG | S_IRUGO,
+  1,
+  0,
+  0,
+  0,
+  0,
+  ipmi_get_info
+  };
+#endif
 
 static struct file_operations kcs_fops = {
-	kcs_llseek,
-	kcs_read,
-	kcs_write,
-	NULL,		/* No Readdir */
-	NULL,		/* No Select */
-	kcs_ioctl,
-	NULL,		/* No mmap */
-	kcs_open,
-	NULL,		/* flush */
-	kcs_release
+  kcs_llseek,
+  kcs_read,
+  kcs_write,
+  NULL,    /* No Readdir */
+  NULL,    /* No Select */
+  kcs_ioctl,
+  NULL,    /* No mmap */
+  kcs_open,
+  NULL,    /* flush */
+  kcs_release
 };
 
 static struct miscdevice kcs_miscdev=
 {
-	IPMI_KCS_MINOR,
-	"ipmi_kcs",
-	&kcs_fops
+  IPMI_KCS_MINOR,
+  "ipmi_kcs",
+  &kcs_fops
 };
 
 #define POLL_FREQ ((HZ/10))
@@ -119,280 +196,733 @@
 
 static long long kcs_llseek(struct file *file, long long offset, int origin)
 {
-	return -ESPIPE;
+  return -ESPIPE;
 }
 
 static ssize_t kcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
-	unsigned char tmp_buffer[MAX_ISA_LENGTH];
-	int						rc;
+  unsigned char tmp_buffer[MAX_ISA_LENGTH];
+  int            rc;
 
-	if (!count)
-		return(0);
-	if (count > MAX_ISA_LENGTH)
-		return -EFBIG;
-	copy_from_user(&tmp_buffer[0], buf, count);
-	if ((rc = kcs_send_message(&tmp_buffer[0],count))<0)
-		{
-		printk("kcs_write(): Unable to send message\n");
-		return(rc);
-		}
-	return(0);
+  if (!count)
+    return(0);
+  if (count > MAX_ISA_LENGTH)
+    return -EFBIG;
+  copy_from_user(&tmp_buffer[0], buf, count);
+  if ((rc = kcs_send_message(&tmp_buffer[0],count))<0)
+    {
+    printk("[IPMI_KCS] Unable to send message\n");
+    return(rc);
+    }
+  return(0);
 }
 
 static ssize_t kcs_read(struct file *file, char *buf, size_t count, loff_t *ptr)
 {
-	unsigned char tmp_buffer[MAX_ISA_LENGTH];
-	int						rc;
+  unsigned char tmp_buffer[MAX_ISA_LENGTH];
+  int            rc;
 
-	/*  Can't seek (pread) on this device  */
-	if (ptr != &file->f_pos)
-		return -ESPIPE;
-
-	if (count > MAX_ISA_LENGTH)
-		count = MAX_ISA_LENGTH;
-
-	switch(MINOR(file->f_dentry->d_inode->i_rdev))
-		{
-		case IPMI_KCS_MINOR:
-			/* Check to see if theres any data to be read */
-			if (head == tail)
-				{
-				if (file->f_flags & O_NONBLOCK)
-					{
-					return -EAGAIN;
-					}
-				else
-					{
-					interruptible_sleep_on(&wq);
-					}
-				}
-			/* If we're here theres data to be read */
-			if ((rc = remove_data_from_ringbuffer(&tmp_buffer[0], count))<0)
-				{
-				printk("kcs_read(): ring buffer remove failure\n");
-				return -EIO;
-				}
-			copy_to_user(buf, &tmp_buffer[0], rc);
-			return(rc);
-			break;
-		default:
-			return -EINVAL;
-		}
+  /*  Can't seek (pread) on this device  */
+  if (ptr != &file->f_pos)
+    return -ESPIPE;
+
+  if (count > MAX_ISA_LENGTH)
+    count = MAX_ISA_LENGTH;
+
+  switch(MINOR(file->f_dentry->d_inode->i_rdev))
+    {
+    case IPMI_KCS_MINOR:
+      /* Check to see if theres any data to be read */
+      if (head == tail)
+        {
+        if (file->f_flags & O_NONBLOCK)
+          return -EAGAIN;
+        else
+          interruptible_sleep_on(&wq);
+        }
+      /* If we're here theres data to be read */
+      if ((rc = remove_data_from_ringbuffer(&tmp_buffer[0], count))<0)
+        {
+        printk("[IPMI_KCS] Ring buffer remove failure\n");
+        return -EIO;
+        }
+      copy_to_user(buf, &tmp_buffer[0], rc);
+      return(rc);
+      break;
+    default:
+      return -EINVAL;
+    }
 }
 
-static int kcs_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-	unsigned long arg)
+static int kcs_ioctl(struct inode  *inode,
+                     struct file   *file,
+                     unsigned int  cmd,
+                     unsigned long arg)
+{
+  unsigned char argument;
+
+  switch(cmd)
+    {
+    case IOCTL_WATCHDOG_SET:
+      if (!arg)
+        return(-EINVAL);
+      if (copy_from_user(&argument, (void *) arg, sizeof(unsigned char)))
+        return(-EFAULT);
+      return(watchdog_set(argument, 0));
+      break;
+    case IOCTL_WATCHDOG_PING:
+      if (arg)
+        return(-EINVAL);
+      return(watchdog_ping());
+      break;
+    case IOCTL_PANEL_LED_SET_BLINK:
+      if (!arg)
+        return(-EINVAL);
+      if (copy_from_user(&argument, (void *) arg, sizeof(unsigned char)))
+        return(-EFAULT);
+      if ((argument != 0x00) && (argument != 0x01))
+        return(-EINVAL);
+      return(panel_set(argument));
+      break;
+    case IOCTL_READ_SENSOR:
+      {
+      struct sensor_request  req;
+      int                    rc;
+
+      if (!arg)
+        return(-EINVAL);
+      if (copy_from_user(&req, (void *) arg, sizeof(req)))
+        return(-EFAULT);
+      rc = read_sensor(&req);
+      if (copy_to_user((struct sensor_request *)arg, &req, sizeof(req)))
+        return(-EFAULT);
+      return(rc);
+      }
+    case IOCTL_SET_ASSET_TAG:
+      {
+      struct asset_tag_request  req;
+      int                        rc;
+      char                      *tag;
+      int                        tag_len;
+
+      if (!arg)
+        return(-EINVAL);
+      if (copy_from_user(&req, (void *) arg, sizeof(req)))
+        return(-EFAULT);
+      tag_len = req.buffer_len;
+      if (tag_len <= 0)
+        return(-EFAULT);
+      if (tag_len > PAGE_SIZE)
+        return(-EOVERFLOW);
+      if (!(tag = kmalloc(PAGE_SIZE, GFP_KERNEL)))
+        return(-ENOMEM);
+      if (copy_from_user(tag, (void *) req.buffer, tag_len))
+        {
+        kfree(tag);
+        return(-EFAULT);
+        }
+      rc = asset_set(tag, tag_len);
+      kfree(tag);
+      return(rc);
+      }
+    case IOCTL_REFRESH_FRU:
+      {
+      int rc;
+
+      memset(board_area, 0, sizeof(board_area));
+      memset(product_area, 0, sizeof(product_area));
+      rc = load_fru();
+      return(rc);
+      }
+    case IOCTL_ADD_SEL_ENTRY:
+      {
+      struct sel_entry_request  req;
+      int                        rc;
+
+      if (!arg)
+        return(-EINVAL);
+      if (copy_from_user(&req, (void *) arg, sizeof(req)))
+        return(-EFAULT);
+
+      rc = add_sel_entry(&req);
+      return(rc);
+      }
+    default:
+      return -EINVAL;
+    }
+  return (0);
+}
+
+static int add_sel_entry(struct sel_entry_request *req)
 {
-	unsigned char argument;
+  ADD_SEL_CMD    cmd;
+  unsigned char  reply[MAX_ISA_LENGTH];
 
-	switch(cmd)
-		{
-		case IOCTL_WATCHDOG_SET:
-			if (!arg)
-				return(-EINVAL);
-			if (copy_from_user(&argument, (void *) arg, sizeof(unsigned char)))
-				return(-EFAULT);
-			return(watchdog_set(argument));
-			break;
-		case IOCTL_WATCHDOG_PING:
-			if (arg)
-				return(-EINVAL);
-			return(watchdog_ping());
-			break;
-		case IOCTL_PANEL_LED_SET_BLINK:
-			if (!arg)
-				return(-EINVAL);
-			if (copy_from_user(&argument, (void *) arg, sizeof(unsigned char)))
-				return(-EFAULT);
-			if ((argument != 0x00) && (argument != 0x01))
-				return(-EINVAL);
-			return(panel_set(argument));
-			break;
-		case IOCTL_READ_SENSOR:
-			{
-			struct sensor_request	req;
-			int										rc;
-
-			if (!arg)
-				return(-EINVAL);
-			if (copy_from_user(&req, (void *) arg, sizeof(req)))
-				return(-EFAULT);
-			rc = read_sensor(&req);
-			if (copy_to_user((struct sensor_request *)arg, &req, sizeof(req)))
-				return(-EFAULT);
-			return(rc);
-			}
-		default:
-			return -EINVAL;
-		}
-	return (0);
+  memset(&cmd, 0, sizeof(cmd));
+
+  cmd.lun = 0;
+  cmd.netfn = STORAGE_REQUEST;
+  cmd.cmd = CMD_ADD_SEL_ENTRY;
+  cmd.record_id = 0x1234;
+  cmd.timestamp = 0x1234;
+  cmd.mfid = VALINUX_MFID;
+  
+  memcpy(&cmd.data, &req->data[0], 6);
+  if (ipmi_kcs_dispatch_internal((void *) &cmd,
+                                sizeof(cmd),
+                                (void *)
+                                &reply)<0)
+    {
+    return(-EIO);
+    }
+  if (reply[2] != (unsigned char) 0x00)
+    {
+    printk("[IPMI_KCS] Command failed (rc = 0x%.2x)\n",
+            reply[2]);
+    return(-EIO);
+    }
+  return(0);
 }
 
 static int read_sensor(struct sensor_request *sensor)
 {
-	SENSOR_CMD		pkt;
+  SENSOR_CMD    pkt;
 
-	memset(&pkt, 0, sizeof(pkt));
-	pkt.netfn=0x04;
-	pkt.lun=0x00;
-	pkt.cmd=0x2d;
-	pkt.sensor_number=sensor->sensor_number;
-	
-	if ((sensor->result_length =ipmi_kcs_dispatch_internal((void *) &pkt,
-																			 sizeof(pkt), 
-																			(void *) &sensor->result_buffer[0]))<0)
-		{
-		printk("read_sensor(): IPMI command timed out for reply\n");
-		return -EIO;
-		}
-	if (sensor->result_buffer[2] != 0x00)
-		{
-		printk("read_sensor(): IPMI command failed (rc = 0x%.2x)\n",
-						sensor->result_buffer[2]);
-		return -EIO;
-		}
-	return(0);
-}
-
-static int watchdog_set(unsigned char args)
-{
-	IPMI_KCS_SET_WATCHDOG	pkt;
-	unsigned char					reply[MAX_ISA_LENGTH];
-
-	memset(&pkt, 0, sizeof(pkt));
-	pkt.netfn=0x06;
-	pkt.lun=0x0;
-	pkt.cmd=0x24;
-	pkt.timer_use = 0x04;
-
-	if (args & WATCHDOG_ACTION_REBOOT)
-		pkt.timeout_action = 0x03;
-	if (args & WATCHDOG_ACTION_NMI)
-		pkt.pre_irq = 0x02;
-
-	pkt.pretimeout_interval = 10;
-	pkt.tuefc_biosfrb2=0x00;
-	pkt.tuefc_biospost=0x0;
-	pkt.tuefc_osload=0x00;
-	pkt.tuefc_smsos=0x01;
-	pkt.initial_count = (30 * 10);
-	if (ipmi_kcs_dispatch_internal((void *) &pkt, sizeof(pkt), (void *) &reply)<0)
-		{
-		printk("watchdog_set(): IPMI command timed out for reply\n");
-		return(-EIO);
-		}
-	if (reply[2] != 0x00)
-		{
-		printk("watchdog_set(): IPMI command failed (rc = 0x%.2x)\n",reply[2]);
-		return(-EIO);
-		}
-	return(0);
+  memset(&pkt, 0, sizeof(pkt));
+  pkt.netfn=0x04;
+  pkt.lun=0x00;
+  pkt.cmd=0x2d;
+  pkt.sensor_number=sensor->sensor_number;
+  
+  if ((sensor->result_length =ipmi_kcs_dispatch_internal((void *) &pkt,
+                                       sizeof(pkt), 
+                                      (void *) &sensor->result_buffer[0]))<0)
+    {
+    return -EIO;
+    }
+  if (sensor->result_buffer[2] != 0x00)
+    {
+    printk("[IPMI_KCS] Command failed (rc = 0x%.2x)\n",
+            sensor->result_buffer[2]);
+    return -EIO;
+    }
+  return(0);
 }
 
-static int watchdog_ping(void)
+static int refresh_asset()
 {
-	IPMI_KCS_RESET_WATCHDOG	pkt;
-	unsigned char	reply[MAX_ISA_LENGTH];
+  FRU_DATA_REQ      data_req;
+  unsigned char      reply[MAX_ISA_LENGTH], num;
+  TL                *tl;
+  int                brtl;
+  int                fru_pos;
+
+  /* Get the type/length byte */
+  data_req.lun = 0;
+  data_req.netfn = STORAGE_REQUEST;
+  data_req.cmd = CMD_READ_FRU_INV_DATA;
+  data_req.offset = asset_offset;
+  data_req.count = 1;
+
+  internal_cmd = 1;
+  if (ipmi_kcs_dispatch_internal((void *) &data_req,
+                                sizeof(data_req),
+                                (void *)
+                                &reply)<0)
+    {
+    return(-EIO);
+    }
+  internal_cmd = 0;
+  if (reply[2] != 0x00)
+    {
+    printk("[IPMI_KCS] Unable to download TL (0x%.2x)\n",reply[2]);
+    return(-EIO);
+    }
+  if (reply[3] != 1)
+    {
+    printk("[IPMI_KCS] TL download size != 1 (%d)\n",reply[3]);
+    return(-EIO);
+    }
+  /*
+   * If the stored length != asset length
+   * the FRU has probably been externally reloaded..
+   * so reload the entire FRU
+   */
+  tl = (TL *) &reply[4];
+  if (tl->num_bytes != asset_length)
+    {
+    printk("[IPMI_KCS] TL size (%d) != asset size (%d), redownloading FRU\n",
+           tl->num_bytes,
+           asset_length);
+    load_fru();
+    return(0);
+    }
+  brtl = tl->num_bytes;
+  fru_pos = asset_offset+1; /* Skip the type length byte */
+  while(brtl)
+    {
+    data_req.lun = 0;
+    data_req.netfn = STORAGE_REQUEST;
+    data_req.cmd = CMD_READ_FRU_INV_DATA;
+    data_req.offset = fru_pos;
+    data_req.count = ((brtl < 16) ? brtl : 16);
+
+    internal_cmd = 1;
+    if (ipmi_kcs_dispatch_internal((void *) &data_req,
+                                  sizeof(data_req),
+                                  (void *)
+                                  &reply)<0)
+      {
+      return(-EIO);
+      }
+    internal_cmd = 0;
+    if (reply[2] != 0x00)
+      {
+      printk("[IPMI_KCS] Unable to refresh asset tag (0x%.2x)\n",reply[2]);
+      return(-EIO);
+      }
+    num = reply[3];
+    memcpy(&fru_buffer[fru_pos], &reply[4], num);
+    fru_pos += num;
+    brtl -= num;
+    }
+  tl = (TL *) &fru_buffer[asset_offset];
+  decode_string(tl->type_code,
+                asset_lang,
+                &fru_buffer[(asset_offset+1)], 
+                product_area[PRODUCT_ASSET],
+                tl->num_bytes);
+  product_area[PRODUCT_ASSET][tl->num_bytes] = 0;
+  return(0);
+}
 
-	memset(&pkt, 0, sizeof(pkt));
-	pkt.netfn = 0x06;
-	pkt.lun = 0x00;
-	pkt.cmd = 0x22;
-
-	if (ipmi_kcs_dispatch_internal((void *) &pkt, sizeof(pkt), (void *) &reply)<0)
-		{
-		printk("watchdog_ping(): IPMI command timed out for reply\n");
-		return(-EIO);
-		}
-	if (reply[2] != 0x00)
-		{
-		printk("watchdog_ping(): IPMI command failed (rc = 0x%.2x)\n",reply[2]);
-		return(-EIO);
-		}
-	return(0);
+static int load_fru()
+{
+  FRU_COMMON_HEADER  *cmn_hdr;
+  FRU_DATA_REQ      data_req;
+  KCS_GENERIC_CMD    inv_info_req;
+  FRU_AREA_INFO      *inv_info_resp;
+  unsigned char      reply[MAX_ISA_LENGTH], num, *p, lang;
+  int                i,fru_pos,brtl,fru_size;
+  TL                *tl;
+
+  memset(&inv_info_req, 0, sizeof(inv_info_req));
+  memset(&reply, 0, sizeof(reply));
+  inv_info_req.lun = 0;
+  inv_info_req.netfn = STORAGE_REQUEST;
+  inv_info_req.cmd = CMD_GET_FRU_INV_AREA_INFO;
+
+  internal_cmd = 1;
+  if (ipmi_kcs_dispatch_internal((void *) &inv_info_req,
+                                sizeof(inv_info_req),
+                                (void *)
+                                &reply)<0)
+    {
+    return(-EIO);
+    }
+  internal_cmd = 0;
+  inv_info_resp = (FRU_AREA_INFO *) &reply[2];
+
+#if 0
+  printk("[IPMI_KCS] FRU is %d bytes (mode %d)\n",
+         inv_info_resp->size,
+         inv_info_resp->accessmode);
+#endif
+  brtl = inv_info_resp->size;
+  fru_pos = 0;
+  fru_size = inv_info_resp->size;
+  if (fru_size > PAGE_SIZE)
+    {
+    printk("[IPMI_KCS] FRU too large\n");
+    return(-ENOBUFS);
+    }
+
+  data_req.lun = 0;
+  data_req.netfn = STORAGE_REQUEST;
+  data_req.cmd = CMD_READ_FRU_INV_DATA;
+  data_req.offset = 0;
+  data_req.count = 16;
+  while(brtl)
+    {
+#if 0
+    printk("Downloading fru hunk size %d offset %d\n",
+            data_req.count,
+            data_req.offset);
+#endif
+    internal_cmd = 1;
+    if (ipmi_kcs_dispatch_internal((void *) &data_req,
+                                  sizeof(data_req),
+                                  (void *)
+                                  &reply)<0)
+      {
+      return(-EIO);
+      }
+    internal_cmd = 0;
+    if (reply[2] != 0x00)
+      {
+      printk("[IPMI_KCS] Unable to download fru (0x%.2x)\n",reply[2]);
+      return(-EIO);
+      }
+    num = reply[3];
+
+    memcpy(&fru_buffer[fru_pos], &reply[4], num);
+
+    fru_pos += num;
+    brtl -= num;
+    if (brtl == 0)
+      break;
+    if (brtl <= 16)
+      data_req.count = brtl;
+    else
+      data_req.count = 16;
+    data_req.offset = fru_pos;
+    }
+
+  cmn_hdr = (FRU_COMMON_HEADER *) &fru_buffer[0];
+  if (cmn_hdr->board_area_offset)
+    {
+    p = &fru_buffer[(cmn_hdr->board_area_offset * 8)];
+    lang = p[2];
+    p +=6;
+    for (i=0; i < BOARD_AREA_NUM_FIELDS; i++)
+      {
+      tl = (TL *) p;
+      p++;
+      decode_string(tl->type_code, lang, p, board_area[i], tl->num_bytes);
+      board_area[i][tl->num_bytes] = 0;
+      p += tl->num_bytes;
+      }
+    }
+  if (cmn_hdr->product_info_offset)
+    {
+    p = &fru_buffer[(cmn_hdr->product_info_offset * 8)];
+    lang = p[2];
+    p +=3;
+    
+    for (i=0; i < PRODUCT_AREA_NUM_FIELDS; i++)
+      {
+      tl = (TL *) p;
+      if (i == PRODUCT_ASSET)
+        {
+        asset_offset = (p - fru_buffer);
+        asset_length = tl->num_bytes;
+        asset_lang = lang;
+        }
+      p++;
+      decode_string(tl->type_code, lang, p, product_area[i], tl->num_bytes);
+      product_area[i][tl->num_bytes] = 0;
+      p += tl->num_bytes;
+      }
+    }
+#if 0
+  for (i=0; i < BOARD_AREA_NUM_FIELDS; i++)
+    printk("Field %d = %s\n",i,board_area[i]);
+  for (i=0; i < PRODUCT_AREA_NUM_FIELDS; i++)
+    printk("Field %d = %s\n",i,product_area[i]);
+#endif
+  return(0);
+}
+
+static int asset_set(unsigned char *tag, int len)
+{
+  unsigned short  *fru_write_offset;
+  KCS_GENERIC_CMD  *cmd;
+  TL              *tl;
+  unsigned char    *write_buffer;
+  int              write_len;
+  int              bytes_reserved;
+  unsigned char    reply[MAX_ISA_LENGTH];
+
+  if (asset_offset < 0)
+    return(-ELIBBAD);
+  tl = (TL *) &fru_buffer[asset_offset];
+  bytes_reserved = tl->num_bytes;
+  if (len > bytes_reserved)
+    return(-EOVERFLOW);
+  write_len = bytes_reserved + 
+              sizeof(unsigned short) + 
+              sizeof(TL) +
+              sizeof(KCS_GENERIC_CMD);
+  if (write_len > PAGE_SIZE)
+    return(-EOVERFLOW);
+
+  if (!(write_buffer= kmalloc(PAGE_SIZE, GFP_KERNEL)))
+    return(-ENOMEM);
+
+  cmd = (KCS_GENERIC_CMD *) &write_buffer[0];
+  fru_write_offset = (unsigned short *) &write_buffer[2];
+  tl = (TL *) &write_buffer[4];
+
+  cmd->lun = 0x0;
+  cmd->netfn = STORAGE_REQUEST;
+  cmd->cmd = CMD_WRITE_FRU_INV_DATA;
+
+  *fru_write_offset= asset_offset;
+
+  tl->type_code= 0x03;
+  tl->num_bytes = bytes_reserved;
+
+  memset(&write_buffer[5], 0, bytes_reserved);
+  memcpy(&write_buffer[5], tag, len);
+
+  internal_cmd = 1;
+  if (ipmi_kcs_dispatch_internal((void *) &write_buffer[0],
+                                write_len,
+                                (void *)
+                                &reply)<0)
+    {
+    return(-EIO);
+    }
+  internal_cmd = 0;
+  if (reply[2] != 0x00)
+    {
+    printk("[IPMI_KCS] Unable to set asset tag (0x%.2x)\n",reply[2]);
+    return(-EIO);
+    }
+  memset(board_area, 0, sizeof(board_area));
+  memset(product_area, 0, sizeof(product_area));
+  load_fru();
+  return(0);
+}
+
+static int get_deviceid()
+{
+  KCS_GENERIC_CMD      cmd;
+  unsigned char        reply[MAX_ISA_LENGTH];
+  DEVICE_ID_RESPONSE  *resp;
+
+  memset(&cmd, 0, sizeof(cmd));
+  memset(reply, 0, sizeof(reply));
+
+  cmd.lun = 0;
+  cmd.netfn = APP_REQUEST;
+  cmd.cmd = CMD_GET_DEVICE_ID;
+
+  internal_cmd = 1;
+  if (ipmi_kcs_dispatch_internal((void *) &cmd,
+                                sizeof(cmd),
+                                (void *)
+                                &reply[0])<0)
+    {
+    internal_cmd = 0;
+    return(-EIO);
+    }
+  internal_cmd = 0;
+
+  if (reply[2] != 0x00)
+    {
+    printk("[IPMI_KCS] GET_DEVICE_ID command failed (0x%x)\n",reply[2]);
+    return(-EIO);
+    }
+  resp = (DEVICE_ID_RESPONSE *) &reply[2];
+  memcpy(&dev_id, resp, sizeof(DEVICE_ID_RESPONSE));
+  return(0);
 }
 
+static int watchdog_get()
+{
+  KCS_GENERIC_CMD        cmd;
+  unsigned char          reply[MAX_ISA_LENGTH];
+  GET_WATCHDOG_RESPONSE  *resp;
+
+  memset(&cmd, 0, sizeof(cmd));
+  memset(reply, 0, sizeof(reply));
+
+  cmd.lun = 0;
+  cmd.netfn = APP_REQUEST;
+  cmd.cmd = CMD_WATCHDOG_GET;
+
+  internal_cmd = 1;
+  if (ipmi_kcs_dispatch_internal((void *) &cmd,
+                                sizeof(cmd),
+                                (void *)
+                                &reply)<0)
+    {
+    internal_cmd = 0;
+    return(-EIO);
+    }
+  internal_cmd = 0;
+  if (reply[2] != 0x00)
+    {
+    printk("[IPMI_KCS] GET_WATCHDOG command failed (0x%x)\n",reply[2]);
+    return(-EIO);
+    }
+  resp = (GET_WATCHDOG_RESPONSE *) &reply[2];
+  memcpy(&wd_status, resp, sizeof(GET_WATCHDOG_RESPONSE));
+  return(0);
+}
+
+static int watchdog_set(unsigned char args, int disable)
+{
+  IPMI_KCS_SET_WATCHDOG  pkt;
+  unsigned char          reply[MAX_ISA_LENGTH];
+  int                    ns_fixup = 0;
+
+  memset(&pkt, 0, sizeof(pkt));
+  pkt.netfn=APP_REQUEST;
+  pkt.lun=0x0;
+  pkt.cmd=CMD_WATCHDOG_SET;
+  pkt.timer_use = 0x04;
+
+  if (args & WATCHDOG_ACTION_REBOOT)
+    pkt.timeout_action = 0x03;
+  if (args & WATCHDOG_ACTION_NMI)
+    pkt.pre_irq = 0x02;
+
+  if (disable)
+    {
+    pkt.timeout_action = 0x00;
+    pkt.pre_irq = 0x00;
+    }
+  pkt.pretimeout_interval = 10;
+  pkt.tuefc_biosfrb2=0x00;
+  pkt.tuefc_biospost=0x0;
+  pkt.tuefc_osload=0x00;
+  pkt.tuefc_smsos=0x01;
+  pkt.initial_count = (30 * 10);
+  while(1)
+    {
+    if (disable)
+      internal_cmd = 1;
+    if (ipmi_kcs_dispatch_internal((void *) &pkt,
+                                  sizeof(pkt),
+                                  (void *)
+                                  &reply)<0)
+      {
+      internal_cmd = 0;
+      return(-EIO);
+      }
+    if (disable)
+      {
+      internal_cmd = 0;
+      if (reply[2] != (unsigned char) 0x00)
+        return(-EIO);
+      return(0);
+      }
+    if (reply[2] == (unsigned char) 0xcc)
+      {
+      ns_fixup++;
+      if (ns_fixup == 2)
+        {
+        printk("[IPMI KCS] Flakey NMI fixes failed\n");
+        return(-EIO);
+        }
+      printk("[IPMI KCS] Flakey NMI fixups enabled\n");
+      pkt.pretimeout_interval = 0;
+      pkt.pre_irq = 0;
+      }
+    else if (reply[2] == 0x00)
+      break;
+    else
+      {
+      printk("[IPMI_KCS] Command failed (rc = 0x%.2x)\n",reply[2]);
+      return(-EIO);
+      }
+    }
+  if (ns_fixup)
+    printk("[IPMI KCS] Flakey NMI fixups functional\n");
+  watchdog_active = 1;
+  return(0);
+}
+
+static int watchdog_ping(void)
+{
+  IPMI_KCS_RESET_WATCHDOG  pkt;
+  unsigned char            reply[MAX_ISA_LENGTH];
+  time_t                  now = CURRENT_TIME;
+
+  memset(&pkt, 0, sizeof(pkt));
+  pkt.netfn = APP_REQUEST;
+  pkt.lun = 0x00;
+  pkt.cmd = CMD_WATCHDOG_RESET;
+
+  if (ipmi_kcs_dispatch_internal((void *) &pkt, sizeof(pkt), (void *) &reply)<0)
+    {
+    return(-EIO);
+    }
+  if (reply[2] != 0x00)
+    {
+    printk("[IPMI_KCS] Command failed (rc = 0x%.2x)\n",reply[2]);
+    return(-EIO);
+    }
+  watchdog_last_pet = now;
+  return(0);
+}
+
 static int panel_set(unsigned char state)
 {
-	unsigned char	reply[MAX_ISA_LENGTH];
-	struct blinky_cmd
-		{
-		unsigned char	lun		:2;
-		unsigned char	netfn	:6;
-
-		unsigned char	cmd;
-		} pkt;
-
-	memset(&pkt, 0, sizeof(pkt));
-	pkt.netfn= 0x06;
-	pkt.lun= 0x00;
-	if (state == PANEL_LED_BLINK)
-		pkt.cmd= 0x55;
-	else
-		pkt.cmd= 0x56;
-
-	if (ipmi_kcs_dispatch_internal((void *) &pkt, sizeof(pkt), (void *) &reply)<0)
-		{
-		printk("panel_set(): IPMI command timed out for reply\n");
-		return(-EIO);
-		}
-	if (reply[2] != 0x00)
-		{
-		printk("panel_set(): IPMI command failed (rc = 0x%.2x)\n",reply[2]);
-		return(-EIO);
-		}
-	return(0);
+  unsigned char  reply[MAX_ISA_LENGTH];
+  struct blinky_cmd
+    {
+    unsigned char  lun    :2;
+    unsigned char  netfn  :6;
+
+    unsigned char  cmd;
+    } pkt;
+
+  memset(&pkt, 0, sizeof(pkt));
+  pkt.netfn= APP_REQUEST;
+  pkt.lun= 0x00;
+  if (state == PANEL_LED_BLINK)
+    pkt.cmd= 0x55;
+  else
+    pkt.cmd= 0x56;
+
+  if (ipmi_kcs_dispatch_internal((void *) &pkt, sizeof(pkt), (void *) &reply)<0)
+    {
+    return(-EIO);
+    }
+  if (reply[2] != 0x00)
+    {
+    printk("[IPMI_KCS] Command failed (rc = 0x%.2x)\n",reply[2]);
+    return(-EIO);
+    }
+  return(0);
 }
 
 static int ipmi_kcs_dispatch_internal(void *data, int size, void *reply)
 {
-	volatile unsigned char	status;
-	int											rc;
-	int											msglen;
-	unsigned int						jiffystart;
-	int											i;
-
-	if ((rc =kcs_send_message((unsigned char *) data, size))<0)
-		{
-		printk("[IPMI_KCS] Message dispatch failed. (rc %d)\n",rc);
-		return(-EIO);
-		}
-
-	interruptible_sleep_on(&wq);
-	if ((rc = remove_data_from_ringbuffer((unsigned char *) reply,
-																				MAX_ISA_LENGTH))<0)
-		{
-		printk("[IPMI_KCS] Ring buffer remove failure\n");
-		return -EIO;
-		}
-	return(rc);
+  int                      rc;
+
+  if ((rc =kcs_send_message((unsigned char *) data, size))<0)
+    {
+    printk("[IPMI_KCS] Message dispatch failed. (rc %d)\n",rc);
+    return(-EIO);
+    }
+  interruptible_sleep_on(&wq);
+  if ((rc = remove_data_from_ringbuffer((unsigned char *) reply,
+                                        MAX_ISA_LENGTH))<0)
+    {
+    printk("[IPMI_KCS] Ring buffer remove failure\n");
+    return -EIO;
+    }
+  return(rc);
 }
 
 static int kcs_open(struct inode *inode, struct file *file)
 {
-	switch(MINOR(inode->i_rdev))
-		{
-		case IPMI_KCS_MINOR:
-			{
-			if(kcs_is_open)
-				return -EBUSY;
-			MOD_INC_USE_COUNT;
-	 
-			kcs_is_open=1;
-			return 0;
-			}
-		default:
-			return -ENODEV;
-		}
+  switch(MINOR(inode->i_rdev))
+    {
+    case IPMI_KCS_MINOR:
+      {
+      if(kcs_is_open)
+        return -EBUSY;
+      MOD_INC_USE_COUNT;
+   
+      kcs_is_open=1;
+      return 0;
+      }
+    default:
+      return -ENODEV;
+    }
 }
 
 static int kcs_release(struct inode *inode, struct file *file)
 {
-	if(MINOR(inode->i_rdev)==IPMI_KCS_MINOR)
-		kcs_is_open=0;
-	MOD_DEC_USE_COUNT;
-	return 0;
+  if(MINOR(inode->i_rdev)==IPMI_KCS_MINOR)
+    kcs_is_open=0;
+  MOD_DEC_USE_COUNT;
+  return 0;
 }
 
 #ifdef MODULE
@@ -400,220 +930,411 @@
 
 __initfunc(int kcs_init(void))
 {
-	return(ipmi_kcs_init());
+  return(ipmi_kcs_init());
 }
 
 void cleanup_module(void)
 {
-	printk("[IPMI_KCS] Driver shutting down.\n");
-	kfree(buffer);
-	del_timer(&poll_timer);
-	misc_deregister(&kcs_miscdev);
-	release_region(IO,16);
+  printk("[IPMI_KCS] Driver shutting down.\n");
+#ifdef CONFIG_PROC_FS
+  proc_unregister(&proc_root, ipmi_proc_entry.low_ino);
+#endif
+  kfree(buffer);
+  kfree(fru_buffer);
+  del_timer(&poll_timer);
+  misc_deregister(&kcs_miscdev);
+  release_region(IO,16);
 }
 #endif
 
 static int add_data_to_ringbuffer(unsigned char *data, int length)
 {
-	int i;
-	int diff;
+  int i;
+  int diff;
 
-	/* Make sure we have room in the ringbuffer */
-	diff = tail - head;
-	if (diff <0)
-		diff = PAGE_SIZE - head + tail;
-	diff = PAGE_SIZE - diff;
-	if (length > diff)
-		{
-		printk("[IPMI_KCS] Not enough room in ringbuffer\n");
-		printk("[IPMI_KCS] len = %d, head = %d, tail = %d\n",length, head, tail);
-		return(-1); /* Not enough room */
-		}
-
-	for (i = 0; i < length; i++)
-		{
-		buffer[tail] = data[i];
-		tail++;
-		tail &= (PAGE_SIZE -1);
-		}
-	return(0);
+  /* Make sure we have room in the ringbuffer */
+  diff = tail - head;
+  if (diff <0)
+    diff = PAGE_SIZE - head + tail;
+  diff = PAGE_SIZE - diff;
+  if (length > diff)
+    {
+    printk("[IPMI_KCS] Not enough room in ringbuffer\n");
+    printk("[IPMI_KCS] len = %d, head = %d, tail = %d\n",length, head, tail);
+    return(-1); /* Not enough room */
+    }
+
+  for (i = 0; i < length; i++)
+    {
+    buffer[tail] = data[i];
+    tail++;
+    tail &= (PAGE_SIZE -1);
+    }
+  return(0);
 }
 
 static int remove_data_from_ringbuffer(unsigned char *data, int length)
 {
-	int i;
+  int i;
 
-	if (head == tail)
-		return(0); /* No data to be read */
+  if (head == tail)
+    return(0); /* No data to be read */
 
-	for (i = 0; i < length; i++)
-		{
-		*data++ = buffer[head++];
-		head &= (PAGE_SIZE -1);
-		if (head == tail)
-			{
-			i++;
-			return(i);
-			}
-		}
-	i++;
-	return(i);
+  for (i = 0; i < length; i++)
+    {
+    *data++ = buffer[head++];
+    head &= (PAGE_SIZE -1);
+    if (head == tail)
+      {
+      i++;
+      return(i);
+      }
+    }
+  i++;
+  return(i);
 }
 
 void ipmi_kcs_poll(unsigned long nothing)
 {
-	volatile unsigned char	status;
-	unsigned char						data[MAX_ISA_LENGTH];
-	int											rc;
-	int											msglen;
-
-	poll_timer.expires = jiffies + POLL_FREQ;
-	add_timer(&poll_timer);
-
-	status = inb_p(ISA_BMC_STATUS);
-	if ((!(status & ISA_SMS_MSG_FLAG)) && ((status & ISA_STATE_MASK) !=ISA_READ_STATE))
-		return;
-	/* Something in the queue */
-	if ((rc = kcs_read_message(&msglen, &data[0]))<0)
-		{
-		printk("ipmi_kcs_poll(): Read Message failed\n");
-		return;
-		}
-	if (!kcs_is_open)
-		return; /* Dont store it if nobody is listening.. */
-	if (add_data_to_ringbuffer(&data[0],msglen)<0)
-		{
-		printk("ipmi_kcs_poll(): ring buffer overflow. Dropping newest frame\n");
-		return;
-		}
-	wake_up_interruptible(&wq);
+  volatile unsigned char  status;
+  unsigned char            data[MAX_ISA_LENGTH];
+  int                      rc;
+  int                      msglen;
+
+  poll_timer.expires = jiffies + POLL_FREQ;
+  add_timer(&poll_timer);
+
+  status = inb_p(ISA_BMC_STATUS);
+  if ((!(status & ISA_SMS_MSG_FLAG)) && ((status & ISA_STATE_MASK) !=ISA_READ_STATE))
+    return;
+  /* Something in the queue */
+  if ((rc = kcs_read_message(&msglen, &data[0]))<0)
+    {
+    printk("[IPMI_KCS] Read Message failed\n");
+    return;
+    }
+  if ((!kcs_is_open) && (!internal_cmd))
+    return; /* Dont store it if nobody is listening.. */
+  if (add_data_to_ringbuffer(&data[0],msglen)<0)
+    {
+    printk("[IPMI_KCS] Ring buffer overflow. Dropping newest frame\n");
+    return;
+    }
+
+  wake_up_interruptible(&wq);
 }
 
 
 static int kcs_send_message(unsigned char *buf, int length)
 {
-	volatile unsigned char	chipstatus;
-	int 										status;
-	int											i;
-
-	if ((status = wait_while_ibf(ISA_TIMEOUT)) <0)
-		return(-1);
-
-	outb_p(ISA_WRITE_START, ISA_BMC_COMMAND);
-
-	if ((status = wait_while_ibf(ISA_TIMEOUT)) <0)
-		return(-2);
-
-	for (i=0; i <length -1; i++)
-		{
-		chipstatus = inb_p(ISA_BMC_STATUS);
-
-		if (chipstatus & ISA_OBF_FLAG)
-			{
-			printk("[IPMI_KCS] WARNING: Out of Band data flag set\n");
-			printk("[IPMI_KCS] Email nettwerk@valinux.com\n");
-			inb_p(ISA_BMC_DATA_IN);
-			}
-	
-		if ((chipstatus & ISA_STATE_MASK) != ISA_WRITE_STATE)
-			return(-3);
-		outb_p(buf[i], ISA_BMC_DATA_OUT);
-		if ((status = wait_while_ibf(ISA_TIMEOUT)) <0)
-			return(-4);
-		}
-	outb_p(ISA_WRITE_END, ISA_BMC_COMMAND);
-	if ((status = wait_while_ibf(ISA_TIMEOUT)) <0)
-		return(-5);
-	outb_p(buf[i], ISA_BMC_DATA_OUT);
-	if ((status = wait_while_ibf(ISA_TIMEOUT)) <0)
-		return(-6);
-	
-	chipstatus = inb_p(ISA_BMC_STATUS);
-	if ((chipstatus & ISA_STATE_MASK) ==ISA_WRITE_STATE)
-		return (-7);
-	return(0);
+  volatile unsigned char  chipstatus;
+  int                     status;
+  int                      i;
+
+  if ((status = wait_while_ibf(ISA_TIMEOUT)) <0)
+    return(-1);
+
+  outb_p(ISA_WRITE_START, ISA_BMC_COMMAND);
+
+  if ((status = wait_while_ibf(ISA_TIMEOUT)) <0)
+    return(-2);
+
+  for (i=0; i <length -1; i++)
+    {
+    chipstatus = inb_p(ISA_BMC_STATUS);
+
+    if (chipstatus & ISA_OBF_FLAG)
+      {
+      printk("[IPMI_KCS] WARNING: Out of Band data flag set\n");
+      printk("[IPMI_KCS] Email nettwerk@valinux.com\n");
+      inb_p(ISA_BMC_DATA_IN);
+      }
+  
+    if ((chipstatus & ISA_STATE_MASK) != ISA_WRITE_STATE)
+      return(-3);
+    outb_p(buf[i], ISA_BMC_DATA_OUT);
+    if ((status = wait_while_ibf(ISA_TIMEOUT)) <0)
+      return(-4);
+    }
+  outb_p(ISA_WRITE_END, ISA_BMC_COMMAND);
+  if ((status = wait_while_ibf(ISA_TIMEOUT)) <0)
+    return(-5);
+  outb_p(buf[i], ISA_BMC_DATA_OUT);
+  if ((status = wait_while_ibf(ISA_TIMEOUT)) <0)
+    return(-6);
+  
+  chipstatus = inb_p(ISA_BMC_STATUS);
+  if ((chipstatus & ISA_STATE_MASK) ==ISA_WRITE_STATE)
+    return (-7);
+  return(0);
 }
 
 static int kcs_read_message(int *msglen, unsigned char *buf)
 {
-	int											i;
-	unsigned char						state;
-	volatile unsigned char	status;
-
-	for (i=0; i < MAX_ISA_LENGTH; i++)
-		{
-		status = inb_p(ISA_BMC_STATUS);
-		while(!(status & ISA_OBF_FLAG))
-			{
-			status = inb_p(ISA_BMC_STATUS);
-			state = (unsigned char) (status & ISA_STATE_MASK);
-
-			if (state == ISA_ERROR_STATE)
-				{
-				printk("kcs_read_message(): BAD ISA state\n");
-				return (-EIO);
-				}
-			else if (state == ISA_IDLE_STATE)
-				{
-				*msglen = i;
-				return(0);
-				}
-			}
-		buf[i] = inb_p(ISA_BMC_DATA_IN);
-		if (wait_while_ibf(ISA_TIMEOUT))
-			{
-			printk("kcs_read_message(): ISA timeout in IBF\n");
-			return(-EIO);
-			}
-		outb_p(ISA_READ, ISA_BMC_DATA_OUT);
-		}
-	printk("kcs_read_message(): ISA Message overflow\n");
-	return (-EIO);
+  int                      i;
+  unsigned char            state;
+  volatile unsigned char  status;
+
+  for (i=0; i < MAX_ISA_LENGTH; i++)
+    {
+    status = inb_p(ISA_BMC_STATUS);
+    while(!(status & ISA_OBF_FLAG))
+      {
+      status = inb_p(ISA_BMC_STATUS);
+      state = (unsigned char) (status & ISA_STATE_MASK);
+
+      if (state == ISA_ERROR_STATE)
+        {
+        printk("[IPMI_KCS] BAD ISA state\n");
+        return (-EIO);
+        }
+      else if (state == ISA_IDLE_STATE)
+        {
+        *msglen = i;
+        return(0);
+        }
+      }
+    buf[i] = inb_p(ISA_BMC_DATA_IN);
+    if (wait_while_ibf(ISA_TIMEOUT))
+      {
+      printk("[IPMI_KCS] ISA timeout in IBF\n");
+      return(-EIO);
+      }
+    outb_p(ISA_READ, ISA_BMC_DATA_OUT);
+    }
+  printk("[IPMI_KCS} ISA Message overflow\n");
+  return (-EIO);
 }
 
 static int wait_while_ibf(int timeout)
 {
-	unsigned int master_timeout=5;
-	unsigned char status_byte;
+  unsigned int master_timeout=5;
+  unsigned char status_byte;
 
-	status_byte = (inb_p(ISA_BMC_STATUS) & 0xFF);
-	while(status_byte & ISA_IBF_FLAG)
-		{
-		master_timeout--;
-		if (!master_timeout)
-			return(-1);
-		udelay(100);
-		status_byte = (inb_p(ISA_BMC_STATUS) & 0xFF);
-		}
-	return(0);
+  status_byte = (inb_p(ISA_BMC_STATUS) & 0xFF);
+  while(status_byte & ISA_IBF_FLAG)
+    {
+    master_timeout--;
+    if (!master_timeout)
+      return(-1);
+    udelay(100);
+    status_byte = (inb_p(ISA_BMC_STATUS) & 0xFF);
+    }
+  return(0);
 }
 
 int ipmi_kcs_init()
+{
+  printk("IPMI KCS driver (San Mehat nettwerk@valinux.com) v%d.%d at io 0x%x\n",
+          driver_major,
+          driver_minor,
+          IO);
+  if (!(buffer = kmalloc(PAGE_SIZE, GFP_KERNEL)))
+    {
+    printk("--Unable to allocate 1 page ring buffer\n");
+    return(-ENOMEM);
+    }
+  if (!(fru_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL)))
+    {
+    printk("--Unable to allocate 1 page FRU buffer\n");
+    return(-ENOMEM);
+    }
+  printk("--Using %d byte ring buffer for incomming IPMI data\n",(int)PAGE_SIZE);
+  printk("--Using %d byte ring buffer for board FRU\n",(int)PAGE_SIZE);
+  printk("--Polling frequency set to %d ticks\n",POLL_FREQ);
+  request_region(IO, 16, "ipmi_kcs");
+  if ((inb_p(ISA_BMC_STATUS) == 0xFF) &&
+      (inb_p(ISA_BMC_DATA_IN) == 0xFF))
+    {
+    printk("--IPMI ISA window not present. Driver exiting\n");
+    release_region(IO,16);
+    kfree(buffer);
+    kfree(fru_buffer);
+    return(-ENXIO);
+    }
+  misc_register(&kcs_miscdev);
+  head = tail = 0;
+
+  /* Set up a periodic timer to check if theres unsolicited data to be read */
+  memset(&poll_timer, 0, sizeof(poll_timer));
+  poll_timer.function = ipmi_kcs_poll;
+  poll_timer.expires = jiffies + POLL_FREQ;
+  add_timer(&poll_timer);
+
+  memset(board_area, 0, sizeof(board_area));
+  memset(product_area, 0, sizeof(product_area));
+  memset(&wd_status, 0, sizeof(wd_status));
+  memset(&dev_id, 0, sizeof(dev_id));
+
+  if (watchdog_set(0x00, 1) == 0)
+    printk("--BMC watchdog explicitly disabled\n");
+  else
+    printk("--BMC watchdog disable failed\n");
+  printk("--FRU download commencing\n");
+  load_fru();
+  printk("--FRU download complete\n");
+
+#ifdef CONFIG_PROC_FS
+  proc_register(&proc_root, &ipmi_proc_entry);
+#endif
+  return(0);
+}
+#define STRING_DATA_TYPE_BINARY         0x00
+#define STRING_DATA_TYPE_BCD_PLUS       0x01
+#define STRING_DATA_TYPE_SIX_BIT_ASCII  0x02
+#define STRING_DATA_TYPE_LANG_DEPENDANT 0x03
+
+static void decode_string(unsigned char type,
+                          unsigned char language_code,
+                          unsigned char *source,
+                          char          *target,
+                          int           size)
+{ 
+  unsigned char *s = &source[0];
+  unsigned char *d = &target[0];
+  
+  memset(target, 0, size);
+  if (type == STRING_DATA_TYPE_BCD_PLUS)
+    {
+    while(size)
+      {
+      if ((*s >= (unsigned char) 0x00) && (*s <= (unsigned char) 0x09))
+        *d++ = (unsigned char) 0x30 + (unsigned char) *s++;
+      else
+        {
+        if (*s == (unsigned char) 0x0A)
+          *d++ = (unsigned char) ' ';
+        else if (*s == (unsigned char) 0x0B)
+          *d++ = (unsigned char) '-';
+        else if (*s == (unsigned char) 0x0C)
+          *d++ = (unsigned char) '.';
+        else if ((*s <= (unsigned char) 0x0D) && (*s <= (unsigned char) 0x0F))
+          {
+          *d++ = (unsigned char) '*';
+          }
+        s++;
+        }
+      size --;
+      }
+    }
+  else if (type == STRING_DATA_TYPE_SIX_BIT_ASCII)
+    {
+    printk("[IPMI_KCS] Six bit ASCII decode not supported\n");
+    }
+  else if (type == STRING_DATA_TYPE_LANG_DEPENDANT)
+    {
+    if ((language_code == 0x00) || (language_code == 0x25))
+      {
+      strncpy(target, source, size);
+      target[size] = 0x0;
+      }
+    else
+      {
+      printk("[IPMI_KCS] Language 0x%x dependant decode not supported\n",
+            language_code);
+      return;
+      }
+    }
+  else
+    {
+    printk("Unable to decode type 0x%.2x\n",type);
+    return;
+    }
+}
+
+#ifdef CONFIG_PROC_FS
+int ipmi_get_info(char *buf, char **start, off_t fpos, int length, int dummy)
 {
-	printk("IPMI KCS driver (San Mehat nettwerk@valinux.com) v0.10 at io 0x%x\n",IO);
-	if (!(buffer = kmalloc(PAGE_SIZE, GFP_KERNEL)))
-		{
-		printk("--Unable to allocate 1 page ring buffer\n");
-		return(-ENOMEM);
-		}
-	printk("--Using %d byte ring buffer for incomming IPMI data\n",PAGE_SIZE);
-	printk("--Polling frequency set to %d ticks\n",POLL_FREQ);
-	request_region(IO, 16, "ipmi_kcs");
-	if ((inb_p(ISA_BMC_STATUS) == 0xFF) &&
-			(inb_p(ISA_BMC_DATA_IN) == 0xFF))
-		{
-		printk("--IPMI ISA window not present. Driver exiting\n");
-		release_region(IO,16);
-		kfree(buffer);
-		return(-ENXIO);
-		}
-	misc_register(&kcs_miscdev);
-	head = tail = 0;
-
-	/* Set up a periodic timer to check if theres unsolicited data to be read */
-	memset(&poll_timer, 0, sizeof(poll_timer));
-	poll_timer.function = ipmi_kcs_poll;
-	poll_timer.expires = jiffies + POLL_FREQ;
-	add_timer(&poll_timer);
-	return(0);
+  char    *p;
+  char    nmi[64];
+  time_t  now = CURRENT_TIME;
+
+  p = buf;
+  p+= sprintf(p, "Driver Version\t: %d.%d\n",
+              driver_major,driver_minor);
+  if (get_deviceid()!= 0)
+    {
+    printk("[IPMI_KCS] Unable to get device ID\n");
+    memset(&dev_id, 0, sizeof(dev_id));
+    }
+  p+=sprintf(p, "BMC Version\t: %x.%x\n",
+            dev_id.major_firmware_revision,
+            dev_id.minor_firmware_revision);
+  p+=sprintf(p, "IPMI Version\t: %d.%d\n",
+            dev_id.ipmi_version_major,
+            dev_id.ipmi_version_minor);
+
+  p+=sprintf(p, "\n");
+
+  if (watchdog_get()!= 0)
+    {
+    printk("[IPMI_KCS] Unable to get watchdog status\n");
+    memset(&wd_status, 0, sizeof(wd_status));
+    }
+
+  if (wd_status.timer_status == 0x01)
+    p+= sprintf(p, "WD Timer Status\t\t: STARTED\n");
+  else if (watchdog_active)
+    p+= sprintf(p, "WD Timer Status\t\t: STARTED\n");
+  else
+    p+= sprintf(p, "WD Timer Status\t\t: STOPPED\n");
+
+  if (wd_status.timeout_act == 0x00)
+    p+= sprintf(p, "WD Timeout Action\t: NONE\n");
+  else if (wd_status.timeout_act == 0x01)
+    p+= sprintf(p, "WD Timeout Action\t: HARD RESET\n");
+  else if (wd_status.timeout_act == 0x02)
+    p+= sprintf(p, "WD Timeout Action\t: POWER DOWN\n");
+  else if (wd_status.timeout_act == 0x03)
+    p+= sprintf(p, "WD Timeout Action\t: POWER CYCLE\n");
+  else
+    p+= sprintf(p, "WD Timeout Action\t: UNKNOWN\n");
+
+  if (wd_status.pre_irq_act == 0x00)
+    p+= sprintf(p, "WD Pre-Timeout IRQ\t: NONE\n");
+  else if (wd_status.pre_irq_act == 0x01)
+    p+= sprintf(p, "WD Pre-Timeout IRQ\t: SMI\n");
+  else if (wd_status.pre_irq_act == 0x02)
+    p+= sprintf(p, "WD Pre-Timeout IRQ\t: NMI\n");
+  else
+    p+= sprintf(p, "WD Pre-Timeout IRQ\t: UNKNOWN\n");
+
+  p+= sprintf(p, "WD Last Pet\t\t: %d", (unsigned int) watchdog_last_pet);
+  if (watchdog_last_pet == 0)
+    p+= sprintf(p, " (Never)\n");
+  else
+    p+= sprintf(p, " (%d seconds ago)\n",(unsigned int)(now-watchdog_last_pet));
+  p+= sprintf(p, "WD countdown\t\t: %d\n", (unsigned int) wd_status.current_count);
+  p+= sprintf(p, "\n");
+
+  p+= sprintf(p, "Board Area Records:\n");
+  p+= sprintf(p, "Board Manuf.\t: %s\n",
+        (board_area[BOARD_MANUFACTURER][0] ? board_area[0] : "Not Available"));
+  p+= sprintf(p, "Board Prod Name\t: %s\n",
+        (board_area[BOARD_PRODUCT_NAME][0] ? board_area[1] : "Not Available"));
+  p+= sprintf(p, "Board Serial\t: %s\n",
+        (board_area[BOARD_SERIAL][0] ? board_area[2] : "Not Available"));
+  p+= sprintf(p, "Board Part\t: %s\n",
+        (board_area[BOARD_PART][0] ? board_area[3] : "Not Available"));
+  p+= sprintf(p, "\n");
+
+  p+= sprintf(p, "Product Area Records:\n");
+  p+= sprintf(p, "Product Manuf.\t: %s\n",
+      (product_area[PRODUCT_MANUFACTURER][0] ? product_area[0]:"Not Available"));
+  p+= sprintf(p, "Product Name\t: %s\n",
+      (product_area[PRODUCT_NAME][0] ? product_area[1] : "Not Available"));
+  p+= sprintf(p, "Product Part\t: %s\n",
+      (product_area[PRODUCT_PART][0] ? product_area[2] : "Not Available"));
+  p+= sprintf(p, "Product Version\t: %s\n",
+      (product_area[PRODUCT_VERSION][0] ? product_area[3] : "Not Available"));
+  p+= sprintf(p, "Product Serial\t: %s\n",
+      (product_area[PRODUCT_SERIAL][0] ? product_area[4] : "Not Available"));
+
+  refresh_asset();
+  p+= sprintf(p, "Product Asset\t: %s (%d byte maximum size)\n",
+      (product_area[PRODUCT_ASSET][0] ? product_area[5] : "Not Available"),
+      asset_length);
+
+  return p - buf;
 }
+#endif
Index: oldkernel/linux/drivers/char/ipmi_kcs.h
diff -u linux/drivers/char/ipmi_kcs.h:1.1 linux/drivers/char/ipmi_kcs.h:1.2
--- linux/drivers/char/ipmi_kcs.h:1.1	Thu Jun  1 17:01:59 2000
+++ linux/drivers/char/ipmi_kcs.h	Mon Jul 10 16:23:53 2000
@@ -49,7 +49,7 @@
 #define ISA_TIMEOUT 1000
 
 typedef struct ipmi_ksc_set_watchdog
-	{
+  {
   unsigned char lun               :2;
   unsigned char netfn             :6;
 
@@ -78,6 +78,28 @@
   unsigned short initial_count;
   } IPMI_KCS_SET_WATCHDOG;
 
+typedef struct get_watchdog_response
+  {
+  unsigned char cc           __attribute__ ((packed));
+
+  unsigned char timer_use    :3;
+  unsigned char res1         :3;
+  unsigned char timer_status :1;
+  unsigned char sel_log      :1;
+
+  unsigned char timeout_act  :3;
+  unsigned char res2         :1;
+  unsigned char pre_irq_act  :3;
+  unsigned char res3         :1;
+
+  unsigned char pre_timeout __attribute__ ((packed));
+
+  unsigned char timer_use_xp  __attribute__ ((packed));
+
+  unsigned short init_count  __attribute__ ((packed));
+  unsigned short current_count __attribute__ ((packed));
+  } GET_WATCHDOG_RESPONSE;
+
 typedef struct ipmi_ksc_reset_watchdog
   {
   unsigned char lun               :2;
@@ -94,3 +116,138 @@
   unsigned char cmd;
   unsigned char sensor_number;
   } SENSOR_CMD;
+
+/*
+ * Structure for a generic cmd that requires no args
+ */
+typedef struct ipmi_kcs_generic_cmd
+  {
+  unsigned char lun               :2;
+  unsigned char netfn             :6;
+
+  unsigned char cmd;
+  } KCS_GENERIC_CMD;
+
+/*
+ * FRU area info response
+ */
+typedef struct kcs_fru_area_info
+  {
+  unsigned char    cc    __attribute__ ((packed));
+  unsigned short  size  __attribute__ ((packed));
+
+#define ACCESSMODE_BYTE  0
+#define ACCESSMODE_WORD  1
+  unsigned char    accessmode:1;
+  unsigned char    reserved:7;
+  } FRU_AREA_INFO;
+
+typedef struct kcs_fru_data_req
+  {
+  unsigned char lun               :2;
+  unsigned char netfn             :6;
+
+  unsigned char cmd;
+
+  unsigned short  offset  __attribute__ ((packed));
+  unsigned char    count    __attribute__ ((packed));
+  } FRU_DATA_REQ;
+
+typedef struct fru_common_header
+  {     
+  unsigned char common_header_format_version;
+  unsigned char internal_use_offset;
+  unsigned char chassis_info_offset;
+  unsigned char board_area_offset;
+  unsigned char product_info_offset;
+  unsigned char multirecord_area_offset;
+  unsigned char pad;
+  unsigned char checksum;
+  } FRU_COMMON_HEADER;
+
+typedef struct type_length
+  {
+  unsigned char  num_bytes  :6;
+  unsigned char  type_code  :2;
+  } TL;
+
+typedef struct ipmi_kcs_add_sel_cmd
+  {
+  unsigned char lun               :2;
+  unsigned char netfn             :6;
+
+  unsigned char cmd;
+
+  unsigned short  record_id    __attribute__ ((packed));
+  unsigned char    record_type  __attribute__ ((packed));
+  unsigned int    timestamp    __attribute__ ((packed));
+  unsigned short  mfid        __attribute__ ((packed));
+
+  unsigned char    data[6]      __attribute__ ((packed));
+  } ADD_SEL_CMD;
+
+/* GET_DEVICE_ID RESPONSE */
+typedef struct device_id_response
+  {
+  unsigned char cc;
+  unsigned char device_id;
+  
+  unsigned char device_revision         :4;
+  unsigned char reserved                :3;
+  unsigned char provides_sdr            :1;
+  
+  unsigned char major_firmware_revision :7;
+  #define NORMAL_OPERATION  0
+  #define DEVICE_BUSY       1
+  unsigned char device_available        :1;
+  
+  unsigned char minor_firmware_revision;
+  
+  unsigned char ipmi_version_major      :4;
+  unsigned char ipmi_version_minor      :4;
+  
+  unsigned char supports_sensor_device  :1;
+  unsigned char supports_sdr_device     :1;
+  unsigned char supports_sel_device     :1;
+  unsigned char supports_fru_device     :1;
+  unsigned char supports_ipmb_receiver  :1;
+  unsigned char supports_ipmb_generator :1;
+  unsigned char supports_bridge         :1;
+  unsigned char supports_chassis_device :1;
+  
+  unsigned char manufacturer_id1;
+  unsigned char manufacturer_id2;
+  unsigned char manufacturer_id3;
+  
+  unsigned short product_id;
+  } DEVICE_ID_RESPONSE;
+
+/*
+ * The Netfn codes
+ */
+#define CHASSIS_REQUEST   0x00
+#define CHASSIS_RESPONSE  0x01
+#define BRIDGE_REQUEST    0x02
+#define BRIDGE_RESPONSE   0x03
+#define SENSOR_REQUEST    0x04
+#define SENSOR_RESPONSE   0x05
+#define APP_REQUEST       0x06
+#define APP_RESPONSE      0x07
+#define FIRMWARE_REQUEST  0x08
+#define FIRMWARE_RESPONSE 0x09
+#define STORAGE_REQUEST   0x0A
+#define STORAGE_RESPONSE  0x0B
+
+/*
+ * The cmds
+ */
+#define CMD_GET_FRU_INV_AREA_INFO   0x10
+#define CMD_READ_FRU_INV_DATA       0x11
+#define CMD_WRITE_FRU_INV_DATA      0x12
+#define CMD_ADD_SEL_ENTRY           0x44
+#define CMD_WATCHDOG_SET            0x24
+#define CMD_WATCHDOG_GET            0x25
+#define CMD_WATCHDOG_RESET          0x22
+#define CMD_GET_DEVICE_ID           0x01
+
+#define VALINUX_MFID  0x1B58
Index: oldkernel/linux/include/linux/ipmi_kcs_ioctls.h
diff -u linux/include/linux/ipmi_kcs_ioctls.h:1.1 linux/include/linux/ipmi_kcs_ioctls.h:1.2
--- linux/include/linux/ipmi_kcs_ioctls.h:1.1	Thu Jun  1 17:01:59 2000
+++ linux/include/linux/ipmi_kcs_ioctls.h	Mon Jul 10 16:23:53 2000
@@ -17,23 +17,37 @@
 
 #ifndef _IPMI_KCS_IOCTLS_H
 #define _IPMI_KCS_IOCTLS_H
+  
+#define  IOCTL_WATCHDOG_SET          0x01
+  #define WATCHDOG_ACTION_REBOOT      0x01
+  #define WATCHDOG_ACTION_NMI         0x02
 
-	
-#define	IOCTL_WATCHDOG_SET					0x01
-	#define WATCHDOG_ACTION_REBOOT	0x01
-	#define WATCHDOG_ACTION_NMI			0x02
-
-#define IOCTL_WATCHDOG_PING					0x02
-
-#define IOCTL_PANEL_LED_SET_BLINK		0x03
-	#define PANEL_LED_NORMAL						0x00
-	#define PANEL_LED_BLINK							0x01
+#define IOCTL_WATCHDOG_PING         0x02
 
-#define IOCTL_READ_SENSOR						0x04
+#define IOCTL_PANEL_LED_SET_BLINK   0x03
+  #define PANEL_LED_NORMAL            0x00
+  #define PANEL_LED_BLINK             0x01
+
+#define IOCTL_READ_SENSOR           0x04
 struct sensor_request
-	{
-	unsigned char sensor_number;
-	unsigned char	result_buffer[64];
-	int						result_length;
-	};
+  {
+  unsigned char sensor_number;
+  unsigned char result_buffer[64];
+  int           result_length;
+  };
+
+#define IOCTL_SET_ASSET_TAG         0x05
+struct asset_tag_request
+  {
+  unsigned char buffer_len;
+  char          *buffer;
+  };
+
+#define IOCTL_REFRESH_FRU           0x06
+
+#define IOCTL_ADD_SEL_ENTRY         0x07
+struct sel_entry_request
+  {
+  unsigned char data[6];
+  };
 #endif
