1 /** 2 * Copyright © 2016 IBM 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 #include <iostream> 17 #include <memory> 18 #include <cstdlib> 19 #include <algorithm> 20 21 #include <phosphor-logging/elog-errors.hpp> 22 #include "sensorset.hpp" 23 #include "hwmon.hpp" 24 #include "sysfs.hpp" 25 #include "mainloop.hpp" 26 #include "env.hpp" 27 #include "thresholds.hpp" 28 #include "targets.hpp" 29 #include "fan_speed.hpp" 30 31 #include <xyz/openbmc_project/Sensor/Device/error.hpp> 32 33 using namespace phosphor::logging; 34 35 // Initialization for Warning Objects 36 decltype(Thresholds<WarningObject>::setLo) Thresholds<WarningObject>::setLo = 37 &WarningObject::warningLow; 38 decltype(Thresholds<WarningObject>::setHi) Thresholds<WarningObject>::setHi = 39 &WarningObject::warningHigh; 40 decltype(Thresholds<WarningObject>::getLo) Thresholds<WarningObject>::getLo = 41 &WarningObject::warningLow; 42 decltype(Thresholds<WarningObject>::getHi) Thresholds<WarningObject>::getHi = 43 &WarningObject::warningHigh; 44 decltype(Thresholds<WarningObject>::alarmLo) Thresholds<WarningObject>::alarmLo = 45 &WarningObject::warningAlarmLow; 46 decltype(Thresholds<WarningObject>::alarmHi) Thresholds<WarningObject>::alarmHi = 47 &WarningObject::warningAlarmHigh; 48 49 // Initialization for Critical Objects 50 decltype(Thresholds<CriticalObject>::setLo) Thresholds<CriticalObject>::setLo = 51 &CriticalObject::criticalLow; 52 decltype(Thresholds<CriticalObject>::setHi) Thresholds<CriticalObject>::setHi = 53 &CriticalObject::criticalHigh; 54 decltype(Thresholds<CriticalObject>::getLo) Thresholds<CriticalObject>::getLo = 55 &CriticalObject::criticalLow; 56 decltype(Thresholds<CriticalObject>::getHi) Thresholds<CriticalObject>::getHi = 57 &CriticalObject::criticalHigh; 58 decltype(Thresholds<CriticalObject>::alarmLo) Thresholds<CriticalObject>::alarmLo = 59 &CriticalObject::criticalAlarmLow; 60 decltype(Thresholds<CriticalObject>::alarmHi) Thresholds<CriticalObject>::alarmHi = 61 &CriticalObject::criticalAlarmHigh; 62 63 64 65 static constexpr auto typeAttrMap = 66 { 67 // 1 - hwmon class 68 // 2 - unit 69 // 3 - sysfs scaling factor 70 std::make_tuple( 71 hwmon::type::ctemp, 72 ValueInterface::Unit::DegreesC, 73 -3, 74 "temperature"), 75 std::make_tuple( 76 hwmon::type::cfan, 77 ValueInterface::Unit::RPMS, 78 0, 79 "fan_tach"), 80 std::make_tuple( 81 hwmon::type::cvolt, 82 ValueInterface::Unit::Volts, 83 -3, 84 "voltage"), 85 std::make_tuple( 86 hwmon::type::ccurr, 87 ValueInterface::Unit::Amperes, 88 -3, 89 "current"), 90 std::make_tuple( 91 hwmon::type::cenergy, 92 ValueInterface::Unit::Joules, 93 -6, 94 "energy"), 95 std::make_tuple( 96 hwmon::type::cpower, 97 ValueInterface::Unit::Watts, 98 -6, 99 "power"), 100 }; 101 102 auto getHwmonType(decltype(typeAttrMap)::const_reference attrs) 103 { 104 return std::get<0>(attrs); 105 } 106 107 auto getUnit(decltype(typeAttrMap)::const_reference attrs) 108 { 109 return std::get<1>(attrs); 110 } 111 112 auto getScale(decltype(typeAttrMap)::const_reference attrs) 113 { 114 return std::get<2>(attrs); 115 } 116 117 auto getNamespace(decltype(typeAttrMap)::const_reference attrs) 118 { 119 return std::get<3>(attrs); 120 } 121 122 using AttributeIterator = decltype(*typeAttrMap.begin()); 123 using Attributes 124 = std::remove_cv<std::remove_reference<AttributeIterator>::type>::type; 125 126 auto getAttributes(const std::string& type, Attributes& attributes) 127 { 128 // *INDENT-OFF* 129 auto a = std::find_if( 130 typeAttrMap.begin(), 131 typeAttrMap.end(), 132 [&](const auto & e) 133 { 134 return type == getHwmonType(e); 135 }); 136 // *INDENT-ON* 137 138 if (a == typeAttrMap.end()) 139 { 140 return false; 141 } 142 143 attributes = *a; 144 return true; 145 } 146 147 auto addValue(const SensorSet::key_type& sensor, 148 const std::string& hwmonRoot, 149 const std::string& instance, 150 ObjectInfo& info) 151 { 152 static constexpr bool deferSignals = true; 153 154 // Get the initial value for the value interface. 155 auto& bus = *std::get<sdbusplus::bus::bus*>(info); 156 auto& obj = std::get<Object>(info); 157 auto& objPath = std::get<std::string>(info); 158 159 int val = 0; 160 bool retry = true; 161 size_t count = 10; 162 163 164 //Retry for up to a second if device is busy 165 166 while (retry) 167 { 168 try 169 { 170 val = sysfs::readSysfsWithCallout(hwmonRoot, 171 instance, 172 sensor.first, 173 sensor.second, 174 hwmon::entry::input, 175 count > 0); //throw DeviceBusy until last attempt 176 } 177 catch (sysfs::DeviceBusyException& e) 178 { 179 count--; 180 std::this_thread::sleep_for(std::chrono::milliseconds{100}); 181 continue; 182 } 183 catch(const std::exception& ioe) 184 { 185 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error; 186 commit<ReadFailure>(); 187 188 return static_cast<std::shared_ptr<ValueObject>>(nullptr); 189 } 190 retry = false; 191 } 192 193 auto iface = std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals); 194 iface->value(val); 195 196 Attributes attrs; 197 if (getAttributes(sensor.first, attrs)) 198 { 199 iface->unit(getUnit(attrs)); 200 iface->scale(getScale(attrs)); 201 } 202 203 obj[InterfaceType::VALUE] = iface; 204 return iface; 205 } 206 207 MainLoop::MainLoop( 208 sdbusplus::bus::bus&& bus, 209 const std::string& path, 210 const char* prefix, 211 const char* root) 212 : _bus(std::move(bus)), 213 _manager(_bus, root), 214 _shutdown(false), 215 _hwmonRoot(), 216 _instance(), 217 _prefix(prefix), 218 _root(root), 219 state() 220 { 221 std::string p = path; 222 while (!p.empty() && p.back() == '/') 223 { 224 p.pop_back(); 225 } 226 227 auto n = p.rfind('/'); 228 if (n != std::string::npos) 229 { 230 _instance.assign(p.substr(n + 1)); 231 _hwmonRoot.assign(p.substr(0, n)); 232 } 233 234 assert(!_instance.empty()); 235 assert(!_hwmonRoot.empty()); 236 } 237 238 void MainLoop::shutdown() noexcept 239 { 240 _shutdown = true; 241 } 242 243 void MainLoop::run() 244 { 245 // Check sysfs for available sensors. 246 auto sensors = std::make_unique<SensorSet>(_hwmonRoot + '/' + _instance); 247 248 for (auto& i : *sensors) 249 { 250 std::string label; 251 252 /* 253 * Check if the value of the MODE_<item><X> env variable for the sensor 254 * is "label", then read the sensor number from the <item><X>_label 255 * file. The name of the DBUS object would be the value of the env 256 * variable LABEL_<item><sensorNum>. If the MODE_<item><X> env variable 257 * does'nt exist, then the name of DBUS object is the value of the env 258 * variable LABEL_<item><X>. 259 */ 260 auto mode = getEnv("MODE", i.first); 261 if (!mode.compare(hwmon::entry::label)) 262 { 263 label = getIndirectLabelEnv( 264 "LABEL", _hwmonRoot + '/' + _instance + '/', i.first); 265 if (label.empty()) 266 { 267 continue; 268 } 269 } 270 else 271 { 272 // Ignore inputs without a label. 273 label = getEnv("LABEL", i.first); 274 if (label.empty()) 275 { 276 continue; 277 } 278 } 279 280 Attributes attrs; 281 if (!getAttributes(i.first.first, attrs)) 282 { 283 continue; 284 } 285 286 std::string objectPath{_root}; 287 objectPath.append(1, '/'); 288 objectPath.append(getNamespace(attrs)); 289 objectPath.append(1, '/'); 290 objectPath.append(label); 291 292 ObjectInfo info(&_bus, std::move(objectPath), Object()); 293 auto valueInterface = addValue(i.first, _hwmonRoot, _instance, info); 294 if (!valueInterface) 295 { 296 continue; /* skip adding this sensor for now. */ 297 } 298 auto sensorValue = valueInterface->value(); 299 addThreshold<WarningObject>(i.first, sensorValue, info); 300 addThreshold<CriticalObject>(i.first, sensorValue, info); 301 //TODO openbmc/openbmc#1347 302 // Handle application restarts to set/refresh fan speed values 303 auto target = addTarget<hwmon::FanSpeed>( 304 i.first, _hwmonRoot, _instance, info); 305 306 if (target) 307 { 308 target->enable(); 309 } 310 311 // All the interfaces have been created. Go ahead 312 // and emit InterfacesAdded. 313 valueInterface->emit_object_added(); 314 315 auto value = std::make_tuple( 316 std::move(i.second), 317 std::move(label), 318 std::move(info)); 319 320 state[std::move(i.first)] = std::move(value); 321 } 322 323 /* If there are no sensors specified by labels, exit. */ 324 if (0 == state.size()) 325 { 326 return; 327 } 328 329 { 330 std::string busname{_prefix}; 331 busname.append(1, '.'); 332 busname.append(_instance); 333 _bus.request_name(busname.c_str()); 334 } 335 336 { 337 auto interval = getenv("INTERVAL"); 338 if (interval) 339 { 340 _interval = strtoull(interval, NULL, 10); 341 } 342 } 343 344 // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to 345 // ensure the objects all exist? 346 347 // Polling loop. 348 while (!_shutdown) 349 { 350 std::vector<SensorSet::key_type> destroy; 351 // Iterate through all the sensors. 352 for (auto& i : state) 353 { 354 auto& attrs = std::get<0>(i.second); 355 if (attrs.find(hwmon::entry::input) != attrs.end()) 356 { 357 // Read value from sensor. 358 int value; 359 try 360 { 361 try 362 { 363 value = sysfs::readSysfsWithCallout(_hwmonRoot, 364 _instance, 365 i.first.first, 366 i.first.second, 367 hwmon::entry::input); 368 } 369 catch (sysfs::DeviceBusyException& e) 370 { 371 //Just go with the current values and try again later. 372 //TODO: openbmc/openbmc#2048 could keep an eye on 373 //how long the device is actually busy. 374 continue; 375 } 376 377 auto& objInfo = std::get<ObjectInfo>(i.second); 378 auto& obj = std::get<Object>(objInfo); 379 380 for (auto& iface : obj) 381 { 382 auto valueIface = std::shared_ptr<ValueObject>(); 383 auto warnIface = std::shared_ptr<WarningObject>(); 384 auto critIface = std::shared_ptr<CriticalObject>(); 385 386 switch (iface.first) 387 { 388 case InterfaceType::VALUE: 389 valueIface = std::experimental::any_cast<std::shared_ptr<ValueObject>> 390 (iface.second); 391 valueIface->value(value); 392 break; 393 case InterfaceType::WARN: 394 checkThresholds<WarningObject>(iface.second, value); 395 break; 396 case InterfaceType::CRIT: 397 checkThresholds<CriticalObject>(iface.second, value); 398 break; 399 default: 400 break; 401 } 402 } 403 } 404 catch (const std::exception& e) 405 { 406 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error; 407 commit<ReadFailure>(); 408 409 destroy.push_back(i.first); 410 } 411 } 412 } 413 414 for (auto& i : destroy) 415 { 416 state.erase(i); 417 } 418 419 // Respond to DBus 420 _bus.process_discard(); 421 422 // Sleep until next interval. 423 // TODO: Issue#6 - Optionally look at polling interval sysfs entry. 424 _bus.wait(_interval); 425 426 // TODO: Issue#7 - Should probably periodically check the SensorSet 427 // for new entries. 428 } 429 } 430 431 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 432