blob: eed01689128595d63a42291491b69eb17281f66d [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 Yange50345b2019-04-02 17:26:15 +0800366 std::string labelHead;
367 std::string sensorPathStr = sensorPath.string();
368 std::string sensorNameStr = sensorPath.filename();
Jason Ling5747fab2019-10-02 16:46:23 -0700369 std::string sensorNameSubStr{""};
370 if (std::regex_search(sensorNameStr, matches, sensorNameRegEx))
371 {
Josh Lehan06494452019-10-31 09:49:16 -0700372 // hwmon *_input filename without number:
373 // in, curr, power, temp, ...
Jason Ling5747fab2019-10-02 16:46:23 -0700374 sensorNameSubStr = matches[1];
375 }
376 else
377 {
Josh Lehan06494452019-10-31 09:49:16 -0700378 std::cerr << "Could not extract the alpha prefix from "
Jason Ling5747fab2019-10-02 16:46:23 -0700379 << sensorNameStr;
380 continue;
381 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800382
Cheng C Yangecba9de2019-09-12 23:41:50 +0800383 auto labelPath =
384 boost::replace_all_copy(sensorPathStr, "input", "label");
385 std::ifstream labelFile(labelPath);
386 if (!labelFile.good())
Cheng C Yang209ec562019-03-12 16:37:44 +0800387 {
Josh Lehan432d1ed2019-10-16 12:23:31 -0700388 std::cerr << "Input file " << sensorPath
389 << " has no corresponding label file\n";
Josh Lehan06494452019-10-31 09:49:16 -0700390
391 // hwmon *_input filename with number:
392 // temp1, temp2, temp3, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800393 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800394 }
395 else
396 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800397 std::string label;
398 std::getline(labelFile, label);
399 labelFile.close();
Cheng C Yange50345b2019-04-02 17:26:15 +0800400 auto findSensor = sensors.find(label);
401 if (findSensor != sensors.end())
402 {
403 continue;
404 }
405
Josh Lehan06494452019-10-31 09:49:16 -0700406 // hwmon corresponding *_label file contents:
407 // vin1, vout1, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800408 labelHead = label.substr(0, label.find(" "));
409 }
410
Josh Lehan74d9bd92019-10-31 08:51:58 -0700411 if constexpr (DEBUG)
412 {
413 std::cerr << "Sensor type=\"" << sensorNameSubStr
414 << "\" label=\"" << labelHead << "\"\n";
415 }
416
Cheng C Yang916360b2019-05-07 18:47:16 +0800417 checkPWMSensor(sensorPath, labelHead, *interfacePath, objectServer,
Vijay Khemka996bad12019-05-28 15:15:16 -0700418 psuNames[0]);
Cheng C Yang916360b2019-05-07 18:47:16 +0800419
Vijay Khemka996bad12019-05-28 15:15:16 -0700420 if (!findLabels.empty())
421 {
422 /* Check if this labelHead is enabled in config file */
423 if (std::find(findLabels.begin(), findLabels.end(),
424 labelHead) == findLabels.end())
425 {
Josh Lehan06494452019-10-31 09:49:16 -0700426 std::cerr << "could not find " << labelHead
Jason Ling5747fab2019-10-02 16:46:23 -0700427 << " in the Labels list\n";
Vijay Khemka996bad12019-05-28 15:15:16 -0700428 continue;
429 }
430 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800431
Cheng C Yange50345b2019-04-02 17:26:15 +0800432 auto findProperty = labelMatch.find(labelHead);
433 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800434 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700435 if constexpr (DEBUG)
436 {
437 std::cerr << "Could not find matching default property for "
438 << labelHead << "\n";
439 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800440 continue;
441 }
442
Josh Lehan74d9bd92019-10-31 08:51:58 -0700443 // Protect the hardcoded labelMatch list from changes,
444 // by making a copy and modifying that instead.
445 // Avoid bleedthrough of one device's customizations to
446 // the next device, as each should be independently customizable.
447 psuProperties.push_back(findProperty->second);
448 auto psuProperty = psuProperties.rbegin();
449
450 // Use label head as prefix for reading from config file,
451 // example if temp1: temp1_Name, temp1_Scale, temp1_Min, ...
452 std::string keyName = labelHead + "_Name";
453 std::string keyScale = labelHead + "_Scale";
454 std::string keyMin = labelHead + "_Min";
455 std::string keyMax = labelHead + "_Max";
456
457 bool customizedName = false;
458 auto findCustomName = baseConfig->second.find(keyName);
459 if (findCustomName != baseConfig->second.end())
Josh Lehan432d1ed2019-10-16 12:23:31 -0700460 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700461 try
462 {
463 psuProperty->labelTypeName = std::visit(
464 VariantToStringVisitor(), findCustomName->second);
465 }
466 catch (std::invalid_argument&)
467 {
468 std::cerr << "Unable to parse " << keyName << "\n";
469 continue;
470 }
471
472 // All strings are valid, including empty string
473 customizedName = true;
474 }
475
476 bool customizedScale = false;
477 auto findCustomScale = baseConfig->second.find(keyScale);
478 if (findCustomScale != baseConfig->second.end())
479 {
480 try
481 {
482 psuProperty->sensorScaleFactor = std::visit(
483 VariantToUnsignedIntVisitor(), findCustomScale->second);
484 }
485 catch (std::invalid_argument&)
486 {
487 std::cerr << "Unable to parse " << keyScale << "\n";
488 continue;
489 }
490
491 // Avoid later division by zero
492 if (psuProperty->sensorScaleFactor > 0)
493 {
494 customizedScale = true;
495 }
496 else
497 {
498 std::cerr << "Unable to accept " << keyScale << "\n";
499 continue;
500 }
501 }
502
503 auto findCustomMin = baseConfig->second.find(keyMin);
504 if (findCustomMin != baseConfig->second.end())
505 {
506 try
507 {
508 psuProperty->minReading = std::visit(
509 VariantToDoubleVisitor(), findCustomMin->second);
510 }
511 catch (std::invalid_argument&)
512 {
513 std::cerr << "Unable to parse " << keyMin << "\n";
514 continue;
515 }
516 }
517
518 auto findCustomMax = baseConfig->second.find(keyMax);
519 if (findCustomMax != baseConfig->second.end())
520 {
521 try
522 {
523 psuProperty->maxReading = std::visit(
524 VariantToDoubleVisitor(), findCustomMax->second);
525 }
526 catch (std::invalid_argument&)
527 {
528 std::cerr << "Unable to parse " << keyMax << "\n";
529 continue;
530 }
531 }
532
533 if (!(psuProperty->minReading < psuProperty->maxReading))
534 {
535 std::cerr << "Min must be less than Max\n";
536 continue;
537 }
538
539 // If the sensor name is being customized by config file,
540 // then prefix/suffix composition becomes not necessary,
541 // and in fact not wanted, because it gets in the way.
542 std::string psuNameFromIndex;
543 if (!customizedName)
544 {
545 /* Find out sensor name index for this label */
546 std::regex rgx("[A-Za-z]+([0-9]+)");
547 int nameIndex{0};
548 if (std::regex_search(labelHead, matches, rgx))
549 {
550 nameIndex = std::stoi(matches[1]);
551
552 // Decrement to preserve alignment, because hwmon
553 // human-readable filenames and labels use 1-based
554 // numbering, but the "Name", "Name1", "Name2", etc. naming
555 // convention (the psuNames vector) uses 0-based numbering.
556 if (nameIndex > 0)
557 {
558 --nameIndex;
559 }
560 }
561 else
562 {
563 nameIndex = 0;
564 }
565
566 if (psuNames.size() <= nameIndex)
567 {
568 std::cerr << "Could not pair " << labelHead
569 << " with a Name field\n";
570 continue;
571 }
572
573 psuNameFromIndex = psuNames[nameIndex];
574
575 if constexpr (DEBUG)
576 {
577 std::cerr << "Sensor label head " << labelHead
578 << " paired with " << psuNameFromIndex
579 << " at index " << nameIndex << "\n";
580 }
Josh Lehan432d1ed2019-10-16 12:23:31 -0700581 }
582
Cheng C Yang58b2b532019-05-31 00:19:45 +0800583 checkEventLimits(sensorPathStr, limitEventMatch, eventPathList);
584
Josh Lehan74d9bd92019-10-31 08:51:58 -0700585 // Similarly, if sensor scaling factor is being customized,
586 // then the below power-of-10 constraint becomes unnecessary,
587 // as config should be able to specify an arbitrary divisor.
588 unsigned int factor = psuProperty->sensorScaleFactor;
589 if (!customizedScale)
Vijay Khemka53ca4442019-07-23 11:03:55 -0700590 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700591 // Preserve existing usage of hardcoded labelMatch table below
592 factor = std::pow(10.0, factor);
Vijay Khemka53ca4442019-07-23 11:03:55 -0700593
Josh Lehan74d9bd92019-10-31 08:51:58 -0700594 /* Change first char of substring to uppercase */
595 char firstChar = sensorNameSubStr[0] - 0x20;
596 std::string strScaleFactor =
597 firstChar + sensorNameSubStr.substr(1) + "ScaleFactor";
598
599 // Preserve existing configs by accepting earlier syntax,
600 // example CurrScaleFactor, PowerScaleFactor, ...
601 auto findScaleFactor = baseConfig->second.find(strScaleFactor);
602 if (findScaleFactor != baseConfig->second.end())
603 {
604 factor = std::visit(VariantToIntVisitor(),
605 findScaleFactor->second);
606 }
607
608 if constexpr (DEBUG)
609 {
610 std::cerr << "Sensor scaling factor " << factor
611 << " string " << strScaleFactor << "\n";
612 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700613 }
614
Vijay Khemka996bad12019-05-28 15:15:16 -0700615 std::vector<thresholds::Threshold> sensorThresholds;
616
James Feist17ab6e02019-06-25 12:28:13 -0700617 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
Cheng C Yange50345b2019-04-02 17:26:15 +0800618 {
James Feist17ab6e02019-06-25 12:28:13 -0700619 std::cerr << "error populating thresholds for "
620 << sensorNameSubStr << "\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800621 }
622
623 auto findSensorType = sensorTable.find(sensorNameSubStr);
624 if (findSensorType == sensorTable.end())
625 {
Jason Ling5747fab2019-10-02 16:46:23 -0700626 std::cerr << sensorNameSubStr
Josh Lehan06494452019-10-31 09:49:16 -0700627 << " is not a recognized sensor type\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800628 continue;
629 }
630
Josh Lehan49cfba92019-10-08 16:50:42 -0700631 if constexpr (DEBUG)
632 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700633 std::cerr << "Sensor properties: Name \""
634 << psuProperty->labelTypeName << "\" Scale "
635 << psuProperty->sensorScaleFactor << " Min "
636 << psuProperty->minReading << " Max "
637 << psuProperty->maxReading << "\n";
638 }
639
640 std::string sensorName = psuProperty->labelTypeName;
641 if (customizedName)
642 {
643 if (sensorName.empty())
644 {
645 // Allow selective disabling of an individual sensor,
646 // by customizing its name to an empty string.
647 std::cerr << "Sensor disabled, empty string\n";
648 continue;
649 }
650 }
651 else
652 {
653 // Sensor name not customized, do prefix/suffix composition,
654 // preserving default behavior by using psuNameFromIndex.
655 sensorName =
656 psuNameFromIndex + " " + psuProperty->labelTypeName;
657 }
658
659 if constexpr (DEBUG)
660 {
661 std::cerr << "Sensor name \"" << sensorName << "\" path \""
662 << sensorPathStr << "\" type \"" << sensorType
663 << "\"\n";
Josh Lehan49cfba92019-10-08 16:50:42 -0700664 }
665
Cheng C Yang58b2b532019-05-31 00:19:45 +0800666 sensors[sensorName] = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800667 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800668 sensorName, std::move(sensorThresholds), *interfacePath,
Josh Lehan74d9bd92019-10-31 08:51:58 -0700669 findSensorType->second, factor, psuProperty->maxReading,
670 psuProperty->minReading);
671
672 ++numCreated;
673 if constexpr (DEBUG)
674 {
675 std::cerr << "Created " << numCreated << " sensors so far\n";
676 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800677 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800678
679 // OperationalStatus event
Cheng C Yang92498eb2019-09-26 21:59:25 +0800680 combineEvents[*psuName + "OperationalStatus"] = nullptr;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800681 combineEvents[*psuName + "OperationalStatus"] =
682 std::make_unique<PSUCombineEvent>(
683 objectServer, io, *psuName, eventPathList, "OperationalStatus");
Cheng C Yang209ec562019-03-12 16:37:44 +0800684 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700685
686 if constexpr (DEBUG)
687 {
688 std::cerr << "Created total of " << numCreated << " sensors\n";
689 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800690 return;
691}
692
Cheng C Yang916360b2019-05-07 18:47:16 +0800693void propertyInitialize(void)
Cheng C Yang209ec562019-03-12 16:37:44 +0800694{
Cheng C Yange50345b2019-04-02 17:26:15 +0800695 sensorTable = {{"power", "power/"},
696 {"curr", "current/"},
697 {"temp", "temperature/"},
698 {"in", "voltage/"},
699 {"fan", "fan_tach/"}};
700
701 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
702 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700703 {"pout2", PSUProperty("Output Power", 3000, 0, 6)},
704 {"pout3", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700705 {"power1", PSUProperty("Output Power", 3000, 0, 6)},
Cheng C Yangbdbde962019-04-26 22:50:34 +0800706 {"vin", PSUProperty("Input Voltage", 300, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700707 {"vout1", PSUProperty("Output Voltage", 255, 0, 3)},
708 {"vout2", PSUProperty("Output Voltage", 255, 0, 3)},
709 {"vout3", PSUProperty("Output Voltage", 255, 0, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700710 {"vout4", PSUProperty("Output Voltage", 255, 0, 3)},
711 {"vout5", PSUProperty("Output Voltage", 255, 0, 3)},
712 {"vout6", PSUProperty("Output Voltage", 255, 0, 3)},
713 {"vout7", PSUProperty("Output Voltage", 255, 0, 3)},
714 {"vout8", PSUProperty("Output Voltage", 255, 0, 3)},
715 {"vout9", PSUProperty("Output Voltage", 255, 0, 3)},
716 {"vout10", PSUProperty("Output Voltage", 255, 0, 3)},
717 {"vout11", PSUProperty("Output Voltage", 255, 0, 3)},
718 {"vout12", PSUProperty("Output Voltage", 255, 0, 3)},
719 {"vout13", PSUProperty("Output Voltage", 255, 0, 3)},
720 {"vout14", PSUProperty("Output Voltage", 255, 0, 3)},
721 {"vout15", PSUProperty("Output Voltage", 255, 0, 3)},
722 {"vout16", PSUProperty("Output Voltage", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700723 {"in1", PSUProperty("Output Voltage", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800724 {"iin", PSUProperty("Input Current", 20, 0, 3)},
725 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700726 {"iout2", PSUProperty("Output Current", 255, 0, 3)},
727 {"iout3", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700728 {"curr1", PSUProperty("Output Current", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800729 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700730 {"temp2", PSUProperty("Temperature", 127, -128, 3)},
731 {"temp3", PSUProperty("Temperature", 127, -128, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700732 {"temp4", PSUProperty("Temperature", 127, -128, 3)},
733 {"temp5", PSUProperty("Temperature", 127, -128, 3)},
Cheng C Yang8dbb3952019-05-23 00:03:12 +0800734 {"fan1", PSUProperty("Fan Speed 1", 30000, 0, 0)},
735 {"fan2", PSUProperty("Fan Speed 2", 30000, 0, 0)}};
Cheng C Yang916360b2019-05-07 18:47:16 +0800736
737 pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
Cheng C Yang58b2b532019-05-31 00:19:45 +0800738
739 limitEventMatch = {{"PredictiveFailure", {"max_alarm", "min_alarm"}},
740 {"Failure", {"crit_alarm", "lcrit_alarm"}}};
741
742 eventMatch = {
743 {"PredictiveFailure", {"power1_alarm"}},
744 {"Failure", {"in2_alarm"}},
Cheng C Yang9c45e6b2019-08-13 07:21:32 +0800745 {"ACLost", {"in1_beep"}},
746 {"FanFault", {"fan1_alarm", "fan2_alarm", "fan1_fault", "fan2_fault"}},
747 {"ConfigureError", {"in1_fault"}}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800748}
749
James Feistb6c0b912019-07-09 12:21:44 -0700750int main()
Cheng C Yang209ec562019-03-12 16:37:44 +0800751{
752 boost::asio::io_service io;
753 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
754
755 systemBus->request_name("xyz.openbmc_project.PSUSensor");
756 sdbusplus::asio::object_server objectServer(systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800757 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yang209ec562019-03-12 16:37:44 +0800758
Cheng C Yang916360b2019-05-07 18:47:16 +0800759 propertyInitialize();
Cheng C Yang209ec562019-03-12 16:37:44 +0800760
Cheng C Yang916360b2019-05-07 18:47:16 +0800761 io.post([&]() { createSensors(io, objectServer, systemBus); });
Cheng C Yang209ec562019-03-12 16:37:44 +0800762 boost::asio::deadline_timer filterTimer(io);
763 std::function<void(sdbusplus::message::message&)> eventHandler =
764 [&](sdbusplus::message::message& message) {
765 if (message.is_method_error())
766 {
767 std::cerr << "callback method error\n";
768 return;
769 }
770 filterTimer.expires_from_now(boost::posix_time::seconds(1));
771 filterTimer.async_wait([&](const boost::system::error_code& ec) {
772 if (ec == boost::asio::error::operation_aborted)
773 {
774 return;
775 }
776 else if (ec)
777 {
778 std::cerr << "timer error\n";
779 }
Cheng C Yang916360b2019-05-07 18:47:16 +0800780 createSensors(io, objectServer, systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800781 });
782 };
783
784 for (const char* type : sensorTypes)
785 {
786 auto match = std::make_unique<sdbusplus::bus::match::match>(
787 static_cast<sdbusplus::bus::bus&>(*systemBus),
788 "type='signal',member='PropertiesChanged',path_namespace='" +
789 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
790 eventHandler);
791 matches.emplace_back(std::move(match));
792 }
793 io.run();
794}