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