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