blob: a2c3de437cd43c701b14a7e0015018d88a129933 [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]+)");
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500547 size_t nameIndex{0};
Josh Lehan74d9bd92019-10-31 08:51:58 -0700548 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
Joshi, Mansi14f0ad82019-11-21 10:52:30 +0530617 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds,
618 &labelHead))
Cheng C Yange50345b2019-04-02 17:26:15 +0800619 {
James Feist17ab6e02019-06-25 12:28:13 -0700620 std::cerr << "error populating thresholds for "
621 << sensorNameSubStr << "\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800622 }
623
624 auto findSensorType = sensorTable.find(sensorNameSubStr);
625 if (findSensorType == sensorTable.end())
626 {
Jason Ling5747fab2019-10-02 16:46:23 -0700627 std::cerr << sensorNameSubStr
Josh Lehan06494452019-10-31 09:49:16 -0700628 << " is not a recognized sensor type\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800629 continue;
630 }
631
Josh Lehan49cfba92019-10-08 16:50:42 -0700632 if constexpr (DEBUG)
633 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700634 std::cerr << "Sensor properties: Name \""
635 << psuProperty->labelTypeName << "\" Scale "
636 << psuProperty->sensorScaleFactor << " Min "
637 << psuProperty->minReading << " Max "
638 << psuProperty->maxReading << "\n";
639 }
640
641 std::string sensorName = psuProperty->labelTypeName;
642 if (customizedName)
643 {
644 if (sensorName.empty())
645 {
646 // Allow selective disabling of an individual sensor,
647 // by customizing its name to an empty string.
648 std::cerr << "Sensor disabled, empty string\n";
649 continue;
650 }
651 }
652 else
653 {
654 // Sensor name not customized, do prefix/suffix composition,
655 // preserving default behavior by using psuNameFromIndex.
656 sensorName =
657 psuNameFromIndex + " " + psuProperty->labelTypeName;
658 }
659
660 if constexpr (DEBUG)
661 {
662 std::cerr << "Sensor name \"" << sensorName << "\" path \""
663 << sensorPathStr << "\" type \"" << sensorType
664 << "\"\n";
Josh Lehan49cfba92019-10-08 16:50:42 -0700665 }
666
Cheng C Yang58b2b532019-05-31 00:19:45 +0800667 sensors[sensorName] = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800668 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800669 sensorName, std::move(sensorThresholds), *interfacePath,
Josh Lehan74d9bd92019-10-31 08:51:58 -0700670 findSensorType->second, factor, psuProperty->maxReading,
671 psuProperty->minReading);
672
673 ++numCreated;
674 if constexpr (DEBUG)
675 {
676 std::cerr << "Created " << numCreated << " sensors so far\n";
677 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800678 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800679
680 // OperationalStatus event
Cheng C Yang92498eb2019-09-26 21:59:25 +0800681 combineEvents[*psuName + "OperationalStatus"] = nullptr;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800682 combineEvents[*psuName + "OperationalStatus"] =
683 std::make_unique<PSUCombineEvent>(
684 objectServer, io, *psuName, eventPathList, "OperationalStatus");
Cheng C Yang209ec562019-03-12 16:37:44 +0800685 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700686
687 if constexpr (DEBUG)
688 {
689 std::cerr << "Created total of " << numCreated << " sensors\n";
690 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800691 return;
692}
693
Cheng C Yang916360b2019-05-07 18:47:16 +0800694void propertyInitialize(void)
Cheng C Yang209ec562019-03-12 16:37:44 +0800695{
Cheng C Yange50345b2019-04-02 17:26:15 +0800696 sensorTable = {{"power", "power/"},
697 {"curr", "current/"},
698 {"temp", "temperature/"},
699 {"in", "voltage/"},
700 {"fan", "fan_tach/"}};
701
702 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
703 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700704 {"pout2", PSUProperty("Output Power", 3000, 0, 6)},
705 {"pout3", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700706 {"power1", PSUProperty("Output Power", 3000, 0, 6)},
Cheng C Yangbdbde962019-04-26 22:50:34 +0800707 {"vin", PSUProperty("Input Voltage", 300, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700708 {"vout1", PSUProperty("Output Voltage", 255, 0, 3)},
709 {"vout2", PSUProperty("Output Voltage", 255, 0, 3)},
710 {"vout3", PSUProperty("Output Voltage", 255, 0, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700711 {"vout4", PSUProperty("Output Voltage", 255, 0, 3)},
712 {"vout5", PSUProperty("Output Voltage", 255, 0, 3)},
713 {"vout6", PSUProperty("Output Voltage", 255, 0, 3)},
714 {"vout7", PSUProperty("Output Voltage", 255, 0, 3)},
715 {"vout8", PSUProperty("Output Voltage", 255, 0, 3)},
716 {"vout9", PSUProperty("Output Voltage", 255, 0, 3)},
717 {"vout10", PSUProperty("Output Voltage", 255, 0, 3)},
718 {"vout11", PSUProperty("Output Voltage", 255, 0, 3)},
719 {"vout12", PSUProperty("Output Voltage", 255, 0, 3)},
720 {"vout13", PSUProperty("Output Voltage", 255, 0, 3)},
721 {"vout14", PSUProperty("Output Voltage", 255, 0, 3)},
722 {"vout15", PSUProperty("Output Voltage", 255, 0, 3)},
723 {"vout16", PSUProperty("Output Voltage", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700724 {"in1", PSUProperty("Output Voltage", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800725 {"iin", PSUProperty("Input Current", 20, 0, 3)},
726 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700727 {"iout2", PSUProperty("Output Current", 255, 0, 3)},
728 {"iout3", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700729 {"curr1", PSUProperty("Output Current", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800730 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700731 {"temp2", PSUProperty("Temperature", 127, -128, 3)},
732 {"temp3", PSUProperty("Temperature", 127, -128, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700733 {"temp4", PSUProperty("Temperature", 127, -128, 3)},
734 {"temp5", PSUProperty("Temperature", 127, -128, 3)},
Cheng C Yang8dbb3952019-05-23 00:03:12 +0800735 {"fan1", PSUProperty("Fan Speed 1", 30000, 0, 0)},
736 {"fan2", PSUProperty("Fan Speed 2", 30000, 0, 0)}};
Cheng C Yang916360b2019-05-07 18:47:16 +0800737
738 pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
Cheng C Yang58b2b532019-05-31 00:19:45 +0800739
740 limitEventMatch = {{"PredictiveFailure", {"max_alarm", "min_alarm"}},
741 {"Failure", {"crit_alarm", "lcrit_alarm"}}};
742
743 eventMatch = {
744 {"PredictiveFailure", {"power1_alarm"}},
745 {"Failure", {"in2_alarm"}},
Cheng C Yang9c45e6b2019-08-13 07:21:32 +0800746 {"ACLost", {"in1_beep"}},
747 {"FanFault", {"fan1_alarm", "fan2_alarm", "fan1_fault", "fan2_fault"}},
748 {"ConfigureError", {"in1_fault"}}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800749}
750
James Feistb6c0b912019-07-09 12:21:44 -0700751int main()
Cheng C Yang209ec562019-03-12 16:37:44 +0800752{
753 boost::asio::io_service io;
754 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
755
756 systemBus->request_name("xyz.openbmc_project.PSUSensor");
757 sdbusplus::asio::object_server objectServer(systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800758 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yang209ec562019-03-12 16:37:44 +0800759
Cheng C Yang916360b2019-05-07 18:47:16 +0800760 propertyInitialize();
Cheng C Yang209ec562019-03-12 16:37:44 +0800761
Cheng C Yang916360b2019-05-07 18:47:16 +0800762 io.post([&]() { createSensors(io, objectServer, systemBus); });
Cheng C Yang209ec562019-03-12 16:37:44 +0800763 boost::asio::deadline_timer filterTimer(io);
764 std::function<void(sdbusplus::message::message&)> eventHandler =
765 [&](sdbusplus::message::message& message) {
766 if (message.is_method_error())
767 {
768 std::cerr << "callback method error\n";
769 return;
770 }
771 filterTimer.expires_from_now(boost::posix_time::seconds(1));
772 filterTimer.async_wait([&](const boost::system::error_code& ec) {
773 if (ec == boost::asio::error::operation_aborted)
774 {
775 return;
776 }
777 else if (ec)
778 {
779 std::cerr << "timer error\n";
780 }
Cheng C Yang916360b2019-05-07 18:47:16 +0800781 createSensors(io, objectServer, systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800782 });
783 };
784
785 for (const char* type : sensorTypes)
786 {
787 auto match = std::make_unique<sdbusplus::bus::match::match>(
788 static_cast<sdbusplus::bus::bus&>(*systemBus),
789 "type='signal',member='PropertiesChanged',path_namespace='" +
790 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
791 eventHandler);
792 matches.emplace_back(std::move(match));
793 }
794 io.run();
795}