xref: /openbmc/dbus-sensors/src/ADCSensor.cpp (revision eacbfdd1)
16714a25aSJames Feist /*
26714a25aSJames Feist // Copyright (c) 2017 Intel Corporation
36714a25aSJames Feist //
46714a25aSJames Feist // Licensed under the Apache License, Version 2.0 (the "License");
56714a25aSJames Feist // you may not use this file except in compliance with the License.
66714a25aSJames Feist // You may obtain a copy of the License at
76714a25aSJames Feist //
86714a25aSJames Feist //      http://www.apache.org/licenses/LICENSE-2.0
96714a25aSJames Feist //
106714a25aSJames Feist // Unless required by applicable law or agreed to in writing, software
116714a25aSJames Feist // distributed under the License is distributed on an "AS IS" BASIS,
126714a25aSJames Feist // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136714a25aSJames Feist // See the License for the specific language governing permissions and
146714a25aSJames Feist // limitations under the License.
156714a25aSJames Feist */
166714a25aSJames Feist 
17e73bd0a1SAndrew Jeffery #include "ADCSensor.hpp"
18e73bd0a1SAndrew Jeffery 
19*eacbfdd1SEd Tanous #include "SensorPaths.hpp"
20*eacbfdd1SEd Tanous #include "Thresholds.hpp"
21*eacbfdd1SEd Tanous #include "Utils.hpp"
22*eacbfdd1SEd Tanous #include "sensor.hpp"
236714a25aSJames Feist 
24*eacbfdd1SEd Tanous #include <fcntl.h>
25*eacbfdd1SEd Tanous 
26*eacbfdd1SEd Tanous #include <boost/asio/error.hpp>
27*eacbfdd1SEd Tanous #include <boost/asio/io_context.hpp>
288086aba0SJames Feist #include <boost/asio/read_until.hpp>
29*eacbfdd1SEd Tanous #include <boost/asio/streambuf.hpp>
3038fb5983SJames Feist #include <sdbusplus/asio/connection.hpp>
3138fb5983SJames Feist #include <sdbusplus/asio/object_server.hpp>
3238fb5983SJames Feist 
33*eacbfdd1SEd Tanous #include <chrono>
3496e97db7SPatrick Venture #include <cmath>
35*eacbfdd1SEd Tanous #include <cstddef>
366714a25aSJames Feist #include <iostream>
3796e97db7SPatrick Venture #include <memory>
38a5b1bbccSZhu, Yunge #include <optional>
39*eacbfdd1SEd Tanous #include <stdexcept>
406714a25aSJames Feist #include <string>
41*eacbfdd1SEd Tanous #include <utility>
4296e97db7SPatrick Venture #include <vector>
432da370eeSPatrick Venture 
446714a25aSJames Feist // scaling factor from hwmon
459ced0a38SJae Hyun Yoo static constexpr unsigned int sensorScaleFactor = 1000;
466714a25aSJames Feist 
47cc6d4fe8SJames Feist static constexpr double roundFactor = 10000;     // 3 decimal places
48937eb54eSZhikui Ren static constexpr double maxVoltageReading = 1.8; // pre sensor scaling
49937eb54eSZhikui Ren static constexpr double minVoltageReading = 0;
50cc6d4fe8SJames Feist 
ADCSensor(const std::string & path,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & conn,boost::asio::io_context & io,const std::string & sensorName,std::vector<thresholds::Threshold> && thresholdsIn,const double scaleFactor,const float pollRate,PowerState readState,const std::string & sensorConfiguration,std::optional<BridgeGpio> && bridgeGpio)516714a25aSJames Feist ADCSensor::ADCSensor(const std::string& path,
526714a25aSJames Feist                      sdbusplus::asio::object_server& objectServer,
536714a25aSJames Feist                      std::shared_ptr<sdbusplus::asio::connection>& conn,
541f978631SEd Tanous                      boost::asio::io_context& io, const std::string& sensorName,
557b7a9deaSJeff Lin                      std::vector<thresholds::Threshold>&& thresholdsIn,
56d9cd7040SJeff Lin                      const double scaleFactor, const float pollRate,
57d9cd7040SJeff Lin                      PowerState readState,
58a5b1bbccSZhu, Yunge                      const std::string& sensorConfiguration,
591cbd1c6dSJae Hyun Yoo                      std::optional<BridgeGpio>&& bridgeGpio) :
60da98f095SZhikui Ren     Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
61054aad8fSZev Weiss            "ADC", false, false, maxVoltageReading / scaleFactor,
62054aad8fSZev Weiss            minVoltageReading / scaleFactor, conn, readState),
632049bd26SEd Tanous     objServer(objectServer), inputDev(io), waitTimer(io), path(path),
642049bd26SEd Tanous     scaleFactor(scaleFactor),
65d9cd7040SJeff Lin     sensorPollMs(static_cast<unsigned int>(pollRate * 1000)),
6612c2c0efSZhikui Ren     bridgeGpio(std::move(bridgeGpio)), thresholdTimer(io)
676714a25aSJames Feist {
6899c4409aSEd Tanous     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
6999c4409aSEd Tanous     int fd = open(path.c_str(), O_RDONLY);
7099c4409aSEd Tanous     if (fd < 0)
7199c4409aSEd Tanous     {
7299c4409aSEd Tanous         std::cerr << "unable to open acd device \n";
7399c4409aSEd Tanous     }
7499c4409aSEd Tanous 
7599c4409aSEd Tanous     inputDev.assign(fd);
7699c4409aSEd Tanous 
77251c7823SJames Feist     sensorInterface = objectServer.add_interface(
78251c7823SJames Feist         "/xyz/openbmc_project/sensors/voltage/" + name,
79251c7823SJames Feist         "xyz.openbmc_project.Sensor.Value");
805667808aSJayashree Dhanapal     for (const auto& threshold : thresholds)
816714a25aSJames Feist     {
825667808aSJayashree Dhanapal         std::string interface = thresholds::getInterface(threshold.level);
835667808aSJayashree Dhanapal         thresholdInterfaces[static_cast<size_t>(threshold.level)] =
845667808aSJayashree Dhanapal             objectServer.add_interface(
855667808aSJayashree Dhanapal                 "/xyz/openbmc_project/sensors/voltage/" + name, interface);
866714a25aSJames Feist     }
87078f2324SJames Feist     association = objectServer.add_interface(
882adc95cbSJames Feist         "/xyz/openbmc_project/sensors/voltage/" + name, association::interface);
893928741aSAndrei Kartashev     setInitialProperties(sensor_paths::unitVolts);
906714a25aSJames Feist }
916714a25aSJames Feist 
~ADCSensor()926714a25aSJames Feist ADCSensor::~ADCSensor()
936714a25aSJames Feist {
946714a25aSJames Feist     // close the input dev to cancel async operations
959ced0a38SJae Hyun Yoo     inputDev.close();
969ced0a38SJae Hyun Yoo     waitTimer.cancel();
9712c2c0efSZhikui Ren 
985667808aSJayashree Dhanapal     for (const auto& iface : thresholdInterfaces)
995667808aSJayashree Dhanapal     {
1005667808aSJayashree Dhanapal         objServer.remove_interface(iface);
1015667808aSJayashree Dhanapal     }
102251c7823SJames Feist     objServer.remove_interface(sensorInterface);
103078f2324SJames Feist     objServer.remove_interface(association);
1046714a25aSJames Feist }
1056714a25aSJames Feist 
setupRead()106201a1015SEd Tanous void ADCSensor::setupRead()
1076714a25aSJames Feist {
1081afda6bbSYong Li     std::shared_ptr<boost::asio::streambuf> buffer =
1091afda6bbSYong Li         std::make_shared<boost::asio::streambuf>();
1101afda6bbSYong Li 
1111afda6bbSYong Li     std::weak_ptr<ADCSensor> weakRef = weak_from_this();
1121afda6bbSYong Li 
113a5b1bbccSZhu, Yunge     if (bridgeGpio.has_value())
114a5b1bbccSZhu, Yunge     {
11512bbae01SJae Hyun Yoo         (*bridgeGpio).set(1);
116a5b1bbccSZhu, Yunge         // In case a channel has a bridge circuit,we have to turn the bridge on
117a5b1bbccSZhu, Yunge         // prior to reading a value at least for one scan cycle to get a valid
118a5b1bbccSZhu, Yunge         // value. Guarantee that the HW signal can be stable, the HW signal
119a5b1bbccSZhu, Yunge         // could be instability.
12083db50caSEd Tanous         waitTimer.expires_after(
1219b4a20e9SEd Tanous             std::chrono::milliseconds(bridgeGpio->setupTimeMs));
1221afda6bbSYong Li         waitTimer.async_wait(
1231afda6bbSYong Li             [weakRef, buffer](const boost::system::error_code& ec) {
1241afda6bbSYong Li             std::shared_ptr<ADCSensor> self = weakRef.lock();
125a5b1bbccSZhu, Yunge             if (ec == boost::asio::error::operation_aborted)
126a5b1bbccSZhu, Yunge             {
127a5b1bbccSZhu, Yunge                 return; // we're being canceled
128a5b1bbccSZhu, Yunge             }
1291afda6bbSYong Li 
1301afda6bbSYong Li             if (self)
1311afda6bbSYong Li             {
1326714a25aSJames Feist                 boost::asio::async_read_until(
1331afda6bbSYong Li                     self->inputDev, *buffer, '\n',
1341afda6bbSYong Li                     [weakRef, buffer](const boost::system::error_code& ec,
1351afda6bbSYong Li                                       std::size_t /*bytes_transfered*/) {
1361afda6bbSYong Li                     std::shared_ptr<ADCSensor> self = weakRef.lock();
1371afda6bbSYong Li                     if (self)
1381afda6bbSYong Li                     {
1391afda6bbSYong Li                         self->readBuf = buffer;
1401afda6bbSYong Li                         self->handleResponse(ec);
1411afda6bbSYong Li                     }
1421afda6bbSYong Li                 });
1431afda6bbSYong Li             }
144a5b1bbccSZhu, Yunge         });
145a5b1bbccSZhu, Yunge     }
146a5b1bbccSZhu, Yunge     else
147a5b1bbccSZhu, Yunge     {
148a5b1bbccSZhu, Yunge         boost::asio::async_read_until(
1491afda6bbSYong Li             inputDev, *buffer, '\n',
1501afda6bbSYong Li             [weakRef, buffer](const boost::system::error_code& ec,
1511afda6bbSYong Li                               std::size_t /*bytes_transfered*/) {
1521afda6bbSYong Li             std::shared_ptr<ADCSensor> self = weakRef.lock();
1531afda6bbSYong Li             if (self)
1541afda6bbSYong Li             {
1551afda6bbSYong Li                 self->readBuf = buffer;
1561afda6bbSYong Li                 self->handleResponse(ec);
1571afda6bbSYong Li             }
1581afda6bbSYong Li         });
159a5b1bbccSZhu, Yunge     }
1606714a25aSJames Feist }
1616714a25aSJames Feist 
handleResponse(const boost::system::error_code & err)1629ced0a38SJae Hyun Yoo void ADCSensor::handleResponse(const boost::system::error_code& err)
1636714a25aSJames Feist {
1641afda6bbSYong Li     std::weak_ptr<ADCSensor> weakRef = weak_from_this();
1651afda6bbSYong Li 
1666714a25aSJames Feist     if (err == boost::system::errc::bad_file_descriptor)
1676714a25aSJames Feist     {
1686714a25aSJames Feist         return; // we're being destroyed
1696714a25aSJames Feist     }
1701afda6bbSYong Li     std::istream responseStream(readBuf.get());
1716714a25aSJames Feist 
1726714a25aSJames Feist     if (!err)
1736714a25aSJames Feist     {
1746714a25aSJames Feist         std::string response;
1759ced0a38SJae Hyun Yoo         std::getline(responseStream, response);
1766714a25aSJames Feist 
1776714a25aSJames Feist         // todo read scaling factors from configuration
1786714a25aSJames Feist         try
1796714a25aSJames Feist         {
180d3da1280SZhikui Ren             rawValue = std::stod(response);
181d3da1280SZhikui Ren             double nvalue = (rawValue / sensorScaleFactor) / scaleFactor;
182cc6d4fe8SJames Feist             nvalue = std::round(nvalue * roundFactor) / roundFactor;
1839ced0a38SJae Hyun Yoo             updateValue(nvalue);
1846714a25aSJames Feist         }
18526601e89SPatrick Williams         catch (const std::invalid_argument&)
1866714a25aSJames Feist         {
187961bf09aSJames Feist             incrementError();
1886714a25aSJames Feist         }
1896714a25aSJames Feist     }
1906714a25aSJames Feist     else
1916714a25aSJames Feist     {
192961bf09aSJames Feist         incrementError();
1936714a25aSJames Feist     }
1946714a25aSJames Feist 
1959ced0a38SJae Hyun Yoo     responseStream.clear();
1969ced0a38SJae Hyun Yoo     inputDev.close();
197a5b1bbccSZhu, Yunge     if (bridgeGpio.has_value())
198a5b1bbccSZhu, Yunge     {
19912bbae01SJae Hyun Yoo         (*bridgeGpio).set(0);
200a5b1bbccSZhu, Yunge     }
20199c4409aSEd Tanous 
20299c4409aSEd Tanous     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
2036714a25aSJames Feist     int fd = open(path.c_str(), O_RDONLY);
204ef9665a8SJae Hyun Yoo     if (fd < 0)
2056714a25aSJames Feist     {
206d3da1280SZhikui Ren         std::cerr << "adcsensor " << name << " failed to open " << path << "\n";
2076714a25aSJames Feist         return; // we're no longer valid
2086714a25aSJames Feist     }
2099ced0a38SJae Hyun Yoo     inputDev.assign(fd);
21083db50caSEd Tanous     waitTimer.expires_after(std::chrono::milliseconds(sensorPollMs));
2111afda6bbSYong Li     waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
2121afda6bbSYong Li         std::shared_ptr<ADCSensor> self = weakRef.lock();
2136714a25aSJames Feist         if (ec == boost::asio::error::operation_aborted)
2146714a25aSJames Feist         {
215d3da1280SZhikui Ren             if (self)
216d3da1280SZhikui Ren             {
217d3da1280SZhikui Ren                 std::cerr << "adcsensor " << self->name << " read cancelled\n";
218d3da1280SZhikui Ren             }
219d3da1280SZhikui Ren             else
220d3da1280SZhikui Ren             {
221d3da1280SZhikui Ren                 std::cerr << "adcsensor read cancelled no self\n";
222d3da1280SZhikui Ren             }
2236714a25aSJames Feist             return; // we're being canceled
2246714a25aSJames Feist         }
2251afda6bbSYong Li 
2261afda6bbSYong Li         if (self)
2271afda6bbSYong Li         {
2281afda6bbSYong Li             self->setupRead();
2291afda6bbSYong Li         }
230d3da1280SZhikui Ren         else
231d3da1280SZhikui Ren         {
232d3da1280SZhikui Ren             std::cerr << "adcsensor weakref no self\n";
233d3da1280SZhikui Ren         }
2346714a25aSJames Feist     });
2356714a25aSJames Feist }
2366714a25aSJames Feist 
checkThresholds()237201a1015SEd Tanous void ADCSensor::checkThresholds()
2386714a25aSJames Feist {
239961bf09aSJames Feist     if (!readingStateGood())
24071d31b2eSJames Feist     {
24171d31b2eSJames Feist         return;
24271d31b2eSJames Feist     }
24346342ec3SJames Feist 
24412c2c0efSZhikui Ren     thresholds::checkThresholdsPowerDelay(weak_from_this(), thresholdTimer);
2456714a25aSJames Feist }
246