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 ...@@ -43,11 +43,13 @@ config IPMI_DEVICE_INTERFACE
This provides an IOCTL interface to the IPMI message handler so This provides an IOCTL interface to the IPMI message handler so
userland processes may use IPMI. It supports poll() and select(). userland processes may use IPMI. It supports poll() and select().
config IPMI_KCS config IPMI_SI
tristate 'IPMI KCS handler' tristate 'IPMI System Interface handler'
depends on IPMI_HANDLER depends on IPMI_HANDLER
help 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 config IPMI_WATCHDOG
tristate 'IPMI Watchdog Timer' tristate 'IPMI Watchdog Timer'
......
...@@ -2,12 +2,13 @@ ...@@ -2,12 +2,13 @@
# Makefile for the ipmi drivers. # 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_HANDLER) += ipmi_msghandler.o
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.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 obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
ipmi_kcs_drv.o: $(ipmi_kcs_drv-objs) ipmi_si.o: $(ipmi_si-objs)
$(LD) -r -o $@ $(ipmi_kcs_drv-objs) $(LD) -r -o $@ $(ipmi_si-objs)
This diff is collapsed.
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <asm/system.h> #include <asm/system.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -44,6 +45,8 @@ ...@@ -44,6 +45,8 @@
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <linux/init.h> #include <linux/init.h>
#define IPMI_DEVINTF_VERSION "v31"
struct ipmi_file_private struct ipmi_file_private
{ {
ipmi_user_t user; ipmi_user_t user;
...@@ -53,6 +56,8 @@ struct ipmi_file_private ...@@ -53,6 +56,8 @@ struct ipmi_file_private
struct fasync_struct *fasync_queue; struct fasync_struct *fasync_queue;
wait_queue_head_t wait; wait_queue_head_t wait;
struct semaphore recv_sem; struct semaphore recv_sem;
int default_retries;
unsigned int default_retry_time_ms;
}; };
static void file_receive_handler(struct ipmi_recv_msg *msg, static void file_receive_handler(struct ipmi_recv_msg *msg,
...@@ -138,6 +143,10 @@ static int ipmi_open(struct inode *inode, struct file *file) ...@@ -138,6 +143,10 @@ static int ipmi_open(struct inode *inode, struct file *file)
priv->fasync_queue = NULL; priv->fasync_queue = NULL;
sema_init(&(priv->recv_sem), 1); sema_init(&(priv->recv_sem), 1);
/* Use the low-level defaults. */
priv->default_retries = -1;
priv->default_retry_time_ms = 0;
return 0; return 0;
} }
...@@ -158,6 +167,63 @@ static int ipmi_release(struct inode *inode, struct file *file) ...@@ -158,6 +167,63 @@ static int ipmi_release(struct inode *inode, struct file *file)
return 0; 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, static int ipmi_ioctl(struct inode *inode,
struct file *file, struct file *file,
unsigned int cmd, unsigned int cmd,
...@@ -170,54 +236,33 @@ static int ipmi_ioctl(struct inode *inode, ...@@ -170,54 +236,33 @@ static int ipmi_ioctl(struct inode *inode,
{ {
case IPMICTL_SEND_COMMAND: case IPMICTL_SEND_COMMAND:
{ {
struct ipmi_req req; struct ipmi_req req;
struct ipmi_addr addr;
unsigned char msgdata[IPMI_MAX_MSG_LENGTH];
if (copy_from_user(&req, (void *) data, sizeof(req))) { if (copy_from_user(&req, (void *) data, sizeof(req))) {
rv = -EFAULT; rv = -EFAULT;
break; break;
} }
if (req.addr_len > sizeof(struct ipmi_addr)) rv = handle_send_req(priv->user,
{ &req,
rv = -EINVAL; priv->default_retries,
break; 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; rv = -EFAULT;
break; break;
} }
rv = ipmi_validate_addr(&addr, req.addr_len); rv = handle_send_req(priv->user,
if (rv) &req.req,
break; req.retries,
req.retry_time_ms);
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);
break; break;
} }
...@@ -416,7 +461,36 @@ static int ipmi_ioctl(struct inode *inode, ...@@ -416,7 +461,36 @@ static int ipmi_ioctl(struct inode *inode,
rv = 0; rv = 0;
break; 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; return rv;
...@@ -435,29 +509,30 @@ static struct file_operations ipmi_fops = { ...@@ -435,29 +509,30 @@ static struct file_operations ipmi_fops = {
#define DEVICE_NAME "ipmidev" #define DEVICE_NAME "ipmidev"
static int ipmi_major = 0; static int ipmi_major = 0;
MODULE_PARM(ipmi_major, "i"); module_param(ipmi_major, int, 0);
MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By"
#define MAX_DEVICES 10 " 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) static void ipmi_new_smi(int if_num)
{ {
if (if_num <= MAX_DEVICES) { devfs_mk_cdev(MKDEV(ipmi_major, if_num),
devfs_mk_cdev(MKDEV(ipmi_major, if_num), S_IFCHR | S_IRUSR | S_IWUSR,
S_IFCHR | S_IRUSR | S_IWUSR, "ipmidev/%d", if_num);
"ipmidev/%d", if_num);
}
} }
static void ipmi_smi_gone(int 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 = static struct ipmi_smi_watcher smi_watcher =
{ {
.new_smi = ipmi_new_smi, .owner = THIS_MODULE,
.smi_gone = ipmi_smi_gone, .new_smi = ipmi_new_smi,
.smi_gone = ipmi_smi_gone,
}; };
static __init int init_ipmi_devintf(void) static __init int init_ipmi_devintf(void)
...@@ -467,6 +542,9 @@ static __init int init_ipmi_devintf(void) ...@@ -467,6 +542,9 @@ static __init int init_ipmi_devintf(void)
if (ipmi_major < 0) if (ipmi_major < 0)
return -EINVAL; return -EINVAL;
printk(KERN_INFO "ipmi device interface version "
IPMI_DEVINTF_VERSION "\n");
rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops); rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops);
if (rv < 0) { if (rv < 0) {
printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major); printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major);
...@@ -482,13 +560,10 @@ static __init int init_ipmi_devintf(void) ...@@ -482,13 +560,10 @@ static __init int init_ipmi_devintf(void)
rv = ipmi_smi_watcher_register(&smi_watcher); rv = ipmi_smi_watcher_register(&smi_watcher);
if (rv) { if (rv) {
unregister_chrdev(ipmi_major, DEVICE_NAME); 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; return rv;
} }
printk(KERN_INFO "ipmi: device interface at char major %d\n",
ipmi_major);
return 0; return 0;
} }
module_init(init_ipmi_devintf); module_init(init_ipmi_devintf);
...@@ -500,21 +575,5 @@ static __exit void cleanup_ipmi(void) ...@@ -500,21 +575,5 @@ static __exit void cleanup_ipmi(void)
unregister_chrdev(ipmi_major, DEVICE_NAME); unregister_chrdev(ipmi_major, DEVICE_NAME);
} }
module_exit(cleanup_ipmi); 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"); MODULE_LICENSE("GPL");
This diff is collapsed.
...@@ -37,13 +37,12 @@ ...@@ -37,13 +37,12 @@
* that document. * 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> #define IPMI_KCS_VERSION "v31"
#include <asm/string.h> /* Gets rid of memcpy warning */
#include <asm/system.h>
#include "ipmi_kcs_sm.h"
/* Set this if you want a printout of why the state machine was hosed /* Set this if you want a printout of why the state machine was hosed
when it gets hosed. */ when it gets hosed. */
...@@ -95,32 +94,28 @@ enum kcs_states { ...@@ -95,32 +94,28 @@ enum kcs_states {
#define OBF_RETRY_TIMEOUT 1000000 #define OBF_RETRY_TIMEOUT 1000000
#define MAX_ERROR_RETRIES 10 #define MAX_ERROR_RETRIES 10
#define IPMI_ERR_MSG_TRUNCATED 0xc6 struct si_sm_data
#define IPMI_ERR_UNSPECIFIED 0xff
struct kcs_data
{ {
enum kcs_states state; enum kcs_states state;
unsigned int port; struct si_sm_io *io;
unsigned char *addr; unsigned char write_data[MAX_KCS_WRITE_SIZE];
unsigned char write_data[MAX_KCS_WRITE_SIZE]; int write_pos;
int write_pos; int write_count;
int write_count; int orig_write_count;
int orig_write_count; unsigned char read_data[MAX_KCS_READ_SIZE];
unsigned char read_data[MAX_KCS_READ_SIZE]; int read_pos;
int read_pos; int truncated;
int truncated;
unsigned int error_retries; unsigned int error_retries;
long ibf_timeout; long ibf_timeout;
long obf_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->state = KCS_IDLE;
kcs->port = port; kcs->io = io;
kcs->addr = addr;
kcs->write_pos = 0; kcs->write_pos = 0;
kcs->write_count = 0; kcs->write_count = 0;
kcs->orig_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) ...@@ -129,40 +124,29 @@ void init_kcs_data(struct kcs_data *kcs, unsigned int port, unsigned char *addr)
kcs->truncated = 0; kcs->truncated = 0;
kcs->ibf_timeout = IBF_RETRY_TIMEOUT; kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
kcs->obf_timeout = OBF_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 kcs->io->inputb(kcs->io, 1);
return inb(kcs->port + 1);
else
return readb(kcs->addr + 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 kcs->io->inputb(kcs->io, 0);
return inb(kcs->port + 0);
else
return readb(kcs->addr + 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) kcs->io->outputb(kcs->io, 1, data);
outb(data, kcs->port + 1);
else
writeb(data, kcs->addr + 1);
} }
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) kcs->io->outputb(kcs->io, 0, data);
outb(data, kcs->port + 0);
else
writeb(data, kcs->addr + 0);
} }
/* Control codes. */ /* Control codes. */
...@@ -182,14 +166,14 @@ static inline void write_data(struct kcs_data *kcs, unsigned char data) ...@@ -182,14 +166,14 @@ static inline void write_data(struct kcs_data *kcs, unsigned char data)
#define GET_STATUS_OBF(status) ((status) & 0x01) #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]); write_data(kcs, kcs->write_data[kcs->write_pos]);
(kcs->write_pos)++; (kcs->write_pos)++;
(kcs->write_count)--; (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)++; (kcs->error_retries)++;
if (kcs->error_retries > MAX_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) ...@@ -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) { if (kcs->read_pos >= MAX_KCS_READ_SIZE) {
/* Throw the data away and mark it truncated. */ /* Throw the data away and mark it truncated. */
...@@ -215,9 +199,8 @@ static inline void read_next_byte(struct kcs_data *kcs) ...@@ -215,9 +199,8 @@ static inline void read_next_byte(struct kcs_data *kcs)
write_data(kcs, KCS_READ_BYTE); write_data(kcs, KCS_READ_BYTE);
} }
static inline int check_ibf(struct kcs_data *kcs, static inline int check_ibf(struct si_sm_data *kcs, unsigned char status,
unsigned char status, long time)
long time)
{ {
if (GET_STATUS_IBF(status)) { if (GET_STATUS_IBF(status)) {
kcs->ibf_timeout -= time; kcs->ibf_timeout -= time;
...@@ -232,9 +215,8 @@ static inline int check_ibf(struct kcs_data *kcs, ...@@ -232,9 +215,8 @@ static inline int check_ibf(struct kcs_data *kcs,
return 1; return 1;
} }
static inline int check_obf(struct kcs_data *kcs, static inline int check_obf(struct si_sm_data *kcs, unsigned char status,
unsigned char status, long time)
long time)
{ {
if (! GET_STATUS_OBF(status)) { if (! GET_STATUS_OBF(status)) {
kcs->obf_timeout -= time; kcs->obf_timeout -= time;
...@@ -248,13 +230,13 @@ static inline int check_obf(struct kcs_data *kcs, ...@@ -248,13 +230,13 @@ static inline int check_obf(struct kcs_data *kcs,
return 1; 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)) if (GET_STATUS_OBF(status))
read_data(kcs); 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_count = kcs->orig_write_count;
kcs->write_pos = 0; kcs->write_pos = 0;
...@@ -265,7 +247,8 @@ static void restart_kcs_transaction(struct kcs_data *kcs) ...@@ -265,7 +247,8 @@ static void restart_kcs_transaction(struct kcs_data *kcs)
write_cmd(kcs, KCS_WRITE_START); 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)) { if ((size < 2) || (size > MAX_KCS_WRITE_SIZE)) {
return -1; return -1;
...@@ -287,7 +270,8 @@ int start_kcs_transaction(struct kcs_data *kcs, char *data, unsigned int size) ...@@ -287,7 +270,8 @@ int start_kcs_transaction(struct kcs_data *kcs, char *data, unsigned int size)
return 0; 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) { if (length < kcs->read_pos) {
kcs->read_pos = length; kcs->read_pos = length;
...@@ -316,7 +300,7 @@ int kcs_get_result(struct kcs_data *kcs, unsigned char *data, int 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 /* This implements the state machine defined in the IPMI manual, see
that for details on how this works. Divide that flowchart into that for details on how this works. Divide that flowchart into
sections delimited by "Wait for IBF" and this will become clear. */ 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 status;
unsigned char state; unsigned char state;
...@@ -328,7 +312,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time) ...@@ -328,7 +312,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
#endif #endif
/* All states wait for ibf, so just do it here. */ /* All states wait for ibf, so just do it here. */
if (!check_ibf(kcs, status, time)) 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. */ /* Just about everything looks at the KCS state, so grab that, too. */
state = GET_STATUS_STATE(status); state = GET_STATUS_STATE(status);
...@@ -339,9 +323,9 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time) ...@@ -339,9 +323,9 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
clear_obf(kcs, status); clear_obf(kcs, status);
if (GET_STATUS_ATN(status)) if (GET_STATUS_ATN(status))
return KCS_ATTN; return SI_SM_ATTN;
else else
return KCS_SM_IDLE; return SI_SM_IDLE;
case KCS_START_OP: case KCS_START_OP:
if (state != KCS_IDLE) { if (state != KCS_IDLE) {
...@@ -408,7 +392,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time) ...@@ -408,7 +392,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
if (state == KCS_READ_STATE) { if (state == KCS_READ_STATE) {
if (! check_obf(kcs, status, time)) if (! check_obf(kcs, status, time))
return KCS_CALL_WITH_DELAY; return SI_SM_CALL_WITH_DELAY;
read_next_byte(kcs); read_next_byte(kcs);
} else { } else {
/* We don't implement this exactly like the state /* We don't implement this exactly like the state
...@@ -421,7 +405,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time) ...@@ -421,7 +405,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
clear_obf(kcs, status); clear_obf(kcs, status);
kcs->orig_write_count = 0; kcs->orig_write_count = 0;
kcs->state = KCS_IDLE; kcs->state = KCS_IDLE;
return KCS_TRANSACTION_COMPLETE; return SI_SM_TRANSACTION_COMPLETE;
} }
break; break;
...@@ -444,7 +428,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time) ...@@ -444,7 +428,7 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
break; break;
} }
if (! check_obf(kcs, status, time)) if (! check_obf(kcs, status, time))
return KCS_CALL_WITH_DELAY; return SI_SM_CALL_WITH_DELAY;
clear_obf(kcs, status); clear_obf(kcs, status);
write_data(kcs, KCS_READ_BYTE); write_data(kcs, KCS_READ_BYTE);
...@@ -459,14 +443,14 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time) ...@@ -459,14 +443,14 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
} }
if (! check_obf(kcs, status, time)) if (! check_obf(kcs, status, time))
return KCS_CALL_WITH_DELAY; return SI_SM_CALL_WITH_DELAY;
clear_obf(kcs, status); clear_obf(kcs, status);
if (kcs->orig_write_count) { if (kcs->orig_write_count) {
restart_kcs_transaction(kcs); restart_kcs_transaction(kcs);
} else { } else {
kcs->state = KCS_IDLE; kcs->state = KCS_IDLE;
return KCS_TRANSACTION_COMPLETE; return SI_SM_TRANSACTION_COMPLETE;
} }
break; break;
...@@ -475,14 +459,42 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time) ...@@ -475,14 +459,42 @@ enum kcs_result kcs_event(struct kcs_data *kcs, long time)
} }
if (kcs->state == KCS_HOSED) { if (kcs->state == KCS_HOSED) {
init_kcs_data(kcs, kcs->port, kcs->addr); init_kcs_data(kcs, kcs->io);
return KCS_SM_HOSED; 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. * Author: MontaVista Software, Inc.
* Corey Minyard <minyard@mvista.com> * Corey Minyard <minyard@mvista.com>
...@@ -31,40 +34,84 @@ ...@@ -31,40 +34,84 @@
* 675 Mass Ave, Cambridge, MA 02139, USA. * 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, /* The structure for doing I/O in the state machine. The state
unsigned int port, machine doesn't have the actual I/O routines, they are done through
unsigned char *addr); this interface. */
struct si_sm_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 unsigned char (*inputb)(struct si_sm_io *io, unsigned int offset);
large or too small), or 0 if the transaction is successfully void (*outputb)(struct si_sm_io *io,
completed. */ unsigned int offset,
int start_kcs_transaction(struct kcs_data *kcs, char *data, unsigned int size); unsigned char b);
/* Return the results after the transaction. This will return -1 if /* Generic info used by the actual handling routines, the
the buffer is too small, zero if no transaction is present, or the state machine shouldn't touch these. */
actual length of the result data. */ void *info;
int kcs_get_result(struct kcs_data *kcs, unsigned char *data, int length); void *addr;
};
enum kcs_result /* Results of SMI events. */
enum si_sm_result
{ {
KCS_CALL_WITHOUT_DELAY, /* Call the driver again immediately */ SI_SM_CALL_WITHOUT_DELAY, /* Call the driver again immediately */
KCS_CALL_WITH_DELAY, /* Delay some before calling again. */ SI_SM_CALL_WITH_DELAY, /* Delay some before calling again. */
KCS_TRANSACTION_COMPLETE, /* A transaction is finished. */ SI_SM_TRANSACTION_COMPLETE, /* A transaction is finished. */
KCS_SM_IDLE, /* The SM is in idle state. */ SI_SM_IDLE, /* The SM is in idle state. */
KCS_SM_HOSED, /* The hardware violated the state machine. */ SI_SM_HOSED, /* The hardware violated the state machine. */
KCS_ATTN /* The hardware is asserting attn and the SI_SM_ATTN /* The hardware is asserting attn and the
state machine is idle. */ state machine is idle. */
}; };
/* Call this periodically (for a polled interface) or upon receiving /* Handlers for the SMI state machine. */
an interrupt (for a interrupt-driven interface). If interrupt struct si_sm_handlers
driven, you should probably poll this periodically when not in idle {
state. This should be called with the time that passed since the /* Put the version number of the state machine here so the
last call, if it is significant. Time is in microseconds. */ upper layer can print it. */
enum kcs_result kcs_event(struct kcs_data *kcs, long time); 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 @@ ...@@ -53,6 +53,7 @@
#define IPMI_SET_BMC_GLOBAL_ENABLES_CMD 0x2e #define IPMI_SET_BMC_GLOBAL_ENABLES_CMD 0x2e
#define IPMI_GET_BMC_GLOBAL_ENABLES_CMD 0x2f #define IPMI_GET_BMC_GLOBAL_ENABLES_CMD 0x2f
#define IPMI_READ_EVENT_MSG_BUFFER_CMD 0x35 #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_REQUEST 0x0a
#define IPMI_NETFN_STORAGE_RESPONSE 0x0b #define IPMI_NETFN_STORAGE_RESPONSE 0x0b
...@@ -61,8 +62,39 @@ ...@@ -61,8 +62,39 @@
/* The default slave address */ /* The default slave address */
#define IPMI_BMC_SLAVE_ADDR 0x20 #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 */ #endif /* __LINUX_IPMI_MSGDEFS_H */
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
#define __LINUX_IPMI_SMI_H #define __LINUX_IPMI_SMI_H
#include <linux/ipmi_msgdefs.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 /* This files describes the interface for IPMI system management interface
drivers to bind into the IPMI message handler. */ drivers to bind into the IPMI message handler. */
...@@ -48,7 +50,7 @@ typedef struct ipmi_smi *ipmi_smi_t; ...@@ -48,7 +50,7 @@ typedef struct ipmi_smi *ipmi_smi_t;
* been received, it will report this same data structure back up to * been received, it will report this same data structure back up to
* the upper layer. If an error occurs, it should fill in the * the upper layer. If an error occurs, it should fill in the
* response with an error code in the completion code location. When * 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 * data_size is set to zero and the response holds the data from the
* get message or get event command that the interface initiated. * get message or get event command that the interface initiated.
* Note that it is the interfaces responsibility to detect * Note that it is the interfaces responsibility to detect
...@@ -62,9 +64,6 @@ struct ipmi_smi_msg ...@@ -62,9 +64,6 @@ struct ipmi_smi_msg
long msgid; long msgid;
void *user_data; void *user_data;
/* If 0, add to the end of the queue. If 1, add to the beginning. */
int prio;
int data_size; int data_size;
unsigned char data[IPMI_MAX_MSG_LENGTH]; unsigned char data[IPMI_MAX_MSG_LENGTH];
...@@ -134,4 +133,11 @@ static inline void ipmi_free_smi_msg(struct ipmi_smi_msg *msg) ...@@ -134,4 +133,11 @@ static inline void ipmi_free_smi_msg(struct ipmi_smi_msg *msg)
msg->done(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 */ #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