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_context& 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