1e7d23d0eSVijay Khemka /* 2e7d23d0eSVijay Khemka * Copyright (c) 2018 Intel Corporation. 3e7d23d0eSVijay Khemka * Copyright (c) 2018-present Facebook. 4e7d23d0eSVijay Khemka * 5e7d23d0eSVijay Khemka * Licensed under the Apache License, Version 2.0 (the "License"); 6e7d23d0eSVijay Khemka * you may not use this file except in compliance with the License. 7e7d23d0eSVijay Khemka * You may obtain a copy of the License at 8e7d23d0eSVijay Khemka * 9e7d23d0eSVijay Khemka * http://www.apache.org/licenses/LICENSE-2.0 10e7d23d0eSVijay Khemka * 11e7d23d0eSVijay Khemka * Unless required by applicable law or agreed to in writing, software 12e7d23d0eSVijay Khemka * distributed under the License is distributed on an "AS IS" BASIS, 13e7d23d0eSVijay Khemka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14e7d23d0eSVijay Khemka * See the License for the specific language governing permissions and 15e7d23d0eSVijay Khemka * limitations under the License. 16e7d23d0eSVijay Khemka */ 17e7d23d0eSVijay Khemka 18e7d23d0eSVijay Khemka #pragma once 19e7d23d0eSVijay Khemka #include <ipmid/api.h> 20e7d23d0eSVijay Khemka 21*63c99be4SVijay Khemka #include <phosphor-logging/log.hpp> 22*63c99be4SVijay Khemka 23e7d23d0eSVijay Khemka #include <cmath> 24e7d23d0eSVijay Khemka #include <iostream> 25e7d23d0eSVijay Khemka 26e7d23d0eSVijay Khemka namespace ipmi 27e7d23d0eSVijay Khemka { 28e7d23d0eSVijay Khemka 29e7d23d0eSVijay Khemka static constexpr int16_t maxInt10 = 0x1FF; 30e7d23d0eSVijay Khemka static constexpr int16_t minInt10 = -0x200; 31e7d23d0eSVijay Khemka static constexpr int8_t maxInt4 = 7; 32e7d23d0eSVijay Khemka static constexpr int8_t minInt4 = -8; 33e7d23d0eSVijay Khemka 34e7d23d0eSVijay Khemka enum class SensorUnits : uint8_t 35e7d23d0eSVijay Khemka { 36e7d23d0eSVijay Khemka unspecified = 0x0, 37e7d23d0eSVijay Khemka degreesC = 0x1, 38e7d23d0eSVijay Khemka volts = 0x4, 39e7d23d0eSVijay Khemka amps = 0x5, 40e7d23d0eSVijay Khemka watts = 0x6, 41e7d23d0eSVijay Khemka rpm = 0x12, 42e7d23d0eSVijay Khemka }; 43e7d23d0eSVijay Khemka 44e7d23d0eSVijay Khemka enum class SensorTypeCodes : uint8_t 45e7d23d0eSVijay Khemka { 46e7d23d0eSVijay Khemka reserved = 0x0, 47e7d23d0eSVijay Khemka temperature = 0x1, 48e7d23d0eSVijay Khemka voltage = 0x2, 49e7d23d0eSVijay Khemka current = 0x3, 50e7d23d0eSVijay Khemka fan = 0x4, 51e7d23d0eSVijay Khemka other = 0xB, 52e7d23d0eSVijay Khemka }; 53e7d23d0eSVijay Khemka 54e7d23d0eSVijay Khemka struct CmpStrVersion 55e7d23d0eSVijay Khemka { 56e7d23d0eSVijay Khemka bool operator()(std::string a, std::string b) const 57e7d23d0eSVijay Khemka { 58e7d23d0eSVijay Khemka return strverscmp(a.c_str(), b.c_str()) < 0; 59e7d23d0eSVijay Khemka } 60e7d23d0eSVijay Khemka }; 61e7d23d0eSVijay Khemka 62e7d23d0eSVijay Khemka using SensorSubTree = boost::container::flat_map< 63e7d23d0eSVijay Khemka std::string, 64e7d23d0eSVijay Khemka boost::container::flat_map<std::string, std::vector<std::string>>, 65e7d23d0eSVijay Khemka CmpStrVersion>; 66e7d23d0eSVijay Khemka 67e7d23d0eSVijay Khemka inline static bool getSensorSubtree(SensorSubTree& subtree) 68e7d23d0eSVijay Khemka { 69e7d23d0eSVijay Khemka sd_bus* bus = NULL; 70e7d23d0eSVijay Khemka int ret = sd_bus_default_system(&bus); 71e7d23d0eSVijay Khemka if (ret < 0) 72e7d23d0eSVijay Khemka { 73e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::ERR>( 74e7d23d0eSVijay Khemka "Failed to connect to system bus", 75e7d23d0eSVijay Khemka phosphor::logging::entry("ERRNO=0x%X", -ret)); 76e7d23d0eSVijay Khemka sd_bus_unref(bus); 77e7d23d0eSVijay Khemka return false; 78e7d23d0eSVijay Khemka } 79e7d23d0eSVijay Khemka sdbusplus::bus::bus dbus(bus); 80e7d23d0eSVijay Khemka auto mapperCall = 81e7d23d0eSVijay Khemka dbus.new_method_call("xyz.openbmc_project.ObjectMapper", 82e7d23d0eSVijay Khemka "/xyz/openbmc_project/object_mapper", 83e7d23d0eSVijay Khemka "xyz.openbmc_project.ObjectMapper", "GetSubTree"); 84e7d23d0eSVijay Khemka static constexpr const auto depth = 2; 85e7d23d0eSVijay Khemka static constexpr std::array<const char*, 3> interfaces = { 86e7d23d0eSVijay Khemka "xyz.openbmc_project.Sensor.Value", 87e7d23d0eSVijay Khemka "xyz.openbmc_project.Sensor.Threshold.Warning", 88e7d23d0eSVijay Khemka "xyz.openbmc_project.Sensor.Threshold.Critical"}; 89e7d23d0eSVijay Khemka mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces); 90e7d23d0eSVijay Khemka 91e7d23d0eSVijay Khemka try 92e7d23d0eSVijay Khemka { 93e7d23d0eSVijay Khemka auto mapperReply = dbus.call(mapperCall); 94e7d23d0eSVijay Khemka subtree.clear(); 95e7d23d0eSVijay Khemka mapperReply.read(subtree); 96e7d23d0eSVijay Khemka } 97e7d23d0eSVijay Khemka catch (sdbusplus::exception_t& e) 98e7d23d0eSVijay Khemka { 99e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 100e7d23d0eSVijay Khemka return false; 101e7d23d0eSVijay Khemka } 102e7d23d0eSVijay Khemka return true; 103e7d23d0eSVijay Khemka } 104e7d23d0eSVijay Khemka 105e7d23d0eSVijay Khemka // Specify the comparison required to sort and find char* map objects 106e7d23d0eSVijay Khemka struct CmpStr 107e7d23d0eSVijay Khemka { 108e7d23d0eSVijay Khemka bool operator()(const char* a, const char* b) const 109e7d23d0eSVijay Khemka { 110e7d23d0eSVijay Khemka return std::strcmp(a, b) < 0; 111e7d23d0eSVijay Khemka } 112e7d23d0eSVijay Khemka }; 113e7d23d0eSVijay Khemka 114e7d23d0eSVijay Khemka const static boost::container::flat_map<const char*, SensorUnits, CmpStr> 115e7d23d0eSVijay Khemka sensorUnits{{{"temperature", SensorUnits::degreesC}, 116e7d23d0eSVijay Khemka {"voltage", SensorUnits::volts}, 117e7d23d0eSVijay Khemka {"current", SensorUnits::amps}, 118e7d23d0eSVijay Khemka {"fan_tach", SensorUnits::rpm}, 119e7d23d0eSVijay Khemka {"power", SensorUnits::watts}}}; 120e7d23d0eSVijay Khemka 121e7d23d0eSVijay Khemka const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr> 122e7d23d0eSVijay Khemka sensorTypes{{{"temperature", SensorTypeCodes::temperature}, 123e7d23d0eSVijay Khemka {"voltage", SensorTypeCodes::voltage}, 124e7d23d0eSVijay Khemka {"current", SensorTypeCodes::current}, 125e7d23d0eSVijay Khemka {"fan_tach", SensorTypeCodes::fan}, 126e7d23d0eSVijay Khemka {"fan_pwm", SensorTypeCodes::fan}, 127e7d23d0eSVijay Khemka {"power", SensorTypeCodes::other}}}; 128e7d23d0eSVijay Khemka 129e7d23d0eSVijay Khemka inline static std::string getSensorTypeStringFromPath(const std::string& path) 130e7d23d0eSVijay Khemka { 131e7d23d0eSVijay Khemka // get sensor type string from path, path is defined as 132e7d23d0eSVijay Khemka // /xyz/openbmc_project/sensors/<type>/label 133e7d23d0eSVijay Khemka size_t typeEnd = path.rfind("/"); 134e7d23d0eSVijay Khemka if (typeEnd == std::string::npos) 135e7d23d0eSVijay Khemka { 136e7d23d0eSVijay Khemka return path; 137e7d23d0eSVijay Khemka } 138e7d23d0eSVijay Khemka size_t typeStart = path.rfind("/", typeEnd - 1); 139e7d23d0eSVijay Khemka if (typeStart == std::string::npos) 140e7d23d0eSVijay Khemka { 141e7d23d0eSVijay Khemka return path; 142e7d23d0eSVijay Khemka } 143e7d23d0eSVijay Khemka // Start at the character after the '/' 144e7d23d0eSVijay Khemka typeStart++; 145e7d23d0eSVijay Khemka return path.substr(typeStart, typeEnd - typeStart); 146e7d23d0eSVijay Khemka } 147e7d23d0eSVijay Khemka 148e7d23d0eSVijay Khemka inline static uint8_t getSensorTypeFromPath(const std::string& path) 149e7d23d0eSVijay Khemka { 150e7d23d0eSVijay Khemka uint8_t sensorType = 0; 151e7d23d0eSVijay Khemka std::string type = getSensorTypeStringFromPath(path); 152e7d23d0eSVijay Khemka auto findSensor = sensorTypes.find(type.c_str()); 153e7d23d0eSVijay Khemka if (findSensor != sensorTypes.end()) 154e7d23d0eSVijay Khemka { 155e7d23d0eSVijay Khemka sensorType = static_cast<uint8_t>(findSensor->second); 156e7d23d0eSVijay Khemka } // else default 0x0 RESERVED 157e7d23d0eSVijay Khemka 158e7d23d0eSVijay Khemka return sensorType; 159e7d23d0eSVijay Khemka } 160e7d23d0eSVijay Khemka 161e7d23d0eSVijay Khemka inline static uint8_t getSensorEventTypeFromPath(const std::string& path) 162e7d23d0eSVijay Khemka { 163e7d23d0eSVijay Khemka // TODO: Add support for additional reading types as needed 164e7d23d0eSVijay Khemka return 0x1; // reading type = threshold 165e7d23d0eSVijay Khemka } 166e7d23d0eSVijay Khemka 167e7d23d0eSVijay Khemka static inline bool getSensorAttributes(const double max, const double min, 168e7d23d0eSVijay Khemka int16_t& mValue, int8_t& rExp, 169e7d23d0eSVijay Khemka int16_t& bValue, int8_t& bExp, 170e7d23d0eSVijay Khemka bool& bSigned) 171e7d23d0eSVijay Khemka { 172e7d23d0eSVijay Khemka // computing y = (10^rRexp) * (Mx + (B*(10^Bexp))) 173e7d23d0eSVijay Khemka // check for 0, assume always positive 174e7d23d0eSVijay Khemka double mDouble; 175e7d23d0eSVijay Khemka double bDouble; 176e7d23d0eSVijay Khemka if (max <= min) 177e7d23d0eSVijay Khemka { 178e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::DEBUG>( 179e7d23d0eSVijay Khemka "getSensorAttributes: Max must be greater than min"); 180e7d23d0eSVijay Khemka return false; 181e7d23d0eSVijay Khemka } 182e7d23d0eSVijay Khemka 183e7d23d0eSVijay Khemka mDouble = (max - min) / 0xFF; 184e7d23d0eSVijay Khemka 185e7d23d0eSVijay Khemka if (min < 0) 186e7d23d0eSVijay Khemka { 187e7d23d0eSVijay Khemka bSigned = true; 188e7d23d0eSVijay Khemka bDouble = floor(0.5 + ((max + min) / 2)); 189e7d23d0eSVijay Khemka } 190e7d23d0eSVijay Khemka else 191e7d23d0eSVijay Khemka { 192e7d23d0eSVijay Khemka bSigned = false; 193e7d23d0eSVijay Khemka bDouble = min; 194e7d23d0eSVijay Khemka } 195e7d23d0eSVijay Khemka 196e7d23d0eSVijay Khemka rExp = 0; 197e7d23d0eSVijay Khemka 198e7d23d0eSVijay Khemka // M too big for 10 bit variable 199e7d23d0eSVijay Khemka while (mDouble > maxInt10) 200e7d23d0eSVijay Khemka { 201e7d23d0eSVijay Khemka if (rExp >= maxInt4) 202e7d23d0eSVijay Khemka { 203e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::DEBUG>( 204e7d23d0eSVijay Khemka "rExp Too big, Max and Min range too far", 205e7d23d0eSVijay Khemka phosphor::logging::entry("REXP=%d", rExp)); 206e7d23d0eSVijay Khemka return false; 207e7d23d0eSVijay Khemka } 208e7d23d0eSVijay Khemka mDouble /= 10; 209e7d23d0eSVijay Khemka rExp++; 210e7d23d0eSVijay Khemka } 211e7d23d0eSVijay Khemka 212e7d23d0eSVijay Khemka // M too small, loop until we lose less than 1 eight bit count of precision 213e7d23d0eSVijay Khemka while (((mDouble - floor(mDouble)) / mDouble) > (1.0 / 255)) 214e7d23d0eSVijay Khemka { 215e7d23d0eSVijay Khemka if (rExp <= minInt4) 216e7d23d0eSVijay Khemka { 217e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::DEBUG>( 218e7d23d0eSVijay Khemka "rExp Too Small, Max and Min range too close"); 219e7d23d0eSVijay Khemka return false; 220e7d23d0eSVijay Khemka } 221e7d23d0eSVijay Khemka // check to see if we reached the limit of where we can adjust back the 222e7d23d0eSVijay Khemka // B value 223e7d23d0eSVijay Khemka if (bDouble / std::pow(10, rExp + minInt4 - 1) > bDouble) 224e7d23d0eSVijay Khemka { 225e7d23d0eSVijay Khemka if (mDouble < 1.0) 226e7d23d0eSVijay Khemka { 227e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::DEBUG>( 228e7d23d0eSVijay Khemka "Could not find mValue and B value with enough " 229e7d23d0eSVijay Khemka "precision."); 230e7d23d0eSVijay Khemka return false; 231e7d23d0eSVijay Khemka } 232e7d23d0eSVijay Khemka break; 233e7d23d0eSVijay Khemka } 234e7d23d0eSVijay Khemka // can't multiply M any more, max precision reached 235e7d23d0eSVijay Khemka else if (mDouble * 10 > maxInt10) 236e7d23d0eSVijay Khemka { 237e7d23d0eSVijay Khemka break; 238e7d23d0eSVijay Khemka } 239e7d23d0eSVijay Khemka mDouble *= 10; 240e7d23d0eSVijay Khemka rExp--; 241e7d23d0eSVijay Khemka } 242e7d23d0eSVijay Khemka 243e7d23d0eSVijay Khemka bDouble /= std::pow(10, rExp); 244e7d23d0eSVijay Khemka bExp = 0; 245e7d23d0eSVijay Khemka 246e7d23d0eSVijay Khemka // B too big for 10 bit variable 247e7d23d0eSVijay Khemka while (bDouble > maxInt10 || bDouble < minInt10) 248e7d23d0eSVijay Khemka { 249e7d23d0eSVijay Khemka if (bExp >= maxInt4) 250e7d23d0eSVijay Khemka { 251e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::DEBUG>( 252e7d23d0eSVijay Khemka "bExp Too Big, Max and Min range need to be adjusted"); 253e7d23d0eSVijay Khemka return false; 254e7d23d0eSVijay Khemka } 255e7d23d0eSVijay Khemka bDouble /= 10; 256e7d23d0eSVijay Khemka bExp++; 257e7d23d0eSVijay Khemka } 258e7d23d0eSVijay Khemka 259e7d23d0eSVijay Khemka while (((fabs(bDouble) - floor(fabs(bDouble))) / fabs(bDouble)) > 260e7d23d0eSVijay Khemka (1.0 / 255)) 261e7d23d0eSVijay Khemka { 262e7d23d0eSVijay Khemka if (bExp <= minInt4) 263e7d23d0eSVijay Khemka { 264e7d23d0eSVijay Khemka phosphor::logging::log<phosphor::logging::level::DEBUG>( 265e7d23d0eSVijay Khemka "bExp Too Small, Max and Min range need to be adjusted"); 266e7d23d0eSVijay Khemka return false; 267e7d23d0eSVijay Khemka } 268e7d23d0eSVijay Khemka bDouble *= 10; 269e7d23d0eSVijay Khemka bExp -= 1; 270e7d23d0eSVijay Khemka } 271e7d23d0eSVijay Khemka 272e7d23d0eSVijay Khemka mValue = static_cast<int16_t>(mDouble) & maxInt10; 273e7d23d0eSVijay Khemka bValue = static_cast<int16_t>(bDouble) & maxInt10; 274e7d23d0eSVijay Khemka 275e7d23d0eSVijay Khemka return true; 276e7d23d0eSVijay Khemka } 277e7d23d0eSVijay Khemka 278e7d23d0eSVijay Khemka static inline uint8_t 279e7d23d0eSVijay Khemka scaleIPMIValueFromDouble(const double value, const uint16_t mValue, 280e7d23d0eSVijay Khemka const int8_t rExp, const uint16_t bValue, 281e7d23d0eSVijay Khemka const int8_t bExp, const bool bSigned) 282e7d23d0eSVijay Khemka { 283e7d23d0eSVijay Khemka uint32_t scaledValue = 284e7d23d0eSVijay Khemka (value - (bValue * std::pow(10, bExp) * std::pow(10, rExp))) / 285e7d23d0eSVijay Khemka (mValue * std::pow(10, rExp)); 286e7d23d0eSVijay Khemka 287e7d23d0eSVijay Khemka if (scaledValue > std::numeric_limits<uint8_t>::max() || 288e7d23d0eSVijay Khemka scaledValue < std::numeric_limits<uint8_t>::lowest()) 289e7d23d0eSVijay Khemka { 290e7d23d0eSVijay Khemka throw std::out_of_range("Value out of range"); 291e7d23d0eSVijay Khemka } 292e7d23d0eSVijay Khemka if (bSigned) 293e7d23d0eSVijay Khemka { 294e7d23d0eSVijay Khemka return static_cast<int8_t>(scaledValue); 295e7d23d0eSVijay Khemka } 296e7d23d0eSVijay Khemka else 297e7d23d0eSVijay Khemka { 298e7d23d0eSVijay Khemka return static_cast<uint8_t>(scaledValue); 299e7d23d0eSVijay Khemka } 300e7d23d0eSVijay Khemka } 301e7d23d0eSVijay Khemka 302e7d23d0eSVijay Khemka static inline uint8_t getScaledIPMIValue(const double value, const double max, 303e7d23d0eSVijay Khemka const double min) 304e7d23d0eSVijay Khemka { 305e7d23d0eSVijay Khemka int16_t mValue = 0; 306e7d23d0eSVijay Khemka int8_t rExp = 0; 307e7d23d0eSVijay Khemka int16_t bValue = 0; 308e7d23d0eSVijay Khemka int8_t bExp = 0; 309e7d23d0eSVijay Khemka bool bSigned = 0; 310e7d23d0eSVijay Khemka bool result = 0; 311e7d23d0eSVijay Khemka 312e7d23d0eSVijay Khemka result = getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned); 313e7d23d0eSVijay Khemka if (!result) 314e7d23d0eSVijay Khemka { 315e7d23d0eSVijay Khemka throw std::runtime_error("Illegal sensor attributes"); 316e7d23d0eSVijay Khemka } 317e7d23d0eSVijay Khemka return scaleIPMIValueFromDouble(value, mValue, rExp, bValue, bExp, bSigned); 318e7d23d0eSVijay Khemka } 319e7d23d0eSVijay Khemka } // namespace ipmi 320