13f7c5e40SJason M. Bills /* 23f7c5e40SJason M. Bills // Copyright (c) 2017 2018 Intel Corporation 33f7c5e40SJason M. Bills // 43f7c5e40SJason M. Bills // Licensed under the Apache License, Version 2.0 (the "License"); 53f7c5e40SJason M. Bills // you may not use this file except in compliance with the License. 63f7c5e40SJason M. Bills // You may obtain a copy of the License at 73f7c5e40SJason M. Bills // 83f7c5e40SJason M. Bills // http://www.apache.org/licenses/LICENSE-2.0 93f7c5e40SJason M. Bills // 103f7c5e40SJason M. Bills // Unless required by applicable law or agreed to in writing, software 113f7c5e40SJason M. Bills // distributed under the License is distributed on an "AS IS" BASIS, 123f7c5e40SJason M. Bills // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133f7c5e40SJason M. Bills // See the License for the specific language governing permissions and 143f7c5e40SJason M. Bills // limitations under the License. 153f7c5e40SJason M. Bills */ 163f7c5e40SJason M. Bills 173f7c5e40SJason M. Bills #pragma once 1872867debSJason M. Bills #include <host-ipmid/ipmid-api.h> 1972867debSJason M. Bills 2072867debSJason M. Bills #include <cmath> 2172867debSJason M. Bills #include <iostream> 2272867debSJason M. Bills #include <phosphor-logging/log.hpp> 233f7c5e40SJason M. Bills 243f7c5e40SJason M. Bills namespace ipmi 253f7c5e40SJason M. Bills { 2672867debSJason M. Bills static constexpr int16_t maxInt10 = 0x1FF; 27*39417c7bSJames Feist static constexpr int16_t minInt10 = -0x200; 2872867debSJason M. Bills static constexpr int8_t maxInt4 = 7; 2972867debSJason M. Bills static constexpr int8_t minInt4 = -8; 3072867debSJason M. Bills 3172867debSJason M. Bills static inline bool getSensorAttributes(const double max, const double min, 3272867debSJason M. Bills int16_t& mValue, int8_t& rExp, 3372867debSJason M. Bills int16_t& bValue, int8_t& bExp, 3472867debSJason M. Bills bool& bSigned) 3572867debSJason M. Bills { 3672867debSJason M. Bills // computing y = (10^rRexp) * (Mx + (B*(10^Bexp))) 3772867debSJason M. Bills // check for 0, assume always positive 3872867debSJason M. Bills double mDouble; 3972867debSJason M. Bills double bDouble; 40*39417c7bSJames Feist if (max <= min) 4172867debSJason M. Bills { 4272867debSJason M. Bills phosphor::logging::log<phosphor::logging::level::DEBUG>( 4372867debSJason M. Bills "getSensorAttributes: Max must be greater than min"); 4472867debSJason M. Bills return false; 4572867debSJason M. Bills } 46*39417c7bSJames Feist 4772867debSJason M. Bills mDouble = (max - min) / 0xFF; 4872867debSJason M. Bills 4972867debSJason M. Bills if (min < 0) 5072867debSJason M. Bills { 5172867debSJason M. Bills bSigned = true; 5272867debSJason M. Bills bDouble = floor(0.5 + ((max + min) / 2)); 5372867debSJason M. Bills } 5472867debSJason M. Bills else 5572867debSJason M. Bills { 5672867debSJason M. Bills bSigned = false; 5772867debSJason M. Bills bDouble = min; 5872867debSJason M. Bills } 5972867debSJason M. Bills 6072867debSJason M. Bills rExp = 0; 6172867debSJason M. Bills 6272867debSJason M. Bills // M too big for 10 bit variable 6372867debSJason M. Bills while (mDouble > maxInt10) 6472867debSJason M. Bills { 65*39417c7bSJames Feist if (rExp >= maxInt4) 6672867debSJason M. Bills { 6772867debSJason M. Bills phosphor::logging::log<phosphor::logging::level::DEBUG>( 6872867debSJason M. Bills "rExp Too big, Max and Min range too far", 6972867debSJason M. Bills phosphor::logging::entry("REXP=%d", rExp)); 7072867debSJason M. Bills return false; 7172867debSJason M. Bills } 7272867debSJason M. Bills mDouble /= 10; 73*39417c7bSJames Feist rExp++; 7472867debSJason M. Bills } 7572867debSJason M. Bills 7672867debSJason M. Bills // M too small, loop until we lose less than 1 eight bit count of precision 7772867debSJason M. Bills while (((mDouble - floor(mDouble)) / mDouble) > (1.0 / 255)) 7872867debSJason M. Bills { 79*39417c7bSJames Feist if (rExp <= minInt4) 8072867debSJason M. Bills { 8172867debSJason M. Bills phosphor::logging::log<phosphor::logging::level::DEBUG>( 8272867debSJason M. Bills "rExp Too Small, Max and Min range too close"); 8372867debSJason M. Bills return false; 8472867debSJason M. Bills } 8572867debSJason M. Bills // check to see if we reached the limit of where we can adjust back the 8672867debSJason M. Bills // B value 8772867debSJason M. Bills if (bDouble / std::pow(10, rExp + minInt4 - 1) > bDouble) 8872867debSJason M. Bills { 8972867debSJason M. Bills if (mDouble < 1.0) 9072867debSJason M. Bills { 9172867debSJason M. Bills phosphor::logging::log<phosphor::logging::level::DEBUG>( 9272867debSJason M. Bills "Could not find mValue and B value with enough " 9372867debSJason M. Bills "precision."); 9472867debSJason M. Bills return false; 9572867debSJason M. Bills } 9672867debSJason M. Bills break; 9772867debSJason M. Bills } 9872867debSJason M. Bills // can't multiply M any more, max precision reached 9972867debSJason M. Bills else if (mDouble * 10 > maxInt10) 10072867debSJason M. Bills { 10172867debSJason M. Bills break; 10272867debSJason M. Bills } 10372867debSJason M. Bills mDouble *= 10; 104*39417c7bSJames Feist rExp--; 10572867debSJason M. Bills } 10672867debSJason M. Bills 10772867debSJason M. Bills bDouble /= std::pow(10, rExp); 10872867debSJason M. Bills bExp = 0; 10972867debSJason M. Bills 11072867debSJason M. Bills // B too big for 10 bit variable 11172867debSJason M. Bills while (bDouble > maxInt10 || bDouble < minInt10) 11272867debSJason M. Bills { 113*39417c7bSJames Feist if (bExp >= maxInt4) 11472867debSJason M. Bills { 11572867debSJason M. Bills phosphor::logging::log<phosphor::logging::level::DEBUG>( 11672867debSJason M. Bills "bExp Too Big, Max and Min range need to be adjusted"); 11772867debSJason M. Bills return false; 11872867debSJason M. Bills } 11972867debSJason M. Bills bDouble /= 10; 120*39417c7bSJames Feist bExp++; 12172867debSJason M. Bills } 12272867debSJason M. Bills 12372867debSJason M. Bills while (((fabs(bDouble) - floor(fabs(bDouble))) / fabs(bDouble)) > 12472867debSJason M. Bills (1.0 / 255)) 12572867debSJason M. Bills { 126*39417c7bSJames Feist if (bExp <= minInt4) 12772867debSJason M. Bills { 12872867debSJason M. Bills phosphor::logging::log<phosphor::logging::level::DEBUG>( 12972867debSJason M. Bills "bExp Too Small, Max and Min range need to be adjusted"); 13072867debSJason M. Bills return false; 13172867debSJason M. Bills } 13272867debSJason M. Bills bDouble *= 10; 13372867debSJason M. Bills bExp -= 1; 13472867debSJason M. Bills } 13572867debSJason M. Bills 136*39417c7bSJames Feist mValue = static_cast<int16_t>(mDouble) & maxInt10; 137*39417c7bSJames Feist bValue = static_cast<int16_t>(bDouble) & maxInt10; 13872867debSJason M. Bills 13972867debSJason M. Bills return true; 14072867debSJason M. Bills } 14172867debSJason M. Bills 14272867debSJason M. Bills static inline uint8_t 14372867debSJason M. Bills scaleIPMIValueFromDouble(const double value, const uint16_t mValue, 1443f7c5e40SJason M. Bills const int8_t rExp, const uint16_t bValue, 14572867debSJason M. Bills const int8_t bExp, const bool bSigned) 14672867debSJason M. Bills { 14772867debSJason M. Bills uint32_t scaledValue = 14872867debSJason M. Bills (value - (bValue * std::pow(10, bExp) * std::pow(10, rExp))) / 14972867debSJason M. Bills (mValue * std::pow(10, rExp)); 150*39417c7bSJames Feist 151*39417c7bSJames Feist if (scaledValue > std::numeric_limits<uint8_t>::max() || 152*39417c7bSJames Feist scaledValue < std::numeric_limits<uint8_t>::lowest()) 153*39417c7bSJames Feist { 154*39417c7bSJames Feist throw std::out_of_range("Value out of range"); 155*39417c7bSJames Feist } 15672867debSJason M. Bills if (bSigned) 15772867debSJason M. Bills { 15872867debSJason M. Bills return static_cast<int8_t>(scaledValue); 15972867debSJason M. Bills } 16072867debSJason M. Bills else 16172867debSJason M. Bills { 16272867debSJason M. Bills return static_cast<uint8_t>(scaledValue); 16372867debSJason M. Bills } 16472867debSJason M. Bills } 16572867debSJason M. Bills 16672867debSJason M. Bills static inline uint8_t getScaledIPMIValue(const double value, const double max, 16772867debSJason M. Bills const double min) 16872867debSJason M. Bills { 16972867debSJason M. Bills int16_t mValue = 0; 17072867debSJason M. Bills int8_t rExp = 0; 17172867debSJason M. Bills int16_t bValue = 0; 17272867debSJason M. Bills int8_t bExp = 0; 17372867debSJason M. Bills bool bSigned = 0; 17472867debSJason M. Bills bool result = 0; 17572867debSJason M. Bills 17672867debSJason M. Bills result = getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned); 17772867debSJason M. Bills if (!result) 17872867debSJason M. Bills { 179*39417c7bSJames Feist throw std::runtime_error("Illegal sensor attributes"); 18072867debSJason M. Bills } 18172867debSJason M. Bills return scaleIPMIValueFromDouble(value, mValue, rExp, bValue, bExp, bSigned); 18272867debSJason M. Bills } 18372867debSJason M. Bills 1843f7c5e40SJason M. Bills } // namespace ipmi