1 /* 2 // Copyright (c) 2017 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 17 #pragma once 18 #include <cmath> 19 #include <iostream> 20 #include <phosphor-logging/log.hpp> 21 22 namespace ipmi 23 { 24 static constexpr int16_t maxInt10 = 0x1FF; 25 static constexpr int16_t minInt10 = -0x200; 26 static constexpr int8_t maxInt4 = 7; 27 static constexpr int8_t minInt4 = -8; 28 29 static inline bool getSensorAttributes(const double max, const double min, 30 int16_t& mValue, int8_t& rExp, 31 int16_t& bValue, int8_t& bExp, 32 bool& bSigned) 33 { 34 // computing y = (10^rRexp) * (Mx + (B*(10^Bexp))) 35 // check for 0, assume always positive 36 double mDouble; 37 double bDouble; 38 if (max <= min) 39 { 40 phosphor::logging::log<phosphor::logging::level::DEBUG>( 41 "getSensorAttributes: Max must be greater than min"); 42 return false; 43 } 44 45 mDouble = (max - min) / 0xFF; 46 47 if (min < 0) 48 { 49 bSigned = true; 50 bDouble = floor(0.5 + ((max + min) / 2)); 51 } 52 else 53 { 54 bSigned = false; 55 bDouble = min; 56 } 57 58 rExp = 0; 59 60 // M too big for 10 bit variable 61 while (mDouble > maxInt10) 62 { 63 if (rExp >= maxInt4) 64 { 65 phosphor::logging::log<phosphor::logging::level::DEBUG>( 66 "rExp Too big, Max and Min range too far", 67 phosphor::logging::entry("REXP=%d", rExp)); 68 return false; 69 } 70 mDouble /= 10; 71 rExp++; 72 } 73 74 // M too small, loop until we lose less than 1 eight bit count of precision 75 while (((mDouble - floor(mDouble)) / mDouble) > (1.0 / 255)) 76 { 77 if (rExp <= minInt4) 78 { 79 phosphor::logging::log<phosphor::logging::level::DEBUG>( 80 "rExp Too Small, Max and Min range too close"); 81 return false; 82 } 83 // check to see if we reached the limit of where we can adjust back the 84 // B value 85 if (bDouble / std::pow(10, rExp + minInt4 - 1) > bDouble) 86 { 87 if (mDouble < 1.0) 88 { 89 phosphor::logging::log<phosphor::logging::level::DEBUG>( 90 "Could not find mValue and B value with enough " 91 "precision."); 92 return false; 93 } 94 break; 95 } 96 // can't multiply M any more, max precision reached 97 else if (mDouble * 10 > maxInt10) 98 { 99 break; 100 } 101 mDouble *= 10; 102 rExp--; 103 } 104 105 bDouble /= std::pow(10, rExp); 106 bExp = 0; 107 108 // B too big for 10 bit variable 109 while (bDouble > maxInt10 || bDouble < minInt10) 110 { 111 if (bExp >= maxInt4) 112 { 113 phosphor::logging::log<phosphor::logging::level::DEBUG>( 114 "bExp Too Big, Max and Min range need to be adjusted"); 115 return false; 116 } 117 bDouble /= 10; 118 bExp++; 119 } 120 121 while (((fabs(bDouble) - floor(fabs(bDouble))) / fabs(bDouble)) > 122 (1.0 / 255)) 123 { 124 if (bExp <= minInt4) 125 { 126 phosphor::logging::log<phosphor::logging::level::DEBUG>( 127 "bExp Too Small, Max and Min range need to be adjusted"); 128 return false; 129 } 130 bDouble *= 10; 131 bExp -= 1; 132 } 133 134 mValue = static_cast<int16_t>(std::round(mDouble)) & maxInt10; 135 bValue = static_cast<int16_t>(bDouble) & maxInt10; 136 137 return true; 138 } 139 140 static inline uint8_t 141 scaleIPMIValueFromDouble(const double value, const uint16_t mValue, 142 const int8_t rExp, const uint16_t bValue, 143 const int8_t bExp, const bool bSigned) 144 { 145 uint32_t scaledValue = 146 (value - (bValue * std::pow(10, bExp) * std::pow(10, rExp))) / 147 (mValue * std::pow(10, rExp)); 148 149 if (scaledValue > std::numeric_limits<uint8_t>::max() || 150 scaledValue < std::numeric_limits<uint8_t>::lowest()) 151 { 152 throw std::out_of_range("Value out of range"); 153 } 154 if (bSigned) 155 { 156 return static_cast<int8_t>(scaledValue); 157 } 158 else 159 { 160 return static_cast<uint8_t>(scaledValue); 161 } 162 } 163 164 static inline uint8_t getScaledIPMIValue(const double value, const double max, 165 const double min) 166 { 167 int16_t mValue = 0; 168 int8_t rExp = 0; 169 int16_t bValue = 0; 170 int8_t bExp = 0; 171 bool bSigned = 0; 172 bool result = 0; 173 174 result = getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned); 175 if (!result) 176 { 177 throw std::runtime_error("Illegal sensor attributes"); 178 } 179 return scaleIPMIValueFromDouble(value, mValue, rExp, bValue, bExp, bSigned); 180 } 181 182 } // namespace ipmi 183