xref: /openbmc/dbus-sensors/src/adc/ADCSensor.cpp (revision af1724b84b7558037c665d2106ec44d7362aca6b)
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 <limits>
38 #include <memory>
39 #include <optional>
40 #include <stdexcept>
41 #include <string>
42 #include <utility>
43 #include <vector>
44 
45 // scaling factor from hwmon
46 static constexpr unsigned int sensorScaleFactor = 1000;
47 
48 static constexpr double roundFactor = 10000;     // 3 decimal places
49 static constexpr double maxVoltageReading = 1.8; // pre sensor scaling
50 static constexpr double minVoltageReading = 0;
51 
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)52 ADCSensor::ADCSensor(
53     const std::string& path, sdbusplus::asio::object_server& objectServer,
54     std::shared_ptr<sdbusplus::asio::connection>& conn,
55     boost::asio::io_context& io, const std::string& sensorName,
56     std::vector<thresholds::Threshold>&& thresholdsIn, const double scaleFactor,
57     const float pollRate, 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     if (!readingStateGood())
109     {
110         markAvailable(false);
111         updateValue(std::numeric_limits<double>::quiet_NaN());
112         restartRead();
113         return;
114     }
115 
116     std::shared_ptr<boost::asio::streambuf> buffer =
117         std::make_shared<boost::asio::streambuf>();
118 
119     std::weak_ptr<ADCSensor> weakRef = weak_from_this();
120 
121     if (bridgeGpio.has_value())
122     {
123         (*bridgeGpio).set(1);
124         // In case a channel has a bridge circuit,we have to turn the bridge on
125         // prior to reading a value at least for one scan cycle to get a valid
126         // value. Guarantee that the HW signal can be stable, the HW signal
127         // could be instability.
128         waitTimer.expires_after(
129             std::chrono::milliseconds(bridgeGpio->setupTimeMs));
130         waitTimer.async_wait(
131             [weakRef, buffer](const boost::system::error_code& ec) {
132                 std::shared_ptr<ADCSensor> self = weakRef.lock();
133                 if (ec == boost::asio::error::operation_aborted)
134                 {
135                     return; // we're being canceled
136                 }
137 
138                 if (self)
139                 {
140                     boost::asio::async_read_until(
141                         self->inputDev, *buffer, '\n',
142                         [weakRef, buffer](const boost::system::error_code& ec,
143                                           std::size_t /*bytes_transfered*/) {
144                             std::shared_ptr<ADCSensor> self = weakRef.lock();
145                             if (self)
146                             {
147                                 self->readBuf = buffer;
148                                 self->handleResponse(ec);
149                             }
150                         });
151                 }
152             });
153     }
154     else
155     {
156         boost::asio::async_read_until(
157             inputDev, *buffer, '\n',
158             [weakRef, buffer](const boost::system::error_code& ec,
159                               std::size_t /*bytes_transfered*/) {
160                 std::shared_ptr<ADCSensor> self = weakRef.lock();
161                 if (self)
162                 {
163                     self->readBuf = buffer;
164                     self->handleResponse(ec);
165                 }
166             });
167     }
168 }
169 
restartRead()170 void ADCSensor::restartRead()
171 {
172     std::weak_ptr<ADCSensor> weakRef = weak_from_this();
173     waitTimer.expires_after(std::chrono::milliseconds(sensorPollMs));
174     waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
175         std::shared_ptr<ADCSensor> self = weakRef.lock();
176         if (ec == boost::asio::error::operation_aborted)
177         {
178             if (self)
179             {
180                 std::cerr << "adcsensor " << self->name << " read cancelled\n";
181             }
182             else
183             {
184                 std::cerr << "adcsensor read cancelled no self\n";
185             }
186             return; // we're being canceled
187         }
188 
189         if (self)
190         {
191             self->setupRead();
192         }
193         else
194         {
195             std::cerr << "adcsensor weakref no self\n";
196         }
197     });
198 }
199 
handleResponse(const boost::system::error_code & err)200 void ADCSensor::handleResponse(const boost::system::error_code& err)
201 {
202     std::weak_ptr<ADCSensor> weakRef = weak_from_this();
203 
204     if (err == boost::system::errc::bad_file_descriptor)
205     {
206         return; // we're being destroyed
207     }
208     std::istream responseStream(readBuf.get());
209 
210     if (!err)
211     {
212         std::string response;
213         std::getline(responseStream, response);
214 
215         // todo read scaling factors from configuration
216         try
217         {
218             rawValue = std::stod(response);
219             double nvalue = (rawValue / sensorScaleFactor) / scaleFactor;
220             nvalue = std::round(nvalue * roundFactor) / roundFactor;
221             updateValue(nvalue);
222         }
223         catch (const std::invalid_argument&)
224         {
225             incrementError();
226         }
227     }
228     else
229     {
230         incrementError();
231     }
232 
233     responseStream.clear();
234     inputDev.close();
235     if (bridgeGpio.has_value())
236     {
237         (*bridgeGpio).set(0);
238     }
239 
240     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
241     int fd = open(path.c_str(), O_RDONLY);
242     if (fd < 0)
243     {
244         std::cerr << "adcsensor " << name << " failed to open " << path << "\n";
245         return; // we're no longer valid
246     }
247     inputDev.assign(fd);
248     restartRead();
249 }
250 
checkThresholds()251 void ADCSensor::checkThresholds()
252 {
253     if (!readingStateGood())
254     {
255         return;
256     }
257 
258     thresholds::checkThresholdsPowerDelay(weak_from_this(), thresholdTimer);
259 }
260