/*
* ipmi_sensor.cpp
*
* Copyright (c) 2003,2004 by FORCE Computers
* Copyright (c) 2005 by ESO Technologies.
*
* Note that this file is based on parts of OpenIPMI
* written by Corey Minyard <minyard@mvista.com>
* of MontaVista Software. Corey's code was helpful
* and many thanks go to him. He gave the permission
* to use this code in OpenHPI under BSD license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This
* file and program are licensed under a BSD style license. See
* the Copying file included with the OpenHPI distribution for
* full licensing terms.
*
* Authors:
* Thomas Kanngieser <thomas.kanngieser@fci.com>
* Pierre Sangouard <psangouard@eso-tech.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <glib.h>
#include <math.h>
#include "ipmi_domain.h"
#include "ipmi_mc.h"
#include "ipmi_sensor.h"
#include "ipmi_entity.h"
#include "ipmi_utils.h"
#include "ipmi_text_buffer.h"
static const char *sensor_types[] =
{
"Unspecified",
"Temperature",
"Voltage",
"Current",
"Fan",
"PhysicalSecurity",
"PlatformSecurity",
"Processor",
"PowerSupply",
"PowerUnit",
"CoolingDevice",
"OtherUnitsBasedSensor",
"Memory",
"DriveSlot",
"PowerMemoryResize",
"SystemFirmwareProgress",
"EventLoggingDisabled",
"Watchdog1",
"SystemEvent",
"CriticalInterrupt",
"Button",
"ModuleBoard",
"MicrocontrollerCoprocessor",
"AddInCard",
"Chassis",
"ChipSet",
"OtherFru",
"CableInterconnect",
"Terminator",
"SystemBootInitiated",
"BootError",
"OsBoot",
"OsCriticalStop",
"SlotConnector",
"SystemAcpiPowerState",
"Watchdog2",
"PlatformAlert",
"EntityPresense",
"MonitorAsicIc",
"Lan",
"ManagementSubsystemHealth",
"Battery"
};
const char *
IpmiSensorTypeToString( tIpmiSensorType val )
{
if ( val > eIpmiSensorTypeBattery )
{
if ( val == eIpmiSensorTypeAtcaHotSwap )
return "AtcaHotswap";
if ( val == eIpmiSensorTypeAtcaIpmb )
return "AtcaIpmb";
return "Invalid";
}
return sensor_types[val];
}
static const char *event_support_types[] =
{
"PerState",
"EntireSensor",
"GlobalDisable",
"None",
};
const char *
IpmiEventSupportToString( tIpmiEventSupport val )
{
if ( val > eIpmiEventSupportNone )
return "Invalid";
return event_support_types[val];
}
static const char *event_reading_types[] =
{
"Unspecified",
"Threshold",
"DiscreteUsage",
"DiscreteState",
"DiscretePredictiveFailure",
"DiscreteLimitExceeded",
"DiscretePerformanceMet",
"DiscreteSeverity",
"DiscreteDevicePresense",
"DiscreteDeviceEnable",
"DiscreteAvailability",
"DiscreteRedundancy",
"DiscreteAcpiPower",
};
const char *
IpmiEventReadingTypeToString( tIpmiEventReadingType val )
{
if ( val == eIpmiEventReadingTypeSensorSpecific )
return "SensorSpecific";
if (( val >= eIpmiEventReadingTypeOemFirst )
&& ( val <= eIpmiEventReadingTypeOemLast ))
return "Oem";
if ( val > eIpmiEventReadingTypeDiscreteAcpiPower )
return "Invalid";
return event_reading_types[val];
}
cIpmiSensor::cIpmiSensor( cIpmiMc *mc )
: cIpmiRdr( mc, SAHPI_SENSOR_RDR ), m_source_mc( 0 ),
m_destroyed( false ),
m_use_count( 0 ),
m_owner( 0 ), m_channel( 0 ),
m_num( 0 ),
m_sensor_init_scanning( false ),
m_sensor_init_events( false ),
m_sensor_init_type( false ),
m_sensor_init_pu_events( false ),
m_sensor_init_pu_scanning( false ),
m_ignore_if_no_entity( false ),
m_supports_auto_rearm( false ),
m_assertion_event_mask( 0 ),
m_deassertion_event_mask( 0 ),
m_reading_mask( 0 ),
m_enabled( SAHPI_TRUE ),
m_event_support( eIpmiEventSupportPerState ),
m_sensor_type( eIpmiSensorTypeInvalid ),
m_event_reading_type( eIpmiEventReadingTypeInvalid ),
m_oem( 0 ),
m_sensor_type_string( 0 ),
m_event_reading_type_string( 0 ),
m_rate_unit_string( 0 ),
m_base_unit_string( 0 ),
m_modifier_unit_string( 0 ),
m_sdr( 0 )
{
}
cIpmiSensor::~cIpmiSensor()
{
}
bool
cIpmiSensor::GetDataFromSdr( cIpmiMc *mc, cIpmiSdr *sdr )
{
m_use_count = 1;
m_destroyed = false;
m_mc = mc;
m_source_mc = mc;
m_sdr_type = sdr->m_data[3]; /*or sdr->m_type; */
m_owner = sdr->m_data[5];
m_channel = sdr->m_data[6] >> 4;
m_lun = sdr->m_data[6] & 0x03;
m_num = sdr->m_data[7];
m_sensor_init_scanning = (sdr->m_data[10] >> 6) & 1;
m_sensor_init_events = (sdr->m_data[10] >> 5) & 1;
if ( m_sensor_init_events )
m_events_enabled = SAHPI_TRUE;
else
m_events_enabled = SAHPI_FALSE;
m_sensor_init_type = (sdr->m_data[10] >> 2) & 1;
m_sensor_init_pu_events = (sdr->m_data[10] >> 1) & 1;
m_sensor_init_pu_scanning = (sdr->m_data[10] >> 0) & 1;
m_ignore_if_no_entity = (sdr->m_data[11] >> 7) & 1;
m_supports_auto_rearm = (sdr->m_data[11] >> 6) & 1;
m_event_support = (tIpmiEventSupport)(sdr->m_data[11] & 3);
m_sensor_type = (tIpmiSensorType)sdr->m_data[12];
m_event_reading_type = (tIpmiEventReadingType)(sdr->m_data[13] & 0x7f);
m_oem = sdr->m_data[46];
IdString().SetIpmi( sdr->m_data+47 );
if ( m_owner != mc->GetAddress() )
stdlog << "WARNING : SDR " << sdr->m_record_id << " sensor " << m_num << " slave address " << m_owner << " NOT equal to MC slave address " << (unsigned char)mc->GetAddress() << "\n";
if ( m_channel != mc->GetChannel() )
stdlog << "WARNING : SDR " << sdr->m_record_id << " sensor " << m_num << " channel " << m_channel << " NOT equal to MC channel " << (unsigned short)mc->GetChannel() << "\n";
return true;
}
void
cIpmiSensor::HandleNew( cIpmiDomain *domain )
{
m_sensor_type_string = IpmiSensorTypeToString( m_sensor_type );
m_event_reading_type_string = IpmiEventReadingTypeToString( m_event_reading_type );
}
bool
cIpmiSensor::Cmp( const cIpmiSensor &s2 ) const
{
if ( m_entity_path != s2.m_entity_path )
return false;
if ( m_sensor_init_scanning != s2.m_sensor_init_scanning )
return false;
if ( m_sensor_init_events != s2.m_sensor_init_events )
return false;
if ( m_sensor_init_type != s2.m_sensor_init_type )
return false;
if ( m_sensor_init_pu_events != s2.m_sensor_init_pu_events )
return false;
if ( m_sensor_init_pu_scanning != s2.m_sensor_init_pu_scanning )
return false;
if ( m_ignore_if_no_entity != s2.m_ignore_if_no_entity )
return false;
if ( m_supports_auto_rearm != s2.m_supports_auto_rearm )
return false;
if ( m_event_support != s2.m_event_support )
return false;
if ( m_sensor_type != s2.m_sensor_type )
return false;
if ( m_event_reading_type != s2.m_event_reading_type )
return false;
if ( m_oem != s2.m_oem )
return false;
if ( IdString() != s2.IdString() )
return false;
return true;
}
void
cIpmiSensor::Dump( cIpmiLog &dump ) const
{
char str[256];
IdString().GetAscii( str, 256 );
dump << "Sensor: " << m_num << " " << str << "\n";
}
bool
cIpmiSensor::CreateRdr( SaHpiRptEntryT &resource, SaHpiRdrT &rdr )
{
if ( cIpmiRdr::CreateRdr( resource, rdr ) == false )
return false;
// update resource
resource.ResourceCapabilities |= SAHPI_CAPABILITY_RDR|SAHPI_CAPABILITY_SENSOR;
// sensor record
SaHpiSensorRecT &rec = rdr.RdrTypeUnion.SensorRec;
int v = Resource()->CreateSensorNum( Num() );
if ( v == -1 )
{
stdlog << "too many sensors (> 255) for a resource !\n";
assert( v != -1 );
return false;
}
/* IPMI sensors have a unique index, a number, and a slave addr.
* We need to save the real number and slave addr so that FindRdr
* will always work properly. */
SetSNum(Num());
SetSa(m_owner);
m_virtual_num = v;
rec.Num = v;
rec.Type = HpiSensorType(SensorType());
rec.Category = HpiEventCategory(EventReadingType());
rec.Oem = GetOem();
switch( EventSupport() )
{
case eIpmiEventSupportPerState:
m_event_control = SAHPI_SEC_PER_EVENT;
break;
case eIpmiEventSupportEntireSensor:
case eIpmiEventSupportGlobalEnable:
m_event_control = SAHPI_SEC_READ_ONLY_MASKS;
break;
case eIpmiEventSupportNone:
m_event_control = SAHPI_SEC_READ_ONLY;
break;
}
rec.Events = m_reading_mask;
rec.EventCtrl = m_event_control;
rec.EnableCtrl = SAHPI_TRUE;
return true;
}
SaErrorT
cIpmiSensor::GetSensorData( cIpmiMsg &rsp )
{
unsigned char sa, chan = 0;
unsigned char n = m_num;
if (m_channel != 0) { /* Get sensor reading for ME/NM has diff channel */
chan = m_channel;
sa = m_owner;
} else sa = dIpmiBmcSlaveAddr;
cIpmiMsg msg( eIpmiNetfnSensorEvent, eIpmiCmdGetSensorReading, 1,&n, sa,chan);
if (m_sdr_type == eSdrTypeEventOnlySensorRecord)
{
/* EventOnly sensors do not support readings, so just return 0 */
memset(rsp.m_data,0,5);
rsp.m_data_len = 5;
return SA_OK;
}
SaErrorT rv = Resource()->SendCommandReadLock( this, msg, rsp, m_lun );
if ( rv != SA_OK )
{
stdlog << "IPMI error getting states: " << rv << " \n";
return rv;
}
if ( rsp.m_data[0] != 0 )
{
stdlog << "IPMI error getting " << m_num << " reading: " << rsp.m_data[0] << " !\n";
return SA_ERR_HPI_INVALID_DATA;
}
if ( rsp.m_data_len < 4 )
{
stdlog << "IPMI error getting reading: data too small "
<< rsp.m_data_len << " !\n";
return SA_ERR_HPI_INVALID_DATA;
}
if ( (m_sdr_type == eSdrTypeFullSensorRecord)
&& ((rsp.m_data[2] & 0x20) != 0) )
{
stdlog << "IPMI sensor " << m_num << " is in Init state\n";
return SA_ERR_HPI_INVALID_REQUEST;
}
return SA_OK;
}
SaErrorT
cIpmiSensor::GetEnable( SaHpiBoolT &enable )
{
enable = m_enabled;
return SA_OK;
}
SaErrorT
cIpmiSensor::SetEnable( const SaHpiBoolT &enable )
{
if (m_enabled == enable)
return SA_OK;
m_enabled = enable;
CreateEnableChangeEvent();
return SA_OK;
}
SaErrorT
cIpmiSensor::GetEventEnables( SaHpiBoolT &enables )
{
SaErrorT rv = GetEventEnableHw( m_events_enabled );
enables = m_events_enabled;
return rv;
}
SaErrorT
cIpmiSensor::SetEventEnables( const SaHpiBoolT &enables )
{
if ( m_event_control == SAHPI_SEC_READ_ONLY )
return SA_ERR_HPI_READ_ONLY;
if ( m_events_enabled == enables )
return SA_OK;
m_events_enabled = enables;
SaErrorT rv = SetEventEnableHw( m_events_enabled );
if ( rv == SA_OK )
CreateEnableChangeEvent();
return rv;
}
SaErrorT
cIpmiSensor::GetEventMasks( SaHpiEventStateT &AssertEventMask,
SaHpiEventStateT &DeassertEventMask
)
{
SaErrorT rv = GetEventMasksHw( m_current_hpi_assert_mask,
m_current_hpi_deassert_mask
);
stdlog << "GetEventMasks sensor " << m_num << " assert " << m_current_hpi_assert_mask << " deassert " << m_current_hpi_deassert_mask << "\n";
if (&AssertEventMask) AssertEventMask = m_current_hpi_assert_mask;
if (&DeassertEventMask) DeassertEventMask = m_current_hpi_deassert_mask;
return rv;
}
SaErrorT
cIpmiSensor::SetEventMasks( const SaHpiSensorEventMaskActionT &act,
SaHpiEventStateT &AssertEventMask,
SaHpiEventStateT &DeassertEventMask
)
{
if ( m_event_control != SAHPI_SEC_PER_EVENT )
return SA_ERR_HPI_READ_ONLY;
if ( AssertEventMask == SAHPI_ALL_EVENT_STATES )
AssertEventMask = m_hpi_assert_mask;
if ( DeassertEventMask == SAHPI_ALL_EVENT_STATES )
DeassertEventMask = m_hpi_deassert_mask;
if ( act == SAHPI_SENS_ADD_EVENTS_TO_MASKS )
{
if ((( AssertEventMask & ~m_hpi_assert_mask ) != 0 )
|| (( DeassertEventMask & ~m_hpi_deassert_mask ) != 0 ))
return SA_ERR_HPI_INVALID_DATA;
}
SaHpiEventStateT save_assert_mask = m_current_hpi_assert_mask;
SaHpiEventStateT save_deassert_mask = m_current_hpi_deassert_mask;
if ( act == SAHPI_SENS_ADD_EVENTS_TO_MASKS )
{
m_current_hpi_assert_mask |= AssertEventMask;
m_current_hpi_deassert_mask |= DeassertEventMask;
}
else if ( act == SAHPI_SENS_REMOVE_EVENTS_FROM_MASKS )
{
m_current_hpi_assert_mask &= (AssertEventMask ^ SAHPI_ALL_EVENT_STATES);
m_current_hpi_deassert_mask &= (DeassertEventMask ^ SAHPI_ALL_EVENT_STATES);
}
else
return SA_ERR_HPI_INVALID_PARAMS;
stdlog << "SetEventMasks sensor " << m_num << " assert " << m_current_hpi_assert_mask << " deassert " << m_current_hpi_deassert_mask << "\n";
if (( save_assert_mask == m_current_hpi_assert_mask )
&& ( save_deassert_mask == m_current_hpi_deassert_mask ))
return SA_OK;
SaErrorT rv = SetEventMasksHw( m_current_hpi_assert_mask,
m_current_hpi_deassert_mask
);
if ( rv == SA_OK )
CreateEnableChangeEvent();
return rv;
}
SaErrorT
cIpmiSensor::GetEventEnableHw( SaHpiBoolT &enables )
{
cIpmiMsg msg( eIpmiNetfnSensorEvent, eIpmiCmdGetSensorEventEnable );
msg.m_data_len = 1;
msg.m_data[0] = m_num;
cIpmiMsg rsp;
stdlog << "get event enables command for sensor : " << m_num << " !\n";
SaErrorT rv = Resource()->SendCommandReadLock( this, msg, rsp, m_lun );
if ( rv != SA_OK )
{
stdlog << "Error sending get event enables command: " << rv << " !\n";
return rv;
}
if ( rsp.m_data[0] )
{
stdlog << "IPMI error getting sensor enables: " << rsp.m_data[0] << " !\n";
return SA_ERR_HPI_INVALID_CMD;
}
enables = (rsp.m_data[1] & 0x80) ? SAHPI_TRUE : SAHPI_FALSE;
return SA_OK;
}
SaErrorT
cIpmiSensor::SetEventEnableHw( const SaHpiBoolT &enables )
{
cIpmiMsg msg;
msg.m_netfn = eIpmiNetfnSensorEvent;
msg.m_cmd = eIpmiCmdSetSensorEventEnable;
msg.m_data[0] = m_num;
msg.m_data[1] = ((m_events_enabled == SAHPI_TRUE) ? 0x80 : 0) | (1<<6);
msg.m_data_len = 2;
cIpmiMsg rsp;
stdlog << "set event enables command for sensor : " << m_num << " !\n";
SaErrorT rv = Resource()->SendCommandReadLock( this, msg, rsp, m_lun );
if ( rv != SA_OK )
{
stdlog << "Error sending set event enables command: " << rv << " !\n";
return rv;
}
if ( rsp.m_data[0] )
{
stdlog << "IPMI error setting sensor enables: " << rsp.m_data[0] << " !\n";
return SA_ERR_HPI_INVALID_CMD;
}
return SA_OK;
}
SaErrorT
cIpmiSensor::GetEventMasksHw( cIpmiMsg &rsp )
{
cIpmiMsg msg( eIpmiNetfnSensorEvent, eIpmiCmdGetSensorEventEnable );
msg.m_data_len = 1;
msg.m_data[0] = m_num;
stdlog << "get event enables command for sensor : " << m_num << " !\n";
SaErrorT rv = Resource()->SendCommandReadLock( this, msg, rsp, m_lun );
if ( rv != SA_OK )
{
stdlog << "Error sending get event enables command: " << rv << " !\n";
return rv;
}
if ( rsp.m_data[0] )
{
stdlog << "IPMI error getting sensor enables: " << rsp.m_data[0] << " !\n";
return SA_ERR_HPI_INVALID_CMD;
}
return SA_OK;
}
SaErrorT
cIpmiSensor::SetEventMasksHw( cIpmiMsg &msg,
bool evt_enable)
{
msg.m_netfn = eIpmiNetfnSensorEvent;
msg.m_cmd = eIpmiCmdSetSensorEventEnable;
msg.m_data[0] = m_num;
msg.m_data[1] = ((m_events_enabled == SAHPI_TRUE) ? 0x80 : 0) | (1<<6);
if ( m_event_support == eIpmiEventSupportEntireSensor )
{
// We can only turn on/off the entire sensor, just pass the
// status to the sensor.
msg.m_data_len = 2;
}
else
{
if ( evt_enable == true )
msg.m_data[1] |= (1<<4); // enable selected event messages
else
msg.m_data[1] |= (1<<5); // disable selected event messages
msg.m_data_len = 6;
}
cIpmiMsg rsp;
stdlog << "set event enables command for sensor : " << m_num << " !\n";
SaErrorT rv = Resource()->SendCommandReadLock( this, msg, rsp, m_lun );
if ( rv != SA_OK )
{
stdlog << "Error sending set event enables command: " << rv << " !\n";
return rv;
}
if ( rsp.m_data[0] )
{
stdlog << "IPMI error setting sensor enables: " << rsp.m_data[0] << " !\n";
return SA_ERR_HPI_INVALID_CMD;
}
return SA_OK;
}
SaHpiSensorTypeT
cIpmiSensor::HpiSensorType(tIpmiSensorType sensor_type)
{
if ( sensor_type >= eIpmiSensorTypeOemFirst)
return SAHPI_OEM_SENSOR;
return (SaHpiSensorTypeT)sensor_type;
}
SaHpiEventCategoryT
cIpmiSensor::HpiEventCategory(tIpmiEventReadingType reading_type)
{
if ( reading_type == eIpmiEventReadingTypeSensorSpecific )
return SAHPI_EC_SENSOR_SPECIFIC;
if (( reading_type >= eIpmiEventReadingTypeOemFirst )
&& ( reading_type <= eIpmiEventReadingTypeOemLast ))
return SAHPI_EC_GENERIC;
return (SaHpiEventCategoryT)reading_type;
}
void
cIpmiSensor::CreateEnableChangeEvent()
{
cIpmiResource *res = Resource();
if( !res )
{
stdlog << "CreateEnableChangeEvent: No resource !\n";
return;
}
oh_event *e = (oh_event *)g_malloc0( sizeof( struct oh_event ) );
e->event.EventType = SAHPI_ET_SENSOR_ENABLE_CHANGE;
SaHpiRptEntryT *rptentry = oh_get_resource_by_id( res->Domain()->GetHandler()->rptcache, res->m_resource_id );
SaHpiRdrT *rdrentry = oh_get_rdr_by_id( res->Domain()->GetHandler()->rptcache, res->m_resource_id, m_record_id );
if ( rptentry )
e->resource = *rptentry;
else
e->resource.ResourceCapabilities = 0;
if ( rdrentry )
e->rdrs = g_slist_append(e->rdrs, g_memdup(rdrentry, sizeof(SaHpiRdrT)));
else
e->rdrs = NULL;
// hpi event
e->event.Source = res->m_resource_id;
e->event.EventType = SAHPI_ET_SENSOR_ENABLE_CHANGE;
e->event.Severity = SAHPI_INFORMATIONAL;
oh_gettimeofday(&e->event.Timestamp);
// sensor enable event
SaHpiSensorEnableChangeEventT *se = &e->event.EventDataUnion.SensorEnableChangeEvent;
se->SensorNum = m_num;
se->SensorType = HpiSensorType(SensorType());
se->EventCategory = HpiEventCategory(EventReadingType());
se->SensorEnable = m_enabled;
se->SensorEventEnable = m_events_enabled;
se->AssertEventMask = m_current_hpi_assert_mask;
se->DeassertEventMask = m_current_hpi_deassert_mask;
stdlog << "cIpmiSensor::CreateEnableChangeEvent OH_ET_HPI Event enable change resource " << res->m_resource_id << "\n";
m_mc->Domain()->AddHpiEvent( e );
return;
}
SaErrorT
cIpmiSensor::CreateEvent( cIpmiEvent *event, SaHpiEventT &h )
{
memset( &h, 0, sizeof( SaHpiEventT ) );
cIpmiResource *res = Resource();
if( !res )
{
stdlog << "CreateEvent: No resource !\n";
return SA_ERR_HPI_NOT_PRESENT;
}
h.Source = res->m_resource_id;
h.EventType = SAHPI_ET_SENSOR;
h.Timestamp = (SaHpiTimeT)IpmiGetUint32( event->m_data );
if ( h.Timestamp == 0 )
h.Timestamp = SAHPI_TIME_UNSPECIFIED;
else
h.Timestamp *= 1000000000;
// sensor event
SaHpiSensorEventT &s = h.EventDataUnion.SensorEvent;
s.SensorNum = m_num;
s.SensorType = HpiSensorType((tIpmiSensorType)event->m_data[7]);
tIpmiEventReadingType reading_type = (tIpmiEventReadingType)(event->m_data[9] & 0x7f);
s.EventCategory = HpiEventCategory(reading_type);
return SA_OK;
}
void
cIpmiSensor::HandleEvent( cIpmiEvent *event )
{
cIpmiResource *res = Resource();
if( !res )
{
stdlog << "HandleEvent: No resource !\n";
return;
}
SaHpiRptEntryT *rptentry;
SaHpiRdrT *rdrentry;
// Sensor disabled -> ignore event
if (m_enabled == SAHPI_FALSE)
{
stdlog << "reading event : Ignore (Sensor disabled).\n";
return;
}
stdlog << "reading event.\n";
oh_event *e = (oh_event *)g_malloc0( sizeof( struct oh_event ) );
rptentry = oh_get_resource_by_id( res->Domain()->GetHandler()->rptcache, res->m_resource_id );
rdrentry = oh_get_rdr_by_id( res->Domain()->GetHandler()->rptcache, res->m_resource_id, m_record_id );
if ( rptentry )
e->resource = *rptentry;
else
e->resource.ResourceCapabilities = 0;
if ( rdrentry )
e->rdrs = g_slist_append(e->rdrs, g_memdup(rdrentry, sizeof(SaHpiRdrT)));
else
e->rdrs = NULL;
// hpi event
SaHpiEventT &he = e->event;
int rv = CreateEvent( event, he );
if ( rv != SA_OK )
return;
stdlog << "cIpmiSensor::HandleEvent OH_ET_HPI Event resource " << res->m_resource_id << "\n";
m_mc->Domain()->AddHpiEvent( e );
}