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 <unistd.h>
18 
19 #include <HwmonTempSensor.hpp>
20 #include <boost/algorithm/string/predicate.hpp>
21 #include <boost/asio/read_until.hpp>
22 #include <boost/date_time/posix_time/posix_time.hpp>
23 #include <sdbusplus/asio/connection.hpp>
24 #include <sdbusplus/asio/object_server.hpp>
25 
26 #include <iostream>
27 #include <istream>
28 #include <limits>
29 #include <memory>
30 #include <string>
31 #include <vector>
32 
33 // Temperatures are read in milli degrees Celsius, we need degrees Celsius.
34 // Pressures are read in kilopascal, we need Pascals.  On D-Bus for Open BMC
35 // we use the International System of Units without prefixes.
36 // Links to the kernel documentation:
37 // https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
38 // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-iio
39 // For IIO RAW sensors we get a raw_value, an offset, and scale to compute
40 // the value = (raw_value + offset) * scale
41 
42 HwmonTempSensor::HwmonTempSensor(
43     const std::string& path, const std::string& objectType,
44     sdbusplus::asio::object_server& objectServer,
45     std::shared_ptr<sdbusplus::asio::connection>& conn,
46     boost::asio::io_service& io, const std::string& sensorName,
47     std::vector<thresholds::Threshold>&& thresholdsIn,
48     const struct SensorParams& thisSensorParameters, const float pollRate,
49     const std::string& sensorConfiguration, const PowerState powerState) :
50     Sensor(boost::replace_all_copy(sensorName, " ", "_"),
51            std::move(thresholdsIn), sensorConfiguration, objectType, false,
52            false, thisSensorParameters.maxValue, thisSensorParameters.minValue,
53            conn, powerState),
54     objServer(objectServer), inputDev(io), waitTimer(io), path(path),
55     offsetValue(thisSensorParameters.offsetValue),
56     scaleValue(thisSensorParameters.scaleValue),
57     sensorPollMs(static_cast<unsigned int>(pollRate * 1000))
58 {
59     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
60     int fd = open(path.c_str(), O_RDONLY);
61     if (fd < 0)
62     {
63         std::cerr << "HwmonTempSensor " << sensorName << " failed to open "
64                   << path << "\n";
65     }
66     inputDev.assign(fd);
67 
68     sensorInterface = objectServer.add_interface(
69         "/xyz/openbmc_project/sensors/" + thisSensorParameters.typeName + "/" +
70             name,
71         "xyz.openbmc_project.Sensor.Value");
72 
73     for (const auto& threshold : thresholds)
74     {
75         std::string interface = thresholds::getInterface(threshold.level);
76         thresholdInterfaces[static_cast<size_t>(threshold.level)] =
77             objectServer.add_interface("/xyz/openbmc_project/sensors/" +
78                                            thisSensorParameters.typeName + "/" +
79                                            name,
80                                        interface);
81     }
82     association = objectServer.add_interface("/xyz/openbmc_project/sensors/" +
83                                                  thisSensorParameters.typeName +
84                                                  "/" + name,
85                                              association::interface);
86     setInitialProperties(thisSensorParameters.units);
87 }
88 
89 HwmonTempSensor::~HwmonTempSensor()
90 {
91     // close the input dev to cancel async operations
92     inputDev.close();
93     waitTimer.cancel();
94     for (const auto& iface : thresholdInterfaces)
95     {
96         objServer.remove_interface(iface);
97     }
98     objServer.remove_interface(sensorInterface);
99     objServer.remove_interface(association);
100 }
101 
102 void HwmonTempSensor::setupRead(void)
103 {
104     if (!readingStateGood())
105     {
106         markAvailable(false);
107         updateValue(std::numeric_limits<double>::quiet_NaN());
108         restartRead();
109         return;
110     }
111 
112     std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this();
113     boost::asio::async_read_until(inputDev, readBuf, '\n',
114                                   [weakRef](const boost::system::error_code& ec,
115                                             std::size_t /*bytes_transfered*/) {
116         std::shared_ptr<HwmonTempSensor> self = weakRef.lock();
117         if (self)
118         {
119             self->handleResponse(ec);
120         }
121     });
122 }
123 
124 void HwmonTempSensor::restartRead()
125 {
126     std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this();
127     waitTimer.expires_from_now(boost::posix_time::milliseconds(sensorPollMs));
128     waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
129         if (ec == boost::asio::error::operation_aborted)
130         {
131             return; // we're being canceled
132         }
133         std::shared_ptr<HwmonTempSensor> self = weakRef.lock();
134         if (!self)
135         {
136             return;
137         }
138         self->setupRead();
139     });
140 }
141 
142 void HwmonTempSensor::handleResponse(const boost::system::error_code& err)
143 {
144     if ((err == boost::system::errc::bad_file_descriptor) ||
145         (err == boost::asio::error::misc_errors::not_found))
146     {
147         std::cerr << "Hwmon temp sensor " << name << " removed " << path
148                   << "\n";
149         return; // we're being destroyed
150     }
151     std::istream responseStream(&readBuf);
152     if (!err)
153     {
154         std::string response;
155         std::getline(responseStream, response);
156         try
157         {
158             rawValue = std::stod(response);
159             double nvalue = (rawValue + offsetValue) * scaleValue;
160             updateValue(nvalue);
161         }
162         catch (const std::invalid_argument&)
163         {
164             incrementError();
165         }
166     }
167     else
168     {
169         incrementError();
170     }
171 
172     responseStream.clear();
173     inputDev.close();
174 
175     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
176     int fd = open(path.c_str(), O_RDONLY);
177     if (fd < 0)
178     {
179         std::cerr << "Hwmon temp sensor " << name << " not valid " << path
180                   << "\n";
181         return; // we're no longer valid
182     }
183     inputDev.assign(fd);
184     restartRead();
185 }
186 
187 void HwmonTempSensor::checkThresholds(void)
188 {
189     thresholds::checkThresholds(this);
190 }
191