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 178a57ec09SEd Tanous #include <HwmonTempSensor.hpp> 188a57ec09SEd Tanous #include <Utils.hpp> 196714a25aSJames Feist #include <boost/algorithm/string/predicate.hpp> 206714a25aSJames Feist #include <boost/algorithm/string/replace.hpp> 2196e97db7SPatrick Venture #include <boost/container/flat_map.hpp> 226714a25aSJames Feist #include <boost/container/flat_set.hpp> 2338fb5983SJames Feist #include <sdbusplus/asio/connection.hpp> 2438fb5983SJames Feist #include <sdbusplus/asio/object_server.hpp> 2538fb5983SJames Feist #include <sdbusplus/bus/match.hpp> 2638fb5983SJames Feist 2738fb5983SJames Feist #include <array> 2824f02f24SJames Feist #include <filesystem> 296714a25aSJames Feist #include <fstream> 3096e97db7SPatrick Venture #include <functional> 3196e97db7SPatrick Venture #include <memory> 326714a25aSJames Feist #include <regex> 3396e97db7SPatrick Venture #include <stdexcept> 3496e97db7SPatrick Venture #include <string> 3596e97db7SPatrick Venture #include <utility> 3696e97db7SPatrick Venture #include <variant> 3796e97db7SPatrick Venture #include <vector> 386714a25aSJames Feist 3987bc67f7SJeff Lin static constexpr float pollRateDefault = 0.5; 406714a25aSJames Feist 41cf3bce6eSJames Feist namespace fs = std::filesystem; 425770a6fdSOskar Senft static constexpr std::array<const char*, 17> sensorTypes = { 43381636e2SGilbert Chen "xyz.openbmc_project.Configuration.EMC1412", 448b3f7d40SAlex Qiu "xyz.openbmc_project.Configuration.EMC1413", 45381636e2SGilbert Chen "xyz.openbmc_project.Configuration.EMC1414", 468b3f7d40SAlex Qiu "xyz.openbmc_project.Configuration.MAX31725", 478b3f7d40SAlex Qiu "xyz.openbmc_project.Configuration.MAX31730", 4816e7af1dSJason Ling "xyz.openbmc_project.Configuration.MAX6581", 493840d0adSJosh Lehan "xyz.openbmc_project.Configuration.MAX6654", 505770a6fdSOskar Senft "xyz.openbmc_project.Configuration.NCT7802", 518fb0a013SJosh Lehan "xyz.openbmc_project.Configuration.SBTSI", 525a3b3f8aSKonstantin Aladyshev "xyz.openbmc_project.Configuration.LM95234", 5355ab2afbSJohn Wang "xyz.openbmc_project.Configuration.TMP112", 543546adb9SPatrick Venture "xyz.openbmc_project.Configuration.TMP175", 558b3f7d40SAlex Qiu "xyz.openbmc_project.Configuration.TMP421", 568b3f7d40SAlex Qiu "xyz.openbmc_project.Configuration.TMP441", 5734fc75a7SKonstantin Aladyshev "xyz.openbmc_project.Configuration.LM75A", 587ea918f2SZev Weiss "xyz.openbmc_project.Configuration.TMP75", 597ea918f2SZev Weiss "xyz.openbmc_project.Configuration.W83773G"}; 606714a25aSJames Feist 616714a25aSJames Feist void createSensors( 626714a25aSJames Feist boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer, 63f3fd1915SYong Li boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>& 646714a25aSJames Feist sensors, 656714a25aSJames Feist std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 665591cf08SJames Feist const std::shared_ptr<boost::container::flat_set<std::string>>& 676714a25aSJames Feist sensorsChanged) 686714a25aSJames Feist { 69df515159SJames Feist auto getter = std::make_shared<GetSensorConfiguration>( 70df515159SJames Feist dbusConnection, 718a17c303SEd Tanous [&io, &objectServer, &sensors, &dbusConnection, 728a17c303SEd Tanous sensorsChanged](const ManagedObjectType& sensorConfigurations) { 736714a25aSJames Feist bool firstScan = sensorsChanged == nullptr; 74df515159SJames Feist 756714a25aSJames Feist std::vector<fs::path> paths; 76df515159SJames Feist if (!findFiles(fs::path("/sys/class/hwmon"), R"(temp\d+_input)", 77df515159SJames Feist paths)) 786714a25aSJames Feist { 796714a25aSJames Feist std::cerr << "No temperature sensors in system\n"; 806714a25aSJames Feist return; 816714a25aSJames Feist } 826714a25aSJames Feist 8337266ca9SJames Feist boost::container::flat_set<std::string> directories; 8437266ca9SJames Feist 85df515159SJames Feist // iterate through all found temp sensors, and try to match them 86df515159SJames Feist // with configuration 876714a25aSJames Feist for (auto& path : paths) 886714a25aSJames Feist { 896714a25aSJames Feist std::smatch match; 906714a25aSJames Feist auto directory = path.parent_path(); 916714a25aSJames Feist 9237266ca9SJames Feist auto ret = directories.insert(directory.string()); 9337266ca9SJames Feist if (!ret.second) 946714a25aSJames Feist { 9537266ca9SJames Feist continue; // already searched this path 9637266ca9SJames Feist } 9737266ca9SJames Feist 98b6c0b914SJames Feist fs::path device = directory / "device"; 9937266ca9SJames Feist std::string deviceName = fs::canonical(device).stem(); 1008a57ec09SEd Tanous auto findHyphen = deviceName.find('-'); 10137266ca9SJames Feist if (findHyphen == std::string::npos) 10237266ca9SJames Feist { 10337266ca9SJames Feist std::cerr << "found bad device " << deviceName << "\n"; 1046714a25aSJames Feist continue; 1056714a25aSJames Feist } 10637266ca9SJames Feist std::string busStr = deviceName.substr(0, findHyphen); 10737266ca9SJames Feist std::string addrStr = deviceName.substr(findHyphen + 1); 10837266ca9SJames Feist 10937266ca9SJames Feist size_t bus = 0; 11037266ca9SJames Feist size_t addr = 0; 11137266ca9SJames Feist try 1126714a25aSJames Feist { 11337266ca9SJames Feist bus = std::stoi(busStr); 1148a57ec09SEd Tanous addr = std::stoi(addrStr, nullptr, 16); 11537266ca9SJames Feist } 116*26601e89SPatrick Williams catch (const std::invalid_argument&) 11737266ca9SJames Feist { 1186714a25aSJames Feist continue; 1196714a25aSJames Feist } 1206714a25aSJames Feist const SensorData* sensorData = nullptr; 1216714a25aSJames Feist const std::string* interfacePath = nullptr; 12237266ca9SJames Feist const char* sensorType = nullptr; 1238b3f7d40SAlex Qiu const SensorBaseConfiguration* baseConfiguration = nullptr; 1248b3f7d40SAlex Qiu const SensorBaseConfigMap* baseConfigMap = nullptr; 12537266ca9SJames Feist 126df515159SJames Feist for (const std::pair<sdbusplus::message::object_path, 127df515159SJames Feist SensorData>& sensor : sensorConfigurations) 12837266ca9SJames Feist { 12937266ca9SJames Feist sensorData = &(sensor.second); 1309ced0a38SJae Hyun Yoo for (const char* type : sensorTypes) 1316714a25aSJames Feist { 1326714a25aSJames Feist auto sensorBase = sensorData->find(type); 1336714a25aSJames Feist if (sensorBase != sensorData->end()) 1346714a25aSJames Feist { 1356714a25aSJames Feist baseConfiguration = &(*sensorBase); 1366714a25aSJames Feist sensorType = type; 1376714a25aSJames Feist break; 1386714a25aSJames Feist } 1396714a25aSJames Feist } 1406714a25aSJames Feist if (baseConfiguration == nullptr) 1416714a25aSJames Feist { 14237266ca9SJames Feist std::cerr << "error finding base configuration for " 14337266ca9SJames Feist << deviceName << "\n"; 14437266ca9SJames Feist continue; 14537266ca9SJames Feist } 1468b3f7d40SAlex Qiu baseConfigMap = &baseConfiguration->second; 1478b3f7d40SAlex Qiu auto configurationBus = baseConfigMap->find("Bus"); 1488b3f7d40SAlex Qiu auto configurationAddress = baseConfigMap->find("Address"); 14937266ca9SJames Feist 1508b3f7d40SAlex Qiu if (configurationBus == baseConfigMap->end() || 1518b3f7d40SAlex Qiu configurationAddress == baseConfigMap->end()) 15237266ca9SJames Feist { 1538b3f7d40SAlex Qiu std::cerr << "error finding bus or address in " 1548b3f7d40SAlex Qiu "configuration\n"; 15537266ca9SJames Feist continue; 15637266ca9SJames Feist } 15737266ca9SJames Feist 1583eb82629SJames Feist if (std::get<uint64_t>(configurationBus->second) != bus || 159df515159SJames Feist std::get<uint64_t>(configurationAddress->second) != 160df515159SJames Feist addr) 16137266ca9SJames Feist { 16237266ca9SJames Feist continue; 16337266ca9SJames Feist } 16437266ca9SJames Feist 16537266ca9SJames Feist interfacePath = &(sensor.first.str); 16637266ca9SJames Feist break; 16737266ca9SJames Feist } 16837266ca9SJames Feist if (interfacePath == nullptr) 16937266ca9SJames Feist { 170df515159SJames Feist std::cerr << "failed to find match for " << deviceName 171df515159SJames Feist << "\n"; 1726714a25aSJames Feist continue; 1736714a25aSJames Feist } 1746714a25aSJames Feist 1758b3f7d40SAlex Qiu auto findSensorName = baseConfigMap->find("Name"); 1768b3f7d40SAlex Qiu if (findSensorName == baseConfigMap->end()) 1776714a25aSJames Feist { 1786714a25aSJames Feist std::cerr << "could not determine configuration name for " 17937266ca9SJames Feist << deviceName << "\n"; 1806714a25aSJames Feist continue; 1816714a25aSJames Feist } 182df515159SJames Feist std::string sensorName = 183df515159SJames Feist std::get<std::string>(findSensorName->second); 1846714a25aSJames Feist // on rescans, only update sensors we were signaled by 1856714a25aSJames Feist auto findSensor = sensors.find(sensorName); 1866714a25aSJames Feist if (!firstScan && findSensor != sensors.end()) 1876714a25aSJames Feist { 1886714a25aSJames Feist bool found = false; 189d653b75cSBruce Mitchell auto it = sensorsChanged->begin(); 190d653b75cSBruce Mitchell while (it != sensorsChanged->end()) 1916714a25aSJames Feist { 1926714a25aSJames Feist if (boost::ends_with(*it, findSensor->second->name)) 1936714a25aSJames Feist { 194d653b75cSBruce Mitchell it = sensorsChanged->erase(it); 1956714a25aSJames Feist findSensor->second = nullptr; 1966714a25aSJames Feist found = true; 1976714a25aSJames Feist break; 1986714a25aSJames Feist } 199d653b75cSBruce Mitchell ++it; 2006714a25aSJames Feist } 2016714a25aSJames Feist if (!found) 2026714a25aSJames Feist { 2036714a25aSJames Feist continue; 2046714a25aSJames Feist } 2056714a25aSJames Feist } 2065636d52bSMatt Spinler 2076714a25aSJames Feist std::vector<thresholds::Threshold> sensorThresholds; 2085636d52bSMatt Spinler int index = 1; 2095636d52bSMatt Spinler 2105636d52bSMatt Spinler if (!parseThresholdsFromConfig(*sensorData, sensorThresholds, 2115636d52bSMatt Spinler nullptr, &index)) 2126714a25aSJames Feist { 213df515159SJames Feist std::cerr << "error populating thresholds for " 2145636d52bSMatt Spinler << sensorName << " index 1\n"; 2156714a25aSJames Feist } 21687bc67f7SJeff Lin 21787bc67f7SJeff Lin auto findPollRate = baseConfiguration->second.find("PollRate"); 21887bc67f7SJeff Lin float pollRate = pollRateDefault; 21987bc67f7SJeff Lin if (findPollRate != baseConfiguration->second.end()) 22087bc67f7SJeff Lin { 22187bc67f7SJeff Lin pollRate = std::visit(VariantToFloatVisitor(), 22287bc67f7SJeff Lin findPollRate->second); 22387bc67f7SJeff Lin if (pollRate <= 0.0f) 22487bc67f7SJeff Lin { 22587bc67f7SJeff Lin pollRate = pollRateDefault; // polling time too short 22687bc67f7SJeff Lin } 22787bc67f7SJeff Lin } 22887bc67f7SJeff Lin 229f9b01b6dSJames Feist auto findPowerOn = baseConfiguration->second.find("PowerState"); 230f9b01b6dSJames Feist PowerState readState = PowerState::always; 231f9b01b6dSJames Feist if (findPowerOn != baseConfiguration->second.end()) 232f9b01b6dSJames Feist { 233f9b01b6dSJames Feist std::string powerState = std::visit( 234f9b01b6dSJames Feist VariantToStringVisitor(), findPowerOn->second); 235f9b01b6dSJames Feist setReadState(powerState, readState); 236f9b01b6dSJames Feist } 237100c20bfSJason Ling 238100c20bfSJason Ling auto permitSet = getPermitSet(*baseConfigMap); 2398b3f7d40SAlex Qiu auto& sensor = sensors[sensorName]; 2408b3f7d40SAlex Qiu sensor = nullptr; 241100c20bfSJason Ling auto hwmonFile = getFullHwmonFilePath(directory.string(), 242100c20bfSJason Ling "temp1", permitSet); 243100c20bfSJason Ling if (hwmonFile) 244100c20bfSJason Ling { 245f3fd1915SYong Li sensor = std::make_shared<HwmonTempSensor>( 246100c20bfSJason Ling *hwmonFile, sensorType, objectServer, dbusConnection, 24787bc67f7SJeff Lin io, sensorName, std::move(sensorThresholds), pollRate, 248100c20bfSJason Ling *interfacePath, readState); 249f3fd1915SYong Li sensor->setupRead(); 250100c20bfSJason Ling } 2518b3f7d40SAlex Qiu // Looking for keys like "Name1" for temp2_input, 2528b3f7d40SAlex Qiu // "Name2" for temp3_input, etc. 2538b3f7d40SAlex Qiu int i = 0; 2548b3f7d40SAlex Qiu while (true) 25537266ca9SJames Feist { 2568b3f7d40SAlex Qiu ++i; 2578b3f7d40SAlex Qiu auto findKey = 2588b8bcc87SJason Ling baseConfigMap->find("Name" + std::to_string(i)); 2598b3f7d40SAlex Qiu if (findKey == baseConfigMap->end()) 2608b3f7d40SAlex Qiu { 2618b3f7d40SAlex Qiu break; 26237266ca9SJames Feist } 2638b3f7d40SAlex Qiu std::string sensorName = 2648b3f7d40SAlex Qiu std::get<std::string>(findKey->second); 265100c20bfSJason Ling hwmonFile = getFullHwmonFilePath( 266100c20bfSJason Ling directory.string(), "temp" + std::to_string(i + 1), 267100c20bfSJason Ling permitSet); 268100c20bfSJason Ling if (hwmonFile) 269100c20bfSJason Ling { 2705636d52bSMatt Spinler // To look up thresholds for these additional sensors, 2715636d52bSMatt Spinler // match on the Index property in the threshold data 2725636d52bSMatt Spinler // where the index comes from the sysfs file we're on, 2735636d52bSMatt Spinler // i.e. index = 2 for temp2_input. 2745636d52bSMatt Spinler int index = i + 1; 2755636d52bSMatt Spinler std::vector<thresholds::Threshold> thresholds; 2765636d52bSMatt Spinler 2775636d52bSMatt Spinler if (!parseThresholdsFromConfig(*sensorData, thresholds, 2785636d52bSMatt Spinler nullptr, &index)) 2795636d52bSMatt Spinler { 2805636d52bSMatt Spinler std::cerr << "error populating thresholds for " 2815636d52bSMatt Spinler << sensorName << " index " << index 2825636d52bSMatt Spinler << "\n"; 2835636d52bSMatt Spinler } 2845636d52bSMatt Spinler 2858b3f7d40SAlex Qiu auto& sensor = sensors[sensorName]; 2868b3f7d40SAlex Qiu sensor = nullptr; 287f3fd1915SYong Li sensor = std::make_shared<HwmonTempSensor>( 288100c20bfSJason Ling *hwmonFile, sensorType, objectServer, 289100c20bfSJason Ling dbusConnection, io, sensorName, 2905636d52bSMatt Spinler std::move(thresholds), pollRate, *interfacePath, 2915636d52bSMatt Spinler readState); 292f3fd1915SYong Li sensor->setupRead(); 2938b3f7d40SAlex Qiu } 2946714a25aSJames Feist } 295100c20bfSJason Ling } 2968a17c303SEd Tanous }); 297df515159SJames Feist getter->getConfiguration( 298df515159SJames Feist std::vector<std::string>(sensorTypes.begin(), sensorTypes.end())); 2996714a25aSJames Feist } 3006714a25aSJames Feist 30120bf2c1cSMatt Spinler void interfaceRemoved( 30220bf2c1cSMatt Spinler sdbusplus::message::message& message, 30320bf2c1cSMatt Spinler boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>& 30420bf2c1cSMatt Spinler sensors) 30520bf2c1cSMatt Spinler { 30620bf2c1cSMatt Spinler if (message.is_method_error()) 30720bf2c1cSMatt Spinler { 30820bf2c1cSMatt Spinler std::cerr << "interfacesRemoved callback method error\n"; 30920bf2c1cSMatt Spinler return; 31020bf2c1cSMatt Spinler } 31120bf2c1cSMatt Spinler 31220bf2c1cSMatt Spinler sdbusplus::message::object_path path; 31320bf2c1cSMatt Spinler std::vector<std::string> interfaces; 31420bf2c1cSMatt Spinler 31520bf2c1cSMatt Spinler message.read(path, interfaces); 31620bf2c1cSMatt Spinler 31720bf2c1cSMatt Spinler // If the xyz.openbmc_project.Confguration.X interface was removed 31820bf2c1cSMatt Spinler // for one or more sensors, delete those sensor objects. 31920bf2c1cSMatt Spinler auto sensorIt = sensors.begin(); 32020bf2c1cSMatt Spinler while (sensorIt != sensors.end()) 32120bf2c1cSMatt Spinler { 32220bf2c1cSMatt Spinler if ((sensorIt->second->configurationPath == path) && 32320bf2c1cSMatt Spinler (std::find(interfaces.begin(), interfaces.end(), 32420bf2c1cSMatt Spinler sensorIt->second->objectType) != interfaces.end())) 32520bf2c1cSMatt Spinler { 32620bf2c1cSMatt Spinler sensorIt = sensors.erase(sensorIt); 32720bf2c1cSMatt Spinler } 32820bf2c1cSMatt Spinler else 32920bf2c1cSMatt Spinler { 33020bf2c1cSMatt Spinler sensorIt++; 33120bf2c1cSMatt Spinler } 33220bf2c1cSMatt Spinler } 33320bf2c1cSMatt Spinler } 33420bf2c1cSMatt Spinler 335b6c0b914SJames Feist int main() 3366714a25aSJames Feist { 3376714a25aSJames Feist boost::asio::io_service io; 3386714a25aSJames Feist auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); 3396714a25aSJames Feist systemBus->request_name("xyz.openbmc_project.HwmonTempSensor"); 3406714a25aSJames Feist sdbusplus::asio::object_server objectServer(systemBus); 341f3fd1915SYong Li boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>> 3426714a25aSJames Feist sensors; 3436714a25aSJames Feist std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches; 3445591cf08SJames Feist auto sensorsChanged = 3455591cf08SJames Feist std::make_shared<boost::container::flat_set<std::string>>(); 3466714a25aSJames Feist 3476714a25aSJames Feist io.post([&]() { 3486714a25aSJames Feist createSensors(io, objectServer, sensors, systemBus, nullptr); 3496714a25aSJames Feist }); 3506714a25aSJames Feist 3516714a25aSJames Feist boost::asio::deadline_timer filterTimer(io); 3526714a25aSJames Feist std::function<void(sdbusplus::message::message&)> eventHandler = 3536714a25aSJames Feist [&](sdbusplus::message::message& message) { 3546714a25aSJames Feist if (message.is_method_error()) 3556714a25aSJames Feist { 3566714a25aSJames Feist std::cerr << "callback method error\n"; 3576714a25aSJames Feist return; 3586714a25aSJames Feist } 3596714a25aSJames Feist sensorsChanged->insert(message.get_path()); 3606714a25aSJames Feist // this implicitly cancels the timer 3616714a25aSJames Feist filterTimer.expires_from_now(boost::posix_time::seconds(1)); 3626714a25aSJames Feist 3636714a25aSJames Feist filterTimer.async_wait([&](const boost::system::error_code& ec) { 3646714a25aSJames Feist if (ec == boost::asio::error::operation_aborted) 3656714a25aSJames Feist { 3666714a25aSJames Feist /* we were canceled*/ 3676714a25aSJames Feist return; 3686714a25aSJames Feist } 3698a57ec09SEd Tanous if (ec) 3706714a25aSJames Feist { 3716714a25aSJames Feist std::cerr << "timer error\n"; 3726714a25aSJames Feist return; 3736714a25aSJames Feist } 3746714a25aSJames Feist createSensors(io, objectServer, sensors, systemBus, 3756714a25aSJames Feist sensorsChanged); 3766714a25aSJames Feist }); 3776714a25aSJames Feist }; 3786714a25aSJames Feist 3799ced0a38SJae Hyun Yoo for (const char* type : sensorTypes) 3806714a25aSJames Feist { 3816714a25aSJames Feist auto match = std::make_unique<sdbusplus::bus::match::match>( 3826714a25aSJames Feist static_cast<sdbusplus::bus::bus&>(*systemBus), 3836714a25aSJames Feist "type='signal',member='PropertiesChanged',path_namespace='" + 3849ced0a38SJae Hyun Yoo std::string(inventoryPath) + "',arg0namespace='" + type + "'", 3856714a25aSJames Feist eventHandler); 3866714a25aSJames Feist matches.emplace_back(std::move(match)); 3876714a25aSJames Feist } 3886714a25aSJames Feist 3891263c3daSBruce Lee setupManufacturingModeMatch(*systemBus); 39020bf2c1cSMatt Spinler 39120bf2c1cSMatt Spinler // Watch for entity-manager to remove configuration interfaces 39220bf2c1cSMatt Spinler // so the corresponding sensors can be removed. 39320bf2c1cSMatt Spinler auto ifaceRemovedMatch = std::make_unique<sdbusplus::bus::match::match>( 39420bf2c1cSMatt Spinler static_cast<sdbusplus::bus::bus&>(*systemBus), 39520bf2c1cSMatt Spinler "type='signal',member='InterfacesRemoved',arg0path='" + 39620bf2c1cSMatt Spinler std::string(inventoryPath) + "/'", 39720bf2c1cSMatt Spinler [&sensors](sdbusplus::message::message& msg) { 39820bf2c1cSMatt Spinler interfaceRemoved(msg, sensors); 39920bf2c1cSMatt Spinler }); 40020bf2c1cSMatt Spinler 40120bf2c1cSMatt Spinler matches.emplace_back(std::move(ifaceRemovedMatch)); 40220bf2c1cSMatt Spinler 4036714a25aSJames Feist io.run(); 4046714a25aSJames Feist } 405