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 std::enable_shared_from_this<HwmonTempSensor>(), objServer(objectServer), 55 inputDev(io, open(path.c_str(), O_RDONLY)), waitTimer(io), path(path), 56 offsetValue(thisSensorParameters.offsetValue), 57 scaleValue(thisSensorParameters.scaleValue), 58 sensorPollMs(static_cast<unsigned int>(pollRate * 1000)) 59 { 60 sensorInterface = objectServer.add_interface( 61 "/xyz/openbmc_project/sensors/" + thisSensorParameters.typeName + "/" + 62 name, 63 "xyz.openbmc_project.Sensor.Value"); 64 65 for (const auto& threshold : thresholds) 66 { 67 std::string interface = thresholds::getInterface(threshold.level); 68 thresholdInterfaces[static_cast<size_t>(threshold.level)] = 69 objectServer.add_interface("/xyz/openbmc_project/sensors/" + 70 thisSensorParameters.typeName + "/" + 71 name, 72 interface); 73 } 74 association = objectServer.add_interface("/xyz/openbmc_project/sensors/" + 75 thisSensorParameters.typeName + 76 "/" + name, 77 association::interface); 78 setInitialProperties(thisSensorParameters.units); 79 } 80 81 HwmonTempSensor::~HwmonTempSensor() 82 { 83 // close the input dev to cancel async operations 84 inputDev.close(); 85 waitTimer.cancel(); 86 for (const auto& iface : thresholdInterfaces) 87 { 88 objServer.remove_interface(iface); 89 } 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 + offsetValue) * scaleValue; 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