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 176714a25aSJames Feist #include <PwmSensor.hpp> 186714a25aSJames Feist #include <TachSensor.hpp> 196714a25aSJames Feist #include <Utils.hpp> 206714a25aSJames Feist #include <VariantVisitors.hpp> 216714a25aSJames Feist #include <boost/algorithm/string/predicate.hpp> 226714a25aSJames Feist #include <boost/algorithm/string/replace.hpp> 236714a25aSJames Feist #include <boost/container/flat_set.hpp> 246714a25aSJames Feist #include <boost/lexical_cast.hpp> 256714a25aSJames Feist #include <experimental/filesystem> 266714a25aSJames Feist #include <fstream> 276714a25aSJames Feist #include <regex> 286714a25aSJames Feist #include <sdbusplus/asio/connection.hpp> 296714a25aSJames Feist #include <sdbusplus/asio/object_server.hpp> 306714a25aSJames Feist 316714a25aSJames Feist static constexpr bool DEBUG = false; 326714a25aSJames Feist 336714a25aSJames Feist namespace fs = std::experimental::filesystem; 34*5093805cSYoo, Jae Hyun namespace variant_ns = sdbusplus::message::variant_ns; 356714a25aSJames Feist static constexpr std::array<const char*, 1> SENSOR_TYPES = { 366714a25aSJames Feist "xyz.openbmc_project.Configuration.AspeedFan"}; 376714a25aSJames Feist static std::regex INPUT_REGEX(R"(fan(\d+)_input)"); 386714a25aSJames Feist 396714a25aSJames Feist void createSensors( 406714a25aSJames Feist boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer, 416714a25aSJames Feist boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>& 426714a25aSJames Feist tachSensors, 436714a25aSJames Feist boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>& 446714a25aSJames Feist pwmSensors, 456714a25aSJames Feist std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 466714a25aSJames Feist const std::unique_ptr<boost::container::flat_set<std::string>>& 476714a25aSJames Feist sensorsChanged) 486714a25aSJames Feist { 496714a25aSJames Feist bool firstScan = sensorsChanged == nullptr; 506714a25aSJames Feist // use new data the first time, then refresh 516714a25aSJames Feist ManagedObjectType sensorConfigurations; 526714a25aSJames Feist bool useCache = false; 536714a25aSJames Feist for (const char* type : SENSOR_TYPES) 546714a25aSJames Feist { 556714a25aSJames Feist if (!getSensorConfiguration(type, dbusConnection, sensorConfigurations, 566714a25aSJames Feist useCache)) 576714a25aSJames Feist { 586714a25aSJames Feist std::cerr << "error communicating to entity manager\n"; 596714a25aSJames Feist return; 606714a25aSJames Feist } 616714a25aSJames Feist useCache = true; 626714a25aSJames Feist } 636714a25aSJames Feist std::vector<fs::path> paths; 646714a25aSJames Feist if (!find_files(fs::path("/sys/class/hwmon"), R"(fan\d+_input)", paths)) 656714a25aSJames Feist { 666714a25aSJames Feist std::cerr << "No temperature sensors in system\n"; 676714a25aSJames Feist return; 686714a25aSJames Feist } 696714a25aSJames Feist 706714a25aSJames Feist // iterate through all found fan sensors, and try to match them with 716714a25aSJames Feist // configuration 726714a25aSJames Feist for (auto& path : paths) 736714a25aSJames Feist { 746714a25aSJames Feist std::smatch match; 756714a25aSJames Feist std::string pathStr = path.string(); 766714a25aSJames Feist 776714a25aSJames Feist std::regex_search(pathStr, match, INPUT_REGEX); 786714a25aSJames Feist std::string indexStr = *(match.begin() + 1); 796714a25aSJames Feist 806714a25aSJames Feist auto directory = path.parent_path(); 816714a25aSJames Feist // convert to 0 based 826714a25aSJames Feist size_t index = std::stoul(indexStr) - 1; 836714a25aSJames Feist 846714a25aSJames Feist const char* baseType; 856714a25aSJames Feist const SensorData* sensorData = nullptr; 866714a25aSJames Feist const std::string* interfacePath = nullptr; 876714a25aSJames Feist const std::pair<std::string, boost::container::flat_map< 886714a25aSJames Feist std::string, BasicVariantType>>* 896714a25aSJames Feist baseConfiguration = nullptr; 906714a25aSJames Feist for (const std::pair<sdbusplus::message::object_path, SensorData>& 916714a25aSJames Feist sensor : sensorConfigurations) 926714a25aSJames Feist { 936714a25aSJames Feist // find the base of the configuration to see if indexes match 946714a25aSJames Feist for (const char* type : SENSOR_TYPES) 956714a25aSJames Feist { 966714a25aSJames Feist auto sensorBaseFind = sensor.second.find(type); 976714a25aSJames Feist if (sensorBaseFind != sensor.second.end()) 986714a25aSJames Feist { 996714a25aSJames Feist baseConfiguration = &(*sensorBaseFind); 1006714a25aSJames Feist interfacePath = &(sensor.first.str); 1016714a25aSJames Feist baseType = type; 1026714a25aSJames Feist break; 1036714a25aSJames Feist } 1046714a25aSJames Feist } 1056714a25aSJames Feist if (baseConfiguration == nullptr) 1066714a25aSJames Feist { 1076714a25aSJames Feist continue; 1086714a25aSJames Feist } 1096714a25aSJames Feist auto connector = 1106714a25aSJames Feist sensor.second.find(baseType + std::string(".Connector")); 1116714a25aSJames Feist if (connector == sensor.second.end()) 1126714a25aSJames Feist { 1136714a25aSJames Feist std::cerr << baseConfiguration->first << " missing connector\n"; 1146714a25aSJames Feist continue; 1156714a25aSJames Feist } 1166714a25aSJames Feist auto findPwmIndex = connector->second.find("Pwm"); 1176714a25aSJames Feist if (findPwmIndex == connector->second.end()) 1186714a25aSJames Feist { 1196714a25aSJames Feist continue; 1206714a25aSJames Feist } 121*5093805cSYoo, Jae Hyun uint16_t pwmIndex = variant_ns::visit(VariantToUnsignedIntVisitor(), 122*5093805cSYoo, Jae Hyun findPwmIndex->second); 1236714a25aSJames Feist auto oemNamePath = directory.string() + R"(/of_node/oemname)" + 1246714a25aSJames Feist std::to_string(pwmIndex); 1256714a25aSJames Feist 1266714a25aSJames Feist if (DEBUG) 1276714a25aSJames Feist std::cout << "Checking path " << oemNamePath << "\n"; 1286714a25aSJames Feist std::ifstream nameFile(oemNamePath); 1296714a25aSJames Feist if (!nameFile.good()) 1306714a25aSJames Feist { 1316714a25aSJames Feist continue; 1326714a25aSJames Feist } 1336714a25aSJames Feist std::string oemName; 1346714a25aSJames Feist std::getline(nameFile, oemName); 1356714a25aSJames Feist nameFile.close(); 1366714a25aSJames Feist if (!oemName.size()) 1376714a25aSJames Feist { 1386714a25aSJames Feist // shouldn't have an empty name file 1396714a25aSJames Feist continue; 1406714a25aSJames Feist } 1416714a25aSJames Feist oemName.pop_back(); // remove trailing null 1426714a25aSJames Feist auto findIndex = baseConfiguration->second.find("Index"); 1436714a25aSJames Feist if (findIndex == baseConfiguration->second.end()) 1446714a25aSJames Feist { 1456714a25aSJames Feist std::cerr << baseConfiguration->first << " missing index\n"; 1466714a25aSJames Feist continue; 1476714a25aSJames Feist } 148*5093805cSYoo, Jae Hyun unsigned int configIndex = variant_ns::visit( 1496714a25aSJames Feist VariantToUnsignedIntVisitor(), findIndex->second); 1506714a25aSJames Feist 1516714a25aSJames Feist if (configIndex != index) 1526714a25aSJames Feist { 1536714a25aSJames Feist continue; 1546714a25aSJames Feist } 1556714a25aSJames Feist // now that the indexes match, verify the connector 1566714a25aSJames Feist auto findConnectorName = connector->second.find("Name"); 1576714a25aSJames Feist if (findConnectorName == connector->second.end()) 1586714a25aSJames Feist { 1596714a25aSJames Feist continue; 1606714a25aSJames Feist } 161*5093805cSYoo, Jae Hyun std::string connectorName = variant_ns::visit( 1626714a25aSJames Feist VariantToStringVisitor(), findConnectorName->second); 1636714a25aSJames Feist boost::replace_all(connectorName, " ", "_"); 1646714a25aSJames Feist if (connectorName == oemName) 1656714a25aSJames Feist { 1666714a25aSJames Feist sensorData = &(sensor.second); 1676714a25aSJames Feist break; 1686714a25aSJames Feist } 1696714a25aSJames Feist } 1706714a25aSJames Feist if (sensorData == nullptr) 1716714a25aSJames Feist { 1726714a25aSJames Feist std::cerr << "failed to find match for " << path.string() << "\n"; 1736714a25aSJames Feist continue; 1746714a25aSJames Feist } 1756714a25aSJames Feist 1766714a25aSJames Feist auto findSensorName = baseConfiguration->second.find("Name"); 1776714a25aSJames Feist if (findSensorName == baseConfiguration->second.end()) 1786714a25aSJames Feist { 1796714a25aSJames Feist std::cerr << "could not determine configuration name for " 1806714a25aSJames Feist << path.string() << "\n"; 1816714a25aSJames Feist continue; 1826714a25aSJames Feist } 1836714a25aSJames Feist std::string sensorName = 1846714a25aSJames Feist sdbusplus::message::variant_ns::get<std::string>( 1856714a25aSJames Feist findSensorName->second); 1866714a25aSJames Feist // on rescans, only update sensors we were signaled by 1876714a25aSJames Feist auto findSensor = tachSensors.find(sensorName); 1886714a25aSJames Feist if (!firstScan && findSensor != tachSensors.end()) 1896714a25aSJames Feist { 1906714a25aSJames Feist bool found = false; 1916714a25aSJames Feist for (auto it = sensorsChanged->begin(); it != sensorsChanged->end(); 1926714a25aSJames Feist it++) 1936714a25aSJames Feist { 1946714a25aSJames Feist if (boost::ends_with(*it, findSensor->second->name)) 1956714a25aSJames Feist { 1966714a25aSJames Feist sensorsChanged->erase(it); 1976714a25aSJames Feist findSensor->second = nullptr; 1986714a25aSJames Feist found = true; 1996714a25aSJames Feist break; 2006714a25aSJames Feist } 2016714a25aSJames Feist } 2026714a25aSJames Feist if (!found) 2036714a25aSJames Feist { 2046714a25aSJames Feist continue; 2056714a25aSJames Feist } 2066714a25aSJames Feist } 2076714a25aSJames Feist std::vector<thresholds::Threshold> sensorThresholds; 2086714a25aSJames Feist if (!ParseThresholdsFromConfig(*sensorData, sensorThresholds)) 2096714a25aSJames Feist { 2106714a25aSJames Feist std::cerr << "error populating thresholds for " << sensorName 2116714a25aSJames Feist << "\n"; 2126714a25aSJames Feist } 2136714a25aSJames Feist 2146714a25aSJames Feist tachSensors[sensorName] = std::make_unique<TachSensor>( 2156714a25aSJames Feist path.string(), objectServer, dbusConnection, io, sensorName, 2166714a25aSJames Feist std::move(sensorThresholds), *interfacePath); 2176714a25aSJames Feist } 2186714a25aSJames Feist std::vector<fs::path> pwms; 2196714a25aSJames Feist if (!find_files(fs::path("/sys/class/hwmon"), R"(pwm\d+)", pwms)) 2206714a25aSJames Feist { 2216714a25aSJames Feist std::cerr << "No pwm in system\n"; 2226714a25aSJames Feist return; 2236714a25aSJames Feist } 2246714a25aSJames Feist for (const fs::path& pwm : pwms) 2256714a25aSJames Feist { 2266714a25aSJames Feist // only add new elements 2276714a25aSJames Feist pwmSensors.insert(std::pair<std::string, std::unique_ptr<PwmSensor>>( 2286714a25aSJames Feist pwm.string(), 2296714a25aSJames Feist std::make_unique<PwmSensor>(pwm.string(), objectServer))); 2306714a25aSJames Feist } 2316714a25aSJames Feist } 2326714a25aSJames Feist 2336714a25aSJames Feist int main(int argc, char** argv) 2346714a25aSJames Feist { 2356714a25aSJames Feist boost::asio::io_service io; 2366714a25aSJames Feist auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); 2376714a25aSJames Feist systemBus->request_name("xyz.openbmc_project.FanSensor"); 2386714a25aSJames Feist sdbusplus::asio::object_server objectServer(systemBus); 2396714a25aSJames Feist boost::container::flat_map<std::string, std::unique_ptr<TachSensor>> 2406714a25aSJames Feist tachSensors; 2416714a25aSJames Feist boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>> 2426714a25aSJames Feist pwmSensors; 2436714a25aSJames Feist std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches; 2446714a25aSJames Feist std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged = 2456714a25aSJames Feist std::make_unique<boost::container::flat_set<std::string>>(); 2466714a25aSJames Feist 2476714a25aSJames Feist io.post([&]() { 2486714a25aSJames Feist createSensors(io, objectServer, tachSensors, pwmSensors, systemBus, 2496714a25aSJames Feist nullptr); 2506714a25aSJames Feist }); 2516714a25aSJames Feist 2526714a25aSJames Feist boost::asio::deadline_timer filterTimer(io); 2536714a25aSJames Feist std::function<void(sdbusplus::message::message&)> eventHandler = 2546714a25aSJames Feist [&](sdbusplus::message::message& message) { 2556714a25aSJames Feist if (message.is_method_error()) 2566714a25aSJames Feist { 2576714a25aSJames Feist std::cerr << "callback method error\n"; 2586714a25aSJames Feist return; 2596714a25aSJames Feist } 2606714a25aSJames Feist sensorsChanged->insert(message.get_path()); 2616714a25aSJames Feist // this implicitly cancels the timer 2626714a25aSJames Feist filterTimer.expires_from_now(boost::posix_time::seconds(1)); 2636714a25aSJames Feist 2646714a25aSJames Feist filterTimer.async_wait([&](const boost::system::error_code& ec) { 2656714a25aSJames Feist if (ec == boost::asio::error::operation_aborted) 2666714a25aSJames Feist { 2676714a25aSJames Feist /* we were canceled*/ 2686714a25aSJames Feist return; 2696714a25aSJames Feist } 2706714a25aSJames Feist else if (ec) 2716714a25aSJames Feist { 2726714a25aSJames Feist std::cerr << "timer error\n"; 2736714a25aSJames Feist return; 2746714a25aSJames Feist } 2756714a25aSJames Feist createSensors(io, objectServer, tachSensors, pwmSensors, 2766714a25aSJames Feist systemBus, sensorsChanged); 2776714a25aSJames Feist }); 2786714a25aSJames Feist }; 2796714a25aSJames Feist 2806714a25aSJames Feist for (const char* type : SENSOR_TYPES) 2816714a25aSJames Feist { 2826714a25aSJames Feist auto match = std::make_unique<sdbusplus::bus::match::match>( 2836714a25aSJames Feist static_cast<sdbusplus::bus::bus&>(*systemBus), 2846714a25aSJames Feist "type='signal',member='PropertiesChanged',path_namespace='" + 2856714a25aSJames Feist std::string(INVENTORY_PATH) + "',arg0namespace='" + type + "'", 2866714a25aSJames Feist eventHandler); 2876714a25aSJames Feist matches.emplace_back(std::move(match)); 2886714a25aSJames Feist } 2896714a25aSJames Feist 2906714a25aSJames Feist io.run(); 2916714a25aSJames Feist } 292