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 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 92 bool HwmonTempSensor::isActive() 93 { 94 return inputDev.is_open(); 95 } 96 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 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 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 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 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 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 203 void HwmonTempSensor::checkThresholds() 204 { 205 thresholds::checkThresholds(this); 206 } 207