Commit 0ab2d668 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] IPMI driver updates

From: Corey Minyard <minyard@acm.org>

- Add support for messaging through an IPMI LAN interface, which is
  required for some system software that already exists on other IPMI
  drivers.  It also does some renaming and a lot of little cleanups.

- Add the "System Interface" driver.  The previous driver for system
  interfaces only supported the KCS interface, this driver supports all
  system interfaces defined in the IPMI standard.  It also does a much better
  job of handling ACPI and SMBIOS tables for detecting IPMI system
  interfaces.
parent 87c22e84
This diff is collapsed.
......@@ -43,11 +43,13 @@ config IPMI_DEVICE_INTERFACE
This provides an IOCTL interface to the IPMI message handler so
userland processes may use IPMI. It supports poll() and select().
config IPMI_KCS
tristate 'IPMI KCS handler'
config IPMI_SI
tristate 'IPMI System Interface handler'
depends on IPMI_HANDLER
help
Provides a driver for a KCS-style interface to a BMC.
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'
......
......@@ -2,12 +2,13 @@
# Makefile for the ipmi drivers.
#
ipmi_kcs_drv-objs := ipmi_kcs_sm.o ipmi_kcs_intf.o
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_KCS) += ipmi_kcs_drv.o
obj-$(CONFIG_IPMI_SI) += ipmi_si.o
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
ipmi_kcs_drv.o: $(ipmi_kcs_drv-objs)
$(LD) -r -o $@ $(ipmi_kcs_drv-objs)
ipmi_si.o: $(ipmi_si-objs)
$(LD) -r -o $@ $(ipmi_si-objs)
This diff is collapsed.
......@@ -33,6 +33,7 @@
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <asm/system.h>
#include <linux/sched.h>
......@@ -44,6 +45,8 @@
#include <asm/semaphore.h>
#include <linux/init.h>
#define IPMI_DEVINTF_VERSION "v31"
struct ipmi_file_private
{
ipmi_user_t user;
......@@ -53,6 +56,8 @@ struct ipmi_file_private
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,
......@@ -138,6 +143,10 @@ static int ipmi_open(struct inode *inode, struct file *file)
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;
}
......@@ -158,6 +167,63 @@ static int ipmi_release(struct inode *inode, struct file *file)
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;
unsigned char *msgdata;
if (req->addr_len > sizeof(struct ipmi_addr))
return -EINVAL;
if (copy_from_user(&addr, req->addr, req->addr_len))
return -EFAULT;
msgdata = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
if (!msgdata)
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(&msgdata,
req->msg.data,
req->msg.data_len))
{
rv = -EFAULT;
goto out;
}
} else {
req->msg.data_len = 0;
}
req->msg.data = msgdata;
rv = ipmi_request_settime(user,
&addr,
req->msgid,
&(req->msg),
NULL,
0,
retries,
retry_time_ms);
out:
kfree(msgdata);
return rv;
}
static int ipmi_ioctl(struct inode *inode,
struct file *file,
unsigned int cmd,
......@@ -170,54 +236,33 @@ static int ipmi_ioctl(struct inode *inode,
{
case IPMICTL_SEND_COMMAND:
{
struct ipmi_req req;
struct ipmi_addr addr;
unsigned char msgdata[IPMI_MAX_MSG_LENGTH];
struct ipmi_req req;
if (copy_from_user(&req, (void *) data, sizeof(req))) {
rv = -EFAULT;
break;
}
if (req.addr_len > sizeof(struct ipmi_addr))
{
rv = -EINVAL;
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(&addr, req.addr, req.addr_len)) {
if (copy_from_user(&req, (void *) data, sizeof(req))) {
rv = -EFAULT;
break;
}
rv = ipmi_validate_addr(&addr, req.addr_len);
if (rv)
break;
if (req.msg.data != NULL) {
if (req.msg.data_len > IPMI_MAX_MSG_LENGTH) {
rv = -EMSGSIZE;
break;
}
if (copy_from_user(&msgdata,
req.msg.data,
req.msg.data_len))
{
rv = -EFAULT;
break;
}
} else {
req.msg.data_len = 0;
}
req.msg.data = msgdata;
rv = ipmi_request(priv->user,
&addr,
req.msgid,
&(req.msg),
0);
rv = handle_send_req(priv->user,
&req.req,
req.retries,
req.retry_time_ms);
break;
}
......@@ -416,7 +461,36 @@ static int ipmi_ioctl(struct inode *inode,
rv = 0;
break;
}
case IPMICTL_SET_TIMING_PARMS_CMD:
{
struct ipmi_timing_parms parms;
if (copy_from_user(&parms, (void *) data, 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((void *) data, &parms, sizeof(parms))) {
rv = -EFAULT;
break;
}
rv = 0;
break;
}
}
return rv;
......@@ -435,29 +509,30 @@ static struct file_operations ipmi_fops = {
#define DEVICE_NAME "ipmidev"
static int ipmi_major = 0;
MODULE_PARM(ipmi_major, "i");
#define MAX_DEVICES 10
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)
{
if (if_num <= MAX_DEVICES) {
devfs_mk_cdev(MKDEV(ipmi_major, if_num),
S_IFCHR | S_IRUSR | S_IWUSR,
"ipmidev/%d", 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)
{
if (if_num <= MAX_DEVICES)
devfs_remove("ipmidev/%d", if_num);
devfs_remove("ipmidev/%d", if_num);
}
static struct ipmi_smi_watcher smi_watcher =
{
.new_smi = ipmi_new_smi,
.smi_gone = ipmi_smi_gone,
.owner = THIS_MODULE,
.new_smi = ipmi_new_smi,
.smi_gone = ipmi_smi_gone,
};
static __init int init_ipmi_devintf(void)
......@@ -467,6 +542,9 @@ static __init int init_ipmi_devintf(void)
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);
......@@ -482,13 +560,10 @@ static __init int init_ipmi_devintf(void)
rv = ipmi_smi_watcher_register(&smi_watcher);
if (rv) {
unregister_chrdev(ipmi_major, DEVICE_NAME);
printk(KERN_WARNING "ipmi: can't register smi watcher");
printk(KERN_WARNING "ipmi: can't register smi watcher\n");
return rv;
}
printk(KERN_INFO "ipmi: device interface at char major %d\n",
ipmi_major);
return 0;
}
module_init(init_ipmi_devintf);
......@@ -500,21 +575,5 @@ static __exit void cleanup_ipmi(void)
unregister_chrdev(ipmi_major, DEVICE_NAME);
}
module_exit(cleanup_ipmi);
#ifndef MODULE
static __init int ipmi_setup (char *str)
{
int x;
if (get_option (&str, &x)) {
/* ipmi=x sets the major number to x. */
ipmi_major = x;
} else if (!strcmp(str, "off")) {
ipmi_major = -1;
}
return 1;
}
#endif
__setup("ipmi=", ipmi_setup);
MODULE_LICENSE("GPL");
This diff is collapsed.
......@@ -37,13 +37,12 @@
* that document.
*/
#include <linux/types.h>
#include <linux/kernel.h> /* For printk. */
#include <linux/string.h>
#include <linux/ipmi_msgdefs.h> /* for completion codes */
#include "ipmi_si_sm.h"
#include <asm/io.h>
#include <asm/string.h> /* Gets rid of memcpy warning */
#include <asm/system.h>
#include "ipmi_kcs_sm.h"
#define IPMI_KCS_VERSION "v31"
/* Set this if you want a printout of why the state machine was hosed
when it gets hosed. */
......@@ -95,32 +94,28 @@ enum kcs_states {
#define OBF_RETRY_TIMEOUT 1000000
#define MAX_ERROR_RETRIES 10
#define IPMI_ERR_MSG_TRUNCATED 0xc6
#define IPMI_ERR_UNSPECIFIED 0xff
struct kcs_data
struct si_sm_data
{
enum kcs_states state;
unsigned int port;
unsigned char *addr;
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;
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;
};
void init_kcs_data(struct kcs_data *kcs, unsigned int port, unsigned char *addr)
static unsigned int init_kcs_data(struct si_sm_data *kcs,
struct si_sm_io *io)
{
kcs->state = KCS_IDLE;
kcs->port = port;
kcs->addr = addr;
kcs->io = io;
kcs->write_pos = 0;
kcs->write_count = 0;
kcs->orig_write_count = 0;
......@@ -129,40 +124,29 @@ void init_kcs_data(struct kcs_data *kcs, unsigned int port, unsigned char *addr)
kcs->truncated = 0;
kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
kcs->obf_timeout = OBF_RETRY_TIMEOUT;
}
/* Remember, init_one_kcs() insured port and addr can't both be set */
/* Reserve 2 I/O bytes. */
return 2;
}
static inline unsigned char read_status(struct kcs_data *kcs)
static inline unsigned char read_status(struct si_sm_data *kcs)
{
if (kcs->port)
return inb(kcs->port + 1);
else
return readb(kcs->addr + 1);
return kcs->io->inputb(kcs->io, 1);
}
static inline unsigned char read_data(struct kcs_data *kcs)
static inline unsigned char read_data(struct si_sm_data *kcs)
{
if (kcs->port)
return inb(kcs->port + 0);
else
return readb(kcs->addr + 0);
return kcs->io->inputb(kcs->io, 0);
}
static inline void write_cmd(struct kcs_data *kcs, unsigned char data)
static inline void write_cmd(struct si_sm_data *kcs, unsigned char data)
{
if (kcs->port)
outb(data, kcs->port + 1);
else
writeb(data, kcs->addr + 1);
kcs->io->outputb(kcs->io, 1, data);
}
static inline void write_data(struct kcs_data *kcs, unsigned char data)
static inline void write_data(struct si_sm_data *kcs, unsigned char data)
{
if (kcs->port)
outb(data, kcs->port + 0);
else
writeb(data, kcs->addr + 0);
kcs->io->outputb(kcs->io, 0, data);
}
/* Control codes. */
......@@ -182,14 +166,14 @@ static inline void write_data(struct kcs_data *kcs, unsigned char data)
#define GET_STATUS_OBF(status) ((status) & 0x01)
static inline void write_next_byte(struct kcs_data *kcs)
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 kcs_data *kcs, char *reason)
static inline void start_error_recovery(struct si_sm_data *kcs, char *reason)
{
(kcs->error_retries)++;
if (kcs->error_retries > MAX_ERROR_RETRIES) {
......@@ -202,7 +186,7 @@ static inline void start_error_recovery(struct kcs_data *kcs, char *reason)
}
}
static inline void read_next_byte(struct kcs_data *kcs)
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. */
......@@ -215,9 +199,8 @@ static inline void read_next_byte(struct kcs_data *kcs)
write_data(kcs, KCS_READ_BYTE);
}
static inline int check_ibf(struct kcs_data *kcs,
unsigned char status,
long time)
static inline int check_ibf(struct si_sm_data *kcs, unsigned char status,
long time)
{
if (GET_STATUS_IBF(status)) {
kcs->ibf_timeout -= time;
......@@ -232,9 +215,8 @@ static inline int check_ibf(struct kcs_data *kcs,
return 1;
}
static inline int check_obf(struct kcs_data *kcs,
unsigned char status,
long time)
static inline int check_obf(struct si_sm_data *kcs, unsigned char status,
long time)
{
if (! GET_STATUS_OBF(status)) {
kcs->obf_timeout -= time;
......@@ -248,13 +230,13 @@ static inline int check_obf(struct kcs_data *kcs,
return 1;
}
static void clear_obf(struct kcs_data *kcs, unsigned char status)
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 kcs_data *kcs)
static void restart_kcs_transaction(struct si_sm_data *kcs)
{
kcs->write_count = kcs->orig_write_count;
kcs->write_pos = 0;
......@@ -265,7 +247,8 @@ static void restart_kcs_transaction(struct kcs_data *kcs)
write_cmd(kcs, KCS_WRITE_START);
}
int start_kcs_transaction(struct kcs_data *kcs, char *data, unsigned int size)
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;
......@@ -287,7 +270,8 @@ int start_kcs_transaction(struct kcs_data *kcs, char *data, unsigned int size)
return 0;
}
int kcs_get_result(struct kcs_data *kcs, unsigned char *data, int length)
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;
......@@ -316,7 +300,7 @@ int kcs_get_result(struct kcs_data *kcs, unsigned char *data, int length)
/* 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. */
enum kcs_result kcs_event(struct kcs_data *kcs, long time)
static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
{
unsigned char status;
unsigned char state;
......@@ -328,7 +312,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
#endif
/* All states wait for ibf, so just do it here. */
if (!check_ibf(kcs, status, time))
return KCS_CALL_WITH_DELAY;
return SI_SM_CALL_WITH_DELAY;
/* Just about everything looks at the KCS state, so grab that, too. */
state = GET_STATUS_STATE(status);
......@@ -339,9 +323,9 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
clear_obf(kcs, status);
if (GET_STATUS_ATN(status))
return KCS_ATTN;
return SI_SM_ATTN;
else
return KCS_SM_IDLE;
return SI_SM_IDLE;
case KCS_START_OP:
if (state != KCS_IDLE) {
......@@ -408,7 +392,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
if (state == KCS_READ_STATE) {
if (! check_obf(kcs, status, time))
return KCS_CALL_WITH_DELAY;
return SI_SM_CALL_WITH_DELAY;
read_next_byte(kcs);
} else {
/* We don't implement this exactly like the state
......@@ -421,7 +405,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
clear_obf(kcs, status);
kcs->orig_write_count = 0;
kcs->state = KCS_IDLE;
return KCS_TRANSACTION_COMPLETE;
return SI_SM_TRANSACTION_COMPLETE;
}
break;
......@@ -444,7 +428,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
break;
}
if (! check_obf(kcs, status, time))
return KCS_CALL_WITH_DELAY;
return SI_SM_CALL_WITH_DELAY;
clear_obf(kcs, status);
write_data(kcs, KCS_READ_BYTE);
......@@ -459,14 +443,14 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
}
if (! check_obf(kcs, status, time))
return KCS_CALL_WITH_DELAY;
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 KCS_TRANSACTION_COMPLETE;
return SI_SM_TRANSACTION_COMPLETE;
}
break;
......@@ -475,14 +459,42 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
}
if (kcs->state == KCS_HOSED) {
init_kcs_data(kcs, kcs->port, kcs->addr);
return KCS_SM_HOSED;
init_kcs_data(kcs, kcs->io);
return SI_SM_HOSED;
}
return KCS_CALL_WITHOUT_DELAY;
return SI_SM_CALL_WITHOUT_DELAY;
}
int kcs_size(void)
static int kcs_size(void)
{
return sizeof(struct kcs_data);
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,
};
This diff is collapsed.
This diff is collapsed.
/*
* ipmi_kcs_sm.h
* ipmi_si_sm.h
*
* State machine for handling IPMI KCS interfaces.
* 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>
......@@ -31,40 +34,84 @@
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
struct kcs_data;
/* This is defined by the state machines themselves, it is an opaque
data type for them to use. */
struct si_sm_data;
void init_kcs_data(struct kcs_data *kcs,
unsigned int port,
unsigned char *addr);
/* 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_kcs_transaction(struct kcs_data *kcs, char *data, unsigned int size);
/* 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);
/* 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 kcs_get_result(struct kcs_data *kcs, unsigned char *data, int length);
/* Generic info used by the actual handling routines, the
state machine shouldn't touch these. */
void *info;
void *addr;
};
enum kcs_result
/* Results of SMI events. */
enum si_sm_result
{
KCS_CALL_WITHOUT_DELAY, /* Call the driver again immediately */
KCS_CALL_WITH_DELAY, /* Delay some before calling again. */
KCS_TRANSACTION_COMPLETE, /* A transaction is finished. */
KCS_SM_IDLE, /* The SM is in idle state. */
KCS_SM_HOSED, /* The hardware violated the state machine. */
KCS_ATTN /* The hardware is asserting attn and the
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. */
};
/* 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 kcs_result kcs_event(struct kcs_data *kcs, long time);
/* 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;
/* Return the size of the KCS structure in bytes. */
int kcs_size(void);
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -53,6 +53,7 @@
#define IPMI_SET_BMC_GLOBAL_ENABLES_CMD 0x2e
#define IPMI_GET_BMC_GLOBAL_ENABLES_CMD 0x2f
#define IPMI_READ_EVENT_MSG_BUFFER_CMD 0x35
#define IPMI_GET_CHANNEL_INFO_CMD 0x42
#define IPMI_NETFN_STORAGE_REQUEST 0x0a
#define IPMI_NETFN_STORAGE_RESPONSE 0x0b
......@@ -61,8 +62,39 @@
/* The default slave address */
#define IPMI_BMC_SLAVE_ADDR 0x20
#define IPMI_MAX_MSG_LENGTH 80
/* The BT interface on high-end HP systems supports up to 255 bytes in
* one transfer. Its "virtual" BMC supports some commands that are longer
* than 128 bytes. Use the full 256, plus NetFn/LUN, Cmd, cCode, plus
* some overhead. It would be nice to base this on the "BT Capabilities"
* but that's too hard to propogate to the rest of the driver. */
#define IPMI_MAX_MSG_LENGTH 272 /* multiple of 16 */
#define IPMI_CC_NO_ERROR 0
#define IPMI_CC_NO_ERROR 0x00
#define IPMI_NODE_BUSY_ERR 0xc0
#define IPMI_ERR_MSG_TRUNCATED 0xc6
#define IPMI_LOST_ARBITRATION_ERR 0x81
#define IPMI_ERR_UNSPECIFIED 0xff
#define IPMI_CHANNEL_PROTOCOL_IPMB 1
#define IPMI_CHANNEL_PROTOCOL_ICMB 2
#define IPMI_CHANNEL_PROTOCOL_SMBUS 4
#define IPMI_CHANNEL_PROTOCOL_KCS 5
#define IPMI_CHANNEL_PROTOCOL_SMIC 6
#define IPMI_CHANNEL_PROTOCOL_BT10 7
#define IPMI_CHANNEL_PROTOCOL_BT15 8
#define IPMI_CHANNEL_PROTOCOL_TMODE 9
#define IPMI_CHANNEL_MEDIUM_IPMB 1
#define IPMI_CHANNEL_MEDIUM_ICMB10 2
#define IPMI_CHANNEL_MEDIUM_ICMB09 3
#define IPMI_CHANNEL_MEDIUM_8023LAN 4
#define IPMI_CHANNEL_MEDIUM_ASYNC 5
#define IPMI_CHANNEL_MEDIUM_OTHER_LAN 6
#define IPMI_CHANNEL_MEDIUM_PCI_SMBUS 7
#define IPMI_CHANNEL_MEDIUM_SMBUS1 8
#define IPMI_CHANNEL_MEDIUM_SMBUS2 9
#define IPMI_CHANNEL_MEDIUM_USB1 10
#define IPMI_CHANNEL_MEDIUM_USB2 11
#define IPMI_CHANNEL_MEDIUM_SYSINTF 12
#endif /* __LINUX_IPMI_MSGDEFS_H */
......@@ -35,6 +35,8 @@
#define __LINUX_IPMI_SMI_H
#include <linux/ipmi_msgdefs.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
/* This files describes the interface for IPMI system management interface
drivers to bind into the IPMI message handler. */
......@@ -48,7 +50,7 @@ typedef struct ipmi_smi *ipmi_smi_t;
* been received, it will report this same data structure back up to
* the upper layer. If an error occurs, it should fill in the
* response with an error code in the completion code location. When
* asyncronous data is received, one of these is allocated, the
* asynchronous data is received, one of these is allocated, the
* data_size is set to zero and the response holds the data from the
* get message or get event command that the interface initiated.
* Note that it is the interfaces responsibility to detect
......@@ -62,9 +64,6 @@ struct ipmi_smi_msg
long msgid;
void *user_data;
/* If 0, add to the end of the queue. If 1, add to the beginning. */
int prio;
int data_size;
unsigned char data[IPMI_MAX_MSG_LENGTH];
......@@ -134,4 +133,11 @@ static inline void ipmi_free_smi_msg(struct ipmi_smi_msg *msg)
msg->done(msg);
}
/* Allow the lower layer to add things to the proc filesystem
directory for this interface. Note that the entry will
automatically be dstroyed when the interface is destroyed. */
int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
read_proc_t *read_proc, write_proc_t *write_proc,
void *data, struct module *owner);
#endif /* __LINUX_IPMI_SMI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment