1 /* 2 // Copyright (c) 2018 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 <TachSensor.hpp> 20 #include <Utils.hpp> 21 #include <boost/algorithm/string/predicate.hpp> 22 #include <boost/algorithm/string/replace.hpp> 23 #include <boost/asio/read_until.hpp> 24 #include <boost/date_time/posix_time/posix_time.hpp> 25 #include <gpiod.hpp> 26 #include <sdbusplus/asio/connection.hpp> 27 #include <sdbusplus/asio/object_server.hpp> 28 29 #include <fstream> 30 #include <iostream> 31 #include <istream> 32 #include <limits> 33 #include <memory> 34 #include <optional> 35 #include <stdexcept> 36 #include <string> 37 #include <utility> 38 #include <vector> 39 40 static constexpr unsigned int pwmPollMs = 500; 41 static constexpr size_t warnAfterErrorCount = 10; 42 43 TachSensor::TachSensor(const std::string& path, const std::string& objectType, 44 sdbusplus::asio::object_server& objectServer, 45 std::shared_ptr<sdbusplus::asio::connection>& conn, 46 std::unique_ptr<PresenceSensor>&& presenceSensor, 47 std::optional<RedundancySensor>* redundancy, 48 boost::asio::io_service& io, const std::string& fanName, 49 std::vector<thresholds::Threshold>&& thresholdsIn, 50 const std::string& sensorConfiguration, 51 const std::pair<size_t, size_t>& limits, 52 const PowerState& powerState, 53 const std::optional<std::string>& ledIn) : 54 Sensor(boost::replace_all_copy(fanName, " ", "_"), std::move(thresholdsIn), 55 sensorConfiguration, objectType, limits.second, limits.first, conn, 56 powerState), 57 objServer(objectServer), redundancy(redundancy), 58 presence(std::move(presenceSensor)), 59 inputDev(io, open(path.c_str(), O_RDONLY)), waitTimer(io), path(path), 60 led(ledIn) 61 { 62 sensorInterface = objectServer.add_interface( 63 "/xyz/openbmc_project/sensors/fan_tach/" + name, 64 "xyz.openbmc_project.Sensor.Value"); 65 66 if (thresholds::hasWarningInterface(thresholds)) 67 { 68 thresholdInterfaceWarning = objectServer.add_interface( 69 "/xyz/openbmc_project/sensors/fan_tach/" + name, 70 "xyz.openbmc_project.Sensor.Threshold.Warning"); 71 } 72 if (thresholds::hasCriticalInterface(thresholds)) 73 { 74 thresholdInterfaceCritical = objectServer.add_interface( 75 "/xyz/openbmc_project/sensors/fan_tach/" + name, 76 "xyz.openbmc_project.Sensor.Threshold.Critical"); 77 } 78 association = objectServer.add_interface( 79 "/xyz/openbmc_project/sensors/fan_tach/" + name, 80 association::interface); 81 82 if (presence) 83 { 84 itemIface = 85 objectServer.add_interface("/xyz/openbmc_project/inventory/" + name, 86 "xyz.openbmc_project.Inventory.Item"); 87 itemIface->register_property("PrettyName", 88 std::string()); // unused property 89 itemIface->register_property("Present", true); 90 itemIface->initialize(); 91 itemAssoc = objectServer.add_interface( 92 "/xyz/openbmc_project/inventory/" + name, association::interface); 93 itemAssoc->register_property( 94 "associations", 95 std::vector<Association>{ 96 {"sensors", "inventory", 97 "/xyz/openbmc_project/sensors/fan_tach/" + name}}); 98 itemAssoc->initialize(); 99 } 100 setInitialProperties(conn); 101 setupRead(); 102 } 103 104 TachSensor::~TachSensor() 105 { 106 // close the input dev to cancel async operations 107 inputDev.close(); 108 waitTimer.cancel(); 109 objServer.remove_interface(thresholdInterfaceWarning); 110 objServer.remove_interface(thresholdInterfaceCritical); 111 objServer.remove_interface(sensorInterface); 112 objServer.remove_interface(association); 113 objServer.remove_interface(itemIface); 114 objServer.remove_interface(itemAssoc); 115 } 116 117 void TachSensor::setupRead(void) 118 { 119 boost::asio::async_read_until( 120 inputDev, readBuf, '\n', 121 [&](const boost::system::error_code& ec, 122 std::size_t /*bytes_transfered*/) { handleResponse(ec); }); 123 } 124 125 void TachSensor::handleResponse(const boost::system::error_code& err) 126 { 127 if (err == boost::system::errc::bad_file_descriptor) 128 { 129 return; // we're being destroyed 130 } 131 bool missing = false; 132 size_t pollTime = pwmPollMs; 133 if (presence) 134 { 135 if (!presence->getValue()) 136 { 137 markAvailable(false); 138 missing = true; 139 pollTime = sensorFailedPollTimeMs; 140 } 141 itemIface->set_property("Present", !missing); 142 } 143 std::istream responseStream(&readBuf); 144 if (!missing) 145 { 146 if (!err) 147 { 148 std::string response; 149 try 150 { 151 std::getline(responseStream, response); 152 rawValue = std::stod(response); 153 responseStream.clear(); 154 updateValue(rawValue); 155 } 156 catch (const std::invalid_argument&) 157 { 158 incrementError(); 159 pollTime = sensorFailedPollTimeMs; 160 } 161 } 162 else 163 { 164 incrementError(); 165 pollTime = sensorFailedPollTimeMs; 166 } 167 } 168 responseStream.clear(); 169 inputDev.close(); 170 int fd = open(path.c_str(), O_RDONLY); 171 if (fd < 0) 172 { 173 return; // we're no longer valid 174 } 175 inputDev.assign(fd); 176 waitTimer.expires_from_now(boost::posix_time::milliseconds(pollTime)); 177 waitTimer.async_wait([&](const boost::system::error_code& ec) { 178 if (ec == boost::asio::error::operation_aborted) 179 { 180 return; // we're being canceled 181 } 182 setupRead(); 183 }); 184 } 185 186 void TachSensor::checkThresholds(void) 187 { 188 bool status = thresholds::checkThresholds(this); 189 190 if (redundancy && *redundancy) 191 { 192 (*redundancy) 193 ->update("/xyz/openbmc_project/sensors/fan_tach/" + name, !status); 194 } 195 196 bool curLed = !status; 197 if (led && ledState != curLed) 198 { 199 ledState = curLed; 200 setLed(dbusConnection, *led, curLed); 201 } 202 } 203 204 PresenceSensor::PresenceSensor(const std::string& gpioName, bool inverted, 205 boost::asio::io_service& io, 206 const std::string& name) : 207 inverted(inverted), 208 gpioLine(gpiod::find_line(gpioName)), gpioFd(io), name(name) 209 { 210 if (!gpioLine) 211 { 212 std::cerr << "Error requesting gpio: " << gpioName << "\n"; 213 status = false; 214 return; 215 } 216 217 try 218 { 219 gpioLine.request({"FanSensor", gpiod::line_request::EVENT_BOTH_EDGES, 220 inverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0}); 221 status = gpioLine.get_value(); 222 223 int gpioLineFd = gpioLine.event_get_fd(); 224 if (gpioLineFd < 0) 225 { 226 std::cerr << "Failed to get " << gpioName << " fd\n"; 227 return; 228 } 229 230 gpioFd.assign(gpioLineFd); 231 } 232 catch (std::system_error&) 233 { 234 std::cerr << "Error reading gpio: " << gpioName << "\n"; 235 status = false; 236 return; 237 } 238 239 monitorPresence(); 240 } 241 242 PresenceSensor::~PresenceSensor() 243 { 244 gpioFd.close(); 245 gpioLine.release(); 246 } 247 248 void PresenceSensor::monitorPresence(void) 249 { 250 gpioFd.async_wait(boost::asio::posix::stream_descriptor::wait_read, 251 [this](const boost::system::error_code& ec) { 252 if (ec == boost::system::errc::bad_file_descriptor) 253 { 254 return; // we're being destroyed 255 } 256 if (ec) 257 { 258 std::cerr << "Error on presence sensor " << name 259 << " \n"; 260 ; 261 } 262 else 263 { 264 read(); 265 } 266 monitorPresence(); 267 }); 268 } 269 270 void PresenceSensor::read(void) 271 { 272 gpioLine.event_read(); 273 status = gpioLine.get_value(); 274 // Read is invoked when an edge event is detected by monitorPresence 275 if (status) 276 { 277 logFanInserted(name); 278 } 279 else 280 { 281 logFanRemoved(name); 282 } 283 } 284 285 bool PresenceSensor::getValue(void) 286 { 287 return status; 288 } 289 290 RedundancySensor::RedundancySensor(size_t count, 291 const std::vector<std::string>& children, 292 sdbusplus::asio::object_server& objectServer, 293 const std::string& sensorConfiguration) : 294 count(count), 295 iface(objectServer.add_interface( 296 "/xyz/openbmc_project/control/FanRedundancy/Tach", 297 "xyz.openbmc_project.Control.FanRedundancy")), 298 association(objectServer.add_interface( 299 "/xyz/openbmc_project/control/FanRedundancy/Tach", 300 association::interface)), 301 objectServer(objectServer) 302 { 303 createAssociation(association, sensorConfiguration); 304 iface->register_property("Collection", children); 305 iface->register_property("Status", std::string("Full")); 306 iface->register_property("AllowedFailures", static_cast<uint8_t>(count)); 307 iface->initialize(); 308 } 309 RedundancySensor::~RedundancySensor() 310 { 311 objectServer.remove_interface(association); 312 objectServer.remove_interface(iface); 313 } 314 void RedundancySensor::update(const std::string& name, bool failed) 315 { 316 statuses[name] = failed; 317 size_t failedCount = 0; 318 319 std::string newState = redundancy::full; 320 for (const auto& status : statuses) 321 { 322 if (status.second) 323 { 324 failedCount++; 325 } 326 if (failedCount > count) 327 { 328 newState = redundancy::failed; 329 break; 330 } 331 if (failedCount) 332 { 333 newState = redundancy::degraded; 334 } 335 } 336 if (state != newState) 337 { 338 if (state == redundancy::full) 339 { 340 logFanRedundancyLost(); 341 } 342 else if (newState == redundancy::full) 343 { 344 logFanRedundancyRestored(); 345 } 346 state = newState; 347 iface->set_property("Status", state); 348 } 349 } 350