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