xref: /openbmc/dbus-sensors/src/fan/FanMain.cpp (revision e34e123bb6d6976cd5a27ad91202096a3542b9ac)
1d7be555eSGeorge Liu /*
2d7be555eSGeorge Liu // Copyright (c) 2017 Intel Corporation
3d7be555eSGeorge Liu //
4d7be555eSGeorge Liu // Licensed under the Apache License, Version 2.0 (the "License");
5d7be555eSGeorge Liu // you may not use this file except in compliance with the License.
6d7be555eSGeorge Liu // You may obtain a copy of the License at
7d7be555eSGeorge Liu //
8d7be555eSGeorge Liu //      http://www.apache.org/licenses/LICENSE-2.0
9d7be555eSGeorge Liu //
10d7be555eSGeorge Liu // Unless required by applicable law or agreed to in writing, software
11d7be555eSGeorge Liu // distributed under the License is distributed on an "AS IS" BASIS,
12d7be555eSGeorge Liu // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7be555eSGeorge Liu // See the License for the specific language governing permissions and
14d7be555eSGeorge Liu // limitations under the License.
15d7be555eSGeorge Liu */
16d7be555eSGeorge Liu 
17d7be555eSGeorge Liu #include "PresenceGpio.hpp"
18d7be555eSGeorge Liu #include "PwmSensor.hpp"
19d7be555eSGeorge Liu #include "TachSensor.hpp"
20d7be555eSGeorge Liu #include "Thresholds.hpp"
21d7be555eSGeorge Liu #include "Utils.hpp"
22d7be555eSGeorge Liu #include "VariantVisitors.hpp"
23d7be555eSGeorge Liu 
24d7be555eSGeorge Liu #include <boost/algorithm/string/replace.hpp>
25d7be555eSGeorge Liu #include <boost/asio/error.hpp>
26d7be555eSGeorge Liu #include <boost/asio/io_context.hpp>
27d7be555eSGeorge Liu #include <boost/asio/post.hpp>
28d7be555eSGeorge Liu #include <boost/asio/steady_timer.hpp>
29d7be555eSGeorge Liu #include <boost/container/flat_map.hpp>
30d7be555eSGeorge Liu #include <boost/container/flat_set.hpp>
31*e34e123bSGeorge Liu #include <phosphor-logging/lg2.hpp>
32d7be555eSGeorge Liu #include <sdbusplus/asio/connection.hpp>
33d7be555eSGeorge Liu #include <sdbusplus/asio/object_server.hpp>
34d7be555eSGeorge Liu #include <sdbusplus/bus.hpp>
35d7be555eSGeorge Liu #include <sdbusplus/bus/match.hpp>
36d7be555eSGeorge Liu #include <sdbusplus/message.hpp>
37d7be555eSGeorge Liu 
38d7be555eSGeorge Liu #include <array>
39d7be555eSGeorge Liu #include <chrono>
40d7be555eSGeorge Liu #include <cstddef>
41d7be555eSGeorge Liu #include <cstdint>
42d7be555eSGeorge Liu #include <filesystem>
43d7be555eSGeorge Liu #include <fstream>
44d7be555eSGeorge Liu #include <functional>
45d7be555eSGeorge Liu #include <ios>
46d7be555eSGeorge Liu #include <map>
47d7be555eSGeorge Liu #include <memory>
48d7be555eSGeorge Liu #include <optional>
49d7be555eSGeorge Liu #include <regex>
50d7be555eSGeorge Liu #include <string>
51d7be555eSGeorge Liu #include <system_error>
52d7be555eSGeorge Liu #include <utility>
53d7be555eSGeorge Liu #include <variant>
54d7be555eSGeorge Liu #include <vector>
55d7be555eSGeorge Liu 
56d7be555eSGeorge Liu // The following two structures need to be consistent
57d7be555eSGeorge Liu static auto sensorTypes{std::to_array<const char*>(
58d7be555eSGeorge Liu     {"AspeedFan", "I2CFan", "NuvotonFan", "HPEFan"})};
59d7be555eSGeorge Liu 
60d7be555eSGeorge Liu enum FanTypes
61d7be555eSGeorge Liu {
62d7be555eSGeorge Liu     aspeed = 0,
63d7be555eSGeorge Liu     i2c,
64d7be555eSGeorge Liu     nuvoton,
65d7be555eSGeorge Liu     hpe,
66d7be555eSGeorge Liu     max,
67d7be555eSGeorge Liu };
68d7be555eSGeorge Liu 
69d7be555eSGeorge Liu static_assert(std::tuple_size<decltype(sensorTypes)>::value == FanTypes::max,
70d7be555eSGeorge Liu               "sensorTypes element number is not equal to FanTypes number");
71d7be555eSGeorge Liu 
72d7be555eSGeorge Liu constexpr const char* redundancyConfiguration =
73d7be555eSGeorge Liu     "xyz.openbmc_project.Configuration.FanRedundancy";
74d7be555eSGeorge Liu static std::regex inputRegex(R"(fan(\d+)_input)");
75d7be555eSGeorge Liu 
76d7be555eSGeorge Liu // todo: power supply fan redundancy
77d7be555eSGeorge Liu std::optional<RedundancySensor> systemRedundancy;
78d7be555eSGeorge Liu 
79d7be555eSGeorge Liu static const std::map<std::string, FanTypes> compatibleFanTypes = {
80d7be555eSGeorge Liu     {"aspeed,ast2400-pwm-tacho", FanTypes::aspeed},
81d7be555eSGeorge Liu     {"aspeed,ast2500-pwm-tacho", FanTypes::aspeed},
82d7be555eSGeorge Liu     {"aspeed,ast2600-pwm-tach", FanTypes::aspeed},
83d7be555eSGeorge Liu     {"nuvoton,npcm750-pwm-fan", FanTypes::nuvoton},
84d7be555eSGeorge Liu     {"nuvoton,npcm845-pwm-fan", FanTypes::nuvoton},
85d7be555eSGeorge Liu     {"hpe,gxp-fan-ctrl", FanTypes::hpe}
86d7be555eSGeorge Liu     // add compatible string here for new fan type
87d7be555eSGeorge Liu };
88d7be555eSGeorge Liu 
getFanType(const std::filesystem::path & parentPath)892e466967SEd Tanous FanTypes getFanType(const std::filesystem::path& parentPath)
90d7be555eSGeorge Liu {
912e466967SEd Tanous     std::filesystem::path linkPath = parentPath / "of_node";
922e466967SEd Tanous     if (!std::filesystem::exists(linkPath))
93d7be555eSGeorge Liu     {
94d7be555eSGeorge Liu         return FanTypes::i2c;
95d7be555eSGeorge Liu     }
96d7be555eSGeorge Liu 
972e466967SEd Tanous     std::string canonical = std::filesystem::canonical(linkPath);
98d7be555eSGeorge Liu     std::string compatiblePath = canonical + "/compatible";
99d7be555eSGeorge Liu     std::ifstream compatibleStream(compatiblePath);
100d7be555eSGeorge Liu 
101d7be555eSGeorge Liu     if (!compatibleStream)
102d7be555eSGeorge Liu     {
103*e34e123bSGeorge Liu         lg2::error("Error opening '{PATH}'", "PATH", compatiblePath);
104d7be555eSGeorge Liu         return FanTypes::i2c;
105d7be555eSGeorge Liu     }
106d7be555eSGeorge Liu 
107d7be555eSGeorge Liu     std::string compatibleString;
108d7be555eSGeorge Liu     while (std::getline(compatibleStream, compatibleString))
109d7be555eSGeorge Liu     {
110d7be555eSGeorge Liu         compatibleString.pop_back(); // trim EOL before comparisons
111d7be555eSGeorge Liu 
112d7be555eSGeorge Liu         std::map<std::string, FanTypes>::const_iterator compatibleIterator =
113d7be555eSGeorge Liu             compatibleFanTypes.find(compatibleString);
114d7be555eSGeorge Liu 
115d7be555eSGeorge Liu         if (compatibleIterator != compatibleFanTypes.end())
116d7be555eSGeorge Liu         {
117d7be555eSGeorge Liu             return compatibleIterator->second;
118d7be555eSGeorge Liu         }
119d7be555eSGeorge Liu     }
120d7be555eSGeorge Liu 
121d7be555eSGeorge Liu     return FanTypes::i2c;
122d7be555eSGeorge Liu }
enablePwm(const std::filesystem::path & filePath)1232e466967SEd Tanous void enablePwm(const std::filesystem::path& filePath)
124d7be555eSGeorge Liu {
125d7be555eSGeorge Liu     std::fstream enableFile(filePath, std::ios::in | std::ios::out);
126d7be555eSGeorge Liu     if (!enableFile.good())
127d7be555eSGeorge Liu     {
128*e34e123bSGeorge Liu         lg2::error("Error read/write '{PATH}'", "PATH", filePath);
129d7be555eSGeorge Liu         return;
130d7be555eSGeorge Liu     }
131d7be555eSGeorge Liu 
132d7be555eSGeorge Liu     std::string regulateMode;
133d7be555eSGeorge Liu     std::getline(enableFile, regulateMode);
134d7be555eSGeorge Liu     if (regulateMode == "0")
135d7be555eSGeorge Liu     {
136d7be555eSGeorge Liu         enableFile << 1;
137d7be555eSGeorge Liu     }
138d7be555eSGeorge Liu }
findPwmfanPath(unsigned int configPwmfanIndex,std::filesystem::path & pwmPath)1392e466967SEd Tanous bool findPwmfanPath(unsigned int configPwmfanIndex,
1402e466967SEd Tanous                     std::filesystem::path& pwmPath)
141d7be555eSGeorge Liu {
142d7be555eSGeorge Liu     /* Search PWM since pwm-fan had separated
143d7be555eSGeorge Liu      * PWM from tach directory and 1 channel only*/
1442e466967SEd Tanous     std::vector<std::filesystem::path> pwmfanPaths;
145d7be555eSGeorge Liu     std::string pwnfanDevName("pwm-fan");
146d7be555eSGeorge Liu 
147d7be555eSGeorge Liu     pwnfanDevName += std::to_string(configPwmfanIndex);
148d7be555eSGeorge Liu 
1492e466967SEd Tanous     if (!findFiles(std::filesystem::path("/sys/class/hwmon"), R"(pwm\d+)",
1502e466967SEd Tanous                    pwmfanPaths))
151d7be555eSGeorge Liu     {
152*e34e123bSGeorge Liu         lg2::error("No PWMs are found!");
153d7be555eSGeorge Liu         return false;
154d7be555eSGeorge Liu     }
155d7be555eSGeorge Liu     for (const auto& path : pwmfanPaths)
156d7be555eSGeorge Liu     {
157d7be555eSGeorge Liu         std::error_code ec;
1582e466967SEd Tanous         std::filesystem::path link =
1592e466967SEd Tanous             std::filesystem::read_symlink(path.parent_path() / "device", ec);
160d7be555eSGeorge Liu 
161d7be555eSGeorge Liu         if (ec)
162d7be555eSGeorge Liu         {
163*e34e123bSGeorge Liu             lg2::error("read_symlink() failed: '{ERROR_MESSAGE}'",
164*e34e123bSGeorge Liu                        "ERROR_MESSAGE", ec.message());
165d7be555eSGeorge Liu             continue;
166d7be555eSGeorge Liu         }
167d7be555eSGeorge Liu 
168d7be555eSGeorge Liu         if (link.filename().string() == pwnfanDevName)
169d7be555eSGeorge Liu         {
170d7be555eSGeorge Liu             pwmPath = path;
171d7be555eSGeorge Liu             return true;
172d7be555eSGeorge Liu         }
173d7be555eSGeorge Liu     }
174d7be555eSGeorge Liu     return false;
175d7be555eSGeorge Liu }
findPwmPath(const std::filesystem::path & directory,unsigned int pwm,std::filesystem::path & pwmPath)1762e466967SEd Tanous bool findPwmPath(const std::filesystem::path& directory, unsigned int pwm,
1772e466967SEd Tanous                  std::filesystem::path& pwmPath)
178d7be555eSGeorge Liu {
179d7be555eSGeorge Liu     std::error_code ec;
180d7be555eSGeorge Liu 
181d7be555eSGeorge Liu     /* Assuming PWM file is appeared in the same directory as fanX_input */
182d7be555eSGeorge Liu     auto path = directory / ("pwm" + std::to_string(pwm + 1));
1832e466967SEd Tanous     bool exists = std::filesystem::exists(path, ec);
184d7be555eSGeorge Liu 
185d7be555eSGeorge Liu     if (ec || !exists)
186d7be555eSGeorge Liu     {
187d7be555eSGeorge Liu         /* PWM file not exist or error happened */
188d7be555eSGeorge Liu         if (ec)
189d7be555eSGeorge Liu         {
190*e34e123bSGeorge Liu             lg2::error("exists() failed: '{ERROR_MESSAGE}'", "ERROR_MESSAGE",
191*e34e123bSGeorge Liu                        ec.message());
192d7be555eSGeorge Liu         }
193d7be555eSGeorge Liu         /* try search form pwm-fanX directory */
194d7be555eSGeorge Liu         return findPwmfanPath(pwm, pwmPath);
195d7be555eSGeorge Liu     }
196d7be555eSGeorge Liu 
197d7be555eSGeorge Liu     pwmPath = path;
198d7be555eSGeorge Liu     return true;
199d7be555eSGeorge Liu }
200d7be555eSGeorge Liu 
201d7be555eSGeorge Liu // The argument to this function should be the fanN_input file that we want to
202d7be555eSGeorge Liu // enable. The function will locate the corresponding fanN_enable file if it
203d7be555eSGeorge Liu // exists. Note that some drivers don't provide this file if the sensors are
204d7be555eSGeorge Liu // always enabled.
enableFanInput(const std::filesystem::path & fanInputPath)2052e466967SEd Tanous void enableFanInput(const std::filesystem::path& fanInputPath)
206d7be555eSGeorge Liu {
207d7be555eSGeorge Liu     std::error_code ec;
208d7be555eSGeorge Liu     std::string path(fanInputPath.string());
209d7be555eSGeorge Liu     boost::replace_last(path, "input", "enable");
210d7be555eSGeorge Liu 
2112e466967SEd Tanous     bool exists = std::filesystem::exists(path, ec);
212d7be555eSGeorge Liu     if (ec || !exists)
213d7be555eSGeorge Liu     {
214d7be555eSGeorge Liu         return;
215d7be555eSGeorge Liu     }
216d7be555eSGeorge Liu 
217d7be555eSGeorge Liu     std::fstream enableFile(path, std::ios::out);
218d7be555eSGeorge Liu     if (!enableFile.good())
219d7be555eSGeorge Liu     {
220d7be555eSGeorge Liu         return;
221d7be555eSGeorge Liu     }
222d7be555eSGeorge Liu     enableFile << 1;
223d7be555eSGeorge Liu }
224d7be555eSGeorge Liu 
createRedundancySensor(const boost::container::flat_map<std::string,std::shared_ptr<TachSensor>> & sensors,const std::shared_ptr<sdbusplus::asio::connection> & conn,sdbusplus::asio::object_server & objectServer)225d7be555eSGeorge Liu void createRedundancySensor(
226d7be555eSGeorge Liu     const boost::container::flat_map<std::string, std::shared_ptr<TachSensor>>&
227d7be555eSGeorge Liu         sensors,
228d7be555eSGeorge Liu     const std::shared_ptr<sdbusplus::asio::connection>& conn,
229d7be555eSGeorge Liu     sdbusplus::asio::object_server& objectServer)
230d7be555eSGeorge Liu {
231d7be555eSGeorge Liu     conn->async_method_call(
232d7be555eSGeorge Liu         [&objectServer, &sensors](boost::system::error_code& ec,
233d7be555eSGeorge Liu                                   const ManagedObjectType& managedObj) {
234d7be555eSGeorge Liu             if (ec)
235d7be555eSGeorge Liu             {
236*e34e123bSGeorge Liu                 lg2::error("Error calling entity manager");
237d7be555eSGeorge Liu                 return;
238d7be555eSGeorge Liu             }
239d7be555eSGeorge Liu             for (const auto& [path, interfaces] : managedObj)
240d7be555eSGeorge Liu             {
241d7be555eSGeorge Liu                 for (const auto& [intf, cfg] : interfaces)
242d7be555eSGeorge Liu                 {
243d7be555eSGeorge Liu                     if (intf == redundancyConfiguration)
244d7be555eSGeorge Liu                     {
245d7be555eSGeorge Liu                         // currently only support one
246d7be555eSGeorge Liu                         auto findCount = cfg.find("AllowedFailures");
247d7be555eSGeorge Liu                         if (findCount == cfg.end())
248d7be555eSGeorge Liu                         {
249*e34e123bSGeorge Liu                             lg2::error("Malformed redundancy record");
250d7be555eSGeorge Liu                             return;
251d7be555eSGeorge Liu                         }
252d7be555eSGeorge Liu                         std::vector<std::string> sensorList;
253d7be555eSGeorge Liu 
254d7be555eSGeorge Liu                         for (const auto& [name, sensor] : sensors)
255d7be555eSGeorge Liu                         {
256d7be555eSGeorge Liu                             sensorList.push_back(
257d7be555eSGeorge Liu                                 "/xyz/openbmc_project/sensors/fan_tach/" +
258d7be555eSGeorge Liu                                 sensor->name);
259d7be555eSGeorge Liu                         }
260d7be555eSGeorge Liu                         systemRedundancy.reset();
261d7be555eSGeorge Liu                         systemRedundancy.emplace(RedundancySensor(
262d7be555eSGeorge Liu                             std::get<uint64_t>(findCount->second), sensorList,
263d7be555eSGeorge Liu                             objectServer, path));
264d7be555eSGeorge Liu 
265d7be555eSGeorge Liu                         return;
266d7be555eSGeorge Liu                     }
267d7be555eSGeorge Liu                 }
268d7be555eSGeorge Liu             }
269d7be555eSGeorge Liu         },
270d7be555eSGeorge Liu         "xyz.openbmc_project.EntityManager", "/xyz/openbmc_project/inventory",
271d7be555eSGeorge Liu         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
272d7be555eSGeorge Liu }
273d7be555eSGeorge Liu 
createSensors(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,boost::container::flat_map<std::string,std::shared_ptr<TachSensor>> & tachSensors,boost::container::flat_map<std::string,std::unique_ptr<PwmSensor>> & pwmSensors,boost::container::flat_map<std::string,std::weak_ptr<PresenceGpio>> & presenceGpios,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,const std::shared_ptr<boost::container::flat_set<std::string>> & sensorsChanged,size_t retries=0)274d7be555eSGeorge Liu void createSensors(
275d7be555eSGeorge Liu     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
276d7be555eSGeorge Liu     boost::container::flat_map<std::string, std::shared_ptr<TachSensor>>&
277d7be555eSGeorge Liu         tachSensors,
278d7be555eSGeorge Liu     boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>&
279d7be555eSGeorge Liu         pwmSensors,
280d7be555eSGeorge Liu     boost::container::flat_map<std::string, std::weak_ptr<PresenceGpio>>&
281d7be555eSGeorge Liu         presenceGpios,
282d7be555eSGeorge Liu     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
283d7be555eSGeorge Liu     const std::shared_ptr<boost::container::flat_set<std::string>>&
284d7be555eSGeorge Liu         sensorsChanged,
285d7be555eSGeorge Liu     size_t retries = 0)
286d7be555eSGeorge Liu {
287c45e18f9SChris Cain     auto getter = std::make_shared<
288c45e18f9SChris Cain         GetSensorConfiguration>(dbusConnection, [&io, &objectServer,
289c45e18f9SChris Cain                                                  &tachSensors, &pwmSensors,
290c45e18f9SChris Cain                                                  &presenceGpios,
291d7be555eSGeorge Liu                                                  &dbusConnection,
292c45e18f9SChris Cain                                                  sensorsChanged](
293c45e18f9SChris Cain                                                     const ManagedObjectType&
294c45e18f9SChris Cain                                                         sensorConfigurations) {
295d7be555eSGeorge Liu         bool firstScan = sensorsChanged == nullptr;
2962e466967SEd Tanous         std::vector<std::filesystem::path> paths;
2972e466967SEd Tanous         if (!findFiles(std::filesystem::path("/sys/class/hwmon"),
2982e466967SEd Tanous                        R"(fan\d+_input)", paths))
299d7be555eSGeorge Liu         {
300*e34e123bSGeorge Liu             lg2::error("No fan sensors in system");
301d7be555eSGeorge Liu             return;
302d7be555eSGeorge Liu         }
303d7be555eSGeorge Liu 
304d7be555eSGeorge Liu         // iterate through all found fan sensors, and try to match them with
305d7be555eSGeorge Liu         // configuration
306d7be555eSGeorge Liu         for (const auto& path : paths)
307d7be555eSGeorge Liu         {
308d7be555eSGeorge Liu             std::smatch match;
309d7be555eSGeorge Liu             std::string pathStr = path.string();
310d7be555eSGeorge Liu 
311d7be555eSGeorge Liu             std::regex_search(pathStr, match, inputRegex);
312d7be555eSGeorge Liu             std::string indexStr = *(match.begin() + 1);
313d7be555eSGeorge Liu 
3142e466967SEd Tanous             std::filesystem::path directory = path.parent_path();
315d7be555eSGeorge Liu             FanTypes fanType = getFanType(directory);
316d7be555eSGeorge Liu             std::string cfgIntf = configInterfaceName(sensorTypes[fanType]);
317d7be555eSGeorge Liu 
318d7be555eSGeorge Liu             // convert to 0 based
319d7be555eSGeorge Liu             size_t index = std::stoul(indexStr) - 1;
320d7be555eSGeorge Liu 
321d7be555eSGeorge Liu             const char* baseType = nullptr;
322d7be555eSGeorge Liu             const SensorData* sensorData = nullptr;
323d7be555eSGeorge Liu             const std::string* interfacePath = nullptr;
324d7be555eSGeorge Liu             const SensorBaseConfiguration* baseConfiguration = nullptr;
325d7be555eSGeorge Liu             for (const auto& [path, cfgData] : sensorConfigurations)
326d7be555eSGeorge Liu             {
327d7be555eSGeorge Liu                 // find the base of the configuration to see if indexes
328d7be555eSGeorge Liu                 // match
329d7be555eSGeorge Liu                 auto sensorBaseFind = cfgData.find(cfgIntf);
330d7be555eSGeorge Liu                 if (sensorBaseFind == cfgData.end())
331d7be555eSGeorge Liu                 {
332d7be555eSGeorge Liu                     continue;
333d7be555eSGeorge Liu                 }
334d7be555eSGeorge Liu 
335d7be555eSGeorge Liu                 baseConfiguration = &(*sensorBaseFind);
336d7be555eSGeorge Liu                 interfacePath = &path.str;
337d7be555eSGeorge Liu                 baseType = sensorTypes[fanType];
338d7be555eSGeorge Liu 
339d7be555eSGeorge Liu                 auto findIndex = baseConfiguration->second.find("Index");
340d7be555eSGeorge Liu                 if (findIndex == baseConfiguration->second.end())
341d7be555eSGeorge Liu                 {
342*e34e123bSGeorge Liu                     lg2::error("'{INTERFACE}' missing index", "INTERFACE",
343*e34e123bSGeorge Liu                                baseConfiguration->first);
344d7be555eSGeorge Liu                     continue;
345d7be555eSGeorge Liu                 }
346d7be555eSGeorge Liu                 unsigned int configIndex = std::visit(
347d7be555eSGeorge Liu                     VariantToUnsignedIntVisitor(), findIndex->second);
348d7be555eSGeorge Liu                 if (configIndex != index)
349d7be555eSGeorge Liu                 {
350d7be555eSGeorge Liu                     continue;
351d7be555eSGeorge Liu                 }
352d7be555eSGeorge Liu                 if (fanType == FanTypes::aspeed ||
353c45e18f9SChris Cain                     fanType == FanTypes::nuvoton || fanType == FanTypes::hpe)
354d7be555eSGeorge Liu                 {
355d7be555eSGeorge Liu                     // there will be only 1 aspeed or nuvoton or hpe sensor
356d7be555eSGeorge Liu                     // object in sysfs, we found the fan
357d7be555eSGeorge Liu                     sensorData = &cfgData;
358d7be555eSGeorge Liu                     break;
359d7be555eSGeorge Liu                 }
360d7be555eSGeorge Liu                 if (fanType == FanTypes::i2c)
361d7be555eSGeorge Liu                 {
362d7be555eSGeorge Liu                     std::string deviceName =
3632e466967SEd Tanous                         std::filesystem::read_symlink(directory / "device")
3642e466967SEd Tanous                             .filename();
365d7be555eSGeorge Liu 
366d7be555eSGeorge Liu                     size_t bus = 0;
367d7be555eSGeorge Liu                     size_t addr = 0;
368d7be555eSGeorge Liu                     if (!getDeviceBusAddr(deviceName, bus, addr))
369d7be555eSGeorge Liu                     {
370d7be555eSGeorge Liu                         continue;
371d7be555eSGeorge Liu                     }
372d7be555eSGeorge Liu 
373d7be555eSGeorge Liu                     auto findBus = baseConfiguration->second.find("Bus");
374d7be555eSGeorge Liu                     auto findAddress =
375d7be555eSGeorge Liu                         baseConfiguration->second.find("Address");
376d7be555eSGeorge Liu                     if (findBus == baseConfiguration->second.end() ||
377d7be555eSGeorge Liu                         findAddress == baseConfiguration->second.end())
378d7be555eSGeorge Liu                     {
379*e34e123bSGeorge Liu                         lg2::error("'{INTERFACE}' missing bus or address",
380*e34e123bSGeorge Liu                                    "INTERFACE", baseConfiguration->first);
381d7be555eSGeorge Liu                         continue;
382d7be555eSGeorge Liu                     }
383d7be555eSGeorge Liu                     unsigned int configBus = std::visit(
384d7be555eSGeorge Liu                         VariantToUnsignedIntVisitor(), findBus->second);
385d7be555eSGeorge Liu                     unsigned int configAddress = std::visit(
386d7be555eSGeorge Liu                         VariantToUnsignedIntVisitor(), findAddress->second);
387d7be555eSGeorge Liu 
388d7be555eSGeorge Liu                     if (configBus == bus && configAddress == addr)
389d7be555eSGeorge Liu                     {
390d7be555eSGeorge Liu                         sensorData = &cfgData;
391d7be555eSGeorge Liu                         break;
392d7be555eSGeorge Liu                     }
393d7be555eSGeorge Liu                 }
394d7be555eSGeorge Liu             }
395d7be555eSGeorge Liu             if (sensorData == nullptr)
396d7be555eSGeorge Liu             {
397*e34e123bSGeorge Liu                 lg2::error("failed to find match for '{PATH}'", "PATH",
398*e34e123bSGeorge Liu                            path.string());
399d7be555eSGeorge Liu                 continue;
400d7be555eSGeorge Liu             }
401d7be555eSGeorge Liu 
402d7be555eSGeorge Liu             auto findSensorName = baseConfiguration->second.find("Name");
403d7be555eSGeorge Liu 
404d7be555eSGeorge Liu             if (findSensorName == baseConfiguration->second.end())
405d7be555eSGeorge Liu             {
406*e34e123bSGeorge Liu                 lg2::error(
407*e34e123bSGeorge Liu                     "could not determine configuration name for '{PATH}'",
408*e34e123bSGeorge Liu                     "PATH", path.string());
409d7be555eSGeorge Liu                 continue;
410d7be555eSGeorge Liu             }
411d7be555eSGeorge Liu             std::string sensorName =
412d7be555eSGeorge Liu                 std::get<std::string>(findSensorName->second);
413d7be555eSGeorge Liu 
414d7be555eSGeorge Liu             // on rescans, only update sensors we were signaled by
415d7be555eSGeorge Liu             auto findSensor = tachSensors.find(sensorName);
416d7be555eSGeorge Liu             if (!firstScan && findSensor != tachSensors.end())
417d7be555eSGeorge Liu             {
418d7be555eSGeorge Liu                 bool found = false;
419d7be555eSGeorge Liu                 for (auto it = sensorsChanged->begin();
420d7be555eSGeorge Liu                      it != sensorsChanged->end(); it++)
421d7be555eSGeorge Liu                 {
422d7be555eSGeorge Liu                     if (it->ends_with(findSensor->second->name))
423d7be555eSGeorge Liu                     {
424d7be555eSGeorge Liu                         sensorsChanged->erase(it);
425d7be555eSGeorge Liu                         findSensor->second = nullptr;
426d7be555eSGeorge Liu                         found = true;
427d7be555eSGeorge Liu                         break;
428d7be555eSGeorge Liu                     }
429d7be555eSGeorge Liu                 }
430d7be555eSGeorge Liu                 if (!found)
431d7be555eSGeorge Liu                 {
432d7be555eSGeorge Liu                     continue;
433d7be555eSGeorge Liu                 }
434d7be555eSGeorge Liu             }
435d7be555eSGeorge Liu             std::vector<thresholds::Threshold> sensorThresholds;
436d7be555eSGeorge Liu             if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
437d7be555eSGeorge Liu             {
438*e34e123bSGeorge Liu                 lg2::error("error populating thresholds for '{NAME}'", "NAME",
439*e34e123bSGeorge Liu                            sensorName);
440d7be555eSGeorge Liu             }
441d7be555eSGeorge Liu 
442d7be555eSGeorge Liu             auto presenceConfig =
443d7be555eSGeorge Liu                 sensorData->find(cfgIntf + std::string(".Presence"));
444d7be555eSGeorge Liu 
445d7be555eSGeorge Liu             std::shared_ptr<PresenceGpio> presenceGpio(nullptr);
446d7be555eSGeorge Liu 
447d7be555eSGeorge Liu             // presence sensors are optional
448d7be555eSGeorge Liu             if (presenceConfig != sensorData->end())
449d7be555eSGeorge Liu             {
450d7be555eSGeorge Liu                 auto findPolarity = presenceConfig->second.find("Polarity");
451d7be555eSGeorge Liu                 auto findPinName = presenceConfig->second.find("PinName");
452d7be555eSGeorge Liu 
453d7be555eSGeorge Liu                 if (findPinName == presenceConfig->second.end() ||
454d7be555eSGeorge Liu                     findPolarity == presenceConfig->second.end())
455d7be555eSGeorge Liu                 {
456*e34e123bSGeorge Liu                     lg2::error("Malformed Presence Configuration");
457d7be555eSGeorge Liu                 }
458d7be555eSGeorge Liu                 else
459d7be555eSGeorge Liu                 {
460c45e18f9SChris Cain                     bool inverted =
461c45e18f9SChris Cain                         std::get<std::string>(findPolarity->second) == "Low";
462d7be555eSGeorge Liu                     const auto* pinName =
463d7be555eSGeorge Liu                         std::get_if<std::string>(&findPinName->second);
464d7be555eSGeorge Liu 
465d7be555eSGeorge Liu                     if (pinName != nullptr)
466d7be555eSGeorge Liu                     {
467c45e18f9SChris Cain                         auto findPresenceGpio = presenceGpios.find(*pinName);
468d7be555eSGeorge Liu                         if (findPresenceGpio != presenceGpios.end())
469d7be555eSGeorge Liu                         {
470d7be555eSGeorge Liu                             auto p = findPresenceGpio->second.lock();
471d7be555eSGeorge Liu                             if (p)
472d7be555eSGeorge Liu                             {
473d7be555eSGeorge Liu                                 presenceGpio = p;
474d7be555eSGeorge Liu                             }
475d7be555eSGeorge Liu                         }
476d7be555eSGeorge Liu                         if (!presenceGpio)
477d7be555eSGeorge Liu                         {
4784bbd02dcSXiaochao Ma                             auto findMonitorType =
4794bbd02dcSXiaochao Ma                                 presenceConfig->second.find("MonitorType");
4804bbd02dcSXiaochao Ma                             bool polling = false;
4814bbd02dcSXiaochao Ma                             if (findMonitorType != presenceConfig->second.end())
4824bbd02dcSXiaochao Ma                             {
4834bbd02dcSXiaochao Ma                                 auto mType = std::get<std::string>(
4844bbd02dcSXiaochao Ma                                     findMonitorType->second);
4854bbd02dcSXiaochao Ma                                 if (mType == "Polling")
4864bbd02dcSXiaochao Ma                                 {
4874bbd02dcSXiaochao Ma                                     polling = true;
4884bbd02dcSXiaochao Ma                                 }
4894bbd02dcSXiaochao Ma                                 else if (mType != "Event")
4904bbd02dcSXiaochao Ma                                 {
491*e34e123bSGeorge Liu                                     lg2::error(
492*e34e123bSGeorge Liu                                         "Unsupported GPIO MonitorType of '{TYPE}' for '{NAME}', "
493*e34e123bSGeorge Liu                                         "supported types: Polling, Event default",
494*e34e123bSGeorge Liu                                         "TYPE", mType, "NAME", sensorName);
4954bbd02dcSXiaochao Ma                                 }
4964bbd02dcSXiaochao Ma                             }
497c45e18f9SChris Cain                             try
498c45e18f9SChris Cain                             {
4994bbd02dcSXiaochao Ma                                 if (polling)
5004bbd02dcSXiaochao Ma                                 {
5014bbd02dcSXiaochao Ma                                     presenceGpio =
5024bbd02dcSXiaochao Ma                                         std::make_shared<PollingPresenceGpio>(
5034bbd02dcSXiaochao Ma                                             "Fan", sensorName, *pinName,
5044bbd02dcSXiaochao Ma                                             inverted, io);
5054bbd02dcSXiaochao Ma                                 }
5064bbd02dcSXiaochao Ma                                 else
5074bbd02dcSXiaochao Ma                                 {
508d7be555eSGeorge Liu                                     presenceGpio =
509d7be555eSGeorge Liu                                         std::make_shared<EventPresenceGpio>(
5104bbd02dcSXiaochao Ma                                             "Fan", sensorName, *pinName,
5114bbd02dcSXiaochao Ma                                             inverted, io);
5124bbd02dcSXiaochao Ma                                 }
513d7be555eSGeorge Liu                                 presenceGpios[*pinName] = presenceGpio;
514d7be555eSGeorge Liu                             }
515c45e18f9SChris Cain                             catch (const std::system_error& e)
516c45e18f9SChris Cain                             {
517*e34e123bSGeorge Liu                                 lg2::error(
518*e34e123bSGeorge Liu                                     "Failed to create GPIO monitor object for "
519*e34e123bSGeorge Liu                                     "'{PIN_NAME}' / '{SENSOR_NAME}': '{ERROR}'",
520*e34e123bSGeorge Liu                                     "PIN_NAME", *pinName, "SENSOR_NAME",
521*e34e123bSGeorge Liu                                     sensorName, "ERROR", e);
522c45e18f9SChris Cain                             }
523c45e18f9SChris Cain                         }
524d7be555eSGeorge Liu                     }
525d7be555eSGeorge Liu                     else
526d7be555eSGeorge Liu                     {
527*e34e123bSGeorge Liu                         lg2::error(
528*e34e123bSGeorge Liu                             "Malformed Presence pinName for sensor '{NAME}'",
529*e34e123bSGeorge Liu                             "NAME", sensorName);
530d7be555eSGeorge Liu                     }
531d7be555eSGeorge Liu                 }
532d7be555eSGeorge Liu             }
533d7be555eSGeorge Liu             std::optional<RedundancySensor>* redundancy = nullptr;
534d7be555eSGeorge Liu             if (fanType == FanTypes::aspeed)
535d7be555eSGeorge Liu             {
536d7be555eSGeorge Liu                 redundancy = &systemRedundancy;
537d7be555eSGeorge Liu             }
538d7be555eSGeorge Liu 
539c45e18f9SChris Cain             PowerState powerState = getPowerState(baseConfiguration->second);
540d7be555eSGeorge Liu 
541d7be555eSGeorge Liu             constexpr double defaultMaxReading = 25000;
542d7be555eSGeorge Liu             constexpr double defaultMinReading = 0;
543d7be555eSGeorge Liu             std::pair<double, double> limits =
544d7be555eSGeorge Liu                 std::make_pair(defaultMinReading, defaultMaxReading);
545d7be555eSGeorge Liu 
546d7be555eSGeorge Liu             auto connector =
547d7be555eSGeorge Liu                 sensorData->find(cfgIntf + std::string(".Connector"));
548d7be555eSGeorge Liu 
549d7be555eSGeorge Liu             std::optional<std::string> led;
550d7be555eSGeorge Liu             std::string pwmName;
5512e466967SEd Tanous             std::filesystem::path pwmPath;
552d7be555eSGeorge Liu 
553d7be555eSGeorge Liu             // The Mutable parameter is optional, defaulting to false
554d7be555eSGeorge Liu             bool isValueMutable = false;
555d7be555eSGeorge Liu             if (connector != sensorData->end())
556d7be555eSGeorge Liu             {
557d7be555eSGeorge Liu                 auto findPwm = connector->second.find("Pwm");
558d7be555eSGeorge Liu                 if (findPwm != connector->second.end())
559d7be555eSGeorge Liu                 {
560d7be555eSGeorge Liu                     size_t pwm = std::visit(VariantToUnsignedIntVisitor(),
561d7be555eSGeorge Liu                                             findPwm->second);
562d7be555eSGeorge Liu                     if (!findPwmPath(directory, pwm, pwmPath))
563d7be555eSGeorge Liu                     {
564*e34e123bSGeorge Liu                         lg2::error(
565*e34e123bSGeorge Liu                             "Connector for '{NAME}' no pwm channel found!",
566*e34e123bSGeorge Liu                             "NAME", sensorName);
567d7be555eSGeorge Liu                         continue;
568d7be555eSGeorge Liu                     }
569d7be555eSGeorge Liu 
5702e466967SEd Tanous                     std::filesystem::path pwmEnableFile =
571d7be555eSGeorge Liu                         "pwm" + std::to_string(pwm + 1) + "_enable";
5722e466967SEd Tanous                     std::filesystem::path enablePath =
5732e466967SEd Tanous                         pwmPath.parent_path() / pwmEnableFile;
574d7be555eSGeorge Liu                     enablePwm(enablePath);
575d7be555eSGeorge Liu 
576d7be555eSGeorge Liu                     /* use pwm name override if found in configuration else
577d7be555eSGeorge Liu                      * use default */
578d7be555eSGeorge Liu                     auto findOverride = connector->second.find("PwmName");
579d7be555eSGeorge Liu                     if (findOverride != connector->second.end())
580d7be555eSGeorge Liu                     {
581d7be555eSGeorge Liu                         pwmName = std::visit(VariantToStringVisitor(),
582d7be555eSGeorge Liu                                              findOverride->second);
583d7be555eSGeorge Liu                     }
584d7be555eSGeorge Liu                     else
585d7be555eSGeorge Liu                     {
586d7be555eSGeorge Liu                         pwmName = "Pwm_" + std::to_string(pwm + 1);
587d7be555eSGeorge Liu                     }
588d7be555eSGeorge Liu 
589d7be555eSGeorge Liu                     // Check PWM sensor mutability
590d7be555eSGeorge Liu                     auto findMutable = connector->second.find("Mutable");
591d7be555eSGeorge Liu                     if (findMutable != connector->second.end())
592d7be555eSGeorge Liu                     {
593d7be555eSGeorge Liu                         const auto* ptrMutable =
594d7be555eSGeorge Liu                             std::get_if<bool>(&(findMutable->second));
595d7be555eSGeorge Liu                         if (ptrMutable != nullptr)
596d7be555eSGeorge Liu                         {
597d7be555eSGeorge Liu                             isValueMutable = *ptrMutable;
598d7be555eSGeorge Liu                         }
599d7be555eSGeorge Liu                     }
600d7be555eSGeorge Liu                 }
601d7be555eSGeorge Liu                 else
602d7be555eSGeorge Liu                 {
603*e34e123bSGeorge Liu                     lg2::error("Connector for '{NAME}' missing pwm!", "NAME",
604*e34e123bSGeorge Liu                                sensorName);
605d7be555eSGeorge Liu                 }
606d7be555eSGeorge Liu 
607d7be555eSGeorge Liu                 auto findLED = connector->second.find("LED");
608d7be555eSGeorge Liu                 if (findLED != connector->second.end())
609d7be555eSGeorge Liu                 {
610d7be555eSGeorge Liu                     const auto* ledName =
611d7be555eSGeorge Liu                         std::get_if<std::string>(&(findLED->second));
612d7be555eSGeorge Liu                     if (ledName == nullptr)
613d7be555eSGeorge Liu                     {
614*e34e123bSGeorge Liu                         lg2::error("Wrong format for LED of '{NAME}'", "NAME",
615*e34e123bSGeorge Liu                                    sensorName);
616d7be555eSGeorge Liu                     }
617d7be555eSGeorge Liu                     else
618d7be555eSGeorge Liu                     {
619d7be555eSGeorge Liu                         led = *ledName;
620d7be555eSGeorge Liu                     }
621d7be555eSGeorge Liu                 }
622d7be555eSGeorge Liu             }
623d7be555eSGeorge Liu 
624d7be555eSGeorge Liu             findLimits(limits, baseConfiguration);
625d7be555eSGeorge Liu 
626d7be555eSGeorge Liu             enableFanInput(path);
627d7be555eSGeorge Liu 
628d7be555eSGeorge Liu             auto& tachSensor = tachSensors[sensorName];
629d7be555eSGeorge Liu             tachSensor = nullptr;
630d7be555eSGeorge Liu             tachSensor = std::make_shared<TachSensor>(
631d7be555eSGeorge Liu                 path.string(), baseType, objectServer, dbusConnection,
632d7be555eSGeorge Liu                 presenceGpio, redundancy, io, sensorName,
633c45e18f9SChris Cain                 std::move(sensorThresholds), *interfacePath, limits, powerState,
634c45e18f9SChris Cain                 led);
635d7be555eSGeorge Liu             tachSensor->setupRead();
636d7be555eSGeorge Liu 
6372e466967SEd Tanous             if (!pwmPath.empty() && std::filesystem::exists(pwmPath) &&
638d7be555eSGeorge Liu                 (pwmSensors.count(pwmPath) == 0U))
639d7be555eSGeorge Liu             {
640d7be555eSGeorge Liu                 pwmSensors[pwmPath] = std::make_unique<PwmSensor>(
641d7be555eSGeorge Liu                     pwmName, pwmPath, dbusConnection, objectServer,
642d7be555eSGeorge Liu                     *interfacePath, "Fan", isValueMutable);
643d7be555eSGeorge Liu             }
644d7be555eSGeorge Liu         }
645d7be555eSGeorge Liu 
646d7be555eSGeorge Liu         createRedundancySensor(tachSensors, dbusConnection, objectServer);
647d7be555eSGeorge Liu     });
648d7be555eSGeorge Liu     getter->getConfiguration(
649d7be555eSGeorge Liu         std::vector<std::string>{sensorTypes.begin(), sensorTypes.end()},
650d7be555eSGeorge Liu         retries);
651d7be555eSGeorge Liu }
652d7be555eSGeorge Liu 
main()653d7be555eSGeorge Liu int main()
654d7be555eSGeorge Liu {
655d7be555eSGeorge Liu     boost::asio::io_context io;
656d7be555eSGeorge Liu     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
657d7be555eSGeorge Liu     sdbusplus::asio::object_server objectServer(systemBus, true);
658d7be555eSGeorge Liu 
659d7be555eSGeorge Liu     objectServer.add_manager("/xyz/openbmc_project/sensors");
660d7be555eSGeorge Liu     objectServer.add_manager("/xyz/openbmc_project/control");
661d7be555eSGeorge Liu     objectServer.add_manager("/xyz/openbmc_project/inventory");
662d7be555eSGeorge Liu     systemBus->request_name("xyz.openbmc_project.FanSensor");
663d7be555eSGeorge Liu     boost::container::flat_map<std::string, std::shared_ptr<TachSensor>>
664d7be555eSGeorge Liu         tachSensors;
665d7be555eSGeorge Liu     boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
666d7be555eSGeorge Liu         pwmSensors;
667d7be555eSGeorge Liu     boost::container::flat_map<std::string, std::weak_ptr<PresenceGpio>>
668d7be555eSGeorge Liu         presenceGpios;
669d7be555eSGeorge Liu     auto sensorsChanged =
670d7be555eSGeorge Liu         std::make_shared<boost::container::flat_set<std::string>>();
671d7be555eSGeorge Liu 
672d7be555eSGeorge Liu     boost::asio::post(io, [&]() {
673d7be555eSGeorge Liu         createSensors(io, objectServer, tachSensors, pwmSensors, presenceGpios,
674d7be555eSGeorge Liu                       systemBus, nullptr);
675d7be555eSGeorge Liu     });
676d7be555eSGeorge Liu 
677d7be555eSGeorge Liu     boost::asio::steady_timer filterTimer(io);
678d7be555eSGeorge Liu     std::function<void(sdbusplus::message_t&)> eventHandler =
679d7be555eSGeorge Liu         [&](sdbusplus::message_t& message) {
680d7be555eSGeorge Liu             if (message.is_method_error())
681d7be555eSGeorge Liu             {
682*e34e123bSGeorge Liu                 lg2::error("callback method error");
683d7be555eSGeorge Liu                 return;
684d7be555eSGeorge Liu             }
685d7be555eSGeorge Liu             sensorsChanged->insert(message.get_path());
686d7be555eSGeorge Liu             // this implicitly cancels the timer
687d7be555eSGeorge Liu             filterTimer.expires_after(std::chrono::seconds(1));
688d7be555eSGeorge Liu 
689d7be555eSGeorge Liu             filterTimer.async_wait([&](const boost::system::error_code& ec) {
690d7be555eSGeorge Liu                 if (ec == boost::asio::error::operation_aborted)
691d7be555eSGeorge Liu                 {
692d7be555eSGeorge Liu                     /* we were canceled*/
693d7be555eSGeorge Liu                     return;
694d7be555eSGeorge Liu                 }
695d7be555eSGeorge Liu                 if (ec)
696d7be555eSGeorge Liu                 {
697*e34e123bSGeorge Liu                     lg2::error("timer error");
698d7be555eSGeorge Liu                     return;
699d7be555eSGeorge Liu                 }
700d7be555eSGeorge Liu                 createSensors(io, objectServer, tachSensors, pwmSensors,
701d7be555eSGeorge Liu                               presenceGpios, systemBus, sensorsChanged, 5);
702d7be555eSGeorge Liu             });
703d7be555eSGeorge Liu         };
704d7be555eSGeorge Liu 
705d7be555eSGeorge Liu     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
706d7be555eSGeorge Liu         setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler);
707d7be555eSGeorge Liu 
708d7be555eSGeorge Liu     // redundancy sensor
709d7be555eSGeorge Liu     std::function<void(sdbusplus::message_t&)> redundancyHandler =
710d7be555eSGeorge Liu         [&tachSensors, &systemBus, &objectServer](sdbusplus::message_t&) {
711d7be555eSGeorge Liu             createRedundancySensor(tachSensors, systemBus, objectServer);
712d7be555eSGeorge Liu         };
713d7be555eSGeorge Liu     auto match = std::make_unique<sdbusplus::bus::match_t>(
714d7be555eSGeorge Liu         static_cast<sdbusplus::bus_t&>(*systemBus),
715d7be555eSGeorge Liu         "type='signal',member='PropertiesChanged',path_namespace='" +
716d7be555eSGeorge Liu             std::string(inventoryPath) + "',arg0namespace='" +
717d7be555eSGeorge Liu             redundancyConfiguration + "'",
718d7be555eSGeorge Liu         std::move(redundancyHandler));
719d7be555eSGeorge Liu     matches.emplace_back(std::move(match));
720d7be555eSGeorge Liu 
721d7be555eSGeorge Liu     setupManufacturingModeMatch(*systemBus);
722d7be555eSGeorge Liu     io.run();
723d7be555eSGeorge Liu     return 0;
724d7be555eSGeorge Liu }
725