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 
206714a25aSJames Feist #include <boost/algorithm/string/predicate.hpp>
216714a25aSJames Feist #include <boost/algorithm/string/replace.hpp>
2296e97db7SPatrick Venture #include <boost/container/flat_map.hpp>
236714a25aSJames Feist #include <boost/container/flat_set.hpp>
2438fb5983SJames Feist #include <sdbusplus/asio/connection.hpp>
2538fb5983SJames Feist #include <sdbusplus/asio/object_server.hpp>
2638fb5983SJames Feist #include <sdbusplus/bus/match.hpp>
2738fb5983SJames Feist 
2838fb5983SJames Feist #include <array>
2924f02f24SJames Feist #include <filesystem>
306714a25aSJames Feist #include <fstream>
3196e97db7SPatrick Venture #include <functional>
3296e97db7SPatrick Venture #include <memory>
336714a25aSJames Feist #include <regex>
3496e97db7SPatrick Venture #include <stdexcept>
3596e97db7SPatrick Venture #include <string>
3696e97db7SPatrick Venture #include <utility>
3796e97db7SPatrick Venture #include <variant>
3896e97db7SPatrick Venture #include <vector>
396714a25aSJames Feist 
406714a25aSJames Feist static constexpr bool DEBUG = false;
416714a25aSJames Feist 
42cf3bce6eSJames Feist namespace fs = std::filesystem;
43*381636e2SGilbert Chen static constexpr std::array<const char*, 13> sensorTypes = {
44*381636e2SGilbert Chen     "xyz.openbmc_project.Configuration.EMC1412",
458b3f7d40SAlex Qiu     "xyz.openbmc_project.Configuration.EMC1413",
46*381636e2SGilbert Chen     "xyz.openbmc_project.Configuration.EMC1414",
478b3f7d40SAlex Qiu     "xyz.openbmc_project.Configuration.MAX31725",
488b3f7d40SAlex Qiu     "xyz.openbmc_project.Configuration.MAX31730",
4916e7af1dSJason Ling     "xyz.openbmc_project.Configuration.MAX6581",
503840d0adSJosh Lehan     "xyz.openbmc_project.Configuration.MAX6654",
518fb0a013SJosh Lehan     "xyz.openbmc_project.Configuration.SBTSI",
5255ab2afbSJohn Wang     "xyz.openbmc_project.Configuration.TMP112",
533546adb9SPatrick Venture     "xyz.openbmc_project.Configuration.TMP175",
548b3f7d40SAlex Qiu     "xyz.openbmc_project.Configuration.TMP421",
558b3f7d40SAlex Qiu     "xyz.openbmc_project.Configuration.TMP441",
568b3f7d40SAlex Qiu     "xyz.openbmc_project.Configuration.TMP75"};
576714a25aSJames Feist 
586714a25aSJames Feist void createSensors(
596714a25aSJames Feist     boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
60f3fd1915SYong Li     boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>&
616714a25aSJames Feist         sensors,
626714a25aSJames Feist     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
635591cf08SJames Feist     const std::shared_ptr<boost::container::flat_set<std::string>>&
646714a25aSJames Feist         sensorsChanged)
656714a25aSJames Feist {
66df515159SJames Feist     auto getter = std::make_shared<GetSensorConfiguration>(
67df515159SJames Feist         dbusConnection,
68df515159SJames Feist         std::move([&io, &objectServer, &sensors, &dbusConnection,
695591cf08SJames Feist                    sensorsChanged](
70df515159SJames Feist                       const ManagedObjectType& sensorConfigurations) {
716714a25aSJames Feist             bool firstScan = sensorsChanged == nullptr;
72df515159SJames Feist 
736714a25aSJames Feist             std::vector<fs::path> paths;
74df515159SJames Feist             if (!findFiles(fs::path("/sys/class/hwmon"), R"(temp\d+_input)",
75df515159SJames Feist                            paths))
766714a25aSJames Feist             {
776714a25aSJames Feist                 std::cerr << "No temperature sensors in system\n";
786714a25aSJames Feist                 return;
796714a25aSJames Feist             }
806714a25aSJames Feist 
8137266ca9SJames Feist             boost::container::flat_set<std::string> directories;
8237266ca9SJames Feist 
83df515159SJames Feist             // iterate through all found temp sensors, and try to match them
84df515159SJames Feist             // with configuration
856714a25aSJames Feist             for (auto& path : paths)
866714a25aSJames Feist             {
876714a25aSJames Feist                 std::smatch match;
8837266ca9SJames Feist                 const std::string& pathStr = path.string();
896714a25aSJames Feist                 auto directory = path.parent_path();
906714a25aSJames Feist 
9137266ca9SJames Feist                 auto ret = directories.insert(directory.string());
9237266ca9SJames Feist                 if (!ret.second)
936714a25aSJames Feist                 {
9437266ca9SJames Feist                     continue; // already searched this path
9537266ca9SJames Feist                 }
9637266ca9SJames Feist 
97b6c0b914SJames Feist                 fs::path device = directory / "device";
9837266ca9SJames Feist                 std::string deviceName = fs::canonical(device).stem();
9937266ca9SJames Feist                 auto findHyphen = deviceName.find("-");
10037266ca9SJames Feist                 if (findHyphen == std::string::npos)
10137266ca9SJames Feist                 {
10237266ca9SJames Feist                     std::cerr << "found bad device " << deviceName << "\n";
1036714a25aSJames Feist                     continue;
1046714a25aSJames Feist                 }
10537266ca9SJames Feist                 std::string busStr = deviceName.substr(0, findHyphen);
10637266ca9SJames Feist                 std::string addrStr = deviceName.substr(findHyphen + 1);
10737266ca9SJames Feist 
10837266ca9SJames Feist                 size_t bus = 0;
10937266ca9SJames Feist                 size_t addr = 0;
11037266ca9SJames Feist                 try
1116714a25aSJames Feist                 {
11237266ca9SJames Feist                     bus = std::stoi(busStr);
11337266ca9SJames Feist                     addr = std::stoi(addrStr, 0, 16);
11437266ca9SJames Feist                 }
115b6c0b914SJames Feist                 catch (std::invalid_argument&)
11637266ca9SJames Feist                 {
1176714a25aSJames Feist                     continue;
1186714a25aSJames Feist                 }
1196714a25aSJames Feist                 const SensorData* sensorData = nullptr;
1206714a25aSJames Feist                 const std::string* interfacePath = nullptr;
12137266ca9SJames Feist                 const char* sensorType = nullptr;
1228b3f7d40SAlex Qiu                 const SensorBaseConfiguration* baseConfiguration = nullptr;
1238b3f7d40SAlex Qiu                 const SensorBaseConfigMap* baseConfigMap = nullptr;
12437266ca9SJames Feist 
125df515159SJames Feist                 for (const std::pair<sdbusplus::message::object_path,
126df515159SJames Feist                                      SensorData>& sensor : sensorConfigurations)
12737266ca9SJames Feist                 {
12837266ca9SJames Feist                     sensorData = &(sensor.second);
1299ced0a38SJae Hyun Yoo                     for (const char* type : sensorTypes)
1306714a25aSJames Feist                     {
1316714a25aSJames Feist                         auto sensorBase = sensorData->find(type);
1326714a25aSJames Feist                         if (sensorBase != sensorData->end())
1336714a25aSJames Feist                         {
1346714a25aSJames Feist                             baseConfiguration = &(*sensorBase);
1356714a25aSJames Feist                             sensorType = type;
1366714a25aSJames Feist                             break;
1376714a25aSJames Feist                         }
1386714a25aSJames Feist                     }
1396714a25aSJames Feist                     if (baseConfiguration == nullptr)
1406714a25aSJames Feist                     {
14137266ca9SJames Feist                         std::cerr << "error finding base configuration for "
14237266ca9SJames Feist                                   << deviceName << "\n";
14337266ca9SJames Feist                         continue;
14437266ca9SJames Feist                     }
1458b3f7d40SAlex Qiu                     baseConfigMap = &baseConfiguration->second;
1468b3f7d40SAlex Qiu                     auto configurationBus = baseConfigMap->find("Bus");
1478b3f7d40SAlex Qiu                     auto configurationAddress = baseConfigMap->find("Address");
14837266ca9SJames Feist 
1498b3f7d40SAlex Qiu                     if (configurationBus == baseConfigMap->end() ||
1508b3f7d40SAlex Qiu                         configurationAddress == baseConfigMap->end())
15137266ca9SJames Feist                     {
1528b3f7d40SAlex Qiu                         std::cerr << "error finding bus or address in "
1538b3f7d40SAlex Qiu                                      "configuration\n";
15437266ca9SJames Feist                         continue;
15537266ca9SJames Feist                     }
15637266ca9SJames Feist 
1573eb82629SJames Feist                     if (std::get<uint64_t>(configurationBus->second) != bus ||
158df515159SJames Feist                         std::get<uint64_t>(configurationAddress->second) !=
159df515159SJames Feist                             addr)
16037266ca9SJames Feist                     {
16137266ca9SJames Feist                         continue;
16237266ca9SJames Feist                     }
16337266ca9SJames Feist 
16437266ca9SJames Feist                     interfacePath = &(sensor.first.str);
16537266ca9SJames Feist                     break;
16637266ca9SJames Feist                 }
16737266ca9SJames Feist                 if (interfacePath == nullptr)
16837266ca9SJames Feist                 {
169df515159SJames Feist                     std::cerr << "failed to find match for " << deviceName
170df515159SJames Feist                               << "\n";
1716714a25aSJames Feist                     continue;
1726714a25aSJames Feist                 }
1736714a25aSJames Feist 
1748b3f7d40SAlex Qiu                 auto findSensorName = baseConfigMap->find("Name");
1758b3f7d40SAlex Qiu                 if (findSensorName == baseConfigMap->end())
1766714a25aSJames Feist                 {
1776714a25aSJames Feist                     std::cerr << "could not determine configuration name for "
17837266ca9SJames Feist                               << deviceName << "\n";
1796714a25aSJames Feist                     continue;
1806714a25aSJames Feist                 }
181df515159SJames Feist                 std::string sensorName =
182df515159SJames Feist                     std::get<std::string>(findSensorName->second);
1836714a25aSJames Feist                 // on rescans, only update sensors we were signaled by
1846714a25aSJames Feist                 auto findSensor = sensors.find(sensorName);
1856714a25aSJames Feist                 if (!firstScan && findSensor != sensors.end())
1866714a25aSJames Feist                 {
1876714a25aSJames Feist                     bool found = false;
188df515159SJames Feist                     for (auto it = sensorsChanged->begin();
189df515159SJames Feist                          it != sensorsChanged->end(); it++)
1906714a25aSJames Feist                     {
1916714a25aSJames Feist                         if (boost::ends_with(*it, findSensor->second->name))
1926714a25aSJames Feist                         {
1936714a25aSJames Feist                             sensorsChanged->erase(it);
1946714a25aSJames Feist                             findSensor->second = nullptr;
1956714a25aSJames Feist                             found = true;
1966714a25aSJames Feist                             break;
1976714a25aSJames Feist                         }
1986714a25aSJames Feist                     }
1996714a25aSJames Feist                     if (!found)
2006714a25aSJames Feist                     {
2016714a25aSJames Feist                         continue;
2026714a25aSJames Feist                     }
2036714a25aSJames Feist                 }
2046714a25aSJames Feist                 std::vector<thresholds::Threshold> sensorThresholds;
2059ced0a38SJae Hyun Yoo                 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
2066714a25aSJames Feist                 {
207df515159SJames Feist                     std::cerr << "error populating thresholds for "
208df515159SJames Feist                               << sensorName << "\n";
2096714a25aSJames Feist                 }
210f9b01b6dSJames Feist                 auto findPowerOn = baseConfiguration->second.find("PowerState");
211f9b01b6dSJames Feist                 PowerState readState = PowerState::always;
212f9b01b6dSJames Feist                 if (findPowerOn != baseConfiguration->second.end())
213f9b01b6dSJames Feist                 {
214f9b01b6dSJames Feist                     std::string powerState = std::visit(
215f9b01b6dSJames Feist                         VariantToStringVisitor(), findPowerOn->second);
216f9b01b6dSJames Feist                     setReadState(powerState, readState);
217f9b01b6dSJames Feist                 }
218100c20bfSJason Ling 
219100c20bfSJason Ling                 auto permitSet = getPermitSet(*baseConfigMap);
2208b3f7d40SAlex Qiu                 auto& sensor = sensors[sensorName];
2218b3f7d40SAlex Qiu                 sensor = nullptr;
222100c20bfSJason Ling                 auto hwmonFile = getFullHwmonFilePath(directory.string(),
223100c20bfSJason Ling                                                       "temp1", permitSet);
224100c20bfSJason Ling                 if (hwmonFile)
225100c20bfSJason Ling                 {
226f3fd1915SYong Li                     sensor = std::make_shared<HwmonTempSensor>(
227100c20bfSJason Ling                         *hwmonFile, sensorType, objectServer, dbusConnection,
228100c20bfSJason Ling                         io, sensorName, std::move(sensorThresholds),
229100c20bfSJason Ling                         *interfacePath, readState);
230f3fd1915SYong Li                     sensor->setupRead();
231100c20bfSJason Ling                 }
2328b3f7d40SAlex Qiu                 // Looking for keys like "Name1" for temp2_input,
2338b3f7d40SAlex Qiu                 // "Name2" for temp3_input, etc.
2348b3f7d40SAlex Qiu                 int i = 0;
2358b3f7d40SAlex Qiu                 while (true)
23637266ca9SJames Feist                 {
2378b3f7d40SAlex Qiu                     ++i;
2388b3f7d40SAlex Qiu                     auto findKey =
2398b8bcc87SJason Ling                         baseConfigMap->find("Name" + std::to_string(i));
2408b3f7d40SAlex Qiu                     if (findKey == baseConfigMap->end())
2418b3f7d40SAlex Qiu                     {
2428b3f7d40SAlex Qiu                         break;
24337266ca9SJames Feist                     }
2448b3f7d40SAlex Qiu                     std::string sensorName =
2458b3f7d40SAlex Qiu                         std::get<std::string>(findKey->second);
246100c20bfSJason Ling                     hwmonFile = getFullHwmonFilePath(
247100c20bfSJason Ling                         directory.string(), "temp" + std::to_string(i + 1),
248100c20bfSJason Ling                         permitSet);
249100c20bfSJason Ling                     if (hwmonFile)
250100c20bfSJason Ling                     {
2518b3f7d40SAlex Qiu                         auto& sensor = sensors[sensorName];
2528b3f7d40SAlex Qiu                         sensor = nullptr;
253f3fd1915SYong Li                         sensor = std::make_shared<HwmonTempSensor>(
254100c20bfSJason Ling                             *hwmonFile, sensorType, objectServer,
255100c20bfSJason Ling                             dbusConnection, io, sensorName,
256100c20bfSJason Ling                             std::vector<thresholds::Threshold>(),
257f9b01b6dSJames Feist                             *interfacePath, readState);
258f3fd1915SYong Li                         sensor->setupRead();
2598b3f7d40SAlex Qiu                     }
2606714a25aSJames Feist                 }
261100c20bfSJason Ling             }
262df515159SJames Feist         }));
263df515159SJames Feist     getter->getConfiguration(
264df515159SJames Feist         std::vector<std::string>(sensorTypes.begin(), sensorTypes.end()));
2656714a25aSJames Feist }
2666714a25aSJames Feist 
267b6c0b914SJames Feist int main()
2686714a25aSJames Feist {
2696714a25aSJames Feist     boost::asio::io_service io;
2706714a25aSJames Feist     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
2716714a25aSJames Feist     systemBus->request_name("xyz.openbmc_project.HwmonTempSensor");
2726714a25aSJames Feist     sdbusplus::asio::object_server objectServer(systemBus);
273f3fd1915SYong Li     boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>
2746714a25aSJames Feist         sensors;
2756714a25aSJames Feist     std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
2765591cf08SJames Feist     auto sensorsChanged =
2775591cf08SJames Feist         std::make_shared<boost::container::flat_set<std::string>>();
2786714a25aSJames Feist 
2796714a25aSJames Feist     io.post([&]() {
2806714a25aSJames Feist         createSensors(io, objectServer, sensors, systemBus, nullptr);
2816714a25aSJames Feist     });
2826714a25aSJames Feist 
2836714a25aSJames Feist     boost::asio::deadline_timer filterTimer(io);
2846714a25aSJames Feist     std::function<void(sdbusplus::message::message&)> eventHandler =
2856714a25aSJames Feist         [&](sdbusplus::message::message& message) {
2866714a25aSJames Feist             if (message.is_method_error())
2876714a25aSJames Feist             {
2886714a25aSJames Feist                 std::cerr << "callback method error\n";
2896714a25aSJames Feist                 return;
2906714a25aSJames Feist             }
2916714a25aSJames Feist             sensorsChanged->insert(message.get_path());
2926714a25aSJames Feist             // this implicitly cancels the timer
2936714a25aSJames Feist             filterTimer.expires_from_now(boost::posix_time::seconds(1));
2946714a25aSJames Feist 
2956714a25aSJames Feist             filterTimer.async_wait([&](const boost::system::error_code& ec) {
2966714a25aSJames Feist                 if (ec == boost::asio::error::operation_aborted)
2976714a25aSJames Feist                 {
2986714a25aSJames Feist                     /* we were canceled*/
2996714a25aSJames Feist                     return;
3006714a25aSJames Feist                 }
3016714a25aSJames Feist                 else if (ec)
3026714a25aSJames Feist                 {
3036714a25aSJames Feist                     std::cerr << "timer error\n";
3046714a25aSJames Feist                     return;
3056714a25aSJames Feist                 }
3066714a25aSJames Feist                 createSensors(io, objectServer, sensors, systemBus,
3076714a25aSJames Feist                               sensorsChanged);
3086714a25aSJames Feist             });
3096714a25aSJames Feist         };
3106714a25aSJames Feist 
3119ced0a38SJae Hyun Yoo     for (const char* type : sensorTypes)
3126714a25aSJames Feist     {
3136714a25aSJames Feist         auto match = std::make_unique<sdbusplus::bus::match::match>(
3146714a25aSJames Feist             static_cast<sdbusplus::bus::bus&>(*systemBus),
3156714a25aSJames Feist             "type='signal',member='PropertiesChanged',path_namespace='" +
3169ced0a38SJae Hyun Yoo                 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
3176714a25aSJames Feist             eventHandler);
3186714a25aSJames Feist         matches.emplace_back(std::move(match));
3196714a25aSJames Feist     }
3206714a25aSJames Feist 
3216714a25aSJames Feist     io.run();
3226714a25aSJames Feist }
323