diff options
Diffstat (limited to 'drivers/char/ipmi/ipmi_poweroff.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_poweroff.c | 137 |
1 files changed, 116 insertions, 21 deletions
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index cb5cdc6f14bf..f951c30236c9 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -31,10 +31,13 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <asm/semaphore.h> -#include <linux/kdev_t.h> +#include <linux/config.h> #include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/proc_fs.h> #include <linux/string.h> +#include <linux/completion.h> +#include <linux/kdev_t.h> #include <linux/ipmi.h> #include <linux/ipmi_smi.h> @@ -44,6 +47,18 @@ /* Where to we insert our poweroff function? */ extern void (*pm_power_off)(void); +/* Definitions for controlling power off (if the system supports it). It + * conveniently matches the IPMI chassis control values. */ +#define IPMI_CHASSIS_POWER_DOWN 0 /* power down, the default. */ +#define IPMI_CHASSIS_POWER_CYCLE 0x02 /* power cycle */ + +/* the IPMI data command */ +static int poweroff_control = IPMI_CHASSIS_POWER_DOWN; + +/* parameter definition to allow user to flag power cycle */ +module_param(poweroff_control, int, IPMI_CHASSIS_POWER_DOWN); +MODULE_PARM_DESC(poweroff_control, " Set to 2 to enable power cycle instead of power down. Power cycle is contingent on hardware support, otherwise it defaults back to power down."); + /* Stuff from the get device id command. */ static unsigned int mfg_id; static unsigned int prod_id; @@ -75,10 +90,10 @@ static struct ipmi_recv_msg halt_recv_msg = static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) { - struct semaphore *sem = recv_msg->user_msg_data; + struct completion *comp = recv_msg->user_msg_data; - if (sem) - up(sem); + if (comp) + complete(comp); } static struct ipmi_user_hndl ipmi_poweroff_handler = @@ -91,27 +106,27 @@ static int ipmi_request_wait_for_response(ipmi_user_t user, struct ipmi_addr *addr, struct kernel_ipmi_msg *send_msg) { - int rv; - struct semaphore sem; + int rv; + struct completion comp; - sema_init (&sem, 0); + init_completion(&comp); - rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &sem, + rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &comp, &halt_smi_msg, &halt_recv_msg, 0); if (rv) return rv; - down (&sem); + wait_for_completion(&comp); return halt_recv_msg.msg.data[0]; } -/* We are in run-to-completion mode, no semaphore is desired. */ +/* We are in run-to-completion mode, no completion is desired. */ static int ipmi_request_in_rc_mode(ipmi_user_t user, struct ipmi_addr *addr, struct kernel_ipmi_msg *send_msg) { - int rv; + int rv; rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL, &halt_smi_msg, &halt_recv_msg, 0); @@ -349,26 +364,38 @@ static void ipmi_poweroff_chassis (ipmi_user_t user) smi_addr.channel = IPMI_BMC_CHANNEL; smi_addr.lun = 0; - printk(KERN_INFO PFX "Powering down via IPMI chassis control command\n"); + powercyclefailed: + printk(KERN_INFO PFX "Powering %s via IPMI chassis control command\n", + ((poweroff_control != IPMI_CHASSIS_POWER_CYCLE) ? "down" : "cycle")); /* * Power down */ send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; - data[0] = 0; /* Power down */ + data[0] = poweroff_control; send_msg.data = data; send_msg.data_len = sizeof(data); rv = ipmi_request_in_rc_mode(user, (struct ipmi_addr *) &smi_addr, &send_msg); if (rv) { - printk(KERN_ERR PFX "Unable to send chassis powerdown message," - " IPMI error 0x%x\n", rv); - goto out; + switch (poweroff_control) { + case IPMI_CHASSIS_POWER_CYCLE: + /* power cycle failed, default to power down */ + printk(KERN_ERR PFX "Unable to send chassis power " \ + "cycle message, IPMI error 0x%x\n", rv); + poweroff_control = IPMI_CHASSIS_POWER_DOWN; + goto powercyclefailed; + + case IPMI_CHASSIS_POWER_DOWN: + default: + printk(KERN_ERR PFX "Unable to send chassis power " \ + "down message, IPMI error 0x%x\n", rv); + break; + } } - out: return; } @@ -430,7 +457,8 @@ static void ipmi_po_new_smi(int if_num) if (ready) return; - rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, &ipmi_user); + rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, + &ipmi_user); if (rv) { printk(KERN_ERR PFX "could not create IPMI user, error %d\n", rv); @@ -509,21 +537,84 @@ static struct ipmi_smi_watcher smi_watcher = }; +#ifdef CONFIG_PROC_FS +/* displays properties to proc */ +static int proc_read_chassctrl(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + return sprintf(page, "%d\t[ 0=powerdown 2=powercycle ]\n", + poweroff_control); +} + +/* process property writes from proc */ +static int proc_write_chassctrl(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int rv = count; + unsigned int newval = 0; + + sscanf(buffer, "%d", &newval); + switch (newval) { + case IPMI_CHASSIS_POWER_CYCLE: + printk(KERN_INFO PFX "power cycle is now enabled\n"); + poweroff_control = newval; + break; + + case IPMI_CHASSIS_POWER_DOWN: + poweroff_control = IPMI_CHASSIS_POWER_DOWN; + break; + + default: + rv = -EINVAL; + break; + } + + return rv; +} +#endif /* CONFIG_PROC_FS */ + /* * Startup and shutdown functions. */ static int ipmi_poweroff_init (void) { - int rv; + int rv; + struct proc_dir_entry *file; printk ("Copyright (C) 2004 MontaVista Software -" " IPMI Powerdown via sys_reboot version " IPMI_POWEROFF_VERSION ".\n"); + switch (poweroff_control) { + case IPMI_CHASSIS_POWER_CYCLE: + printk(KERN_INFO PFX "Power cycle is enabled.\n"); + break; + + case IPMI_CHASSIS_POWER_DOWN: + default: + poweroff_control = IPMI_CHASSIS_POWER_DOWN; + break; + } + rv = ipmi_smi_watcher_register(&smi_watcher); - if (rv) + if (rv) { printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv); + goto out_err; + } + +#ifdef CONFIG_PROC_FS + file = create_proc_entry("poweroff_control", 0, proc_ipmi_root); + if (!file) { + printk(KERN_ERR PFX "Unable to create proc power control\n"); + } else { + file->nlink = 1; + file->read_proc = proc_read_chassctrl; + file->write_proc = proc_write_chassctrl; + file->owner = THIS_MODULE; + } +#endif + out_err: return rv; } @@ -532,6 +623,10 @@ static __exit void ipmi_poweroff_cleanup(void) { int rv; +#ifdef CONFIG_PROC_FS + remove_proc_entry("poweroff_control", proc_ipmi_root); +#endif + ipmi_smi_watcher_unregister(&smi_watcher); if (ready) { |