blob: b5bbf81b013ab07617c61dbc3210d50c10ec0dff [file] [log] [blame]
Cheng C Yang209ec562019-03-12 16:37:44 +08001/*
2// Copyright (c) 2019 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*/
Josh Lehan0830c7b2019-10-08 16:35:09 -070016
Patrick Ventureca44b2f2019-10-31 11:02:26 -070017#include "PSUEvent.hpp"
18#include "PSUSensor.hpp"
19#include "Utils.hpp"
20
Patrick Venture96e97db2019-10-31 13:44:38 -070021#include <array>
Cheng C Yang209ec562019-03-12 16:37:44 +080022#include <boost/algorithm/string/predicate.hpp>
23#include <boost/algorithm/string/replace.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070024#include <boost/container/flat_map.hpp>
Cheng C Yang209ec562019-03-12 16:37:44 +080025#include <boost/container/flat_set.hpp>
Josh Lehan74d9bd92019-10-31 08:51:58 -070026#include <cmath>
James Feist24f02f22019-04-15 11:05:39 -070027#include <filesystem>
Cheng C Yang209ec562019-03-12 16:37:44 +080028#include <fstream>
Patrick Venture96e97db2019-10-31 13:44:38 -070029#include <functional>
Cheng C Yang58b2b532019-05-31 00:19:45 +080030#include <iostream>
Cheng C Yang209ec562019-03-12 16:37:44 +080031#include <regex>
32#include <sdbusplus/asio/connection.hpp>
33#include <sdbusplus/asio/object_server.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070034#include <sdbusplus/bus/match.hpp>
35#include <string>
36#include <utility>
37#include <variant>
38#include <vector>
Cheng C Yang209ec562019-03-12 16:37:44 +080039
Josh Lehan49cfba92019-10-08 16:50:42 -070040static constexpr bool DEBUG = false;
41
Devjit Gopalpurde65ef72019-10-30 04:47:35 -070042static constexpr std::array<const char*, 4> sensorTypes = {
Jason Ling5747fab2019-10-02 16:46:23 -070043 "xyz.openbmc_project.Configuration.pmbus",
Josh Lehan0830c7b2019-10-08 16:35:09 -070044 "xyz.openbmc_project.Configuration.MAX34451",
Devjit Gopalpurde65ef72019-10-30 04:47:35 -070045 "xyz.openbmc_project.Configuration.INA230",
Josh Lehan0830c7b2019-10-08 16:35:09 -070046 "xyz.openbmc_project.Configuration.ISL68137"};
Cheng C Yang209ec562019-03-12 16:37:44 +080047
Josh Lehan0830c7b2019-10-08 16:35:09 -070048static std::vector<std::string> pmbusNames = {"pmbus", "pxe1610", "ina219",
49 "ina230", "max34451", "isl68137"};
50
Cheng C Yang209ec562019-03-12 16:37:44 +080051namespace fs = std::filesystem;
52
Cheng C Yang916360b2019-05-07 18:47:16 +080053static boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>>
54 sensors;
Cheng C Yang58b2b532019-05-31 00:19:45 +080055static boost::container::flat_map<std::string, std::unique_ptr<PSUCombineEvent>>
56 combineEvents;
Cheng C Yang916360b2019-05-07 18:47:16 +080057static boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
58 pwmSensors;
59static boost::container::flat_map<std::string, std::string> sensorTable;
60static boost::container::flat_map<std::string, PSUProperty> labelMatch;
61static boost::container::flat_map<std::string, std::string> pwmTable;
Cheng C Yang58b2b532019-05-31 00:19:45 +080062static boost::container::flat_map<std::string, std::vector<std::string>>
63 eventMatch;
64static boost::container::flat_map<std::string, std::vector<std::string>>
65 limitEventMatch;
66
Josh Lehan74d9bd92019-10-31 08:51:58 -070067static std::vector<PSUProperty> psuProperties;
68
Cheng C Yang58b2b532019-05-31 00:19:45 +080069// Function CheckEvent will check each attribute from eventMatch table in the
70// sysfs. If the attributes exists in sysfs, then store the complete path
71// of the attribute into eventPathList.
72void checkEvent(
73 const std::string& directory,
74 const boost::container::flat_map<std::string, std::vector<std::string>>&
75 eventMatch,
76 boost::container::flat_map<std::string, std::vector<std::string>>&
77 eventPathList)
78{
79 for (const auto& match : eventMatch)
80 {
81 const std::vector<std::string>& eventAttrs = match.second;
82 const std::string& eventName = match.first;
83 for (const auto& eventAttr : eventAttrs)
84 {
85 auto eventPath = directory + "/" + eventAttr;
86
87 std::ifstream eventFile(eventPath);
88 if (!eventFile.good())
89 {
90 continue;
91 }
92
93 eventPathList[eventName].push_back(eventPath);
94 }
95 }
96}
97
98// Function checkEventLimits will check all the psu related xxx_input attributes
99// in sysfs to see if xxx_crit_alarm xxx_lcrit_alarm xxx_max_alarm
100// xxx_min_alarm exist, then store the existing paths of the alarm attributes
101// to eventPathList.
102void checkEventLimits(
103 const std::string& sensorPathStr,
104 const boost::container::flat_map<std::string, std::vector<std::string>>&
105 limitEventMatch,
106 boost::container::flat_map<std::string, std::vector<std::string>>&
107 eventPathList)
108{
109 for (const auto& limitMatch : limitEventMatch)
110 {
111 const std::vector<std::string>& limitEventAttrs = limitMatch.second;
112 const std::string& eventName = limitMatch.first;
113 for (const auto& limitEventAttr : limitEventAttrs)
114 {
115 auto limitEventPath =
116 boost::replace_all_copy(sensorPathStr, "input", limitEventAttr);
117 std::ifstream eventFile(limitEventPath);
118 if (!eventFile.good())
119 {
120 continue;
121 }
122 eventPathList[eventName].push_back(limitEventPath);
123 }
124 }
125}
Cheng C Yang916360b2019-05-07 18:47:16 +0800126
127static void checkPWMSensor(const fs::path& sensorPath, std::string& labelHead,
128 const std::string& interfacePath,
129 sdbusplus::asio::object_server& objectServer,
Patrick Venturea2b2d842019-10-11 07:48:47 -0700130 const std::string& psuName)
Cheng C Yang916360b2019-05-07 18:47:16 +0800131{
132 for (const auto& pwmName : pwmTable)
133 {
134 if (pwmName.first != labelHead)
135 {
136 continue;
137 }
138
139 const std::string& sensorPathStr = sensorPath.string();
140 const std::string& pwmPathStr =
141 boost::replace_all_copy(sensorPathStr, "input", "target");
142 std::ifstream pwmFile(pwmPathStr);
143 if (!pwmFile.good())
144 {
145 continue;
146 }
147
148 auto findPWMSensor = pwmSensors.find(psuName + labelHead);
149 if (findPWMSensor != pwmSensors.end())
150 {
151 continue;
152 }
153
154 pwmSensors[psuName + labelHead] = std::make_unique<PwmSensor>(
155 "Pwm_" + psuName + "_" + pwmName.second, pwmPathStr, objectServer,
Cheng C Yang15266a92019-06-12 08:42:52 +0800156 interfacePath + "_" + pwmName.second);
Cheng C Yang916360b2019-05-07 18:47:16 +0800157 }
158}
159
160void createSensors(boost::asio::io_service& io,
161 sdbusplus::asio::object_server& objectServer,
162 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
Cheng C Yang209ec562019-03-12 16:37:44 +0800163{
164
165 ManagedObjectType sensorConfigs;
Josh Lehan49cfba92019-10-08 16:50:42 -0700166 int numCreated = 0;
Cheng C Yang209ec562019-03-12 16:37:44 +0800167 bool useCache = false;
168
Cheng C Yang58b2b532019-05-31 00:19:45 +0800169 // TODO may need only modify the ones that need to be changed.
170 sensors.clear();
Cheng C Yang92498eb2019-09-26 21:59:25 +0800171 combineEvents.clear();
Cheng C Yang209ec562019-03-12 16:37:44 +0800172 for (const char* type : sensorTypes)
173 {
174 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
175 useCache))
176 {
177 std::cerr << "error get sensor config from entity manager\n";
178 return;
179 }
180 useCache = true;
181 }
182
183 std::vector<fs::path> pmbusPaths;
184 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
185 {
186 std::cerr << "No PSU sensors in system\n";
187 return;
188 }
189
190 boost::container::flat_set<std::string> directories;
191 for (const auto& pmbusPath : pmbusPaths)
192 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800193 boost::container::flat_map<std::string, std::vector<std::string>>
194 eventPathList;
195
196 std::ifstream nameFile(pmbusPath);
197 if (!nameFile.good())
198 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700199 std::cerr << "Failure finding pmbus path " << pmbusPath << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800200 continue;
201 }
202
203 std::string pmbusName;
204 std::getline(nameFile, pmbusName);
205 nameFile.close();
Vijay Khemka996bad12019-05-28 15:15:16 -0700206
207 if (std::find(pmbusNames.begin(), pmbusNames.end(), pmbusName) ==
208 pmbusNames.end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800209 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700210 // To avoid this error message, add your driver name to
211 // the pmbusNames vector at the top of this file.
212 std::cerr << "Driver name " << pmbusName
213 << " not found in sensor whitelist\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800214 continue;
215 }
216
217 const std::string* psuName;
Cheng C Yang209ec562019-03-12 16:37:44 +0800218 auto directory = pmbusPath.parent_path();
219
220 auto ret = directories.insert(directory.string());
221 if (!ret.second)
222 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700223 std::cerr << "Duplicate path " << directory.string() << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800224 continue; // check if path has already been searched
Cheng C Yang209ec562019-03-12 16:37:44 +0800225 }
226
James Feistb6c0b912019-07-09 12:21:44 -0700227 fs::path device = directory / "device";
Cheng C Yang209ec562019-03-12 16:37:44 +0800228 std::string deviceName = fs::canonical(device).stem();
229 auto findHyphen = deviceName.find("-");
230 if (findHyphen == std::string::npos)
231 {
232 std::cerr << "found bad device" << deviceName << "\n";
233 continue;
234 }
235 std::string busStr = deviceName.substr(0, findHyphen);
236 std::string addrStr = deviceName.substr(findHyphen + 1);
237
238 size_t bus = 0;
239 size_t addr = 0;
240
241 try
242 {
243 bus = std::stoi(busStr);
244 addr = std::stoi(addrStr, 0, 16);
245 }
James Feistb6c0b912019-07-09 12:21:44 -0700246 catch (std::invalid_argument&)
Cheng C Yang209ec562019-03-12 16:37:44 +0800247 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700248 std::cerr << "Error parsing bus " << busStr << " addr " << addrStr
249 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800250 continue;
251 }
252
Cheng C Yang209ec562019-03-12 16:37:44 +0800253 const std::pair<std::string, boost::container::flat_map<
254 std::string, BasicVariantType>>*
255 baseConfig = nullptr;
256 const SensorData* sensorData = nullptr;
257 const std::string* interfacePath = nullptr;
258 const char* sensorType = nullptr;
259
260 for (const std::pair<sdbusplus::message::object_path, SensorData>&
261 sensor : sensorConfigs)
262 {
263 sensorData = &(sensor.second);
264 for (const char* type : sensorTypes)
265 {
266 auto sensorBase = sensorData->find(type);
267 if (sensorBase != sensorData->end())
268 {
269 baseConfig = &(*sensorBase);
270 sensorType = type;
271 break;
272 }
273 }
274 if (baseConfig == nullptr)
275 {
276 std::cerr << "error finding base configuration for "
277 << deviceName << "\n";
278 continue;
279 }
280
281 auto configBus = baseConfig->second.find("Bus");
282 auto configAddress = baseConfig->second.find("Address");
283
284 if (configBus == baseConfig->second.end() ||
285 configAddress == baseConfig->second.end())
286 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800287 std::cerr << "error finding necessary entry in configuration\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800288 continue;
289 }
290
Cheng C Yang58b2b532019-05-31 00:19:45 +0800291 const uint64_t* confBus;
292 const uint64_t* confAddr;
293 if (!(confBus = std::get_if<uint64_t>(&(configBus->second))) ||
294 !(confAddr = std::get_if<uint64_t>(&(configAddress->second))))
295 {
296 std::cerr
Josh Lehan49cfba92019-10-08 16:50:42 -0700297 << "Cannot get bus or address, invalid configuration\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800298 continue;
299 }
300
301 if ((*confBus != bus) || (*confAddr != addr))
Cheng C Yang209ec562019-03-12 16:37:44 +0800302 {
Josh Lehan432d1ed2019-10-16 12:23:31 -0700303 std::cerr << "Configuration skipping " << *confBus << "-"
304 << *confAddr << " because not " << bus << "-" << addr
305 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800306 continue;
307 }
308
309 interfacePath = &(sensor.first.str);
310 break;
311 }
312 if (interfacePath == nullptr)
313 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700314 // To avoid this error message, add your export map entry,
315 // from Entity Manager, to sensorTypes at the top of this file.
Cheng C Yang209ec562019-03-12 16:37:44 +0800316 std::cerr << "failed to find match for " << deviceName << "\n";
317 continue;
318 }
319
Cheng C Yange50345b2019-04-02 17:26:15 +0800320 auto findPSUName = baseConfig->second.find("Name");
321 if (findPSUName == baseConfig->second.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800322 {
323 std::cerr << "could not determine configuration name for "
324 << deviceName << "\n";
325 continue;
326 }
327
Cheng C Yang58b2b532019-05-31 00:19:45 +0800328 if (!(psuName = std::get_if<std::string>(&(findPSUName->second))))
329 {
330 std::cerr << "Cannot find psu name, invalid configuration\n";
331 continue;
332 }
333 checkEvent(directory.string(), eventMatch, eventPathList);
334
Vijay Khemka996bad12019-05-28 15:15:16 -0700335 /* Check if there are more sensors in the same interface */
336 int i = 1;
337 std::vector<std::string> psuNames;
338 do
339 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700340 // Individual string fields: Name, Name1, Name2, Name3, ...
Vijay Khemka996bad12019-05-28 15:15:16 -0700341 psuNames.push_back(std::get<std::string>(findPSUName->second));
342 findPSUName = baseConfig->second.find("Name" + std::to_string(i++));
343 } while (findPSUName != baseConfig->second.end());
344
Cheng C Yange50345b2019-04-02 17:26:15 +0800345 std::vector<fs::path> sensorPaths;
James Feistb6c0b912019-07-09 12:21:44 -0700346 if (!findFiles(directory, R"(\w\d+_input$)", sensorPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800347 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800348 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800349 continue;
350 }
351
Vijay Khemka996bad12019-05-28 15:15:16 -0700352 /* Find array of labels to be exposed if it is defined in config */
353 std::vector<std::string> findLabels;
354 auto findLabelObj = baseConfig->second.find("Labels");
355 if (findLabelObj != baseConfig->second.end())
356 {
357 findLabels =
358 std::get<std::vector<std::string>>(findLabelObj->second);
359 }
360
Jason Ling5747fab2019-10-02 16:46:23 -0700361 std::regex sensorNameRegEx("([A-Za-z]+)[0-9]*_");
362 std::smatch matches;
363
Cheng C Yange50345b2019-04-02 17:26:15 +0800364 for (const auto& sensorPath : sensorPaths)
Cheng C Yang209ec562019-03-12 16:37:44 +0800365 {
Cheng C Yang209ec562019-03-12 16:37:44 +0800366
Cheng C Yange50345b2019-04-02 17:26:15 +0800367 std::string labelHead;
368 std::string sensorPathStr = sensorPath.string();
369 std::string sensorNameStr = sensorPath.filename();
Jason Ling5747fab2019-10-02 16:46:23 -0700370 std::string sensorNameSubStr{""};
371 if (std::regex_search(sensorNameStr, matches, sensorNameRegEx))
372 {
373 sensorNameSubStr = matches[1];
374 }
375 else
376 {
377 std::cerr << "Couldn't extract the alpha prefix from "
378 << sensorNameStr;
379 continue;
380 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800381
Cheng C Yangecba9de2019-09-12 23:41:50 +0800382 auto labelPath =
383 boost::replace_all_copy(sensorPathStr, "input", "label");
384 std::ifstream labelFile(labelPath);
385 if (!labelFile.good())
Cheng C Yang209ec562019-03-12 16:37:44 +0800386 {
Josh Lehan432d1ed2019-10-16 12:23:31 -0700387 std::cerr << "Input file " << sensorPath
388 << " has no corresponding label file\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800389 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800390 }
391 else
392 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800393 std::string label;
394 std::getline(labelFile, label);
395 labelFile.close();
Cheng C Yange50345b2019-04-02 17:26:15 +0800396 auto findSensor = sensors.find(label);
397 if (findSensor != sensors.end())
398 {
399 continue;
400 }
401
402 labelHead = label.substr(0, label.find(" "));
403 }
404
Josh Lehan74d9bd92019-10-31 08:51:58 -0700405 if constexpr (DEBUG)
406 {
407 std::cerr << "Sensor type=\"" << sensorNameSubStr
408 << "\" label=\"" << labelHead << "\"\n";
409 }
410
Cheng C Yang916360b2019-05-07 18:47:16 +0800411 checkPWMSensor(sensorPath, labelHead, *interfacePath, objectServer,
Vijay Khemka996bad12019-05-28 15:15:16 -0700412 psuNames[0]);
Cheng C Yang916360b2019-05-07 18:47:16 +0800413
Vijay Khemka996bad12019-05-28 15:15:16 -0700414 if (!findLabels.empty())
415 {
416 /* Check if this labelHead is enabled in config file */
417 if (std::find(findLabels.begin(), findLabels.end(),
418 labelHead) == findLabels.end())
419 {
Jason Ling5747fab2019-10-02 16:46:23 -0700420 std::cerr << "couldn't find " << labelHead
421 << " in the Labels list\n";
Vijay Khemka996bad12019-05-28 15:15:16 -0700422 continue;
423 }
424 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800425
Cheng C Yange50345b2019-04-02 17:26:15 +0800426 auto findProperty = labelMatch.find(labelHead);
427 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800428 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700429 if constexpr (DEBUG)
430 {
431 std::cerr << "Could not find matching default property for "
432 << labelHead << "\n";
433 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800434 continue;
435 }
436
Josh Lehan74d9bd92019-10-31 08:51:58 -0700437 // Protect the hardcoded labelMatch list from changes,
438 // by making a copy and modifying that instead.
439 // Avoid bleedthrough of one device's customizations to
440 // the next device, as each should be independently customizable.
441 psuProperties.push_back(findProperty->second);
442 auto psuProperty = psuProperties.rbegin();
443
444 // Use label head as prefix for reading from config file,
445 // example if temp1: temp1_Name, temp1_Scale, temp1_Min, ...
446 std::string keyName = labelHead + "_Name";
447 std::string keyScale = labelHead + "_Scale";
448 std::string keyMin = labelHead + "_Min";
449 std::string keyMax = labelHead + "_Max";
450
451 bool customizedName = false;
452 auto findCustomName = baseConfig->second.find(keyName);
453 if (findCustomName != baseConfig->second.end())
Josh Lehan432d1ed2019-10-16 12:23:31 -0700454 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700455 try
456 {
457 psuProperty->labelTypeName = std::visit(
458 VariantToStringVisitor(), findCustomName->second);
459 }
460 catch (std::invalid_argument&)
461 {
462 std::cerr << "Unable to parse " << keyName << "\n";
463 continue;
464 }
465
466 // All strings are valid, including empty string
467 customizedName = true;
468 }
469
470 bool customizedScale = false;
471 auto findCustomScale = baseConfig->second.find(keyScale);
472 if (findCustomScale != baseConfig->second.end())
473 {
474 try
475 {
476 psuProperty->sensorScaleFactor = std::visit(
477 VariantToUnsignedIntVisitor(), findCustomScale->second);
478 }
479 catch (std::invalid_argument&)
480 {
481 std::cerr << "Unable to parse " << keyScale << "\n";
482 continue;
483 }
484
485 // Avoid later division by zero
486 if (psuProperty->sensorScaleFactor > 0)
487 {
488 customizedScale = true;
489 }
490 else
491 {
492 std::cerr << "Unable to accept " << keyScale << "\n";
493 continue;
494 }
495 }
496
497 auto findCustomMin = baseConfig->second.find(keyMin);
498 if (findCustomMin != baseConfig->second.end())
499 {
500 try
501 {
502 psuProperty->minReading = std::visit(
503 VariantToDoubleVisitor(), findCustomMin->second);
504 }
505 catch (std::invalid_argument&)
506 {
507 std::cerr << "Unable to parse " << keyMin << "\n";
508 continue;
509 }
510 }
511
512 auto findCustomMax = baseConfig->second.find(keyMax);
513 if (findCustomMax != baseConfig->second.end())
514 {
515 try
516 {
517 psuProperty->maxReading = std::visit(
518 VariantToDoubleVisitor(), findCustomMax->second);
519 }
520 catch (std::invalid_argument&)
521 {
522 std::cerr << "Unable to parse " << keyMax << "\n";
523 continue;
524 }
525 }
526
527 if (!(psuProperty->minReading < psuProperty->maxReading))
528 {
529 std::cerr << "Min must be less than Max\n";
530 continue;
531 }
532
533 // If the sensor name is being customized by config file,
534 // then prefix/suffix composition becomes not necessary,
535 // and in fact not wanted, because it gets in the way.
536 std::string psuNameFromIndex;
537 if (!customizedName)
538 {
539 /* Find out sensor name index for this label */
540 std::regex rgx("[A-Za-z]+([0-9]+)");
541 int nameIndex{0};
542 if (std::regex_search(labelHead, matches, rgx))
543 {
544 nameIndex = std::stoi(matches[1]);
545
546 // Decrement to preserve alignment, because hwmon
547 // human-readable filenames and labels use 1-based
548 // numbering, but the "Name", "Name1", "Name2", etc. naming
549 // convention (the psuNames vector) uses 0-based numbering.
550 if (nameIndex > 0)
551 {
552 --nameIndex;
553 }
554 }
555 else
556 {
557 nameIndex = 0;
558 }
559
560 if (psuNames.size() <= nameIndex)
561 {
562 std::cerr << "Could not pair " << labelHead
563 << " with a Name field\n";
564 continue;
565 }
566
567 psuNameFromIndex = psuNames[nameIndex];
568
569 if constexpr (DEBUG)
570 {
571 std::cerr << "Sensor label head " << labelHead
572 << " paired with " << psuNameFromIndex
573 << " at index " << nameIndex << "\n";
574 }
Josh Lehan432d1ed2019-10-16 12:23:31 -0700575 }
576
Cheng C Yang58b2b532019-05-31 00:19:45 +0800577 checkEventLimits(sensorPathStr, limitEventMatch, eventPathList);
578
Josh Lehan74d9bd92019-10-31 08:51:58 -0700579 // Similarly, if sensor scaling factor is being customized,
580 // then the below power-of-10 constraint becomes unnecessary,
581 // as config should be able to specify an arbitrary divisor.
582 unsigned int factor = psuProperty->sensorScaleFactor;
583 if (!customizedScale)
Vijay Khemka53ca4442019-07-23 11:03:55 -0700584 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700585 // Preserve existing usage of hardcoded labelMatch table below
586 factor = std::pow(10.0, factor);
Vijay Khemka53ca4442019-07-23 11:03:55 -0700587
Josh Lehan74d9bd92019-10-31 08:51:58 -0700588 /* Change first char of substring to uppercase */
589 char firstChar = sensorNameSubStr[0] - 0x20;
590 std::string strScaleFactor =
591 firstChar + sensorNameSubStr.substr(1) + "ScaleFactor";
592
593 // Preserve existing configs by accepting earlier syntax,
594 // example CurrScaleFactor, PowerScaleFactor, ...
595 auto findScaleFactor = baseConfig->second.find(strScaleFactor);
596 if (findScaleFactor != baseConfig->second.end())
597 {
598 factor = std::visit(VariantToIntVisitor(),
599 findScaleFactor->second);
600 }
601
602 if constexpr (DEBUG)
603 {
604 std::cerr << "Sensor scaling factor " << factor
605 << " string " << strScaleFactor << "\n";
606 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700607 }
608
Vijay Khemka996bad12019-05-28 15:15:16 -0700609 std::vector<thresholds::Threshold> sensorThresholds;
610
James Feist17ab6e02019-06-25 12:28:13 -0700611 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
Cheng C Yange50345b2019-04-02 17:26:15 +0800612 {
James Feist17ab6e02019-06-25 12:28:13 -0700613 std::cerr << "error populating thresholds for "
614 << sensorNameSubStr << "\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800615 }
616
617 auto findSensorType = sensorTable.find(sensorNameSubStr);
618 if (findSensorType == sensorTable.end())
619 {
Jason Ling5747fab2019-10-02 16:46:23 -0700620 std::cerr << sensorNameSubStr
621 << " is not a recognize sensor file\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800622 continue;
623 }
624
Josh Lehan49cfba92019-10-08 16:50:42 -0700625 if constexpr (DEBUG)
626 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700627 std::cerr << "Sensor properties: Name \""
628 << psuProperty->labelTypeName << "\" Scale "
629 << psuProperty->sensorScaleFactor << " Min "
630 << psuProperty->minReading << " Max "
631 << psuProperty->maxReading << "\n";
632 }
633
634 std::string sensorName = psuProperty->labelTypeName;
635 if (customizedName)
636 {
637 if (sensorName.empty())
638 {
639 // Allow selective disabling of an individual sensor,
640 // by customizing its name to an empty string.
641 std::cerr << "Sensor disabled, empty string\n";
642 continue;
643 }
644 }
645 else
646 {
647 // Sensor name not customized, do prefix/suffix composition,
648 // preserving default behavior by using psuNameFromIndex.
649 sensorName =
650 psuNameFromIndex + " " + psuProperty->labelTypeName;
651 }
652
653 if constexpr (DEBUG)
654 {
655 std::cerr << "Sensor name \"" << sensorName << "\" path \""
656 << sensorPathStr << "\" type \"" << sensorType
657 << "\"\n";
Josh Lehan49cfba92019-10-08 16:50:42 -0700658 }
659
Cheng C Yang58b2b532019-05-31 00:19:45 +0800660 sensors[sensorName] = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800661 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800662 sensorName, std::move(sensorThresholds), *interfacePath,
Josh Lehan74d9bd92019-10-31 08:51:58 -0700663 findSensorType->second, factor, psuProperty->maxReading,
664 psuProperty->minReading);
665
666 ++numCreated;
667 if constexpr (DEBUG)
668 {
669 std::cerr << "Created " << numCreated << " sensors so far\n";
670 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800671 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800672
673 // OperationalStatus event
Cheng C Yang92498eb2019-09-26 21:59:25 +0800674 combineEvents[*psuName + "OperationalStatus"] = nullptr;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800675 combineEvents[*psuName + "OperationalStatus"] =
676 std::make_unique<PSUCombineEvent>(
677 objectServer, io, *psuName, eventPathList, "OperationalStatus");
Cheng C Yang209ec562019-03-12 16:37:44 +0800678 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700679
680 if constexpr (DEBUG)
681 {
682 std::cerr << "Created total of " << numCreated << " sensors\n";
683 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800684 return;
685}
686
Cheng C Yang916360b2019-05-07 18:47:16 +0800687void propertyInitialize(void)
Cheng C Yang209ec562019-03-12 16:37:44 +0800688{
Cheng C Yange50345b2019-04-02 17:26:15 +0800689 sensorTable = {{"power", "power/"},
690 {"curr", "current/"},
691 {"temp", "temperature/"},
692 {"in", "voltage/"},
693 {"fan", "fan_tach/"}};
694
695 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
696 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700697 {"pout2", PSUProperty("Output Power", 3000, 0, 6)},
698 {"pout3", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700699 {"power1", PSUProperty("Output Power", 3000, 0, 6)},
Cheng C Yangbdbde962019-04-26 22:50:34 +0800700 {"vin", PSUProperty("Input Voltage", 300, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700701 {"vout1", PSUProperty("Output Voltage", 255, 0, 3)},
702 {"vout2", PSUProperty("Output Voltage", 255, 0, 3)},
703 {"vout3", PSUProperty("Output Voltage", 255, 0, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700704 {"vout4", PSUProperty("Output Voltage", 255, 0, 3)},
705 {"vout5", PSUProperty("Output Voltage", 255, 0, 3)},
706 {"vout6", PSUProperty("Output Voltage", 255, 0, 3)},
707 {"vout7", PSUProperty("Output Voltage", 255, 0, 3)},
708 {"vout8", PSUProperty("Output Voltage", 255, 0, 3)},
709 {"vout9", PSUProperty("Output Voltage", 255, 0, 3)},
710 {"vout10", PSUProperty("Output Voltage", 255, 0, 3)},
711 {"vout11", PSUProperty("Output Voltage", 255, 0, 3)},
712 {"vout12", PSUProperty("Output Voltage", 255, 0, 3)},
713 {"vout13", PSUProperty("Output Voltage", 255, 0, 3)},
714 {"vout14", PSUProperty("Output Voltage", 255, 0, 3)},
715 {"vout15", PSUProperty("Output Voltage", 255, 0, 3)},
716 {"vout16", PSUProperty("Output Voltage", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700717 {"in1", PSUProperty("Output Voltage", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800718 {"iin", PSUProperty("Input Current", 20, 0, 3)},
719 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700720 {"iout2", PSUProperty("Output Current", 255, 0, 3)},
721 {"iout3", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700722 {"curr1", PSUProperty("Output Current", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800723 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700724 {"temp2", PSUProperty("Temperature", 127, -128, 3)},
725 {"temp3", PSUProperty("Temperature", 127, -128, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700726 {"temp4", PSUProperty("Temperature", 127, -128, 3)},
727 {"temp5", PSUProperty("Temperature", 127, -128, 3)},
Cheng C Yang8dbb3952019-05-23 00:03:12 +0800728 {"fan1", PSUProperty("Fan Speed 1", 30000, 0, 0)},
729 {"fan2", PSUProperty("Fan Speed 2", 30000, 0, 0)}};
Cheng C Yang916360b2019-05-07 18:47:16 +0800730
731 pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
Cheng C Yang58b2b532019-05-31 00:19:45 +0800732
733 limitEventMatch = {{"PredictiveFailure", {"max_alarm", "min_alarm"}},
734 {"Failure", {"crit_alarm", "lcrit_alarm"}}};
735
736 eventMatch = {
737 {"PredictiveFailure", {"power1_alarm"}},
738 {"Failure", {"in2_alarm"}},
Cheng C Yang9c45e6b2019-08-13 07:21:32 +0800739 {"ACLost", {"in1_beep"}},
740 {"FanFault", {"fan1_alarm", "fan2_alarm", "fan1_fault", "fan2_fault"}},
741 {"ConfigureError", {"in1_fault"}}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800742}
743
James Feistb6c0b912019-07-09 12:21:44 -0700744int main()
Cheng C Yang209ec562019-03-12 16:37:44 +0800745{
746 boost::asio::io_service io;
747 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
748
749 systemBus->request_name("xyz.openbmc_project.PSUSensor");
750 sdbusplus::asio::object_server objectServer(systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800751 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yang209ec562019-03-12 16:37:44 +0800752
Cheng C Yang916360b2019-05-07 18:47:16 +0800753 propertyInitialize();
Cheng C Yang209ec562019-03-12 16:37:44 +0800754
Cheng C Yang916360b2019-05-07 18:47:16 +0800755 io.post([&]() { createSensors(io, objectServer, systemBus); });
Cheng C Yang209ec562019-03-12 16:37:44 +0800756 boost::asio::deadline_timer filterTimer(io);
757 std::function<void(sdbusplus::message::message&)> eventHandler =
758 [&](sdbusplus::message::message& message) {
759 if (message.is_method_error())
760 {
761 std::cerr << "callback method error\n";
762 return;
763 }
764 filterTimer.expires_from_now(boost::posix_time::seconds(1));
765 filterTimer.async_wait([&](const boost::system::error_code& ec) {
766 if (ec == boost::asio::error::operation_aborted)
767 {
768 return;
769 }
770 else if (ec)
771 {
772 std::cerr << "timer error\n";
773 }
Cheng C Yang916360b2019-05-07 18:47:16 +0800774 createSensors(io, objectServer, systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800775 });
776 };
777
778 for (const char* type : sensorTypes)
779 {
780 auto match = std::make_unique<sdbusplus::bus::match::match>(
781 static_cast<sdbusplus::bus::bus&>(*systemBus),
782 "type='signal',member='PropertiesChanged',path_namespace='" +
783 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
784 eventHandler);
785 matches.emplace_back(std::move(match));
786 }
787 io.run();
788}