/*
* example_oem.c
*
* Example OEM code
*
* (C) 2003 MontaVista Software, Inc. All right reserved.
*
* This code is placed into the public domain, you may use this code
* incorporate it into a design, or whatever you want.
*
* 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.
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <OpenIPMI/ipmi_oem.h>
#include <OpenIPMI/ipmi_mc.h>
#include <OpenIPMI/ipmi_sensor.h>
#include <OpenIPMI/ipmi_control.h>
#include <OpenIPMI/ipmi_entity.h>
#include <OpenIPMI/ipmi_addr.h>
#include <OpenIPMI/ipmi_err.h>
#include <OpenIPMI/ipmi_int.h>
#include <OpenIPMI/ipmi_msgbits.h>
/* These are the identifiers used in the get device id command to
identify the various board types. */
#define OEM_MANUFACTURER_ID 0x998877
#define OEM_PRODUCT_ID 0x6655
/* Information common to all sensors. */
typedef struct oem_sensor_header_s
{
unsigned int assert_events;
unsigned int deassert_events;
void *data;
void (*data_freer)(void *);
} oem_sensor_header_t;
/* Information common to all controls. */
typedef struct oem_control_header_s
{
void *data;
} oem_control_header_t;
/* Various LED settings. */
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[] = /* A flashing blue LED. */
{
{ IPMI_CONTROL_COLOR_BLUE, 500 },
{ IPMI_CONTROL_COLOR_BLACK, 500 },
};
/* Setting 0 if off, setting 1 is solid on, setting 2 is flashing. */
static ipmi_control_setting_t blue_blinking_led_set[] =
{
{ 1, off_led },
{ 1, on_blue_led },
{ 2, blue_led1 },
};
static ipmi_control_light_t blue_blinking_led[]
= {{ 3, blue_blinking_led_set }};
typedef struct oem_sens_info_s oem_sens_info_t;
typedef void (*oem_states_get_val_cb)(ipmi_sensor_t *sensor,
oem_sens_info_t *sens_info,
unsigned char *data,
ipmi_states_t *states);
/* Should return the new error. */
typedef int (*oem_states_err_cb)(ipmi_sensor_t *sensor,
oem_sens_info_t *sens_info,
int err,
unsigned char *data,
ipmi_states_t *states);
struct oem_sens_info_s
{
ipmi_sensor_op_info_t sdata;
void *sdinfo;
oem_states_get_val_cb get_states;
oem_states_err_cb err_states;
ipmi_states_read_cb done;
void *cb_data;
};
static oem_sens_info_t *
alloc_sens_info(void *sdinfo, ipmi_states_read_cb done, void *cb_data)
{
oem_sens_info_t *sens_info;
sens_info = ipmi_mem_alloc(sizeof(*sens_info));
if (!sens_info)
return NULL;
memset(sens_info, 0, sizeof(*sens_info));
sens_info->sdinfo = sdinfo;
sens_info->done = done;
sens_info->cb_data = cb_data;
return sens_info;
}
typedef struct oem_control_info_s oem_control_info_t;
typedef int (*oem_control_get_val_cb)(ipmi_control_t *control,
oem_control_info_t *control_info,
unsigned char *data);
struct oem_control_info_s
{
ipmi_control_op_info_t sdata;
unsigned char vals[4];
void *idinfo;
ipmi_control_op_cb done_set;
ipmi_control_val_cb done_get;
oem_control_get_val_cb get_val;
ipmi_control_identifier_val_cb get_identifier_val;
void *cb_data;
};
typedef struct oem_reading_done_s
{
ipmi_sensor_op_info_t sdata;
void *sdinfo;
ipmi_reading_done_cb done;
void *cb_data;
} oem_reading_done_t;
/* If registered against an MC, this will be called with each sensor.
If you return 1, the sensor will NOT be added to the standard set
of sensors for the MC. */
static int
oem_new_sensor(ipmi_mc_t *mc,
ipmi_entity_t *ent,
ipmi_sensor_t *sensor,
void *link,
void *cb_data)
{
int lun, num;
ipmi_sensor_get_num(sensor, &lun, &num);
/* This is where you fix up broken SDR info, and set sensors as
hot-swap sensors, and the like. You can also set custom data
converters or whatever you like here. */
switch (num) {
default:
}
return 0;
}
/* The following are various OEM operations that you will need to
override (or just use the standard ones */
static int
oem_events_enable_set(ipmi_sensor_t *sensor,
ipmi_event_state_t *states,
ipmi_sensor_done_cb done,
void *cb_data)
{
return ENOTSUP;
}
static int
oem_events_enable_get(ipmi_sensor_t *sensor,
ipmi_event_enables_get_cb done,
void *cb_data)
{
ipmi_event_state_t state;
oem_sensor_header_t *hdr = ipmi_sensor_get_oem_info(sensor);
if (done) {
ipmi_event_state_init(&state);
ipmi_event_state_set_scanning_enabled(&state, 1);
state.__assertion_events = hdr->assert_events;
state.__deassertion_events = hdr->deassert_events;
done(sensor, 0, &state, cb_data);
}
return 0;
}
static int
oem_sensor_get_hysteresis(ipmi_sensor_t *sensor,
ipmi_hysteresis_get_cb done,
void *cb_data)
{
return ENOSYS;
}
static int
oem_sensor_set_hysteresis(ipmi_sensor_t *sensor,
unsigned int positive_hysteresis,
unsigned int negative_hysteresis,
ipmi_sensor_done_cb done,
void *cb_data)
{
return ENOSYS;
}
static int
oem_thresholds_get(ipmi_sensor_t *sensor,
ipmi_thresh_get_cb done,
void *cb_data)
{
ipmi_thresholds_t th;
int rv;
rv = ipmi_get_default_sensor_thresholds(sensor, 0, &th);
if (done)
done(sensor, rv, &th, cb_data);
return 0;
}
static int
oem_thresholds_set(ipmi_sensor_t *sensor,
ipmi_thresholds_t *thresholds,
ipmi_sensor_done_cb done,
void *cb_data)
{
return ENOSYS;
}
static int
oem_sensor_get_tolerance(ipmi_sensor_t *sensor,
int val,
double *tolerance)
{
return ENOSYS;
}
static int
oem_sensor_get_accuracy(ipmi_sensor_t *sensor,
int val,
double *accuracy)
{
return ENOSYS;
}
/*
* The get done operation for most discrete sensors is pretty
* standard. We call a function to extract the states from the
* message, then call the user's callback.
*/
static void
oem_discrete_sensor_get_done(ipmi_sensor_t *sensor,
int err,
ipmi_msg_t *rsp,
void *cb_data)
{
oem_sens_info_t *sens_info = cb_data;
ipmi_states_t states;
ipmi_init_states(&states);
ipmi_set_sensor_scanning_enabled(&states, 1);
if (err) {
/* Check the error handler first, and let it handle the error. */
if (sens_info->err_states) {
err = sens_info->err_states(sensor, sens_info, err,
rsp->data, &states);
if (!err)
goto deliver;
}
if (sens_info->done)
sens_info->done(sensor, err,
&states, sens_info->cb_data);
goto out;
}
if (rsp->data[0] != 0) {
ipmi_log(IPMI_LOG_ERR_INFO,
"oem_discrete_sensor_get_done: Received IPMI error: %x",
rsp->data[0]);
if (sens_info->done)
sens_info->done(sensor, IPMI_IPMI_ERR_VAL(rsp->data[0]),
&states, sens_info->cb_data);
goto out;
}
sens_info->get_states(sensor, sens_info, rsp->data, &states);
deliver:
if (sens_info->done)
sens_info->done(sensor, 0, &states, sens_info->cb_data);
out:
ipmi_sensor_opq_done(sensor);
ipmi_mem_free(sens_info);
}
/* Called to free the OEM data we attach to the sensor. */
static void
oem_cleanup_sensor_oem_info(ipmi_sensor_t *sensor, void *oem_info)
{
oem_sensor_header_t *hdr = oem_info;
if (hdr) {
if (hdr->data_freer)
hdr->data_freer(hdr->data);
ipmi_mem_free(hdr);
}
}
/* Allocate basic sensor information. The parms are:
* data - Generic data for the sensor-specific handling to use.
* data_freer() - a function to handle cleaning up data when the
* sensor is freed
* sensor_type - the IPMI sensor type.
* reading_type - the IPMI reading type.
* id - The string name for the sensor.
* assert_events - The supported assertion event bitmask
* deassert_events - The supported deassertion event bitmask
*/
static int
oem_alloc_basic_sensor(
void *data,
void (*data_freer)(void *),
unsigned int sensor_type,
unsigned int reading_type,
char *id,
unsigned int assert_events,
unsigned int deassert_events,
ipmi_sensor_t **sensor)
{
int rv;
oem_sensor_header_t *hdr;
hdr = ipmi_mem_alloc(sizeof(*hdr));
if (!hdr)
return ENOMEM;
hdr->assert_events = assert_events;
hdr->deassert_events = deassert_events;
hdr->data = data;
hdr->data_freer = data_freer;
/* Allocate the sensor. */
rv = ipmi_sensor_alloc_nonstandard(sensor);
if (rv) {
ipmi_mem_free(hdr);
return rv;
}
/* Fill out a bunch of default values. */
ipmi_sensor_set_oem_info(*sensor, hdr, oem_cleanup_sensor_oem_info);
ipmi_sensor_set_entity_instance_logical(*sensor, 0);
ipmi_sensor_set_sensor_init_scanning(*sensor, 1);
ipmi_sensor_set_sensor_init_events(*sensor, 0);
ipmi_sensor_set_sensor_init_thresholds(*sensor, 0);
ipmi_sensor_set_sensor_init_hysteresis(*sensor, 0);
ipmi_sensor_set_sensor_init_type(*sensor, 1);
ipmi_sensor_set_sensor_init_pu_events(*sensor, 0);
ipmi_sensor_set_sensor_init_pu_scanning(*sensor, 1);
ipmi_sensor_set_ignore_for_presence(*sensor, 1);
ipmi_sensor_set_supports_auto_rearm(*sensor, 1);
if (assert_events || deassert_events)
ipmi_sensor_set_event_support(*sensor,
IPMI_EVENT_SUPPORT_GLOBAL_ENABLE);
else
ipmi_sensor_set_event_support(*sensor, IPMI_EVENT_SUPPORT_NONE);
ipmi_sensor_set_sensor_type(*sensor, sensor_type);
ipmi_sensor_set_event_reading_type(*sensor, reading_type);
ipmi_sensor_set_id(*sensor, id);
ipmi_sensor_set_sensor_type_string(
*sensor,
ipmi_get_sensor_type_string(sensor_type));
ipmi_sensor_set_event_reading_type_string(
*sensor,
ipmi_get_event_reading_type_string(reading_type));
return rv;
}
/*
* Finish the sensor handling, basically add it to the MC and entity.
*/
static int
oem_finish_sensor(ipmi_mc_t *mc,
ipmi_sensor_t *sensor,
unsigned int num,
ipmi_entity_t *entity)
{
int rv;
/* Add it to the MC and entity. */
rv = ipmi_sensor_add_nonstandard(mc, sensor, num, entity, NULL, NULL);
if (rv) {
void *hdr;
hdr = ipmi_sensor_get_oem_info(sensor);
ipmi_sensor_destroy(sensor);
ipmi_mem_free(hdr);
}
return rv;
}
/*
* Allocate discrete sensor information. The first are the same as for the
* basic sensor above. The other parms are:
* states_get - A function that gets the current states.
* sensor_reading_name_string - A function to get the reading type
* string. This may be NULL and the standard one will be
* used.
*/
static int
oem_alloc_discrete_sensor(
ipmi_mc_t *mc,
ipmi_entity_t *entity,
unsigned int num,
void *data,
void (*data_freer)(void *),
unsigned int sensor_type,
unsigned int reading_type,
char *id,
unsigned int assert_events,
unsigned int deassert_events,
ipmi_states_get_cb states_get,
ipmi_sensor_reading_name_string_cb sensor_reading_name_string,
ipmi_sensor_t **sensor)
{
int rv;
ipmi_sensor_cbs_t cbs;
int i;
rv = oem_alloc_basic_sensor(data,
data_freer,
sensor_type,
reading_type,
id,
assert_events,
deassert_events,
sensor);
if (rv)
return rv;
/* If the event can be asserted or deasserted, assume it can be
returned and generates an event both ways. */
for (i=0; i<=14; i++) {
int aval = assert_events & 1;
int dval = deassert_events & 1;
ipmi_sensor_set_discrete_assertion_event_supported(*sensor, i, aval);
ipmi_sensor_set_discrete_deassertion_event_supported(*sensor, i, dval);
ipmi_sensor_discrete_set_event_readable(*sensor, i, aval | dval);
assert_events >>= 1;
deassert_events >>= 1;
}
/* Create all the callbacks in the data structure. */
memset(&cbs, 0, sizeof(cbs));
cbs.ipmi_sensor_events_enable_set = oem_events_enable_set;
cbs.ipmi_sensor_events_enable_get = oem_events_enable_get;
cbs.ipmi_states_get = states_get;
/* If ths user supply a function to get the name strings, use it.
Otherwise use the standard one. */
if (sensor_reading_name_string)
cbs.ipmi_sensor_reading_name_string = sensor_reading_name_string;
else
cbs.ipmi_sensor_reading_name_string
= ipmi_standard_sensor_cb.ipmi_sensor_reading_name_string;
ipmi_sensor_set_callbacks(*sensor, &cbs);
rv = oem_finish_sensor(mc, *sensor, num, entity);
return rv;
}
/*
* Allocate a semi-standard threshold sensor information. Semi-standard
* means that it can use standard conversion formulas, is linear, and
* is a more "normal" sensor. The first parmse are the same as for the
* basic sensor above. The other parms are:
* reading_get - A function that gets the current reading.
* raw_nominal - the raw (0-255) value for the nominal reading.
* raw_normal_max - the raw value for the max value inside the
* threshold.
* raw_normal_max - the raw value for the min value inside the
* threshold.
* m, b, b_exp, r_exp - The sensor conversion values.
*/
static int
oem_alloc_semi_stand_threshold_sensor(
ipmi_mc_t *mc,
ipmi_entity_t *entity,
unsigned int num,
void *data,
void (*data_freer)(void *),
unsigned int sensor_type,
unsigned int base_unit,
char *id,
unsigned int assert_events,
unsigned int deassert_events,
ipmi_reading_get_cb reading_get,
int raw_nominal, /* -1 disables. */
int raw_normal_min, /* -1 disables. */
int raw_normal_max, /* -1 disables. */
int m,
int b,
int b_exp,
int r_exp,
ipmi_sensor_t **sensor)
{
int rv;
ipmi_sensor_cbs_t cbs;
int i;
enum ipmi_thresh_e thresh;
rv = oem_alloc_basic_sensor(data,
data_freer,
sensor_type,
IPMI_EVENT_READING_TYPE_THRESHOLD,
id,
assert_events,
deassert_events,
sensor);
if (rv)
return rv;
ipmi_sensor_set_rate_unit_string(*sensor,
ipmi_get_rate_unit_string(0));
ipmi_sensor_set_base_unit_string(*sensor,
ipmi_get_unit_type_string(base_unit));
ipmi_sensor_set_modifier_unit_string(*sensor,
ipmi_get_unit_type_string(0));
ipmi_sensor_set_hysteresis_support(*sensor, 0);
ipmi_sensor_set_threshold_access(*sensor, 0);
ipmi_sensor_set_analog_data_format(*sensor,
IPMI_ANALOG_DATA_FORMAT_UNSIGNED);
ipmi_sensor_set_rate_unit(*sensor, 0);
ipmi_sensor_set_modifier_unit_use(*sensor, 0);
ipmi_sensor_set_percentage(*sensor, 0);
ipmi_sensor_set_base_unit(*sensor, base_unit);
ipmi_sensor_set_modifier_unit(*sensor, 0);
ipmi_sensor_set_linearization(*sensor, 0);
if (raw_normal_min >= 0) {
ipmi_sensor_set_raw_normal_min(*sensor, raw_normal_min);
ipmi_sensor_set_normal_min_specified(*sensor, 1);
} else {
ipmi_sensor_set_raw_normal_min(*sensor, 0);
ipmi_sensor_set_normal_min_specified(*sensor, 0);
}
if (raw_normal_max >= 0) {
ipmi_sensor_set_raw_normal_max(*sensor, raw_normal_max);
ipmi_sensor_set_normal_max_specified(*sensor, 1);
} else {
ipmi_sensor_set_raw_normal_max(*sensor, 0);
ipmi_sensor_set_normal_max_specified(*sensor, 0);
}
if (raw_nominal >= 0) {
ipmi_sensor_set_raw_nominal_reading(*sensor, raw_nominal);
ipmi_sensor_set_nominal_reading_specified(*sensor, 1);
} else {
ipmi_sensor_set_raw_nominal_reading(*sensor, 0);
ipmi_sensor_set_nominal_reading_specified(*sensor, 0);
}
ipmi_sensor_set_raw_sensor_max(*sensor, 0xff);
ipmi_sensor_set_raw_sensor_min(*sensor, 0);
ipmi_sensor_set_raw_upper_non_recoverable_threshold(*sensor, 0);
ipmi_sensor_set_raw_upper_critical_threshold(*sensor, 0);
ipmi_sensor_set_raw_upper_non_critical_threshold(*sensor, 0);
ipmi_sensor_set_raw_lower_non_recoverable_threshold(*sensor, 0);
ipmi_sensor_set_raw_lower_critical_threshold(*sensor, 0);
ipmi_sensor_set_raw_lower_non_critical_threshold(*sensor, 0);
ipmi_sensor_set_positive_going_threshold_hysteresis(*sensor, 0);
ipmi_sensor_set_negative_going_threshold_hysteresis(*sensor, 0);
for (thresh = IPMI_LOWER_NON_CRITICAL;
thresh <= IPMI_UPPER_NON_RECOVERABLE;
thresh++)
{
if ((1 << ((thresh*2) + IPMI_GOING_LOW)) & assert_events)
ipmi_sensor_set_threshold_assertion_event_supported
(*sensor,
thresh,
IPMI_GOING_LOW,
1);
if ((1 << ((thresh*2) + IPMI_GOING_HIGH)) & assert_events)
ipmi_sensor_set_threshold_assertion_event_supported
(*sensor,
thresh,
IPMI_GOING_HIGH,
1);
if ((1 << ((thresh*2) + IPMI_GOING_LOW)) & deassert_events)
ipmi_sensor_set_threshold_deassertion_event_supported
(*sensor,
thresh,
IPMI_GOING_LOW,
1);
if ((1 << ((thresh*2) + IPMI_GOING_HIGH)) & deassert_events)
ipmi_sensor_set_threshold_deassertion_event_supported
(*sensor,
thresh,
IPMI_GOING_HIGH,
1);
/* No thresholds are readable, they are all fixed. */
ipmi_sensor_threshold_set_readable(*sensor, thresh, 0);
ipmi_sensor_threshold_set_settable(*sensor, thresh, 0);
}
for (i=0; i<256; i++) {
ipmi_sensor_set_raw_m(*sensor, i, m);
ipmi_sensor_set_raw_b(*sensor, i, b);
ipmi_sensor_set_raw_b_exp(*sensor, i, b_exp);
ipmi_sensor_set_raw_r_exp(*sensor, i, r_exp);
ipmi_sensor_set_raw_accuracy(*sensor, i, m);
ipmi_sensor_set_raw_accuracy_exp(*sensor, i, r_exp);
}
/* Create all the callbacks in the data structure. */
memset(&cbs, 0, sizeof(cbs));
cbs.ipmi_sensor_events_enable_set = oem_events_enable_set;
cbs.ipmi_sensor_events_enable_get = oem_events_enable_get;
cbs.ipmi_sensor_convert_from_raw
= ipmi_standard_sensor_cb.ipmi_sensor_convert_from_raw;
cbs.ipmi_sensor_convert_to_raw
= ipmi_standard_sensor_cb.ipmi_sensor_convert_to_raw;
cbs.ipmi_sensor_get_accuracy = oem_sensor_get_accuracy;
cbs.ipmi_sensor_get_tolerance = oem_sensor_get_tolerance;
cbs.ipmi_sensor_get_hysteresis = oem_sensor_get_hysteresis;
cbs.ipmi_sensor_set_hysteresis = oem_sensor_set_hysteresis;
cbs.ipmi_thresholds_set = oem_thresholds_set;
cbs.ipmi_thresholds_get = oem_thresholds_get;
cbs.ipmi_reading_get = reading_get;
ipmi_sensor_set_callbacks(*sensor, &cbs);
rv = oem_finish_sensor(mc, *sensor, num, entity);
return rv;
}
/*
* This is used for controls, the handling for the set_done is common
* for controls, so just do it in one place.
*/
static void
oem_control_set_done(ipmi_control_t *control,
int err,
ipmi_msg_t *rsp,
void *cb_data)
{
oem_control_info_t *control_info = cb_data;
if (err) {
if (control_info->done_set)
control_info->done_set(control, err, control_info->cb_data);
goto out;
}
if (rsp->data[0] != 0) {
ipmi_log(IPMI_LOG_ERR_INFO,
"oem_control_set_done: Received IPMI error: %x",
rsp->data[0]);
if (control_info->done_set)
control_info->done_set(control,
IPMI_IPMI_ERR_VAL(rsp->data[0]),
control_info->cb_data);
goto out;
}
if (control_info->done_set)
control_info->done_set(control, 0, control_info->cb_data);
out:
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
/*
* This is used for controls, the handling for the get_done is usually
* common for controls, so just do it in one place. Note that this
* only handles controls with one value, multi-valued controls need
* their own custom handler.
*/
static void
oem_control_get_done(ipmi_control_t *control,
int err,
ipmi_msg_t *rsp,
void *cb_data)
{
oem_control_info_t *control_info = cb_data;
int val;
if (err) {
if (control_info->done_get)
control_info->done_get(control, err, 0, control_info->cb_data);
goto out;
}
if (rsp->data[0] != 0) {
ipmi_log(IPMI_LOG_ERR_INFO,
"oem_control_get_done: Received IPMI error: %x",
rsp->data[0]);
if (control_info->done_get)
control_info->done_get(control,
IPMI_IPMI_ERR_VAL(rsp->data[0]),
NULL, control_info->cb_data);
goto out;
}
val = control_info->get_val(control, control_info, rsp->data);
if (control_info->done_get)
control_info->done_get(control, 0, &val, control_info->cb_data);
out:
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
/* Called to free the OEM data we attach to the control. */
static void
oem_cleanup_control_oem_info(ipmi_control_t *control, void *oem_info)
{
oem_sensor_header_t *hdr = oem_info;
if (hdr) {
ipmi_mem_free(hdr);
}
}
/*
* Allocate a control. The parms are:
* mc - The MC the control sits on.
* entity - The entity the control belongs to.
* num - The number of the control in the MC. This must be unique
* for the given MC.
* data - generic data for the specific control.
* control_type - The type of control (from ipmiif.h).
* id - The string name of the control.
* set_val - The function to set the value of the control.
* get_val - The function to get the current value of the control.
*/
static int
oem_alloc_control(ipmi_mc_t *mc,
ipmi_entity_t *entity,
unsigned int num,
void *data,
unsigned int control_type,
char *id,
ipmi_control_set_val_cb set_val,
ipmi_control_get_val_cb get_val,
ipmi_control_t **control)
{
int rv;
ipmi_control_cbs_t cbs;
oem_control_header_t *hdr;
hdr = ipmi_mem_alloc(sizeof(*hdr));
if (!hdr)
return ENOMEM;
hdr->data = data;
/* Allocate the sensor. */
rv = ipmi_control_alloc_nonstandard(control);
if (rv) {
ipmi_mem_free(hdr);
return rv;
}
/* Fill out default values. */
ipmi_control_set_oem_info(*control, hdr, oem_cleanup_control_oem_info);
ipmi_control_set_type(*control, control_type);
ipmi_control_set_id(*control, id);
ipmi_control_set_ignore_for_presence(*control, 1);
/* Assume we can read and set the value. */
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 = set_val;
cbs.get_val = get_val;
ipmi_control_set_callbacks(*control, &cbs);
/* Add it to the MC and entity. */
rv = ipmi_control_add_nonstandard(mc, *control, num, entity, NULL, NULL);
if (rv) {
ipmi_control_destroy(*control);
ipmi_mem_free(hdr);
*control = NULL;
}
return rv;
}
typedef struct oem_info_s
{
int dummy; /* No OEM info for now. */
} oem_info_t;
/* These are the sensor numbers for our board. */
#define OEM_SLOT_SENSOR_NUM 0
/* These are the control numbers for our board. */
#define OEM_BOARD_RESET_NUM 0
#define OEM_BOARD_BLUE_LED_NUM 1
/* These are the message definitions for doing some OEM operations
on a board. */
#define OEM_NETFN_OEM1 0x30
#define OEM_SET_SLOT_RESET_CMD 1
#define OEM_GET_SLOT_EJECT_CMD 2
#define OEM_SET_BLUE_LED_CMD 3
#define OEM_GET_BLUE_LED_CMD 4
/*
* Some events are system-specific, and need special handling. The
* following is an example, because we have a special sensor for the
* ejector handle, we need to handle it's events, too.
*/
/* Structure passed around to handle events. */
typedef struct mc_event_info_s
{
oem_info_t *info;
ipmi_mc_t *mc;
ipmi_event_t *event;
ipmi_event_t event_copy;
int handled;
} mc_event_info_t;
static void
oem_board_ejector_changed_event(ipmi_sensor_t *sensor, void *cb_data)
{
mc_event_info_t *einfo = cb_data;
ipmi_sensor_discrete_event_handler_cb handler;
void *h_cb_data;
enum ipmi_event_dir_e assertion;
ipmi_event_t *event = &(einfo->event_copy);
if (event->data[9] & 0x80)
assertion = IPMI_DEASSERTION;
else
assertion = IPMI_ASSERTION;
ipmi_sensor_discrete_get_event_handler(sensor, &handler, &h_cb_data);
if (handler) {
handler(sensor,
assertion,
6, /* Offset 6 is the ejector offset. */
-1, -1,
h_cb_data,
einfo->event);
einfo->event = NULL;
}
}
static int
oem_event_handler(ipmi_mc_t *mc,
ipmi_event_t *event,
void *cb_data)
{
int rv;
ipmi_sensor_id_t id;
mc_event_info_t einfo;
unsigned long timestamp;
ipmi_mc_id_t mc_id;
if ((event->type != 2) && (event->type != 3))
/* Not a system event record or OEM event. */
return 0;
if (event->data[6] != 3)
/* Not a 1.5 event version */
return 0;
timestamp = ipmi_get_uint32(&(event->data[0]));
if (timestamp < ipmi_mc_get_startup_SEL_time(mc))
/* It's an old event, ignore it. */
return 0;
einfo.info = ipmi_mc_get_oem_data(mc);
einfo.mc = mc;
einfo.event = event;
memcpy(&einfo.event_copy, event, sizeof(einfo.event_copy));
einfo.handled = 0;
/* We have to do a sensor callback so we hold the sensor lock when
we operate on it. */
mc_id = ipmi_mc_convert_to_id(mc);
id.bmc = mc_id.bmc;
id.channel = mc_id.channel;
id.mc_num = mc_id.mc_num;
id.lun = 4;
switch (event->data[8]) {
case 1:
id.sensor_num = OEM_SLOT_SENSOR_NUM;
rv = ipmi_sensor_pointer_cb(id,
oem_board_ejector_changed_event,
&einfo);
break;
}
/* If the event was handled but not delivered to the user, then
deliver it to the unhandled handler. */
if (einfo.handled && (einfo.event != NULL))
ipmi_handle_unhandled_event(mc, event);
return einfo.handled;
}
/* Start a reset operation for the board. */
static void
board_reset_set_start(ipmi_control_t *control, int err, void *cb_data)
{
oem_control_info_t *control_info = cb_data;
int rv;
ipmi_msg_t msg;
unsigned char data[4];
if (err) {
if (control_info->done_set)
control_info->done_set(control, err, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
return;
}
msg.netfn = OEM_NETFN_OEM1;
msg.cmd = OEM_SET_SLOT_RESET_CMD;
msg.data_len = 1;
msg.data = data;
data[0] = control_info->vals[0];
rv = ipmi_control_send_command(control, ipmi_control_get_mc(control), 0,
&msg, oem_control_set_done,
&(control_info->sdata), control_info);
if (rv) {
if (control_info->done_set)
control_info->done_set(control, rv, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
}
/*
* This is the startup operation for setting the board's reset value.
*/
static int
board_reset_set(ipmi_control_t *control,
int *val,
ipmi_control_op_cb handler,
void *cb_data)
{
oem_control_info_t *control_info;
int rv;
control_info = ipmi_mem_alloc(sizeof(*control_info));
if (!control_info)
return ENOMEM;
control_info->done_set = handler;
control_info->cb_data = cb_data;
control_info->vals[0] = *val;
/* We use the control's operation queue to serialize operations to
the control. Basically, for each control, only one operation a
time is allowed. */
rv = ipmi_control_add_opq(control, board_reset_set_start,
&(control_info->sdata), control_info);
if (rv)
ipmi_mem_free(control_info);
return rv;
}
/*
* Get the reset value. This is not supported.
*/
static int
board_reset_get(ipmi_control_t *control,
ipmi_control_val_cb handler,
void *cb_data)
{
return ENOSYS;
}
/*
* This is for the board slot sensor, we set the states properly from
* the response to the OEM_GET_SLOT_EJECT_CMD we sent earlier.
*/
static void
board_slot_get_cb(ipmi_sensor_t *sensor,
oem_sens_info_t *sens_info,
unsigned char *data,
ipmi_states_t *states)
{
if (data[1])
ipmi_set_state(states, 6, 1); /* Ejector extraction request */
else
ipmi_set_state(states, 6, 0); /* Ejector is closed */
}
/*
* Actually start the slot get operation, once the opq runs us. This
* will send the message to get the ejector handle's current state.
*/
static void
board_slot_get_start(ipmi_sensor_t *sensor, int err, void *cb_data)
{
oem_sens_info_t *get_info = cb_data;
ipmi_msg_t msg;
int rv;
ipmi_states_t states;
ipmi_init_states(&states);
ipmi_set_sensor_scanning_enabled(&states, 1);
if (err) {
if (get_info->done)
get_info->done(sensor, err, &states, get_info->cb_data);
ipmi_sensor_opq_done(sensor);
ipmi_mem_free(get_info);
return;
}
msg.netfn = OEM_NETFN_OEM1;
msg.cmd = OEM_GET_SLOT_EJECT_CMD;
msg.data_len = 0;
msg.data = NULL;
rv = ipmi_sensor_send_command(sensor, ipmi_sensor_get_mc(sensor), 0,
&msg, oem_discrete_sensor_get_done,
&(get_info->sdata), get_info);
if (rv) {
if (get_info->done)
get_info->done(sensor, rv, &states, get_info->cb_data);
ipmi_sensor_opq_done(sensor);
ipmi_mem_free(get_info);
}
}
/*
* Called to get the board's ejector handle state. This will allocate
* some info and schedule an operation to happen.
*/
static int
board_slot_get(ipmi_sensor_t *sensor,
ipmi_states_read_cb done,
void *cb_data)
{
int rv;
oem_sens_info_t *get_info;
get_info = alloc_sens_info(NULL, done, cb_data);
if (!get_info)
return ENOMEM;
get_info->get_states = board_slot_get_cb;
rv = ipmi_sensor_add_opq(sensor, board_slot_get_start,
&(get_info->sdata), get_info);
if (rv)
ipmi_mem_free(get_info);
return rv;
}
/*
* Send the message to set the blue led value.
*/
static void
board_blue_led_set_start(ipmi_control_t *control, int err, void *cb_data)
{
oem_control_info_t *control_info = cb_data;
int rv;
ipmi_msg_t msg;
unsigned char data[4];
if (err) {
if (control_info->done_set)
control_info->done_set(control, err, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
return;
}
msg.netfn = OEM_NETFN_OEM1;
msg.cmd = OEM_SET_BLUE_LED_CMD;
msg.data_len = 1;
data[0] = control_info->vals[0];
rv = ipmi_control_send_command(control, ipmi_control_get_mc(control), 0,
&msg, oem_control_set_done,
&(control_info->sdata), control_info);
if (rv) {
if (control_info->done_set)
control_info->done_set(control, rv, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
}
/*
* Allocate info an schedule us to set the blue led info.
*/
static int
board_blue_led_set(ipmi_control_t *control,
int *val,
ipmi_control_op_cb handler,
void *cb_data)
{
oem_control_info_t *control_info;
int rv;
control_info = ipmi_mem_alloc(sizeof(*control_info));
if (!control_info)
return ENOMEM;
control_info->done_set = handler;
control_info->cb_data = cb_data;
control_info->vals[0] = *val;
rv = ipmi_control_add_opq(control, board_blue_led_set_start,
&(control_info->sdata), control_info);
if (rv)
ipmi_mem_free(control_info);
return rv;
}
/*
* Get the value of the blue led from the response to
* OEM_GET_BLUE_LED_CMD.
*/
static int
board_blue_led_get_cb(ipmi_control_t *control,
oem_control_info_t *control_info,
unsigned char *data)
{
return data[1];
}
/* Start the operation to get the blue LED's value. */
static void
board_blue_led_get_start(ipmi_control_t *control, int err, void *cb_data)
{
oem_control_info_t *control_info = cb_data;
int rv;
ipmi_msg_t msg;
if (err) {
if (control_info->done_get)
control_info->done_get(control, err, NULL, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
return;
}
msg.netfn = OEM_NETFN_OEM1;
msg.cmd = OEM_GET_BLUE_LED_CMD;
msg.data_len = 0;
msg.data = NULL;
rv = ipmi_control_send_command(control, ipmi_control_get_mc(control), 0,
&msg, oem_control_get_done,
&(control_info->sdata), control_info);
if (rv) {
if (control_info->done_get)
control_info->done_get(control, rv, NULL, control_info->cb_data);
ipmi_control_opq_done(control);
ipmi_mem_free(control_info);
}
}
/*
* Allocate info and schedule an operation to get the blue LED's
* value.
*/
static int
board_blue_led_get(ipmi_control_t *control,
ipmi_control_val_cb handler,
void *cb_data)
{
oem_control_info_t *control_info;
int rv;
control_info = ipmi_mem_alloc(sizeof(*control_info));
if (!control_info)
return ENOMEM;
control_info->done_get = handler;
control_info->cb_data = cb_data;
control_info->get_val = board_blue_led_get_cb;
rv = ipmi_control_add_opq(control, board_blue_led_get_start,
&(control_info->sdata), control_info);
if (rv)
ipmi_mem_free(control_info);
return rv;
}
/*
* Allocate new general board sensors for the board.
*/
static int
new_board_sensors(ipmi_mc_t *mc,
ipmi_entity_t *ent,
oem_info_t *info)
{
int rv;
ipmi_sensor_t *sensor;
ipmi_control_t *control;
/* The slot sensor */
rv = oem_alloc_discrete_sensor(
mc, ent,
OEM_SLOT_SENSOR_NUM,
info, NULL,
IPMI_SENSOR_TYPE_SLOT_CONNECTOR,
IPMI_EVENT_READING_TYPE_SENSOR_SPECIFIC,
"slot",
0x40, 0x40, /* offset 6 is supported (hot-swap requester). */
board_slot_get,
NULL,
&sensor);
if (rv)
goto out_err;
ipmi_sensor_set_hot_swap_requester(sensor, 6, 1); /* offset 6 is for
hot-swap */
/* Reset control */
rv = oem_alloc_control(mc, ent,
OEM_BOARD_RESET_NUM,
info,
IPMI_CONTROL_RESET,
"reset",
board_reset_set,
board_reset_get,
&control);
if (rv)
goto out_err;
ipmi_control_set_num_elements(control, 1);
/* The reset control is not readable. */
ipmi_control_set_readable(control, 0);
/* Blue LED control */
rv = oem_alloc_control(mc, ent,
OEM_BOARD_BLUE_LED_NUM,
info,
IPMI_CONTROL_LIGHT,
"blue led",
board_blue_led_set,
board_blue_led_get,
&control);
if (rv)
goto out_err;
ipmi_control_light_set_lights(control, 1, blue_blinking_led);
ipmi_control_set_hot_swap_indicator(control, 1);
out_err:
return rv;
}
/* Write some I2C data on an MC. */
static void
i2c_write(ipmi_mc_t *mc,
unsigned int bus,
unsigned int addr,
unsigned int offset,
unsigned int val)
{
ipmi_msg_t msg;
unsigned char data[5];
int rv;
msg.netfn = IPMI_APP_NETFN;
msg.cmd = IPMI_MASTER_READ_WRITE_CMD;
msg.data_len = 4;
msg.data = data;
data[0] = bus;
data[1] = addr;
data[2] = 0; /* Read no bytes */
data[3] = offset;
data[4] = val;
rv = ipmi_send_command(mc, 0, &msg, NULL, NULL);
if (rv)
ipmi_log(IPMI_LOG_WARNING,
"Could not to I2C write to %x.%x.%x, error %x\n",
bus, addr, offset, rv);
}
typedef struct i2c_sens_s
{
unsigned int bus;
unsigned int addr;
unsigned int offset;
} i2c_sens_t;
/*
* Handle data read back from the I2C. Convert it and present it
* as the sensor's data.
*/
static void
i2c_sens_reading_cb(ipmi_sensor_t *sensor,
int err,
ipmi_msg_t *rsp,
void *cb_data)
{
oem_reading_done_t *get_info = cb_data;
ipmi_states_t states;
unsigned int raw_val;
double val;
enum ipmi_value_present_e present;
int rv;
ipmi_init_states(&states);
ipmi_set_sensor_scanning_enabled(&states, 1);
if (err) {
if (get_info->done)
get_info->done(sensor, err,
IPMI_NO_VALUES_PRESENT, 0, 0.0, &states,
get_info->cb_data);
ipmi_sensor_opq_done(sensor);
ipmi_mem_free(get_info);
return;
}
if (rsp->data[0] != 0) {
ipmi_log(IPMI_LOG_ERR_INFO,
"i2c_sens_reading_cb: Received IPMI error: %x",
rsp->data[0]);
if (get_info->done)
get_info->done(sensor,
IPMI_IPMI_ERR_VAL(rsp->data[0]),
IPMI_NO_VALUES_PRESENT,
0,
0.0,
&states,
get_info->cb_data);
goto out;
}
raw_val = rsp->data[1];
rv = ipmi_sensor_convert_from_raw(sensor, raw_val, &val);
if (rv)
present = IPMI_RAW_VALUE_PRESENT;
else
present = IPMI_BOTH_VALUES_PRESENT;
if (get_info->done)
get_info->done(sensor,
0,
present,
raw_val,
val,
&states,
get_info->cb_data);
out:
ipmi_sensor_opq_done(sensor);
ipmi_mem_free(get_info);
}
/*
* Send the master read/write command to read a byte from the
* I2C bus to be reported as the sensor reading.
*/
static void
i2c_sens_get_reading_start(ipmi_sensor_t *sensor, int err, void *cb_data)
{
oem_reading_done_t *get_info = cb_data;
i2c_sens_t *info = get_info->sdinfo;
ipmi_msg_t msg;
unsigned char data[4];
int rv;
ipmi_states_t states;
ipmi_init_states(&states);
ipmi_set_sensor_scanning_enabled(&states, 1);
if (err) {
if (get_info->done)
get_info->done(sensor, err,
IPMI_NO_VALUES_PRESENT, 0, 0.0, &states,
get_info->cb_data);
ipmi_sensor_opq_done(sensor);
ipmi_mem_free(get_info);
return;
}
msg.netfn = IPMI_APP_NETFN;
msg.cmd = IPMI_MASTER_READ_WRITE_CMD;
msg.data_len = 4;
msg.data = data;
data[0] = info->bus;
data[1] = info->addr;
data[2] = 1; /* Read one byte */
data[3] = info->offset; /* Offset to read from. */
rv = ipmi_sensor_send_command(sensor, ipmi_sensor_get_mc(sensor), 0,
&msg, i2c_sens_reading_cb,
&(get_info->sdata), get_info);
if (rv) {
if (get_info->done)
get_info->done(sensor, rv,
IPMI_NO_VALUES_PRESENT, 0, 0.0, &states,
get_info->cb_data);
ipmi_sensor_opq_done(sensor);
ipmi_mem_free(get_info);
}
}
/*
* Allocate some info and schedule an operation to read some data
* from I2C to report as the sensor's reading.
*/
static int
i2c_sens_get_reading(ipmi_sensor_t *sensor,
ipmi_reading_done_cb done,
void *cb_data)
{
oem_sensor_header_t *hdr = ipmi_sensor_get_oem_info(sensor);
i2c_sens_t *info = hdr->data;
int rv;
oem_reading_done_t *get_info;
get_info = ipmi_mem_alloc(sizeof(*get_info));
if (!get_info)
return ENOMEM;
get_info->sdinfo = info;
get_info->done = done;
get_info->cb_data = cb_data;
rv = ipmi_sensor_add_opq(sensor, i2c_sens_get_reading_start,
&(get_info->sdata), get_info);
if (rv)
ipmi_mem_free(get_info);
return rv;
}
/*
* Allocate a sensor for an ADM 1021 temperature sensor.
*/
static int
alloc_adm1021_sensor(ipmi_mc_t *mc,
ipmi_entity_t *ent,
unsigned int num,
unsigned int bus,
unsigned int addr,
char *id)
{
int rv;
i2c_sens_t *info;
ipmi_sensor_t *sensor;
info = ipmi_mem_alloc(sizeof(*info));
if (!info)
return ENOMEM;
info->bus = bus;
info->addr = addr;
info->offset = 1; /* Offset 1 is the remote temp sens. */
i2c_write(mc, bus, addr, 0xa, 4); /* Do 1 conversion a second. */
i2c_write(mc, bus, addr, 0x9, 0); /* Enable conversion. */
rv = oem_alloc_semi_stand_threshold_sensor(mc, ent, num,
info, ipmi_mem_free,
IPMI_SENSOR_TYPE_TEMPERATURE,
IPMI_UNIT_TYPE_DEGREES_C,
id,
0, 0,
i2c_sens_get_reading,
-1, -1, 105,
1, 0, 0, 0,
&sensor);
if (rv) {
ipmi_mem_free(info);
goto out;
}
ipmi_sensor_set_analog_data_format(sensor,
IPMI_ANALOG_DATA_FORMAT_2_COMPL);
ipmi_sensor_set_raw_sensor_max(sensor, 0x7f);
ipmi_sensor_set_raw_sensor_min(sensor, 0x80);
out:
return rv;
}
/*
* Called to add the entity to the SDR info. We don't support this.
*/
static int
oem_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;
}
/*
* Called when the MC is removed from the system.
*/
static void oem_mc_removed(ipmi_mc_t *bmc,
ipmi_mc_t *mc,
void *cb_data)
{
free(cb_data);
}
/* We convert addresses to instances by taking the actual I2C address
(the upper 7 bits of the IPMB address) and subtracting 58 from it.
Boards start at 0x58, so this makes the instance numbers for boards
start at zero. */
static unsigned int
oem_addr_to_instance(unsigned int slave_addr)
{
slave_addr /= 2;
if (slave_addr >= 0x58) {
if (slave_addr >= 0x61)
slave_addr--;
return slave_addr - 0x58;
} else
return slave_addr;
}
/*
* Called when an MC with the matching manufacturer and product id
* are detected.
*/
static int
oem_handler(ipmi_mc_t *mc,
void *cb_data)
{
unsigned int slave_addr = ipmi_mc_get_address(mc);
ipmi_entity_info_t *ents;
ipmi_entity_t *ent;
int rv;
oem_info_t *info;
ipmi_domain_t *domain = ipmi_mc_get_domain(mc);
info = malloc(sizeof(*info));
if (!info)
return ENOMEM;
i_ipmi_domain_entity_lock(domain);
ents = ipmi_domain_get_entities(domain);
rv = ipmi_entity_add(ents, domain, 0, slave_addr, 0,
IPMI_ENTITY_ID_PROCESSING_BLADE,
oem_addr_to_instance(slave_addr),
"my-name",
oem_entity_sdr_add,
NULL, &ent);
if (rv) {
ipmi_log(IPMI_LOG_WARNING,
"oem_handler: could not add entity");
goto out;
}
rv = ipmi_mc_set_oem_new_sensor_handler(mc, oem_new_sensor, info);
if (rv) {
ipmi_log(IPMI_LOG_WARNING,
"oem_handler: could not register new sensor handler");
goto out;
}
rv = ipmi_mc_add_oem_removed_handler(mc, oem_mc_removed, info);
if (rv) {
ipmi_log(IPMI_LOG_WARNING,
"oem_handler: could not set OEM removal handler");
goto out;
}
ipmi_mc_set_oem_data(mc, info);
rv = new_board_sensors(mc, ent, info);
if (rv)
goto out;
rv = alloc_adm1021_sensor(mc, ent, 0x80, 0x01, 0x9c, "Proc Temp");
if (rv)
goto out;
rv = ipmi_mc_set_oem_event_handler(mc, oem_event_handler, info);
out:
i_ipmi_domain_entity_unlock(domain);
return rv;
}
/*
* The user calls this to set up handling for this OEM MC. This should
* be the only non-static function in this file.
*/
int
my_oem_init(void)
{
int rv;
rv = ipmi_register_oem_handler(OEM_MANUFACTURER_ID,
OEM_PRODUCT_ID,
oem_handler,
NULL,
NULL);
return rv;
}