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 <HwmonTempSensor.hpp>
186714a25aSJames Feist #include <Utils.hpp>
196714a25aSJames Feist #include <boost/algorithm/string/predicate.hpp>
206714a25aSJames Feist #include <boost/algorithm/string/replace.hpp>
216714a25aSJames Feist #include <boost/container/flat_set.hpp>
2224f02f24SJames Feist #include <filesystem>
236714a25aSJames Feist #include <fstream>
246714a25aSJames Feist #include <regex>
256714a25aSJames Feist #include <sdbusplus/asio/connection.hpp>
266714a25aSJames Feist #include <sdbusplus/asio/object_server.hpp>
276714a25aSJames Feist 
286714a25aSJames Feist static constexpr bool DEBUG = false;
296714a25aSJames Feist 
30cf3bce6eSJames Feist namespace fs = std::filesystem;
31*3546adb9SPatrick Venture static constexpr std::array<const char*, 7> sensorTypes = {
326714a25aSJames Feist     "xyz.openbmc_project.Configuration.TMP75",
3355ab2afbSJohn Wang     "xyz.openbmc_project.Configuration.TMP421",
347fa475d3SPatrick Venture     "xyz.openbmc_project.Configuration.TMP441",
3555ab2afbSJohn Wang     "xyz.openbmc_project.Configuration.TMP112",
36*3546adb9SPatrick Venture     "xyz.openbmc_project.Configuration.TMP175",
37bd1a9d5dSPatrick Venture     "xyz.openbmc_project.Configuration.EMC1413",
38bd1a9d5dSPatrick Venture     "xyz.openbmc_project.Configuration.MAX31725"};
396714a25aSJames Feist 
406714a25aSJames Feist void createSensors(
416714a25aSJames Feist     boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
426714a25aSJames Feist     boost::container::flat_map<std::string, std::unique_ptr<HwmonTempSensor>>&
436714a25aSJames Feist         sensors,
446714a25aSJames Feist     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
456714a25aSJames Feist     const std::unique_ptr<boost::container::flat_set<std::string>>&
466714a25aSJames Feist         sensorsChanged)
476714a25aSJames Feist {
486714a25aSJames Feist     bool firstScan = sensorsChanged == nullptr;
496714a25aSJames Feist     // use new data the first time, then refresh
506714a25aSJames Feist     ManagedObjectType sensorConfigurations;
516714a25aSJames Feist     bool useCache = false;
529ced0a38SJae Hyun Yoo     for (const char* type : sensorTypes)
536714a25aSJames Feist     {
546714a25aSJames Feist         if (!getSensorConfiguration(type, dbusConnection, sensorConfigurations,
556714a25aSJames Feist                                     useCache))
566714a25aSJames Feist         {
576714a25aSJames Feist             std::cerr << "error communicating to entity manager\n";
586714a25aSJames Feist             return;
596714a25aSJames Feist         }
606714a25aSJames Feist         useCache = true;
616714a25aSJames Feist     }
626714a25aSJames Feist     std::vector<fs::path> paths;
639ced0a38SJae Hyun Yoo     if (!findFiles(fs::path("/sys/class/hwmon"), R"(temp\d+_input)", paths))
646714a25aSJames Feist     {
656714a25aSJames Feist         std::cerr << "No temperature sensors in system\n";
666714a25aSJames Feist         return;
676714a25aSJames Feist     }
686714a25aSJames Feist 
6937266ca9SJames Feist     boost::container::flat_set<std::string> directories;
7037266ca9SJames Feist 
716714a25aSJames Feist     // iterate through all found temp sensors, and try to match them with
726714a25aSJames Feist     // configuration
736714a25aSJames Feist     for (auto& path : paths)
746714a25aSJames Feist     {
756714a25aSJames Feist         std::smatch match;
7637266ca9SJames Feist         const std::string& pathStr = path.string();
776714a25aSJames Feist         auto directory = path.parent_path();
786714a25aSJames Feist 
7937266ca9SJames Feist         auto ret = directories.insert(directory.string());
8037266ca9SJames Feist         if (!ret.second)
816714a25aSJames Feist         {
8237266ca9SJames Feist             continue; // already searched this path
8337266ca9SJames Feist         }
8437266ca9SJames Feist 
85b6c0b914SJames Feist         fs::path device = directory / "device";
8637266ca9SJames Feist         std::string deviceName = fs::canonical(device).stem();
8737266ca9SJames Feist         auto findHyphen = deviceName.find("-");
8837266ca9SJames Feist         if (findHyphen == std::string::npos)
8937266ca9SJames Feist         {
9037266ca9SJames Feist             std::cerr << "found bad device " << deviceName << "\n";
916714a25aSJames Feist             continue;
926714a25aSJames Feist         }
9337266ca9SJames Feist         std::string busStr = deviceName.substr(0, findHyphen);
9437266ca9SJames Feist         std::string addrStr = deviceName.substr(findHyphen + 1);
9537266ca9SJames Feist 
9637266ca9SJames Feist         size_t bus = 0;
9737266ca9SJames Feist         size_t addr = 0;
9837266ca9SJames Feist         try
996714a25aSJames Feist         {
10037266ca9SJames Feist             bus = std::stoi(busStr);
10137266ca9SJames Feist             addr = std::stoi(addrStr, 0, 16);
10237266ca9SJames Feist         }
103b6c0b914SJames Feist         catch (std::invalid_argument&)
10437266ca9SJames Feist         {
1056714a25aSJames Feist             continue;
1066714a25aSJames Feist         }
1076714a25aSJames Feist         const SensorData* sensorData = nullptr;
1086714a25aSJames Feist         const std::string* interfacePath = nullptr;
10937266ca9SJames Feist         const char* sensorType = nullptr;
1106714a25aSJames Feist         const std::pair<std::string, boost::container::flat_map<
1116714a25aSJames Feist                                          std::string, BasicVariantType>>*
1126714a25aSJames Feist             baseConfiguration = nullptr;
11337266ca9SJames Feist 
11437266ca9SJames Feist         for (const std::pair<sdbusplus::message::object_path, SensorData>&
11537266ca9SJames Feist                  sensor : sensorConfigurations)
11637266ca9SJames Feist         {
11737266ca9SJames Feist             sensorData = &(sensor.second);
1189ced0a38SJae Hyun Yoo             for (const char* type : sensorTypes)
1196714a25aSJames Feist             {
1206714a25aSJames Feist                 auto sensorBase = sensorData->find(type);
1216714a25aSJames Feist                 if (sensorBase != sensorData->end())
1226714a25aSJames Feist                 {
1236714a25aSJames Feist                     baseConfiguration = &(*sensorBase);
1246714a25aSJames Feist                     sensorType = type;
1256714a25aSJames Feist                     break;
1266714a25aSJames Feist                 }
1276714a25aSJames Feist             }
1286714a25aSJames Feist             if (baseConfiguration == nullptr)
1296714a25aSJames Feist             {
13037266ca9SJames Feist                 std::cerr << "error finding base configuration for "
13137266ca9SJames Feist                           << deviceName << "\n";
13237266ca9SJames Feist                 continue;
13337266ca9SJames Feist             }
13437266ca9SJames Feist             auto configurationBus = baseConfiguration->second.find("Bus");
13537266ca9SJames Feist             auto configurationAddress =
13637266ca9SJames Feist                 baseConfiguration->second.find("Address");
13737266ca9SJames Feist 
13837266ca9SJames Feist             if (configurationBus == baseConfiguration->second.end() ||
13937266ca9SJames Feist                 configurationAddress == baseConfiguration->second.end())
14037266ca9SJames Feist             {
14137266ca9SJames Feist                 std::cerr << "error finding bus or address in configuration";
14237266ca9SJames Feist                 continue;
14337266ca9SJames Feist             }
14437266ca9SJames Feist 
1453eb82629SJames Feist             if (std::get<uint64_t>(configurationBus->second) != bus ||
1463eb82629SJames Feist                 std::get<uint64_t>(configurationAddress->second) != addr)
14737266ca9SJames Feist             {
14837266ca9SJames Feist                 continue;
14937266ca9SJames Feist             }
15037266ca9SJames Feist 
15137266ca9SJames Feist             interfacePath = &(sensor.first.str);
15237266ca9SJames Feist             break;
15337266ca9SJames Feist         }
15437266ca9SJames Feist         if (interfacePath == nullptr)
15537266ca9SJames Feist         {
15637266ca9SJames Feist             std::cerr << "failed to find match for " << deviceName << "\n";
1576714a25aSJames Feist             continue;
1586714a25aSJames Feist         }
1596714a25aSJames Feist 
1606714a25aSJames Feist         auto findSensorName = baseConfiguration->second.find("Name");
1616714a25aSJames Feist         if (findSensorName == baseConfiguration->second.end())
1626714a25aSJames Feist         {
1636714a25aSJames Feist             std::cerr << "could not determine configuration name for "
16437266ca9SJames Feist                       << deviceName << "\n";
1656714a25aSJames Feist             continue;
1666714a25aSJames Feist         }
1673eb82629SJames Feist         std::string sensorName = std::get<std::string>(findSensorName->second);
1686714a25aSJames Feist         // on rescans, only update sensors we were signaled by
1696714a25aSJames Feist         auto findSensor = sensors.find(sensorName);
1706714a25aSJames Feist         if (!firstScan && findSensor != sensors.end())
1716714a25aSJames Feist         {
1726714a25aSJames Feist             bool found = false;
1736714a25aSJames Feist             for (auto it = sensorsChanged->begin(); it != sensorsChanged->end();
1746714a25aSJames Feist                  it++)
1756714a25aSJames Feist             {
1766714a25aSJames Feist                 if (boost::ends_with(*it, findSensor->second->name))
1776714a25aSJames Feist                 {
1786714a25aSJames Feist                     sensorsChanged->erase(it);
1796714a25aSJames Feist                     findSensor->second = nullptr;
1806714a25aSJames Feist                     found = true;
1816714a25aSJames Feist                     break;
1826714a25aSJames Feist                 }
1836714a25aSJames Feist             }
1846714a25aSJames Feist             if (!found)
1856714a25aSJames Feist             {
1866714a25aSJames Feist                 continue;
1876714a25aSJames Feist             }
1886714a25aSJames Feist         }
1896714a25aSJames Feist         std::vector<thresholds::Threshold> sensorThresholds;
1909ced0a38SJae Hyun Yoo         if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
1916714a25aSJames Feist         {
1926714a25aSJames Feist             std::cerr << "error populating thresholds for " << sensorName
1936714a25aSJames Feist                       << "\n";
1946714a25aSJames Feist         }
1956714a25aSJames Feist 
1966714a25aSJames Feist         sensors[sensorName] = std::make_unique<HwmonTempSensor>(
19737266ca9SJames Feist             directory.string() + "/temp1_input", sensorType, objectServer,
19837266ca9SJames Feist             dbusConnection, io, sensorName, std::move(sensorThresholds),
19937266ca9SJames Feist             *interfacePath);
20037266ca9SJames Feist         auto findSecondName = baseConfiguration->second.find("Name1");
20137266ca9SJames Feist         if (findSecondName == baseConfiguration->second.end())
20237266ca9SJames Feist         {
20337266ca9SJames Feist             continue;
20437266ca9SJames Feist         }
20537266ca9SJames Feist 
2063eb82629SJames Feist         sensorName = std::get<std::string>(findSecondName->second);
20737266ca9SJames Feist         sensors[sensorName] = std::make_unique<HwmonTempSensor>(
20837266ca9SJames Feist             directory.string() + "/temp2_input", sensorType, objectServer,
20937266ca9SJames Feist             dbusConnection, io, sensorName,
21037266ca9SJames Feist             std::vector<thresholds::Threshold>(), *interfacePath);
2116714a25aSJames Feist     }
2126714a25aSJames Feist }
2136714a25aSJames Feist 
214b6c0b914SJames Feist int main()
2156714a25aSJames Feist {
2166714a25aSJames Feist     boost::asio::io_service io;
2176714a25aSJames Feist     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
2186714a25aSJames Feist     systemBus->request_name("xyz.openbmc_project.HwmonTempSensor");
2196714a25aSJames Feist     sdbusplus::asio::object_server objectServer(systemBus);
2206714a25aSJames Feist     boost::container::flat_map<std::string, std::unique_ptr<HwmonTempSensor>>
2216714a25aSJames Feist         sensors;
2226714a25aSJames Feist     std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
2236714a25aSJames Feist     std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged =
2246714a25aSJames Feist         std::make_unique<boost::container::flat_set<std::string>>();
2256714a25aSJames Feist 
2266714a25aSJames Feist     io.post([&]() {
2276714a25aSJames Feist         createSensors(io, objectServer, sensors, systemBus, nullptr);
2286714a25aSJames Feist     });
2296714a25aSJames Feist 
2306714a25aSJames Feist     boost::asio::deadline_timer filterTimer(io);
2316714a25aSJames Feist     std::function<void(sdbusplus::message::message&)> eventHandler =
2326714a25aSJames Feist         [&](sdbusplus::message::message& message) {
2336714a25aSJames Feist             if (message.is_method_error())
2346714a25aSJames Feist             {
2356714a25aSJames Feist                 std::cerr << "callback method error\n";
2366714a25aSJames Feist                 return;
2376714a25aSJames Feist             }
2386714a25aSJames Feist             sensorsChanged->insert(message.get_path());
2396714a25aSJames Feist             // this implicitly cancels the timer
2406714a25aSJames Feist             filterTimer.expires_from_now(boost::posix_time::seconds(1));
2416714a25aSJames Feist 
2426714a25aSJames Feist             filterTimer.async_wait([&](const boost::system::error_code& ec) {
2436714a25aSJames Feist                 if (ec == boost::asio::error::operation_aborted)
2446714a25aSJames Feist                 {
2456714a25aSJames Feist                     /* we were canceled*/
2466714a25aSJames Feist                     return;
2476714a25aSJames Feist                 }
2486714a25aSJames Feist                 else if (ec)
2496714a25aSJames Feist                 {
2506714a25aSJames Feist                     std::cerr << "timer error\n";
2516714a25aSJames Feist                     return;
2526714a25aSJames Feist                 }
2536714a25aSJames Feist                 createSensors(io, objectServer, sensors, systemBus,
2546714a25aSJames Feist                               sensorsChanged);
2556714a25aSJames Feist             });
2566714a25aSJames Feist         };
2576714a25aSJames Feist 
2589ced0a38SJae Hyun Yoo     for (const char* type : sensorTypes)
2596714a25aSJames Feist     {
2606714a25aSJames Feist         auto match = std::make_unique<sdbusplus::bus::match::match>(
2616714a25aSJames Feist             static_cast<sdbusplus::bus::bus&>(*systemBus),
2626714a25aSJames Feist             "type='signal',member='PropertiesChanged',path_namespace='" +
2639ced0a38SJae Hyun Yoo                 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
2646714a25aSJames Feist             eventHandler);
2656714a25aSJames Feist         matches.emplace_back(std::move(match));
2666714a25aSJames Feist     }
2676714a25aSJames Feist 
2686714a25aSJames Feist     io.run();
2696714a25aSJames Feist }
270