xref: /openbmc/dbus-sensors/src/ADCSensor.cpp (revision eacbfdd1)
1 /*
2 // Copyright (c) 2017 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 #include "ADCSensor.hpp"
18 
19 #include "SensorPaths.hpp"
20 #include "Thresholds.hpp"
21 #include "Utils.hpp"
22 #include "sensor.hpp"
23 
24 #include <fcntl.h>
25 
26 #include <boost/asio/error.hpp>
27 #include <boost/asio/io_context.hpp>
28 #include <boost/asio/read_until.hpp>
29 #include <boost/asio/streambuf.hpp>
30 #include <sdbusplus/asio/connection.hpp>
31 #include <sdbusplus/asio/object_server.hpp>
32 
33 #include <chrono>
34 #include <cmath>
35 #include <cstddef>
36 #include <iostream>
37 #include <memory>
38 #include <optional>
39 #include <stdexcept>
40 #include <string>
41 #include <utility>
42 #include <vector>
43 
44 // scaling factor from hwmon
45 static constexpr unsigned int sensorScaleFactor = 1000;
46 
47 static constexpr double roundFactor = 10000;     // 3 decimal places
48 static constexpr double maxVoltageReading = 1.8; // pre sensor scaling
49 static constexpr double minVoltageReading = 0;
50 
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)51 ADCSensor::ADCSensor(const std::string& path,
52                      sdbusplus::asio::object_server& objectServer,
53                      std::shared_ptr<sdbusplus::asio::connection>& conn,
54                      boost::asio::io_context& io, const std::string& sensorName,
55                      std::vector<thresholds::Threshold>&& thresholdsIn,
56                      const double scaleFactor, const float pollRate,
57                      PowerState readState,
58                      const std::string& sensorConfiguration,
59                      std::optional<BridgeGpio>&& bridgeGpio) :
60     Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
61            "ADC", false, false, maxVoltageReading / scaleFactor,
62            minVoltageReading / scaleFactor, conn, readState),
63     objServer(objectServer), inputDev(io), waitTimer(io), path(path),
64     scaleFactor(scaleFactor),
65     sensorPollMs(static_cast<unsigned int>(pollRate * 1000)),
66     bridgeGpio(std::move(bridgeGpio)), thresholdTimer(io)
67 {
68     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
69     int fd = open(path.c_str(), O_RDONLY);
70     if (fd < 0)
71     {
72         std::cerr << "unable to open acd device \n";
73     }
74 
75     inputDev.assign(fd);
76 
77     sensorInterface = objectServer.add_interface(
78         "/xyz/openbmc_project/sensors/voltage/" + name,
79         "xyz.openbmc_project.Sensor.Value");
80     for (const auto& threshold : thresholds)
81     {
82         std::string interface = thresholds::getInterface(threshold.level);
83         thresholdInterfaces[static_cast<size_t>(threshold.level)] =
84             objectServer.add_interface(
85                 "/xyz/openbmc_project/sensors/voltage/" + name, interface);
86     }
87     association = objectServer.add_interface(
88         "/xyz/openbmc_project/sensors/voltage/" + name, association::interface);
89     setInitialProperties(sensor_paths::unitVolts);
90 }
91 
~ADCSensor()92 ADCSensor::~ADCSensor()
93 {
94     // close the input dev to cancel async operations
95     inputDev.close();
96     waitTimer.cancel();
97 
98     for (const auto& iface : thresholdInterfaces)
99     {
100         objServer.remove_interface(iface);
101     }
102     objServer.remove_interface(sensorInterface);
103     objServer.remove_interface(association);
104 }
105 
setupRead()106 void ADCSensor::setupRead()
107 {
108     std::shared_ptr<boost::asio::streambuf> buffer =
109         std::make_shared<boost::asio::streambuf>();
110 
111     std::weak_ptr<ADCSensor> weakRef = weak_from_this();
112 
113     if (bridgeGpio.has_value())
114     {
115         (*bridgeGpio).set(1);
116         // In case a channel has a bridge circuit,we have to turn the bridge on
117         // prior to reading a value at least for one scan cycle to get a valid
118         // value. Guarantee that the HW signal can be stable, the HW signal
119         // could be instability.
120         waitTimer.expires_after(
121             std::chrono::milliseconds(bridgeGpio->setupTimeMs));
122         waitTimer.async_wait(
123             [weakRef, buffer](const boost::system::error_code& ec) {
124             std::shared_ptr<ADCSensor> self = weakRef.lock();
125             if (ec == boost::asio::error::operation_aborted)
126             {
127                 return; // we're being canceled
128             }
129 
130             if (self)
131             {
132                 boost::asio::async_read_until(
133                     self->inputDev, *buffer, '\n',
134                     [weakRef, buffer](const boost::system::error_code& ec,
135                                       std::size_t /*bytes_transfered*/) {
136                     std::shared_ptr<ADCSensor> self = weakRef.lock();
137                     if (self)
138                     {
139                         self->readBuf = buffer;
140                         self->handleResponse(ec);
141                     }
142                 });
143             }
144         });
145     }
146     else
147     {
148         boost::asio::async_read_until(
149             inputDev, *buffer, '\n',
150             [weakRef, buffer](const boost::system::error_code& ec,
151                               std::size_t /*bytes_transfered*/) {
152             std::shared_ptr<ADCSensor> self = weakRef.lock();
153             if (self)
154             {
155                 self->readBuf = buffer;
156                 self->handleResponse(ec);
157             }
158         });
159     }
160 }
161 
handleResponse(const boost::system::error_code & err)162 void ADCSensor::handleResponse(const boost::system::error_code& err)
163 {
164     std::weak_ptr<ADCSensor> weakRef = weak_from_this();
165 
166     if (err == boost::system::errc::bad_file_descriptor)
167     {
168         return; // we're being destroyed
169     }
170     std::istream responseStream(readBuf.get());
171 
172     if (!err)
173     {
174         std::string response;
175         std::getline(responseStream, response);
176 
177         // todo read scaling factors from configuration
178         try
179         {
180             rawValue = std::stod(response);
181             double nvalue = (rawValue / sensorScaleFactor) / scaleFactor;
182             nvalue = std::round(nvalue * roundFactor) / roundFactor;
183             updateValue(nvalue);
184         }
185         catch (const std::invalid_argument&)
186         {
187             incrementError();
188         }
189     }
190     else
191     {
192         incrementError();
193     }
194 
195     responseStream.clear();
196     inputDev.close();
197     if (bridgeGpio.has_value())
198     {
199         (*bridgeGpio).set(0);
200     }
201 
202     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
203     int fd = open(path.c_str(), O_RDONLY);
204     if (fd < 0)
205     {
206         std::cerr << "adcsensor " << name << " failed to open " << path << "\n";
207         return; // we're no longer valid
208     }
209     inputDev.assign(fd);
210     waitTimer.expires_after(std::chrono::milliseconds(sensorPollMs));
211     waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
212         std::shared_ptr<ADCSensor> self = weakRef.lock();
213         if (ec == boost::asio::error::operation_aborted)
214         {
215             if (self)
216             {
217                 std::cerr << "adcsensor " << self->name << " read cancelled\n";
218             }
219             else
220             {
221                 std::cerr << "adcsensor read cancelled no self\n";
222             }
223             return; // we're being canceled
224         }
225 
226         if (self)
227         {
228             self->setupRead();
229         }
230         else
231         {
232             std::cerr << "adcsensor weakref no self\n";
233         }
234     });
235 }
236 
checkThresholds()237 void ADCSensor::checkThresholds()
238 {
239     if (!readingStateGood())
240     {
241         return;
242     }
243 
244     thresholds::checkThresholdsPowerDelay(weak_from_this(), thresholdTimer);
245 }
246