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 
20*96e97db7SPatrick Venture #include <array>
216714a25aSJames Feist #include <boost/algorithm/string/predicate.hpp>
226714a25aSJames Feist #include <boost/algorithm/string/replace.hpp>
23*96e97db7SPatrick Venture #include <boost/container/flat_map.hpp>
246714a25aSJames Feist #include <boost/container/flat_set.hpp>
2524f02f24SJames Feist #include <filesystem>
266714a25aSJames Feist #include <fstream>
27*96e97db7SPatrick Venture #include <functional>
28*96e97db7SPatrick Venture #include <memory>
296714a25aSJames Feist #include <regex>
306714a25aSJames Feist #include <sdbusplus/asio/connection.hpp>
316714a25aSJames Feist #include <sdbusplus/asio/object_server.hpp>
32*96e97db7SPatrick Venture #include <sdbusplus/bus/match.hpp>
33*96e97db7SPatrick Venture #include <stdexcept>
34*96e97db7SPatrick Venture #include <string>
35*96e97db7SPatrick Venture #include <utility>
36*96e97db7SPatrick Venture #include <variant>
37*96e97db7SPatrick Venture #include <vector>
386714a25aSJames Feist 
396714a25aSJames Feist static constexpr bool DEBUG = false;
406714a25aSJames Feist 
41cf3bce6eSJames Feist namespace fs = std::filesystem;
423546adb9SPatrick Venture static constexpr std::array<const char*, 7> sensorTypes = {
436714a25aSJames Feist     "xyz.openbmc_project.Configuration.TMP75",
4455ab2afbSJohn Wang     "xyz.openbmc_project.Configuration.TMP421",
457fa475d3SPatrick Venture     "xyz.openbmc_project.Configuration.TMP441",
4655ab2afbSJohn Wang     "xyz.openbmc_project.Configuration.TMP112",
473546adb9SPatrick Venture     "xyz.openbmc_project.Configuration.TMP175",
48bd1a9d5dSPatrick Venture     "xyz.openbmc_project.Configuration.EMC1413",
49bd1a9d5dSPatrick Venture     "xyz.openbmc_project.Configuration.MAX31725"};
506714a25aSJames Feist 
516714a25aSJames Feist void createSensors(
526714a25aSJames Feist     boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
536714a25aSJames Feist     boost::container::flat_map<std::string, std::unique_ptr<HwmonTempSensor>>&
546714a25aSJames Feist         sensors,
556714a25aSJames Feist     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
566714a25aSJames Feist     const std::unique_ptr<boost::container::flat_set<std::string>>&
576714a25aSJames Feist         sensorsChanged)
586714a25aSJames Feist {
59df515159SJames Feist     auto getter = std::make_shared<GetSensorConfiguration>(
60df515159SJames Feist         dbusConnection,
61df515159SJames Feist         std::move([&io, &objectServer, &sensors, &dbusConnection,
62df515159SJames Feist                    &sensorsChanged](
63df515159SJames Feist                       const ManagedObjectType& sensorConfigurations) {
646714a25aSJames Feist             bool firstScan = sensorsChanged == nullptr;
65df515159SJames Feist 
666714a25aSJames Feist             std::vector<fs::path> paths;
67df515159SJames Feist             if (!findFiles(fs::path("/sys/class/hwmon"), R"(temp\d+_input)",
68df515159SJames Feist                            paths))
696714a25aSJames Feist             {
706714a25aSJames Feist                 std::cerr << "No temperature sensors in system\n";
716714a25aSJames Feist                 return;
726714a25aSJames Feist             }
736714a25aSJames Feist 
7437266ca9SJames Feist             boost::container::flat_set<std::string> directories;
7537266ca9SJames Feist 
76df515159SJames Feist             // iterate through all found temp sensors, and try to match them
77df515159SJames Feist             // with configuration
786714a25aSJames Feist             for (auto& path : paths)
796714a25aSJames Feist             {
806714a25aSJames Feist                 std::smatch match;
8137266ca9SJames Feist                 const std::string& pathStr = path.string();
826714a25aSJames Feist                 auto directory = path.parent_path();
836714a25aSJames Feist 
8437266ca9SJames Feist                 auto ret = directories.insert(directory.string());
8537266ca9SJames Feist                 if (!ret.second)
866714a25aSJames Feist                 {
8737266ca9SJames Feist                     continue; // already searched this path
8837266ca9SJames Feist                 }
8937266ca9SJames Feist 
90b6c0b914SJames Feist                 fs::path device = directory / "device";
9137266ca9SJames Feist                 std::string deviceName = fs::canonical(device).stem();
9237266ca9SJames Feist                 auto findHyphen = deviceName.find("-");
9337266ca9SJames Feist                 if (findHyphen == std::string::npos)
9437266ca9SJames Feist                 {
9537266ca9SJames Feist                     std::cerr << "found bad device " << deviceName << "\n";
966714a25aSJames Feist                     continue;
976714a25aSJames Feist                 }
9837266ca9SJames Feist                 std::string busStr = deviceName.substr(0, findHyphen);
9937266ca9SJames Feist                 std::string addrStr = deviceName.substr(findHyphen + 1);
10037266ca9SJames Feist 
10137266ca9SJames Feist                 size_t bus = 0;
10237266ca9SJames Feist                 size_t addr = 0;
10337266ca9SJames Feist                 try
1046714a25aSJames Feist                 {
10537266ca9SJames Feist                     bus = std::stoi(busStr);
10637266ca9SJames Feist                     addr = std::stoi(addrStr, 0, 16);
10737266ca9SJames Feist                 }
108b6c0b914SJames Feist                 catch (std::invalid_argument&)
10937266ca9SJames Feist                 {
1106714a25aSJames Feist                     continue;
1116714a25aSJames Feist                 }
1126714a25aSJames Feist                 const SensorData* sensorData = nullptr;
1136714a25aSJames Feist                 const std::string* interfacePath = nullptr;
11437266ca9SJames Feist                 const char* sensorType = nullptr;
115df515159SJames Feist                 const std::pair<
116df515159SJames Feist                     std::string,
117df515159SJames Feist                     boost::container::flat_map<std::string, BasicVariantType>>*
1186714a25aSJames Feist                     baseConfiguration = nullptr;
11937266ca9SJames Feist 
120df515159SJames Feist                 for (const std::pair<sdbusplus::message::object_path,
121df515159SJames Feist                                      SensorData>& sensor : sensorConfigurations)
12237266ca9SJames Feist                 {
12337266ca9SJames Feist                     sensorData = &(sensor.second);
1249ced0a38SJae Hyun Yoo                     for (const char* type : sensorTypes)
1256714a25aSJames Feist                     {
1266714a25aSJames Feist                         auto sensorBase = sensorData->find(type);
1276714a25aSJames Feist                         if (sensorBase != sensorData->end())
1286714a25aSJames Feist                         {
1296714a25aSJames Feist                             baseConfiguration = &(*sensorBase);
1306714a25aSJames Feist                             sensorType = type;
1316714a25aSJames Feist                             break;
1326714a25aSJames Feist                         }
1336714a25aSJames Feist                     }
1346714a25aSJames Feist                     if (baseConfiguration == nullptr)
1356714a25aSJames Feist                     {
13637266ca9SJames Feist                         std::cerr << "error finding base configuration for "
13737266ca9SJames Feist                                   << deviceName << "\n";
13837266ca9SJames Feist                         continue;
13937266ca9SJames Feist                     }
140df515159SJames Feist                     auto configurationBus =
141df515159SJames Feist                         baseConfiguration->second.find("Bus");
14237266ca9SJames Feist                     auto configurationAddress =
14337266ca9SJames Feist                         baseConfiguration->second.find("Address");
14437266ca9SJames Feist 
14537266ca9SJames Feist                     if (configurationBus == baseConfiguration->second.end() ||
14637266ca9SJames Feist                         configurationAddress == baseConfiguration->second.end())
14737266ca9SJames Feist                     {
148df515159SJames Feist                         std::cerr
149df515159SJames Feist                             << "error finding bus or address in configuration";
15037266ca9SJames Feist                         continue;
15137266ca9SJames Feist                     }
15237266ca9SJames Feist 
1533eb82629SJames Feist                     if (std::get<uint64_t>(configurationBus->second) != bus ||
154df515159SJames Feist                         std::get<uint64_t>(configurationAddress->second) !=
155df515159SJames Feist                             addr)
15637266ca9SJames Feist                     {
15737266ca9SJames Feist                         continue;
15837266ca9SJames Feist                     }
15937266ca9SJames Feist 
16037266ca9SJames Feist                     interfacePath = &(sensor.first.str);
16137266ca9SJames Feist                     break;
16237266ca9SJames Feist                 }
16337266ca9SJames Feist                 if (interfacePath == nullptr)
16437266ca9SJames Feist                 {
165df515159SJames Feist                     std::cerr << "failed to find match for " << deviceName
166df515159SJames Feist                               << "\n";
1676714a25aSJames Feist                     continue;
1686714a25aSJames Feist                 }
1696714a25aSJames Feist 
1706714a25aSJames Feist                 auto findSensorName = baseConfiguration->second.find("Name");
1716714a25aSJames Feist                 if (findSensorName == baseConfiguration->second.end())
1726714a25aSJames Feist                 {
1736714a25aSJames Feist                     std::cerr << "could not determine configuration name for "
17437266ca9SJames Feist                               << deviceName << "\n";
1756714a25aSJames Feist                     continue;
1766714a25aSJames Feist                 }
177df515159SJames Feist                 std::string sensorName =
178df515159SJames Feist                     std::get<std::string>(findSensorName->second);
1796714a25aSJames Feist                 // on rescans, only update sensors we were signaled by
1806714a25aSJames Feist                 auto findSensor = sensors.find(sensorName);
1816714a25aSJames Feist                 if (!firstScan && findSensor != sensors.end())
1826714a25aSJames Feist                 {
1836714a25aSJames Feist                     bool found = false;
184df515159SJames Feist                     for (auto it = sensorsChanged->begin();
185df515159SJames Feist                          it != sensorsChanged->end(); it++)
1866714a25aSJames Feist                     {
1876714a25aSJames Feist                         if (boost::ends_with(*it, findSensor->second->name))
1886714a25aSJames Feist                         {
1896714a25aSJames Feist                             sensorsChanged->erase(it);
1906714a25aSJames Feist                             findSensor->second = nullptr;
1916714a25aSJames Feist                             found = true;
1926714a25aSJames Feist                             break;
1936714a25aSJames Feist                         }
1946714a25aSJames Feist                     }
1956714a25aSJames Feist                     if (!found)
1966714a25aSJames Feist                     {
1976714a25aSJames Feist                         continue;
1986714a25aSJames Feist                     }
1996714a25aSJames Feist                 }
2006714a25aSJames Feist                 std::vector<thresholds::Threshold> sensorThresholds;
2019ced0a38SJae Hyun Yoo                 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
2026714a25aSJames Feist                 {
203df515159SJames Feist                     std::cerr << "error populating thresholds for "
204df515159SJames Feist                               << sensorName << "\n";
2056714a25aSJames Feist                 }
206df515159SJames Feist                 auto& sensor1 = sensors[sensorName];
207df515159SJames Feist                 sensor1 = nullptr;
208df515159SJames Feist                 sensor1 = std::make_unique<HwmonTempSensor>(
209df515159SJames Feist                     directory.string() + "/temp1_input", sensorType,
210df515159SJames Feist                     objectServer, dbusConnection, io, sensorName,
211df515159SJames Feist                     std::move(sensorThresholds), *interfacePath);
21237266ca9SJames Feist                 auto findSecondName = baseConfiguration->second.find("Name1");
21337266ca9SJames Feist                 if (findSecondName == baseConfiguration->second.end())
21437266ca9SJames Feist                 {
21537266ca9SJames Feist                     continue;
21637266ca9SJames Feist                 }
2173eb82629SJames Feist                 sensorName = std::get<std::string>(findSecondName->second);
218df515159SJames Feist                 auto& sensor2 = sensors[sensorName];
219df515159SJames Feist                 sensor2 = nullptr;
220df515159SJames Feist                 sensor2 = std::make_unique<HwmonTempSensor>(
221df515159SJames Feist                     directory.string() + "/temp2_input", sensorType,
222df515159SJames Feist                     objectServer, dbusConnection, io, sensorName,
22337266ca9SJames Feist                     std::vector<thresholds::Threshold>(), *interfacePath);
2246714a25aSJames Feist             }
225df515159SJames Feist         }));
226df515159SJames Feist     getter->getConfiguration(
227df515159SJames Feist         std::vector<std::string>(sensorTypes.begin(), sensorTypes.end()));
2286714a25aSJames Feist }
2296714a25aSJames Feist 
230b6c0b914SJames Feist int main()
2316714a25aSJames Feist {
2326714a25aSJames Feist     boost::asio::io_service io;
2336714a25aSJames Feist     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
2346714a25aSJames Feist     systemBus->request_name("xyz.openbmc_project.HwmonTempSensor");
2356714a25aSJames Feist     sdbusplus::asio::object_server objectServer(systemBus);
2366714a25aSJames Feist     boost::container::flat_map<std::string, std::unique_ptr<HwmonTempSensor>>
2376714a25aSJames Feist         sensors;
2386714a25aSJames Feist     std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
2396714a25aSJames Feist     std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged =
2406714a25aSJames Feist         std::make_unique<boost::container::flat_set<std::string>>();
2416714a25aSJames Feist 
2426714a25aSJames Feist     io.post([&]() {
2436714a25aSJames Feist         createSensors(io, objectServer, sensors, systemBus, nullptr);
2446714a25aSJames Feist     });
2456714a25aSJames Feist 
2466714a25aSJames Feist     boost::asio::deadline_timer filterTimer(io);
2476714a25aSJames Feist     std::function<void(sdbusplus::message::message&)> eventHandler =
2486714a25aSJames Feist         [&](sdbusplus::message::message& message) {
2496714a25aSJames Feist             if (message.is_method_error())
2506714a25aSJames Feist             {
2516714a25aSJames Feist                 std::cerr << "callback method error\n";
2526714a25aSJames Feist                 return;
2536714a25aSJames Feist             }
2546714a25aSJames Feist             sensorsChanged->insert(message.get_path());
2556714a25aSJames Feist             // this implicitly cancels the timer
2566714a25aSJames Feist             filterTimer.expires_from_now(boost::posix_time::seconds(1));
2576714a25aSJames Feist 
2586714a25aSJames Feist             filterTimer.async_wait([&](const boost::system::error_code& ec) {
2596714a25aSJames Feist                 if (ec == boost::asio::error::operation_aborted)
2606714a25aSJames Feist                 {
2616714a25aSJames Feist                     /* we were canceled*/
2626714a25aSJames Feist                     return;
2636714a25aSJames Feist                 }
2646714a25aSJames Feist                 else if (ec)
2656714a25aSJames Feist                 {
2666714a25aSJames Feist                     std::cerr << "timer error\n";
2676714a25aSJames Feist                     return;
2686714a25aSJames Feist                 }
2696714a25aSJames Feist                 createSensors(io, objectServer, sensors, systemBus,
2706714a25aSJames Feist                               sensorsChanged);
2716714a25aSJames Feist             });
2726714a25aSJames Feist         };
2736714a25aSJames Feist 
2749ced0a38SJae Hyun Yoo     for (const char* type : sensorTypes)
2756714a25aSJames Feist     {
2766714a25aSJames Feist         auto match = std::make_unique<sdbusplus::bus::match::match>(
2776714a25aSJames Feist             static_cast<sdbusplus::bus::bus&>(*systemBus),
2786714a25aSJames Feist             "type='signal',member='PropertiesChanged',path_namespace='" +
2799ced0a38SJae Hyun Yoo                 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
2806714a25aSJames Feist             eventHandler);
2816714a25aSJames Feist         matches.emplace_back(std::move(match));
2826714a25aSJames Feist     }
2836714a25aSJames Feist 
2846714a25aSJames Feist     io.run();
2856714a25aSJames Feist }
286