/*
* sensor.c
*
* MontaVista IPMI code for handling sensors
*
* Author: MontaVista Software, Inc.
* Corey Minyard <minyard@mvista.com>
* source@mvista.com
*
* Copyright 2004 MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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 Lesser General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <OpenIPMI/ipmiif.h>
#include <OpenIPMI/ipmi_err.h>
#include <OpenIPMI/internal/ipmi_event.h>
#include <OpenIPMI/internal/ipmi_domain.h>
#include <OpenIPMI/internal/ipmi_mc.h>
#include <OpenIPMI/internal/ipmi_oem.h>
#include <OpenIPMI/internal/ipmi_sensor.h>
#include <OpenIPMI/internal/ipmi_control.h>
#include <OpenIPMI/internal/ipmi_int.h>
/* We use a block of MontaVista's private enterprise IDs for our own
purposes. */
#define MONTAVISTA_MFG_ID 4753
#define MONTAVISTA_TEST_START 0xf00
#define MONTAVISTA_TEST_END 0xfff
/* Control numbers. */
#define POWER_CONTROL(ipmb) ((ipmb) >> 1)
#define HS_LED_CONTROL(ipmb) (((ipmb) >> 1) | 0x80)
static int
dummy_entity_sdr_add(ipmi_entity_t *ent,
ipmi_sdr_info_t *sdrs,
void *cb_data)
{
/* Don't put the entities into an SDR */
return 0;
}
/***********************************************************************
*
* Sensor handling.
*
**********************************************************************/
static int
test_sensor_handler_0(ipmi_mc_t *mc,
ipmi_entity_t *ent,
ipmi_sensor_t *sensor,
void *link,
void *cb_data)
{
int lun, num;
int rv;
rv = ipmi_sensor_get_num(sensor, &lun, &num);
if (rv)
return rv;
/* This is the slot sensor. Set the hot-swap requester bit. */
if ((lun == 0) && (num == 1)) {
ipmi_sensor_set_hot_swap_requester(sensor, 6, 1);
}
return 0;
}
/***********************************************************************
*
* Event handling.
*
**********************************************************************/
typedef struct event_info_s
{
int err;
ipmi_event_t *event;
int valid_vals[1];
int vals[1];
int handled;
} event_info_t;
void
event_control_cb(ipmi_control_t *control, void *cb_data)
{
event_info_t *info = cb_data;
ipmi_control_call_val_event_handlers(control,
info->valid_vals,
info->vals,
&info->event,
&info->handled);
if (info->handled == IPMI_EVENT_NOT_HANDLED)
info->err = EINVAL;
}
static int
test_event_handler_0(ipmi_mc_t *mc,
ipmi_event_t *event,
void *cb_data)
{
unsigned char data[13];
ipmi_domain_t *domain = ipmi_mc_get_domain(mc);
ipmi_mc_t *src_mc;
ipmi_ipmb_addr_t addr;
if (ipmi_event_get_type(event) == 0xc0) {
ipmi_control_id_t id;
event_info_t info;
int rv;
ipmi_time_t timestamp;
if (ipmi_event_get_data(event, data, 0, 13) != 13)
return 0;
timestamp = ipmi_get_uint32(&data[0]);
if (timestamp < ipmi_mc_get_startup_SEL_time(mc))
/* It's an old event, ignore it. */
return 0;
if (data[6] != 1)
/* Wrong version */
return 0;
addr.addr_type = IPMI_IPMB_ADDR_TYPE;
addr.channel = 0;
addr.lun = 0;
addr.slave_addr = data[4];
/* Find the MC. */
src_mc = i_ipmi_find_mc_by_addr(domain,
(ipmi_addr_t *) &addr,
sizeof(addr));
if (!src_mc)
return 0;
id.mcid = ipmi_mc_convert_to_id(src_mc);
id.lun = 4;
id.control_num = POWER_CONTROL(data[8]);
info.err = 0;
info.event = event;
info.valid_vals[0] = 1;
info.vals[0] = data[10];
info.handled = IPMI_EVENT_NOT_HANDLED;
rv = ipmi_control_pointer_cb(id, event_control_cb, &info);
if (!rv)
rv = info.err;
i_ipmi_mc_put(src_mc);
if (!rv)
return 1;
}
return 0;
}
/***********************************************************************
*
* The hot-swap led control.
*
**********************************************************************/
typedef struct hs_led_set_info_s
{
ipmi_control_op_cb handler;
void *cb_data;
ipmi_control_op_info_t sdata;
int vals[1];
} hs_led_set_info_t;
static void
hs_led_set_cb(ipmi_control_t *control,
int err,
ipmi_msg_t *rsp,
void *cb_data)
{
hs_led_set_info_t *control_info = cb_data;
if (err) {
if (control_info->handler)
control_info->handler(control, err, control_info->cb_data);
goto out;
}
if (rsp->data[0] != 0) {
ipmi_log(IPMI_LOG_ERR_INFO,
"%soem_test.c(hs_led_set_cb): Received IPMI error: %x",
CONTROL_NAME(control), rsp->data[0]);
if (control_info->handler)
control_info->handler(control,
IPMI_IPMI_ERR_VAL(rsp->data[0]),
control_info->cb_data);
goto out;
}
if (control_info->handler)
control_info->handler(control, 0, control_info->cb_data);
out:
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
static void
hs_led_set_start(ipmi_control_t *control, int err, void *cb_data)
{
hs_led_set_info_t *control_info = cb_data;
ipmi_msg_t msg;
unsigned char data[1];
ipmi_mc_t *mc = ipmi_control_get_mc(control);
int rv;
if (err) {
if (control_info->handler)
control_info->handler(control, err, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
return;
}
msg.netfn = 0x30; /* OEM NETFN */
msg.cmd = 0x03; /* Set hs_led */
msg.data_len = 1;
msg.data = data;
data[0] = control_info->vals[0];
rv = ipmi_control_send_command(control, mc, 0,
&msg, hs_led_set_cb,
&(control_info->sdata), control_info);
if (rv) {
if (control_info->handler)
control_info->handler(control, rv, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
}
static int
hs_led_set(ipmi_control_t *control,
int *val,
ipmi_control_op_cb handler,
void *cb_data)
{
hs_led_set_info_t *control_info;
int rv;
control_info = ipmi_mem_alloc(sizeof(*control_info));
if (!control_info)
return ENOMEM;
control_info->handler = handler;
control_info->cb_data = cb_data;
control_info->vals[0] = val[0];
rv = ipmi_control_add_opq(control, hs_led_set_start,
&(control_info->sdata), control_info);
if (rv)
ipmi_mem_free(control_info);
return rv;
}
typedef struct hs_led_get_info_s
{
ipmi_control_val_cb handler;
void *cb_data;
ipmi_control_op_info_t sdata;
} hs_led_get_info_t;
static void
hs_led_get_cb(ipmi_control_t *control,
int err,
ipmi_msg_t *rsp,
void *cb_data)
{
hs_led_get_info_t *control_info = cb_data;
int val[1];
if (err) {
if (control_info->handler)
control_info->handler(control, err, NULL, control_info->cb_data);
goto out;
}
if (rsp->data[0] != 0) {
ipmi_log(IPMI_LOG_ERR_INFO,
"%soem_test.c(hs_led_get_cb): Received IPMI error: %x",
CONTROL_NAME(control), rsp->data[0]);
if (control_info->handler)
control_info->handler(control,
IPMI_IPMI_ERR_VAL(rsp->data[0]),
NULL, control_info->cb_data);
goto out;
}
if (rsp->data_len < 2) {
ipmi_log(IPMI_LOG_ERR_INFO,
"%soem_test.c(hs_led_get_cb): response too short: %d",
CONTROL_NAME(control), rsp->data_len);
if (control_info->handler)
control_info->handler(control, EINVAL,
NULL, control_info->cb_data);
goto out;
}
val[0] = rsp->data[1];
if (control_info->handler)
control_info->handler(control, 0,
val, control_info->cb_data);
out:
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
static void
hs_led_get_start(ipmi_control_t *control, int err, void *cb_data)
{
hs_led_get_info_t *control_info = cb_data;
int rv;
ipmi_msg_t msg;
ipmi_mc_t *mc = ipmi_control_get_mc(control);
if (err) {
if (control_info->handler)
control_info->handler(control, err, 0, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
return;
}
msg.netfn = 0x30; /* OEM netfn */
msg.cmd = 0x04; /* Get hs_led */
msg.data_len = 0;
msg.data = NULL;
rv = ipmi_control_send_command(control, mc, 0,
&msg, hs_led_get_cb,
&(control_info->sdata), control_info);
if (rv) {
if (control_info->handler)
control_info->handler(control, err, 0, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
}
static int
hs_led_get(ipmi_control_t *control,
ipmi_control_val_cb handler,
void *cb_data)
{
hs_led_get_info_t *control_info;
int rv;
control_info = ipmi_mem_alloc(sizeof(*control_info));
if (!control_info)
return ENOMEM;
memset(control_info, 0, sizeof(*control_info));
control_info->handler = handler;
control_info->cb_data = cb_data;
rv = ipmi_control_add_opq(control, hs_led_get_start,
&(control_info->sdata), control_info);
if (rv)
ipmi_mem_free(control_info);
return rv;
}
/***********************************************************************
*
* The test power control.
*
**********************************************************************/
typedef struct power_set_info_s
{
ipmi_control_op_cb handler;
void *cb_data;
ipmi_control_op_info_t sdata;
int vals[1];
} power_set_info_t;
static void
power_set_cb(ipmi_control_t *control,
int err,
ipmi_msg_t *rsp,
void *cb_data)
{
power_set_info_t *control_info = cb_data;
if (err) {
if (control_info->handler)
control_info->handler(control, err, control_info->cb_data);
goto out;
}
if (rsp->data[0] != 0) {
ipmi_log(IPMI_LOG_ERR_INFO,
"%soem_test.c(power_set_cb): Received IPMI error: %x",
CONTROL_NAME(control), rsp->data[0]);
if (control_info->handler)
control_info->handler(control,
IPMI_IPMI_ERR_VAL(rsp->data[0]),
control_info->cb_data);
goto out;
}
if (control_info->handler)
control_info->handler(control, 0, control_info->cb_data);
out:
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
static void
power_set_start(ipmi_control_t *control, int err, void *cb_data)
{
power_set_info_t *control_info = cb_data;
ipmi_msg_t msg;
unsigned char data[1];
ipmi_mc_t *mc = ipmi_control_get_mc(control);
int rv;
if (err) {
if (control_info->handler)
control_info->handler(control, err, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
return;
}
msg.netfn = 0x30; /* OEM NETFN */
msg.cmd = 0x01; /* Set power */
msg.data_len = 1;
msg.data = data;
if (control_info->vals[0])
data[0] = 1;
else
data[0] = 0;
rv = ipmi_control_send_command(control, mc, 0,
&msg, power_set_cb,
&(control_info->sdata), control_info);
if (rv) {
if (control_info->handler)
control_info->handler(control, rv, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
}
static int
power_set(ipmi_control_t *control,
int *val,
ipmi_control_op_cb handler,
void *cb_data)
{
power_set_info_t *control_info;
int rv;
control_info = ipmi_mem_alloc(sizeof(*control_info));
if (!control_info)
return ENOMEM;
control_info->handler = handler;
control_info->cb_data = cb_data;
control_info->vals[0] = val[0];
rv = ipmi_control_add_opq(control, power_set_start,
&(control_info->sdata), control_info);
if (rv)
ipmi_mem_free(control_info);
return rv;
}
typedef struct power_get_info_s
{
ipmi_control_val_cb handler;
void *cb_data;
ipmi_control_op_info_t sdata;
} power_get_info_t;
static void
power_get_cb(ipmi_control_t *control,
int err,
ipmi_msg_t *rsp,
void *cb_data)
{
power_get_info_t *control_info = cb_data;
int val[1];
if (err) {
if (control_info->handler)
control_info->handler(control, err, NULL, control_info->cb_data);
goto out;
}
if (rsp->data[0] != 0) {
ipmi_log(IPMI_LOG_ERR_INFO,
"%soem_test.c(power_get_cb): Received IPMI error: %x",
CONTROL_NAME(control), rsp->data[0]);
if (control_info->handler)
control_info->handler(control,
IPMI_IPMI_ERR_VAL(rsp->data[0]),
NULL, control_info->cb_data);
goto out;
}
if (rsp->data_len < 2) {
ipmi_log(IPMI_LOG_ERR_INFO,
"%soem_test.c(power_get_cb): response too short: %d",
CONTROL_NAME(control), rsp->data_len);
if (control_info->handler)
control_info->handler(control, EINVAL,
NULL, control_info->cb_data);
goto out;
}
val[0] = rsp->data[1] != 0;
if (control_info->handler)
control_info->handler(control, 0,
val, control_info->cb_data);
out:
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
static void
power_get_start(ipmi_control_t *control, int err, void *cb_data)
{
power_get_info_t *control_info = cb_data;
int rv;
ipmi_msg_t msg;
ipmi_mc_t *mc = ipmi_control_get_mc(control);
if (err) {
if (control_info->handler)
control_info->handler(control, err, 0, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
return;
}
msg.netfn = 0x30; /* OEM netfn */
msg.cmd = 0x02; /* Get power */
msg.data_len = 0;
msg.data = NULL;
rv = ipmi_control_send_command(control, mc, 0,
&msg, power_get_cb,
&(control_info->sdata), control_info);
if (rv) {
if (control_info->handler)
control_info->handler(control, err, 0, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
}
static int
power_get(ipmi_control_t *control,
ipmi_control_val_cb handler,
void *cb_data)
{
power_get_info_t *control_info;
int rv;
control_info = ipmi_mem_alloc(sizeof(*control_info));
if (!control_info)
return ENOMEM;
memset(control_info, 0, sizeof(*control_info));
control_info->handler = handler;
control_info->cb_data = cb_data;
rv = ipmi_control_add_opq(control, power_get_start,
&(control_info->sdata), control_info);
if (rv)
ipmi_mem_free(control_info);
return rv;
}
/***********************************************************************
*
* The main setup for the test MC 0.
*
**********************************************************************/
static void
mc_control_removal_handler(ipmi_domain_t *domain,
ipmi_mc_t *mc,
void *cb_data)
{
ipmi_control_t *control = cb_data;
ipmi_control_destroy(control);
}
static ipmi_control_transition_t off_led[] = { {IPMI_CONTROL_COLOR_BLACK, 1 } };
static ipmi_control_transition_t on_blue_led[] = { { IPMI_CONTROL_COLOR_BLUE, 1 } };
static ipmi_control_transition_t blue_led1[] =
{
{ IPMI_CONTROL_COLOR_BLUE, 100 },
{ IPMI_CONTROL_COLOR_BLACK, 900 },
};
static ipmi_control_transition_t blue_led2[] =
{
{ IPMI_CONTROL_COLOR_BLUE, 900 },
{ IPMI_CONTROL_COLOR_BLACK, 100 },
};
static ipmi_control_value_t hs_led_values[] =
{
{ 2, blue_led1 },
{ 1, off_led },
{ 2, blue_led2 },
{ 1, on_blue_led },
};
static ipmi_control_light_t hs_led[] = {{ 4, hs_led_values }};
static int
test_handler_0(ipmi_mc_t *mc,
void *cb_data)
{
ipmi_domain_t *domain = ipmi_mc_get_domain(mc);
ipmi_entity_info_t *ents = ipmi_domain_get_entities(domain);
ipmi_entity_t *ent = NULL;
ipmi_control_t *control;
int rv = 0;
ipmi_control_cbs_t cbs;
if (ipmi_mc_get_channel(mc) == IPMI_BMC_CHANNEL)
/* Ignore the connection MCs. */
return 0;
rv = ipmi_mc_set_oem_new_sensor_handler(mc, test_sensor_handler_0, NULL);
if (rv) {
ipmi_log(IPMI_LOG_WARNING,
"%soem_test.c(test_handler_0): "
"Could not set OEM sensor handler: %x",
MC_NAME(mc), rv);
goto out;
}
rv = ipmi_mc_set_sel_oem_event_handler(mc, test_event_handler_0, NULL);
if (rv) {
ipmi_log(IPMI_LOG_WARNING,
"%soem_test.c(test_handler_0): "
"Could not set OEM event handler: %x",
MC_NAME(mc), rv);
goto out;
}
/* Power control for the entity for MC 0x40 (18.2) */
rv = ipmi_entity_add(ents, domain, 0, 0, 0,
IPMI_ENTITY_ID_PROCESSOR_BOARD, 0x20,
NULL, IPMI_ASCII_STR, 0,
dummy_entity_sdr_add,
NULL, &ent);
if (rv) {
ipmi_log(IPMI_LOG_WARNING,
"%soem_test.c(test_handler_0): "
"Could not add the MC entity: %x",
MC_NAME(mc), rv);
goto out;
}
/* Allocate the power control. */
rv = ipmi_control_alloc_nonstandard(&control);
if (rv) {
goto out;
}
ipmi_control_set_type(control, IPMI_CONTROL_POWER);
ipmi_control_set_ignore_for_presence(control, 1);
ipmi_control_set_id(control, "power", IPMI_ASCII_STR, 5);
ipmi_control_set_hot_swap_power(control, 1);
ipmi_control_set_settable(control, 1);
ipmi_control_set_readable(control, 1);
/* Create all the callbacks in the data structure. */
memset(&cbs, 0, sizeof(cbs));
cbs.set_val = power_set;
cbs.get_val = power_get;
ipmi_control_set_callbacks(control, &cbs);
ipmi_control_set_num_elements(control, 1);
/* Add it to the MC and entity. We presume this comes from the
"main" SDR, so set the source_mc to NULL. */
rv = ipmi_control_add_nonstandard(mc, NULL, control,
POWER_CONTROL(0x40),
ent, NULL, NULL);
if (rv) {
ipmi_log(IPMI_LOG_WARNING,
"%soem_test.c(test_handler_0): "
"Could not add the power control: %x",
MC_NAME(mc), rv);
ipmi_control_destroy(control);
goto out;
}
rv = ipmi_mc_add_oem_removed_handler(mc,
mc_control_removal_handler,
control);
if (rv) {
ipmi_log(IPMI_LOG_WARNING,
"%soem_test.c(test_handler_0): "
"Could not add the power control removal handler: %x",
MC_NAME(mc), rv);
ipmi_control_destroy(control);
goto out;
}
/* Allocate the LED control. */
rv = ipmi_control_alloc_nonstandard(&control);
if (rv) {
goto out;
}
ipmi_control_set_type(control, IPMI_CONTROL_LIGHT);
ipmi_control_set_ignore_for_presence(control, 1);
ipmi_control_set_id(control, "Hotswap LED", IPMI_ASCII_STR, 11);
ipmi_control_light_set_lights(control, 1, hs_led);
ipmi_control_set_hot_swap_indicator(control, 1, 0, 1, 2, 3);
ipmi_control_set_settable(control, 1);
ipmi_control_set_readable(control, 1);
/* Create all the callbacks in the data structure. */
memset(&cbs, 0, sizeof(cbs));
cbs.set_val = hs_led_set;
cbs.get_val = hs_led_get;
ipmi_control_set_callbacks(control, &cbs);
ipmi_control_set_num_elements(control, 1);
/* Add it to the MC and entity. We presume this comes from the
"main" SDR, so set the source_mc to NULL. */
rv = ipmi_control_add_nonstandard(mc, NULL, control,
HS_LED_CONTROL(0x40),
ent, NULL, NULL);
if (rv) {
ipmi_log(IPMI_LOG_WARNING,
"%soem_test.c(test_handler_0): "
"Could not add the power control: %x",
MC_NAME(mc), rv);
ipmi_control_destroy(control);
i_ipmi_control_put(control);
goto out;
}
rv = ipmi_mc_add_oem_removed_handler(mc,
mc_control_removal_handler,
control);
if (rv) {
ipmi_log(IPMI_LOG_WARNING,
"%soem_test.c(test_handler_0): "
"Could not add the power control removal handler: %x",
MC_NAME(mc), rv);
ipmi_control_destroy(control);
i_ipmi_control_put(control);
goto out;
}
i_ipmi_control_put(control);
out:
if (ent)
i_ipmi_entity_put(ent);
return rv;
}
/***********************************************************************
*
* Setup.
*
**********************************************************************/
int
init_oem_test(void)
{
int rv;
rv = ipmi_register_oem_handler(MONTAVISTA_MFG_ID,
MONTAVISTA_TEST_START+0,
test_handler_0,
NULL,
NULL);
if (rv)
return rv;
return 0;
}