(2006-08-06) rescue-bootcd
This commit is contained in:
67
extra/linux-2.6.10/drivers/char/ipmi/Kconfig
Normal file
67
extra/linux-2.6.10/drivers/char/ipmi/Kconfig
Normal file
@@ -0,0 +1,67 @@
|
||||
#
|
||||
# IPMI device configuration
|
||||
#
|
||||
|
||||
menu "IPMI"
|
||||
config IPMI_HANDLER
|
||||
tristate 'IPMI top-level message handler'
|
||||
help
|
||||
This enables the central IPMI message handler, required for IPMI
|
||||
to work.
|
||||
|
||||
IPMI is a standard for managing sensors (temperature,
|
||||
voltage, etc.) in a system.
|
||||
|
||||
See Documentation/IPMI.txt for more details on the driver.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config IPMI_PANIC_EVENT
|
||||
bool 'Generate a panic event to all BMCs on a panic'
|
||||
depends on IPMI_HANDLER
|
||||
help
|
||||
When a panic occurs, this will cause the IPMI message handler to
|
||||
generate an IPMI event describing the panic to each interface
|
||||
registered with the message handler.
|
||||
|
||||
config IPMI_PANIC_STRING
|
||||
bool 'Generate OEM events containing the panic string'
|
||||
depends on IPMI_PANIC_EVENT
|
||||
help
|
||||
When a panic occurs, this will cause the IPMI message handler to
|
||||
generate IPMI OEM type f0 events holding the IPMB address of the
|
||||
panic generator (byte 4 of the event), a sequence number for the
|
||||
string (byte 5 of the event) and part of the string (the rest of the
|
||||
event). Bytes 1, 2, and 3 are the normal usage for an OEM event.
|
||||
You can fetch these events and use the sequence numbers to piece the
|
||||
string together.
|
||||
|
||||
config IPMI_DEVICE_INTERFACE
|
||||
tristate 'Device interface for IPMI'
|
||||
depends on IPMI_HANDLER
|
||||
help
|
||||
This provides an IOCTL interface to the IPMI message handler so
|
||||
userland processes may use IPMI. It supports poll() and select().
|
||||
|
||||
config IPMI_SI
|
||||
tristate 'IPMI System Interface handler'
|
||||
depends on IPMI_HANDLER
|
||||
help
|
||||
Provides a driver for System Interfaces (KCS, SMIC, BT).
|
||||
Currently, only KCS and SMIC are supported. If
|
||||
you are using IPMI, you should probably say "y" here.
|
||||
|
||||
config IPMI_WATCHDOG
|
||||
tristate 'IPMI Watchdog Timer'
|
||||
depends on IPMI_HANDLER
|
||||
help
|
||||
This enables the IPMI watchdog timer.
|
||||
|
||||
config IPMI_POWEROFF
|
||||
tristate 'IPMI Poweroff'
|
||||
depends on IPMI_HANDLER
|
||||
help
|
||||
This enables a function to power off the system with IPMI if
|
||||
the IPMI management controller is capable of this.
|
||||
|
||||
endmenu
|
||||
15
extra/linux-2.6.10/drivers/char/ipmi/Makefile
Normal file
15
extra/linux-2.6.10/drivers/char/ipmi/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# Makefile for the ipmi drivers.
|
||||
#
|
||||
|
||||
ipmi_si-objs := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o
|
||||
|
||||
obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
|
||||
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
|
||||
obj-$(CONFIG_IPMI_SI) += ipmi_si.o
|
||||
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
|
||||
obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
|
||||
|
||||
ipmi_si.o: $(ipmi_si-objs)
|
||||
$(LD) -r -o $@ $(ipmi_si-objs)
|
||||
|
||||
513
extra/linux-2.6.10/drivers/char/ipmi/ipmi_bt_sm.c
Normal file
513
extra/linux-2.6.10/drivers/char/ipmi/ipmi_bt_sm.c
Normal file
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
* ipmi_bt_sm.c
|
||||
*
|
||||
* The state machine for an Open IPMI BT sub-driver under ipmi_si.c, part
|
||||
* of the driver architecture at http://sourceforge.net/project/openipmi
|
||||
*
|
||||
* Author: Rocky Craig <first.last@hp.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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
#include <linux/kernel.h> /* For printk. */
|
||||
#include <linux/string.h>
|
||||
#include <linux/ipmi_msgdefs.h> /* for completion codes */
|
||||
#include "ipmi_si_sm.h"
|
||||
|
||||
#define IPMI_BT_VERSION "v33"
|
||||
|
||||
static int bt_debug = 0x00; /* Production value 0, see following flags */
|
||||
|
||||
#define BT_DEBUG_ENABLE 1
|
||||
#define BT_DEBUG_MSG 2
|
||||
#define BT_DEBUG_STATES 4
|
||||
|
||||
/* Typical "Get BT Capabilities" values are 2-3 retries, 5-10 seconds,
|
||||
and 64 byte buffers. However, one HP implementation wants 255 bytes of
|
||||
buffer (with a documented message of 160 bytes) so go for the max.
|
||||
Since the Open IPMI architecture is single-message oriented at this
|
||||
stage, the queue depth of BT is of no concern. */
|
||||
|
||||
#define BT_NORMAL_TIMEOUT 2000000 /* seconds in microseconds */
|
||||
#define BT_RETRY_LIMIT 2
|
||||
#define BT_RESET_DELAY 6000000 /* 6 seconds after warm reset */
|
||||
|
||||
enum bt_states {
|
||||
BT_STATE_IDLE,
|
||||
BT_STATE_XACTION_START,
|
||||
BT_STATE_WRITE_BYTES,
|
||||
BT_STATE_WRITE_END,
|
||||
BT_STATE_WRITE_CONSUME,
|
||||
BT_STATE_B2H_WAIT,
|
||||
BT_STATE_READ_END,
|
||||
BT_STATE_RESET1, /* These must come last */
|
||||
BT_STATE_RESET2,
|
||||
BT_STATE_RESET3,
|
||||
BT_STATE_RESTART,
|
||||
BT_STATE_HOSED
|
||||
};
|
||||
|
||||
struct si_sm_data {
|
||||
enum bt_states state;
|
||||
enum bt_states last_state; /* assist printing and resets */
|
||||
unsigned char seq; /* BT sequence number */
|
||||
struct si_sm_io *io;
|
||||
unsigned char write_data[IPMI_MAX_MSG_LENGTH];
|
||||
int write_count;
|
||||
unsigned char read_data[IPMI_MAX_MSG_LENGTH];
|
||||
int read_count;
|
||||
int truncated;
|
||||
long timeout;
|
||||
unsigned int error_retries; /* end of "common" fields */
|
||||
int nonzero_status; /* hung BMCs stay all 0 */
|
||||
};
|
||||
|
||||
#define BT_CLR_WR_PTR 0x01 /* See IPMI 1.5 table 11.6.4 */
|
||||
#define BT_CLR_RD_PTR 0x02
|
||||
#define BT_H2B_ATN 0x04
|
||||
#define BT_B2H_ATN 0x08
|
||||
#define BT_SMS_ATN 0x10
|
||||
#define BT_OEM0 0x20
|
||||
#define BT_H_BUSY 0x40
|
||||
#define BT_B_BUSY 0x80
|
||||
|
||||
/* Some bits are toggled on each write: write once to set it, once
|
||||
more to clear it; writing a zero does nothing. To absolutely
|
||||
clear it, check its state and write if set. This avoids the "get
|
||||
current then use as mask" scheme to modify one bit. Note that the
|
||||
variable "bt" is hardcoded into these macros. */
|
||||
|
||||
#define BT_STATUS bt->io->inputb(bt->io, 0)
|
||||
#define BT_CONTROL(x) bt->io->outputb(bt->io, 0, x)
|
||||
|
||||
#define BMC2HOST bt->io->inputb(bt->io, 1)
|
||||
#define HOST2BMC(x) bt->io->outputb(bt->io, 1, x)
|
||||
|
||||
#define BT_INTMASK_R bt->io->inputb(bt->io, 2)
|
||||
#define BT_INTMASK_W(x) bt->io->outputb(bt->io, 2, x)
|
||||
|
||||
/* Convenience routines for debugging. These are not multi-open safe!
|
||||
Note the macros have hardcoded variables in them. */
|
||||
|
||||
static char *state2txt(unsigned char state)
|
||||
{
|
||||
switch (state) {
|
||||
case BT_STATE_IDLE: return("IDLE");
|
||||
case BT_STATE_XACTION_START: return("XACTION");
|
||||
case BT_STATE_WRITE_BYTES: return("WR_BYTES");
|
||||
case BT_STATE_WRITE_END: return("WR_END");
|
||||
case BT_STATE_WRITE_CONSUME: return("WR_CONSUME");
|
||||
case BT_STATE_B2H_WAIT: return("B2H_WAIT");
|
||||
case BT_STATE_READ_END: return("RD_END");
|
||||
case BT_STATE_RESET1: return("RESET1");
|
||||
case BT_STATE_RESET2: return("RESET2");
|
||||
case BT_STATE_RESET3: return("RESET3");
|
||||
case BT_STATE_RESTART: return("RESTART");
|
||||
case BT_STATE_HOSED: return("HOSED");
|
||||
}
|
||||
return("BAD STATE");
|
||||
}
|
||||
#define STATE2TXT state2txt(bt->state)
|
||||
|
||||
static char *status2txt(unsigned char status, char *buf)
|
||||
{
|
||||
strcpy(buf, "[ ");
|
||||
if (status & BT_B_BUSY) strcat(buf, "B_BUSY ");
|
||||
if (status & BT_H_BUSY) strcat(buf, "H_BUSY ");
|
||||
if (status & BT_OEM0) strcat(buf, "OEM0 ");
|
||||
if (status & BT_SMS_ATN) strcat(buf, "SMS ");
|
||||
if (status & BT_B2H_ATN) strcat(buf, "B2H ");
|
||||
if (status & BT_H2B_ATN) strcat(buf, "H2B ");
|
||||
strcat(buf, "]");
|
||||
return buf;
|
||||
}
|
||||
#define STATUS2TXT(buf) status2txt(status, buf)
|
||||
|
||||
/* This will be called from within this module on a hosed condition */
|
||||
#define FIRST_SEQ 0
|
||||
static unsigned int bt_init_data(struct si_sm_data *bt, struct si_sm_io *io)
|
||||
{
|
||||
bt->state = BT_STATE_IDLE;
|
||||
bt->last_state = BT_STATE_IDLE;
|
||||
bt->seq = FIRST_SEQ;
|
||||
bt->io = io;
|
||||
bt->write_count = 0;
|
||||
bt->read_count = 0;
|
||||
bt->error_retries = 0;
|
||||
bt->nonzero_status = 0;
|
||||
bt->truncated = 0;
|
||||
bt->timeout = BT_NORMAL_TIMEOUT;
|
||||
return 3; /* We claim 3 bytes of space; ought to check SPMI table */
|
||||
}
|
||||
|
||||
static int bt_start_transaction(struct si_sm_data *bt,
|
||||
unsigned char *data,
|
||||
unsigned int size)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if ((size < 2) || (size > IPMI_MAX_MSG_LENGTH)) return -1;
|
||||
|
||||
if ((bt->state != BT_STATE_IDLE) && (bt->state != BT_STATE_HOSED))
|
||||
return -2;
|
||||
|
||||
if (bt_debug & BT_DEBUG_MSG) {
|
||||
printk(KERN_WARNING "+++++++++++++++++++++++++++++++++++++\n");
|
||||
printk(KERN_WARNING "BT: write seq=0x%02X:", bt->seq);
|
||||
for (i = 0; i < size; i ++) printk (" %02x", data[i]);
|
||||
printk("\n");
|
||||
}
|
||||
bt->write_data[0] = size + 1; /* all data plus seq byte */
|
||||
bt->write_data[1] = *data; /* NetFn/LUN */
|
||||
bt->write_data[2] = bt->seq;
|
||||
memcpy(bt->write_data + 3, data + 1, size - 1);
|
||||
bt->write_count = size + 2;
|
||||
|
||||
bt->error_retries = 0;
|
||||
bt->nonzero_status = 0;
|
||||
bt->read_count = 0;
|
||||
bt->truncated = 0;
|
||||
bt->state = BT_STATE_XACTION_START;
|
||||
bt->last_state = BT_STATE_IDLE;
|
||||
bt->timeout = BT_NORMAL_TIMEOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* After the upper state machine has been told SI_SM_TRANSACTION_COMPLETE
|
||||
it calls this. Strip out the length and seq bytes. */
|
||||
|
||||
static int bt_get_result(struct si_sm_data *bt,
|
||||
unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
int i, msg_len;
|
||||
|
||||
msg_len = bt->read_count - 2; /* account for length & seq */
|
||||
/* Always NetFn, Cmd, cCode */
|
||||
if (msg_len < 3 || msg_len > IPMI_MAX_MSG_LENGTH) {
|
||||
printk(KERN_WARNING "BT results: bad msg_len = %d\n", msg_len);
|
||||
data[0] = bt->write_data[1] | 0x4; /* Kludge a response */
|
||||
data[1] = bt->write_data[3];
|
||||
data[2] = IPMI_ERR_UNSPECIFIED;
|
||||
msg_len = 3;
|
||||
} else {
|
||||
data[0] = bt->read_data[1];
|
||||
data[1] = bt->read_data[3];
|
||||
if (length < msg_len) bt->truncated = 1;
|
||||
if (bt->truncated) { /* can be set in read_all_bytes() */
|
||||
data[2] = IPMI_ERR_MSG_TRUNCATED;
|
||||
msg_len = 3;
|
||||
} else memcpy(data + 2, bt->read_data + 4, msg_len - 2);
|
||||
|
||||
if (bt_debug & BT_DEBUG_MSG) {
|
||||
printk (KERN_WARNING "BT: res (raw)");
|
||||
for (i = 0; i < msg_len; i++) printk(" %02x", data[i]);
|
||||
printk ("\n");
|
||||
}
|
||||
}
|
||||
bt->read_count = 0; /* paranoia */
|
||||
return msg_len;
|
||||
}
|
||||
|
||||
/* This bit's functionality is optional */
|
||||
#define BT_BMC_HWRST 0x80
|
||||
|
||||
static void reset_flags(struct si_sm_data *bt)
|
||||
{
|
||||
if (BT_STATUS & BT_H_BUSY) BT_CONTROL(BT_H_BUSY);
|
||||
if (BT_STATUS & BT_B_BUSY) BT_CONTROL(BT_B_BUSY);
|
||||
BT_CONTROL(BT_CLR_WR_PTR);
|
||||
BT_CONTROL(BT_SMS_ATN);
|
||||
BT_INTMASK_W(BT_BMC_HWRST);
|
||||
#ifdef DEVELOPMENT_ONLY_NOT_FOR_PRODUCTION
|
||||
if (BT_STATUS & BT_B2H_ATN) {
|
||||
int i;
|
||||
BT_CONTROL(BT_H_BUSY);
|
||||
BT_CONTROL(BT_B2H_ATN);
|
||||
BT_CONTROL(BT_CLR_RD_PTR);
|
||||
for (i = 0; i < IPMI_MAX_MSG_LENGTH + 2; i++) BMC2HOST;
|
||||
BT_CONTROL(BT_H_BUSY);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void write_all_bytes(struct si_sm_data *bt)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (bt_debug & BT_DEBUG_MSG) {
|
||||
printk(KERN_WARNING "BT: write %d bytes seq=0x%02X",
|
||||
bt->write_count, bt->seq);
|
||||
for (i = 0; i < bt->write_count; i++)
|
||||
printk (" %02x", bt->write_data[i]);
|
||||
printk ("\n");
|
||||
}
|
||||
for (i = 0; i < bt->write_count; i++) HOST2BMC(bt->write_data[i]);
|
||||
}
|
||||
|
||||
static inline int read_all_bytes(struct si_sm_data *bt)
|
||||
{
|
||||
unsigned char i;
|
||||
|
||||
bt->read_data[0] = BMC2HOST;
|
||||
bt->read_count = bt->read_data[0];
|
||||
if (bt_debug & BT_DEBUG_MSG)
|
||||
printk(KERN_WARNING "BT: read %d bytes:", bt->read_count);
|
||||
|
||||
/* minimum: length, NetFn, Seq, Cmd, cCode == 5 total, or 4 more
|
||||
following the length byte. */
|
||||
if (bt->read_count < 4 || bt->read_count >= IPMI_MAX_MSG_LENGTH) {
|
||||
if (bt_debug & BT_DEBUG_MSG)
|
||||
printk("bad length %d\n", bt->read_count);
|
||||
bt->truncated = 1;
|
||||
return 1; /* let next XACTION START clean it up */
|
||||
}
|
||||
for (i = 1; i <= bt->read_count; i++) bt->read_data[i] = BMC2HOST;
|
||||
bt->read_count++; /* account for the length byte */
|
||||
|
||||
if (bt_debug & BT_DEBUG_MSG) {
|
||||
for (i = 0; i < bt->read_count; i++)
|
||||
printk (" %02x", bt->read_data[i]);
|
||||
printk ("\n");
|
||||
}
|
||||
if (bt->seq != bt->write_data[2]) /* idiot check */
|
||||
printk(KERN_WARNING "BT: internal error: sequence mismatch\n");
|
||||
|
||||
/* per the spec, the (NetFn, Seq, Cmd) tuples should match */
|
||||
if ((bt->read_data[3] == bt->write_data[3]) && /* Cmd */
|
||||
(bt->read_data[2] == bt->write_data[2]) && /* Sequence */
|
||||
((bt->read_data[1] & 0xF8) == (bt->write_data[1] & 0xF8)))
|
||||
return 1;
|
||||
|
||||
if (bt_debug & BT_DEBUG_MSG) printk(KERN_WARNING "BT: bad packet: "
|
||||
"want 0x(%02X, %02X, %02X) got (%02X, %02X, %02X)\n",
|
||||
bt->write_data[1], bt->write_data[2], bt->write_data[3],
|
||||
bt->read_data[1], bt->read_data[2], bt->read_data[3]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Modifies bt->state appropriately, need to get into the bt_event() switch */
|
||||
|
||||
static void error_recovery(struct si_sm_data *bt, char *reason)
|
||||
{
|
||||
unsigned char status;
|
||||
char buf[40]; /* For getting status */
|
||||
|
||||
bt->timeout = BT_NORMAL_TIMEOUT; /* various places want to retry */
|
||||
|
||||
status = BT_STATUS;
|
||||
printk(KERN_WARNING "BT: %s in %s %s ", reason, STATE2TXT,
|
||||
STATUS2TXT(buf));
|
||||
|
||||
(bt->error_retries)++;
|
||||
if (bt->error_retries > BT_RETRY_LIMIT) {
|
||||
printk("retry limit (%d) exceeded\n", BT_RETRY_LIMIT);
|
||||
bt->state = BT_STATE_HOSED;
|
||||
if (!bt->nonzero_status)
|
||||
printk(KERN_ERR "IPMI: BT stuck, try power cycle\n");
|
||||
else if (bt->seq == FIRST_SEQ + BT_RETRY_LIMIT) {
|
||||
/* most likely during insmod */
|
||||
printk(KERN_WARNING "IPMI: BT reset (takes 5 secs)\n");
|
||||
bt->state = BT_STATE_RESET1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Sometimes the BMC queues get in an "off-by-one" state...*/
|
||||
if ((bt->state == BT_STATE_B2H_WAIT) && (status & BT_B2H_ATN)) {
|
||||
printk("retry B2H_WAIT\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printk("restart command\n");
|
||||
bt->state = BT_STATE_RESTART;
|
||||
}
|
||||
|
||||
/* Check the status and (possibly) advance the BT state machine. The
|
||||
default return is SI_SM_CALL_WITH_DELAY. */
|
||||
|
||||
static enum si_sm_result bt_event(struct si_sm_data *bt, long time)
|
||||
{
|
||||
unsigned char status;
|
||||
char buf[40]; /* For getting status */
|
||||
int i;
|
||||
|
||||
status = BT_STATUS;
|
||||
bt->nonzero_status |= status;
|
||||
|
||||
if ((bt_debug & BT_DEBUG_STATES) && (bt->state != bt->last_state))
|
||||
printk(KERN_WARNING "BT: %s %s TO=%ld - %ld \n",
|
||||
STATE2TXT,
|
||||
STATUS2TXT(buf),
|
||||
bt->timeout,
|
||||
time);
|
||||
bt->last_state = bt->state;
|
||||
|
||||
if (bt->state == BT_STATE_HOSED) return SI_SM_HOSED;
|
||||
|
||||
if (bt->state != BT_STATE_IDLE) { /* do timeout test */
|
||||
|
||||
/* Certain states, on error conditions, can lock up a CPU
|
||||
because they are effectively in an infinite loop with
|
||||
CALL_WITHOUT_DELAY (right back here with time == 0).
|
||||
Prevent infinite lockup by ALWAYS decrementing timeout. */
|
||||
|
||||
/* FIXME: bt_event is sometimes called with time > BT_NORMAL_TIMEOUT
|
||||
(noticed in ipmi_smic_sm.c January 2004) */
|
||||
|
||||
if ((time <= 0) || (time >= BT_NORMAL_TIMEOUT)) time = 100;
|
||||
bt->timeout -= time;
|
||||
if ((bt->timeout < 0) && (bt->state < BT_STATE_RESET1)) {
|
||||
error_recovery(bt, "timed out");
|
||||
return SI_SM_CALL_WITHOUT_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
switch (bt->state) {
|
||||
|
||||
case BT_STATE_IDLE: /* check for asynchronous messages */
|
||||
if (status & BT_SMS_ATN) {
|
||||
BT_CONTROL(BT_SMS_ATN); /* clear it */
|
||||
return SI_SM_ATTN;
|
||||
}
|
||||
return SI_SM_IDLE;
|
||||
|
||||
case BT_STATE_XACTION_START:
|
||||
if (status & BT_H_BUSY) {
|
||||
BT_CONTROL(BT_H_BUSY);
|
||||
break;
|
||||
}
|
||||
if (status & BT_B2H_ATN) break;
|
||||
bt->state = BT_STATE_WRITE_BYTES;
|
||||
return SI_SM_CALL_WITHOUT_DELAY; /* for logging */
|
||||
|
||||
case BT_STATE_WRITE_BYTES:
|
||||
if (status & (BT_B_BUSY | BT_H2B_ATN)) break;
|
||||
BT_CONTROL(BT_CLR_WR_PTR);
|
||||
write_all_bytes(bt);
|
||||
BT_CONTROL(BT_H2B_ATN); /* clears too fast to catch? */
|
||||
bt->state = BT_STATE_WRITE_CONSUME;
|
||||
return SI_SM_CALL_WITHOUT_DELAY; /* it MIGHT sail through */
|
||||
|
||||
case BT_STATE_WRITE_CONSUME: /* BMCs usually blow right thru here */
|
||||
if (status & (BT_H2B_ATN | BT_B_BUSY)) break;
|
||||
bt->state = BT_STATE_B2H_WAIT;
|
||||
/* fall through with status */
|
||||
|
||||
/* Stay in BT_STATE_B2H_WAIT until a packet matches. However, spinning
|
||||
hard here, constantly reading status, seems to hold off the
|
||||
generation of B2H_ATN so ALWAYS return CALL_WITH_DELAY. */
|
||||
|
||||
case BT_STATE_B2H_WAIT:
|
||||
if (!(status & BT_B2H_ATN)) break;
|
||||
|
||||
/* Assume ordered, uncached writes: no need to wait */
|
||||
if (!(status & BT_H_BUSY)) BT_CONTROL(BT_H_BUSY); /* set */
|
||||
BT_CONTROL(BT_B2H_ATN); /* clear it, ACK to the BMC */
|
||||
BT_CONTROL(BT_CLR_RD_PTR); /* reset the queue */
|
||||
i = read_all_bytes(bt);
|
||||
BT_CONTROL(BT_H_BUSY); /* clear */
|
||||
if (!i) break; /* Try this state again */
|
||||
bt->state = BT_STATE_READ_END;
|
||||
return SI_SM_CALL_WITHOUT_DELAY; /* for logging */
|
||||
|
||||
case BT_STATE_READ_END:
|
||||
|
||||
/* I could wait on BT_H_BUSY to go clear for a truly clean
|
||||
exit. However, this is already done in XACTION_START
|
||||
and the (possible) extra loop/status/possible wait affects
|
||||
performance. So, as long as it works, just ignore H_BUSY */
|
||||
|
||||
#ifdef MAKE_THIS_TRUE_IF_NECESSARY
|
||||
|
||||
if (status & BT_H_BUSY) break;
|
||||
#endif
|
||||
bt->seq++;
|
||||
bt->state = BT_STATE_IDLE;
|
||||
return SI_SM_TRANSACTION_COMPLETE;
|
||||
|
||||
case BT_STATE_RESET1:
|
||||
reset_flags(bt);
|
||||
bt->timeout = BT_RESET_DELAY;
|
||||
bt->state = BT_STATE_RESET2;
|
||||
break;
|
||||
|
||||
case BT_STATE_RESET2: /* Send a soft reset */
|
||||
BT_CONTROL(BT_CLR_WR_PTR);
|
||||
HOST2BMC(3); /* number of bytes following */
|
||||
HOST2BMC(0x18); /* NetFn/LUN == Application, LUN 0 */
|
||||
HOST2BMC(42); /* Sequence number */
|
||||
HOST2BMC(3); /* Cmd == Soft reset */
|
||||
BT_CONTROL(BT_H2B_ATN);
|
||||
bt->state = BT_STATE_RESET3;
|
||||
break;
|
||||
|
||||
case BT_STATE_RESET3:
|
||||
if (bt->timeout > 0) return SI_SM_CALL_WITH_DELAY;
|
||||
bt->state = BT_STATE_RESTART; /* printk in debug modes */
|
||||
break;
|
||||
|
||||
case BT_STATE_RESTART: /* don't reset retries! */
|
||||
bt->write_data[2] = ++bt->seq;
|
||||
bt->read_count = 0;
|
||||
bt->nonzero_status = 0;
|
||||
bt->timeout = BT_NORMAL_TIMEOUT;
|
||||
bt->state = BT_STATE_XACTION_START;
|
||||
break;
|
||||
|
||||
default: /* HOSED is supposed to be caught much earlier */
|
||||
error_recovery(bt, "internal logic error");
|
||||
break;
|
||||
}
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
|
||||
static int bt_detect(struct si_sm_data *bt)
|
||||
{
|
||||
/* It's impossible for the BT status and interrupt registers to be
|
||||
all 1's, (assuming a properly functioning, self-initialized BMC)
|
||||
but that's what you get from reading a bogus address, so we
|
||||
test that first. The calling routine uses negative logic. */
|
||||
|
||||
if ((BT_STATUS == 0xFF) && (BT_INTMASK_R == 0xFF)) return 1;
|
||||
reset_flags(bt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bt_cleanup(struct si_sm_data *bt)
|
||||
{
|
||||
}
|
||||
|
||||
static int bt_size(void)
|
||||
{
|
||||
return sizeof(struct si_sm_data);
|
||||
}
|
||||
|
||||
struct si_sm_handlers bt_smi_handlers =
|
||||
{
|
||||
.version = IPMI_BT_VERSION,
|
||||
.init_data = bt_init_data,
|
||||
.start_transaction = bt_start_transaction,
|
||||
.get_result = bt_get_result,
|
||||
.event = bt_event,
|
||||
.detect = bt_detect,
|
||||
.cleanup = bt_cleanup,
|
||||
.size = bt_size,
|
||||
};
|
||||
582
extra/linux-2.6.10/drivers/char/ipmi/ipmi_devintf.c
Normal file
582
extra/linux-2.6.10/drivers/char/ipmi/ipmi_devintf.c
Normal file
@@ -0,0 +1,582 @@
|
||||
/*
|
||||
* ipmi_devintf.c
|
||||
*
|
||||
* Linux device interface for the IPMI message handler.
|
||||
*
|
||||
* Author: MontaVista Software, Inc.
|
||||
* Corey Minyard <minyard@mvista.com>
|
||||
* source@mvista.com
|
||||
*
|
||||
* Copyright 2002 MontaVista Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/system.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/devfs_fs_kernel.h>
|
||||
#include <linux/ipmi.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define IPMI_DEVINTF_VERSION "v33"
|
||||
|
||||
struct ipmi_file_private
|
||||
{
|
||||
ipmi_user_t user;
|
||||
spinlock_t recv_msg_lock;
|
||||
struct list_head recv_msgs;
|
||||
struct file *file;
|
||||
struct fasync_struct *fasync_queue;
|
||||
wait_queue_head_t wait;
|
||||
struct semaphore recv_sem;
|
||||
int default_retries;
|
||||
unsigned int default_retry_time_ms;
|
||||
};
|
||||
|
||||
static void file_receive_handler(struct ipmi_recv_msg *msg,
|
||||
void *handler_data)
|
||||
{
|
||||
struct ipmi_file_private *priv = handler_data;
|
||||
int was_empty;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&(priv->recv_msg_lock), flags);
|
||||
|
||||
was_empty = list_empty(&(priv->recv_msgs));
|
||||
list_add_tail(&(msg->link), &(priv->recv_msgs));
|
||||
|
||||
if (was_empty) {
|
||||
wake_up_interruptible(&priv->wait);
|
||||
kill_fasync(&priv->fasync_queue, SIGIO, POLL_IN);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
|
||||
}
|
||||
|
||||
static unsigned int ipmi_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct ipmi_file_private *priv = file->private_data;
|
||||
unsigned int mask = 0;
|
||||
unsigned long flags;
|
||||
|
||||
poll_wait(file, &priv->wait, wait);
|
||||
|
||||
spin_lock_irqsave(&priv->recv_msg_lock, flags);
|
||||
|
||||
if (! list_empty(&(priv->recv_msgs)))
|
||||
mask |= (POLLIN | POLLRDNORM);
|
||||
|
||||
spin_unlock_irqrestore(&priv->recv_msg_lock, flags);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static int ipmi_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
struct ipmi_file_private *priv = file->private_data;
|
||||
int result;
|
||||
|
||||
result = fasync_helper(fd, file, on, &priv->fasync_queue);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
static struct ipmi_user_hndl ipmi_hndlrs =
|
||||
{
|
||||
.ipmi_recv_hndl = file_receive_handler,
|
||||
};
|
||||
|
||||
static int ipmi_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int if_num = iminor(inode);
|
||||
int rv;
|
||||
struct ipmi_file_private *priv;
|
||||
|
||||
|
||||
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->file = file;
|
||||
|
||||
rv = ipmi_create_user(if_num,
|
||||
&ipmi_hndlrs,
|
||||
priv,
|
||||
&(priv->user));
|
||||
if (rv) {
|
||||
kfree(priv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
file->private_data = priv;
|
||||
|
||||
spin_lock_init(&(priv->recv_msg_lock));
|
||||
INIT_LIST_HEAD(&(priv->recv_msgs));
|
||||
init_waitqueue_head(&priv->wait);
|
||||
priv->fasync_queue = NULL;
|
||||
sema_init(&(priv->recv_sem), 1);
|
||||
|
||||
/* Use the low-level defaults. */
|
||||
priv->default_retries = -1;
|
||||
priv->default_retry_time_ms = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipmi_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct ipmi_file_private *priv = file->private_data;
|
||||
int rv;
|
||||
|
||||
rv = ipmi_destroy_user(priv->user);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
ipmi_fasync (-1, file, 0);
|
||||
|
||||
/* FIXME - free the messages in the list. */
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_send_req(ipmi_user_t user,
|
||||
struct ipmi_req *req,
|
||||
int retries,
|
||||
unsigned int retry_time_ms)
|
||||
{
|
||||
int rv;
|
||||
struct ipmi_addr addr;
|
||||
struct kernel_ipmi_msg msg;
|
||||
|
||||
if (req->addr_len > sizeof(struct ipmi_addr))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&addr, req->addr, req->addr_len))
|
||||
return -EFAULT;
|
||||
|
||||
msg.netfn = req->msg.netfn;
|
||||
msg.cmd = req->msg.cmd;
|
||||
msg.data_len = req->msg.data_len;
|
||||
msg.data = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
|
||||
if (!msg.data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* From here out we cannot return, we must jump to "out" for
|
||||
error exits to free msgdata. */
|
||||
|
||||
rv = ipmi_validate_addr(&addr, req->addr_len);
|
||||
if (rv)
|
||||
goto out;
|
||||
|
||||
if (req->msg.data != NULL) {
|
||||
if (req->msg.data_len > IPMI_MAX_MSG_LENGTH) {
|
||||
rv = -EMSGSIZE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_from_user(msg.data,
|
||||
req->msg.data,
|
||||
req->msg.data_len))
|
||||
{
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
msg.data_len = 0;
|
||||
}
|
||||
|
||||
rv = ipmi_request_settime(user,
|
||||
&addr,
|
||||
req->msgid,
|
||||
&msg,
|
||||
NULL,
|
||||
0,
|
||||
retries,
|
||||
retry_time_ms);
|
||||
out:
|
||||
kfree(msg.data);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int ipmi_ioctl(struct inode *inode,
|
||||
struct file *file,
|
||||
unsigned int cmd,
|
||||
unsigned long data)
|
||||
{
|
||||
int rv = -EINVAL;
|
||||
struct ipmi_file_private *priv = file->private_data;
|
||||
void __user *arg = (void __user *)data;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case IPMICTL_SEND_COMMAND:
|
||||
{
|
||||
struct ipmi_req req;
|
||||
|
||||
if (copy_from_user(&req, arg, sizeof(req))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
rv = handle_send_req(priv->user,
|
||||
&req,
|
||||
priv->default_retries,
|
||||
priv->default_retry_time_ms);
|
||||
break;
|
||||
}
|
||||
|
||||
case IPMICTL_SEND_COMMAND_SETTIME:
|
||||
{
|
||||
struct ipmi_req_settime req;
|
||||
|
||||
if (copy_from_user(&req, arg, sizeof(req))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
rv = handle_send_req(priv->user,
|
||||
&req.req,
|
||||
req.retries,
|
||||
req.retry_time_ms);
|
||||
break;
|
||||
}
|
||||
|
||||
case IPMICTL_RECEIVE_MSG:
|
||||
case IPMICTL_RECEIVE_MSG_TRUNC:
|
||||
{
|
||||
struct ipmi_recv rsp;
|
||||
int addr_len;
|
||||
struct list_head *entry;
|
||||
struct ipmi_recv_msg *msg;
|
||||
unsigned long flags;
|
||||
|
||||
|
||||
rv = 0;
|
||||
if (copy_from_user(&rsp, arg, sizeof(rsp))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
/* We claim a semaphore because we don't want two
|
||||
users getting something from the queue at a time.
|
||||
Since we have to release the spinlock before we can
|
||||
copy the data to the user, it's possible another
|
||||
user will grab something from the queue, too. Then
|
||||
the messages might get out of order if something
|
||||
fails and the message gets put back onto the
|
||||
queue. This semaphore prevents that problem. */
|
||||
down(&(priv->recv_sem));
|
||||
|
||||
/* Grab the message off the list. */
|
||||
spin_lock_irqsave(&(priv->recv_msg_lock), flags);
|
||||
if (list_empty(&(priv->recv_msgs))) {
|
||||
spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
|
||||
rv = -EAGAIN;
|
||||
goto recv_err;
|
||||
}
|
||||
entry = priv->recv_msgs.next;
|
||||
msg = list_entry(entry, struct ipmi_recv_msg, link);
|
||||
list_del(entry);
|
||||
spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
|
||||
|
||||
addr_len = ipmi_addr_length(msg->addr.addr_type);
|
||||
if (rsp.addr_len < addr_len)
|
||||
{
|
||||
rv = -EINVAL;
|
||||
goto recv_putback_on_err;
|
||||
}
|
||||
|
||||
if (copy_to_user(rsp.addr, &(msg->addr), addr_len)) {
|
||||
rv = -EFAULT;
|
||||
goto recv_putback_on_err;
|
||||
}
|
||||
rsp.addr_len = addr_len;
|
||||
|
||||
rsp.recv_type = msg->recv_type;
|
||||
rsp.msgid = msg->msgid;
|
||||
rsp.msg.netfn = msg->msg.netfn;
|
||||
rsp.msg.cmd = msg->msg.cmd;
|
||||
|
||||
if (msg->msg.data_len > 0) {
|
||||
if (rsp.msg.data_len < msg->msg.data_len) {
|
||||
rv = -EMSGSIZE;
|
||||
if (cmd == IPMICTL_RECEIVE_MSG_TRUNC) {
|
||||
msg->msg.data_len = rsp.msg.data_len;
|
||||
} else {
|
||||
goto recv_putback_on_err;
|
||||
}
|
||||
}
|
||||
|
||||
if (copy_to_user(rsp.msg.data,
|
||||
msg->msg.data,
|
||||
msg->msg.data_len))
|
||||
{
|
||||
rv = -EFAULT;
|
||||
goto recv_putback_on_err;
|
||||
}
|
||||
rsp.msg.data_len = msg->msg.data_len;
|
||||
} else {
|
||||
rsp.msg.data_len = 0;
|
||||
}
|
||||
|
||||
if (copy_to_user(arg, &rsp, sizeof(rsp))) {
|
||||
rv = -EFAULT;
|
||||
goto recv_putback_on_err;
|
||||
}
|
||||
|
||||
up(&(priv->recv_sem));
|
||||
ipmi_free_recv_msg(msg);
|
||||
break;
|
||||
|
||||
recv_putback_on_err:
|
||||
/* If we got an error, put the message back onto
|
||||
the head of the queue. */
|
||||
spin_lock_irqsave(&(priv->recv_msg_lock), flags);
|
||||
list_add(entry, &(priv->recv_msgs));
|
||||
spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
|
||||
up(&(priv->recv_sem));
|
||||
break;
|
||||
|
||||
recv_err:
|
||||
up(&(priv->recv_sem));
|
||||
break;
|
||||
}
|
||||
|
||||
case IPMICTL_REGISTER_FOR_CMD:
|
||||
{
|
||||
struct ipmi_cmdspec val;
|
||||
|
||||
if (copy_from_user(&val, arg, sizeof(val))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
case IPMICTL_UNREGISTER_FOR_CMD:
|
||||
{
|
||||
struct ipmi_cmdspec val;
|
||||
|
||||
if (copy_from_user(&val, arg, sizeof(val))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
case IPMICTL_SET_GETS_EVENTS_CMD:
|
||||
{
|
||||
int val;
|
||||
|
||||
if (copy_from_user(&val, arg, sizeof(val))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
rv = ipmi_set_gets_events(priv->user, val);
|
||||
break;
|
||||
}
|
||||
|
||||
case IPMICTL_SET_MY_ADDRESS_CMD:
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
if (copy_from_user(&val, arg, sizeof(val))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ipmi_set_my_address(priv->user, val);
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case IPMICTL_GET_MY_ADDRESS_CMD:
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
val = ipmi_get_my_address(priv->user);
|
||||
|
||||
if (copy_to_user(arg, &val, sizeof(val))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case IPMICTL_SET_MY_LUN_CMD:
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
if (copy_from_user(&val, arg, sizeof(val))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
ipmi_set_my_LUN(priv->user, val);
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case IPMICTL_GET_MY_LUN_CMD:
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
val = ipmi_get_my_LUN(priv->user);
|
||||
|
||||
if (copy_to_user(arg, &val, sizeof(val))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
case IPMICTL_SET_TIMING_PARMS_CMD:
|
||||
{
|
||||
struct ipmi_timing_parms parms;
|
||||
|
||||
if (copy_from_user(&parms, arg, sizeof(parms))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
priv->default_retries = parms.retries;
|
||||
priv->default_retry_time_ms = parms.retry_time_ms;
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case IPMICTL_GET_TIMING_PARMS_CMD:
|
||||
{
|
||||
struct ipmi_timing_parms parms;
|
||||
|
||||
parms.retries = priv->default_retries;
|
||||
parms.retry_time_ms = priv->default_retry_time_ms;
|
||||
|
||||
if (copy_to_user(arg, &parms, sizeof(parms))) {
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static struct file_operations ipmi_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.ioctl = ipmi_ioctl,
|
||||
.open = ipmi_open,
|
||||
.release = ipmi_release,
|
||||
.fasync = ipmi_fasync,
|
||||
.poll = ipmi_poll,
|
||||
};
|
||||
|
||||
#define DEVICE_NAME "ipmidev"
|
||||
|
||||
static int ipmi_major = 0;
|
||||
module_param(ipmi_major, int, 0);
|
||||
MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By"
|
||||
" default, or if you set it to zero, it will choose the next"
|
||||
" available device. Setting it to -1 will disable the"
|
||||
" interface. Other values will set the major device number"
|
||||
" to that value.");
|
||||
|
||||
static void ipmi_new_smi(int if_num)
|
||||
{
|
||||
devfs_mk_cdev(MKDEV(ipmi_major, if_num),
|
||||
S_IFCHR | S_IRUSR | S_IWUSR,
|
||||
"ipmidev/%d", if_num);
|
||||
}
|
||||
|
||||
static void ipmi_smi_gone(int if_num)
|
||||
{
|
||||
devfs_remove("ipmidev/%d", if_num);
|
||||
}
|
||||
|
||||
static struct ipmi_smi_watcher smi_watcher =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.new_smi = ipmi_new_smi,
|
||||
.smi_gone = ipmi_smi_gone,
|
||||
};
|
||||
|
||||
static __init int init_ipmi_devintf(void)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if (ipmi_major < 0)
|
||||
return -EINVAL;
|
||||
|
||||
printk(KERN_INFO "ipmi device interface version "
|
||||
IPMI_DEVINTF_VERSION "\n");
|
||||
|
||||
rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
|
||||
if (rv < 0) {
|
||||
printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (ipmi_major == 0) {
|
||||
ipmi_major = rv;
|
||||
}
|
||||
|
||||
devfs_mk_dir(DEVICE_NAME);
|
||||
|
||||
rv = ipmi_smi_watcher_register(&smi_watcher);
|
||||
if (rv) {
|
||||
unregister_chrdev(ipmi_major, DEVICE_NAME);
|
||||
printk(KERN_WARNING "ipmi: can't register smi watcher\n");
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(init_ipmi_devintf);
|
||||
|
||||
static __exit void cleanup_ipmi(void)
|
||||
{
|
||||
ipmi_smi_watcher_unregister(&smi_watcher);
|
||||
devfs_remove(DEVICE_NAME);
|
||||
unregister_chrdev(ipmi_major, DEVICE_NAME);
|
||||
}
|
||||
module_exit(cleanup_ipmi);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
500
extra/linux-2.6.10/drivers/char/ipmi/ipmi_kcs_sm.c
Normal file
500
extra/linux-2.6.10/drivers/char/ipmi/ipmi_kcs_sm.c
Normal file
@@ -0,0 +1,500 @@
|
||||
/*
|
||||
* ipmi_kcs_sm.c
|
||||
*
|
||||
* State machine for handling IPMI KCS interfaces.
|
||||
*
|
||||
* Author: MontaVista Software, Inc.
|
||||
* Corey Minyard <minyard@mvista.com>
|
||||
* source@mvista.com
|
||||
*
|
||||
* Copyright 2002 MontaVista Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This state machine is taken from the state machine in the IPMI spec,
|
||||
* pretty much verbatim. If you have questions about the states, see
|
||||
* that document.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h> /* For printk. */
|
||||
#include <linux/string.h>
|
||||
#include <linux/ipmi_msgdefs.h> /* for completion codes */
|
||||
#include "ipmi_si_sm.h"
|
||||
|
||||
#define IPMI_KCS_VERSION "v33"
|
||||
|
||||
/* Set this if you want a printout of why the state machine was hosed
|
||||
when it gets hosed. */
|
||||
#define DEBUG_HOSED_REASON
|
||||
|
||||
/* Print the state machine state on entry every time. */
|
||||
#undef DEBUG_STATE
|
||||
|
||||
/* The states the KCS driver may be in. */
|
||||
enum kcs_states {
|
||||
KCS_IDLE, /* The KCS interface is currently
|
||||
doing nothing. */
|
||||
KCS_START_OP, /* We are starting an operation. The
|
||||
data is in the output buffer, but
|
||||
nothing has been done to the
|
||||
interface yet. This was added to
|
||||
the state machine in the spec to
|
||||
wait for the initial IBF. */
|
||||
KCS_WAIT_WRITE_START, /* We have written a write cmd to the
|
||||
interface. */
|
||||
KCS_WAIT_WRITE, /* We are writing bytes to the
|
||||
interface. */
|
||||
KCS_WAIT_WRITE_END, /* We have written the write end cmd
|
||||
to the interface, and still need to
|
||||
write the last byte. */
|
||||
KCS_WAIT_READ, /* We are waiting to read data from
|
||||
the interface. */
|
||||
KCS_ERROR0, /* State to transition to the error
|
||||
handler, this was added to the
|
||||
state machine in the spec to be
|
||||
sure IBF was there. */
|
||||
KCS_ERROR1, /* First stage error handler, wait for
|
||||
the interface to respond. */
|
||||
KCS_ERROR2, /* The abort cmd has been written,
|
||||
wait for the interface to
|
||||
respond. */
|
||||
KCS_ERROR3, /* We wrote some data to the
|
||||
interface, wait for it to switch to
|
||||
read mode. */
|
||||
KCS_HOSED /* The hardware failed to follow the
|
||||
state machine. */
|
||||
};
|
||||
|
||||
#define MAX_KCS_READ_SIZE 80
|
||||
#define MAX_KCS_WRITE_SIZE 80
|
||||
|
||||
/* Timeouts in microseconds. */
|
||||
#define IBF_RETRY_TIMEOUT 1000000
|
||||
#define OBF_RETRY_TIMEOUT 1000000
|
||||
#define MAX_ERROR_RETRIES 10
|
||||
|
||||
struct si_sm_data
|
||||
{
|
||||
enum kcs_states state;
|
||||
struct si_sm_io *io;
|
||||
unsigned char write_data[MAX_KCS_WRITE_SIZE];
|
||||
int write_pos;
|
||||
int write_count;
|
||||
int orig_write_count;
|
||||
unsigned char read_data[MAX_KCS_READ_SIZE];
|
||||
int read_pos;
|
||||
int truncated;
|
||||
|
||||
unsigned int error_retries;
|
||||
long ibf_timeout;
|
||||
long obf_timeout;
|
||||
};
|
||||
|
||||
static unsigned int init_kcs_data(struct si_sm_data *kcs,
|
||||
struct si_sm_io *io)
|
||||
{
|
||||
kcs->state = KCS_IDLE;
|
||||
kcs->io = io;
|
||||
kcs->write_pos = 0;
|
||||
kcs->write_count = 0;
|
||||
kcs->orig_write_count = 0;
|
||||
kcs->read_pos = 0;
|
||||
kcs->error_retries = 0;
|
||||
kcs->truncated = 0;
|
||||
kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
|
||||
kcs->obf_timeout = OBF_RETRY_TIMEOUT;
|
||||
|
||||
/* Reserve 2 I/O bytes. */
|
||||
return 2;
|
||||
}
|
||||
|
||||
static inline unsigned char read_status(struct si_sm_data *kcs)
|
||||
{
|
||||
return kcs->io->inputb(kcs->io, 1);
|
||||
}
|
||||
|
||||
static inline unsigned char read_data(struct si_sm_data *kcs)
|
||||
{
|
||||
return kcs->io->inputb(kcs->io, 0);
|
||||
}
|
||||
|
||||
static inline void write_cmd(struct si_sm_data *kcs, unsigned char data)
|
||||
{
|
||||
kcs->io->outputb(kcs->io, 1, data);
|
||||
}
|
||||
|
||||
static inline void write_data(struct si_sm_data *kcs, unsigned char data)
|
||||
{
|
||||
kcs->io->outputb(kcs->io, 0, data);
|
||||
}
|
||||
|
||||
/* Control codes. */
|
||||
#define KCS_GET_STATUS_ABORT 0x60
|
||||
#define KCS_WRITE_START 0x61
|
||||
#define KCS_WRITE_END 0x62
|
||||
#define KCS_READ_BYTE 0x68
|
||||
|
||||
/* Status bits. */
|
||||
#define GET_STATUS_STATE(status) (((status) >> 6) & 0x03)
|
||||
#define KCS_IDLE_STATE 0
|
||||
#define KCS_READ_STATE 1
|
||||
#define KCS_WRITE_STATE 2
|
||||
#define KCS_ERROR_STATE 3
|
||||
#define GET_STATUS_ATN(status) ((status) & 0x04)
|
||||
#define GET_STATUS_IBF(status) ((status) & 0x02)
|
||||
#define GET_STATUS_OBF(status) ((status) & 0x01)
|
||||
|
||||
|
||||
static inline void write_next_byte(struct si_sm_data *kcs)
|
||||
{
|
||||
write_data(kcs, kcs->write_data[kcs->write_pos]);
|
||||
(kcs->write_pos)++;
|
||||
(kcs->write_count)--;
|
||||
}
|
||||
|
||||
static inline void start_error_recovery(struct si_sm_data *kcs, char *reason)
|
||||
{
|
||||
(kcs->error_retries)++;
|
||||
if (kcs->error_retries > MAX_ERROR_RETRIES) {
|
||||
#ifdef DEBUG_HOSED_REASON
|
||||
printk("ipmi_kcs_sm: kcs hosed: %s\n", reason);
|
||||
#endif
|
||||
kcs->state = KCS_HOSED;
|
||||
} else {
|
||||
kcs->state = KCS_ERROR0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void read_next_byte(struct si_sm_data *kcs)
|
||||
{
|
||||
if (kcs->read_pos >= MAX_KCS_READ_SIZE) {
|
||||
/* Throw the data away and mark it truncated. */
|
||||
read_data(kcs);
|
||||
kcs->truncated = 1;
|
||||
} else {
|
||||
kcs->read_data[kcs->read_pos] = read_data(kcs);
|
||||
(kcs->read_pos)++;
|
||||
}
|
||||
write_data(kcs, KCS_READ_BYTE);
|
||||
}
|
||||
|
||||
static inline int check_ibf(struct si_sm_data *kcs, unsigned char status,
|
||||
long time)
|
||||
{
|
||||
if (GET_STATUS_IBF(status)) {
|
||||
kcs->ibf_timeout -= time;
|
||||
if (kcs->ibf_timeout < 0) {
|
||||
start_error_recovery(kcs, "IBF not ready in time");
|
||||
kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int check_obf(struct si_sm_data *kcs, unsigned char status,
|
||||
long time)
|
||||
{
|
||||
if (! GET_STATUS_OBF(status)) {
|
||||
kcs->obf_timeout -= time;
|
||||
if (kcs->obf_timeout < 0) {
|
||||
start_error_recovery(kcs, "OBF not ready in time");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
kcs->obf_timeout = OBF_RETRY_TIMEOUT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void clear_obf(struct si_sm_data *kcs, unsigned char status)
|
||||
{
|
||||
if (GET_STATUS_OBF(status))
|
||||
read_data(kcs);
|
||||
}
|
||||
|
||||
static void restart_kcs_transaction(struct si_sm_data *kcs)
|
||||
{
|
||||
kcs->write_count = kcs->orig_write_count;
|
||||
kcs->write_pos = 0;
|
||||
kcs->read_pos = 0;
|
||||
kcs->state = KCS_WAIT_WRITE_START;
|
||||
kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
|
||||
kcs->obf_timeout = OBF_RETRY_TIMEOUT;
|
||||
write_cmd(kcs, KCS_WRITE_START);
|
||||
}
|
||||
|
||||
static int start_kcs_transaction(struct si_sm_data *kcs, unsigned char *data,
|
||||
unsigned int size)
|
||||
{
|
||||
if ((size < 2) || (size > MAX_KCS_WRITE_SIZE)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((kcs->state != KCS_IDLE) && (kcs->state != KCS_HOSED)) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
kcs->error_retries = 0;
|
||||
memcpy(kcs->write_data, data, size);
|
||||
kcs->write_count = size;
|
||||
kcs->orig_write_count = size;
|
||||
kcs->write_pos = 0;
|
||||
kcs->read_pos = 0;
|
||||
kcs->state = KCS_START_OP;
|
||||
kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
|
||||
kcs->obf_timeout = OBF_RETRY_TIMEOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_kcs_result(struct si_sm_data *kcs, unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
if (length < kcs->read_pos) {
|
||||
kcs->read_pos = length;
|
||||
kcs->truncated = 1;
|
||||
}
|
||||
|
||||
memcpy(data, kcs->read_data, kcs->read_pos);
|
||||
|
||||
if ((length >= 3) && (kcs->read_pos < 3)) {
|
||||
/* Guarantee that we return at least 3 bytes, with an
|
||||
error in the third byte if it is too short. */
|
||||
data[2] = IPMI_ERR_UNSPECIFIED;
|
||||
kcs->read_pos = 3;
|
||||
}
|
||||
if (kcs->truncated) {
|
||||
/* Report a truncated error. We might overwrite
|
||||
another error, but that's too bad, the user needs
|
||||
to know it was truncated. */
|
||||
data[2] = IPMI_ERR_MSG_TRUNCATED;
|
||||
kcs->truncated = 0;
|
||||
}
|
||||
|
||||
return kcs->read_pos;
|
||||
}
|
||||
|
||||
/* This implements the state machine defined in the IPMI manual, see
|
||||
that for details on how this works. Divide that flowchart into
|
||||
sections delimited by "Wait for IBF" and this will become clear. */
|
||||
static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
|
||||
{
|
||||
unsigned char status;
|
||||
unsigned char state;
|
||||
|
||||
status = read_status(kcs);
|
||||
|
||||
#ifdef DEBUG_STATE
|
||||
printk(" State = %d, %x\n", kcs->state, status);
|
||||
#endif
|
||||
/* All states wait for ibf, so just do it here. */
|
||||
if (!check_ibf(kcs, status, time))
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
|
||||
/* Just about everything looks at the KCS state, so grab that, too. */
|
||||
state = GET_STATUS_STATE(status);
|
||||
|
||||
switch (kcs->state) {
|
||||
case KCS_IDLE:
|
||||
/* If there's and interrupt source, turn it off. */
|
||||
clear_obf(kcs, status);
|
||||
|
||||
if (GET_STATUS_ATN(status))
|
||||
return SI_SM_ATTN;
|
||||
else
|
||||
return SI_SM_IDLE;
|
||||
|
||||
case KCS_START_OP:
|
||||
if (state != KCS_IDLE) {
|
||||
start_error_recovery(kcs,
|
||||
"State machine not idle at start");
|
||||
break;
|
||||
}
|
||||
|
||||
clear_obf(kcs, status);
|
||||
write_cmd(kcs, KCS_WRITE_START);
|
||||
kcs->state = KCS_WAIT_WRITE_START;
|
||||
break;
|
||||
|
||||
case KCS_WAIT_WRITE_START:
|
||||
if (state != KCS_WRITE_STATE) {
|
||||
start_error_recovery(
|
||||
kcs,
|
||||
"Not in write state at write start");
|
||||
break;
|
||||
}
|
||||
read_data(kcs);
|
||||
if (kcs->write_count == 1) {
|
||||
write_cmd(kcs, KCS_WRITE_END);
|
||||
kcs->state = KCS_WAIT_WRITE_END;
|
||||
} else {
|
||||
write_next_byte(kcs);
|
||||
kcs->state = KCS_WAIT_WRITE;
|
||||
}
|
||||
break;
|
||||
|
||||
case KCS_WAIT_WRITE:
|
||||
if (state != KCS_WRITE_STATE) {
|
||||
start_error_recovery(kcs,
|
||||
"Not in write state for write");
|
||||
break;
|
||||
}
|
||||
clear_obf(kcs, status);
|
||||
if (kcs->write_count == 1) {
|
||||
write_cmd(kcs, KCS_WRITE_END);
|
||||
kcs->state = KCS_WAIT_WRITE_END;
|
||||
} else {
|
||||
write_next_byte(kcs);
|
||||
}
|
||||
break;
|
||||
|
||||
case KCS_WAIT_WRITE_END:
|
||||
if (state != KCS_WRITE_STATE) {
|
||||
start_error_recovery(kcs,
|
||||
"Not in write state for write end");
|
||||
break;
|
||||
}
|
||||
clear_obf(kcs, status);
|
||||
write_next_byte(kcs);
|
||||
kcs->state = KCS_WAIT_READ;
|
||||
break;
|
||||
|
||||
case KCS_WAIT_READ:
|
||||
if ((state != KCS_READ_STATE) && (state != KCS_IDLE_STATE)) {
|
||||
start_error_recovery(
|
||||
kcs,
|
||||
"Not in read or idle in read state");
|
||||
break;
|
||||
}
|
||||
|
||||
if (state == KCS_READ_STATE) {
|
||||
if (! check_obf(kcs, status, time))
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
read_next_byte(kcs);
|
||||
} else {
|
||||
/* We don't implement this exactly like the state
|
||||
machine in the spec. Some broken hardware
|
||||
does not write the final dummy byte to the
|
||||
read register. Thus obf will never go high
|
||||
here. We just go straight to idle, and we
|
||||
handle clearing out obf in idle state if it
|
||||
happens to come in. */
|
||||
clear_obf(kcs, status);
|
||||
kcs->orig_write_count = 0;
|
||||
kcs->state = KCS_IDLE;
|
||||
return SI_SM_TRANSACTION_COMPLETE;
|
||||
}
|
||||
break;
|
||||
|
||||
case KCS_ERROR0:
|
||||
clear_obf(kcs, status);
|
||||
write_cmd(kcs, KCS_GET_STATUS_ABORT);
|
||||
kcs->state = KCS_ERROR1;
|
||||
break;
|
||||
|
||||
case KCS_ERROR1:
|
||||
clear_obf(kcs, status);
|
||||
write_data(kcs, 0);
|
||||
kcs->state = KCS_ERROR2;
|
||||
break;
|
||||
|
||||
case KCS_ERROR2:
|
||||
if (state != KCS_READ_STATE) {
|
||||
start_error_recovery(kcs,
|
||||
"Not in read state for error2");
|
||||
break;
|
||||
}
|
||||
if (! check_obf(kcs, status, time))
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
|
||||
clear_obf(kcs, status);
|
||||
write_data(kcs, KCS_READ_BYTE);
|
||||
kcs->state = KCS_ERROR3;
|
||||
break;
|
||||
|
||||
case KCS_ERROR3:
|
||||
if (state != KCS_IDLE_STATE) {
|
||||
start_error_recovery(kcs,
|
||||
"Not in idle state for error3");
|
||||
break;
|
||||
}
|
||||
|
||||
if (! check_obf(kcs, status, time))
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
|
||||
clear_obf(kcs, status);
|
||||
if (kcs->orig_write_count) {
|
||||
restart_kcs_transaction(kcs);
|
||||
} else {
|
||||
kcs->state = KCS_IDLE;
|
||||
return SI_SM_TRANSACTION_COMPLETE;
|
||||
}
|
||||
break;
|
||||
|
||||
case KCS_HOSED:
|
||||
break;
|
||||
}
|
||||
|
||||
if (kcs->state == KCS_HOSED) {
|
||||
init_kcs_data(kcs, kcs->io);
|
||||
return SI_SM_HOSED;
|
||||
}
|
||||
|
||||
return SI_SM_CALL_WITHOUT_DELAY;
|
||||
}
|
||||
|
||||
static int kcs_size(void)
|
||||
{
|
||||
return sizeof(struct si_sm_data);
|
||||
}
|
||||
|
||||
static int kcs_detect(struct si_sm_data *kcs)
|
||||
{
|
||||
/* It's impossible for the KCS status register to be all 1's,
|
||||
(assuming a properly functioning, self-initialized BMC)
|
||||
but that's what you get from reading a bogus address, so we
|
||||
test that first. */
|
||||
if (read_status(kcs) == 0xff)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kcs_cleanup(struct si_sm_data *kcs)
|
||||
{
|
||||
}
|
||||
|
||||
struct si_sm_handlers kcs_smi_handlers =
|
||||
{
|
||||
.version = IPMI_KCS_VERSION,
|
||||
.init_data = init_kcs_data,
|
||||
.start_transaction = start_kcs_transaction,
|
||||
.get_result = get_kcs_result,
|
||||
.event = kcs_event,
|
||||
.detect = kcs_detect,
|
||||
.cleanup = kcs_cleanup,
|
||||
.size = kcs_size,
|
||||
};
|
||||
3243
extra/linux-2.6.10/drivers/char/ipmi/ipmi_msghandler.c
Normal file
3243
extra/linux-2.6.10/drivers/char/ipmi/ipmi_msghandler.c
Normal file
File diff suppressed because it is too large
Load Diff
543
extra/linux-2.6.10/drivers/char/ipmi/ipmi_poweroff.c
Normal file
543
extra/linux-2.6.10/drivers/char/ipmi/ipmi_poweroff.c
Normal file
@@ -0,0 +1,543 @@
|
||||
/*
|
||||
* ipmi_poweroff.c
|
||||
*
|
||||
* MontaVista IPMI Poweroff extension to sys_reboot
|
||||
*
|
||||
* Author: MontaVista Software, Inc.
|
||||
* Steven Dake <sdake@mvista.com>
|
||||
* Corey Minyard <cminyard@mvista.com>
|
||||
* source@mvista.com
|
||||
*
|
||||
* Copyright 2002,2004 MontaVista Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <asm/semaphore.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ipmi.h>
|
||||
#include <linux/ipmi_smi.h>
|
||||
|
||||
#define PFX "IPMI poweroff: "
|
||||
#define IPMI_POWEROFF_VERSION "v33"
|
||||
|
||||
/* Where to we insert our poweroff function? */
|
||||
extern void (*pm_power_off)(void);
|
||||
|
||||
/* Stuff from the get device id command. */
|
||||
unsigned int mfg_id;
|
||||
unsigned int prod_id;
|
||||
unsigned char capabilities;
|
||||
|
||||
/* We use our own messages for this operation, we don't let the system
|
||||
allocate them, since we may be in a panic situation. The whole
|
||||
thing is single-threaded, anyway, so multiple messages are not
|
||||
required. */
|
||||
static void dummy_smi_free(struct ipmi_smi_msg *msg)
|
||||
{
|
||||
}
|
||||
static void dummy_recv_free(struct ipmi_recv_msg *msg)
|
||||
{
|
||||
}
|
||||
static struct ipmi_smi_msg halt_smi_msg =
|
||||
{
|
||||
.done = dummy_smi_free
|
||||
};
|
||||
static struct ipmi_recv_msg halt_recv_msg =
|
||||
{
|
||||
.done = dummy_recv_free
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Code to send a message and wait for the reponse.
|
||||
*/
|
||||
|
||||
static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data)
|
||||
{
|
||||
struct semaphore *sem = recv_msg->user_msg_data;
|
||||
|
||||
if (sem)
|
||||
up(sem);
|
||||
}
|
||||
|
||||
static struct ipmi_user_hndl ipmi_poweroff_handler =
|
||||
{
|
||||
.ipmi_recv_hndl = receive_handler
|
||||
};
|
||||
|
||||
|
||||
static int ipmi_request_wait_for_response(ipmi_user_t user,
|
||||
struct ipmi_addr *addr,
|
||||
struct kernel_ipmi_msg *send_msg)
|
||||
{
|
||||
int rv;
|
||||
struct semaphore sem;
|
||||
|
||||
sema_init (&sem, 0);
|
||||
|
||||
rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &sem,
|
||||
&halt_smi_msg, &halt_recv_msg, 0);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
down (&sem);
|
||||
|
||||
return halt_recv_msg.msg.data[0];
|
||||
}
|
||||
|
||||
/* We are in run-to-completion mode, no semaphore is desired. */
|
||||
static int ipmi_request_in_rc_mode(ipmi_user_t user,
|
||||
struct ipmi_addr *addr,
|
||||
struct kernel_ipmi_msg *send_msg)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL,
|
||||
&halt_smi_msg, &halt_recv_msg, 0);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
return halt_recv_msg.msg.data[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* ATCA Support
|
||||
*/
|
||||
|
||||
#define IPMI_NETFN_ATCA 0x2c
|
||||
#define IPMI_ATCA_SET_POWER_CMD 0x11
|
||||
#define IPMI_ATCA_GET_ADDR_INFO_CMD 0x01
|
||||
#define IPMI_PICMG_ID 0
|
||||
|
||||
static int ipmi_atca_detect (ipmi_user_t user)
|
||||
{
|
||||
struct ipmi_system_interface_addr smi_addr;
|
||||
struct kernel_ipmi_msg send_msg;
|
||||
int rv;
|
||||
unsigned char data[1];
|
||||
|
||||
/*
|
||||
* Configure IPMI address for local access
|
||||
*/
|
||||
smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
|
||||
smi_addr.channel = IPMI_BMC_CHANNEL;
|
||||
smi_addr.lun = 0;
|
||||
|
||||
/*
|
||||
* Use get address info to check and see if we are ATCA
|
||||
*/
|
||||
send_msg.netfn = IPMI_NETFN_ATCA;
|
||||
send_msg.cmd = IPMI_ATCA_GET_ADDR_INFO_CMD;
|
||||
data[0] = IPMI_PICMG_ID;
|
||||
send_msg.data = data;
|
||||
send_msg.data_len = sizeof(data);
|
||||
rv = ipmi_request_wait_for_response(user,
|
||||
(struct ipmi_addr *) &smi_addr,
|
||||
&send_msg);
|
||||
return !rv;
|
||||
}
|
||||
|
||||
static void ipmi_poweroff_atca (ipmi_user_t user)
|
||||
{
|
||||
struct ipmi_system_interface_addr smi_addr;
|
||||
struct kernel_ipmi_msg send_msg;
|
||||
int rv;
|
||||
unsigned char data[4];
|
||||
|
||||
/*
|
||||
* Configure IPMI address for local access
|
||||
*/
|
||||
smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
|
||||
smi_addr.channel = IPMI_BMC_CHANNEL;
|
||||
smi_addr.lun = 0;
|
||||
|
||||
printk(KERN_INFO PFX "Powering down via ATCA power command\n");
|
||||
|
||||
/*
|
||||
* Power down
|
||||
*/
|
||||
send_msg.netfn = IPMI_NETFN_ATCA;
|
||||
send_msg.cmd = IPMI_ATCA_SET_POWER_CMD;
|
||||
data[0] = IPMI_PICMG_ID;
|
||||
data[1] = 0; /* FRU id */
|
||||
data[2] = 0; /* Power Level */
|
||||
data[3] = 0; /* Don't change saved presets */
|
||||
send_msg.data = data;
|
||||
send_msg.data_len = sizeof (data);
|
||||
rv = ipmi_request_in_rc_mode(user,
|
||||
(struct ipmi_addr *) &smi_addr,
|
||||
&send_msg);
|
||||
if (rv) {
|
||||
printk(KERN_ERR PFX "Unable to send ATCA powerdown message,"
|
||||
" IPMI error 0x%x\n", rv);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* CPI1 Support
|
||||
*/
|
||||
|
||||
#define IPMI_NETFN_OEM_1 0xf8
|
||||
#define OEM_GRP_CMD_SET_RESET_STATE 0x84
|
||||
#define OEM_GRP_CMD_SET_POWER_STATE 0x82
|
||||
#define IPMI_NETFN_OEM_8 0xf8
|
||||
#define OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL 0x80
|
||||
#define OEM_GRP_CMD_GET_SLOT_GA 0xa3
|
||||
#define IPMI_NETFN_SENSOR_EVT 0x10
|
||||
#define IPMI_CMD_GET_EVENT_RECEIVER 0x01
|
||||
|
||||
#define IPMI_CPI1_PRODUCT_ID 0x000157
|
||||
#define IPMI_CPI1_MANUFACTURER_ID 0x0108
|
||||
|
||||
static int ipmi_cpi1_detect (ipmi_user_t user)
|
||||
{
|
||||
return ((mfg_id == IPMI_CPI1_MANUFACTURER_ID)
|
||||
&& (prod_id == IPMI_CPI1_PRODUCT_ID));
|
||||
}
|
||||
|
||||
static void ipmi_poweroff_cpi1 (ipmi_user_t user)
|
||||
{
|
||||
struct ipmi_system_interface_addr smi_addr;
|
||||
struct ipmi_ipmb_addr ipmb_addr;
|
||||
struct kernel_ipmi_msg send_msg;
|
||||
int rv;
|
||||
unsigned char data[1];
|
||||
int slot;
|
||||
unsigned char hotswap_ipmb;
|
||||
unsigned char aer_addr;
|
||||
unsigned char aer_lun;
|
||||
|
||||
/*
|
||||
* Configure IPMI address for local access
|
||||
*/
|
||||
smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
|
||||
smi_addr.channel = IPMI_BMC_CHANNEL;
|
||||
smi_addr.lun = 0;
|
||||
|
||||
printk(KERN_INFO PFX "Powering down via CPI1 power command\n");
|
||||
|
||||
/*
|
||||
* Get IPMI ipmb address
|
||||
*/
|
||||
send_msg.netfn = IPMI_NETFN_OEM_8 >> 2;
|
||||
send_msg.cmd = OEM_GRP_CMD_GET_SLOT_GA;
|
||||
send_msg.data = NULL;
|
||||
send_msg.data_len = 0;
|
||||
rv = ipmi_request_in_rc_mode(user,
|
||||
(struct ipmi_addr *) &smi_addr,
|
||||
&send_msg);
|
||||
if (rv)
|
||||
goto out;
|
||||
slot = halt_recv_msg.msg.data[1];
|
||||
hotswap_ipmb = (slot > 9) ? (0xb0 + 2 * slot) : (0xae + 2 * slot);
|
||||
|
||||
/*
|
||||
* Get active event receiver
|
||||
*/
|
||||
send_msg.netfn = IPMI_NETFN_SENSOR_EVT >> 2;
|
||||
send_msg.cmd = IPMI_CMD_GET_EVENT_RECEIVER;
|
||||
send_msg.data = NULL;
|
||||
send_msg.data_len = 0;
|
||||
rv = ipmi_request_in_rc_mode(user,
|
||||
(struct ipmi_addr *) &smi_addr,
|
||||
&send_msg);
|
||||
if (rv)
|
||||
goto out;
|
||||
aer_addr = halt_recv_msg.msg.data[1];
|
||||
aer_lun = halt_recv_msg.msg.data[2];
|
||||
|
||||
/*
|
||||
* Setup IPMB address target instead of local target
|
||||
*/
|
||||
ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE;
|
||||
ipmb_addr.channel = 0;
|
||||
ipmb_addr.slave_addr = aer_addr;
|
||||
ipmb_addr.lun = aer_lun;
|
||||
|
||||
/*
|
||||
* Send request hotswap control to remove blade from dpv
|
||||
*/
|
||||
send_msg.netfn = IPMI_NETFN_OEM_8 >> 2;
|
||||
send_msg.cmd = OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL;
|
||||
send_msg.data = &hotswap_ipmb;
|
||||
send_msg.data_len = 1;
|
||||
ipmi_request_in_rc_mode(user,
|
||||
(struct ipmi_addr *) &ipmb_addr,
|
||||
&send_msg);
|
||||
|
||||
/*
|
||||
* Set reset asserted
|
||||
*/
|
||||
send_msg.netfn = IPMI_NETFN_OEM_1 >> 2;
|
||||
send_msg.cmd = OEM_GRP_CMD_SET_RESET_STATE;
|
||||
send_msg.data = data;
|
||||
data[0] = 1; /* Reset asserted state */
|
||||
send_msg.data_len = 1;
|
||||
rv = ipmi_request_in_rc_mode(user,
|
||||
(struct ipmi_addr *) &smi_addr,
|
||||
&send_msg);
|
||||
if (rv)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Power down
|
||||
*/
|
||||
send_msg.netfn = IPMI_NETFN_OEM_1 >> 2;
|
||||
send_msg.cmd = OEM_GRP_CMD_SET_POWER_STATE;
|
||||
send_msg.data = data;
|
||||
data[0] = 1; /* Power down state */
|
||||
send_msg.data_len = 1;
|
||||
rv = ipmi_request_in_rc_mode(user,
|
||||
(struct ipmi_addr *) &smi_addr,
|
||||
&send_msg);
|
||||
if (rv)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Standard chassis support
|
||||
*/
|
||||
|
||||
#define IPMI_NETFN_CHASSIS_REQUEST 0
|
||||
#define IPMI_CHASSIS_CONTROL_CMD 0x02
|
||||
|
||||
static int ipmi_chassis_detect (ipmi_user_t user)
|
||||
{
|
||||
/* Chassis support, use it. */
|
||||
return (capabilities & 0x80);
|
||||
}
|
||||
|
||||
static void ipmi_poweroff_chassis (ipmi_user_t user)
|
||||
{
|
||||
struct ipmi_system_interface_addr smi_addr;
|
||||
struct kernel_ipmi_msg send_msg;
|
||||
int rv;
|
||||
unsigned char data[1];
|
||||
|
||||
/*
|
||||
* Configure IPMI address for local access
|
||||
*/
|
||||
smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
|
||||
smi_addr.channel = IPMI_BMC_CHANNEL;
|
||||
smi_addr.lun = 0;
|
||||
|
||||
printk(KERN_INFO PFX "Powering down via IPMI chassis control command\n");
|
||||
|
||||
/*
|
||||
* Power down
|
||||
*/
|
||||
send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST;
|
||||
send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD;
|
||||
data[0] = 0; /* Power down */
|
||||
send_msg.data = data;
|
||||
send_msg.data_len = sizeof(data);
|
||||
rv = ipmi_request_in_rc_mode(user,
|
||||
(struct ipmi_addr *) &smi_addr,
|
||||
&send_msg);
|
||||
if (rv) {
|
||||
printk(KERN_ERR PFX "Unable to send chassis powerdown message,"
|
||||
" IPMI error 0x%x\n", rv);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Table of possible power off functions. */
|
||||
struct poweroff_function {
|
||||
char *platform_type;
|
||||
int (*detect)(ipmi_user_t user);
|
||||
void (*poweroff_func)(ipmi_user_t user);
|
||||
};
|
||||
|
||||
static struct poweroff_function poweroff_functions[] = {
|
||||
{ "ATCA", ipmi_atca_detect, ipmi_poweroff_atca },
|
||||
{ "CPI1", ipmi_cpi1_detect, ipmi_poweroff_cpi1 },
|
||||
/* Chassis should generally be last, other things should override
|
||||
it. */
|
||||
{ "chassis", ipmi_chassis_detect, ipmi_poweroff_chassis },
|
||||
};
|
||||
#define NUM_PO_FUNCS (sizeof(poweroff_functions) \
|
||||
/ sizeof(struct poweroff_function))
|
||||
|
||||
|
||||
/* Our local state. */
|
||||
static int ready = 0;
|
||||
static ipmi_user_t ipmi_user;
|
||||
static void (*specific_poweroff_func)(ipmi_user_t user) = NULL;
|
||||
|
||||
/* Holds the old poweroff function so we can restore it on removal. */
|
||||
static void (*old_poweroff_func)(void);
|
||||
|
||||
|
||||
/* Called on a powerdown request. */
|
||||
static void ipmi_poweroff_function (void)
|
||||
{
|
||||
if (!ready)
|
||||
return;
|
||||
|
||||
/* Use run-to-completion mode, since interrupts may be off. */
|
||||
ipmi_user_set_run_to_completion(ipmi_user, 1);
|
||||
specific_poweroff_func(ipmi_user);
|
||||
ipmi_user_set_run_to_completion(ipmi_user, 0);
|
||||
}
|
||||
|
||||
/* Wait for an IPMI interface to be installed, the first one installed
|
||||
will be grabbed by this code and used to perform the powerdown. */
|
||||
static void ipmi_po_new_smi(int if_num)
|
||||
{
|
||||
struct ipmi_system_interface_addr smi_addr;
|
||||
struct kernel_ipmi_msg send_msg;
|
||||
int rv;
|
||||
int i;
|
||||
|
||||
if (ready)
|
||||
return;
|
||||
|
||||
rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, &ipmi_user);
|
||||
if (rv) {
|
||||
printk(KERN_ERR PFX "could not create IPMI user, error %d\n",
|
||||
rv);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a get device ide and store some results, since this is
|
||||
* used by several functions.
|
||||
*/
|
||||
smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
|
||||
smi_addr.channel = IPMI_BMC_CHANNEL;
|
||||
smi_addr.lun = 0;
|
||||
|
||||
send_msg.netfn = IPMI_NETFN_APP_REQUEST;
|
||||
send_msg.cmd = IPMI_GET_DEVICE_ID_CMD;
|
||||
send_msg.data = NULL;
|
||||
send_msg.data_len = 0;
|
||||
rv = ipmi_request_wait_for_response(ipmi_user,
|
||||
(struct ipmi_addr *) &smi_addr,
|
||||
&send_msg);
|
||||
if (rv) {
|
||||
printk(KERN_ERR PFX "Unable to send IPMI get device id info,"
|
||||
" IPMI error 0x%x\n", rv);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (halt_recv_msg.msg.data_len < 12) {
|
||||
printk(KERN_ERR PFX "(chassis) IPMI get device id info too,"
|
||||
" short, was %d bytes, needed %d bytes\n",
|
||||
halt_recv_msg.msg.data_len, 12);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
mfg_id = (halt_recv_msg.msg.data[7]
|
||||
| (halt_recv_msg.msg.data[8] << 8)
|
||||
| (halt_recv_msg.msg.data[9] << 16));
|
||||
prod_id = (halt_recv_msg.msg.data[10]
|
||||
| (halt_recv_msg.msg.data[11] << 8));
|
||||
capabilities = halt_recv_msg.msg.data[6];
|
||||
|
||||
|
||||
/* Scan for a poweroff method */
|
||||
for (i=0; i<NUM_PO_FUNCS; i++) {
|
||||
if (poweroff_functions[i].detect(ipmi_user))
|
||||
goto found;
|
||||
}
|
||||
|
||||
out_err:
|
||||
printk(KERN_ERR PFX "Unable to find a poweroff function that"
|
||||
" will work, giving up\n");
|
||||
ipmi_destroy_user(ipmi_user);
|
||||
return;
|
||||
|
||||
found:
|
||||
printk(KERN_INFO PFX "Found a %s style poweroff function\n",
|
||||
poweroff_functions[i].platform_type);
|
||||
specific_poweroff_func = poweroff_functions[i].poweroff_func;
|
||||
old_poweroff_func = pm_power_off;
|
||||
pm_power_off = ipmi_poweroff_function;
|
||||
ready = 1;
|
||||
}
|
||||
|
||||
static void ipmi_po_smi_gone(int if_num)
|
||||
{
|
||||
/* This can never be called, because once poweroff driver is
|
||||
registered, the interface can't go away until the power
|
||||
driver is unregistered. */
|
||||
}
|
||||
|
||||
static struct ipmi_smi_watcher smi_watcher =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.new_smi = ipmi_po_new_smi,
|
||||
.smi_gone = ipmi_po_smi_gone
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Startup and shutdown functions.
|
||||
*/
|
||||
static int ipmi_poweroff_init (void)
|
||||
{
|
||||
int rv;
|
||||
|
||||
printk ("Copyright (C) 2004 MontaVista Software -"
|
||||
" IPMI Powerdown via sys_reboot version "
|
||||
IPMI_POWEROFF_VERSION ".\n");
|
||||
|
||||
rv = ipmi_smi_watcher_register(&smi_watcher);
|
||||
if (rv)
|
||||
printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
static __exit void ipmi_poweroff_cleanup(void)
|
||||
{
|
||||
int rv;
|
||||
|
||||
ipmi_smi_watcher_unregister(&smi_watcher);
|
||||
|
||||
if (ready) {
|
||||
rv = ipmi_destroy_user(ipmi_user);
|
||||
if (rv)
|
||||
printk(KERN_ERR PFX "could not cleanup the IPMI"
|
||||
" user: 0x%x\n", rv);
|
||||
pm_power_off = old_poweroff_func;
|
||||
}
|
||||
}
|
||||
module_exit(ipmi_poweroff_cleanup);
|
||||
#endif
|
||||
|
||||
module_init(ipmi_poweroff_init);
|
||||
MODULE_LICENSE("GPL");
|
||||
2323
extra/linux-2.6.10/drivers/char/ipmi/ipmi_si_intf.c
Normal file
2323
extra/linux-2.6.10/drivers/char/ipmi/ipmi_si_intf.c
Normal file
File diff suppressed because it is too large
Load Diff
120
extra/linux-2.6.10/drivers/char/ipmi/ipmi_si_sm.h
Normal file
120
extra/linux-2.6.10/drivers/char/ipmi/ipmi_si_sm.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* ipmi_si_sm.h
|
||||
*
|
||||
* State machine interface for low-level IPMI system management
|
||||
* interface state machines. This code is the interface between
|
||||
* the ipmi_smi code (that handles the policy of a KCS, SMIC, or
|
||||
* BT interface) and the actual low-level state machine.
|
||||
*
|
||||
* Author: MontaVista Software, Inc.
|
||||
* Corey Minyard <minyard@mvista.com>
|
||||
* source@mvista.com
|
||||
*
|
||||
* Copyright 2002 MontaVista Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* This is defined by the state machines themselves, it is an opaque
|
||||
data type for them to use. */
|
||||
struct si_sm_data;
|
||||
|
||||
/* The structure for doing I/O in the state machine. The state
|
||||
machine doesn't have the actual I/O routines, they are done through
|
||||
this interface. */
|
||||
struct si_sm_io
|
||||
{
|
||||
unsigned char (*inputb)(struct si_sm_io *io, unsigned int offset);
|
||||
void (*outputb)(struct si_sm_io *io,
|
||||
unsigned int offset,
|
||||
unsigned char b);
|
||||
|
||||
/* Generic info used by the actual handling routines, the
|
||||
state machine shouldn't touch these. */
|
||||
void *info;
|
||||
void *addr;
|
||||
int regspacing;
|
||||
int regsize;
|
||||
int regshift;
|
||||
};
|
||||
|
||||
/* Results of SMI events. */
|
||||
enum si_sm_result
|
||||
{
|
||||
SI_SM_CALL_WITHOUT_DELAY, /* Call the driver again immediately */
|
||||
SI_SM_CALL_WITH_DELAY, /* Delay some before calling again. */
|
||||
SI_SM_TRANSACTION_COMPLETE, /* A transaction is finished. */
|
||||
SI_SM_IDLE, /* The SM is in idle state. */
|
||||
SI_SM_HOSED, /* The hardware violated the state machine. */
|
||||
SI_SM_ATTN /* The hardware is asserting attn and the
|
||||
state machine is idle. */
|
||||
};
|
||||
|
||||
/* Handlers for the SMI state machine. */
|
||||
struct si_sm_handlers
|
||||
{
|
||||
/* Put the version number of the state machine here so the
|
||||
upper layer can print it. */
|
||||
char *version;
|
||||
|
||||
/* Initialize the data and return the amount of I/O space to
|
||||
reserve for the space. */
|
||||
unsigned int (*init_data)(struct si_sm_data *smi,
|
||||
struct si_sm_io *io);
|
||||
|
||||
/* Start a new transaction in the state machine. This will
|
||||
return -2 if the state machine is not idle, -1 if the size
|
||||
is invalid (to large or too small), or 0 if the transaction
|
||||
is successfully completed. */
|
||||
int (*start_transaction)(struct si_sm_data *smi,
|
||||
unsigned char *data, unsigned int size);
|
||||
|
||||
/* Return the results after the transaction. This will return
|
||||
-1 if the buffer is too small, zero if no transaction is
|
||||
present, or the actual length of the result data. */
|
||||
int (*get_result)(struct si_sm_data *smi,
|
||||
unsigned char *data, unsigned int length);
|
||||
|
||||
/* Call this periodically (for a polled interface) or upon
|
||||
receiving an interrupt (for a interrupt-driven interface).
|
||||
If interrupt driven, you should probably poll this
|
||||
periodically when not in idle state. This should be called
|
||||
with the time that passed since the last call, if it is
|
||||
significant. Time is in microseconds. */
|
||||
enum si_sm_result (*event)(struct si_sm_data *smi, long time);
|
||||
|
||||
/* Attempt to detect an SMI. Returns 0 on success or nonzero
|
||||
on failure. */
|
||||
int (*detect)(struct si_sm_data *smi);
|
||||
|
||||
/* The interface is shutting down, so clean it up. */
|
||||
void (*cleanup)(struct si_sm_data *smi);
|
||||
|
||||
/* Return the size of the SMI structure in bytes. */
|
||||
int (*size)(void);
|
||||
};
|
||||
|
||||
/* Current state machines that we can use. */
|
||||
extern struct si_sm_handlers kcs_smi_handlers;
|
||||
extern struct si_sm_handlers smic_smi_handlers;
|
||||
extern struct si_sm_handlers bt_smi_handlers;
|
||||
|
||||
599
extra/linux-2.6.10/drivers/char/ipmi/ipmi_smic_sm.c
Normal file
599
extra/linux-2.6.10/drivers/char/ipmi/ipmi_smic_sm.c
Normal file
@@ -0,0 +1,599 @@
|
||||
/*
|
||||
* ipmi_smic_sm.c
|
||||
*
|
||||
* The state-machine driver for an IPMI SMIC driver
|
||||
*
|
||||
* It started as a copy of Corey Minyard's driver for the KSC interface
|
||||
* and the kernel patch "mmcdev-patch-245" by HP
|
||||
*
|
||||
* modified by: Hannes Schulz <schulz@schwaar.com>
|
||||
* ipmi@schwaar.com
|
||||
*
|
||||
*
|
||||
* Corey Minyard's driver for the KSC interface has the following
|
||||
* copyright notice:
|
||||
* Copyright 2002 MontaVista Software Inc.
|
||||
*
|
||||
* the kernel patch "mmcdev-patch-245" by HP has the following
|
||||
* copyright notice:
|
||||
* (c) Copyright 2001 Grant Grundler (c) Copyright
|
||||
* 2001 Hewlett-Packard Company
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
#include <linux/kernel.h> /* For printk. */
|
||||
#include <linux/string.h>
|
||||
#include <linux/ipmi_msgdefs.h> /* for completion codes */
|
||||
#include "ipmi_si_sm.h"
|
||||
|
||||
#define IPMI_SMIC_VERSION "v33"
|
||||
|
||||
/* smic_debug is a bit-field
|
||||
* SMIC_DEBUG_ENABLE - turned on for now
|
||||
* SMIC_DEBUG_MSG - commands and their responses
|
||||
* SMIC_DEBUG_STATES - state machine
|
||||
*/
|
||||
#define SMIC_DEBUG_STATES 4
|
||||
#define SMIC_DEBUG_MSG 2
|
||||
#define SMIC_DEBUG_ENABLE 1
|
||||
|
||||
static int smic_debug = 1;
|
||||
|
||||
enum smic_states {
|
||||
SMIC_IDLE,
|
||||
SMIC_START_OP,
|
||||
SMIC_OP_OK,
|
||||
SMIC_WRITE_START,
|
||||
SMIC_WRITE_NEXT,
|
||||
SMIC_WRITE_END,
|
||||
SMIC_WRITE2READ,
|
||||
SMIC_READ_START,
|
||||
SMIC_READ_NEXT,
|
||||
SMIC_READ_END,
|
||||
SMIC_HOSED
|
||||
};
|
||||
|
||||
#define MAX_SMIC_READ_SIZE 80
|
||||
#define MAX_SMIC_WRITE_SIZE 80
|
||||
#define SMIC_MAX_ERROR_RETRIES 3
|
||||
|
||||
/* Timeouts in microseconds. */
|
||||
#define SMIC_RETRY_TIMEOUT 100000
|
||||
|
||||
/* SMIC Flags Register Bits */
|
||||
#define SMIC_RX_DATA_READY 0x80
|
||||
#define SMIC_TX_DATA_READY 0x40
|
||||
#define SMIC_SMI 0x10
|
||||
#define SMIC_EVM_DATA_AVAIL 0x08
|
||||
#define SMIC_SMS_DATA_AVAIL 0x04
|
||||
#define SMIC_FLAG_BSY 0x01
|
||||
|
||||
/* SMIC Error Codes */
|
||||
#define EC_NO_ERROR 0x00
|
||||
#define EC_ABORTED 0x01
|
||||
#define EC_ILLEGAL_CONTROL 0x02
|
||||
#define EC_NO_RESPONSE 0x03
|
||||
#define EC_ILLEGAL_COMMAND 0x04
|
||||
#define EC_BUFFER_FULL 0x05
|
||||
|
||||
struct si_sm_data
|
||||
{
|
||||
enum smic_states state;
|
||||
struct si_sm_io *io;
|
||||
unsigned char write_data[MAX_SMIC_WRITE_SIZE];
|
||||
int write_pos;
|
||||
int write_count;
|
||||
int orig_write_count;
|
||||
unsigned char read_data[MAX_SMIC_READ_SIZE];
|
||||
int read_pos;
|
||||
int truncated;
|
||||
unsigned int error_retries;
|
||||
long smic_timeout;
|
||||
};
|
||||
|
||||
static unsigned int init_smic_data (struct si_sm_data *smic,
|
||||
struct si_sm_io *io)
|
||||
{
|
||||
smic->state = SMIC_IDLE;
|
||||
smic->io = io;
|
||||
smic->write_pos = 0;
|
||||
smic->write_count = 0;
|
||||
smic->orig_write_count = 0;
|
||||
smic->read_pos = 0;
|
||||
smic->error_retries = 0;
|
||||
smic->truncated = 0;
|
||||
smic->smic_timeout = SMIC_RETRY_TIMEOUT;
|
||||
|
||||
/* We use 3 bytes of I/O. */
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int start_smic_transaction(struct si_sm_data *smic,
|
||||
unsigned char *data, unsigned int size)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if ((size < 2) || (size > MAX_SMIC_WRITE_SIZE)) {
|
||||
return -1;
|
||||
}
|
||||
if ((smic->state != SMIC_IDLE) && (smic->state != SMIC_HOSED)) {
|
||||
return -2;
|
||||
}
|
||||
if (smic_debug & SMIC_DEBUG_MSG) {
|
||||
printk(KERN_INFO "start_smic_transaction -");
|
||||
for (i = 0; i < size; i ++) {
|
||||
printk (" %02x", (unsigned char) (data [i]));
|
||||
}
|
||||
printk ("\n");
|
||||
}
|
||||
smic->error_retries = 0;
|
||||
memcpy(smic->write_data, data, size);
|
||||
smic->write_count = size;
|
||||
smic->orig_write_count = size;
|
||||
smic->write_pos = 0;
|
||||
smic->read_pos = 0;
|
||||
smic->state = SMIC_START_OP;
|
||||
smic->smic_timeout = SMIC_RETRY_TIMEOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smic_get_result(struct si_sm_data *smic,
|
||||
unsigned char *data, unsigned int length)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (smic_debug & SMIC_DEBUG_MSG) {
|
||||
printk (KERN_INFO "smic_get result -");
|
||||
for (i = 0; i < smic->read_pos; i ++) {
|
||||
printk (" %02x", (smic->read_data [i]));
|
||||
}
|
||||
printk ("\n");
|
||||
}
|
||||
if (length < smic->read_pos) {
|
||||
smic->read_pos = length;
|
||||
smic->truncated = 1;
|
||||
}
|
||||
memcpy(data, smic->read_data, smic->read_pos);
|
||||
|
||||
if ((length >= 3) && (smic->read_pos < 3)) {
|
||||
data[2] = IPMI_ERR_UNSPECIFIED;
|
||||
smic->read_pos = 3;
|
||||
}
|
||||
if (smic->truncated) {
|
||||
data[2] = IPMI_ERR_MSG_TRUNCATED;
|
||||
smic->truncated = 0;
|
||||
}
|
||||
return smic->read_pos;
|
||||
}
|
||||
|
||||
static inline unsigned char read_smic_flags(struct si_sm_data *smic)
|
||||
{
|
||||
return smic->io->inputb(smic->io, 2);
|
||||
}
|
||||
|
||||
static inline unsigned char read_smic_status(struct si_sm_data *smic)
|
||||
{
|
||||
return smic->io->inputb(smic->io, 1);
|
||||
}
|
||||
|
||||
static inline unsigned char read_smic_data(struct si_sm_data *smic)
|
||||
{
|
||||
return smic->io->inputb(smic->io, 0);
|
||||
}
|
||||
|
||||
static inline void write_smic_flags(struct si_sm_data *smic,
|
||||
unsigned char flags)
|
||||
{
|
||||
smic->io->outputb(smic->io, 2, flags);
|
||||
}
|
||||
|
||||
static inline void write_smic_control(struct si_sm_data *smic,
|
||||
unsigned char control)
|
||||
{
|
||||
smic->io->outputb(smic->io, 1, control);
|
||||
}
|
||||
|
||||
static inline void write_si_sm_data (struct si_sm_data *smic,
|
||||
unsigned char data)
|
||||
{
|
||||
smic->io->outputb(smic->io, 0, data);
|
||||
}
|
||||
|
||||
static inline void start_error_recovery(struct si_sm_data *smic, char *reason)
|
||||
{
|
||||
(smic->error_retries)++;
|
||||
if (smic->error_retries > SMIC_MAX_ERROR_RETRIES) {
|
||||
if (smic_debug & SMIC_DEBUG_ENABLE) {
|
||||
printk(KERN_WARNING
|
||||
"ipmi_smic_drv: smic hosed: %s\n", reason);
|
||||
}
|
||||
smic->state = SMIC_HOSED;
|
||||
} else {
|
||||
smic->write_count = smic->orig_write_count;
|
||||
smic->write_pos = 0;
|
||||
smic->read_pos = 0;
|
||||
smic->state = SMIC_START_OP;
|
||||
smic->smic_timeout = SMIC_RETRY_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void write_next_byte(struct si_sm_data *smic)
|
||||
{
|
||||
write_si_sm_data(smic, smic->write_data[smic->write_pos]);
|
||||
(smic->write_pos)++;
|
||||
(smic->write_count)--;
|
||||
}
|
||||
|
||||
static inline void read_next_byte (struct si_sm_data *smic)
|
||||
{
|
||||
if (smic->read_pos >= MAX_SMIC_READ_SIZE) {
|
||||
read_smic_data (smic);
|
||||
smic->truncated = 1;
|
||||
} else {
|
||||
smic->read_data[smic->read_pos] = read_smic_data(smic);
|
||||
(smic->read_pos)++;
|
||||
}
|
||||
}
|
||||
|
||||
/* SMIC Control/Status Code Components */
|
||||
#define SMIC_GET_STATUS 0x00 /* Control form's name */
|
||||
#define SMIC_READY 0x00 /* Status form's name */
|
||||
#define SMIC_WR_START 0x01 /* Unified Control/Status names... */
|
||||
#define SMIC_WR_NEXT 0x02
|
||||
#define SMIC_WR_END 0x03
|
||||
#define SMIC_RD_START 0x04
|
||||
#define SMIC_RD_NEXT 0x05
|
||||
#define SMIC_RD_END 0x06
|
||||
#define SMIC_CODE_MASK 0x0f
|
||||
|
||||
#define SMIC_CONTROL 0x00
|
||||
#define SMIC_STATUS 0x80
|
||||
#define SMIC_CS_MASK 0x80
|
||||
|
||||
#define SMIC_SMS 0x40
|
||||
#define SMIC_SMM 0x60
|
||||
#define SMIC_STREAM_MASK 0x60
|
||||
|
||||
/* SMIC Control Codes */
|
||||
#define SMIC_CC_SMS_GET_STATUS (SMIC_CONTROL|SMIC_SMS|SMIC_GET_STATUS)
|
||||
#define SMIC_CC_SMS_WR_START (SMIC_CONTROL|SMIC_SMS|SMIC_WR_START)
|
||||
#define SMIC_CC_SMS_WR_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_WR_NEXT)
|
||||
#define SMIC_CC_SMS_WR_END (SMIC_CONTROL|SMIC_SMS|SMIC_WR_END)
|
||||
#define SMIC_CC_SMS_RD_START (SMIC_CONTROL|SMIC_SMS|SMIC_RD_START)
|
||||
#define SMIC_CC_SMS_RD_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_RD_NEXT)
|
||||
#define SMIC_CC_SMS_RD_END (SMIC_CONTROL|SMIC_SMS|SMIC_RD_END)
|
||||
|
||||
#define SMIC_CC_SMM_GET_STATUS (SMIC_CONTROL|SMIC_SMM|SMIC_GET_STATUS)
|
||||
#define SMIC_CC_SMM_WR_START (SMIC_CONTROL|SMIC_SMM|SMIC_WR_START)
|
||||
#define SMIC_CC_SMM_WR_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_WR_NEXT)
|
||||
#define SMIC_CC_SMM_WR_END (SMIC_CONTROL|SMIC_SMM|SMIC_WR_END)
|
||||
#define SMIC_CC_SMM_RD_START (SMIC_CONTROL|SMIC_SMM|SMIC_RD_START)
|
||||
#define SMIC_CC_SMM_RD_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_RD_NEXT)
|
||||
#define SMIC_CC_SMM_RD_END (SMIC_CONTROL|SMIC_SMM|SMIC_RD_END)
|
||||
|
||||
/* SMIC Status Codes */
|
||||
#define SMIC_SC_SMS_READY (SMIC_STATUS|SMIC_SMS|SMIC_READY)
|
||||
#define SMIC_SC_SMS_WR_START (SMIC_STATUS|SMIC_SMS|SMIC_WR_START)
|
||||
#define SMIC_SC_SMS_WR_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_WR_NEXT)
|
||||
#define SMIC_SC_SMS_WR_END (SMIC_STATUS|SMIC_SMS|SMIC_WR_END)
|
||||
#define SMIC_SC_SMS_RD_START (SMIC_STATUS|SMIC_SMS|SMIC_RD_START)
|
||||
#define SMIC_SC_SMS_RD_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_RD_NEXT)
|
||||
#define SMIC_SC_SMS_RD_END (SMIC_STATUS|SMIC_SMS|SMIC_RD_END)
|
||||
|
||||
#define SMIC_SC_SMM_READY (SMIC_STATUS|SMIC_SMM|SMIC_READY)
|
||||
#define SMIC_SC_SMM_WR_START (SMIC_STATUS|SMIC_SMM|SMIC_WR_START)
|
||||
#define SMIC_SC_SMM_WR_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_WR_NEXT)
|
||||
#define SMIC_SC_SMM_WR_END (SMIC_STATUS|SMIC_SMM|SMIC_WR_END)
|
||||
#define SMIC_SC_SMM_RD_START (SMIC_STATUS|SMIC_SMM|SMIC_RD_START)
|
||||
#define SMIC_SC_SMM_RD_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_RD_NEXT)
|
||||
#define SMIC_SC_SMM_RD_END (SMIC_STATUS|SMIC_SMM|SMIC_RD_END)
|
||||
|
||||
/* these are the control/status codes we actually use
|
||||
SMIC_CC_SMS_GET_STATUS 0x40
|
||||
SMIC_CC_SMS_WR_START 0x41
|
||||
SMIC_CC_SMS_WR_NEXT 0x42
|
||||
SMIC_CC_SMS_WR_END 0x43
|
||||
SMIC_CC_SMS_RD_START 0x44
|
||||
SMIC_CC_SMS_RD_NEXT 0x45
|
||||
SMIC_CC_SMS_RD_END 0x46
|
||||
|
||||
SMIC_SC_SMS_READY 0xC0
|
||||
SMIC_SC_SMS_WR_START 0xC1
|
||||
SMIC_SC_SMS_WR_NEXT 0xC2
|
||||
SMIC_SC_SMS_WR_END 0xC3
|
||||
SMIC_SC_SMS_RD_START 0xC4
|
||||
SMIC_SC_SMS_RD_NEXT 0xC5
|
||||
SMIC_SC_SMS_RD_END 0xC6
|
||||
*/
|
||||
|
||||
static enum si_sm_result smic_event (struct si_sm_data *smic, long time)
|
||||
{
|
||||
unsigned char status;
|
||||
unsigned char flags;
|
||||
unsigned char data;
|
||||
|
||||
if (smic->state == SMIC_HOSED) {
|
||||
init_smic_data(smic, smic->io);
|
||||
return SI_SM_HOSED;
|
||||
}
|
||||
if (smic->state != SMIC_IDLE) {
|
||||
if (smic_debug & SMIC_DEBUG_STATES) {
|
||||
printk(KERN_INFO
|
||||
"smic_event - smic->smic_timeout = %ld,"
|
||||
" time = %ld\n",
|
||||
smic->smic_timeout, time);
|
||||
}
|
||||
/* FIXME: smic_event is sometimes called with time > SMIC_RETRY_TIMEOUT */
|
||||
if (time < SMIC_RETRY_TIMEOUT) {
|
||||
smic->smic_timeout -= time;
|
||||
if (smic->smic_timeout < 0) {
|
||||
start_error_recovery(smic, "smic timed out.");
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
flags = read_smic_flags(smic);
|
||||
if (flags & SMIC_FLAG_BSY)
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
|
||||
status = read_smic_status (smic);
|
||||
if (smic_debug & SMIC_DEBUG_STATES)
|
||||
printk(KERN_INFO
|
||||
"smic_event - state = %d, flags = 0x%02x,"
|
||||
" status = 0x%02x\n",
|
||||
smic->state, flags, status);
|
||||
|
||||
switch (smic->state) {
|
||||
case SMIC_IDLE:
|
||||
/* in IDLE we check for available messages */
|
||||
if (flags & (SMIC_SMI |
|
||||
SMIC_EVM_DATA_AVAIL | SMIC_SMS_DATA_AVAIL))
|
||||
{
|
||||
return SI_SM_ATTN;
|
||||
}
|
||||
return SI_SM_IDLE;
|
||||
|
||||
case SMIC_START_OP:
|
||||
/* sanity check whether smic is really idle */
|
||||
write_smic_control(smic, SMIC_CC_SMS_GET_STATUS);
|
||||
write_smic_flags(smic, flags | SMIC_FLAG_BSY);
|
||||
smic->state = SMIC_OP_OK;
|
||||
break;
|
||||
|
||||
case SMIC_OP_OK:
|
||||
if (status != SMIC_SC_SMS_READY) {
|
||||
/* this should not happen */
|
||||
start_error_recovery(smic,
|
||||
"state = SMIC_OP_OK,"
|
||||
" status != SMIC_SC_SMS_READY");
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
/* OK so far; smic is idle let us start ... */
|
||||
write_smic_control(smic, SMIC_CC_SMS_WR_START);
|
||||
write_next_byte(smic);
|
||||
write_smic_flags(smic, flags | SMIC_FLAG_BSY);
|
||||
smic->state = SMIC_WRITE_START;
|
||||
break;
|
||||
|
||||
case SMIC_WRITE_START:
|
||||
if (status != SMIC_SC_SMS_WR_START) {
|
||||
start_error_recovery(smic,
|
||||
"state = SMIC_WRITE_START, "
|
||||
"status != SMIC_SC_SMS_WR_START");
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
/* we must not issue WR_(NEXT|END) unless
|
||||
TX_DATA_READY is set */
|
||||
if (flags & SMIC_TX_DATA_READY) {
|
||||
if (smic->write_count == 1) {
|
||||
/* last byte */
|
||||
write_smic_control(smic, SMIC_CC_SMS_WR_END);
|
||||
smic->state = SMIC_WRITE_END;
|
||||
} else {
|
||||
write_smic_control(smic, SMIC_CC_SMS_WR_NEXT);
|
||||
smic->state = SMIC_WRITE_NEXT;
|
||||
}
|
||||
write_next_byte(smic);
|
||||
write_smic_flags(smic, flags | SMIC_FLAG_BSY);
|
||||
}
|
||||
else {
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
break;
|
||||
|
||||
case SMIC_WRITE_NEXT:
|
||||
if (status != SMIC_SC_SMS_WR_NEXT) {
|
||||
start_error_recovery(smic,
|
||||
"state = SMIC_WRITE_NEXT, "
|
||||
"status != SMIC_SC_SMS_WR_NEXT");
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
/* this is the same code as in SMIC_WRITE_START */
|
||||
if (flags & SMIC_TX_DATA_READY) {
|
||||
if (smic->write_count == 1) {
|
||||
write_smic_control(smic, SMIC_CC_SMS_WR_END);
|
||||
smic->state = SMIC_WRITE_END;
|
||||
}
|
||||
else {
|
||||
write_smic_control(smic, SMIC_CC_SMS_WR_NEXT);
|
||||
smic->state = SMIC_WRITE_NEXT;
|
||||
}
|
||||
write_next_byte(smic);
|
||||
write_smic_flags(smic, flags | SMIC_FLAG_BSY);
|
||||
}
|
||||
else {
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
break;
|
||||
|
||||
case SMIC_WRITE_END:
|
||||
if (status != SMIC_SC_SMS_WR_END) {
|
||||
start_error_recovery (smic,
|
||||
"state = SMIC_WRITE_END, "
|
||||
"status != SMIC_SC_SMS_WR_END");
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
/* data register holds an error code */
|
||||
data = read_smic_data(smic);
|
||||
if (data != 0) {
|
||||
if (smic_debug & SMIC_DEBUG_ENABLE) {
|
||||
printk(KERN_INFO
|
||||
"SMIC_WRITE_END: data = %02x\n", data);
|
||||
}
|
||||
start_error_recovery(smic,
|
||||
"state = SMIC_WRITE_END, "
|
||||
"data != SUCCESS");
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
} else {
|
||||
smic->state = SMIC_WRITE2READ;
|
||||
}
|
||||
break;
|
||||
|
||||
case SMIC_WRITE2READ:
|
||||
/* we must wait for RX_DATA_READY to be set before we
|
||||
can continue */
|
||||
if (flags & SMIC_RX_DATA_READY) {
|
||||
write_smic_control(smic, SMIC_CC_SMS_RD_START);
|
||||
write_smic_flags(smic, flags | SMIC_FLAG_BSY);
|
||||
smic->state = SMIC_READ_START;
|
||||
} else {
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
break;
|
||||
|
||||
case SMIC_READ_START:
|
||||
if (status != SMIC_SC_SMS_RD_START) {
|
||||
start_error_recovery(smic,
|
||||
"state = SMIC_READ_START, "
|
||||
"status != SMIC_SC_SMS_RD_START");
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
if (flags & SMIC_RX_DATA_READY) {
|
||||
read_next_byte(smic);
|
||||
write_smic_control(smic, SMIC_CC_SMS_RD_NEXT);
|
||||
write_smic_flags(smic, flags | SMIC_FLAG_BSY);
|
||||
smic->state = SMIC_READ_NEXT;
|
||||
} else {
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
break;
|
||||
|
||||
case SMIC_READ_NEXT:
|
||||
switch (status) {
|
||||
/* smic tells us that this is the last byte to be read
|
||||
--> clean up */
|
||||
case SMIC_SC_SMS_RD_END:
|
||||
read_next_byte(smic);
|
||||
write_smic_control(smic, SMIC_CC_SMS_RD_END);
|
||||
write_smic_flags(smic, flags | SMIC_FLAG_BSY);
|
||||
smic->state = SMIC_READ_END;
|
||||
break;
|
||||
case SMIC_SC_SMS_RD_NEXT:
|
||||
if (flags & SMIC_RX_DATA_READY) {
|
||||
read_next_byte(smic);
|
||||
write_smic_control(smic, SMIC_CC_SMS_RD_NEXT);
|
||||
write_smic_flags(smic, flags | SMIC_FLAG_BSY);
|
||||
smic->state = SMIC_READ_NEXT;
|
||||
} else {
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
start_error_recovery(
|
||||
smic,
|
||||
"state = SMIC_READ_NEXT, "
|
||||
"status != SMIC_SC_SMS_RD_(NEXT|END)");
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
break;
|
||||
|
||||
case SMIC_READ_END:
|
||||
if (status != SMIC_SC_SMS_READY) {
|
||||
start_error_recovery(smic,
|
||||
"state = SMIC_READ_END, "
|
||||
"status != SMIC_SC_SMS_READY");
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
data = read_smic_data(smic);
|
||||
/* data register holds an error code */
|
||||
if (data != 0) {
|
||||
if (smic_debug & SMIC_DEBUG_ENABLE) {
|
||||
printk(KERN_INFO
|
||||
"SMIC_READ_END: data = %02x\n", data);
|
||||
}
|
||||
start_error_recovery(smic,
|
||||
"state = SMIC_READ_END, "
|
||||
"data != SUCCESS");
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
} else {
|
||||
smic->state = SMIC_IDLE;
|
||||
return SI_SM_TRANSACTION_COMPLETE;
|
||||
}
|
||||
|
||||
case SMIC_HOSED:
|
||||
init_smic_data(smic, smic->io);
|
||||
return SI_SM_HOSED;
|
||||
|
||||
default:
|
||||
if (smic_debug & SMIC_DEBUG_ENABLE) {
|
||||
printk(KERN_WARNING "smic->state = %d\n", smic->state);
|
||||
start_error_recovery(smic, "state = UNKNOWN");
|
||||
return SI_SM_CALL_WITH_DELAY;
|
||||
}
|
||||
}
|
||||
smic->smic_timeout = SMIC_RETRY_TIMEOUT;
|
||||
return SI_SM_CALL_WITHOUT_DELAY;
|
||||
}
|
||||
|
||||
static int smic_detect(struct si_sm_data *smic)
|
||||
{
|
||||
/* It's impossible for the SMIC fnags register to be all 1's,
|
||||
(assuming a properly functioning, self-initialized BMC)
|
||||
but that's what you get from reading a bogus address, so we
|
||||
test that first. */
|
||||
if (read_smic_flags(smic) == 0xff)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void smic_cleanup(struct si_sm_data *kcs)
|
||||
{
|
||||
}
|
||||
|
||||
static int smic_size(void)
|
||||
{
|
||||
return sizeof(struct si_sm_data);
|
||||
}
|
||||
|
||||
struct si_sm_handlers smic_smi_handlers =
|
||||
{
|
||||
.version = IPMI_SMIC_VERSION,
|
||||
.init_data = init_smic_data,
|
||||
.start_transaction = start_smic_transaction,
|
||||
.get_result = smic_get_result,
|
||||
.event = smic_event,
|
||||
.detect = smic_detect,
|
||||
.cleanup = smic_cleanup,
|
||||
.size = smic_size,
|
||||
};
|
||||
1091
extra/linux-2.6.10/drivers/char/ipmi/ipmi_watchdog.c
Normal file
1091
extra/linux-2.6.10/drivers/char/ipmi/ipmi_watchdog.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user