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