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 static constexpr double sensorOffset = 0.0;
42 static constexpr double sensorScale = 0.001;
43 static constexpr size_t warnAfterErrorCount = 10;
44 
45 static constexpr double maxReading = 127;
46 static constexpr double minReading = -128;
47 
48 HwmonTempSensor::HwmonTempSensor(
49     const std::string& path, const std::string& objectType,
50     sdbusplus::asio::object_server& objectServer,
51     std::shared_ptr<sdbusplus::asio::connection>& conn,
52     boost::asio::io_service& io, const std::string& sensorName,
53     std::vector<thresholds::Threshold>&& thresholdsIn, const float pollRate,
54     const std::string& sensorConfiguration, const PowerState powerState) :
55     Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
56            objectType, false, false, maxReading, minReading, conn, powerState),
57     std::enable_shared_from_this<HwmonTempSensor>(), objServer(objectServer),
58     inputDev(io, open(path.c_str(), O_RDONLY)), waitTimer(io), path(path),
59     sensorPollMs(static_cast<unsigned int>(pollRate * 1000))
60 {
61     sensorInterface = objectServer.add_interface(
62         "/xyz/openbmc_project/sensors/temperature/" + name,
63         "xyz.openbmc_project.Sensor.Value");
64 
65     if (thresholds::hasWarningInterface(thresholds))
66     {
67         thresholdInterfaceWarning = objectServer.add_interface(
68             "/xyz/openbmc_project/sensors/temperature/" + name,
69             "xyz.openbmc_project.Sensor.Threshold.Warning");
70     }
71     if (thresholds::hasCriticalInterface(thresholds))
72     {
73         thresholdInterfaceCritical = objectServer.add_interface(
74             "/xyz/openbmc_project/sensors/temperature/" + name,
75             "xyz.openbmc_project.Sensor.Threshold.Critical");
76     }
77     association = objectServer.add_interface(
78         "/xyz/openbmc_project/sensors/temperature/" + name,
79         association::interface);
80     setInitialProperties(conn, sensor_paths::unitDegreesC);
81 }
82 
83 HwmonTempSensor::~HwmonTempSensor()
84 {
85     // close the input dev to cancel async operations
86     inputDev.close();
87     waitTimer.cancel();
88     objServer.remove_interface(thresholdInterfaceWarning);
89     objServer.remove_interface(thresholdInterfaceCritical);
90     objServer.remove_interface(sensorInterface);
91     objServer.remove_interface(association);
92 }
93 
94 void HwmonTempSensor::setupRead(void)
95 {
96     if (!readingStateGood())
97     {
98         markAvailable(false);
99         updateValue(std::numeric_limits<double>::quiet_NaN());
100         restartRead();
101         return;
102     }
103 
104     std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this();
105     boost::asio::async_read_until(inputDev, readBuf, '\n',
106                                   [weakRef](const boost::system::error_code& ec,
107                                             std::size_t /*bytes_transfered*/) {
108                                       std::shared_ptr<HwmonTempSensor> self =
109                                           weakRef.lock();
110                                       if (self)
111                                       {
112                                           self->handleResponse(ec);
113                                       }
114                                   });
115 }
116 
117 void HwmonTempSensor::restartRead()
118 {
119     std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this();
120     waitTimer.expires_from_now(boost::posix_time::milliseconds(sensorPollMs));
121     waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
122         if (ec == boost::asio::error::operation_aborted)
123         {
124             return; // we're being canceled
125         }
126         std::shared_ptr<HwmonTempSensor> self = weakRef.lock();
127         if (!self)
128         {
129             return;
130         }
131         self->setupRead();
132     });
133 }
134 
135 void HwmonTempSensor::handleResponse(const boost::system::error_code& err)
136 {
137     if ((err == boost::system::errc::bad_file_descriptor) ||
138         (err == boost::asio::error::misc_errors::not_found))
139     {
140         std::cerr << "Hwmon temp sensor " << name << " removed " << path
141                   << "\n";
142         return; // we're being destroyed
143     }
144     std::istream responseStream(&readBuf);
145     if (!err)
146     {
147         std::string response;
148         std::getline(responseStream, response);
149         try
150         {
151             rawValue = std::stod(response);
152             double nvalue = (rawValue + sensorOffset) * sensorScale;
153             updateValue(nvalue);
154         }
155         catch (const std::invalid_argument&)
156         {
157             incrementError();
158         }
159     }
160     else
161     {
162         incrementError();
163     }
164 
165     responseStream.clear();
166     inputDev.close();
167     int fd = open(path.c_str(), O_RDONLY);
168     if (fd < 0)
169     {
170         std::cerr << "Hwmon temp sensor " << name << " not valid " << path
171                   << "\n";
172         return; // we're no longer valid
173     }
174     inputDev.assign(fd);
175     restartRead();
176 }
177 
178 void HwmonTempSensor::checkThresholds(void)
179 {
180     thresholds::checkThresholds(this);
181 }
182