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; 160 try 161 { 162 val = sysfs::readSysfsWithCallout(hwmonRoot, 163 instance, 164 sensor.first, 165 sensor.second, 166 hwmon::entry::input); 167 } 168 catch(const std::exception& ioe) 169 { 170 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error; 171 commit<ReadFailure>(); 172 173 return static_cast<std::shared_ptr<ValueObject>>(nullptr); 174 } 175 176 auto iface = std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals); 177 iface->value(val); 178 179 Attributes attrs; 180 if (getAttributes(sensor.first, attrs)) 181 { 182 iface->unit(getUnit(attrs)); 183 iface->scale(getScale(attrs)); 184 } 185 186 obj[InterfaceType::VALUE] = iface; 187 return iface; 188 } 189 190 MainLoop::MainLoop( 191 sdbusplus::bus::bus&& bus, 192 const std::string& path, 193 const char* prefix, 194 const char* root) 195 : _bus(std::move(bus)), 196 _manager(_bus, root), 197 _shutdown(false), 198 _hwmonRoot(), 199 _instance(), 200 _prefix(prefix), 201 _root(root), 202 state() 203 { 204 std::string p = path; 205 while (!p.empty() && p.back() == '/') 206 { 207 p.pop_back(); 208 } 209 210 auto n = p.rfind('/'); 211 if (n != std::string::npos) 212 { 213 _instance.assign(p.substr(n + 1)); 214 _hwmonRoot.assign(p.substr(0, n)); 215 } 216 217 assert(!_instance.empty()); 218 assert(!_hwmonRoot.empty()); 219 } 220 221 void MainLoop::shutdown() noexcept 222 { 223 _shutdown = true; 224 } 225 226 void MainLoop::run() 227 { 228 // Check sysfs for available sensors. 229 auto sensors = std::make_unique<SensorSet>(_hwmonRoot + '/' + _instance); 230 231 for (auto& i : *sensors) 232 { 233 std::string label; 234 235 /* 236 * Check if the value of the MODE_<item><X> env variable for the sensor 237 * is "label", then read the sensor number from the <item><X>_label 238 * file. The name of the DBUS object would be the value of the env 239 * variable LABEL_<item><sensorNum>. If the MODE_<item><X> env variable 240 * does'nt exist, then the name of DBUS object is the value of the env 241 * variable LABEL_<item><X>. 242 */ 243 auto mode = getEnv("MODE", i.first); 244 if (!mode.compare(hwmon::entry::label)) 245 { 246 label = getIndirectLabelEnv( 247 "LABEL", _hwmonRoot + '/' + _instance + '/', i.first); 248 if (label.empty()) 249 { 250 continue; 251 } 252 } 253 else 254 { 255 // Ignore inputs without a label. 256 label = getEnv("LABEL", i.first); 257 if (label.empty()) 258 { 259 continue; 260 } 261 } 262 263 Attributes attrs; 264 if (!getAttributes(i.first.first, attrs)) 265 { 266 continue; 267 } 268 269 std::string objectPath{_root}; 270 objectPath.append(1, '/'); 271 objectPath.append(getNamespace(attrs)); 272 objectPath.append(1, '/'); 273 objectPath.append(label); 274 275 ObjectInfo info(&_bus, std::move(objectPath), Object()); 276 auto valueInterface = addValue(i.first, _hwmonRoot, _instance, info); 277 if (!valueInterface) 278 { 279 continue; /* skip adding this sensor for now. */ 280 } 281 auto sensorValue = valueInterface->value(); 282 addThreshold<WarningObject>(i.first, sensorValue, info); 283 addThreshold<CriticalObject>(i.first, sensorValue, info); 284 //TODO openbmc/openbmc#1347 285 // Handle application restarts to set/refresh fan speed values 286 auto target = addTarget<hwmon::FanSpeed>( 287 i.first, _hwmonRoot, _instance, info); 288 289 if (target) 290 { 291 target->enable(); 292 } 293 294 // All the interfaces have been created. Go ahead 295 // and emit InterfacesAdded. 296 valueInterface->emit_object_added(); 297 298 auto value = std::make_tuple( 299 std::move(i.second), 300 std::move(label), 301 std::move(info)); 302 303 state[std::move(i.first)] = std::move(value); 304 } 305 306 /* If there are no sensors specified by labels, exit. */ 307 if (0 == state.size()) 308 { 309 return; 310 } 311 312 { 313 std::string busname{_prefix}; 314 busname.append(1, '.'); 315 busname.append(_instance); 316 _bus.request_name(busname.c_str()); 317 } 318 319 { 320 auto interval = getenv("INTERVAL"); 321 if (interval) 322 { 323 _interval = strtoull(interval, NULL, 10); 324 } 325 } 326 327 // TODO: Issue#3 - Need to make calls to the dbus sensor cache here to 328 // ensure the objects all exist? 329 330 // Polling loop. 331 while (!_shutdown) 332 { 333 std::vector<SensorSet::key_type> destroy; 334 // Iterate through all the sensors. 335 for (auto& i : state) 336 { 337 auto& attrs = std::get<0>(i.second); 338 if (attrs.find(hwmon::entry::input) != attrs.end()) 339 { 340 // Read value from sensor. 341 int value; 342 try 343 { 344 value = sysfs::readSysfsWithCallout(_hwmonRoot, 345 _instance, 346 i.first.first, 347 i.first.second, 348 hwmon::entry::input); 349 350 auto& objInfo = std::get<ObjectInfo>(i.second); 351 auto& obj = std::get<Object>(objInfo); 352 353 for (auto& iface : obj) 354 { 355 auto valueIface = std::shared_ptr<ValueObject>(); 356 auto warnIface = std::shared_ptr<WarningObject>(); 357 auto critIface = std::shared_ptr<CriticalObject>(); 358 359 switch (iface.first) 360 { 361 case InterfaceType::VALUE: 362 valueIface = std::experimental::any_cast<std::shared_ptr<ValueObject>> 363 (iface.second); 364 valueIface->value(value); 365 break; 366 case InterfaceType::WARN: 367 checkThresholds<WarningObject>(iface.second, value); 368 break; 369 case InterfaceType::CRIT: 370 checkThresholds<CriticalObject>(iface.second, value); 371 break; 372 default: 373 break; 374 } 375 } 376 } 377 catch (const std::exception& e) 378 { 379 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::Error; 380 commit<ReadFailure>(); 381 382 destroy.push_back(i.first); 383 } 384 } 385 } 386 387 for (auto& i : destroy) 388 { 389 state.erase(i); 390 } 391 392 // Respond to DBus 393 _bus.process_discard(); 394 395 // Sleep until next interval. 396 // TODO: Issue#6 - Optionally look at polling interval sysfs entry. 397 _bus.wait(_interval); 398 399 // TODO: Issue#7 - Should probably periodically check the SensorSet 400 // for new entries. 401 } 402 } 403 404 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 405