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