blob: d8eb6e7ab9849cbf9bc5af20b2648ec9c02abcb9 [file] [log] [blame]
James Feist6714a252018-09-10 15:26:18 -07001/*
2// Copyright (c) 2017 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include <PwmSensor.hpp>
18#include <TachSensor.hpp>
19#include <Utils.hpp>
20#include <VariantVisitors.hpp>
21#include <boost/algorithm/string/predicate.hpp>
22#include <boost/algorithm/string/replace.hpp>
23#include <boost/container/flat_set.hpp>
24#include <boost/lexical_cast.hpp>
James Feist24f02f22019-04-15 11:05:39 -070025#include <filesystem>
James Feist6714a252018-09-10 15:26:18 -070026#include <fstream>
27#include <regex>
28#include <sdbusplus/asio/connection.hpp>
29#include <sdbusplus/asio/object_server.hpp>
30
31static constexpr bool DEBUG = false;
32
James Feistcf3bce62019-01-08 10:07:19 -080033namespace fs = std::filesystem;
James Feist3eb82622019-02-08 13:10:22 -080034
Peter Lundgren8843b622019-09-12 10:33:41 -070035static constexpr std::array<const char*, 3> sensorTypes = {
James Feist95b079b2018-11-21 09:28:00 -080036 "xyz.openbmc_project.Configuration.AspeedFan",
Peter Lundgren8843b622019-09-12 10:33:41 -070037 "xyz.openbmc_project.Configuration.I2CFan",
38 "xyz.openbmc_project.Configuration.NuvotonFan"};
James Feistdc6c55f2018-10-31 12:53:20 -070039constexpr const char* redundancyConfiguration =
40 "xyz.openbmc_project.Configuration.FanRedundancy";
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070041static std::regex inputRegex(R"(fan(\d+)_input)");
James Feist6714a252018-09-10 15:26:18 -070042
James Feist95b079b2018-11-21 09:28:00 -080043enum class FanTypes
44{
45 aspeed,
Peter Lundgren8843b622019-09-12 10:33:41 -070046 i2c,
47 nuvoton
James Feist95b079b2018-11-21 09:28:00 -080048};
49
James Feistdc6c55f2018-10-31 12:53:20 -070050// todo: power supply fan redundancy
James Feist7b18b1e2019-05-14 13:42:09 -070051std::optional<RedundancySensor> systemRedundancy;
James Feist95b079b2018-11-21 09:28:00 -080052
53FanTypes getFanType(const fs::path& parentPath)
54{
55 fs::path linkPath = parentPath / "device";
56 std::string canonical = fs::read_symlink(linkPath);
57 if (boost::ends_with(canonical, "1e786000.pwm-tacho-controller"))
58 {
59 return FanTypes::aspeed;
60 }
Peter Lundgren8843b622019-09-12 10:33:41 -070061 else if (boost::ends_with(canonical, "f0103000.pwm-fan-controller"))
62 {
63 return FanTypes::nuvoton;
64 }
James Feist95b079b2018-11-21 09:28:00 -080065 // todo: will we need to support other types?
66 return FanTypes::i2c;
67}
James Feistdc6c55f2018-10-31 12:53:20 -070068
James Feist6714a252018-09-10 15:26:18 -070069void createSensors(
70 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
71 boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>&
72 tachSensors,
73 boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>&
74 pwmSensors,
75 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
76 const std::unique_ptr<boost::container::flat_set<std::string>>&
77 sensorsChanged)
78{
79 bool firstScan = sensorsChanged == nullptr;
80 // use new data the first time, then refresh
81 ManagedObjectType sensorConfigurations;
82 bool useCache = false;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070083 for (const char* type : sensorTypes)
James Feist6714a252018-09-10 15:26:18 -070084 {
85 if (!getSensorConfiguration(type, dbusConnection, sensorConfigurations,
86 useCache))
87 {
88 std::cerr << "error communicating to entity manager\n";
89 return;
90 }
91 useCache = true;
92 }
93 std::vector<fs::path> paths;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070094 if (!findFiles(fs::path("/sys/class/hwmon"), R"(fan\d+_input)", paths))
James Feist6714a252018-09-10 15:26:18 -070095 {
96 std::cerr << "No temperature sensors in system\n";
97 return;
98 }
99
James Feist82bac4c2019-03-11 11:16:53 -0700100 std::vector<std::pair<uint8_t, std::string>> pwmNumbers;
James Feist8e94c202019-02-12 10:09:23 -0800101
James Feist6714a252018-09-10 15:26:18 -0700102 // iterate through all found fan sensors, and try to match them with
103 // configuration
James Feist95b079b2018-11-21 09:28:00 -0800104 for (const auto& path : paths)
James Feist6714a252018-09-10 15:26:18 -0700105 {
106 std::smatch match;
107 std::string pathStr = path.string();
108
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700109 std::regex_search(pathStr, match, inputRegex);
James Feist6714a252018-09-10 15:26:18 -0700110 std::string indexStr = *(match.begin() + 1);
111
112 auto directory = path.parent_path();
James Feist95b079b2018-11-21 09:28:00 -0800113 FanTypes fanType = getFanType(directory);
114 size_t bus = 0;
115 size_t address = 0;
116 if (fanType == FanTypes::i2c)
117 {
118 std::string link =
119 fs::read_symlink(directory / "device").filename();
120
121 size_t findDash = link.find("-");
122 if (findDash == std::string::npos || link.size() <= findDash + 1)
123 {
124 std::cerr << "Error finding device from symlink";
125 }
126 bus = std::stoi(link.substr(0, findDash));
127 address = std::stoi(link.substr(findDash + 1), nullptr, 16);
128 }
James Feist6714a252018-09-10 15:26:18 -0700129 // convert to 0 based
130 size_t index = std::stoul(indexStr) - 1;
131
132 const char* baseType;
133 const SensorData* sensorData = nullptr;
134 const std::string* interfacePath = nullptr;
James Feist87d713a2018-12-06 16:06:24 -0800135 const SensorBaseConfiguration* baseConfiguration = nullptr;
James Feist6714a252018-09-10 15:26:18 -0700136 for (const std::pair<sdbusplus::message::object_path, SensorData>&
137 sensor : sensorConfigurations)
138 {
139 // find the base of the configuration to see if indexes match
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700140 for (const char* type : sensorTypes)
James Feist6714a252018-09-10 15:26:18 -0700141 {
142 auto sensorBaseFind = sensor.second.find(type);
143 if (sensorBaseFind != sensor.second.end())
144 {
145 baseConfiguration = &(*sensorBaseFind);
146 interfacePath = &(sensor.first.str);
147 baseType = type;
148 break;
149 }
150 }
151 if (baseConfiguration == nullptr)
152 {
153 continue;
154 }
James Feist6714a252018-09-10 15:26:18 -0700155 auto findIndex = baseConfiguration->second.find("Index");
156 if (findIndex == baseConfiguration->second.end())
157 {
158 std::cerr << baseConfiguration->first << " missing index\n";
159 continue;
160 }
James Feist3eb82622019-02-08 13:10:22 -0800161 unsigned int configIndex =
162 std::visit(VariantToUnsignedIntVisitor(), findIndex->second);
James Feist6714a252018-09-10 15:26:18 -0700163 if (configIndex != index)
164 {
165 continue;
166 }
Peter Lundgren8843b622019-09-12 10:33:41 -0700167 if (fanType == FanTypes::aspeed || fanType == FanTypes::nuvoton)
James Feist6714a252018-09-10 15:26:18 -0700168 {
Peter Lundgren8843b622019-09-12 10:33:41 -0700169 // there will be only 1 aspeed or nuvoton sensor object in
170 // sysfs, we found the fan
James Feist6714a252018-09-10 15:26:18 -0700171 sensorData = &(sensor.second);
172 break;
173 }
James Feistb6c0b912019-07-09 12:21:44 -0700174 else if (baseType ==
175 std::string("xyz.openbmc_project.Configuration.I2CFan"))
James Feist95b079b2018-11-21 09:28:00 -0800176 {
177 auto findBus = baseConfiguration->second.find("Bus");
178 auto findAddress = baseConfiguration->second.find("Address");
179 if (findBus == baseConfiguration->second.end() ||
180 findAddress == baseConfiguration->second.end())
181 {
182 std::cerr << baseConfiguration->first
183 << " missing bus or address\n";
184 continue;
185 }
James Feist3eb82622019-02-08 13:10:22 -0800186 unsigned int configBus =
187 std::visit(VariantToUnsignedIntVisitor(), findBus->second);
188 unsigned int configAddress = std::visit(
James Feist95b079b2018-11-21 09:28:00 -0800189 VariantToUnsignedIntVisitor(), findAddress->second);
190
Jae Hyun Yoo84e9e662019-04-22 13:30:42 -0700191 if (configBus == bus && configAddress == address)
James Feist95b079b2018-11-21 09:28:00 -0800192 {
193 sensorData = &(sensor.second);
194 break;
195 }
196 }
James Feist6714a252018-09-10 15:26:18 -0700197 }
198 if (sensorData == nullptr)
199 {
200 std::cerr << "failed to find match for " << path.string() << "\n";
201 continue;
202 }
203
204 auto findSensorName = baseConfiguration->second.find("Name");
205 if (findSensorName == baseConfiguration->second.end())
206 {
207 std::cerr << "could not determine configuration name for "
208 << path.string() << "\n";
209 continue;
210 }
James Feist3eb82622019-02-08 13:10:22 -0800211 std::string sensorName = std::get<std::string>(findSensorName->second);
James Feist6714a252018-09-10 15:26:18 -0700212 // on rescans, only update sensors we were signaled by
213 auto findSensor = tachSensors.find(sensorName);
214 if (!firstScan && findSensor != tachSensors.end())
215 {
216 bool found = false;
217 for (auto it = sensorsChanged->begin(); it != sensorsChanged->end();
218 it++)
219 {
220 if (boost::ends_with(*it, findSensor->second->name))
221 {
222 sensorsChanged->erase(it);
223 findSensor->second = nullptr;
224 found = true;
225 break;
226 }
227 }
228 if (!found)
229 {
230 continue;
231 }
232 }
233 std::vector<thresholds::Threshold> sensorThresholds;
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700234 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
James Feist6714a252018-09-10 15:26:18 -0700235 {
236 std::cerr << "error populating thresholds for " << sensorName
237 << "\n";
238 }
239
James Feist7bc2bab2018-10-26 14:09:45 -0700240 auto presenceConfig =
241 sensorData->find(baseType + std::string(".Presence"));
242
243 std::unique_ptr<PresenceSensor> presenceSensor(nullptr);
244
245 // presence sensors are optional
246 if (presenceConfig != sensorData->end())
247 {
248 auto findIndex = presenceConfig->second.find("Index");
249 auto findPolarity = presenceConfig->second.find("Polarity");
250
251 if (findIndex == presenceConfig->second.end() ||
252 findPolarity == presenceConfig->second.end())
253 {
254 std::cerr << "Malformed Presence Configuration\n";
255 }
256 else
257 {
James Feist3eb82622019-02-08 13:10:22 -0800258 size_t index = std::get<uint64_t>(findIndex->second);
James Feist7bc2bab2018-10-26 14:09:45 -0700259 bool inverted =
James Feist3eb82622019-02-08 13:10:22 -0800260 std::get<std::string>(findPolarity->second) == "Low";
James Feist7b18b1e2019-05-14 13:42:09 -0700261 presenceSensor = std::make_unique<PresenceSensor>(
262 index, inverted, io, sensorName);
James Feist7bc2bab2018-10-26 14:09:45 -0700263 }
264 }
James Feist7b18b1e2019-05-14 13:42:09 -0700265 std::optional<RedundancySensor>* redundancy = nullptr;
James Feist95b079b2018-11-21 09:28:00 -0800266 if (fanType == FanTypes::aspeed)
267 {
James Feist7b18b1e2019-05-14 13:42:09 -0700268 redundancy = &systemRedundancy;
James Feist95b079b2018-11-21 09:28:00 -0800269 }
James Feist7bc2bab2018-10-26 14:09:45 -0700270
James Feist87d713a2018-12-06 16:06:24 -0800271 constexpr double defaultMaxReading = 25000;
272 constexpr double defaultMinReading = 0;
273 auto limits = std::make_pair(defaultMinReading, defaultMaxReading);
274
275 findLimits(limits, baseConfiguration);
James Feist6714a252018-09-10 15:26:18 -0700276 tachSensors[sensorName] = std::make_unique<TachSensor>(
James Feistce3fca42018-11-21 12:58:24 -0800277 path.string(), baseType, objectServer, dbusConnection,
James Feist95b079b2018-11-21 09:28:00 -0800278 std::move(presenceSensor), redundancy, io, sensorName,
James Feist87d713a2018-12-06 16:06:24 -0800279 std::move(sensorThresholds), *interfacePath, limits);
James Feist8e94c202019-02-12 10:09:23 -0800280
281 auto connector = sensorData->find(baseType + std::string(".Connector"));
282 if (connector != sensorData->end())
283 {
284 auto findPwm = connector->second.find("Pwm");
285 if (findPwm == connector->second.end())
286 {
287 std::cerr << "Connector Missing PWM!\n";
288 continue;
289 }
290
291 size_t pwm =
292 std::visit(VariantToUnsignedIntVisitor(), findPwm->second);
James Feist82bac4c2019-03-11 11:16:53 -0700293 pwmNumbers.emplace_back(pwm, *interfacePath);
James Feist8e94c202019-02-12 10:09:23 -0800294 }
James Feist6714a252018-09-10 15:26:18 -0700295 }
296 std::vector<fs::path> pwms;
James Feist8e94c202019-02-12 10:09:23 -0800297 if (!findFiles(fs::path("/sys/class/hwmon"), R"(pwm\d+$)", pwms))
James Feist6714a252018-09-10 15:26:18 -0700298 {
299 std::cerr << "No pwm in system\n";
300 return;
301 }
302 for (const fs::path& pwm : pwms)
303 {
James Feist95b079b2018-11-21 09:28:00 -0800304 if (pwmSensors.find(pwm) != pwmSensors.end())
305 {
306 continue;
307 }
James Feist82bac4c2019-03-11 11:16:53 -0700308 const std::string* path = nullptr;
309 for (const auto& [index, configPath] : pwmNumbers)
James Feist8e94c202019-02-12 10:09:23 -0800310 {
311 if (boost::ends_with(pwm.string(), std::to_string(index + 1)))
312 {
James Feist82bac4c2019-03-11 11:16:53 -0700313 path = &configPath;
James Feist8e94c202019-02-12 10:09:23 -0800314 break;
315 }
316 }
317
James Feist82bac4c2019-03-11 11:16:53 -0700318 if (path == nullptr)
James Feist8e94c202019-02-12 10:09:23 -0800319 {
320 continue;
321 }
322
James Feist6714a252018-09-10 15:26:18 -0700323 // only add new elements
Cheng C Yang916360b2019-05-07 18:47:16 +0800324 const std::string& sysPath = pwm.string();
325 const std::string& pwmName =
326 "Pwm_" + sysPath.substr(sysPath.find_last_of("pwm") + 1);
James Feist6714a252018-09-10 15:26:18 -0700327 pwmSensors.insert(std::pair<std::string, std::unique_ptr<PwmSensor>>(
Cheng C Yang916360b2019-05-07 18:47:16 +0800328 sysPath, std::make_unique<PwmSensor>(pwmName, sysPath, objectServer,
329 *path)));
James Feist6714a252018-09-10 15:26:18 -0700330 }
331}
332
James Feistdc6c55f2018-10-31 12:53:20 -0700333void createRedundancySensor(
334 const boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>&
335 sensors,
336 std::shared_ptr<sdbusplus::asio::connection> conn,
337 sdbusplus::asio::object_server& objectServer)
338{
339
340 conn->async_method_call(
341 [&objectServer, &sensors](boost::system::error_code& ec,
342 const ManagedObjectType managedObj) {
343 if (ec)
344 {
345 std::cerr << "Error calling entity manager \n";
346 return;
347 }
348 for (const auto& pathPair : managedObj)
349 {
350 for (const auto& interfacePair : pathPair.second)
351 {
352 if (interfacePair.first == redundancyConfiguration)
353 {
354 // currently only support one
355 auto findCount =
356 interfacePair.second.find("AllowedFailures");
357 if (findCount == interfacePair.second.end())
358 {
359 std::cerr << "Malformed redundancy record \n";
360 return;
361 }
362 std::vector<std::string> sensorList;
363
364 for (const auto& sensor : sensors)
365 {
366 sensorList.push_back(
367 "/xyz/openbmc_project/sensors/fan_tach/" +
368 sensor.second->name);
369 }
James Feist7b18b1e2019-05-14 13:42:09 -0700370 systemRedundancy.reset();
371 systemRedundancy.emplace(RedundancySensor(
James Feist3eb82622019-02-08 13:10:22 -0800372 std::get<uint64_t>(findCount->second), sensorList,
James Feist7b18b1e2019-05-14 13:42:09 -0700373 objectServer, pathPair.first));
James Feistdc6c55f2018-10-31 12:53:20 -0700374
375 return;
376 }
377 }
378 }
379 },
380 "xyz.openbmc_project.EntityManager", "/",
381 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
382}
383
James Feistb6c0b912019-07-09 12:21:44 -0700384int main()
James Feist6714a252018-09-10 15:26:18 -0700385{
386 boost::asio::io_service io;
387 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
388 systemBus->request_name("xyz.openbmc_project.FanSensor");
389 sdbusplus::asio::object_server objectServer(systemBus);
390 boost::container::flat_map<std::string, std::unique_ptr<TachSensor>>
391 tachSensors;
392 boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
393 pwmSensors;
394 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
395 std::unique_ptr<boost::container::flat_set<std::string>> sensorsChanged =
396 std::make_unique<boost::container::flat_set<std::string>>();
397
398 io.post([&]() {
399 createSensors(io, objectServer, tachSensors, pwmSensors, systemBus,
400 nullptr);
James Feistdc6c55f2018-10-31 12:53:20 -0700401 createRedundancySensor(tachSensors, systemBus, objectServer);
James Feist6714a252018-09-10 15:26:18 -0700402 });
403
404 boost::asio::deadline_timer filterTimer(io);
405 std::function<void(sdbusplus::message::message&)> eventHandler =
406 [&](sdbusplus::message::message& message) {
407 if (message.is_method_error())
408 {
409 std::cerr << "callback method error\n";
410 return;
411 }
412 sensorsChanged->insert(message.get_path());
413 // this implicitly cancels the timer
414 filterTimer.expires_from_now(boost::posix_time::seconds(1));
415
416 filterTimer.async_wait([&](const boost::system::error_code& ec) {
417 if (ec == boost::asio::error::operation_aborted)
418 {
419 /* we were canceled*/
420 return;
421 }
422 else if (ec)
423 {
424 std::cerr << "timer error\n";
425 return;
426 }
427 createSensors(io, objectServer, tachSensors, pwmSensors,
428 systemBus, sensorsChanged);
429 });
430 };
431
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700432 for (const char* type : sensorTypes)
James Feist6714a252018-09-10 15:26:18 -0700433 {
434 auto match = std::make_unique<sdbusplus::bus::match::match>(
435 static_cast<sdbusplus::bus::bus&>(*systemBus),
436 "type='signal',member='PropertiesChanged',path_namespace='" +
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700437 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
James Feist6714a252018-09-10 15:26:18 -0700438 eventHandler);
439 matches.emplace_back(std::move(match));
440 }
441
James Feistdc6c55f2018-10-31 12:53:20 -0700442 // redundancy sensor
443 std::function<void(sdbusplus::message::message&)> redundancyHandler =
444 [&tachSensors, &systemBus,
James Feistb6c0b912019-07-09 12:21:44 -0700445 &objectServer](sdbusplus::message::message&) {
James Feistdc6c55f2018-10-31 12:53:20 -0700446 createRedundancySensor(tachSensors, systemBus, objectServer);
447 };
448 auto match = std::make_unique<sdbusplus::bus::match::match>(
449 static_cast<sdbusplus::bus::bus&>(*systemBus),
450 "type='signal',member='PropertiesChanged',path_namespace='" +
451 std::string(inventoryPath) + "',arg0namespace='" +
452 redundancyConfiguration + "'",
James Feistb6c0b912019-07-09 12:21:44 -0700453 std::move(redundancyHandler));
James Feistdc6c55f2018-10-31 12:53:20 -0700454 matches.emplace_back(std::move(match));
455
James Feist6714a252018-09-10 15:26:18 -0700456 io.run();
457}