From: Corey Minyard Some people found some bugs and some missing functions in the IPMI driver, so I have patching things together for the next release. The attached patch moves to version 33 of the driver and contains: * SMBIOS table support for specifying register spacing. This allows non-contiguous registers to be specified and some machines do this. * ACPI table updates to support all the possible register sizes and bit offsets into the registers for the IPMI information. * Support for command line parameters to specify register spacing, sizes, and bit offsets. * Support for power control with IPMI. This allows a halt to power down a machine with IPMI. * A fix for a race condition with interrupts enabled on an SMP machine. A lock was released then reclaimed, but there was code later that assumed that had not happened. * A fix for protecting the driver against bad responses from the controller chip. In the past, the driver had assumed that the controller chip would not give it bad data. This has turned out to be a bad assumption * ACPI interrupt handlers now return a return value, adjust accordingly. Thank you to all the people who helped me with this. Signed-off-by: Corey Minyard Signed-off-by: Andrew Morton --- 25-akpm/Documentation/IPMI.txt | 25 + 25-akpm/drivers/char/ipmi/Kconfig | 7 25-akpm/drivers/char/ipmi/Makefile | 1 25-akpm/drivers/char/ipmi/ipmi_bt_sm.c | 2 25-akpm/drivers/char/ipmi/ipmi_devintf.c | 2 25-akpm/drivers/char/ipmi/ipmi_kcs_sm.c | 2 25-akpm/drivers/char/ipmi/ipmi_msghandler.c | 58 ++ 25-akpm/drivers/char/ipmi/ipmi_poweroff.c | 543 ++++++++++++++++++++++++++++ 25-akpm/drivers/char/ipmi/ipmi_si_intf.c | 360 +++++++++++++++--- 25-akpm/drivers/char/ipmi/ipmi_si_sm.h | 3 25-akpm/drivers/char/ipmi/ipmi_smic_sm.c | 2 25-akpm/drivers/char/ipmi/ipmi_watchdog.c | 2 25-akpm/include/linux/ipmi.h | 6 13 files changed, 933 insertions(+), 80 deletions(-) diff -puN Documentation/IPMI.txt~ipmi-driver-updates Documentation/IPMI.txt --- 25/Documentation/IPMI.txt~ipmi-driver-updates 2004-08-15 13:47:44.652606240 -0700 +++ 25-akpm/Documentation/IPMI.txt 2004-08-15 13:47:44.674602896 -0700 @@ -340,6 +340,8 @@ You can change this at module load time modprobe ipmi_si.o type=,.... ports=,... addrs=,... irqs=,... trydefaults=[0|1] + regspacings=,,... regsizes=,,... + regshifts=,,... Each of these except si_trydefaults is a list, the first item for the first interface, second item for the second interface, etc. @@ -361,12 +363,35 @@ si_trydefaults sets whether the standard any interfaces specified by ACPE are tried. By default, the driver tries it, set this value to zero to turn this off. +The next three parameters have to do with register layout. The +registers used by the interfaces may not appear at successive +locations and they may not be in 8-bit registers. These parameters +allow the layout of the data in the registers to be more precisely +specified. + +The regspacings parameter give the number of bytes between successive +register start addresses. For instance, if the regspacing is set to 4 +and the start address is 0xca2, then the address for the second +register would be 0xca6. This defaults to 1. + +The regsizes parameter gives the size of a register, in bytes. The +data used by IPMI is 8-bits wide, but it may be inside a larger +register. This parameter allows the read and write type to specified. +It may be 1, 2, 4, or 8. The default is 1. + +Since the register size may be larger than 32 bits, the IPMI data may not +be in the lower 8 bits. The regshifts parameter give the amount to shift +the data to get to the actual IPMI data. + When compiled into the kernel, the addresses can be specified on the kernel command line as: ipmi_si.type=,... ipmi_si.ports=,... ipmi_si.addrs=,... ipmi_si.irqs=,... ipmi_si.trydefaults=[0|1] + ipmi_si.regspacings=,,... + ipmi_si.regsizes=,,... + ipmi_si.regshifts=,,... It works the same as the module parameters of the same names. diff -puN drivers/char/ipmi/ipmi_bt_sm.c~ipmi-driver-updates drivers/char/ipmi/ipmi_bt_sm.c --- 25/drivers/char/ipmi/ipmi_bt_sm.c~ipmi-driver-updates 2004-08-15 13:47:44.653606088 -0700 +++ 25-akpm/drivers/char/ipmi/ipmi_bt_sm.c 2004-08-15 13:47:44.674602896 -0700 @@ -31,7 +31,7 @@ #include /* for completion codes */ #include "ipmi_si_sm.h" -#define IPMI_BT_VERSION "v32" +#define IPMI_BT_VERSION "v33" static int bt_debug = 0x00; /* Production value 0, see following flags */ diff -puN drivers/char/ipmi/ipmi_devintf.c~ipmi-driver-updates drivers/char/ipmi/ipmi_devintf.c --- 25/drivers/char/ipmi/ipmi_devintf.c~ipmi-driver-updates 2004-08-15 13:47:44.655605784 -0700 +++ 25-akpm/drivers/char/ipmi/ipmi_devintf.c 2004-08-15 13:47:44.675602744 -0700 @@ -45,7 +45,7 @@ #include #include -#define IPMI_DEVINTF_VERSION "v32" +#define IPMI_DEVINTF_VERSION "v33" struct ipmi_file_private { diff -puN drivers/char/ipmi/ipmi_kcs_sm.c~ipmi-driver-updates drivers/char/ipmi/ipmi_kcs_sm.c --- 25/drivers/char/ipmi/ipmi_kcs_sm.c~ipmi-driver-updates 2004-08-15 13:47:44.656605632 -0700 +++ 25-akpm/drivers/char/ipmi/ipmi_kcs_sm.c 2004-08-15 13:47:44.676602592 -0700 @@ -42,7 +42,7 @@ #include /* for completion codes */ #include "ipmi_si_sm.h" -#define IPMI_KCS_VERSION "v32" +#define IPMI_KCS_VERSION "v33" /* Set this if you want a printout of why the state machine was hosed when it gets hosed. */ diff -puN drivers/char/ipmi/ipmi_msghandler.c~ipmi-driver-updates drivers/char/ipmi/ipmi_msghandler.c --- 25/drivers/char/ipmi/ipmi_msghandler.c~ipmi-driver-updates 2004-08-15 13:47:44.658605328 -0700 +++ 25-akpm/drivers/char/ipmi/ipmi_msghandler.c 2004-08-15 13:47:44.678602288 -0700 @@ -46,7 +46,8 @@ #include #include -#define IPMI_MSGHANDLER_VERSION "v32" +#define PFX "IPMI message handler: " +#define IPMI_MSGHANDLER_VERSION "v33" struct ipmi_recv_msg *ipmi_alloc_recv_msg(void); static int ipmi_init_msghandler(void); @@ -895,6 +896,12 @@ int ipmi_unregister_for_cmd(ipmi_user_t return rv; } +void ipmi_user_set_run_to_completion(ipmi_user_t user, int val) +{ + user->intf->handlers->set_run_to_completion(user->intf->send_info, + val); +} + static unsigned char ipmb_checksum(unsigned char *data, int size) { @@ -1686,8 +1693,8 @@ channel_handler(ipmi_smi_t intf, struct intf->curr_channel = IPMI_MAX_CHANNELS; wake_up(&intf->waitq); - printk(KERN_WARNING "ipmi_msghandler: Error sending" - "channel information: %d\n", + printk(KERN_WARNING PFX + "Error sending channel information: %d\n", rv); } } @@ -2351,7 +2358,7 @@ static int handle_read_event_rsp(ipmi_sm } else { /* There's too many things in the queue, discard this message. */ - printk(KERN_WARNING "ipmi: Event queue full, discarding an" + printk(KERN_WARNING PFX "Event queue full, discarding an" " incoming event\n"); } @@ -2433,10 +2440,34 @@ static int handle_new_recv_msg(ipmi_smi_ #endif if (msg->rsp_size < 2) { /* Message is too small to be correct. */ - requeue = 0; - } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) - && (msg->rsp[1] == IPMI_SEND_MSG_CMD) - && (msg->user_data != NULL)) + printk(KERN_WARNING PFX "BMC returned to small a message" + " for netfn %x cmd %x, got %d bytes\n", + (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size); + + /* Generate an error response for the message. */ + msg->rsp[0] = msg->data[0] | (1 << 2); + msg->rsp[1] = msg->data[1]; + msg->rsp[2] = IPMI_ERR_UNSPECIFIED; + msg->rsp_size = 3; + } else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1))/* Netfn */ + || (msg->rsp[1] != msg->data[1])) /* Command */ + { + /* The response is not even marginally correct. */ + printk(KERN_WARNING PFX "BMC returned incorrect response," + " expected netfn %x cmd %x, got netfn %x cmd %x\n", + (msg->data[0] >> 2) | 1, msg->data[1], + msg->rsp[0] >> 2, msg->rsp[1]); + + /* Generate an error response for the message. */ + msg->rsp[0] = msg->data[0] | (1 << 2); + msg->rsp[1] = msg->data[1]; + msg->rsp[2] = IPMI_ERR_UNSPECIFIED; + msg->rsp_size = 3; + } + + if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) + && (msg->rsp[1] == IPMI_SEND_MSG_CMD) + && (msg->user_data != NULL)) { /* It's a response to a response we sent. For this we deliver a send message response to the user. */ @@ -2502,7 +2533,9 @@ static int handle_new_recv_msg(ipmi_smi_ requeue = 0; } - } else if (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD) { + } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) + && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) + { /* It's an asyncronous event. */ requeue = handle_read_event_rsp(intf, msg); } else { @@ -3114,7 +3147,7 @@ static int ipmi_init_msghandler(void) proc_ipmi_root = proc_mkdir("ipmi", NULL); if (!proc_ipmi_root) { - printk("Unable to create IPMI proc dir"); + printk(KERN_ERR PFX "Unable to create IPMI proc dir"); return -ENOMEM; } @@ -3166,11 +3199,11 @@ static __exit void cleanup_ipmi(void) /* Check for buffer leaks. */ count = atomic_read(&smi_msg_inuse_count); if (count != 0) - printk("ipmi_msghandler: SMI message count %d at exit\n", + printk(KERN_WARNING PFX "SMI message count %d at exit\n", count); count = atomic_read(&recv_msg_inuse_count); if (count != 0) - printk("ipmi_msghandler: recv message count %d at exit\n", + printk(KERN_WARNING PFX "recv message count %d at exit\n", count); } module_exit(cleanup_ipmi); @@ -3207,3 +3240,4 @@ EXPORT_SYMBOL(ipmi_get_my_address); EXPORT_SYMBOL(ipmi_set_my_LUN); EXPORT_SYMBOL(ipmi_get_my_LUN); EXPORT_SYMBOL(ipmi_smi_add_proc_entry); +EXPORT_SYMBOL(ipmi_user_set_run_to_completion); diff -puN /dev/null drivers/char/ipmi/ipmi_poweroff.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/char/ipmi/ipmi_poweroff.c 2004-08-15 13:47:44.681601832 -0700 @@ -0,0 +1,543 @@ +/* + * ipmi_poweroff.c + * + * MontaVista IPMI Poweroff extension to sys_reboot + * + * Author: MontaVista Software, Inc. + * Steven Dake + * Corey Minyard + * source@mvista.com + * + * Copyright 2002,2004 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include + +#define PFX "IPMI poweroff: " +#define IPMI_POWEROFF_VERSION "v33" + +/* Where to we insert our poweroff function? */ +extern void (*pm_power_off)(void); + +/* Stuff from the get device id command. */ +unsigned int mfg_id; +unsigned int prod_id; +unsigned char capabilities; + +/* We use our own messages for this operation, we don't let the system + allocate them, since we may be in a panic situation. The whole + thing is single-threaded, anyway, so multiple messages are not + required. */ +static void dummy_smi_free(struct ipmi_smi_msg *msg) +{ +} +static void dummy_recv_free(struct ipmi_recv_msg *msg) +{ +} +static struct ipmi_smi_msg halt_smi_msg = +{ + .done = dummy_smi_free +}; +static struct ipmi_recv_msg halt_recv_msg = +{ + .done = dummy_recv_free +}; + + +/* + * Code to send a message and wait for the reponse. + */ + +static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) +{ + struct semaphore *sem = recv_msg->user_msg_data; + + if (sem) + up(sem); +} + +static struct ipmi_user_hndl ipmi_poweroff_handler = +{ + ipmi_recv_hndl : receive_handler +}; + + +static int ipmi_request_wait_for_response(ipmi_user_t user, + struct ipmi_addr *addr, + struct kernel_ipmi_msg *send_msg) +{ + int rv; + struct semaphore sem; + + sema_init (&sem, 0); + + rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &sem, + &halt_smi_msg, &halt_recv_msg, 0); + if (rv) + return rv; + + down (&sem); + + return halt_recv_msg.msg.data[0]; +} + +/* We are in run-to-completion mode, no semaphore is desired. */ +static int ipmi_request_in_rc_mode(ipmi_user_t user, + struct ipmi_addr *addr, + struct kernel_ipmi_msg *send_msg) +{ + int rv; + + rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL, + &halt_smi_msg, &halt_recv_msg, 0); + if (rv) + return rv; + + return halt_recv_msg.msg.data[0]; +} + +/* + * ATCA Support + */ + +#define IPMI_NETFN_ATCA 0x2c +#define IPMI_ATCA_SET_POWER_CMD 0x11 +#define IPMI_ATCA_GET_ADDR_INFO_CMD 0x01 +#define IPMI_PICMG_ID 0 + +static int ipmi_atca_detect (ipmi_user_t user) +{ + struct ipmi_system_interface_addr smi_addr; + struct kernel_ipmi_msg send_msg; + int rv; + unsigned char data[1]; + + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; + + /* + * Use get address info to check and see if we are ATCA + */ + send_msg.netfn = IPMI_NETFN_ATCA; + send_msg.cmd = IPMI_ATCA_GET_ADDR_INFO_CMD; + data[0] = IPMI_PICMG_ID; + send_msg.data = data; + send_msg.data_len = sizeof(data); + rv = ipmi_request_wait_for_response(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + return !rv; +} + +static void ipmi_poweroff_atca (ipmi_user_t user) +{ + struct ipmi_system_interface_addr smi_addr; + struct kernel_ipmi_msg send_msg; + int rv; + unsigned char data[4]; + + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; + + printk(KERN_INFO PFX "Powering down via ATCA power command\n"); + + /* + * Power down + */ + send_msg.netfn = IPMI_NETFN_ATCA; + send_msg.cmd = IPMI_ATCA_SET_POWER_CMD; + data[0] = IPMI_PICMG_ID; + data[1] = 0; /* FRU id */ + data[2] = 0; /* Power Level */ + data[3] = 0; /* Don't change saved presets */ + send_msg.data = data; + send_msg.data_len = sizeof (data); + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) { + printk(KERN_ERR PFX "Unable to send ATCA powerdown message," + " IPMI error 0x%x\n", rv); + goto out; + } + + out: + return; +} + +/* + * CPI1 Support + */ + +#define IPMI_NETFN_OEM_1 0xf8 +#define OEM_GRP_CMD_SET_RESET_STATE 0x84 +#define OEM_GRP_CMD_SET_POWER_STATE 0x82 +#define IPMI_NETFN_OEM_8 0xf8 +#define OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL 0x80 +#define OEM_GRP_CMD_GET_SLOT_GA 0xa3 +#define IPMI_NETFN_SENSOR_EVT 0x10 +#define IPMI_CMD_GET_EVENT_RECEIVER 0x01 + +#define IPMI_CPI1_PRODUCT_ID 0x000157 +#define IPMI_CPI1_MANUFACTURER_ID 0x0108 + +static int ipmi_cpi1_detect (ipmi_user_t user) +{ + return ((mfg_id == IPMI_CPI1_MANUFACTURER_ID) + && (prod_id == IPMI_CPI1_PRODUCT_ID)); +} + +static void ipmi_poweroff_cpi1 (ipmi_user_t user) +{ + struct ipmi_system_interface_addr smi_addr; + struct ipmi_ipmb_addr ipmb_addr; + struct kernel_ipmi_msg send_msg; + int rv; + unsigned char data[1]; + int slot; + unsigned char hotswap_ipmb; + unsigned char aer_addr; + unsigned char aer_lun; + + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; + + printk(KERN_INFO PFX "Powering down via CPI1 power command\n"); + + /* + * Get IPMI ipmb address + */ + send_msg.netfn = IPMI_NETFN_OEM_8 >> 2; + send_msg.cmd = OEM_GRP_CMD_GET_SLOT_GA; + send_msg.data = NULL; + send_msg.data_len = 0; + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) + goto out; + slot = halt_recv_msg.msg.data[1]; + hotswap_ipmb = (slot > 9) ? (0xb0 + 2 * slot) : (0xae + 2 * slot); + + /* + * Get active event receiver + */ + send_msg.netfn = IPMI_NETFN_SENSOR_EVT >> 2; + send_msg.cmd = IPMI_CMD_GET_EVENT_RECEIVER; + send_msg.data = NULL; + send_msg.data_len = 0; + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) + goto out; + aer_addr = halt_recv_msg.msg.data[1]; + aer_lun = halt_recv_msg.msg.data[2]; + + /* + * Setup IPMB address target instead of local target + */ + ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE; + ipmb_addr.channel = 0; + ipmb_addr.slave_addr = aer_addr; + ipmb_addr.lun = aer_lun; + + /* + * Send request hotswap control to remove blade from dpv + */ + send_msg.netfn = IPMI_NETFN_OEM_8 >> 2; + send_msg.cmd = OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL; + send_msg.data = &hotswap_ipmb; + send_msg.data_len = 1; + ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &ipmb_addr, + &send_msg); + + /* + * Set reset asserted + */ + send_msg.netfn = IPMI_NETFN_OEM_1 >> 2; + send_msg.cmd = OEM_GRP_CMD_SET_RESET_STATE; + send_msg.data = data; + data[0] = 1; /* Reset asserted state */ + send_msg.data_len = 1; + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) + goto out; + + /* + * Power down + */ + send_msg.netfn = IPMI_NETFN_OEM_1 >> 2; + send_msg.cmd = OEM_GRP_CMD_SET_POWER_STATE; + send_msg.data = data; + data[0] = 1; /* Power down state */ + send_msg.data_len = 1; + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) + goto out; + + out: + return; +} + +/* + * Standard chassis support + */ + +#define IPMI_NETFN_CHASSIS_REQUEST 0 +#define IPMI_CHASSIS_CONTROL_CMD 0x02 + +static int ipmi_chassis_detect (ipmi_user_t user) +{ + /* Chassis support, use it. */ + return (capabilities & 0x80); +} + +static void ipmi_poweroff_chassis (ipmi_user_t user) +{ + struct ipmi_system_interface_addr smi_addr; + struct kernel_ipmi_msg send_msg; + int rv; + unsigned char data[1]; + + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; + + printk(KERN_INFO PFX "Powering down via IPMI chassis control command\n"); + + /* + * Power down + */ + send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; + send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; + data[0] = 0; /* Power down */ + send_msg.data = data; + send_msg.data_len = sizeof(data); + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) { + printk(KERN_ERR PFX "Unable to send chassis powerdown message," + " IPMI error 0x%x\n", rv); + goto out; + } + + out: + return; +} + + +/* Table of possible power off functions. */ +struct poweroff_function { + char *platform_type; + int (*detect)(ipmi_user_t user); + void (*poweroff_func)(ipmi_user_t user); +}; + +static struct poweroff_function poweroff_functions[] = { + { "ATCA", ipmi_atca_detect, ipmi_poweroff_atca }, + { "CPI1", ipmi_cpi1_detect, ipmi_poweroff_cpi1 }, + /* Chassis should generally be last, other things should override + it. */ + { "chassis", ipmi_chassis_detect, ipmi_poweroff_chassis }, +}; +#define NUM_PO_FUNCS (sizeof(poweroff_functions) \ + / sizeof(struct poweroff_function)) + + +/* Our local state. */ +static int ready = 0; +static ipmi_user_t ipmi_user; +static void (*specific_poweroff_func)(ipmi_user_t user) = NULL; + +/* Holds the old poweroff function so we can restore it on removal. */ +static void (*old_poweroff_func)(void); + + +/* Called on a powerdown request. */ +static void ipmi_poweroff_function (void) +{ + if (!ready) + return; + + /* Use run-to-completion mode, since interrupts may be off. */ + ipmi_user_set_run_to_completion(ipmi_user, 1); + specific_poweroff_func(ipmi_user); + ipmi_user_set_run_to_completion(ipmi_user, 0); +} + +/* Wait for an IPMI interface to be installed, the first one installed + will be grabbed by this code and used to perform the powerdown. */ +static void ipmi_po_new_smi(int if_num) +{ + struct ipmi_system_interface_addr smi_addr; + struct kernel_ipmi_msg send_msg; + int rv; + int i; + + if (ready) + return; + + rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, 0, &ipmi_user); + if (rv) { + printk(KERN_ERR PFX "could not create IPMI user, error %d\n", + rv); + return; + } + + /* + * Do a get device ide and store some results, since this is + * used by several functions. + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; + + send_msg.netfn = IPMI_NETFN_APP_REQUEST; + send_msg.cmd = IPMI_GET_DEVICE_ID_CMD; + send_msg.data = NULL; + send_msg.data_len = 0; + rv = ipmi_request_wait_for_response(ipmi_user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) { + printk(KERN_ERR PFX "Unable to send IPMI get device id info," + " IPMI error 0x%x\n", rv); + goto out_err; + } + + if (halt_recv_msg.msg.data_len < 12) { + printk(KERN_ERR PFX "(chassis) IPMI get device id info too," + " short, was %d bytes, needed %d bytes\n", + halt_recv_msg.msg.data_len, 12); + goto out_err; + } + + mfg_id = (halt_recv_msg.msg.data[7] + | (halt_recv_msg.msg.data[8] << 8) + | (halt_recv_msg.msg.data[9] << 16)); + prod_id = (halt_recv_msg.msg.data[10] + | (halt_recv_msg.msg.data[11] << 8)); + capabilities = halt_recv_msg.msg.data[6]; + + + /* Scan for a poweroff method */ + for (i=0; i -#define IPMI_SI_VERSION "v32" +#define IPMI_SI_VERSION "v33" /* Measure times between events in the driver. */ #undef DEBUG_TIMING @@ -146,6 +146,11 @@ struct smi_info /* The I/O port of an SI interface. */ int port; + /* The space between start addresses of the two ports. For + instance, if the first port is 0xca2 and the spacing is 4, then + the second port is 0xca6. */ + unsigned int spacing; + /* zero if no irq; */ int irq; @@ -452,14 +457,20 @@ static void handle_transaction_done(stru /* Take off the event flag. */ smi_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; + handle_flags(smi_info); } else { spin_lock(&smi_info->count_lock); smi_info->events++; spin_unlock(&smi_info->count_lock); + /* Do this before we deliver the message + because delivering the message releases the + lock and something else can mess with the + state. */ + handle_flags(smi_info); + deliver_recv_msg(smi_info, msg); } - handle_flags(smi_info); break; } @@ -482,14 +493,20 @@ static void handle_transaction_done(stru /* Take off the msg flag. */ smi_info->msg_flags &= ~RECEIVE_MSG_AVAIL; + handle_flags(smi_info); } else { spin_lock(&smi_info->count_lock); smi_info->incoming_messages++; spin_unlock(&smi_info->count_lock); + /* Do this before we deliver the message + because delivering the message releases the + lock and something else can mess with the + state. */ + handle_flags(smi_info); + deliver_recv_msg(smi_info, msg); } - handle_flags(smi_info); break; } @@ -568,6 +585,9 @@ static enum si_sm_result smi_event_handl smi_info->hosed_count++; spin_unlock(&smi_info->count_lock); + /* Do the before return_hosed_msg, because that + releases the lock. */ + smi_info->si_state = SI_NORMAL; if (smi_info->curr_msg != NULL) { /* If we were handling a user message, format a response to send to the upper layer to @@ -575,7 +595,6 @@ static enum si_sm_result smi_event_handl return_hosed_msg(smi_info); } si_sm_result = smi_info->handlers->event(smi_info->si_sm, 0); - smi_info->si_state = SI_NORMAL; } /* We prefer handling attn over new messages. */ @@ -872,9 +891,10 @@ static struct smi_info *smi_infos[SI_MAX #define DEVICE_NAME "ipmi_si" -#define DEFAULT_KCS_IO_PORT 0xca2 -#define DEFAULT_SMIC_IO_PORT 0xca9 -#define DEFAULT_BT_IO_PORT 0xe4 +#define DEFAULT_KCS_IO_PORT 0xca2 +#define DEFAULT_SMIC_IO_PORT 0xca9 +#define DEFAULT_BT_IO_PORT 0xe4 +#define DEFAULT_REGSPACING 1 static int si_trydefaults = 1; static char *si_type[SI_MAX_PARMS] = { NULL, NULL, NULL, NULL }; @@ -886,6 +906,12 @@ static unsigned int ports[SI_MAX_PARMS] static int num_ports = 0; static int irqs[SI_MAX_PARMS] = { 0, 0, 0, 0 }; static int num_irqs = 0; +static int regspacings[SI_MAX_PARMS] = { 0, 0, 0, 0 }; +static int num_regspacings = 0; +static int regsizes[SI_MAX_PARMS] = { 0, 0, 0, 0 }; +static int num_regsizes = 0; +static int regshifts[SI_MAX_PARMS] = { 0, 0, 0, 0 }; +static int num_regshifts = 0; module_param_named(trydefaults, si_trydefaults, bool, 0); @@ -912,6 +938,23 @@ MODULE_PARM_DESC(irqs, "Sets the interru " addresses separated by commas. Only use if an interface" " has an interrupt. Otherwise, set it to zero or leave" " it blank."); +module_param_array(regspacings, int, num_regspacings, 0); +MODULE_PARM_DESC(regspacings, "The number of bytes between the start address" + " and each successive register used by the interface. For" + " instance, if the start address is 0xca2 and the spacing" + " is 2, then the second address is at 0xca4. Defaults" + " to 1."); +module_param_array(regsizes, int, num_regsizes, 0); +MODULE_PARM_DESC(regsizes, "The size of the specific IPMI register in bytes." + " This should generally be 1, 2, 4, or 8 for an 8-bit," + " 16-bit, 32-bit, or 64-bit register. Use this if you" + " the 8-bit IPMI register has to be read from a larger" + " register."); +module_param_array(regshifts, int, num_regshifts, 0); +MODULE_PARM_DESC(regshifts, "The amount to shift the data read from the." + " IPMI register, in bits. For instance, if the data" + " is read from a 32-bit word and the IPMI data is in" + " bit 8-15, then the shift would be 8"); #define IPMI_MEM_ADDR_SPACE 1 #define IPMI_IO_ADDR_SPACE 2 @@ -977,7 +1020,7 @@ static unsigned char port_inb(struct si_ { unsigned int *addr = io->info; - return inb((*addr)+offset); + return inb((*addr)+(offset*io->regspacing)); } static void port_outb(struct si_sm_io *io, unsigned int offset, @@ -985,30 +1028,97 @@ static void port_outb(struct si_sm_io *i { unsigned int *addr = io->info; - outb(b, (*addr)+offset); + outb(b, (*addr)+(offset * io->regspacing)); } -static int port_setup(struct smi_info *info) +static unsigned char port_inw(struct si_sm_io *io, unsigned int offset) { - unsigned int *addr = info->io.info; + unsigned int *addr = io->info; - if (!addr || (!*addr)) - return -ENODEV; + return (inw((*addr)+(offset * io->regspacing)) >> io->regshift) & 0xff; +} - if (request_region(*addr, info->io_size, DEVICE_NAME) == NULL) - return -EIO; - return 0; +static void port_outw(struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + unsigned int *addr = io->info; + + outw(b << io->regshift, (*addr)+(offset * io->regspacing)); +} + +static unsigned char port_inl(struct si_sm_io *io, unsigned int offset) +{ + unsigned int *addr = io->info; + + return (inl((*addr)+(offset * io->regspacing)) >> io->regshift) & 0xff; +} + +static void port_outl(struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + unsigned int *addr = io->info; + + outl(b << io->regshift, (*addr)+(offset * io->regspacing)); } static void port_cleanup(struct smi_info *info) { unsigned int *addr = info->io.info; + int mapsize; - if (addr && (*addr)) - release_region (*addr, info->io_size); + if (addr && (*addr)) { + mapsize = ((info->io_size * info->io.regspacing) + - (info->io.regspacing - info->io.regsize)); + + release_region (*addr, mapsize); + } kfree(info); } +static int port_setup(struct smi_info *info) +{ + unsigned int *addr = info->io.info; + int mapsize; + + if (!addr || (!*addr)) + return -ENODEV; + + info->io_cleanup = port_cleanup; + + /* Figure out the actual inb/inw/inl/etc routine to use based + upon the register size. */ + switch (info->io.regsize) { + case 1: + info->io.inputb = port_inb; + info->io.outputb = port_outb; + break; + case 2: + info->io.inputb = port_inw; + info->io.outputb = port_outw; + break; + case 4: + info->io.inputb = port_inl; + info->io.outputb = port_outl; + break; + default: + printk("ipmi_si: Invalid register size: %d\n", + info->io.regsize); + return -EINVAL; + } + + /* Calculate the total amount of memory to claim. This is an + * unusual looking calculation, but it avoids claiming any + * more memory than it has to. It will claim everything + * between the first address to the end of the last full + * register. */ + mapsize = ((info->io_size * info->io.regspacing) + - (info->io.regspacing - info->io.regsize)); + + if (request_region(*addr, mapsize, DEVICE_NAME) == NULL) + return -EIO; + return 0; +} + static int try_init_port(int intf_num, struct smi_info **new_info) { struct smi_info *info; @@ -1028,11 +1138,15 @@ static int try_init_port(int intf_num, s memset(info, 0, sizeof(*info)); info->io_setup = port_setup; - info->io_cleanup = port_cleanup; - info->io.inputb = port_inb; - info->io.outputb = port_outb; info->io.info = &(ports[intf_num]); info->io.addr = NULL; + info->io.regspacing = regspacings[intf_num]; + if (!info->io.regspacing) + info->io.regspacing = DEFAULT_REGSPACING; + info->io.regsize = regsizes[intf_num]; + if (!info->io.regsize) + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = regshifts[intf_num]; info->irq = 0; info->irq_setup = NULL; *new_info = info; @@ -1047,44 +1161,125 @@ static int try_init_port(int intf_num, s static unsigned char mem_inb(struct si_sm_io *io, unsigned int offset) { - return readb((io->addr)+offset); + return readb((io->addr)+(offset * io->regspacing)); } static void mem_outb(struct si_sm_io *io, unsigned int offset, unsigned char b) { - writeb(b, (io->addr)+offset); + writeb(b, (io->addr)+(offset * io->regspacing)); } -static int mem_setup(struct smi_info *info) +static unsigned char mem_inw(struct si_sm_io *io, unsigned int offset) { - unsigned long *addr = info->io.info; + return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift) + && 0xff; +} - if (!addr || (!*addr)) - return -ENODEV; +static void mem_outw(struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + writeb(b << io->regshift, (io->addr)+(offset * io->regspacing)); +} - if (request_mem_region(*addr, info->io_size, DEVICE_NAME) == NULL) - return -EIO; +static unsigned char mem_inl(struct si_sm_io *io, unsigned int offset) +{ + return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift) + && 0xff; +} - info->io.addr = ioremap(*addr, info->io_size); - if (info->io.addr == NULL) { - release_mem_region(*addr, info->io_size); - return -EIO; - } - return 0; +static void mem_outl(struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + writel(b << io->regshift, (io->addr)+(offset * io->regspacing)); } +#ifdef readq +static unsigned char mem_inq(struct si_sm_io *io, unsigned int offset) +{ + return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift) + && 0xff; +} + +static void mem_outq(struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + writeq(b << io->regshift, (io->addr)+(offset * io->regspacing)); +} +#endif + static void mem_cleanup(struct smi_info *info) { unsigned long *addr = info->io.info; + int mapsize; if (info->io.addr) { iounmap(info->io.addr); - release_mem_region(*addr, info->io_size); + + mapsize = ((info->io_size * info->io.regspacing) + - (info->io.regspacing - info->io.regsize)); + + release_mem_region(*addr, mapsize); } kfree(info); } +static int mem_setup(struct smi_info *info) +{ + unsigned long *addr = info->io.info; + int mapsize; + + if (!addr || (!*addr)) + return -ENODEV; + + info->io_cleanup = mem_cleanup; + + /* Figure out the actual readb/readw/readl/etc routine to use based + upon the register size. */ + switch (info->io.regsize) { + case 1: + info->io.inputb = mem_inb; + info->io.outputb = mem_outb; + break; + case 2: + info->io.inputb = mem_inw; + info->io.outputb = mem_outw; + break; + case 4: + info->io.inputb = mem_inl; + info->io.outputb = mem_outl; + break; +#ifdef readq + case 4: + info->io.inputb = mem_inq; + info->io.outputb = mem_outq; + break; +#endif + default: + printk("ipmi_si: Invalid register size: %d\n", + info->io.regsize); + return -EINVAL; + } + + /* Calculate the total amount of memory to claim. This is an + * unusual looking calculation, but it avoids claiming any + * more memory than it has to. It will claim everything + * between the first address to the end of the last full + * register. */ + mapsize = ((info->io_size * info->io.regspacing) + - (info->io.regspacing - info->io.regsize)); + + if (request_mem_region(*addr, mapsize, DEVICE_NAME) == NULL) + return -EIO; + + info->io.addr = ioremap(*addr, mapsize); + if (info->io.addr == NULL) { + release_mem_region(*addr, mapsize); + return -EIO; + } + return 0; +} + static int try_init_mem(int intf_num, struct smi_info **new_info) { struct smi_info *info; @@ -1104,11 +1299,15 @@ static int try_init_mem(int intf_num, st memset(info, 0, sizeof(*info)); info->io_setup = mem_setup; - info->io_cleanup = mem_cleanup; - info->io.inputb = mem_inb; - info->io.outputb = mem_outb; info->io.info = (void *) addrs[intf_num]; info->io.addr = NULL; + info->io.regspacing = regspacings[intf_num]; + if (!info->io.regspacing) + info->io.regspacing = DEFAULT_REGSPACING; + info->io.regsize = regsizes[intf_num]; + if (!info->io.regsize) + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = regshifts[intf_num]; info->irq = 0; info->irq_setup = NULL; *new_info = info; @@ -1132,7 +1331,7 @@ static int try_init_mem(int intf_num, st static int acpi_failure = 0; /* For GPE-type interrupts. */ -void ipmi_acpi_gpe(void *context) +u32 ipmi_acpi_gpe(void *context) { struct smi_info *smi_info = context; unsigned long flags; @@ -1156,6 +1355,8 @@ void ipmi_acpi_gpe(void *context) smi_event_handler(smi_info, 0); out: spin_unlock_irqrestore(&(smi_info->si_lock), flags); + + return ACPI_INTERRUPT_HANDLED; } static int acpi_gpe_irq_setup(struct smi_info *info) @@ -1182,7 +1383,6 @@ static int acpi_gpe_irq_setup(struct smi printk(" Using ACPI GPE %d\n", info->irq); return 0; } - } static void acpi_gpe_irq_cleanup(struct smi_info *info) @@ -1310,21 +1510,22 @@ static int try_init_acpi(int intf_num, s info->irq_setup = NULL; } + regspacings[intf_num] = spmi->addr.register_bit_width / 8; + info->io.regspacing = spmi->addr.register_bit_width / 8; + regsizes[intf_num] = regspacings[intf_num]; + info->io.regsize = regsizes[intf_num]; + regshifts[intf_num] = spmi->addr.register_bit_offset; + info->io.regshift = regshifts[intf_num]; + if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { io_type = "memory"; info->io_setup = mem_setup; - info->io_cleanup = mem_cleanup; addrs[intf_num] = spmi->addr.address; - info->io.inputb = mem_inb; - info->io.outputb = mem_outb; info->io.info = &(addrs[intf_num]); } else if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_IO) { io_type = "I/O"; info->io_setup = port_setup; - info->io_cleanup = port_cleanup; ports[intf_num] = spmi->addr.address; - info->io.inputb = port_inb; - info->io.outputb = port_outb; info->io.info = &(ports[intf_num]); } else { kfree(info); @@ -1348,6 +1549,7 @@ typedef struct dmi_ipmi_data u8 addr_space; unsigned long base_addr; u8 irq; + u8 offset; }dmi_ipmi_data_t; typedef struct dmi_header @@ -1361,6 +1563,7 @@ static int decode_dmi(dmi_header_t *dm, { u8 *data = (u8 *)dm; unsigned long base_addr; + u8 reg_spacing; ipmi_data->type = data[0x04]; @@ -1375,7 +1578,27 @@ static int decode_dmi(dmi_header_t *dm, ipmi_data->addr_space = IPMI_MEM_ADDR_SPACE; } - ipmi_data->base_addr = base_addr; + /* The top two bits of byte 0x10 hold the register spacing. */ + reg_spacing = (data[0x10] & 0xC0) >> 6; + switch(reg_spacing){ + case 0x00: /* Byte boundaries */ + ipmi_data->offset = 1; + break; + case 0x01: /* 32-bit boundaries */ + ipmi_data->offset = 4; + break; + case 0x02: /* 16-bit boundaries */ + ipmi_data->offset = 2; + default: + printk("ipmi_si: Unknown SMBIOS IPMI Base Addr" + " Modifier: 0x%x\n", reg_spacing); + return -EIO; + } + + /* If bit 4 of byte 0x10 is set, then the lsb for the address + is odd. */ + ipmi_data->base_addr = base_addr | ((data[0x10] & 0x10) >> 4); + ipmi_data->irq = data[0x11]; if (is_new_interface(-1, ipmi_data->addr_space,ipmi_data->base_addr)) @@ -1500,18 +1723,12 @@ static int try_init_smbios(int intf_num, if (ipmi_data.addr_space == 1) { io_type = "memory"; info->io_setup = mem_setup; - info->io_cleanup = mem_cleanup; addrs[intf_num] = ipmi_data.base_addr; - info->io.inputb = mem_inb; - info->io.outputb = mem_outb; info->io.info = &(addrs[intf_num]); } else if (ipmi_data.addr_space == 2) { io_type = "I/O"; info->io_setup = port_setup; - info->io_cleanup = port_cleanup; ports[intf_num] = ipmi_data.base_addr; - info->io.inputb = port_inb; - info->io.outputb = port_outb; info->io.info = &(ports[intf_num]); } else { kfree(info); @@ -1519,6 +1736,13 @@ static int try_init_smbios(int intf_num, return -EIO; } + regspacings[intf_num] = ipmi_data.offset; + info->io.regspacing = regspacings[intf_num]; + if (!info->io.regspacing) + info->io.regspacing = DEFAULT_REGSPACING; + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = regshifts[intf_num]; + irqs[intf_num] = ipmi_data.irq; *new_info = info; @@ -1596,11 +1820,13 @@ static int find_pci_smic(int intf_num, s memset(info, 0, sizeof(*info)); info->io_setup = port_setup; - info->io_cleanup = port_cleanup; ports[intf_num] = base_addr; - info->io.inputb = port_inb; - info->io.outputb = port_outb; info->io.info = &(ports[intf_num]); + info->io.regspacing = regspacings[intf_num]; + if (!info->io.regspacing) + info->io.regspacing = DEFAULT_REGSPACING; + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = regshifts[intf_num]; *new_info = info; @@ -1860,6 +2086,13 @@ static int init_one_smi(int intf_num, st new_smi->timer_stopped = 0; new_smi->stop_operation = 0; + /* Start clearing the flags before we enable interrupts or the + timer to avoid racing with the timer. */ + start_clear_flags(new_smi); + /* IRQ is defined to be set when non-zero. */ + if (new_smi->irq) + new_smi->si_state = SI_CLEARING_FLAGS_THEN_SET_IRQ; + /* The ipmi_register_smi() code does some operations to determine the channel information, so we must be ready to handle operations before it is called. This means we have @@ -1903,12 +2136,6 @@ static int init_one_smi(int intf_num, st goto out_err_stop_timer; } - start_clear_flags(new_smi); - - /* IRQ is defined to be set when non-zero. */ - if (new_smi->irq) - new_smi->si_state = SI_CLEARING_FLAGS_THEN_SET_IRQ; - *smi = new_smi; printk(" IPMI %s interface initialized\n", si_type[intf_num]); @@ -2052,6 +2279,13 @@ void __exit cleanup_one_si(struct smi_in schedule_timeout(1); } + /* Interrupts and timeouts are stopped, now make sure the + interface is in a clean state. */ + while ((to_clean->curr_msg) || (to_clean->si_state != SI_NORMAL)) { + poll(to_clean); + schedule_timeout(1); + } + rv = ipmi_unregister_smi(to_clean->intf); if (rv) { printk(KERN_ERR diff -puN drivers/char/ipmi/ipmi_si_sm.h~ipmi-driver-updates drivers/char/ipmi/ipmi_si_sm.h --- 25/drivers/char/ipmi/ipmi_si_sm.h~ipmi-driver-updates 2004-08-15 13:47:44.662604720 -0700 +++ 25-akpm/drivers/char/ipmi/ipmi_si_sm.h 2004-08-15 13:47:44.686601072 -0700 @@ -52,6 +52,9 @@ struct si_sm_io state machine shouldn't touch these. */ void *info; void *addr; + int regspacing; + int regsize; + int regshift; }; /* Results of SMI events. */ diff -puN drivers/char/ipmi/ipmi_smic_sm.c~ipmi-driver-updates drivers/char/ipmi/ipmi_smic_sm.c --- 25/drivers/char/ipmi/ipmi_smic_sm.c~ipmi-driver-updates 2004-08-15 13:47:44.663604568 -0700 +++ 25-akpm/drivers/char/ipmi/ipmi_smic_sm.c 2004-08-15 13:47:44.686601072 -0700 @@ -46,7 +46,7 @@ #include /* for completion codes */ #include "ipmi_si_sm.h" -#define IPMI_SMIC_VERSION "v32" +#define IPMI_SMIC_VERSION "v33" /* smic_debug is a bit-field * SMIC_DEBUG_ENABLE - turned on for now diff -puN drivers/char/ipmi/ipmi_watchdog.c~ipmi-driver-updates drivers/char/ipmi/ipmi_watchdog.c --- 25/drivers/char/ipmi/ipmi_watchdog.c~ipmi-driver-updates 2004-08-15 13:47:44.665604264 -0700 +++ 25-akpm/drivers/char/ipmi/ipmi_watchdog.c 2004-08-15 13:47:44.687600920 -0700 @@ -53,7 +53,7 @@ #define PFX "IPMI Watchdog: " -#define IPMI_WATCHDOG_VERSION "v32" +#define IPMI_WATCHDOG_VERSION "v33" /* * The IPMI command/response information for the watchdog timer. diff -puN drivers/char/ipmi/Kconfig~ipmi-driver-updates drivers/char/ipmi/Kconfig --- 25/drivers/char/ipmi/Kconfig~ipmi-driver-updates 2004-08-15 13:47:44.667603960 -0700 +++ 25-akpm/drivers/char/ipmi/Kconfig 2004-08-15 13:47:44.688600768 -0700 @@ -57,4 +57,11 @@ config IPMI_WATCHDOG help This enables the IPMI watchdog timer. +config IPMI_POWEROFF + tristate 'IPMI Poweroff' + depends on IPMI_HANDLER + help + This enables a function to power off the system with IPMI if + the IPMI management controller is capable of this. + endmenu diff -puN drivers/char/ipmi/Makefile~ipmi-driver-updates drivers/char/ipmi/Makefile --- 25/drivers/char/ipmi/Makefile~ipmi-driver-updates 2004-08-15 13:47:44.668603808 -0700 +++ 25-akpm/drivers/char/ipmi/Makefile 2004-08-15 13:47:44.688600768 -0700 @@ -8,6 +8,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi_msgha obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o obj-$(CONFIG_IPMI_SI) += ipmi_si.o obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o +obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o ipmi_si.o: $(ipmi_si-objs) $(LD) -r -o $@ $(ipmi_si-objs) diff -puN include/linux/ipmi.h~ipmi-driver-updates include/linux/ipmi.h --- 25/include/linux/ipmi.h~ipmi-driver-updates 2004-08-15 13:47:44.670603504 -0700 +++ 25-akpm/include/linux/ipmi.h 2004-08-15 13:47:44.689600616 -0700 @@ -406,6 +406,12 @@ int ipmi_unregister_for_cmd(ipmi_user_t unsigned char cmd); /* + * Allow run-to-completion mode to be set for the interface of + * a specific user. + */ +void ipmi_user_set_run_to_completion(ipmi_user_t user, int val); + +/* * When the user is created, it will not receive IPMI events by * default. The user must set this to TRUE to get incoming events. * The first user that sets this to TRUE will receive all events that _