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,
...@@ -171,53 +237,32 @@ static int ipmi_ioctl(struct inode *inode, ...@@ -171,53 +237,32 @@ 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);
}
if (copy_from_user(&addr, req.addr, req.addr_len)) {
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; break;
} }
if (copy_from_user(&msgdata, case IPMICTL_SEND_COMMAND_SETTIME:
req.msg.data,
req.msg.data_len))
{ {
struct ipmi_req_settime req;
if (copy_from_user(&req, (void *) data, sizeof(req))) {
rv = -EFAULT; rv = -EFAULT;
break; break;
} }
} else {
req.msg.data_len = 0;
}
req.msg.data = msgdata; rv = handle_send_req(priv->user,
&req.req,
rv = ipmi_request(priv->user, req.retries,
&addr, req.retry_time_ms);
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,27 +509,28 @@ static struct file_operations ipmi_fops = { ...@@ -435,27 +509,28 @@ 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 =
{ {
.owner = THIS_MODULE,
.new_smi = ipmi_new_smi, .new_smi = ipmi_new_smi,
.smi_gone = ipmi_smi_gone, .smi_gone = ipmi_smi_gone,
}; };
...@@ -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,14 +94,10 @@ enum kcs_states { ...@@ -95,14 +94,10 @@ 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;
...@@ -116,11 +111,11 @@ struct kcs_data ...@@ -116,11 +111,11 @@ struct kcs_data
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,8 +199,7 @@ static inline void read_next_byte(struct kcs_data *kcs) ...@@ -215,8 +199,7 @@ 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)) {
...@@ -232,8 +215,7 @@ static inline int check_ibf(struct kcs_data *kcs, ...@@ -232,8 +215,7 @@ 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)) {
...@@ -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;
}
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;
} }
int kcs_size(void) static void kcs_cleanup(struct si_sm_data *kcs)
{ {
return sizeof(struct kcs_data);
} }
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.
...@@ -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/ipmi.h> #include <linux/ipmi.h>
#include <linux/ipmi_smi.h> #include <linux/ipmi_smi.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
...@@ -50,6 +51,8 @@ ...@@ -50,6 +51,8 @@
#include <asm/apic.h> #include <asm/apic.h>
#endif #endif
#define IPMI_WATCHDOG_VERSION "v31"
/* /*
* The IPMI command/response information for the watchdog timer. * The IPMI command/response information for the watchdog timer.
*/ */
...@@ -137,26 +140,41 @@ static int pretimeout = 0; ...@@ -137,26 +140,41 @@ static int pretimeout = 0;
/* Default action is to reset the board on a timeout. */ /* Default action is to reset the board on a timeout. */
static unsigned char action_val = WDOG_TIMEOUT_RESET; static unsigned char action_val = WDOG_TIMEOUT_RESET;
static char *action = "reset"; static char action[16] = "reset";
static unsigned char preaction_val = WDOG_PRETIMEOUT_NONE; static unsigned char preaction_val = WDOG_PRETIMEOUT_NONE;
static char *preaction = "pre_none"; static char preaction[16] = "pre_none";
static unsigned char preop_val = WDOG_PREOP_NONE; static unsigned char preop_val = WDOG_PREOP_NONE;
static char *preop = "preop_none"; static char preop[16] = "preop_none";
static spinlock_t ipmi_read_lock = SPIN_LOCK_UNLOCKED; static spinlock_t ipmi_read_lock = SPIN_LOCK_UNLOCKED;
static char data_to_read = 0; static char data_to_read = 0;
static DECLARE_WAIT_QUEUE_HEAD(read_q); static DECLARE_WAIT_QUEUE_HEAD(read_q);
static struct fasync_struct *fasync_q = NULL; static struct fasync_struct *fasync_q = NULL;
static char pretimeout_since_last_heartbeat = 0; static char pretimeout_since_last_heartbeat = 0;
MODULE_PARM(timeout, "i"); /* If true, the driver will start running as soon as it is configured
MODULE_PARM(pretimeout, "i"); and ready. */
MODULE_PARM(action, "s"); static int start_now = 0;
MODULE_PARM(preaction, "s");
MODULE_PARM(preop, "s"); module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Timeout value in seconds.");
module_param(pretimeout, int, 0);
MODULE_PARM_DESC(pretimeout, "Pretimeout value in seconds.");
module_param_string(action, action, sizeof(action), 0);
MODULE_PARM_DESC(action, "Timeout action. One of: "
"reset, none, power_cycle, power_off.");
module_param_string(preaction, preaction, sizeof(preaction), 0);
MODULE_PARM_DESC(preaction, "Pretimeout action. One of: "
"pre_none, pre_smi, pre_nmi, pre_int.");
module_param_string(preop, preop, sizeof(preop), 0);
MODULE_PARM_DESC(preop, "Pretimeout driver operation. One of: "
"preop_none, preop_panic, preop_give_data.");
module_param(start_now, int, 0);
MODULE_PARM_DESC(start_now, "Set to 1 to start the watchdog as"
"soon as the driver is loaded.");
/* Default state of the timer. */ /* Default state of the timer. */
static unsigned char ipmi_watchdog_state = WDOG_TIMEOUT_NONE; static unsigned char ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
...@@ -167,10 +185,6 @@ static int ipmi_ignore_heartbeat = 0; ...@@ -167,10 +185,6 @@ static int ipmi_ignore_heartbeat = 0;
/* Is someone using the watchdog? Only one user is allowed. */ /* Is someone using the watchdog? Only one user is allowed. */
static int ipmi_wdog_open = 0; static int ipmi_wdog_open = 0;
/* If true, the driver will start running as soon as it is configured
and ready. */
static int start_now = 0;
/* If set to 1, the heartbeat command will set the state to reset and /* If set to 1, the heartbeat command will set the state to reset and
start the timer. The timer doesn't normally run when the driver is start the timer. The timer doesn't normally run when the driver is
first opened until the heartbeat is set the first time, this first opened until the heartbeat is set the first time, this
...@@ -260,6 +274,7 @@ static int i_ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, ...@@ -260,6 +274,7 @@ static int i_ipmi_set_timeout(struct ipmi_smi_msg *smi_msg,
(struct ipmi_addr *) &addr, (struct ipmi_addr *) &addr,
0, 0,
&msg, &msg,
NULL,
smi_msg, smi_msg,
recv_msg, recv_msg,
1); 1);
...@@ -435,6 +450,7 @@ static int ipmi_heartbeat(void) ...@@ -435,6 +450,7 @@ static int ipmi_heartbeat(void)
(struct ipmi_addr *) &addr, (struct ipmi_addr *) &addr,
0, 0,
&msg, &msg,
NULL,
&heartbeat_smi_msg, &heartbeat_smi_msg,
&heartbeat_recv_msg, &heartbeat_recv_msg,
1); 1);
...@@ -483,6 +499,7 @@ static void panic_halt_ipmi_heartbeat(void) ...@@ -483,6 +499,7 @@ static void panic_halt_ipmi_heartbeat(void)
(struct ipmi_addr *) &addr, (struct ipmi_addr *) &addr,
0, 0,
&msg, &msg,
NULL,
&panic_halt_heartbeat_smi_msg, &panic_halt_heartbeat_smi_msg,
&panic_halt_heartbeat_recv_msg, &panic_halt_heartbeat_recv_msg,
1); 1);
...@@ -903,6 +920,7 @@ static void ipmi_smi_gone(int if_num) ...@@ -903,6 +920,7 @@ static void ipmi_smi_gone(int if_num)
static struct ipmi_smi_watcher smi_watcher = static struct ipmi_smi_watcher smi_watcher =
{ {
.owner = THIS_MODULE,
.new_smi = ipmi_new_smi, .new_smi = ipmi_new_smi,
.smi_gone = ipmi_smi_gone .smi_gone = ipmi_smi_gone
}; };
...@@ -911,6 +929,9 @@ static int __init ipmi_wdog_init(void) ...@@ -911,6 +929,9 @@ static int __init ipmi_wdog_init(void)
{ {
int rv; int rv;
printk(KERN_INFO "IPMI watchdog driver version "
IPMI_WATCHDOG_VERSION "\n");
if (strcmp(action, "reset") == 0) { if (strcmp(action, "reset") == 0) {
action_val = WDOG_TIMEOUT_RESET; action_val = WDOG_TIMEOUT_RESET;
} else if (strcmp(action, "none") == 0) { } else if (strcmp(action, "none") == 0) {
...@@ -999,14 +1020,10 @@ static int __init ipmi_wdog_init(void) ...@@ -999,14 +1020,10 @@ static int __init ipmi_wdog_init(void)
register_reboot_notifier(&wdog_reboot_notifier); register_reboot_notifier(&wdog_reboot_notifier);
notifier_chain_register(&panic_notifier_list, &wdog_panic_notifier); notifier_chain_register(&panic_notifier_list, &wdog_panic_notifier);
printk(KERN_INFO "IPMI watchdog by "
"Corey Minyard (minyard@mvista.com)\n");
return 0; return 0;
} }
#ifdef MODULE static __exit void ipmi_unregister_watchdog(void)
static void ipmi_unregister_watchdog(void)
{ {
int rv; int rv;
...@@ -1034,6 +1051,7 @@ static void ipmi_unregister_watchdog(void) ...@@ -1034,6 +1051,7 @@ static void ipmi_unregister_watchdog(void)
pointers to our buffers, we want to make sure they are done before pointers to our buffers, we want to make sure they are done before
we release our memory. */ we release our memory. */
while (atomic_read(&set_timeout_tofree)) { while (atomic_read(&set_timeout_tofree)) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1); schedule_timeout(1);
} }
...@@ -1056,76 +1074,6 @@ static void __exit ipmi_wdog_exit(void) ...@@ -1056,76 +1074,6 @@ static void __exit ipmi_wdog_exit(void)
ipmi_unregister_watchdog(); ipmi_unregister_watchdog();
} }
module_exit(ipmi_wdog_exit); module_exit(ipmi_wdog_exit);
#else
static int __init ipmi_wdog_setup(char *str)
{
int val;
int rv;
char *option;
rv = get_option(&str, &val);
if (rv == 0)
return 1;
if (val > 0)
timeout = val;
if (rv == 1)
return 1;
rv = get_option(&str, &val);
if (rv == 0)
return 1;
if (val >= 0)
pretimeout = val;
if (rv == 1)
return 1;
while ((option = strsep(&str, ",")) != NULL) {
if (strcmp(option, "reset") == 0) {
action = "reset";
}
else if (strcmp(option, "none") == 0) {
action = "none";
}
else if (strcmp(option, "power_cycle") == 0) {
action = "power_cycle";
}
else if (strcmp(option, "power_off") == 0) {
action = "power_off";
}
else if (strcmp(option, "pre_none") == 0) {
preaction = "pre_none";
}
else if (strcmp(option, "pre_smi") == 0) {
preaction = "pre_smi";
}
#ifdef HAVE_NMI_HANDLER
else if (strcmp(option, "pre_nmi") == 0) {
preaction = "pre_nmi";
}
#endif
else if (strcmp(option, "pre_int") == 0) {
preaction = "pre_int";
}
else if (strcmp(option, "start_now") == 0) {
start_now = 1;
}
else if (strcmp(option, "preop_none") == 0) {
preop = "preop_none";
}
else if (strcmp(option, "preop_panic") == 0) {
preop = "preop_panic";
}
else if (strcmp(option, "preop_give_data") == 0) {
preop = "preop_give_data";
} else {
printk("Unknown IPMI watchdog option: '%s'\n", option);
}
}
return 1;
}
__setup("ipmi_wdog=", ipmi_wdog_setup);
#endif
EXPORT_SYMBOL(ipmi_delayed_shutdown); EXPORT_SYMBOL(ipmi_delayed_shutdown);
......
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 */
This diff is collapsed.
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