diff -urN linux-2.2.25.orig/arch/i386/kernel/traps.c linux-2.2.25/arch/i386/kernel/traps.c --- linux-2.2.25.orig/arch/i386/kernel/traps.c Sat Nov 30 02:05:43 2002 +++ linux-2.2.25/arch/i386/kernel/traps.c Mon Aug 11 15:16:03 2003 @@ -329,6 +329,8 @@ printk("Do you have a strange power saving mode enabled?\n"); } +static void bmc_nmi_error(unsigned char reason, struct pt_regs * regs); + asmlinkage void do_nmi(struct pt_regs * regs, long error_code) { unsigned char reason = inb(0x61); @@ -339,10 +341,245 @@ mem_parity_error(reason, regs); if (reason & 0x40) io_check_error(reason, regs); + if (reason & 0x20) + { + bmc_nmi_error(reason, regs); + return; + } if (!(reason & 0xc0)) unknown_nmi_error(reason, regs); } +/* + * The IPMI KCS NMI watchdog handler stuff + */ +#include + +#define IO 0xca2 +#define ISA_BMC_STATUS (IO + 1) +#define ISA_BMC_COMMAND (IO + 1) +#define ISA_BMC_DATA_IN (IO + 0) +#define ISA_BMC_DATA_OUT (IO + 0) +#define ISA_STATE_MASK 0xC0 +#define ISA_IDLE_STATE 0x00 +#define ISA_READ_STATE 0x40 +#define ISA_WRITE_STATE 0x80 +#define ISA_ERROR_STATE 0xC0 +#define ISA_IBF_FLAG 0x02 +#define ISA_OBF_FLAG 0x01 +#define ISA_WRITE_START 0x61 +#define ISA_WRITE_END 0X62 +#define ISA_READ 0X68 +#define MAX_ISA_LENGTH 35 +#define ISA_TIMEOUT 1000 + +static int get_wd_counter(void); +static int kcs_read_message(int *msglen, unsigned char *buf); +static int wait_while_ibf(int timeout); +static int kcs_send_message(unsigned char *buf, int length); +static void blink(void); + +static void bmc_nmi_error(unsigned char reason, struct pt_regs * regs) +{ + unsigned char state; + volatile unsigned char status; + unsigned int wd_cnt; + + /* First see if we were triggered by a BMC error */ + status = inb_p(ISA_BMC_STATUS); + state = (unsigned char) (status & ISA_STATE_MASK); + + if (state == ISA_ERROR_STATE) + { + printk("[KCS_TRAP] Called with ISA interface in error state\n"); + return; + } + /* Now see if the watchdog timer is in danger of tripping */ + if ((wd_cnt = get_wd_counter())<0) + { + printk("[KCS_TRAP] Unable to verify BMC WD status.\n"); + blink(); + return; + } + if (wd_cnt > 10) + { + printk("[KCS_TRAP] tripped due to recoverable BMC error (wd count = %d)\n",wd_cnt); + return; + } + printk("[KCS_TRAP] tripped due to imminent BMC watchdog trip (wd count = %d)\n",wd_cnt); + blink(); + return; +} + +static void blink() +{ + struct cmd + { + unsigned char lun :2; + unsigned char netfn :6; + + unsigned char cmd; + } pkt; + memset(&pkt, 0, sizeof(pkt)); + pkt.lun=0x00; + pkt.netfn=0x06; + pkt.cmd=0x55; + kcs_send_message((unsigned char *) &pkt, sizeof(pkt)); +} + +static int get_wd_counter() +{ + unsigned char reply[MAX_ISA_LENGTH]; + int length; + 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)); + } *g_wd_response; + struct cmd + { + unsigned char lun :2; + unsigned char netfn :6; + + unsigned char cmd; + } pkt; + + pkt.lun = 0; + pkt.netfn = 6; + pkt.cmd = 0x25; + kcs_send_message((unsigned char *) &pkt, sizeof(pkt)); + /* Packet sent. now get the response */ + if (kcs_read_message(&length, &reply[0])<0) + { + printk("[KCS_TRAP] Read Message failed\n"); + return(-1); + } + if (reply[2] != 0x00) + { + printk("[KCS_TRAP] GET_WATCHDOG command failed (0x%x)\n",reply[2]); + return(-1); + } + g_wd_response= (struct get_watchdog_response *) &reply[2]; + return(g_wd_response->current_count); +} + +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_TRAP] ISA interface in ERROR 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_TRAP] ISA timeout in IBF\n"); + return(-EIO); + } + outb_p(ISA_READ, ISA_BMC_DATA_OUT); + } + printk("[KCS_TRAP] ISA Message overflow\n"); + return (-EIO); +} + +static int wait_while_ibf(int timeout) +{ + 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); +} + +static int kcs_send_message(unsigned char *buf, int length) +{ + int status; + int i; + unsigned char chipstatus; + + if ((status = wait_while_ibf(ISA_TIMEOUT)) <0) + return(-EIO); + + outb_p(ISA_WRITE_START, ISA_BMC_COMMAND); + + if ((status = wait_while_ibf(ISA_TIMEOUT)) <0) + return(-EIO); + + for (i=0; i , 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. + * + * (c) Copyright 1999 San Mehat + * + * 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.07. - Integrated watchdog commands into IOCTL's and added + * support for blinking front panel LED + * + * Release 0.08. - Sensor read commands added as ioctl + * + * Release 0.09. - Changed polling freq back to 1 second + * - Fixed possible bug where a chip status variable was + * not declared volatile. + * - Fixed buffer memory leak + * - Fixed ioctl return value problem + * - Changed architecture so that applications calling + * driver ioctl()'s are put to sleep after request + * is sent. The reply is handled by the normal + * driver polling timer queue and ring buffer + * + * 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 + * Release 1.10 - Fixed SMP bug which was causing command failures when + * /proc/ipmi was being read while a command was being + * executed (added semaphore) + * - Fixed /proc/ipmi so commands only get issued once + * Release 1.20 - Removed a bunch of useless warning msgs + * Release 1.30 - Added more stringent error checking when ISA state + * enters ERROR_STATE + * - Added better unexpected OBF handling on transactions + * - Explicitly set power sensor state to NO BLINKY on + * startup + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipmi_kcs.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PROC_FS +#include +#endif + +/* function prototypes */ +static int kcs_send_message(unsigned char *buf, int length); +static int kcs_read_message(int *msglen, unsigned char *buf); +static int wait_while_ibf(int timeout); +static int ipmi_kcs_dispatch_internal(unsigned char *data, int size, unsigned char*reply); + +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, int disable); +static int watchdog_ping(void); +static int panel_set(unsigned char state); +static int read_sensor(struct sensor_request *sensor); + +void ipmi_kcs_poll(unsigned long nothing); +int ipmi_kcs_init(void); + +static int kcs_open(struct inode *inode, struct file *file); +static int kcs_release(struct inode *inode, struct file *file); +static ssize_t kcs_read(struct file *file, char *buf, size_t count, loff_t *ptr); +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); +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); +static int check_obf(void); +static void handle_obf(char *location); + +#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 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 = 3; + +#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; +static struct semaphore kcs_sem = MUTEX; + +#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 +}; + +static struct miscdevice kcs_miscdev= +{ + IPMI_KCS_MINOR, + "ipmi_kcs", + &kcs_fops +}; + +#define POLL_FREQ ((HZ/10)) + +/***************/ + +static long long kcs_llseek(struct file *file, long long offset, int origin) +{ + 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; + + 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; + + /* 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) +{ + 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) +{ + ADD_SEL_CMD cmd; + unsigned char reply[MAX_ISA_LENGTH]; + + 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((unsigned char *) &cmd, + sizeof(cmd), + &reply[0])<0) + { + return(-EIO); + } + if (reply[2] != (unsigned char) 0x00) + { + return(-EIO); + } + return(0); +} + +static int read_sensor(struct sensor_request *sensor) +{ + 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((unsigned char*) &pkt, + sizeof(pkt), + (unsigned char *) &sensor->result_buffer[0]))<0) + { + return -EIO; + } + if (sensor->result_buffer[2] != 0x00) + { + return -EIO; + } + return(0); +} + +static int refresh_asset() +{ + 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((unsigned char*) &data_req, + sizeof(data_req), + &reply[0])<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((unsigned char*) &data_req, + sizeof(data_req), + &reply[0])<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); +} + +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((unsigned char *) &inv_info_req, + sizeof(inv_info_req), + &reply[0])<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((unsigned char *) &data_req, + sizeof(data_req), + &reply[0])<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((unsigned char *) &write_buffer[0], + write_len, + &reply[0])<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((unsigned char *) &cmd, + sizeof(cmd), + &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((unsigned char *) &cmd, + sizeof(cmd), + &reply[0])<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 = 1; + 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((unsigned char *) &pkt, + sizeof(pkt), + &reply[0])<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 + { + 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((unsigned char *) &pkt, sizeof(pkt),&reply[0])<0) + { + return(-EIO); + } + if (reply[2] != 0x00) + { + 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= APP_REQUEST; + pkt.lun= 0x00; + if (state == PANEL_LED_BLINK) + pkt.cmd= 0x55; + else + pkt.cmd= 0x56; + + if (state == 2) + internal_cmd = 1; + if (ipmi_kcs_dispatch_internal((unsigned char *) &pkt, sizeof(pkt),&reply[0])<0) + { + return(-EIO); + } + if (state == 2) + internal_cmd = 0; + if (reply[2] != 0x00) + { + return(-EIO); + } + return(0); +} + +static int ipmi_kcs_dispatch_internal(unsigned char *data, + int size, + unsigned char *reply) +{ + int rc; + char *rep; + int retries = 0; + + rep = (char *) reply; + down(&kcs_sem); + while(1) + { + if ((rc =kcs_send_message(data, size))<0) + { + printk("[IPMI_KCS] Unable to send message to hardware\n"); + goto retry; + } + interruptible_sleep_on(&wq); + if ((rc = remove_data_from_ringbuffer(reply, + MAX_ISA_LENGTH))<0) + { + printk("[IPMI_KCS] Ring buffer remove failure\n"); + up(&kcs_sem); + return -EIO; + } + if ((reply[0]==0xd)&&(reply[1]==0xe)&&(reply[2]==0xa)&&(reply[3]==0xd)) + { + goto retry; + } + break; +retry: + retries++; + if (retries <5) + udelay(500); + else + { + printk("[IPMI_KCS] Aborting transaction due to excessive errors\n"); + rc = -EIO; + break; + } + } + up(&kcs_sem); + 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; + } +} + +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; +} + +#ifdef MODULE +#define kcs_init init_module + +__initfunc(int kcs_init(void)) +{ + return(ipmi_kcs_init()); +} + +void cleanup_module(void) +{ + 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; + + /* 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; + + 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); +} + +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) + { + /* Stub in some data so that whoever reads data out + * will know there was a failure + */ + data[0]= 0xd; + data[1]= 0xe; + data[2]= 0xa; + data[3]= 0xd; + add_data_to_ringbuffer(&data[0], 4); + wake_up_interruptible(&wq); + 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); + + if (check_obf()) + handle_obf("initiating transaction"); + + outb_p(ISA_WRITE_START, ISA_BMC_COMMAND); + + if ((status = wait_while_ibf(ISA_TIMEOUT)) <0) + return(-2); + + for (i=0; i = (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) +{ + char *p; + time_t now; + + if (fpos == 0) + { + if (watchdog_get()!= 0) + { + printk("[IPMI_KCS] Unable to get watchdog status\n"); + memset(&wd_status, 0, sizeof(wd_status)); + } + refresh_asset(); + if (get_deviceid()!= 0) + { + printk("[IPMI_KCS] Unable to get device ID\n"); + memset(&dev_id, 0, sizeof(dev_id)); + } + } + p = buf; + p+= sprintf(p, "Driver Version\t: %d.%d\n", + driver_major,driver_minor); + 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 (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 + { + now = CURRENT_TIME; + 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[BOARD_MANUFACTURER] : "Not Available")); + p+= sprintf(p, "Board Prod Name\t: %s\n", + (board_area[BOARD_PRODUCT_NAME][0] ? board_area[BOARD_PRODUCT_NAME] : "Not Available")); + p+= sprintf(p, "Board Part\t: %s\n", + (board_area[BOARD_PART][0] ? board_area[BOARD_PART] : "Not Available")); + p+= sprintf(p, "Board Serial\t: %s\n", + (board_area[BOARD_SERIAL][0] ? board_area[BOARD_SERIAL] : "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[PRODUCT_MANUFACTURER]:"Not Available")); + p+= sprintf(p, "Product Name\t: %s\n", + (product_area[PRODUCT_NAME][0] ? product_area[PRODUCT_NAME] : "Not Available")); + p+= sprintf(p, "Product Part\t: %s\n", + (product_area[PRODUCT_PART][0] ? product_area[PRODUCT_PART] : "Not Available")); + p+= sprintf(p, "Product Version\t: %s\n", + (product_area[PRODUCT_VERSION][0] ? product_area[PRODUCT_VERSION] : "Not Available")); + p+= sprintf(p, "Product Serial\t: %s\n", + (product_area[PRODUCT_SERIAL][0] ? product_area[PRODUCT_SERIAL] : "Not Available")); + + p+= sprintf(p, "Product Asset\t: %s (%d byte maximum size)\n", + (product_area[PRODUCT_ASSET][0] ? product_area[PRODUCT_ASSET] : "Not Available"), + asset_length); + + return p - buf; +} +#endif diff -urN linux-2.2.25.orig/drivers/char/ipmi_kcs.h linux-2.2.25/drivers/char/ipmi_kcs.h --- linux-2.2.25.orig/drivers/char/ipmi_kcs.h Thu Jan 1 08:00:00 1970 +++ linux-2.2.25/drivers/char/ipmi_kcs.h Mon Aug 11 15:16:03 2003 @@ -0,0 +1,253 @@ +/* + * Intelligent Platform Management Interface driver for Linux 2.x + * + * (c) Copyright 1999 San Mehat & VA Linux Systems + * 1382 Bordeaux Dr. + * Sunnyvale, California + * 94089 + * + * http://www.valinux.com + * + * This driver is provided under the GNU public license, incorporated + * herein by reference. The driver is provided without warranty or + * support. + * + * + */ + +#include + +#define IO 0xca2 +#define ISA_BMC_STATUS (IO + 1) +#define ISA_BMC_COMMAND (IO + 1) +#define ISA_BMC_DATA_IN (IO + 0) +#define ISA_BMC_DATA_OUT (IO + 0) + +/* State bits based on S1 & S0 below */ +#define ISA_STATE_MASK 0xC0 +#define ISA_IDLE_STATE 0x00 +#define ISA_READ_STATE 0x40 +#define ISA_WRITE_STATE 0x80 +#define ISA_ERROR_STATE 0xC0 + +/* Status Register Bits */ +#define ISA_S1_FLAG 0x80 +#define ISA_S0_FLAG 0x40 +#define ISA_CD_FLAG 0x08 +#define ISA_SMS_MSG_FLAG 0x04 +#define ISA_IBF_FLAG 0x02 +#define ISA_OBF_FLAG 0x01 + +/* SMS Transfer Stream Control Codes */ +#define ISA_GET_STATUS_ABORT 0x60 +#define ISA_WRITE_START 0x61 +#define ISA_WRITE_END 0X62 +#define ISA_READ 0X68 + +#define MAX_ISA_LENGTH 35 + +#define ISA_TIMEOUT 1000 + +typedef struct ipmi_ksc_set_watchdog + { + unsigned char lun :2; + unsigned char netfn :6; + + unsigned char cmd; + + unsigned char timer_use :3; + unsigned char res1 :4; + unsigned char dontlog :1; + + unsigned char timeout_action :3; + unsigned char res2 :1; + unsigned char pre_irq :3; + unsigned char res3 :1; + + unsigned char pretimeout_interval; + + unsigned char tuefc_res1 :1; + unsigned char tuefc_biosfrb2 :1; + unsigned char tuefc_biospost :1; + unsigned char tuefc_osload :1; + unsigned char tuefc_smsos :1; + unsigned char tuefc_oem :1; + unsigned char tuefc_res2 :1; + unsigned char tuefc_res3 :1; + + 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; + unsigned char netfn :6; + + unsigned char cmd; + } IPMI_KCS_RESET_WATCHDOG; + +typedef struct sensor_cmd + { + unsigned char lun :2; + unsigned char netfn :6; + + 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 diff -urN linux-2.2.25.orig/drivers/char/misc.c linux-2.2.25/drivers/char/misc.c --- linux-2.2.25.orig/drivers/char/misc.c Sat Nov 3 00:39:06 2001 +++ linux-2.2.25/drivers/char/misc.c Mon Aug 11 15:17:22 2003 @@ -289,6 +289,9 @@ #ifdef CONFIG_TOSHIBA tosh_init(); #endif +#ifdef CONFIG_IPMI_KCS + ipmi_kcs_init(); +#endif if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { printk("unable to get major %d for misc devices\n", MISC_MAJOR); diff -urN linux-2.2.25.orig/include/linux/ipmi_kcs_ioctls.h linux-2.2.25/include/linux/ipmi_kcs_ioctls.h --- linux-2.2.25.orig/include/linux/ipmi_kcs_ioctls.h Thu Jan 1 08:00:00 1970 +++ linux-2.2.25/include/linux/ipmi_kcs_ioctls.h Mon Aug 11 15:16:03 2003 @@ -0,0 +1,53 @@ +/* + * Intelligent Platform Management Interface driver for Linux 2.x + * + * (c) Copyright 1999 San Mehat & VA Linux Systems + * 1382 Bordeaux Dr. + * Sunnyvale, California + * 94089 + * + * http://www.valinux.com + * + * This driver is provided under the GNU public license, incorporated + * herein by reference. The driver is provided without warranty or + * support. + * + * IOCTL definitions for IPMI KCS driver + */ + +#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_PING 0x02 + +#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; + }; + +#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 diff -urN linux-2.2.25.orig/include/linux/miscdevice.h linux-2.2.25/include/linux/miscdevice.h --- linux-2.2.25.orig/include/linux/miscdevice.h Tue Apr 8 16:14:12 2003 +++ linux-2.2.25/include/linux/miscdevice.h Mon Aug 11 15:16:03 2003 @@ -20,6 +20,7 @@ #define MICROCODE_MINOR 184 #define MPT_MINOR 220 #define MISC_DYNAMIC_MINOR 255 +#define IPMI_KCS_MINOR 173 #define SGI_GRAPHICS_MINOR 146 #define SGI_OPENGL_MINOR 147