ipmi: add SET_SENSOR_READING command

SET_SENSOR_READING is a complex IPMI command (see IPMI spec 35.17)
which enables the host software to set the reading value and the event
status of sensors supporting it.

Below is a proposal for all the operations (reading, assert, deassert,
event data) with the following limitations :

 - No event are generated for threshold-based sensors.
 - The case in which the BMC needs to generate its own events is not
   supported.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Corey Minyard <cminyard@mvista.com>
Message-Id: <20191118092429.16149-1-clg@kaod.org>
[Moved the break statement for case SENSOR_GEN_EVENT_DATA above the
 closing brace to keep the indention consistent.]
Signed-off-by: Corey Minyard <cminyard@mvista.com>
This commit is contained in:
Cédric Le Goater 2019-11-18 10:24:29 +01:00 committed by Corey Minyard
parent 789101b73d
commit e3f7320caa

View File

@ -49,6 +49,7 @@
#define IPMI_CMD_GET_SENSOR_READING 0x2d
#define IPMI_CMD_SET_SENSOR_TYPE 0x2e
#define IPMI_CMD_GET_SENSOR_TYPE 0x2f
#define IPMI_CMD_SET_SENSOR_READING 0x30
/* #define IPMI_NETFN_APP 0x06 In ipmi.h */
@ -1747,6 +1748,227 @@ static void get_sensor_type(IPMIBmcSim *ibs,
rsp_buffer_push(rsp, sens->evt_reading_type_code);
}
/*
* bytes parameter
* 1 sensor number
* 2 operation (see below for bits meaning)
* 3 sensor reading
* 4:5 assertion states (optional)
* 6:7 deassertion states (optional)
* 8:10 event data 1,2,3 (optional)
*/
static void set_sensor_reading(IPMIBmcSim *ibs,
uint8_t *cmd, unsigned int cmd_len,
RspBuffer *rsp)
{
IPMISensor *sens;
uint8_t evd1 = 0;
uint8_t evd2 = 0;
uint8_t evd3 = 0;
uint8_t new_reading = 0;
uint16_t new_assert_states = 0;
uint16_t new_deassert_states = 0;
bool change_reading = false;
bool change_assert = false;
bool change_deassert = false;
enum {
SENSOR_GEN_EVENT_NONE,
SENSOR_GEN_EVENT_DATA,
SENSOR_GEN_EVENT_BMC,
} do_gen_event = SENSOR_GEN_EVENT_NONE;
if ((cmd[2] >= MAX_SENSORS) ||
!IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
return;
}
sens = ibs->sensors + cmd[2];
/* [1:0] Sensor Reading operation */
switch ((cmd[3]) & 0x3) {
case 0: /* Do not change */
break;
case 1: /* write given value to sensor reading byte */
new_reading = cmd[4];
if (sens->reading != new_reading) {
change_reading = true;
}
break;
case 2:
case 3:
rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
return;
}
/* [3:2] Deassertion bits operation */
switch ((cmd[3] >> 2) & 0x3) {
case 0: /* Do not change */
break;
case 1: /* write given value */
if (cmd_len > 7) {
new_deassert_states = cmd[7];
change_deassert = true;
}
if (cmd_len > 8) {
new_deassert_states |= (cmd[8] << 8);
}
break;
case 2: /* mask on */
if (cmd_len > 7) {
new_deassert_states = (sens->deassert_states | cmd[7]);
change_deassert = true;
}
if (cmd_len > 8) {
new_deassert_states |= (sens->deassert_states | (cmd[8] << 8));
}
break;
case 3: /* mask off */
if (cmd_len > 7) {
new_deassert_states = (sens->deassert_states & cmd[7]);
change_deassert = true;
}
if (cmd_len > 8) {
new_deassert_states |= (sens->deassert_states & (cmd[8] << 8));
}
break;
}
if (change_deassert && (new_deassert_states == sens->deassert_states)) {
change_deassert = false;
}
/* [5:4] Assertion bits operation */
switch ((cmd[3] >> 4) & 0x3) {
case 0: /* Do not change */
break;
case 1: /* write given value */
if (cmd_len > 5) {
new_assert_states = cmd[5];
change_assert = true;
}
if (cmd_len > 6) {
new_assert_states |= (cmd[6] << 8);
}
break;
case 2: /* mask on */
if (cmd_len > 5) {
new_assert_states = (sens->assert_states | cmd[5]);
change_assert = true;
}
if (cmd_len > 6) {
new_assert_states |= (sens->assert_states | (cmd[6] << 8));
}
break;
case 3: /* mask off */
if (cmd_len > 5) {
new_assert_states = (sens->assert_states & cmd[5]);
change_assert = true;
}
if (cmd_len > 6) {
new_assert_states |= (sens->assert_states & (cmd[6] << 8));
}
break;
}
if (change_assert && (new_assert_states == sens->assert_states)) {
change_assert = false;
}
if (cmd_len > 9) {
evd1 = cmd[9];
}
if (cmd_len > 10) {
evd2 = cmd[10];
}
if (cmd_len > 11) {
evd3 = cmd[11];
}
/* [7:6] Event Data Bytes operation */
switch ((cmd[3] >> 6) & 0x3) {
case 0: /*
* Dont use Event Data bytes from this command. BMC will
* generate it's own Event Data bytes based on its sensor
* implementation.
*/
evd1 = evd2 = evd3 = 0x0;
do_gen_event = SENSOR_GEN_EVENT_BMC;
break;
case 1: /*
* Write given values to event data bytes including bits
* [3:0] Event Data 1.
*/
do_gen_event = SENSOR_GEN_EVENT_DATA;
break;
case 2: /*
* Write given values to event data bytes excluding bits
* [3:0] Event Data 1.
*/
evd1 &= 0xf0;
do_gen_event = SENSOR_GEN_EVENT_DATA;
break;
case 3:
rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
return;
}
/*
* Event Data Bytes operation and parameter are inconsistent. The
* Specs are not clear on that topic but generating an error seems
* correct.
*/
if (do_gen_event == SENSOR_GEN_EVENT_DATA && cmd_len < 10) {
rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
return;
}
/* commit values */
if (change_reading) {
sens->reading = new_reading;
}
if (change_assert) {
sens->assert_states = new_assert_states;
}
if (change_deassert) {
sens->deassert_states = new_deassert_states;
}
/* TODO: handle threshold sensor */
if (!IPMI_SENSOR_IS_DISCRETE(sens)) {
return;
}
switch (do_gen_event) {
case SENSOR_GEN_EVENT_DATA: {
unsigned int bit = evd1 & 0xf;
uint16_t mask = (1 << bit);
if (sens->assert_states & mask & sens->assert_enable) {
gen_event(ibs, cmd[2], 0, evd1, evd2, evd3);
}
if (sens->deassert_states & mask & sens->deassert_enable) {
gen_event(ibs, cmd[2], 1, evd1, evd2, evd3);
}
break;
}
case SENSOR_GEN_EVENT_BMC:
/*
* TODO: generate event and event data bytes depending on the
* sensor
*/
break;
case SENSOR_GEN_EVENT_NONE:
break;
}
}
static const IPMICmdHandler chassis_cmds[] = {
[IPMI_CMD_GET_CHASSIS_CAPABILITIES] = { chassis_capabilities },
@ -1768,6 +1990,7 @@ static const IPMICmdHandler sensor_event_cmds[] = {
[IPMI_CMD_GET_SENSOR_READING] = { get_sensor_reading, 3 },
[IPMI_CMD_SET_SENSOR_TYPE] = { set_sensor_type, 5 },
[IPMI_CMD_GET_SENSOR_TYPE] = { get_sensor_type, 3 },
[IPMI_CMD_SET_SENSOR_READING] = { set_sensor_reading, 5 },
};
static const IPMINetfn sensor_event_netfn = {
.cmd_nums = ARRAY_SIZE(sensor_event_cmds),