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 <unistd.h> 18 19 #include <HwmonTempSensor.hpp> 20 #include <boost/algorithm/string/predicate.hpp> 21 #include <boost/asio/read_until.hpp> 22 #include <boost/date_time/posix_time/posix_time.hpp> 23 #include <sdbusplus/asio/connection.hpp> 24 #include <sdbusplus/asio/object_server.hpp> 25 26 #include <iostream> 27 #include <istream> 28 #include <limits> 29 #include <memory> 30 #include <string> 31 #include <vector> 32 33 // Temperatures are read in milli degrees Celsius, we need degrees Celsius. 34 // Pressures are read in kilopascal, we need Pascals. On D-Bus for Open BMC 35 // we use the International System of Units without prefixes. 36 // Links to the kernel documentation: 37 // https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface 38 // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-iio 39 // For IIO RAW sensors we get a raw_value, an offset, and scale to compute 40 // the value = (raw_value + offset) * scale 41 static constexpr double sensorOffset = 0.0; 42 static constexpr double sensorScale = 0.001; 43 static constexpr size_t warnAfterErrorCount = 10; 44 45 static constexpr double maxReading = 127; 46 static constexpr double minReading = -128; 47 48 HwmonTempSensor::HwmonTempSensor( 49 const std::string& path, const std::string& objectType, 50 sdbusplus::asio::object_server& objectServer, 51 std::shared_ptr<sdbusplus::asio::connection>& conn, 52 boost::asio::io_service& io, const std::string& sensorName, 53 std::vector<thresholds::Threshold>&& thresholdsIn, const float pollRate, 54 const std::string& sensorConfiguration, const PowerState powerState) : 55 Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration, 56 objectType, false, false, maxReading, minReading, conn, powerState), 57 std::enable_shared_from_this<HwmonTempSensor>(), objServer(objectServer), 58 inputDev(io, open(path.c_str(), O_RDONLY)), waitTimer(io), path(path), 59 sensorPollMs(static_cast<unsigned int>(pollRate * 1000)) 60 { 61 sensorInterface = objectServer.add_interface( 62 "/xyz/openbmc_project/sensors/temperature/" + name, 63 "xyz.openbmc_project.Sensor.Value"); 64 65 if (thresholds::hasWarningInterface(thresholds)) 66 { 67 thresholdInterfaceWarning = objectServer.add_interface( 68 "/xyz/openbmc_project/sensors/temperature/" + name, 69 "xyz.openbmc_project.Sensor.Threshold.Warning"); 70 } 71 if (thresholds::hasCriticalInterface(thresholds)) 72 { 73 thresholdInterfaceCritical = objectServer.add_interface( 74 "/xyz/openbmc_project/sensors/temperature/" + name, 75 "xyz.openbmc_project.Sensor.Threshold.Critical"); 76 } 77 association = objectServer.add_interface( 78 "/xyz/openbmc_project/sensors/temperature/" + name, 79 association::interface); 80 setInitialProperties(conn, sensor_paths::unitDegreesC); 81 } 82 83 HwmonTempSensor::~HwmonTempSensor() 84 { 85 // close the input dev to cancel async operations 86 inputDev.close(); 87 waitTimer.cancel(); 88 objServer.remove_interface(thresholdInterfaceWarning); 89 objServer.remove_interface(thresholdInterfaceCritical); 90 objServer.remove_interface(sensorInterface); 91 objServer.remove_interface(association); 92 } 93 94 void HwmonTempSensor::setupRead(void) 95 { 96 if (!readingStateGood()) 97 { 98 markAvailable(false); 99 updateValue(std::numeric_limits<double>::quiet_NaN()); 100 restartRead(); 101 return; 102 } 103 104 std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this(); 105 boost::asio::async_read_until(inputDev, readBuf, '\n', 106 [weakRef](const boost::system::error_code& ec, 107 std::size_t /*bytes_transfered*/) { 108 std::shared_ptr<HwmonTempSensor> self = 109 weakRef.lock(); 110 if (self) 111 { 112 self->handleResponse(ec); 113 } 114 }); 115 } 116 117 void HwmonTempSensor::restartRead() 118 { 119 std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this(); 120 waitTimer.expires_from_now(boost::posix_time::milliseconds(sensorPollMs)); 121 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) { 122 if (ec == boost::asio::error::operation_aborted) 123 { 124 return; // we're being canceled 125 } 126 std::shared_ptr<HwmonTempSensor> self = weakRef.lock(); 127 if (!self) 128 { 129 return; 130 } 131 self->setupRead(); 132 }); 133 } 134 135 void HwmonTempSensor::handleResponse(const boost::system::error_code& err) 136 { 137 if ((err == boost::system::errc::bad_file_descriptor) || 138 (err == boost::asio::error::misc_errors::not_found)) 139 { 140 std::cerr << "Hwmon temp sensor " << name << " removed " << path 141 << "\n"; 142 return; // we're being destroyed 143 } 144 std::istream responseStream(&readBuf); 145 if (!err) 146 { 147 std::string response; 148 std::getline(responseStream, response); 149 try 150 { 151 rawValue = std::stod(response); 152 double nvalue = (rawValue + sensorOffset) * sensorScale; 153 updateValue(nvalue); 154 } 155 catch (const std::invalid_argument&) 156 { 157 incrementError(); 158 } 159 } 160 else 161 { 162 incrementError(); 163 } 164 165 responseStream.clear(); 166 inputDev.close(); 167 int fd = open(path.c_str(), O_RDONLY); 168 if (fd < 0) 169 { 170 std::cerr << "Hwmon temp sensor " << name << " not valid " << path 171 << "\n"; 172 return; // we're no longer valid 173 } 174 inputDev.assign(fd); 175 restartRead(); 176 } 177 178 void HwmonTempSensor::checkThresholds(void) 179 { 180 thresholds::checkThresholds(this); 181 } 182