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