xref: /openbmc/dbus-sensors/src/FanMain.cpp (revision 40c4d685)
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 
17e73bd0a1SAndrew Jeffery #include "PwmSensor.hpp"
18e73bd0a1SAndrew Jeffery #include "TachSensor.hpp"
19eacbfdd1SEd Tanous #include "Thresholds.hpp"
20e73bd0a1SAndrew Jeffery #include "Utils.hpp"
21e73bd0a1SAndrew Jeffery #include "VariantVisitors.hpp"
22e73bd0a1SAndrew Jeffery 
236714a25aSJames Feist #include <boost/algorithm/string/replace.hpp>
24eacbfdd1SEd Tanous #include <boost/asio/error.hpp>
25eacbfdd1SEd Tanous #include <boost/asio/io_context.hpp>
26eacbfdd1SEd Tanous #include <boost/asio/post.hpp>
27eacbfdd1SEd Tanous #include <boost/asio/steady_timer.hpp>
2896e97db7SPatrick Venture #include <boost/container/flat_map.hpp>
296714a25aSJames Feist #include <boost/container/flat_set.hpp>
3038fb5983SJames Feist #include <sdbusplus/asio/connection.hpp>
3138fb5983SJames Feist #include <sdbusplus/asio/object_server.hpp>
32eacbfdd1SEd Tanous #include <sdbusplus/bus.hpp>
3338fb5983SJames Feist #include <sdbusplus/bus/match.hpp>
34eacbfdd1SEd Tanous #include <sdbusplus/message.hpp>
3538fb5983SJames Feist 
3638fb5983SJames Feist #include <array>
37eacbfdd1SEd Tanous #include <chrono>
38eacbfdd1SEd Tanous #include <cstddef>
39eacbfdd1SEd Tanous #include <cstdint>
4024f02f24SJames Feist #include <filesystem>
416714a25aSJames Feist #include <fstream>
4296e97db7SPatrick Venture #include <functional>
43eacbfdd1SEd Tanous #include <ios>
44eacbfdd1SEd Tanous #include <iostream>
45eacbfdd1SEd Tanous #include <map>
4696e97db7SPatrick Venture #include <memory>
4796e97db7SPatrick Venture #include <optional>
486714a25aSJames Feist #include <regex>
4996e97db7SPatrick Venture #include <string>
50eacbfdd1SEd Tanous #include <system_error>
5196e97db7SPatrick Venture #include <utility>
5296e97db7SPatrick Venture #include <variant>
5396e97db7SPatrick Venture #include <vector>
546714a25aSJames Feist 
55cf3bce6eSJames Feist namespace fs = std::filesystem;
563eb82629SJames Feist 
57a3e8f2a3SYong Zhao // The following two structures need to be consistent
58c361e222SChris Sides static auto sensorTypes{std::to_array<const char*>(
59c361e222SChris Sides     {"AspeedFan", "I2CFan", "NuvotonFan", "HPEFan"})};
60a3e8f2a3SYong Zhao 
61a3e8f2a3SYong Zhao enum FanTypes
62a3e8f2a3SYong Zhao {
63a3e8f2a3SYong Zhao     aspeed = 0,
64a3e8f2a3SYong Zhao     i2c,
65a3e8f2a3SYong Zhao     nuvoton,
66c361e222SChris Sides     hpe,
67a3e8f2a3SYong Zhao     max,
68a3e8f2a3SYong Zhao };
69a3e8f2a3SYong Zhao 
70a3e8f2a3SYong Zhao static_assert(std::tuple_size<decltype(sensorTypes)>::value == FanTypes::max,
71a3e8f2a3SYong Zhao               "sensorTypes element number is not equal to FanTypes number");
72a3e8f2a3SYong Zhao 
73dc6c55f3SJames Feist constexpr const char* redundancyConfiguration =
74dc6c55f3SJames Feist     "xyz.openbmc_project.Configuration.FanRedundancy";
759ced0a38SJae Hyun Yoo static std::regex inputRegex(R"(fan(\d+)_input)");
766714a25aSJames Feist 
77dc6c55f3SJames Feist // todo: power supply fan redundancy
787b18b1e0SJames Feist std::optional<RedundancySensor> systemRedundancy;
7995b079b7SJames Feist 
803d5260dcSChris Sides static const std::map<std::string, FanTypes> compatibleFanTypes = {
813d5260dcSChris Sides     {"aspeed,ast2400-pwm-tacho", FanTypes::aspeed},
823d5260dcSChris Sides     {"aspeed,ast2500-pwm-tacho", FanTypes::aspeed},
83*40c4d685SPotin Lai     {"aspeed,ast2600-pwm-tach", FanTypes::aspeed},
84c361e222SChris Sides     {"nuvoton,npcm750-pwm-fan", FanTypes::nuvoton},
85d37e1db5SBrian Ma     {"nuvoton,npcm845-pwm-fan", FanTypes::nuvoton},
86c361e222SChris Sides     {"hpe,gxp-fan-ctrl", FanTypes::hpe}
873d5260dcSChris Sides     // add compatible string here for new fan type
883d5260dcSChris Sides };
893d5260dcSChris Sides 
getFanType(const fs::path & parentPath)9095b079b7SJames Feist FanTypes getFanType(const fs::path& parentPath)
9195b079b7SJames Feist {
929a472e8eSChris Sides     fs::path linkPath = parentPath / "of_node";
9339963226SZhikui Ren     if (!fs::exists(linkPath))
9439963226SZhikui Ren     {
9539963226SZhikui Ren         return FanTypes::i2c;
9639963226SZhikui Ren     }
979a472e8eSChris Sides 
9839963226SZhikui Ren     std::string canonical = fs::canonical(linkPath);
999a472e8eSChris Sides     std::string compatiblePath = canonical + "/compatible";
1009a472e8eSChris Sides     std::ifstream compatibleStream(compatiblePath);
1019a472e8eSChris Sides 
1023d5260dcSChris Sides     if (!compatibleStream)
1039a472e8eSChris Sides     {
1049a472e8eSChris Sides         std::cerr << "Error opening " << compatiblePath << "\n";
1053d5260dcSChris Sides         return FanTypes::i2c;
1069a472e8eSChris Sides     }
1079a472e8eSChris Sides 
1089a472e8eSChris Sides     std::string compatibleString;
1093d5260dcSChris Sides     while (std::getline(compatibleStream, compatibleString))
1109a472e8eSChris Sides     {
1119a472e8eSChris Sides         compatibleString.pop_back(); // trim EOL before comparisons
1129a472e8eSChris Sides 
1133d5260dcSChris Sides         std::map<std::string, FanTypes>::const_iterator compatibleIterator =
1143d5260dcSChris Sides             compatibleFanTypes.find(compatibleString);
1153d5260dcSChris Sides 
1163d5260dcSChris Sides         if (compatibleIterator != compatibleFanTypes.end())
11795b079b7SJames Feist         {
1183d5260dcSChris Sides             return compatibleIterator->second;
1198843b627SPeter Lundgren         }
1209a472e8eSChris Sides     }
1219a472e8eSChris Sides 
12295b079b7SJames Feist     return FanTypes::i2c;
12395b079b7SJames Feist }
enablePwm(const fs::path & filePath)124abf91de1SJeff Lin void enablePwm(const fs::path& filePath)
125abf91de1SJeff Lin {
126abf91de1SJeff Lin     std::fstream enableFile(filePath, std::ios::in | std::ios::out);
127abf91de1SJeff Lin     if (!enableFile.good())
128abf91de1SJeff Lin     {
129abf91de1SJeff Lin         std::cerr << "Error read/write " << filePath << "\n";
130abf91de1SJeff Lin         return;
131abf91de1SJeff Lin     }
132dc6c55f3SJames Feist 
133abf91de1SJeff Lin     std::string regulateMode;
134abf91de1SJeff Lin     std::getline(enableFile, regulateMode);
135abf91de1SJeff Lin     if (regulateMode == "0")
136abf91de1SJeff Lin     {
137abf91de1SJeff Lin         enableFile << 1;
138abf91de1SJeff Lin     }
139abf91de1SJeff Lin }
findPwmfanPath(unsigned int configPwmfanIndex,fs::path & pwmPath)140ddf25d17SHoward Chiu bool findPwmfanPath(unsigned int configPwmfanIndex, fs::path& pwmPath)
141ddf25d17SHoward Chiu {
142ddf25d17SHoward Chiu     /* Search PWM since pwm-fan had separated
143ddf25d17SHoward Chiu      * PWM from tach directory and 1 channel only*/
144ddf25d17SHoward Chiu     std::vector<fs::path> pwmfanPaths;
145ddf25d17SHoward Chiu     std::string pwnfanDevName("pwm-fan");
146ddf25d17SHoward Chiu 
147ddf25d17SHoward Chiu     pwnfanDevName += std::to_string(configPwmfanIndex);
148ddf25d17SHoward Chiu 
149ddf25d17SHoward Chiu     if (!findFiles(fs::path("/sys/class/hwmon"), R"(pwm\d+)", pwmfanPaths))
150ddf25d17SHoward Chiu     {
151ddf25d17SHoward Chiu         std::cerr << "No PWMs are found!\n";
152ddf25d17SHoward Chiu         return false;
153ddf25d17SHoward Chiu     }
154ddf25d17SHoward Chiu     for (const auto& path : pwmfanPaths)
155ddf25d17SHoward Chiu     {
156ddf25d17SHoward Chiu         std::error_code ec;
157ddf25d17SHoward Chiu         fs::path link = fs::read_symlink(path.parent_path() / "device", ec);
158ddf25d17SHoward Chiu 
159ddf25d17SHoward Chiu         if (ec)
160ddf25d17SHoward Chiu         {
161ddf25d17SHoward Chiu             std::cerr << "read_symlink() failed: " << ec.message() << " ("
162ddf25d17SHoward Chiu                       << ec.value() << ")\n";
163ddf25d17SHoward Chiu             continue;
164ddf25d17SHoward Chiu         }
165ddf25d17SHoward Chiu 
166ddf25d17SHoward Chiu         if (link.filename().string() == pwnfanDevName)
167ddf25d17SHoward Chiu         {
168ddf25d17SHoward Chiu             pwmPath = path;
169ddf25d17SHoward Chiu             return true;
170ddf25d17SHoward Chiu         }
171ddf25d17SHoward Chiu     }
172ddf25d17SHoward Chiu     return false;
173ddf25d17SHoward Chiu }
findPwmPath(const fs::path & directory,unsigned int pwm,fs::path & pwmPath)174ddf25d17SHoward Chiu bool findPwmPath(const fs::path& directory, unsigned int pwm, fs::path& pwmPath)
175ddf25d17SHoward Chiu {
176ddf25d17SHoward Chiu     std::error_code ec;
177ddf25d17SHoward Chiu 
178ddf25d17SHoward Chiu     /* Assuming PWM file is appeared in the same directory as fanX_input */
179ddf25d17SHoward Chiu     auto path = directory / ("pwm" + std::to_string(pwm + 1));
180ddf25d17SHoward Chiu     bool exists = fs::exists(path, ec);
181ddf25d17SHoward Chiu 
182ddf25d17SHoward Chiu     if (ec || !exists)
183ddf25d17SHoward Chiu     {
184ddf25d17SHoward Chiu         /* PWM file not exist or error happened */
185ddf25d17SHoward Chiu         if (ec)
186ddf25d17SHoward Chiu         {
187ddf25d17SHoward Chiu             std::cerr << "exists() failed: " << ec.message() << " ("
188ddf25d17SHoward Chiu                       << ec.value() << ")\n";
189ddf25d17SHoward Chiu         }
190ddf25d17SHoward Chiu         /* try search form pwm-fanX directory */
191ddf25d17SHoward Chiu         return findPwmfanPath(pwm, pwmPath);
192ddf25d17SHoward Chiu     }
193ddf25d17SHoward Chiu 
194ddf25d17SHoward Chiu     pwmPath = path;
195ddf25d17SHoward Chiu     return true;
196ddf25d17SHoward Chiu }
1979c47bd7eSJustin Ledford 
1989c47bd7eSJustin Ledford // The argument to this function should be the fanN_input file that we want to
1999c47bd7eSJustin Ledford // enable. The function will locate the corresponding fanN_enable file if it
2009c47bd7eSJustin Ledford // exists. Note that some drivers don't provide this file if the sensors are
2019c47bd7eSJustin Ledford // always enabled.
enableFanInput(const fs::path & fanInputPath)2029c47bd7eSJustin Ledford void enableFanInput(const fs::path& fanInputPath)
2039c47bd7eSJustin Ledford {
2049c47bd7eSJustin Ledford     std::error_code ec;
2059c47bd7eSJustin Ledford     std::string path(fanInputPath.string());
2069c47bd7eSJustin Ledford     boost::replace_last(path, "input", "enable");
2079c47bd7eSJustin Ledford 
2089c47bd7eSJustin Ledford     bool exists = fs::exists(path, ec);
2099c47bd7eSJustin Ledford     if (ec || !exists)
2109c47bd7eSJustin Ledford     {
2119c47bd7eSJustin Ledford         return;
2129c47bd7eSJustin Ledford     }
2139c47bd7eSJustin Ledford 
2149c47bd7eSJustin Ledford     std::fstream enableFile(path, std::ios::out);
2159c47bd7eSJustin Ledford     if (!enableFile.good())
2169c47bd7eSJustin Ledford     {
2179c47bd7eSJustin Ledford         return;
2189c47bd7eSJustin Ledford     }
2199c47bd7eSJustin Ledford     enableFile << 1;
2209c47bd7eSJustin Ledford }
2219c47bd7eSJustin Ledford 
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)222d540741eSKuiying Wang void createRedundancySensor(
2235170fe63SJosh Lehan     const boost::container::flat_map<std::string, std::shared_ptr<TachSensor>>&
224d540741eSKuiying Wang         sensors,
2258a57ec09SEd Tanous     const std::shared_ptr<sdbusplus::asio::connection>& conn,
226d540741eSKuiying Wang     sdbusplus::asio::object_server& objectServer)
227d540741eSKuiying Wang {
228d540741eSKuiying Wang     conn->async_method_call(
229d540741eSKuiying Wang         [&objectServer, &sensors](boost::system::error_code& ec,
2308a57ec09SEd Tanous                                   const ManagedObjectType& managedObj) {
231d540741eSKuiying Wang         if (ec)
232d540741eSKuiying Wang         {
233d540741eSKuiying Wang             std::cerr << "Error calling entity manager \n";
234d540741eSKuiying Wang             return;
235d540741eSKuiying Wang         }
23677636ecbSZev Weiss         for (const auto& [path, interfaces] : managedObj)
237d540741eSKuiying Wang         {
23877636ecbSZev Weiss             for (const auto& [intf, cfg] : interfaces)
239d540741eSKuiying Wang             {
24077636ecbSZev Weiss                 if (intf == redundancyConfiguration)
241d540741eSKuiying Wang                 {
242d540741eSKuiying Wang                     // currently only support one
24377636ecbSZev Weiss                     auto findCount = cfg.find("AllowedFailures");
24477636ecbSZev Weiss                     if (findCount == cfg.end())
245d540741eSKuiying Wang                     {
246d540741eSKuiying Wang                         std::cerr << "Malformed redundancy record \n";
247d540741eSKuiying Wang                         return;
248d540741eSKuiying Wang                     }
249d540741eSKuiying Wang                     std::vector<std::string> sensorList;
250d540741eSKuiying Wang 
25177636ecbSZev Weiss                     for (const auto& [name, sensor] : sensors)
252d540741eSKuiying Wang                     {
253d540741eSKuiying Wang                         sensorList.push_back(
254d540741eSKuiying Wang                             "/xyz/openbmc_project/sensors/fan_tach/" +
25577636ecbSZev Weiss                             sensor->name);
256d540741eSKuiying Wang                     }
257d540741eSKuiying Wang                     systemRedundancy.reset();
25877636ecbSZev Weiss                     systemRedundancy.emplace(
25977636ecbSZev Weiss                         RedundancySensor(std::get<uint64_t>(findCount->second),
26077636ecbSZev Weiss                                          sensorList, objectServer, path));
261d540741eSKuiying Wang 
262d540741eSKuiying Wang                     return;
263d540741eSKuiying Wang                 }
264d540741eSKuiying Wang             }
265d540741eSKuiying Wang         }
266d540741eSKuiying Wang     },
2673e620af1SNan Zhou         "xyz.openbmc_project.EntityManager", "/xyz/openbmc_project/inventory",
268d540741eSKuiying Wang         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
269d540741eSKuiying Wang }
270d540741eSKuiying Wang 
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,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,const std::shared_ptr<boost::container::flat_set<std::string>> & sensorsChanged,size_t retries=0)2716714a25aSJames Feist void createSensors(
2721f978631SEd Tanous     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
2735170fe63SJosh Lehan     boost::container::flat_map<std::string, std::shared_ptr<TachSensor>>&
2746714a25aSJames Feist         tachSensors,
2756714a25aSJames Feist     boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>&
2766714a25aSJames Feist         pwmSensors,
2776714a25aSJames Feist     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
2785591cf08SJames Feist     const std::shared_ptr<boost::container::flat_set<std::string>>&
279f27a55c7SJames Feist         sensorsChanged,
280f27a55c7SJames Feist     size_t retries = 0)
2816714a25aSJames Feist {
282de5e9705SJames Feist     auto getter = std::make_shared<GetSensorConfiguration>(
283de5e9705SJames Feist         dbusConnection,
2848a17c303SEd Tanous         [&io, &objectServer, &tachSensors, &pwmSensors, &dbusConnection,
2858a17c303SEd Tanous          sensorsChanged](const ManagedObjectType& sensorConfigurations) {
2866714a25aSJames Feist         bool firstScan = sensorsChanged == nullptr;
2876714a25aSJames Feist         std::vector<fs::path> paths;
288bb67932aSEd Tanous         if (!findFiles(fs::path("/sys/class/hwmon"), R"(fan\d+_input)", paths))
2896714a25aSJames Feist         {
29077b3add2SYong Zhao             std::cerr << "No fan sensors in system\n";
2916714a25aSJames Feist             return;
2926714a25aSJames Feist         }
2936714a25aSJames Feist 
2946714a25aSJames Feist         // iterate through all found fan sensors, and try to match them with
2956714a25aSJames Feist         // configuration
29695b079b7SJames Feist         for (const auto& path : paths)
2976714a25aSJames Feist         {
2986714a25aSJames Feist             std::smatch match;
2996714a25aSJames Feist             std::string pathStr = path.string();
3006714a25aSJames Feist 
3019ced0a38SJae Hyun Yoo             std::regex_search(pathStr, match, inputRegex);
3026714a25aSJames Feist             std::string indexStr = *(match.begin() + 1);
3036714a25aSJames Feist 
30477b3add2SYong Zhao             fs::path directory = path.parent_path();
30595b079b7SJames Feist             FanTypes fanType = getFanType(directory);
306054aad8fSZev Weiss             std::string cfgIntf = configInterfaceName(sensorTypes[fanType]);
30777b3add2SYong Zhao 
3086714a25aSJames Feist             // convert to 0 based
3096714a25aSJames Feist             size_t index = std::stoul(indexStr) - 1;
3106714a25aSJames Feist 
311a771f6a7SEd Tanous             const char* baseType = nullptr;
3126714a25aSJames Feist             const SensorData* sensorData = nullptr;
3136714a25aSJames Feist             const std::string* interfacePath = nullptr;
31487d713abSJames Feist             const SensorBaseConfiguration* baseConfiguration = nullptr;
31577636ecbSZev Weiss             for (const auto& [path, cfgData] : sensorConfigurations)
3166714a25aSJames Feist             {
317de5e9705SJames Feist                 // find the base of the configuration to see if indexes
318de5e9705SJames Feist                 // match
319054aad8fSZev Weiss                 auto sensorBaseFind = cfgData.find(cfgIntf);
32077636ecbSZev Weiss                 if (sensorBaseFind == cfgData.end())
3216714a25aSJames Feist                 {
3226714a25aSJames Feist                     continue;
3236714a25aSJames Feist                 }
324347dd4e7SZhikui Ren 
325a3e8f2a3SYong Zhao                 baseConfiguration = &(*sensorBaseFind);
32677636ecbSZev Weiss                 interfacePath = &path.str;
327a3e8f2a3SYong Zhao                 baseType = sensorTypes[fanType];
328a3e8f2a3SYong Zhao 
3296714a25aSJames Feist                 auto findIndex = baseConfiguration->second.find("Index");
3306714a25aSJames Feist                 if (findIndex == baseConfiguration->second.end())
3316714a25aSJames Feist                 {
332bb67932aSEd Tanous                     std::cerr << baseConfiguration->first << " missing index\n";
3336714a25aSJames Feist                     continue;
3346714a25aSJames Feist                 }
335de5e9705SJames Feist                 unsigned int configIndex = std::visit(
336de5e9705SJames Feist                     VariantToUnsignedIntVisitor(), findIndex->second);
3376714a25aSJames Feist                 if (configIndex != index)
3386714a25aSJames Feist                 {
3396714a25aSJames Feist                     continue;
3406714a25aSJames Feist                 }
341c361e222SChris Sides                 if (fanType == FanTypes::aspeed ||
342c361e222SChris Sides                     fanType == FanTypes::nuvoton || fanType == FanTypes::hpe)
3436714a25aSJames Feist                 {
344c361e222SChris Sides                     // there will be only 1 aspeed or nuvoton or hpe sensor
3453d5260dcSChris Sides                     // object in sysfs, we found the fan
34677636ecbSZev Weiss                     sensorData = &cfgData;
34795b079b7SJames Feist                     break;
34895b079b7SJames Feist                 }
3498a57ec09SEd Tanous                 if (fanType == FanTypes::i2c)
35095b079b7SJames Feist                 {
35103d333e0SAkshit Shah                     std::string deviceName =
352add46820SYong Zhao                         fs::read_symlink(directory / "device").filename();
353add46820SYong Zhao 
35403d333e0SAkshit Shah                     size_t bus = 0;
35503d333e0SAkshit Shah                     size_t addr = 0;
35603d333e0SAkshit Shah                     if (!getDeviceBusAddr(deviceName, bus, addr))
357add46820SYong Zhao                     {
35803d333e0SAkshit Shah                         continue;
359add46820SYong Zhao                     }
360add46820SYong Zhao 
36195b079b7SJames Feist                     auto findBus = baseConfiguration->second.find("Bus");
362de5e9705SJames Feist                     auto findAddress =
363de5e9705SJames Feist                         baseConfiguration->second.find("Address");
36495b079b7SJames Feist                     if (findBus == baseConfiguration->second.end() ||
36595b079b7SJames Feist                         findAddress == baseConfiguration->second.end())
36695b079b7SJames Feist                     {
36795b079b7SJames Feist                         std::cerr << baseConfiguration->first
36895b079b7SJames Feist                                   << " missing bus or address\n";
3696714a25aSJames Feist                         continue;
3706714a25aSJames Feist                     }
371de5e9705SJames Feist                     unsigned int configBus = std::visit(
372de5e9705SJames Feist                         VariantToUnsignedIntVisitor(), findBus->second);
3733eb82629SJames Feist                     unsigned int configAddress = std::visit(
37495b079b7SJames Feist                         VariantToUnsignedIntVisitor(), findAddress->second);
37595b079b7SJames Feist 
37603d333e0SAkshit Shah                     if (configBus == bus && configAddress == addr)
3776714a25aSJames Feist                     {
37877636ecbSZev Weiss                         sensorData = &cfgData;
3796714a25aSJames Feist                         break;
3806714a25aSJames Feist                     }
3816714a25aSJames Feist                 }
38295b079b7SJames Feist             }
3836714a25aSJames Feist             if (sensorData == nullptr)
3846714a25aSJames Feist             {
385de5e9705SJames Feist                 std::cerr << "failed to find match for " << path.string()
386de5e9705SJames Feist                           << "\n";
3876714a25aSJames Feist                 continue;
3886714a25aSJames Feist             }
3896714a25aSJames Feist 
3906714a25aSJames Feist             auto findSensorName = baseConfiguration->second.find("Name");
391347dd4e7SZhikui Ren 
3926714a25aSJames Feist             if (findSensorName == baseConfiguration->second.end())
3936714a25aSJames Feist             {
3946714a25aSJames Feist                 std::cerr << "could not determine configuration name for "
3956714a25aSJames Feist                           << path.string() << "\n";
3966714a25aSJames Feist                 continue;
3976714a25aSJames Feist             }
398de5e9705SJames Feist             std::string sensorName =
399de5e9705SJames Feist                 std::get<std::string>(findSensorName->second);
400347dd4e7SZhikui Ren 
4016714a25aSJames Feist             // on rescans, only update sensors we were signaled by
4026714a25aSJames Feist             auto findSensor = tachSensors.find(sensorName);
4036714a25aSJames Feist             if (!firstScan && findSensor != tachSensors.end())
4046714a25aSJames Feist             {
4056714a25aSJames Feist                 bool found = false;
406de5e9705SJames Feist                 for (auto it = sensorsChanged->begin();
407de5e9705SJames Feist                      it != sensorsChanged->end(); it++)
4086714a25aSJames Feist                 {
4096c106d66SZev Weiss                     if (it->ends_with(findSensor->second->name))
4106714a25aSJames Feist                     {
4116714a25aSJames Feist                         sensorsChanged->erase(it);
4126714a25aSJames Feist                         findSensor->second = nullptr;
4136714a25aSJames Feist                         found = true;
4146714a25aSJames Feist                         break;
4156714a25aSJames Feist                     }
4166714a25aSJames Feist                 }
4176714a25aSJames Feist                 if (!found)
4186714a25aSJames Feist                 {
4196714a25aSJames Feist                     continue;
4206714a25aSJames Feist                 }
4216714a25aSJames Feist             }
4226714a25aSJames Feist             std::vector<thresholds::Threshold> sensorThresholds;
4239ced0a38SJae Hyun Yoo             if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
4246714a25aSJames Feist             {
425bb67932aSEd Tanous                 std::cerr << "error populating thresholds for " << sensorName
426bb67932aSEd Tanous                           << "\n";
4276714a25aSJames Feist             }
4286714a25aSJames Feist 
4297bc2bab2SJames Feist             auto presenceConfig =
430054aad8fSZev Weiss                 sensorData->find(cfgIntf + std::string(".Presence"));
4317bc2bab2SJames Feist 
4327bc2bab2SJames Feist             std::unique_ptr<PresenceSensor> presenceSensor(nullptr);
4337bc2bab2SJames Feist 
4347bc2bab2SJames Feist             // presence sensors are optional
4357bc2bab2SJames Feist             if (presenceConfig != sensorData->end())
4367bc2bab2SJames Feist             {
4377bc2bab2SJames Feist                 auto findPolarity = presenceConfig->second.find("Polarity");
438347dd4e7SZhikui Ren                 auto findPinName = presenceConfig->second.find("PinName");
4397bc2bab2SJames Feist 
440347dd4e7SZhikui Ren                 if (findPinName == presenceConfig->second.end() ||
4417bc2bab2SJames Feist                     findPolarity == presenceConfig->second.end())
4427bc2bab2SJames Feist                 {
4437bc2bab2SJames Feist                     std::cerr << "Malformed Presence Configuration\n";
4447bc2bab2SJames Feist                 }
4457bc2bab2SJames Feist                 else
4467bc2bab2SJames Feist                 {
447bb67932aSEd Tanous                     bool inverted =
448bb67932aSEd Tanous                         std::get<std::string>(findPolarity->second) == "Low";
4492049bd26SEd Tanous                     if (const auto* pinName =
450347dd4e7SZhikui Ren                             std::get_if<std::string>(&findPinName->second))
451347dd4e7SZhikui Ren                     {
4527b18b1e0SJames Feist                         presenceSensor = std::make_unique<PresenceSensor>(
453347dd4e7SZhikui Ren                             *pinName, inverted, io, sensorName);
454347dd4e7SZhikui Ren                     }
455347dd4e7SZhikui Ren                     else
456347dd4e7SZhikui Ren                     {
457bb67932aSEd Tanous                         std::cerr << "Malformed Presence pinName for sensor "
458347dd4e7SZhikui Ren                                   << sensorName << " \n";
459347dd4e7SZhikui Ren                     }
4607bc2bab2SJames Feist                 }
4617bc2bab2SJames Feist             }
4627b18b1e0SJames Feist             std::optional<RedundancySensor>* redundancy = nullptr;
46395b079b7SJames Feist             if (fanType == FanTypes::aspeed)
46495b079b7SJames Feist             {
4657b18b1e0SJames Feist                 redundancy = &systemRedundancy;
46695b079b7SJames Feist             }
4677bc2bab2SJames Feist 
468a4d2768cSZev Weiss             PowerState powerState = getPowerState(baseConfiguration->second);
469f920e09cSJosh Lehan 
47087d713abSJames Feist             constexpr double defaultMaxReading = 25000;
47187d713abSJames Feist             constexpr double defaultMinReading = 0;
472f69fbf99SEd Tanous             std::pair<double, double> limits =
473de5e9705SJames Feist                 std::make_pair(defaultMinReading, defaultMaxReading);
47487d713abSJames Feist 
475de5e9705SJames Feist             auto connector =
476054aad8fSZev Weiss                 sensorData->find(cfgIntf + std::string(".Connector"));
47749a8ccd6SJames Feist 
47849a8ccd6SJames Feist             std::optional<std::string> led;
47977b3add2SYong Zhao             std::string pwmName;
480d05867c0SZhikui Ren             fs::path pwmPath;
48149a8ccd6SJames Feist 
4823291b9c7SJie Yang             // The Mutable parameter is optional, defaulting to false
4833291b9c7SJie Yang             bool isValueMutable = false;
4848e94c204SJames Feist             if (connector != sensorData->end())
4858e94c204SJames Feist             {
4868e94c204SJames Feist                 auto findPwm = connector->second.find("Pwm");
48749a8ccd6SJames Feist                 if (findPwm != connector->second.end())
4888e94c204SJames Feist                 {
489de5e9705SJames Feist                     size_t pwm = std::visit(VariantToUnsignedIntVisitor(),
490de5e9705SJames Feist                                             findPwm->second);
491ddf25d17SHoward Chiu                     if (!findPwmPath(directory, pwm, pwmPath))
492ddf25d17SHoward Chiu                     {
493ddf25d17SHoward Chiu                         std::cerr << "Connector for " << sensorName
494ddf25d17SHoward Chiu                                   << " no pwm channel found!\n";
495ddf25d17SHoward Chiu                         continue;
496ddf25d17SHoward Chiu                     }
497ddf25d17SHoward Chiu 
498779c96a2SPatrick Williams                     fs::path pwmEnableFile = "pwm" + std::to_string(pwm + 1) +
499779c96a2SPatrick Williams                                              "_enable";
500bb67932aSEd Tanous                     fs::path enablePath = pwmPath.parent_path() / pwmEnableFile;
501ddf25d17SHoward Chiu                     enablePwm(enablePath);
502ddf25d17SHoward Chiu 
50349a8ccd6SJames Feist                     /* use pwm name override if found in configuration else
50449a8ccd6SJames Feist                      * use default */
505d320a2e1SJason Ling                     auto findOverride = connector->second.find("PwmName");
506d320a2e1SJason Ling                     if (findOverride != connector->second.end())
507d320a2e1SJason Ling                     {
508d320a2e1SJason Ling                         pwmName = std::visit(VariantToStringVisitor(),
509d320a2e1SJason Ling                                              findOverride->second);
510d320a2e1SJason Ling                     }
511d320a2e1SJason Ling                     else
512d320a2e1SJason Ling                     {
513d320a2e1SJason Ling                         pwmName = "Pwm_" + std::to_string(pwm + 1);
514d320a2e1SJason Ling                     }
5153291b9c7SJie Yang 
5163291b9c7SJie Yang                     // Check PWM sensor mutability
5173291b9c7SJie Yang                     auto findMutable = connector->second.find("Mutable");
5183291b9c7SJie Yang                     if (findMutable != connector->second.end())
5193291b9c7SJie Yang                     {
5202049bd26SEd Tanous                         const auto* ptrMutable =
5213291b9c7SJie Yang                             std::get_if<bool>(&(findMutable->second));
5222049bd26SEd Tanous                         if (ptrMutable != nullptr)
5233291b9c7SJie Yang                         {
5243291b9c7SJie Yang                             isValueMutable = *ptrMutable;
5253291b9c7SJie Yang                         }
5263291b9c7SJie Yang                     }
5278e94c204SJames Feist                 }
52849a8ccd6SJames Feist                 else
52949a8ccd6SJames Feist                 {
53049a8ccd6SJames Feist                     std::cerr << "Connector for " << sensorName
53149a8ccd6SJames Feist                               << " missing pwm!\n";
53249a8ccd6SJames Feist                 }
53349a8ccd6SJames Feist 
53449a8ccd6SJames Feist                 auto findLED = connector->second.find("LED");
53549a8ccd6SJames Feist                 if (findLED != connector->second.end())
53649a8ccd6SJames Feist                 {
5372049bd26SEd Tanous                     const auto* ledName =
5382049bd26SEd Tanous                         std::get_if<std::string>(&(findLED->second));
53949a8ccd6SJames Feist                     if (ledName == nullptr)
54049a8ccd6SJames Feist                     {
541bb67932aSEd Tanous                         std::cerr << "Wrong format for LED of " << sensorName
542bb67932aSEd Tanous                                   << "\n";
54349a8ccd6SJames Feist                     }
54449a8ccd6SJames Feist                     else
54549a8ccd6SJames Feist                     {
54649a8ccd6SJames Feist                         led = *ledName;
54749a8ccd6SJames Feist                     }
54849a8ccd6SJames Feist                 }
54949a8ccd6SJames Feist             }
55049a8ccd6SJames Feist 
55149a8ccd6SJames Feist             findLimits(limits, baseConfiguration);
5525170fe63SJosh Lehan 
5539c47bd7eSJustin Ledford             enableFanInput(path);
5549c47bd7eSJustin Ledford 
5555170fe63SJosh Lehan             auto& tachSensor = tachSensors[sensorName];
5565170fe63SJosh Lehan             tachSensor = nullptr;
5575170fe63SJosh Lehan             tachSensor = std::make_shared<TachSensor>(
55849a8ccd6SJames Feist                 path.string(), baseType, objectServer, dbusConnection,
55949a8ccd6SJames Feist                 std::move(presenceSensor), redundancy, io, sensorName,
560bb67932aSEd Tanous                 std::move(sensorThresholds), *interfacePath, limits, powerState,
561bb67932aSEd Tanous                 led);
5625170fe63SJosh Lehan             tachSensor->setupRead();
56377b3add2SYong Zhao 
564d05867c0SZhikui Ren             if (!pwmPath.empty() && fs::exists(pwmPath) &&
5652049bd26SEd Tanous                 (pwmSensors.count(pwmPath) == 0U))
56677b3add2SYong Zhao             {
56777b3add2SYong Zhao                 pwmSensors[pwmPath] = std::make_unique<PwmSensor>(
56877b3add2SYong Zhao                     pwmName, pwmPath, dbusConnection, objectServer,
5693291b9c7SJie Yang                     *interfacePath, "Fan", isValueMutable);
5706714a25aSJames Feist             }
57177b3add2SYong Zhao         }
57277b3add2SYong Zhao 
573d540741eSKuiying Wang         createRedundancySensor(tachSensors, dbusConnection, objectServer);
5748a17c303SEd Tanous     });
575de5e9705SJames Feist     getter->getConfiguration(
576f27a55c7SJames Feist         std::vector<std::string>{sensorTypes.begin(), sensorTypes.end()},
577f27a55c7SJames Feist         retries);
5786714a25aSJames Feist }
5796714a25aSJames Feist 
main()580b6c0b914SJames Feist int main()
5816714a25aSJames Feist {
5821f978631SEd Tanous     boost::asio::io_context io;
5836714a25aSJames Feist     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
58414ed5e99SEd Tanous     sdbusplus::asio::object_server objectServer(systemBus, true);
58514ed5e99SEd Tanous 
58614ed5e99SEd Tanous     objectServer.add_manager("/xyz/openbmc_project/sensors");
587d9067251SEd Tanous     objectServer.add_manager("/xyz/openbmc_project/control");
588c2f83fedSLei YU     objectServer.add_manager("/xyz/openbmc_project/inventory");
5896714a25aSJames Feist     systemBus->request_name("xyz.openbmc_project.FanSensor");
5905170fe63SJosh Lehan     boost::container::flat_map<std::string, std::shared_ptr<TachSensor>>
5916714a25aSJames Feist         tachSensors;
5926714a25aSJames Feist     boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
5936714a25aSJames Feist         pwmSensors;
5945591cf08SJames Feist     auto sensorsChanged =
5955591cf08SJames Feist         std::make_shared<boost::container::flat_set<std::string>>();
5966714a25aSJames Feist 
59783db50caSEd Tanous     boost::asio::post(io, [&]() {
5986714a25aSJames Feist         createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
5996714a25aSJames Feist                       nullptr);
6006714a25aSJames Feist     });
6016714a25aSJames Feist 
6029b4a20e9SEd Tanous     boost::asio::steady_timer filterTimer(io);
60392f8f515SPatrick Williams     std::function<void(sdbusplus::message_t&)> eventHandler =
60492f8f515SPatrick Williams         [&](sdbusplus::message_t& message) {
6056714a25aSJames Feist         if (message.is_method_error())
6066714a25aSJames Feist         {
6076714a25aSJames Feist             std::cerr << "callback method error\n";
6086714a25aSJames Feist             return;
6096714a25aSJames Feist         }
6106714a25aSJames Feist         sensorsChanged->insert(message.get_path());
6116714a25aSJames Feist         // this implicitly cancels the timer
61283db50caSEd Tanous         filterTimer.expires_after(std::chrono::seconds(1));
6136714a25aSJames Feist 
6146714a25aSJames Feist         filterTimer.async_wait([&](const boost::system::error_code& ec) {
6156714a25aSJames Feist             if (ec == boost::asio::error::operation_aborted)
6166714a25aSJames Feist             {
6176714a25aSJames Feist                 /* we were canceled*/
6186714a25aSJames Feist                 return;
6196714a25aSJames Feist             }
6208a57ec09SEd Tanous             if (ec)
6216714a25aSJames Feist             {
6226714a25aSJames Feist                 std::cerr << "timer error\n";
6236714a25aSJames Feist                 return;
6246714a25aSJames Feist             }
625bb67932aSEd Tanous             createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
626bb67932aSEd Tanous                           sensorsChanged, 5);
6276714a25aSJames Feist         });
6286714a25aSJames Feist     };
6296714a25aSJames Feist 
630214d9717SZev Weiss     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
631214d9717SZev Weiss         setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler);
6326714a25aSJames Feist 
633dc6c55f3SJames Feist     // redundancy sensor
63492f8f515SPatrick Williams     std::function<void(sdbusplus::message_t&)> redundancyHandler =
63592f8f515SPatrick Williams         [&tachSensors, &systemBus, &objectServer](sdbusplus::message_t&) {
636dc6c55f3SJames Feist         createRedundancySensor(tachSensors, systemBus, objectServer);
637dc6c55f3SJames Feist     };
63892f8f515SPatrick Williams     auto match = std::make_unique<sdbusplus::bus::match_t>(
63992f8f515SPatrick Williams         static_cast<sdbusplus::bus_t&>(*systemBus),
640dc6c55f3SJames Feist         "type='signal',member='PropertiesChanged',path_namespace='" +
641dc6c55f3SJames Feist             std::string(inventoryPath) + "',arg0namespace='" +
642dc6c55f3SJames Feist             redundancyConfiguration + "'",
643b6c0b914SJames Feist         std::move(redundancyHandler));
644dc6c55f3SJames Feist     matches.emplace_back(std::move(match));
645dc6c55f3SJames Feist 
6461263c3daSBruce Lee     setupManufacturingModeMatch(*systemBus);
6476714a25aSJames Feist     io.run();
6488685b17aSZhikui Ren     return 0;
6496714a25aSJames Feist }
650