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 42 HwmonTempSensor::HwmonTempSensor( 43 const std::string& path, const std::string& objectType, 44 sdbusplus::asio::object_server& objectServer, 45 std::shared_ptr<sdbusplus::asio::connection>& conn, 46 boost::asio::io_service& io, const std::string& sensorName, 47 std::vector<thresholds::Threshold>&& thresholdsIn, 48 const struct SensorParams& thisSensorParameters, const float pollRate, 49 const std::string& sensorConfiguration, const PowerState powerState) : 50 Sensor(boost::replace_all_copy(sensorName, " ", "_"), 51 std::move(thresholdsIn), sensorConfiguration, objectType, false, 52 false, thisSensorParameters.maxValue, thisSensorParameters.minValue, 53 conn, powerState), 54 objServer(objectServer), inputDev(io), waitTimer(io), path(path), 55 offsetValue(thisSensorParameters.offsetValue), 56 scaleValue(thisSensorParameters.scaleValue), 57 sensorPollMs(static_cast<unsigned int>(pollRate * 1000)) 58 { 59 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 60 int fd = open(path.c_str(), O_RDONLY); 61 if (fd < 0) 62 { 63 std::cerr << "HwmonTempSensor " << sensorName << " failed to open " 64 << path << "\n"; 65 } 66 inputDev.assign(fd); 67 68 sensorInterface = objectServer.add_interface( 69 "/xyz/openbmc_project/sensors/" + thisSensorParameters.typeName + "/" + 70 name, 71 "xyz.openbmc_project.Sensor.Value"); 72 73 for (const auto& threshold : thresholds) 74 { 75 std::string interface = thresholds::getInterface(threshold.level); 76 thresholdInterfaces[static_cast<size_t>(threshold.level)] = 77 objectServer.add_interface("/xyz/openbmc_project/sensors/" + 78 thisSensorParameters.typeName + "/" + 79 name, 80 interface); 81 } 82 association = objectServer.add_interface("/xyz/openbmc_project/sensors/" + 83 thisSensorParameters.typeName + 84 "/" + name, 85 association::interface); 86 setInitialProperties(thisSensorParameters.units); 87 } 88 89 HwmonTempSensor::~HwmonTempSensor() 90 { 91 // close the input dev to cancel async operations 92 inputDev.close(); 93 waitTimer.cancel(); 94 for (const auto& iface : thresholdInterfaces) 95 { 96 objServer.remove_interface(iface); 97 } 98 objServer.remove_interface(sensorInterface); 99 objServer.remove_interface(association); 100 } 101 102 void HwmonTempSensor::setupRead(void) 103 { 104 if (!readingStateGood()) 105 { 106 markAvailable(false); 107 updateValue(std::numeric_limits<double>::quiet_NaN()); 108 restartRead(); 109 return; 110 } 111 112 std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this(); 113 boost::asio::async_read_until(inputDev, readBuf, '\n', 114 [weakRef](const boost::system::error_code& ec, 115 std::size_t /*bytes_transfered*/) { 116 std::shared_ptr<HwmonTempSensor> self = weakRef.lock(); 117 if (self) 118 { 119 self->handleResponse(ec); 120 } 121 }); 122 } 123 124 void HwmonTempSensor::restartRead() 125 { 126 std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this(); 127 waitTimer.expires_from_now(boost::posix_time::milliseconds(sensorPollMs)); 128 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) { 129 if (ec == boost::asio::error::operation_aborted) 130 { 131 return; // we're being canceled 132 } 133 std::shared_ptr<HwmonTempSensor> self = weakRef.lock(); 134 if (!self) 135 { 136 return; 137 } 138 self->setupRead(); 139 }); 140 } 141 142 void HwmonTempSensor::handleResponse(const boost::system::error_code& err) 143 { 144 if ((err == boost::system::errc::bad_file_descriptor) || 145 (err == boost::asio::error::misc_errors::not_found)) 146 { 147 std::cerr << "Hwmon temp sensor " << name << " removed " << path 148 << "\n"; 149 return; // we're being destroyed 150 } 151 std::istream responseStream(&readBuf); 152 if (!err) 153 { 154 std::string response; 155 std::getline(responseStream, response); 156 try 157 { 158 rawValue = std::stod(response); 159 double nvalue = (rawValue + offsetValue) * scaleValue; 160 updateValue(nvalue); 161 } 162 catch (const std::invalid_argument&) 163 { 164 incrementError(); 165 } 166 } 167 else 168 { 169 incrementError(); 170 } 171 172 responseStream.clear(); 173 inputDev.close(); 174 175 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 176 int fd = open(path.c_str(), O_RDONLY); 177 if (fd < 0) 178 { 179 std::cerr << "Hwmon temp sensor " << name << " not valid " << path 180 << "\n"; 181 return; // we're no longer valid 182 } 183 inputDev.assign(fd); 184 restartRead(); 185 } 186 187 void HwmonTempSensor::checkThresholds(void) 188 { 189 thresholds::checkThresholds(this); 190 } 191