blob: 64f54da8522d779ae2b99d39fccaa66d1089330f [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
Alex Qiu6690d8f2020-01-22 17:56:33 -080042static constexpr std::array<const char*, 7> sensorTypes = {
Devjit Gopalpurde65ef72019-10-30 04:47:35 -070043 "xyz.openbmc_project.Configuration.INA230",
Alex Qiu6690d8f2020-01-22 17:56:33 -080044 "xyz.openbmc_project.Configuration.ISL68137",
45 "xyz.openbmc_project.Configuration.MAX20730",
46 "xyz.openbmc_project.Configuration.MAX20734",
47 "xyz.openbmc_project.Configuration.MAX20796",
48 "xyz.openbmc_project.Configuration.MAX34451",
49 "xyz.openbmc_project.Configuration.pmbus"};
Cheng C Yang209ec562019-03-12 16:37:44 +080050
Alex Qiu6690d8f2020-01-22 17:56:33 -080051static std::vector<std::string> pmbusNames = {
52 "isl68137", "ina219", "ina230", "max20730", "max20734",
53 "max20796", "max34451", "pmbus", "pxe1610"};
Josh Lehan0830c7b2019-10-08 16:35:09 -070054
Cheng C Yang209ec562019-03-12 16:37:44 +080055namespace fs = std::filesystem;
56
Cheng C Yang916360b2019-05-07 18:47:16 +080057static boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>>
58 sensors;
Cheng C Yang58b2b532019-05-31 00:19:45 +080059static boost::container::flat_map<std::string, std::unique_ptr<PSUCombineEvent>>
60 combineEvents;
Cheng C Yang916360b2019-05-07 18:47:16 +080061static boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
62 pwmSensors;
63static boost::container::flat_map<std::string, std::string> sensorTable;
64static boost::container::flat_map<std::string, PSUProperty> labelMatch;
65static boost::container::flat_map<std::string, std::string> pwmTable;
Cheng C Yang58b2b532019-05-31 00:19:45 +080066static boost::container::flat_map<std::string, std::vector<std::string>>
67 eventMatch;
Cheng C Yang202a1ff2020-01-09 09:34:22 +080068static boost::container::flat_map<
69 std::string,
70 boost::container::flat_map<std::string, std::vector<std::string>>>
71 groupEventMatch;
Cheng C Yang58b2b532019-05-31 00:19:45 +080072static boost::container::flat_map<std::string, std::vector<std::string>>
73 limitEventMatch;
74
Josh Lehan74d9bd92019-10-31 08:51:58 -070075static std::vector<PSUProperty> psuProperties;
76
Cheng C Yang58b2b532019-05-31 00:19:45 +080077// Function CheckEvent will check each attribute from eventMatch table in the
78// sysfs. If the attributes exists in sysfs, then store the complete path
79// of the attribute into eventPathList.
80void checkEvent(
81 const std::string& directory,
82 const boost::container::flat_map<std::string, std::vector<std::string>>&
83 eventMatch,
84 boost::container::flat_map<std::string, std::vector<std::string>>&
85 eventPathList)
86{
87 for (const auto& match : eventMatch)
88 {
89 const std::vector<std::string>& eventAttrs = match.second;
90 const std::string& eventName = match.first;
91 for (const auto& eventAttr : eventAttrs)
92 {
93 auto eventPath = directory + "/" + eventAttr;
94
95 std::ifstream eventFile(eventPath);
96 if (!eventFile.good())
97 {
98 continue;
99 }
100
101 eventPathList[eventName].push_back(eventPath);
102 }
103 }
104}
105
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800106// Check Group Events which contains more than one targets in each combine
107// events.
108void checkGroupEvent(
109 const std::string& directory,
110 const boost::container::flat_map<
111 std::string,
112 boost::container::flat_map<std::string, std::vector<std::string>>>&
113 groupEventMatch,
114 boost::container::flat_map<
115 std::string,
116 boost::container::flat_map<std::string, std::vector<std::string>>>&
117 groupEventPathList)
118{
119 for (const auto& match : groupEventMatch)
120 {
121 const std::string& groupEventName = match.first;
122 const boost::container::flat_map<std::string, std::vector<std::string>>
123 events = match.second;
124 boost::container::flat_map<std::string, std::vector<std::string>>
125 pathList;
126 for (const auto& match : events)
127 {
128 const std::string& eventName = match.first;
129 const std::vector<std::string>& eventAttrs = match.second;
130 for (const auto& eventAttr : eventAttrs)
131 {
132 auto eventPath = directory + "/" + eventAttr;
133 std::ifstream eventFile(eventPath);
134 if (!eventFile.good())
135 {
136 continue;
137 }
138
139 pathList[eventName].push_back(eventPath);
140 }
141 }
142 groupEventPathList[groupEventName] = pathList;
143 }
144}
145
Cheng C Yang58b2b532019-05-31 00:19:45 +0800146// Function checkEventLimits will check all the psu related xxx_input attributes
147// in sysfs to see if xxx_crit_alarm xxx_lcrit_alarm xxx_max_alarm
148// xxx_min_alarm exist, then store the existing paths of the alarm attributes
149// to eventPathList.
150void checkEventLimits(
151 const std::string& sensorPathStr,
152 const boost::container::flat_map<std::string, std::vector<std::string>>&
153 limitEventMatch,
154 boost::container::flat_map<std::string, std::vector<std::string>>&
155 eventPathList)
156{
157 for (const auto& limitMatch : limitEventMatch)
158 {
159 const std::vector<std::string>& limitEventAttrs = limitMatch.second;
160 const std::string& eventName = limitMatch.first;
161 for (const auto& limitEventAttr : limitEventAttrs)
162 {
163 auto limitEventPath =
164 boost::replace_all_copy(sensorPathStr, "input", limitEventAttr);
165 std::ifstream eventFile(limitEventPath);
166 if (!eventFile.good())
167 {
168 continue;
169 }
170 eventPathList[eventName].push_back(limitEventPath);
171 }
172 }
173}
Cheng C Yang916360b2019-05-07 18:47:16 +0800174
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530175static void
176 checkPWMSensor(const fs::path& sensorPath, std::string& labelHead,
177 const std::string& interfacePath,
178 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
179 sdbusplus::asio::object_server& objectServer,
180 const std::string& psuName)
Cheng C Yang916360b2019-05-07 18:47:16 +0800181{
182 for (const auto& pwmName : pwmTable)
183 {
184 if (pwmName.first != labelHead)
185 {
186 continue;
187 }
188
189 const std::string& sensorPathStr = sensorPath.string();
190 const std::string& pwmPathStr =
191 boost::replace_all_copy(sensorPathStr, "input", "target");
192 std::ifstream pwmFile(pwmPathStr);
193 if (!pwmFile.good())
194 {
195 continue;
196 }
197
198 auto findPWMSensor = pwmSensors.find(psuName + labelHead);
199 if (findPWMSensor != pwmSensors.end())
200 {
201 continue;
202 }
203
204 pwmSensors[psuName + labelHead] = std::make_unique<PwmSensor>(
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530205 "Pwm_" + psuName + "_" + pwmName.second, pwmPathStr, dbusConnection,
206 objectServer, interfacePath + "_" + pwmName.second, "PSU");
Cheng C Yang916360b2019-05-07 18:47:16 +0800207 }
208}
209
210void createSensors(boost::asio::io_service& io,
211 sdbusplus::asio::object_server& objectServer,
212 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
Cheng C Yang209ec562019-03-12 16:37:44 +0800213{
214
215 ManagedObjectType sensorConfigs;
Josh Lehan49cfba92019-10-08 16:50:42 -0700216 int numCreated = 0;
Cheng C Yang209ec562019-03-12 16:37:44 +0800217 bool useCache = false;
218
Cheng C Yang58b2b532019-05-31 00:19:45 +0800219 // TODO may need only modify the ones that need to be changed.
220 sensors.clear();
Cheng C Yang209ec562019-03-12 16:37:44 +0800221 for (const char* type : sensorTypes)
222 {
223 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
224 useCache))
225 {
226 std::cerr << "error get sensor config from entity manager\n";
227 return;
228 }
229 useCache = true;
230 }
231
232 std::vector<fs::path> pmbusPaths;
233 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
234 {
235 std::cerr << "No PSU sensors in system\n";
236 return;
237 }
238
239 boost::container::flat_set<std::string> directories;
240 for (const auto& pmbusPath : pmbusPaths)
241 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800242 boost::container::flat_map<std::string, std::vector<std::string>>
243 eventPathList;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800244 boost::container::flat_map<
245 std::string,
246 boost::container::flat_map<std::string, std::vector<std::string>>>
247 groupEventPathList;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800248
249 std::ifstream nameFile(pmbusPath);
250 if (!nameFile.good())
251 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700252 std::cerr << "Failure finding pmbus path " << pmbusPath << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800253 continue;
254 }
255
256 std::string pmbusName;
257 std::getline(nameFile, pmbusName);
258 nameFile.close();
Vijay Khemka996bad12019-05-28 15:15:16 -0700259
260 if (std::find(pmbusNames.begin(), pmbusNames.end(), pmbusName) ==
261 pmbusNames.end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800262 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700263 // To avoid this error message, add your driver name to
264 // the pmbusNames vector at the top of this file.
265 std::cerr << "Driver name " << pmbusName
266 << " not found in sensor whitelist\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800267 continue;
268 }
269
270 const std::string* psuName;
Cheng C Yang209ec562019-03-12 16:37:44 +0800271 auto directory = pmbusPath.parent_path();
272
273 auto ret = directories.insert(directory.string());
274 if (!ret.second)
275 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700276 std::cerr << "Duplicate path " << directory.string() << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800277 continue; // check if path has already been searched
Cheng C Yang209ec562019-03-12 16:37:44 +0800278 }
279
James Feistb6c0b912019-07-09 12:21:44 -0700280 fs::path device = directory / "device";
Cheng C Yang209ec562019-03-12 16:37:44 +0800281 std::string deviceName = fs::canonical(device).stem();
282 auto findHyphen = deviceName.find("-");
283 if (findHyphen == std::string::npos)
284 {
285 std::cerr << "found bad device" << deviceName << "\n";
286 continue;
287 }
288 std::string busStr = deviceName.substr(0, findHyphen);
289 std::string addrStr = deviceName.substr(findHyphen + 1);
290
291 size_t bus = 0;
292 size_t addr = 0;
293
294 try
295 {
296 bus = std::stoi(busStr);
297 addr = std::stoi(addrStr, 0, 16);
298 }
James Feistb6c0b912019-07-09 12:21:44 -0700299 catch (std::invalid_argument&)
Cheng C Yang209ec562019-03-12 16:37:44 +0800300 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700301 std::cerr << "Error parsing bus " << busStr << " addr " << addrStr
302 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800303 continue;
304 }
305
Cheng C Yang209ec562019-03-12 16:37:44 +0800306 const std::pair<std::string, boost::container::flat_map<
307 std::string, BasicVariantType>>*
308 baseConfig = nullptr;
309 const SensorData* sensorData = nullptr;
310 const std::string* interfacePath = nullptr;
311 const char* sensorType = nullptr;
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800312 size_t thresholdConfSize = 0;
Cheng C Yang209ec562019-03-12 16:37:44 +0800313
314 for (const std::pair<sdbusplus::message::object_path, SensorData>&
315 sensor : sensorConfigs)
316 {
317 sensorData = &(sensor.second);
318 for (const char* type : sensorTypes)
319 {
320 auto sensorBase = sensorData->find(type);
321 if (sensorBase != sensorData->end())
322 {
323 baseConfig = &(*sensorBase);
324 sensorType = type;
325 break;
326 }
327 }
328 if (baseConfig == nullptr)
329 {
330 std::cerr << "error finding base configuration for "
331 << deviceName << "\n";
332 continue;
333 }
334
335 auto configBus = baseConfig->second.find("Bus");
336 auto configAddress = baseConfig->second.find("Address");
337
338 if (configBus == baseConfig->second.end() ||
339 configAddress == baseConfig->second.end())
340 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800341 std::cerr << "error finding necessary entry in configuration\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800342 continue;
343 }
344
Cheng C Yang58b2b532019-05-31 00:19:45 +0800345 const uint64_t* confBus;
346 const uint64_t* confAddr;
347 if (!(confBus = std::get_if<uint64_t>(&(configBus->second))) ||
348 !(confAddr = std::get_if<uint64_t>(&(configAddress->second))))
349 {
350 std::cerr
Josh Lehan49cfba92019-10-08 16:50:42 -0700351 << "Cannot get bus or address, invalid configuration\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800352 continue;
353 }
354
355 if ((*confBus != bus) || (*confAddr != addr))
Cheng C Yang209ec562019-03-12 16:37:44 +0800356 {
Josh Lehan432d1ed2019-10-16 12:23:31 -0700357 std::cerr << "Configuration skipping " << *confBus << "-"
358 << *confAddr << " because not " << bus << "-" << addr
359 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800360 continue;
361 }
362
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800363 std::vector<thresholds::Threshold> confThresholds;
364 if (!parseThresholdsFromConfig(*sensorData, confThresholds))
365 {
366 std::cerr << "error populating totoal thresholds\n";
367 }
368 thresholdConfSize = confThresholds.size();
369
Cheng C Yang209ec562019-03-12 16:37:44 +0800370 interfacePath = &(sensor.first.str);
371 break;
372 }
373 if (interfacePath == nullptr)
374 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700375 // To avoid this error message, add your export map entry,
376 // from Entity Manager, to sensorTypes at the top of this file.
Cheng C Yang209ec562019-03-12 16:37:44 +0800377 std::cerr << "failed to find match for " << deviceName << "\n";
378 continue;
379 }
380
Cheng C Yange50345b2019-04-02 17:26:15 +0800381 auto findPSUName = baseConfig->second.find("Name");
382 if (findPSUName == baseConfig->second.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800383 {
384 std::cerr << "could not determine configuration name for "
385 << deviceName << "\n";
386 continue;
387 }
388
Cheng C Yang58b2b532019-05-31 00:19:45 +0800389 if (!(psuName = std::get_if<std::string>(&(findPSUName->second))))
390 {
391 std::cerr << "Cannot find psu name, invalid configuration\n";
392 continue;
393 }
394 checkEvent(directory.string(), eventMatch, eventPathList);
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800395 checkGroupEvent(directory.string(), groupEventMatch,
396 groupEventPathList);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800397
Vijay Khemka996bad12019-05-28 15:15:16 -0700398 /* Check if there are more sensors in the same interface */
399 int i = 1;
400 std::vector<std::string> psuNames;
401 do
402 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700403 // Individual string fields: Name, Name1, Name2, Name3, ...
Vijay Khemka996bad12019-05-28 15:15:16 -0700404 psuNames.push_back(std::get<std::string>(findPSUName->second));
405 findPSUName = baseConfig->second.find("Name" + std::to_string(i++));
406 } while (findPSUName != baseConfig->second.end());
407
Cheng C Yange50345b2019-04-02 17:26:15 +0800408 std::vector<fs::path> sensorPaths;
James Feistb6c0b912019-07-09 12:21:44 -0700409 if (!findFiles(directory, R"(\w\d+_input$)", sensorPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800410 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800411 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800412 continue;
413 }
414
Vijay Khemka996bad12019-05-28 15:15:16 -0700415 /* Find array of labels to be exposed if it is defined in config */
416 std::vector<std::string> findLabels;
417 auto findLabelObj = baseConfig->second.find("Labels");
418 if (findLabelObj != baseConfig->second.end())
419 {
420 findLabels =
421 std::get<std::vector<std::string>>(findLabelObj->second);
422 }
423
Jason Ling5747fab2019-10-02 16:46:23 -0700424 std::regex sensorNameRegEx("([A-Za-z]+)[0-9]*_");
425 std::smatch matches;
426
Cheng C Yange50345b2019-04-02 17:26:15 +0800427 for (const auto& sensorPath : sensorPaths)
Cheng C Yang209ec562019-03-12 16:37:44 +0800428 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800429 std::string labelHead;
430 std::string sensorPathStr = sensorPath.string();
431 std::string sensorNameStr = sensorPath.filename();
Jason Ling5747fab2019-10-02 16:46:23 -0700432 std::string sensorNameSubStr{""};
433 if (std::regex_search(sensorNameStr, matches, sensorNameRegEx))
434 {
Josh Lehan06494452019-10-31 09:49:16 -0700435 // hwmon *_input filename without number:
436 // in, curr, power, temp, ...
Jason Ling5747fab2019-10-02 16:46:23 -0700437 sensorNameSubStr = matches[1];
438 }
439 else
440 {
Josh Lehan06494452019-10-31 09:49:16 -0700441 std::cerr << "Could not extract the alpha prefix from "
Jason Ling5747fab2019-10-02 16:46:23 -0700442 << sensorNameStr;
443 continue;
444 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800445
Cheng C Yangecba9de2019-09-12 23:41:50 +0800446 auto labelPath =
447 boost::replace_all_copy(sensorPathStr, "input", "label");
448 std::ifstream labelFile(labelPath);
449 if (!labelFile.good())
Cheng C Yang209ec562019-03-12 16:37:44 +0800450 {
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800451 if constexpr (DEBUG)
452 {
453 std::cerr << "Input file " << sensorPath
454 << " has no corresponding label file\n";
455 }
Josh Lehan06494452019-10-31 09:49:16 -0700456 // hwmon *_input filename with number:
457 // temp1, temp2, temp3, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800458 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800459 }
460 else
461 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800462 std::string label;
463 std::getline(labelFile, label);
464 labelFile.close();
Cheng C Yange50345b2019-04-02 17:26:15 +0800465 auto findSensor = sensors.find(label);
466 if (findSensor != sensors.end())
467 {
468 continue;
469 }
470
Josh Lehan06494452019-10-31 09:49:16 -0700471 // hwmon corresponding *_label file contents:
472 // vin1, vout1, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800473 labelHead = label.substr(0, label.find(" "));
474 }
475
Josh Lehan74d9bd92019-10-31 08:51:58 -0700476 if constexpr (DEBUG)
477 {
478 std::cerr << "Sensor type=\"" << sensorNameSubStr
479 << "\" label=\"" << labelHead << "\"\n";
480 }
481
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530482 checkPWMSensor(sensorPath, labelHead, *interfacePath,
483 dbusConnection, objectServer, psuNames[0]);
Cheng C Yang916360b2019-05-07 18:47:16 +0800484
Vijay Khemka996bad12019-05-28 15:15:16 -0700485 if (!findLabels.empty())
486 {
487 /* Check if this labelHead is enabled in config file */
488 if (std::find(findLabels.begin(), findLabels.end(),
489 labelHead) == findLabels.end())
490 {
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800491 if constexpr (DEBUG)
492 {
493 std::cerr << "could not find " << labelHead
494 << " in the Labels list\n";
495 }
Vijay Khemka996bad12019-05-28 15:15:16 -0700496 continue;
497 }
498 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800499
Cheng C Yange50345b2019-04-02 17:26:15 +0800500 auto findProperty = labelMatch.find(labelHead);
501 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800502 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700503 if constexpr (DEBUG)
504 {
505 std::cerr << "Could not find matching default property for "
506 << labelHead << "\n";
507 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800508 continue;
509 }
510
Josh Lehan74d9bd92019-10-31 08:51:58 -0700511 // Protect the hardcoded labelMatch list from changes,
512 // by making a copy and modifying that instead.
513 // Avoid bleedthrough of one device's customizations to
514 // the next device, as each should be independently customizable.
515 psuProperties.push_back(findProperty->second);
516 auto psuProperty = psuProperties.rbegin();
517
518 // Use label head as prefix for reading from config file,
519 // example if temp1: temp1_Name, temp1_Scale, temp1_Min, ...
520 std::string keyName = labelHead + "_Name";
521 std::string keyScale = labelHead + "_Scale";
522 std::string keyMin = labelHead + "_Min";
523 std::string keyMax = labelHead + "_Max";
524
525 bool customizedName = false;
526 auto findCustomName = baseConfig->second.find(keyName);
527 if (findCustomName != baseConfig->second.end())
Josh Lehan432d1ed2019-10-16 12:23:31 -0700528 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700529 try
530 {
531 psuProperty->labelTypeName = std::visit(
532 VariantToStringVisitor(), findCustomName->second);
533 }
534 catch (std::invalid_argument&)
535 {
536 std::cerr << "Unable to parse " << keyName << "\n";
537 continue;
538 }
539
540 // All strings are valid, including empty string
541 customizedName = true;
542 }
543
544 bool customizedScale = false;
545 auto findCustomScale = baseConfig->second.find(keyScale);
546 if (findCustomScale != baseConfig->second.end())
547 {
548 try
549 {
550 psuProperty->sensorScaleFactor = std::visit(
551 VariantToUnsignedIntVisitor(), findCustomScale->second);
552 }
553 catch (std::invalid_argument&)
554 {
555 std::cerr << "Unable to parse " << keyScale << "\n";
556 continue;
557 }
558
559 // Avoid later division by zero
560 if (psuProperty->sensorScaleFactor > 0)
561 {
562 customizedScale = true;
563 }
564 else
565 {
566 std::cerr << "Unable to accept " << keyScale << "\n";
567 continue;
568 }
569 }
570
571 auto findCustomMin = baseConfig->second.find(keyMin);
572 if (findCustomMin != baseConfig->second.end())
573 {
574 try
575 {
576 psuProperty->minReading = std::visit(
577 VariantToDoubleVisitor(), findCustomMin->second);
578 }
579 catch (std::invalid_argument&)
580 {
581 std::cerr << "Unable to parse " << keyMin << "\n";
582 continue;
583 }
584 }
585
586 auto findCustomMax = baseConfig->second.find(keyMax);
587 if (findCustomMax != baseConfig->second.end())
588 {
589 try
590 {
591 psuProperty->maxReading = std::visit(
592 VariantToDoubleVisitor(), findCustomMax->second);
593 }
594 catch (std::invalid_argument&)
595 {
596 std::cerr << "Unable to parse " << keyMax << "\n";
597 continue;
598 }
599 }
600
601 if (!(psuProperty->minReading < psuProperty->maxReading))
602 {
603 std::cerr << "Min must be less than Max\n";
604 continue;
605 }
606
607 // If the sensor name is being customized by config file,
608 // then prefix/suffix composition becomes not necessary,
609 // and in fact not wanted, because it gets in the way.
610 std::string psuNameFromIndex;
611 if (!customizedName)
612 {
613 /* Find out sensor name index for this label */
614 std::regex rgx("[A-Za-z]+([0-9]+)");
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500615 size_t nameIndex{0};
Josh Lehan74d9bd92019-10-31 08:51:58 -0700616 if (std::regex_search(labelHead, matches, rgx))
617 {
618 nameIndex = std::stoi(matches[1]);
619
620 // Decrement to preserve alignment, because hwmon
621 // human-readable filenames and labels use 1-based
622 // numbering, but the "Name", "Name1", "Name2", etc. naming
623 // convention (the psuNames vector) uses 0-based numbering.
624 if (nameIndex > 0)
625 {
626 --nameIndex;
627 }
628 }
629 else
630 {
631 nameIndex = 0;
632 }
633
634 if (psuNames.size() <= nameIndex)
635 {
636 std::cerr << "Could not pair " << labelHead
637 << " with a Name field\n";
638 continue;
639 }
640
641 psuNameFromIndex = psuNames[nameIndex];
642
643 if constexpr (DEBUG)
644 {
645 std::cerr << "Sensor label head " << labelHead
646 << " paired with " << psuNameFromIndex
647 << " at index " << nameIndex << "\n";
648 }
Josh Lehan432d1ed2019-10-16 12:23:31 -0700649 }
650
Cheng C Yang58b2b532019-05-31 00:19:45 +0800651 checkEventLimits(sensorPathStr, limitEventMatch, eventPathList);
652
Josh Lehan74d9bd92019-10-31 08:51:58 -0700653 // Similarly, if sensor scaling factor is being customized,
654 // then the below power-of-10 constraint becomes unnecessary,
655 // as config should be able to specify an arbitrary divisor.
656 unsigned int factor = psuProperty->sensorScaleFactor;
657 if (!customizedScale)
Vijay Khemka53ca4442019-07-23 11:03:55 -0700658 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700659 // Preserve existing usage of hardcoded labelMatch table below
660 factor = std::pow(10.0, factor);
Vijay Khemka53ca4442019-07-23 11:03:55 -0700661
Josh Lehan74d9bd92019-10-31 08:51:58 -0700662 /* Change first char of substring to uppercase */
663 char firstChar = sensorNameSubStr[0] - 0x20;
664 std::string strScaleFactor =
665 firstChar + sensorNameSubStr.substr(1) + "ScaleFactor";
666
667 // Preserve existing configs by accepting earlier syntax,
668 // example CurrScaleFactor, PowerScaleFactor, ...
669 auto findScaleFactor = baseConfig->second.find(strScaleFactor);
670 if (findScaleFactor != baseConfig->second.end())
671 {
672 factor = std::visit(VariantToIntVisitor(),
673 findScaleFactor->second);
674 }
675
676 if constexpr (DEBUG)
677 {
678 std::cerr << "Sensor scaling factor " << factor
679 << " string " << strScaleFactor << "\n";
680 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700681 }
682
Vijay Khemka996bad12019-05-28 15:15:16 -0700683 std::vector<thresholds::Threshold> sensorThresholds;
Joshi, Mansi14f0ad82019-11-21 10:52:30 +0530684 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds,
685 &labelHead))
Cheng C Yange50345b2019-04-02 17:26:15 +0800686 {
James Feist17ab6e02019-06-25 12:28:13 -0700687 std::cerr << "error populating thresholds for "
688 << sensorNameSubStr << "\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800689 }
690
691 auto findSensorType = sensorTable.find(sensorNameSubStr);
692 if (findSensorType == sensorTable.end())
693 {
Jason Ling5747fab2019-10-02 16:46:23 -0700694 std::cerr << sensorNameSubStr
Josh Lehan06494452019-10-31 09:49:16 -0700695 << " is not a recognized sensor type\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800696 continue;
697 }
698
Josh Lehan49cfba92019-10-08 16:50:42 -0700699 if constexpr (DEBUG)
700 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700701 std::cerr << "Sensor properties: Name \""
702 << psuProperty->labelTypeName << "\" Scale "
703 << psuProperty->sensorScaleFactor << " Min "
704 << psuProperty->minReading << " Max "
705 << psuProperty->maxReading << "\n";
706 }
707
708 std::string sensorName = psuProperty->labelTypeName;
709 if (customizedName)
710 {
711 if (sensorName.empty())
712 {
713 // Allow selective disabling of an individual sensor,
714 // by customizing its name to an empty string.
715 std::cerr << "Sensor disabled, empty string\n";
716 continue;
717 }
718 }
719 else
720 {
721 // Sensor name not customized, do prefix/suffix composition,
722 // preserving default behavior by using psuNameFromIndex.
723 sensorName =
724 psuNameFromIndex + " " + psuProperty->labelTypeName;
725 }
726
727 if constexpr (DEBUG)
728 {
729 std::cerr << "Sensor name \"" << sensorName << "\" path \""
730 << sensorPathStr << "\" type \"" << sensorType
731 << "\"\n";
Josh Lehan49cfba92019-10-08 16:50:42 -0700732 }
733
Cheng C Yang58b2b532019-05-31 00:19:45 +0800734 sensors[sensorName] = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800735 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800736 sensorName, std::move(sensorThresholds), *interfacePath,
Josh Lehan74d9bd92019-10-31 08:51:58 -0700737 findSensorType->second, factor, psuProperty->maxReading,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800738 psuProperty->minReading, labelHead, thresholdConfSize);
Josh Lehan74d9bd92019-10-31 08:51:58 -0700739
740 ++numCreated;
741 if constexpr (DEBUG)
742 {
743 std::cerr << "Created " << numCreated << " sensors so far\n";
744 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800745 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800746
747 // OperationalStatus event
Cheng C Yang92498eb2019-09-26 21:59:25 +0800748 combineEvents[*psuName + "OperationalStatus"] = nullptr;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800749 combineEvents[*psuName + "OperationalStatus"] =
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800750 std::make_unique<PSUCombineEvent>(objectServer, io, *psuName,
751 eventPathList, groupEventPathList,
752 "OperationalStatus");
Cheng C Yang209ec562019-03-12 16:37:44 +0800753 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700754
755 if constexpr (DEBUG)
756 {
757 std::cerr << "Created total of " << numCreated << " sensors\n";
758 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800759 return;
760}
761
Cheng C Yang916360b2019-05-07 18:47:16 +0800762void propertyInitialize(void)
Cheng C Yang209ec562019-03-12 16:37:44 +0800763{
Cheng C Yange50345b2019-04-02 17:26:15 +0800764 sensorTable = {{"power", "power/"},
765 {"curr", "current/"},
766 {"temp", "temperature/"},
767 {"in", "voltage/"},
768 {"fan", "fan_tach/"}};
769
770 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
771 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700772 {"pout2", PSUProperty("Output Power", 3000, 0, 6)},
773 {"pout3", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700774 {"power1", PSUProperty("Output Power", 3000, 0, 6)},
Cheng C Yangbdbde962019-04-26 22:50:34 +0800775 {"vin", PSUProperty("Input Voltage", 300, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700776 {"vout1", PSUProperty("Output Voltage", 255, 0, 3)},
777 {"vout2", PSUProperty("Output Voltage", 255, 0, 3)},
778 {"vout3", PSUProperty("Output Voltage", 255, 0, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700779 {"vout4", PSUProperty("Output Voltage", 255, 0, 3)},
780 {"vout5", PSUProperty("Output Voltage", 255, 0, 3)},
781 {"vout6", PSUProperty("Output Voltage", 255, 0, 3)},
782 {"vout7", PSUProperty("Output Voltage", 255, 0, 3)},
783 {"vout8", PSUProperty("Output Voltage", 255, 0, 3)},
784 {"vout9", PSUProperty("Output Voltage", 255, 0, 3)},
785 {"vout10", PSUProperty("Output Voltage", 255, 0, 3)},
786 {"vout11", PSUProperty("Output Voltage", 255, 0, 3)},
787 {"vout12", PSUProperty("Output Voltage", 255, 0, 3)},
788 {"vout13", PSUProperty("Output Voltage", 255, 0, 3)},
789 {"vout14", PSUProperty("Output Voltage", 255, 0, 3)},
790 {"vout15", PSUProperty("Output Voltage", 255, 0, 3)},
791 {"vout16", PSUProperty("Output Voltage", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700792 {"in1", PSUProperty("Output Voltage", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800793 {"iin", PSUProperty("Input Current", 20, 0, 3)},
794 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700795 {"iout2", PSUProperty("Output Current", 255, 0, 3)},
796 {"iout3", PSUProperty("Output Current", 255, 0, 3)},
Peter Lundgren48b44232020-01-30 16:02:11 -0800797 {"iout4", PSUProperty("Output Current", 255, 0, 3)},
798 {"iout5", PSUProperty("Output Current", 255, 0, 3)},
799 {"iout6", PSUProperty("Output Current", 255, 0, 3)},
800 {"iout7", PSUProperty("Output Current", 255, 0, 3)},
801 {"iout8", PSUProperty("Output Current", 255, 0, 3)},
802 {"iout9", PSUProperty("Output Current", 255, 0, 3)},
803 {"iout10", PSUProperty("Output Current", 255, 0, 3)},
804 {"iout11", PSUProperty("Output Current", 255, 0, 3)},
805 {"iout12", PSUProperty("Output Current", 255, 0, 3)},
806 {"iout13", PSUProperty("Output Current", 255, 0, 3)},
807 {"iout14", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700808 {"curr1", PSUProperty("Output Current", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800809 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700810 {"temp2", PSUProperty("Temperature", 127, -128, 3)},
811 {"temp3", PSUProperty("Temperature", 127, -128, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700812 {"temp4", PSUProperty("Temperature", 127, -128, 3)},
813 {"temp5", PSUProperty("Temperature", 127, -128, 3)},
Cheng C Yang8dbb3952019-05-23 00:03:12 +0800814 {"fan1", PSUProperty("Fan Speed 1", 30000, 0, 0)},
815 {"fan2", PSUProperty("Fan Speed 2", 30000, 0, 0)}};
Cheng C Yang916360b2019-05-07 18:47:16 +0800816
817 pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
Cheng C Yang58b2b532019-05-31 00:19:45 +0800818
819 limitEventMatch = {{"PredictiveFailure", {"max_alarm", "min_alarm"}},
820 {"Failure", {"crit_alarm", "lcrit_alarm"}}};
821
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800822 eventMatch = {{"PredictiveFailure", {"power1_alarm"}},
823 {"Failure", {"in2_alarm"}},
824 {"ACLost", {"in1_beep"}},
825 {"ConfigureError", {"in1_fault"}}};
826
827 groupEventMatch = {{"FanFault",
828 {{"fan1", {"fan1_alarm", "fan1_fault"}},
829 {"fan2", {"fan2_alarm", "fan2_fault"}}}}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800830}
831
James Feistb6c0b912019-07-09 12:21:44 -0700832int main()
Cheng C Yang209ec562019-03-12 16:37:44 +0800833{
834 boost::asio::io_service io;
835 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
836
837 systemBus->request_name("xyz.openbmc_project.PSUSensor");
838 sdbusplus::asio::object_server objectServer(systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800839 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yang209ec562019-03-12 16:37:44 +0800840
Cheng C Yang916360b2019-05-07 18:47:16 +0800841 propertyInitialize();
Cheng C Yang209ec562019-03-12 16:37:44 +0800842
Cheng C Yang916360b2019-05-07 18:47:16 +0800843 io.post([&]() { createSensors(io, objectServer, systemBus); });
Cheng C Yang209ec562019-03-12 16:37:44 +0800844 boost::asio::deadline_timer filterTimer(io);
845 std::function<void(sdbusplus::message::message&)> eventHandler =
846 [&](sdbusplus::message::message& message) {
847 if (message.is_method_error())
848 {
849 std::cerr << "callback method error\n";
850 return;
851 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800852 filterTimer.expires_from_now(boost::posix_time::seconds(3));
Cheng C Yang209ec562019-03-12 16:37:44 +0800853 filterTimer.async_wait([&](const boost::system::error_code& ec) {
854 if (ec == boost::asio::error::operation_aborted)
855 {
856 return;
857 }
858 else if (ec)
859 {
860 std::cerr << "timer error\n";
861 }
Cheng C Yang916360b2019-05-07 18:47:16 +0800862 createSensors(io, objectServer, systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800863 });
864 };
865
866 for (const char* type : sensorTypes)
867 {
868 auto match = std::make_unique<sdbusplus::bus::match::match>(
869 static_cast<sdbusplus::bus::bus&>(*systemBus),
870 "type='signal',member='PropertiesChanged',path_namespace='" +
871 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
872 eventHandler);
873 matches.emplace_back(std::move(match));
874 }
875 io.run();
876}