xref: /openbmc/dbus-sensors/src/FanMain.cpp (revision d37e1db5)
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"
19e73bd0a1SAndrew Jeffery #include "Utils.hpp"
20e73bd0a1SAndrew Jeffery #include "VariantVisitors.hpp"
21e73bd0a1SAndrew Jeffery 
226714a25aSJames Feist #include <boost/algorithm/string/replace.hpp>
2396e97db7SPatrick Venture #include <boost/container/flat_map.hpp>
246714a25aSJames Feist #include <boost/container/flat_set.hpp>
2538fb5983SJames Feist #include <sdbusplus/asio/connection.hpp>
2638fb5983SJames Feist #include <sdbusplus/asio/object_server.hpp>
2738fb5983SJames Feist #include <sdbusplus/bus/match.hpp>
2838fb5983SJames Feist 
2938fb5983SJames Feist #include <array>
3024f02f24SJames Feist #include <filesystem>
316714a25aSJames Feist #include <fstream>
3296e97db7SPatrick Venture #include <functional>
3396e97db7SPatrick Venture #include <memory>
3496e97db7SPatrick Venture #include <optional>
356714a25aSJames Feist #include <regex>
3696e97db7SPatrick Venture #include <string>
3796e97db7SPatrick Venture #include <utility>
3896e97db7SPatrick Venture #include <variant>
3996e97db7SPatrick Venture #include <vector>
406714a25aSJames Feist 
41cf3bce6eSJames Feist namespace fs = std::filesystem;
423eb82629SJames Feist 
43a3e8f2a3SYong Zhao // The following two structures need to be consistent
44c361e222SChris Sides static auto sensorTypes{std::to_array<const char*>(
45c361e222SChris Sides     {"AspeedFan", "I2CFan", "NuvotonFan", "HPEFan"})};
46a3e8f2a3SYong Zhao 
47a3e8f2a3SYong Zhao enum FanTypes
48a3e8f2a3SYong Zhao {
49a3e8f2a3SYong Zhao     aspeed = 0,
50a3e8f2a3SYong Zhao     i2c,
51a3e8f2a3SYong Zhao     nuvoton,
52c361e222SChris Sides     hpe,
53a3e8f2a3SYong Zhao     max,
54a3e8f2a3SYong Zhao };
55a3e8f2a3SYong Zhao 
56a3e8f2a3SYong Zhao static_assert(std::tuple_size<decltype(sensorTypes)>::value == FanTypes::max,
57a3e8f2a3SYong Zhao               "sensorTypes element number is not equal to FanTypes number");
58a3e8f2a3SYong Zhao 
59dc6c55f3SJames Feist constexpr const char* redundancyConfiguration =
60dc6c55f3SJames Feist     "xyz.openbmc_project.Configuration.FanRedundancy";
619ced0a38SJae Hyun Yoo static std::regex inputRegex(R"(fan(\d+)_input)");
626714a25aSJames Feist 
63dc6c55f3SJames Feist // todo: power supply fan redundancy
647b18b1e0SJames Feist std::optional<RedundancySensor> systemRedundancy;
6595b079b7SJames Feist 
663d5260dcSChris Sides static const std::map<std::string, FanTypes> compatibleFanTypes = {
673d5260dcSChris Sides     {"aspeed,ast2400-pwm-tacho", FanTypes::aspeed},
683d5260dcSChris Sides     {"aspeed,ast2500-pwm-tacho", FanTypes::aspeed},
69c361e222SChris Sides     {"nuvoton,npcm750-pwm-fan", FanTypes::nuvoton},
70*d37e1db5SBrian Ma     {"nuvoton,npcm845-pwm-fan", FanTypes::nuvoton},
71c361e222SChris Sides     {"hpe,gxp-fan-ctrl", FanTypes::hpe}
723d5260dcSChris Sides     // add compatible string here for new fan type
733d5260dcSChris Sides };
743d5260dcSChris Sides 
7595b079b7SJames Feist FanTypes getFanType(const fs::path& parentPath)
7695b079b7SJames Feist {
779a472e8eSChris Sides     fs::path linkPath = parentPath / "of_node";
7839963226SZhikui Ren     if (!fs::exists(linkPath))
7939963226SZhikui Ren     {
8039963226SZhikui Ren         return FanTypes::i2c;
8139963226SZhikui Ren     }
829a472e8eSChris Sides 
8339963226SZhikui Ren     std::string canonical = fs::canonical(linkPath);
849a472e8eSChris Sides     std::string compatiblePath = canonical + "/compatible";
859a472e8eSChris Sides     std::ifstream compatibleStream(compatiblePath);
869a472e8eSChris Sides 
873d5260dcSChris Sides     if (!compatibleStream)
889a472e8eSChris Sides     {
899a472e8eSChris Sides         std::cerr << "Error opening " << compatiblePath << "\n";
903d5260dcSChris Sides         return FanTypes::i2c;
919a472e8eSChris Sides     }
929a472e8eSChris Sides 
939a472e8eSChris Sides     std::string compatibleString;
943d5260dcSChris Sides     while (std::getline(compatibleStream, compatibleString))
959a472e8eSChris Sides     {
969a472e8eSChris Sides         compatibleString.pop_back(); // trim EOL before comparisons
979a472e8eSChris Sides 
983d5260dcSChris Sides         std::map<std::string, FanTypes>::const_iterator compatibleIterator =
993d5260dcSChris Sides             compatibleFanTypes.find(compatibleString);
1003d5260dcSChris Sides 
1013d5260dcSChris Sides         if (compatibleIterator != compatibleFanTypes.end())
10295b079b7SJames Feist         {
1033d5260dcSChris Sides             return compatibleIterator->second;
1048843b627SPeter Lundgren         }
1059a472e8eSChris Sides     }
1069a472e8eSChris Sides 
10795b079b7SJames Feist     return FanTypes::i2c;
10895b079b7SJames Feist }
109abf91de1SJeff Lin void enablePwm(const fs::path& filePath)
110abf91de1SJeff Lin {
111abf91de1SJeff Lin     std::fstream enableFile(filePath, std::ios::in | std::ios::out);
112abf91de1SJeff Lin     if (!enableFile.good())
113abf91de1SJeff Lin     {
114abf91de1SJeff Lin         std::cerr << "Error read/write " << filePath << "\n";
115abf91de1SJeff Lin         return;
116abf91de1SJeff Lin     }
117dc6c55f3SJames Feist 
118abf91de1SJeff Lin     std::string regulateMode;
119abf91de1SJeff Lin     std::getline(enableFile, regulateMode);
120abf91de1SJeff Lin     if (regulateMode == "0")
121abf91de1SJeff Lin     {
122abf91de1SJeff Lin         enableFile << 1;
123abf91de1SJeff Lin     }
124abf91de1SJeff Lin }
125ddf25d17SHoward Chiu bool findPwmfanPath(unsigned int configPwmfanIndex, fs::path& pwmPath)
126ddf25d17SHoward Chiu {
127ddf25d17SHoward Chiu     /* Search PWM since pwm-fan had separated
128ddf25d17SHoward Chiu      * PWM from tach directory and 1 channel only*/
129ddf25d17SHoward Chiu     std::vector<fs::path> pwmfanPaths;
130ddf25d17SHoward Chiu     std::string pwnfanDevName("pwm-fan");
131ddf25d17SHoward Chiu 
132ddf25d17SHoward Chiu     pwnfanDevName += std::to_string(configPwmfanIndex);
133ddf25d17SHoward Chiu 
134ddf25d17SHoward Chiu     if (!findFiles(fs::path("/sys/class/hwmon"), R"(pwm\d+)", pwmfanPaths))
135ddf25d17SHoward Chiu     {
136ddf25d17SHoward Chiu         std::cerr << "No PWMs are found!\n";
137ddf25d17SHoward Chiu         return false;
138ddf25d17SHoward Chiu     }
139ddf25d17SHoward Chiu     for (const auto& path : pwmfanPaths)
140ddf25d17SHoward Chiu     {
141ddf25d17SHoward Chiu         std::error_code ec;
142ddf25d17SHoward Chiu         fs::path link = fs::read_symlink(path.parent_path() / "device", ec);
143ddf25d17SHoward Chiu 
144ddf25d17SHoward Chiu         if (ec)
145ddf25d17SHoward Chiu         {
146ddf25d17SHoward Chiu             std::cerr << "read_symlink() failed: " << ec.message() << " ("
147ddf25d17SHoward Chiu                       << ec.value() << ")\n";
148ddf25d17SHoward Chiu             continue;
149ddf25d17SHoward Chiu         }
150ddf25d17SHoward Chiu 
151ddf25d17SHoward Chiu         if (link.filename().string() == pwnfanDevName)
152ddf25d17SHoward Chiu         {
153ddf25d17SHoward Chiu             pwmPath = path;
154ddf25d17SHoward Chiu             return true;
155ddf25d17SHoward Chiu         }
156ddf25d17SHoward Chiu     }
157ddf25d17SHoward Chiu     return false;
158ddf25d17SHoward Chiu }
159ddf25d17SHoward Chiu bool findPwmPath(const fs::path& directory, unsigned int pwm, fs::path& pwmPath)
160ddf25d17SHoward Chiu {
161ddf25d17SHoward Chiu     std::error_code ec;
162ddf25d17SHoward Chiu 
163ddf25d17SHoward Chiu     /* Assuming PWM file is appeared in the same directory as fanX_input */
164ddf25d17SHoward Chiu     auto path = directory / ("pwm" + std::to_string(pwm + 1));
165ddf25d17SHoward Chiu     bool exists = fs::exists(path, ec);
166ddf25d17SHoward Chiu 
167ddf25d17SHoward Chiu     if (ec || !exists)
168ddf25d17SHoward Chiu     {
169ddf25d17SHoward Chiu         /* PWM file not exist or error happened */
170ddf25d17SHoward Chiu         if (ec)
171ddf25d17SHoward Chiu         {
172ddf25d17SHoward Chiu             std::cerr << "exists() failed: " << ec.message() << " ("
173ddf25d17SHoward Chiu                       << ec.value() << ")\n";
174ddf25d17SHoward Chiu         }
175ddf25d17SHoward Chiu         /* try search form pwm-fanX directory */
176ddf25d17SHoward Chiu         return findPwmfanPath(pwm, pwmPath);
177ddf25d17SHoward Chiu     }
178ddf25d17SHoward Chiu 
179ddf25d17SHoward Chiu     pwmPath = path;
180ddf25d17SHoward Chiu     return true;
181ddf25d17SHoward Chiu }
1829c47bd7eSJustin Ledford 
1839c47bd7eSJustin Ledford // The argument to this function should be the fanN_input file that we want to
1849c47bd7eSJustin Ledford // enable. The function will locate the corresponding fanN_enable file if it
1859c47bd7eSJustin Ledford // exists. Note that some drivers don't provide this file if the sensors are
1869c47bd7eSJustin Ledford // always enabled.
1879c47bd7eSJustin Ledford void enableFanInput(const fs::path& fanInputPath)
1889c47bd7eSJustin Ledford {
1899c47bd7eSJustin Ledford     std::error_code ec;
1909c47bd7eSJustin Ledford     std::string path(fanInputPath.string());
1919c47bd7eSJustin Ledford     boost::replace_last(path, "input", "enable");
1929c47bd7eSJustin Ledford 
1939c47bd7eSJustin Ledford     bool exists = fs::exists(path, ec);
1949c47bd7eSJustin Ledford     if (ec || !exists)
1959c47bd7eSJustin Ledford     {
1969c47bd7eSJustin Ledford         return;
1979c47bd7eSJustin Ledford     }
1989c47bd7eSJustin Ledford 
1999c47bd7eSJustin Ledford     std::fstream enableFile(path, std::ios::out);
2009c47bd7eSJustin Ledford     if (!enableFile.good())
2019c47bd7eSJustin Ledford     {
2029c47bd7eSJustin Ledford         return;
2039c47bd7eSJustin Ledford     }
2049c47bd7eSJustin Ledford     enableFile << 1;
2059c47bd7eSJustin Ledford }
2069c47bd7eSJustin Ledford 
207d540741eSKuiying Wang void createRedundancySensor(
2085170fe63SJosh Lehan     const boost::container::flat_map<std::string, std::shared_ptr<TachSensor>>&
209d540741eSKuiying Wang         sensors,
2108a57ec09SEd Tanous     const std::shared_ptr<sdbusplus::asio::connection>& conn,
211d540741eSKuiying Wang     sdbusplus::asio::object_server& objectServer)
212d540741eSKuiying Wang {
213d540741eSKuiying Wang     conn->async_method_call(
214d540741eSKuiying Wang         [&objectServer, &sensors](boost::system::error_code& ec,
2158a57ec09SEd Tanous                                   const ManagedObjectType& managedObj) {
216d540741eSKuiying Wang         if (ec)
217d540741eSKuiying Wang         {
218d540741eSKuiying Wang             std::cerr << "Error calling entity manager \n";
219d540741eSKuiying Wang             return;
220d540741eSKuiying Wang         }
22177636ecbSZev Weiss         for (const auto& [path, interfaces] : managedObj)
222d540741eSKuiying Wang         {
22377636ecbSZev Weiss             for (const auto& [intf, cfg] : interfaces)
224d540741eSKuiying Wang             {
22577636ecbSZev Weiss                 if (intf == redundancyConfiguration)
226d540741eSKuiying Wang                 {
227d540741eSKuiying Wang                     // currently only support one
22877636ecbSZev Weiss                     auto findCount = cfg.find("AllowedFailures");
22977636ecbSZev Weiss                     if (findCount == cfg.end())
230d540741eSKuiying Wang                     {
231d540741eSKuiying Wang                         std::cerr << "Malformed redundancy record \n";
232d540741eSKuiying Wang                         return;
233d540741eSKuiying Wang                     }
234d540741eSKuiying Wang                     std::vector<std::string> sensorList;
235d540741eSKuiying Wang 
23677636ecbSZev Weiss                     for (const auto& [name, sensor] : sensors)
237d540741eSKuiying Wang                     {
238d540741eSKuiying Wang                         sensorList.push_back(
239d540741eSKuiying Wang                             "/xyz/openbmc_project/sensors/fan_tach/" +
24077636ecbSZev Weiss                             sensor->name);
241d540741eSKuiying Wang                     }
242d540741eSKuiying Wang                     systemRedundancy.reset();
24377636ecbSZev Weiss                     systemRedundancy.emplace(
24477636ecbSZev Weiss                         RedundancySensor(std::get<uint64_t>(findCount->second),
24577636ecbSZev Weiss                                          sensorList, objectServer, path));
246d540741eSKuiying Wang 
247d540741eSKuiying Wang                     return;
248d540741eSKuiying Wang                 }
249d540741eSKuiying Wang             }
250d540741eSKuiying Wang         }
251d540741eSKuiying Wang     },
2523e620af1SNan Zhou         "xyz.openbmc_project.EntityManager", "/xyz/openbmc_project/inventory",
253d540741eSKuiying Wang         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
254d540741eSKuiying Wang }
255d540741eSKuiying Wang 
2566714a25aSJames Feist void createSensors(
2571f978631SEd Tanous     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
2585170fe63SJosh Lehan     boost::container::flat_map<std::string, std::shared_ptr<TachSensor>>&
2596714a25aSJames Feist         tachSensors,
2606714a25aSJames Feist     boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>&
2616714a25aSJames Feist         pwmSensors,
2626714a25aSJames Feist     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
2635591cf08SJames Feist     const std::shared_ptr<boost::container::flat_set<std::string>>&
264f27a55c7SJames Feist         sensorsChanged,
265f27a55c7SJames Feist     size_t retries = 0)
2666714a25aSJames Feist {
267de5e9705SJames Feist     auto getter = std::make_shared<GetSensorConfiguration>(
268de5e9705SJames Feist         dbusConnection,
2698a17c303SEd Tanous         [&io, &objectServer, &tachSensors, &pwmSensors, &dbusConnection,
2708a17c303SEd Tanous          sensorsChanged](const ManagedObjectType& sensorConfigurations) {
2716714a25aSJames Feist         bool firstScan = sensorsChanged == nullptr;
2726714a25aSJames Feist         std::vector<fs::path> paths;
273bb67932aSEd Tanous         if (!findFiles(fs::path("/sys/class/hwmon"), R"(fan\d+_input)", paths))
2746714a25aSJames Feist         {
27577b3add2SYong Zhao             std::cerr << "No fan sensors in system\n";
2766714a25aSJames Feist             return;
2776714a25aSJames Feist         }
2786714a25aSJames Feist 
2796714a25aSJames Feist         // iterate through all found fan sensors, and try to match them with
2806714a25aSJames Feist         // configuration
28195b079b7SJames Feist         for (const auto& path : paths)
2826714a25aSJames Feist         {
2836714a25aSJames Feist             std::smatch match;
2846714a25aSJames Feist             std::string pathStr = path.string();
2856714a25aSJames Feist 
2869ced0a38SJae Hyun Yoo             std::regex_search(pathStr, match, inputRegex);
2876714a25aSJames Feist             std::string indexStr = *(match.begin() + 1);
2886714a25aSJames Feist 
28977b3add2SYong Zhao             fs::path directory = path.parent_path();
29095b079b7SJames Feist             FanTypes fanType = getFanType(directory);
291054aad8fSZev Weiss             std::string cfgIntf = configInterfaceName(sensorTypes[fanType]);
29277b3add2SYong Zhao 
2936714a25aSJames Feist             // convert to 0 based
2946714a25aSJames Feist             size_t index = std::stoul(indexStr) - 1;
2956714a25aSJames Feist 
296a771f6a7SEd Tanous             const char* baseType = nullptr;
2976714a25aSJames Feist             const SensorData* sensorData = nullptr;
2986714a25aSJames Feist             const std::string* interfacePath = nullptr;
29987d713abSJames Feist             const SensorBaseConfiguration* baseConfiguration = nullptr;
30077636ecbSZev Weiss             for (const auto& [path, cfgData] : sensorConfigurations)
3016714a25aSJames Feist             {
302de5e9705SJames Feist                 // find the base of the configuration to see if indexes
303de5e9705SJames Feist                 // match
304054aad8fSZev Weiss                 auto sensorBaseFind = cfgData.find(cfgIntf);
30577636ecbSZev Weiss                 if (sensorBaseFind == cfgData.end())
3066714a25aSJames Feist                 {
3076714a25aSJames Feist                     continue;
3086714a25aSJames Feist                 }
309347dd4e7SZhikui Ren 
310a3e8f2a3SYong Zhao                 baseConfiguration = &(*sensorBaseFind);
31177636ecbSZev Weiss                 interfacePath = &path.str;
312a3e8f2a3SYong Zhao                 baseType = sensorTypes[fanType];
313a3e8f2a3SYong Zhao 
3146714a25aSJames Feist                 auto findIndex = baseConfiguration->second.find("Index");
3156714a25aSJames Feist                 if (findIndex == baseConfiguration->second.end())
3166714a25aSJames Feist                 {
317bb67932aSEd Tanous                     std::cerr << baseConfiguration->first << " missing index\n";
3186714a25aSJames Feist                     continue;
3196714a25aSJames Feist                 }
320de5e9705SJames Feist                 unsigned int configIndex = std::visit(
321de5e9705SJames Feist                     VariantToUnsignedIntVisitor(), findIndex->second);
3226714a25aSJames Feist                 if (configIndex != index)
3236714a25aSJames Feist                 {
3246714a25aSJames Feist                     continue;
3256714a25aSJames Feist                 }
326c361e222SChris Sides                 if (fanType == FanTypes::aspeed ||
327c361e222SChris Sides                     fanType == FanTypes::nuvoton || fanType == FanTypes::hpe)
3286714a25aSJames Feist                 {
329c361e222SChris Sides                     // there will be only 1 aspeed or nuvoton or hpe sensor
3303d5260dcSChris Sides                     // object in sysfs, we found the fan
33177636ecbSZev Weiss                     sensorData = &cfgData;
33295b079b7SJames Feist                     break;
33395b079b7SJames Feist                 }
3348a57ec09SEd Tanous                 if (fanType == FanTypes::i2c)
33595b079b7SJames Feist                 {
33603d333e0SAkshit Shah                     std::string deviceName =
337add46820SYong Zhao                         fs::read_symlink(directory / "device").filename();
338add46820SYong Zhao 
33903d333e0SAkshit Shah                     size_t bus = 0;
34003d333e0SAkshit Shah                     size_t addr = 0;
34103d333e0SAkshit Shah                     if (!getDeviceBusAddr(deviceName, bus, addr))
342add46820SYong Zhao                     {
34303d333e0SAkshit Shah                         continue;
344add46820SYong Zhao                     }
345add46820SYong Zhao 
34695b079b7SJames Feist                     auto findBus = baseConfiguration->second.find("Bus");
347de5e9705SJames Feist                     auto findAddress =
348de5e9705SJames Feist                         baseConfiguration->second.find("Address");
34995b079b7SJames Feist                     if (findBus == baseConfiguration->second.end() ||
35095b079b7SJames Feist                         findAddress == baseConfiguration->second.end())
35195b079b7SJames Feist                     {
35295b079b7SJames Feist                         std::cerr << baseConfiguration->first
35395b079b7SJames Feist                                   << " missing bus or address\n";
3546714a25aSJames Feist                         continue;
3556714a25aSJames Feist                     }
356de5e9705SJames Feist                     unsigned int configBus = std::visit(
357de5e9705SJames Feist                         VariantToUnsignedIntVisitor(), findBus->second);
3583eb82629SJames Feist                     unsigned int configAddress = std::visit(
35995b079b7SJames Feist                         VariantToUnsignedIntVisitor(), findAddress->second);
36095b079b7SJames Feist 
36103d333e0SAkshit Shah                     if (configBus == bus && configAddress == addr)
3626714a25aSJames Feist                     {
36377636ecbSZev Weiss                         sensorData = &cfgData;
3646714a25aSJames Feist                         break;
3656714a25aSJames Feist                     }
3666714a25aSJames Feist                 }
36795b079b7SJames Feist             }
3686714a25aSJames Feist             if (sensorData == nullptr)
3696714a25aSJames Feist             {
370de5e9705SJames Feist                 std::cerr << "failed to find match for " << path.string()
371de5e9705SJames Feist                           << "\n";
3726714a25aSJames Feist                 continue;
3736714a25aSJames Feist             }
3746714a25aSJames Feist 
3756714a25aSJames Feist             auto findSensorName = baseConfiguration->second.find("Name");
376347dd4e7SZhikui Ren 
3776714a25aSJames Feist             if (findSensorName == baseConfiguration->second.end())
3786714a25aSJames Feist             {
3796714a25aSJames Feist                 std::cerr << "could not determine configuration name for "
3806714a25aSJames Feist                           << path.string() << "\n";
3816714a25aSJames Feist                 continue;
3826714a25aSJames Feist             }
383de5e9705SJames Feist             std::string sensorName =
384de5e9705SJames Feist                 std::get<std::string>(findSensorName->second);
385347dd4e7SZhikui Ren 
3866714a25aSJames Feist             // on rescans, only update sensors we were signaled by
3876714a25aSJames Feist             auto findSensor = tachSensors.find(sensorName);
3886714a25aSJames Feist             if (!firstScan && findSensor != tachSensors.end())
3896714a25aSJames Feist             {
3906714a25aSJames Feist                 bool found = false;
391de5e9705SJames Feist                 for (auto it = sensorsChanged->begin();
392de5e9705SJames Feist                      it != sensorsChanged->end(); it++)
3936714a25aSJames Feist                 {
3946c106d66SZev Weiss                     if (it->ends_with(findSensor->second->name))
3956714a25aSJames Feist                     {
3966714a25aSJames Feist                         sensorsChanged->erase(it);
3976714a25aSJames Feist                         findSensor->second = nullptr;
3986714a25aSJames Feist                         found = true;
3996714a25aSJames Feist                         break;
4006714a25aSJames Feist                     }
4016714a25aSJames Feist                 }
4026714a25aSJames Feist                 if (!found)
4036714a25aSJames Feist                 {
4046714a25aSJames Feist                     continue;
4056714a25aSJames Feist                 }
4066714a25aSJames Feist             }
4076714a25aSJames Feist             std::vector<thresholds::Threshold> sensorThresholds;
4089ced0a38SJae Hyun Yoo             if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
4096714a25aSJames Feist             {
410bb67932aSEd Tanous                 std::cerr << "error populating thresholds for " << sensorName
411bb67932aSEd Tanous                           << "\n";
4126714a25aSJames Feist             }
4136714a25aSJames Feist 
4147bc2bab2SJames Feist             auto presenceConfig =
415054aad8fSZev Weiss                 sensorData->find(cfgIntf + std::string(".Presence"));
4167bc2bab2SJames Feist 
4177bc2bab2SJames Feist             std::unique_ptr<PresenceSensor> presenceSensor(nullptr);
4187bc2bab2SJames Feist 
4197bc2bab2SJames Feist             // presence sensors are optional
4207bc2bab2SJames Feist             if (presenceConfig != sensorData->end())
4217bc2bab2SJames Feist             {
4227bc2bab2SJames Feist                 auto findPolarity = presenceConfig->second.find("Polarity");
423347dd4e7SZhikui Ren                 auto findPinName = presenceConfig->second.find("PinName");
4247bc2bab2SJames Feist 
425347dd4e7SZhikui Ren                 if (findPinName == presenceConfig->second.end() ||
4267bc2bab2SJames Feist                     findPolarity == presenceConfig->second.end())
4277bc2bab2SJames Feist                 {
4287bc2bab2SJames Feist                     std::cerr << "Malformed Presence Configuration\n";
4297bc2bab2SJames Feist                 }
4307bc2bab2SJames Feist                 else
4317bc2bab2SJames Feist                 {
432bb67932aSEd Tanous                     bool inverted =
433bb67932aSEd Tanous                         std::get<std::string>(findPolarity->second) == "Low";
4342049bd26SEd Tanous                     if (const auto* pinName =
435347dd4e7SZhikui Ren                             std::get_if<std::string>(&findPinName->second))
436347dd4e7SZhikui Ren                     {
4377b18b1e0SJames Feist                         presenceSensor = std::make_unique<PresenceSensor>(
438347dd4e7SZhikui Ren                             *pinName, inverted, io, sensorName);
439347dd4e7SZhikui Ren                     }
440347dd4e7SZhikui Ren                     else
441347dd4e7SZhikui Ren                     {
442bb67932aSEd Tanous                         std::cerr << "Malformed Presence pinName for sensor "
443347dd4e7SZhikui Ren                                   << sensorName << " \n";
444347dd4e7SZhikui Ren                     }
4457bc2bab2SJames Feist                 }
4467bc2bab2SJames Feist             }
4477b18b1e0SJames Feist             std::optional<RedundancySensor>* redundancy = nullptr;
44895b079b7SJames Feist             if (fanType == FanTypes::aspeed)
44995b079b7SJames Feist             {
4507b18b1e0SJames Feist                 redundancy = &systemRedundancy;
45195b079b7SJames Feist             }
4527bc2bab2SJames Feist 
453a4d2768cSZev Weiss             PowerState powerState = getPowerState(baseConfiguration->second);
454f920e09cSJosh Lehan 
45587d713abSJames Feist             constexpr double defaultMaxReading = 25000;
45687d713abSJames Feist             constexpr double defaultMinReading = 0;
457f69fbf99SEd Tanous             std::pair<double, double> limits =
458de5e9705SJames Feist                 std::make_pair(defaultMinReading, defaultMaxReading);
45987d713abSJames Feist 
460de5e9705SJames Feist             auto connector =
461054aad8fSZev Weiss                 sensorData->find(cfgIntf + std::string(".Connector"));
46249a8ccd6SJames Feist 
46349a8ccd6SJames Feist             std::optional<std::string> led;
46477b3add2SYong Zhao             std::string pwmName;
465d05867c0SZhikui Ren             fs::path pwmPath;
46649a8ccd6SJames Feist 
4673291b9c7SJie Yang             // The Mutable parameter is optional, defaulting to false
4683291b9c7SJie Yang             bool isValueMutable = false;
4698e94c204SJames Feist             if (connector != sensorData->end())
4708e94c204SJames Feist             {
4718e94c204SJames Feist                 auto findPwm = connector->second.find("Pwm");
47249a8ccd6SJames Feist                 if (findPwm != connector->second.end())
4738e94c204SJames Feist                 {
474de5e9705SJames Feist                     size_t pwm = std::visit(VariantToUnsignedIntVisitor(),
475de5e9705SJames Feist                                             findPwm->second);
476ddf25d17SHoward Chiu                     if (!findPwmPath(directory, pwm, pwmPath))
477ddf25d17SHoward Chiu                     {
478ddf25d17SHoward Chiu                         std::cerr << "Connector for " << sensorName
479ddf25d17SHoward Chiu                                   << " no pwm channel found!\n";
480ddf25d17SHoward Chiu                         continue;
481ddf25d17SHoward Chiu                     }
482ddf25d17SHoward Chiu 
483779c96a2SPatrick Williams                     fs::path pwmEnableFile = "pwm" + std::to_string(pwm + 1) +
484779c96a2SPatrick Williams                                              "_enable";
485bb67932aSEd Tanous                     fs::path enablePath = pwmPath.parent_path() / pwmEnableFile;
486ddf25d17SHoward Chiu                     enablePwm(enablePath);
487ddf25d17SHoward Chiu 
48849a8ccd6SJames Feist                     /* use pwm name override if found in configuration else
48949a8ccd6SJames Feist                      * use default */
490d320a2e1SJason Ling                     auto findOverride = connector->second.find("PwmName");
491d320a2e1SJason Ling                     if (findOverride != connector->second.end())
492d320a2e1SJason Ling                     {
493d320a2e1SJason Ling                         pwmName = std::visit(VariantToStringVisitor(),
494d320a2e1SJason Ling                                              findOverride->second);
495d320a2e1SJason Ling                     }
496d320a2e1SJason Ling                     else
497d320a2e1SJason Ling                     {
498d320a2e1SJason Ling                         pwmName = "Pwm_" + std::to_string(pwm + 1);
499d320a2e1SJason Ling                     }
5003291b9c7SJie Yang 
5013291b9c7SJie Yang                     // Check PWM sensor mutability
5023291b9c7SJie Yang                     auto findMutable = connector->second.find("Mutable");
5033291b9c7SJie Yang                     if (findMutable != connector->second.end())
5043291b9c7SJie Yang                     {
5052049bd26SEd Tanous                         const auto* ptrMutable =
5063291b9c7SJie Yang                             std::get_if<bool>(&(findMutable->second));
5072049bd26SEd Tanous                         if (ptrMutable != nullptr)
5083291b9c7SJie Yang                         {
5093291b9c7SJie Yang                             isValueMutable = *ptrMutable;
5103291b9c7SJie Yang                         }
5113291b9c7SJie Yang                     }
5128e94c204SJames Feist                 }
51349a8ccd6SJames Feist                 else
51449a8ccd6SJames Feist                 {
51549a8ccd6SJames Feist                     std::cerr << "Connector for " << sensorName
51649a8ccd6SJames Feist                               << " missing pwm!\n";
51749a8ccd6SJames Feist                 }
51849a8ccd6SJames Feist 
51949a8ccd6SJames Feist                 auto findLED = connector->second.find("LED");
52049a8ccd6SJames Feist                 if (findLED != connector->second.end())
52149a8ccd6SJames Feist                 {
5222049bd26SEd Tanous                     const auto* ledName =
5232049bd26SEd Tanous                         std::get_if<std::string>(&(findLED->second));
52449a8ccd6SJames Feist                     if (ledName == nullptr)
52549a8ccd6SJames Feist                     {
526bb67932aSEd Tanous                         std::cerr << "Wrong format for LED of " << sensorName
527bb67932aSEd Tanous                                   << "\n";
52849a8ccd6SJames Feist                     }
52949a8ccd6SJames Feist                     else
53049a8ccd6SJames Feist                     {
53149a8ccd6SJames Feist                         led = *ledName;
53249a8ccd6SJames Feist                     }
53349a8ccd6SJames Feist                 }
53449a8ccd6SJames Feist             }
53549a8ccd6SJames Feist 
53649a8ccd6SJames Feist             findLimits(limits, baseConfiguration);
5375170fe63SJosh Lehan 
5389c47bd7eSJustin Ledford             enableFanInput(path);
5399c47bd7eSJustin Ledford 
5405170fe63SJosh Lehan             auto& tachSensor = tachSensors[sensorName];
5415170fe63SJosh Lehan             tachSensor = nullptr;
5425170fe63SJosh Lehan             tachSensor = std::make_shared<TachSensor>(
54349a8ccd6SJames Feist                 path.string(), baseType, objectServer, dbusConnection,
54449a8ccd6SJames Feist                 std::move(presenceSensor), redundancy, io, sensorName,
545bb67932aSEd Tanous                 std::move(sensorThresholds), *interfacePath, limits, powerState,
546bb67932aSEd Tanous                 led);
5475170fe63SJosh Lehan             tachSensor->setupRead();
54877b3add2SYong Zhao 
549d05867c0SZhikui Ren             if (!pwmPath.empty() && fs::exists(pwmPath) &&
5502049bd26SEd Tanous                 (pwmSensors.count(pwmPath) == 0U))
55177b3add2SYong Zhao             {
55277b3add2SYong Zhao                 pwmSensors[pwmPath] = std::make_unique<PwmSensor>(
55377b3add2SYong Zhao                     pwmName, pwmPath, dbusConnection, objectServer,
5543291b9c7SJie Yang                     *interfacePath, "Fan", isValueMutable);
5556714a25aSJames Feist             }
55677b3add2SYong Zhao         }
55777b3add2SYong Zhao 
558d540741eSKuiying Wang         createRedundancySensor(tachSensors, dbusConnection, objectServer);
5598a17c303SEd Tanous     });
560de5e9705SJames Feist     getter->getConfiguration(
561f27a55c7SJames Feist         std::vector<std::string>{sensorTypes.begin(), sensorTypes.end()},
562f27a55c7SJames Feist         retries);
5636714a25aSJames Feist }
5646714a25aSJames Feist 
565b6c0b914SJames Feist int main()
5666714a25aSJames Feist {
5671f978631SEd Tanous     boost::asio::io_context io;
5686714a25aSJames Feist     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
56914ed5e99SEd Tanous     sdbusplus::asio::object_server objectServer(systemBus, true);
57014ed5e99SEd Tanous 
57114ed5e99SEd Tanous     objectServer.add_manager("/xyz/openbmc_project/sensors");
572d9067251SEd Tanous     objectServer.add_manager("/xyz/openbmc_project/control");
573c2f83fedSLei YU     objectServer.add_manager("/xyz/openbmc_project/inventory");
5746714a25aSJames Feist     systemBus->request_name("xyz.openbmc_project.FanSensor");
5755170fe63SJosh Lehan     boost::container::flat_map<std::string, std::shared_ptr<TachSensor>>
5766714a25aSJames Feist         tachSensors;
5776714a25aSJames Feist     boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
5786714a25aSJames Feist         pwmSensors;
5795591cf08SJames Feist     auto sensorsChanged =
5805591cf08SJames Feist         std::make_shared<boost::container::flat_set<std::string>>();
5816714a25aSJames Feist 
58283db50caSEd Tanous     boost::asio::post(io, [&]() {
5836714a25aSJames Feist         createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
5846714a25aSJames Feist                       nullptr);
5856714a25aSJames Feist     });
5866714a25aSJames Feist 
5879b4a20e9SEd Tanous     boost::asio::steady_timer filterTimer(io);
58892f8f515SPatrick Williams     std::function<void(sdbusplus::message_t&)> eventHandler =
58992f8f515SPatrick Williams         [&](sdbusplus::message_t& message) {
5906714a25aSJames Feist         if (message.is_method_error())
5916714a25aSJames Feist         {
5926714a25aSJames Feist             std::cerr << "callback method error\n";
5936714a25aSJames Feist             return;
5946714a25aSJames Feist         }
5956714a25aSJames Feist         sensorsChanged->insert(message.get_path());
5966714a25aSJames Feist         // this implicitly cancels the timer
59783db50caSEd Tanous         filterTimer.expires_after(std::chrono::seconds(1));
5986714a25aSJames Feist 
5996714a25aSJames Feist         filterTimer.async_wait([&](const boost::system::error_code& ec) {
6006714a25aSJames Feist             if (ec == boost::asio::error::operation_aborted)
6016714a25aSJames Feist             {
6026714a25aSJames Feist                 /* we were canceled*/
6036714a25aSJames Feist                 return;
6046714a25aSJames Feist             }
6058a57ec09SEd Tanous             if (ec)
6066714a25aSJames Feist             {
6076714a25aSJames Feist                 std::cerr << "timer error\n";
6086714a25aSJames Feist                 return;
6096714a25aSJames Feist             }
610bb67932aSEd Tanous             createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
611bb67932aSEd Tanous                           sensorsChanged, 5);
6126714a25aSJames Feist         });
6136714a25aSJames Feist     };
6146714a25aSJames Feist 
615214d9717SZev Weiss     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
616214d9717SZev Weiss         setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler);
6176714a25aSJames Feist 
618dc6c55f3SJames Feist     // redundancy sensor
61992f8f515SPatrick Williams     std::function<void(sdbusplus::message_t&)> redundancyHandler =
62092f8f515SPatrick Williams         [&tachSensors, &systemBus, &objectServer](sdbusplus::message_t&) {
621dc6c55f3SJames Feist         createRedundancySensor(tachSensors, systemBus, objectServer);
622dc6c55f3SJames Feist     };
62392f8f515SPatrick Williams     auto match = std::make_unique<sdbusplus::bus::match_t>(
62492f8f515SPatrick Williams         static_cast<sdbusplus::bus_t&>(*systemBus),
625dc6c55f3SJames Feist         "type='signal',member='PropertiesChanged',path_namespace='" +
626dc6c55f3SJames Feist             std::string(inventoryPath) + "',arg0namespace='" +
627dc6c55f3SJames Feist             redundancyConfiguration + "'",
628b6c0b914SJames Feist         std::move(redundancyHandler));
629dc6c55f3SJames Feist     matches.emplace_back(std::move(match));
630dc6c55f3SJames Feist 
6311263c3daSBruce Lee     setupManufacturingModeMatch(*systemBus);
6326714a25aSJames Feist     io.run();
6338685b17aSZhikui Ren     return 0;
6346714a25aSJames Feist }
635