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 <sdbusplus/asio/connection.hpp>
30 #include <sdbusplus/asio/object_server.hpp>
31
32 #include <charconv>
33 #include <chrono>
34 #include <cstddef>
35 #include <iostream>
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 path = newPath;
101 i2cDevice = newI2CDevice;
102 inputDev.open(path, boost::asio::random_access_file::read_only);
103 markAvailable(true);
104 setupRead();
105 }
106
deactivate()107 void HwmonTempSensor::deactivate()
108 {
109 markAvailable(false);
110 // close the input dev to cancel async operations
111 inputDev.close();
112 waitTimer.cancel();
113 i2cDevice = nullptr;
114 path = "";
115 }
116
~HwmonTempSensor()117 HwmonTempSensor::~HwmonTempSensor()
118 {
119 deactivate();
120
121 for (const auto& iface : thresholdInterfaces)
122 {
123 objServer.remove_interface(iface);
124 }
125 objServer.remove_interface(sensorInterface);
126 objServer.remove_interface(association);
127 }
128
setupRead()129 void HwmonTempSensor::setupRead()
130 {
131 if (!readingStateGood())
132 {
133 markAvailable(false);
134 updateValue(std::numeric_limits<double>::quiet_NaN());
135 restartRead();
136 return;
137 }
138
139 std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this();
140 inputDev.async_read_some_at(
141 0, boost::asio::buffer(readBuf),
142 [weakRef](const boost::system::error_code& ec, std::size_t bytesRead) {
143 std::shared_ptr<HwmonTempSensor> self = weakRef.lock();
144 if (self)
145 {
146 self->handleResponse(ec, bytesRead);
147 }
148 });
149 }
150
restartRead()151 void HwmonTempSensor::restartRead()
152 {
153 std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this();
154 waitTimer.expires_after(std::chrono::milliseconds(sensorPollMs));
155 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
156 if (ec == boost::asio::error::operation_aborted)
157 {
158 return; // we're being canceled
159 }
160 std::shared_ptr<HwmonTempSensor> self = weakRef.lock();
161 if (!self)
162 {
163 return;
164 }
165 self->setupRead();
166 });
167 }
168
handleResponse(const boost::system::error_code & err,size_t bytesRead)169 void HwmonTempSensor::handleResponse(const boost::system::error_code& err,
170 size_t bytesRead)
171 {
172 if ((err == boost::system::errc::bad_file_descriptor) ||
173 (err == boost::asio::error::misc_errors::not_found))
174 {
175 std::cerr << "Hwmon temp sensor " << name << " removed " << path
176 << "\n";
177 return; // we're being destroyed
178 }
179
180 if (!err)
181 {
182 const char* bufEnd = readBuf.data() + bytesRead;
183 int nvalue = 0;
184 std::from_chars_result ret =
185 std::from_chars(readBuf.data(), bufEnd, nvalue);
186 if (ret.ec != std::errc())
187 {
188 incrementError();
189 }
190 else
191 {
192 updateValue((nvalue + offsetValue) * scaleValue);
193 }
194 }
195 else
196 {
197 incrementError();
198 }
199
200 restartRead();
201 }
202
checkThresholds()203 void HwmonTempSensor::checkThresholds()
204 {
205 thresholds::checkThresholds(this);
206 }
207