16714a25aSJames Feist /* 26714a25aSJames Feist // Copyright (c) 2017 Intel Corporation 36714a25aSJames Feist // 46714a25aSJames Feist // Licensed under the Apache License, Version 2.0 (the "License"); 56714a25aSJames Feist // you may not use this file except in compliance with the License. 66714a25aSJames Feist // You may obtain a copy of the License at 76714a25aSJames Feist // 86714a25aSJames Feist // http://www.apache.org/licenses/LICENSE-2.0 96714a25aSJames Feist // 106714a25aSJames Feist // Unless required by applicable law or agreed to in writing, software 116714a25aSJames Feist // distributed under the License is distributed on an "AS IS" BASIS, 126714a25aSJames Feist // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136714a25aSJames Feist // See the License for the specific language governing permissions and 146714a25aSJames Feist // limitations under the License. 156714a25aSJames Feist */ 166714a25aSJames Feist 17ca44b2f3SPatrick Venture #include "HwmonTempSensor.hpp" 18ca44b2f3SPatrick Venture #include "Utils.hpp" 19ca44b2f3SPatrick Venture 2096e97db7SPatrick Venture #include <array> 216714a25aSJames Feist #include <boost/algorithm/string/predicate.hpp> 226714a25aSJames Feist #include <boost/algorithm/string/replace.hpp> 2396e97db7SPatrick Venture #include <boost/container/flat_map.hpp> 246714a25aSJames Feist #include <boost/container/flat_set.hpp> 2524f02f24SJames Feist #include <filesystem> 266714a25aSJames Feist #include <fstream> 2796e97db7SPatrick Venture #include <functional> 2896e97db7SPatrick Venture #include <memory> 296714a25aSJames Feist #include <regex> 306714a25aSJames Feist #include <sdbusplus/asio/connection.hpp> 316714a25aSJames Feist #include <sdbusplus/asio/object_server.hpp> 3296e97db7SPatrick Venture #include <sdbusplus/bus/match.hpp> 3396e97db7SPatrick Venture #include <stdexcept> 3496e97db7SPatrick Venture #include <string> 3596e97db7SPatrick Venture #include <utility> 3696e97db7SPatrick Venture #include <variant> 3796e97db7SPatrick Venture #include <vector> 386714a25aSJames Feist 396714a25aSJames Feist static constexpr bool DEBUG = false; 406714a25aSJames Feist 41cf3bce6eSJames Feist namespace fs = std::filesystem; 428b3f7d40SAlex Qiu static constexpr std::array<const char*, 8> sensorTypes = { 438b3f7d40SAlex Qiu "xyz.openbmc_project.Configuration.EMC1413", 448b3f7d40SAlex Qiu "xyz.openbmc_project.Configuration.MAX31725", 458b3f7d40SAlex Qiu "xyz.openbmc_project.Configuration.MAX31730", 4655ab2afbSJohn Wang "xyz.openbmc_project.Configuration.TMP112", 473546adb9SPatrick Venture "xyz.openbmc_project.Configuration.TMP175", 488b3f7d40SAlex Qiu "xyz.openbmc_project.Configuration.TMP421", 498b3f7d40SAlex Qiu "xyz.openbmc_project.Configuration.TMP441", 508b3f7d40SAlex Qiu "xyz.openbmc_project.Configuration.TMP75"}; 516714a25aSJames Feist 526714a25aSJames Feist void createSensors( 536714a25aSJames Feist boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer, 546714a25aSJames Feist boost::container::flat_map<std::string, std::unique_ptr<HwmonTempSensor>>& 556714a25aSJames Feist sensors, 566714a25aSJames Feist std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 576714a25aSJames Feist const std::unique_ptr<boost::container::flat_set<std::string>>& 586714a25aSJames Feist sensorsChanged) 596714a25aSJames Feist { 60df515159SJames Feist auto getter = std::make_shared<GetSensorConfiguration>( 61df515159SJames Feist dbusConnection, 62df515159SJames Feist std::move([&io, &objectServer, &sensors, &dbusConnection, 63df515159SJames Feist &sensorsChanged]( 64df515159SJames Feist const ManagedObjectType& sensorConfigurations) { 656714a25aSJames Feist bool firstScan = sensorsChanged == nullptr; 66df515159SJames Feist 676714a25aSJames Feist std::vector<fs::path> paths; 68df515159SJames Feist if (!findFiles(fs::path("/sys/class/hwmon"), R"(temp\d+_input)", 69df515159SJames Feist paths)) 706714a25aSJames Feist { 716714a25aSJames Feist std::cerr << "No temperature sensors in system\n"; 726714a25aSJames Feist return; 736714a25aSJames Feist } 746714a25aSJames Feist 7537266ca9SJames Feist boost::container::flat_set<std::string> directories; 7637266ca9SJames Feist 77df515159SJames Feist // iterate through all found temp sensors, and try to match them 78df515159SJames Feist // with configuration 796714a25aSJames Feist for (auto& path : paths) 806714a25aSJames Feist { 816714a25aSJames Feist std::smatch match; 8237266ca9SJames Feist const std::string& pathStr = path.string(); 836714a25aSJames Feist auto directory = path.parent_path(); 846714a25aSJames Feist 8537266ca9SJames Feist auto ret = directories.insert(directory.string()); 8637266ca9SJames Feist if (!ret.second) 876714a25aSJames Feist { 8837266ca9SJames Feist continue; // already searched this path 8937266ca9SJames Feist } 9037266ca9SJames Feist 91b6c0b914SJames Feist fs::path device = directory / "device"; 9237266ca9SJames Feist std::string deviceName = fs::canonical(device).stem(); 9337266ca9SJames Feist auto findHyphen = deviceName.find("-"); 9437266ca9SJames Feist if (findHyphen == std::string::npos) 9537266ca9SJames Feist { 9637266ca9SJames Feist std::cerr << "found bad device " << deviceName << "\n"; 976714a25aSJames Feist continue; 986714a25aSJames Feist } 9937266ca9SJames Feist std::string busStr = deviceName.substr(0, findHyphen); 10037266ca9SJames Feist std::string addrStr = deviceName.substr(findHyphen + 1); 10137266ca9SJames Feist 10237266ca9SJames Feist size_t bus = 0; 10337266ca9SJames Feist size_t addr = 0; 10437266ca9SJames Feist try 1056714a25aSJames Feist { 10637266ca9SJames Feist bus = std::stoi(busStr); 10737266ca9SJames Feist addr = std::stoi(addrStr, 0, 16); 10837266ca9SJames Feist } 109b6c0b914SJames Feist catch (std::invalid_argument&) 11037266ca9SJames Feist { 1116714a25aSJames Feist continue; 1126714a25aSJames Feist } 1136714a25aSJames Feist const SensorData* sensorData = nullptr; 1146714a25aSJames Feist const std::string* interfacePath = nullptr; 11537266ca9SJames Feist const char* sensorType = nullptr; 1168b3f7d40SAlex Qiu const SensorBaseConfiguration* baseConfiguration = nullptr; 1178b3f7d40SAlex Qiu const SensorBaseConfigMap* baseConfigMap = nullptr; 11837266ca9SJames Feist 119df515159SJames Feist for (const std::pair<sdbusplus::message::object_path, 120df515159SJames Feist SensorData>& sensor : sensorConfigurations) 12137266ca9SJames Feist { 12237266ca9SJames Feist sensorData = &(sensor.second); 1239ced0a38SJae Hyun Yoo for (const char* type : sensorTypes) 1246714a25aSJames Feist { 1256714a25aSJames Feist auto sensorBase = sensorData->find(type); 1266714a25aSJames Feist if (sensorBase != sensorData->end()) 1276714a25aSJames Feist { 1286714a25aSJames Feist baseConfiguration = &(*sensorBase); 1296714a25aSJames Feist sensorType = type; 1306714a25aSJames Feist break; 1316714a25aSJames Feist } 1326714a25aSJames Feist } 1336714a25aSJames Feist if (baseConfiguration == nullptr) 1346714a25aSJames Feist { 13537266ca9SJames Feist std::cerr << "error finding base configuration for " 13637266ca9SJames Feist << deviceName << "\n"; 13737266ca9SJames Feist continue; 13837266ca9SJames Feist } 1398b3f7d40SAlex Qiu baseConfigMap = &baseConfiguration->second; 1408b3f7d40SAlex Qiu auto configurationBus = baseConfigMap->find("Bus"); 1418b3f7d40SAlex Qiu auto configurationAddress = baseConfigMap->find("Address"); 14237266ca9SJames Feist 1438b3f7d40SAlex Qiu if (configurationBus == baseConfigMap->end() || 1448b3f7d40SAlex Qiu configurationAddress == baseConfigMap->end()) 14537266ca9SJames Feist { 1468b3f7d40SAlex Qiu std::cerr << "error finding bus or address in " 1478b3f7d40SAlex Qiu "configuration\n"; 14837266ca9SJames Feist continue; 14937266ca9SJames Feist } 15037266ca9SJames Feist 1513eb82629SJames Feist if (std::get<uint64_t>(configurationBus->second) != bus || 152df515159SJames Feist std::get<uint64_t>(configurationAddress->second) != 153df515159SJames Feist addr) 15437266ca9SJames Feist { 15537266ca9SJames Feist continue; 15637266ca9SJames Feist } 15737266ca9SJames Feist 15837266ca9SJames Feist interfacePath = &(sensor.first.str); 15937266ca9SJames Feist break; 16037266ca9SJames Feist } 16137266ca9SJames Feist if (interfacePath == nullptr) 16237266ca9SJames Feist { 163df515159SJames Feist std::cerr << "failed to find match for " << deviceName 164df515159SJames Feist << "\n"; 1656714a25aSJames Feist continue; 1666714a25aSJames Feist } 1676714a25aSJames Feist 1688b3f7d40SAlex Qiu auto findSensorName = baseConfigMap->find("Name"); 1698b3f7d40SAlex Qiu if (findSensorName == baseConfigMap->end()) 1706714a25aSJames Feist { 1716714a25aSJames Feist std::cerr << "could not determine configuration name for " 17237266ca9SJames Feist << deviceName << "\n"; 1736714a25aSJames Feist continue; 1746714a25aSJames Feist } 175df515159SJames Feist std::string sensorName = 176df515159SJames Feist std::get<std::string>(findSensorName->second); 1776714a25aSJames Feist // on rescans, only update sensors we were signaled by 1786714a25aSJames Feist auto findSensor = sensors.find(sensorName); 1796714a25aSJames Feist if (!firstScan && findSensor != sensors.end()) 1806714a25aSJames Feist { 1816714a25aSJames Feist bool found = false; 182df515159SJames Feist for (auto it = sensorsChanged->begin(); 183df515159SJames Feist it != sensorsChanged->end(); it++) 1846714a25aSJames Feist { 1856714a25aSJames Feist if (boost::ends_with(*it, findSensor->second->name)) 1866714a25aSJames Feist { 1876714a25aSJames Feist sensorsChanged->erase(it); 1886714a25aSJames Feist findSensor->second = nullptr; 1896714a25aSJames Feist found = true; 1906714a25aSJames Feist break; 1916714a25aSJames Feist } 1926714a25aSJames Feist } 1936714a25aSJames Feist if (!found) 1946714a25aSJames Feist { 1956714a25aSJames Feist continue; 1966714a25aSJames Feist } 1976714a25aSJames Feist } 1986714a25aSJames Feist std::vector<thresholds::Threshold> sensorThresholds; 1999ced0a38SJae Hyun Yoo if (!parseThresholdsFromConfig(*sensorData, sensorThresholds)) 2006714a25aSJames Feist { 201df515159SJames Feist std::cerr << "error populating thresholds for " 202df515159SJames Feist << sensorName << "\n"; 2036714a25aSJames Feist } 204*f9b01b6dSJames Feist auto findPowerOn = baseConfiguration->second.find("PowerState"); 205*f9b01b6dSJames Feist PowerState readState = PowerState::always; 206*f9b01b6dSJames Feist if (findPowerOn != baseConfiguration->second.end()) 207*f9b01b6dSJames Feist { 208*f9b01b6dSJames Feist std::string powerState = std::visit( 209*f9b01b6dSJames Feist VariantToStringVisitor(), findPowerOn->second); 210*f9b01b6dSJames Feist setReadState(powerState, readState); 211*f9b01b6dSJames Feist } 2128b3f7d40SAlex Qiu auto& sensor = sensors[sensorName]; 2138b3f7d40SAlex Qiu sensor = nullptr; 2148b3f7d40SAlex Qiu sensor = std::make_unique<HwmonTempSensor>( 215df515159SJames Feist directory.string() + "/temp1_input", sensorType, 216df515159SJames Feist objectServer, dbusConnection, io, sensorName, 217*f9b01b6dSJames Feist std::move(sensorThresholds), *interfacePath, readState); 2188b3f7d40SAlex Qiu 2198b3f7d40SAlex Qiu // Looking for keys like "Name1" for temp2_input, 2208b3f7d40SAlex Qiu // "Name2" for temp3_input, etc. 2218b3f7d40SAlex Qiu int i = 0; 2228b3f7d40SAlex Qiu while (true) 22337266ca9SJames Feist { 2248b3f7d40SAlex Qiu ++i; 2258b3f7d40SAlex Qiu auto findKey = 2268b3f7d40SAlex Qiu baseConfigMap->find("Name" + std::string(1, '0' + i)); 2278b3f7d40SAlex Qiu if (findKey == baseConfigMap->end()) 2288b3f7d40SAlex Qiu { 2298b3f7d40SAlex Qiu break; 23037266ca9SJames Feist } 2318b3f7d40SAlex Qiu 2328b3f7d40SAlex Qiu std::string sensorName = 2338b3f7d40SAlex Qiu std::get<std::string>(findKey->second); 2348b3f7d40SAlex Qiu auto& sensor = sensors[sensorName]; 2358b3f7d40SAlex Qiu sensor = nullptr; 2368b3f7d40SAlex Qiu sensor = std::make_unique<HwmonTempSensor>( 2378b3f7d40SAlex Qiu directory.string() + "/temp" + std::string(1, '1' + i) + 2388b3f7d40SAlex Qiu "_input", 2398b3f7d40SAlex Qiu sensorType, objectServer, dbusConnection, io, 2408b3f7d40SAlex Qiu sensorName, std::vector<thresholds::Threshold>(), 241*f9b01b6dSJames Feist *interfacePath, readState); 2428b3f7d40SAlex Qiu } 2436714a25aSJames Feist } 244df515159SJames Feist })); 245df515159SJames Feist getter->getConfiguration( 246df515159SJames Feist std::vector<std::string>(sensorTypes.begin(), sensorTypes.end())); 2476714a25aSJames Feist } 2486714a25aSJames Feist 249b6c0b914SJames Feist int main() 2506714a25aSJames Feist { 2516714a25aSJames Feist boost::asio::io_service io; 2526714a25aSJames Feist auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); 2536714a25aSJames Feist systemBus->request_name("xyz.openbmc_project.HwmonTempSensor"); 2546714a25aSJames Feist sdbusplus::asio::object_server objectServer(systemBus); 2556714a25aSJames Feist boost::container::flat_map<std::string, std::unique_ptr<HwmonTempSensor>> 2566714a25aSJames Feist sensors; 2576714a25aSJames Feist std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches; 2586714a25aSJames Feist std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged = 2596714a25aSJames Feist std::make_unique<boost::container::flat_set<std::string>>(); 2606714a25aSJames Feist 2616714a25aSJames Feist io.post([&]() { 2626714a25aSJames Feist createSensors(io, objectServer, sensors, systemBus, nullptr); 2636714a25aSJames Feist }); 2646714a25aSJames Feist 2656714a25aSJames Feist boost::asio::deadline_timer filterTimer(io); 2666714a25aSJames Feist std::function<void(sdbusplus::message::message&)> eventHandler = 2676714a25aSJames Feist [&](sdbusplus::message::message& message) { 2686714a25aSJames Feist if (message.is_method_error()) 2696714a25aSJames Feist { 2706714a25aSJames Feist std::cerr << "callback method error\n"; 2716714a25aSJames Feist return; 2726714a25aSJames Feist } 2736714a25aSJames Feist sensorsChanged->insert(message.get_path()); 2746714a25aSJames Feist // this implicitly cancels the timer 2756714a25aSJames Feist filterTimer.expires_from_now(boost::posix_time::seconds(1)); 2766714a25aSJames Feist 2776714a25aSJames Feist filterTimer.async_wait([&](const boost::system::error_code& ec) { 2786714a25aSJames Feist if (ec == boost::asio::error::operation_aborted) 2796714a25aSJames Feist { 2806714a25aSJames Feist /* we were canceled*/ 2816714a25aSJames Feist return; 2826714a25aSJames Feist } 2836714a25aSJames Feist else if (ec) 2846714a25aSJames Feist { 2856714a25aSJames Feist std::cerr << "timer error\n"; 2866714a25aSJames Feist return; 2876714a25aSJames Feist } 2886714a25aSJames Feist createSensors(io, objectServer, sensors, systemBus, 2896714a25aSJames Feist sensorsChanged); 2906714a25aSJames Feist }); 2916714a25aSJames Feist }; 2926714a25aSJames Feist 2939ced0a38SJae Hyun Yoo for (const char* type : sensorTypes) 2946714a25aSJames Feist { 2956714a25aSJames Feist auto match = std::make_unique<sdbusplus::bus::match::match>( 2966714a25aSJames Feist static_cast<sdbusplus::bus::bus&>(*systemBus), 2976714a25aSJames Feist "type='signal',member='PropertiesChanged',path_namespace='" + 2989ced0a38SJae Hyun Yoo std::string(inventoryPath) + "',arg0namespace='" + type + "'", 2996714a25aSJames Feist eventHandler); 3006714a25aSJames Feist matches.emplace_back(std::move(match)); 3016714a25aSJames Feist } 3026714a25aSJames Feist 3036714a25aSJames Feist io.run(); 3046714a25aSJames Feist } 305