blob: 766e9dcfc863ed96be729196ef50b04445765012 [file] [log] [blame]
/*
* Copyright (c) 2018 Intel Corporation.
* Copyright (c) 2018-present Facebook.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <ipmid/api.h>
#include <phosphor-logging/log.hpp>
#include <cmath>
#include <iostream>
namespace ipmi
{
static constexpr int16_t maxInt10 = 0x1FF;
static constexpr int16_t minInt10 = -0x200;
static constexpr int8_t maxInt4 = 7;
static constexpr int8_t minInt4 = -8;
enum class SensorUnits : uint8_t
{
unspecified = 0x0,
degreesC = 0x1,
volts = 0x4,
amps = 0x5,
watts = 0x6,
rpm = 0x12,
};
enum class SensorTypeCodes : uint8_t
{
reserved = 0x0,
temperature = 0x1,
voltage = 0x2,
current = 0x3,
fan = 0x4,
other = 0xB,
};
struct CmpStrVersion
{
bool operator()(std::string a, std::string b) const
{
return strverscmp(a.c_str(), b.c_str()) < 0;
}
};
using SensorSubTree = boost::container::flat_map<
std::string,
boost::container::flat_map<std::string, std::vector<std::string>>,
CmpStrVersion>;
inline static bool getSensorSubtree(SensorSubTree& subtree)
{
sd_bus* bus = NULL;
int ret = sd_bus_default_system(&bus);
if (ret < 0)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"Failed to connect to system bus",
phosphor::logging::entry("ERRNO=0x%X", -ret));
sd_bus_unref(bus);
return false;
}
sdbusplus::bus_t dbus(bus);
auto mapperCall =
dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTree");
static constexpr const auto depth = 2;
static constexpr std::array<const char*, 3> interfaces = {
"xyz.openbmc_project.Sensor.Value",
"xyz.openbmc_project.Sensor.Threshold.Warning",
"xyz.openbmc_project.Sensor.Threshold.Critical"};
mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces);
try
{
auto mapperReply = dbus.call(mapperCall);
subtree.clear();
mapperReply.read(subtree);
}
catch (sdbusplus::exception_t& e)
{
phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
return false;
}
return true;
}
// Specify the comparison required to sort and find char* map objects
struct CmpStr
{
bool operator()(const char* a, const char* b) const
{
return std::strcmp(a, b) < 0;
}
};
const static boost::container::flat_map<const char*, SensorUnits, CmpStr>
sensorUnits{{{"temperature", SensorUnits::degreesC},
{"voltage", SensorUnits::volts},
{"current", SensorUnits::amps},
{"fan_tach", SensorUnits::rpm},
{"power", SensorUnits::watts}}};
const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr>
sensorTypes{{{"temperature", SensorTypeCodes::temperature},
{"voltage", SensorTypeCodes::voltage},
{"current", SensorTypeCodes::current},
{"fan_tach", SensorTypeCodes::fan},
{"fan_pwm", SensorTypeCodes::fan},
{"power", SensorTypeCodes::other}}};
inline static std::string getSensorTypeStringFromPath(const std::string& path)
{
// get sensor type string from path, path is defined as
// /xyz/openbmc_project/sensors/<type>/label
size_t typeEnd = path.rfind("/");
if (typeEnd == std::string::npos)
{
return path;
}
size_t typeStart = path.rfind("/", typeEnd - 1);
if (typeStart == std::string::npos)
{
return path;
}
// Start at the character after the '/'
typeStart++;
return path.substr(typeStart, typeEnd - typeStart);
}
inline static uint8_t getSensorTypeFromPath(const std::string& path)
{
uint8_t sensorType = 0;
std::string type = getSensorTypeStringFromPath(path);
auto findSensor = sensorTypes.find(type.c_str());
if (findSensor != sensorTypes.end())
{
sensorType = static_cast<uint8_t>(findSensor->second);
} // else default 0x0 RESERVED
return sensorType;
}
inline static uint8_t getSensorEventTypeFromPath(const std::string&)
{
// TODO: Add support for additional reading types as needed
return 0x1; // reading type = threshold
}
static inline bool getSensorAttributes(
const double max, const double min, int16_t& mValue, int8_t& rExp,
int16_t& bValue, int8_t& bExp, bool& bSigned)
{
// computing y = (10^rRexp) * (Mx + (B*(10^Bexp)))
// check for 0, assume always positive
double mDouble;
double bDouble;
if (max <= min)
{
phosphor::logging::log<phosphor::logging::level::DEBUG>(
"getSensorAttributes: Max must be greater than min");
return false;
}
mDouble = (max - min) / 0xFF;
if (min < 0)
{
bSigned = true;
bDouble = floor(0.5 + ((max + min) / 2));
}
else
{
bSigned = false;
bDouble = min;
}
rExp = 0;
// M too big for 10 bit variable
while (mDouble > maxInt10)
{
if (rExp >= maxInt4)
{
phosphor::logging::log<phosphor::logging::level::DEBUG>(
"rExp Too big, Max and Min range too far",
phosphor::logging::entry("REXP=%d", rExp));
return false;
}
mDouble /= 10;
rExp++;
}
// M too small, loop until we lose less than 1 eight bit count of precision
while (((mDouble - floor(mDouble)) / mDouble) > (1.0 / 255))
{
if (rExp <= minInt4)
{
phosphor::logging::log<phosphor::logging::level::DEBUG>(
"rExp Too Small, Max and Min range too close");
return false;
}
// check to see if we reached the limit of where we can adjust back the
// B value
if (bDouble / std::pow(10, rExp + minInt4 - 1) > bDouble)
{
if (mDouble < 1.0)
{
phosphor::logging::log<phosphor::logging::level::DEBUG>(
"Could not find mValue and B value with enough "
"precision.");
return false;
}
break;
}
// can't multiply M any more, max precision reached
else if (mDouble * 10 > maxInt10)
{
break;
}
mDouble *= 10;
rExp--;
}
bDouble /= std::pow(10, rExp);
bExp = 0;
// B too big for 10 bit variable
while (bDouble > maxInt10 || bDouble < minInt10)
{
if (bExp >= maxInt4)
{
phosphor::logging::log<phosphor::logging::level::DEBUG>(
"bExp Too Big, Max and Min range need to be adjusted");
return false;
}
bDouble /= 10;
bExp++;
}
while (((fabs(bDouble) - floor(fabs(bDouble))) / fabs(bDouble)) >
(1.0 / 255))
{
if (bExp <= minInt4)
{
phosphor::logging::log<phosphor::logging::level::DEBUG>(
"bExp Too Small, Max and Min range need to be adjusted");
return false;
}
bDouble *= 10;
bExp -= 1;
}
mValue = static_cast<int16_t>(mDouble) & maxInt10;
bValue = static_cast<int16_t>(bDouble) & maxInt10;
return true;
}
static inline uint8_t scaleIPMIValueFromDouble(
const double value, const uint16_t mValue, const int8_t rExp,
const uint16_t bValue, const int8_t bExp, const bool bSigned)
{
uint32_t scaledValue =
(value - (bValue * std::pow(10, bExp) * std::pow(10, rExp))) /
(mValue * std::pow(10, rExp));
if (scaledValue > std::numeric_limits<uint8_t>::max() ||
scaledValue < std::numeric_limits<uint8_t>::lowest())
{
throw std::out_of_range("Value out of range");
}
if (bSigned)
{
return static_cast<int8_t>(scaledValue);
}
else
{
return static_cast<uint8_t>(scaledValue);
}
}
static inline uint8_t getScaledIPMIValue(const double value, const double max,
const double min)
{
int16_t mValue = 0;
int8_t rExp = 0;
int16_t bValue = 0;
int8_t bExp = 0;
bool bSigned = 0;
bool result = 0;
result = getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned);
if (!result)
{
throw std::runtime_error("Illegal sensor attributes");
}
return scaleIPMIValueFromDouble(value, mValue, rExp, bValue, bExp, bSigned);
}
} // namespace ipmi