blob: 03af04650218d403dc4f320e72f886e101f94de5 [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
Cheng C Yang209ec562019-03-12 16:37:44 +080021#include <boost/algorithm/string/predicate.hpp>
22#include <boost/algorithm/string/replace.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070023#include <boost/container/flat_map.hpp>
Cheng C Yang209ec562019-03-12 16:37:44 +080024#include <boost/container/flat_set.hpp>
James Feist38fb5982020-05-28 10:09:54 -070025#include <sdbusplus/asio/connection.hpp>
26#include <sdbusplus/asio/object_server.hpp>
27#include <sdbusplus/bus/match.hpp>
28
29#include <array>
Josh Lehan74d9bd92019-10-31 08:51:58 -070030#include <cmath>
James Feist24f02f22019-04-15 11:05:39 -070031#include <filesystem>
Cheng C Yang209ec562019-03-12 16:37:44 +080032#include <fstream>
Patrick Venture96e97db2019-10-31 13:44:38 -070033#include <functional>
Cheng C Yang58b2b532019-05-31 00:19:45 +080034#include <iostream>
Cheng C Yang209ec562019-03-12 16:37:44 +080035#include <regex>
Patrick Venture96e97db2019-10-31 13:44:38 -070036#include <string>
37#include <utility>
38#include <variant>
39#include <vector>
Cheng C Yang209ec562019-03-12 16:37:44 +080040
Josh Lehan49cfba92019-10-08 16:50:42 -070041static constexpr bool DEBUG = false;
42
Alex Qiuf5709b42020-01-29 13:29:50 -080043static constexpr std::array<const char*, 8> sensorTypes = {
Devjit Gopalpurde65ef72019-10-30 04:47:35 -070044 "xyz.openbmc_project.Configuration.INA230",
Alex Qiu6690d8f2020-01-22 17:56:33 -080045 "xyz.openbmc_project.Configuration.ISL68137",
Alex Qiuf5709b42020-01-29 13:29:50 -080046 "xyz.openbmc_project.Configuration.MAX16601",
Alex Qiu6690d8f2020-01-22 17:56:33 -080047 "xyz.openbmc_project.Configuration.MAX20730",
48 "xyz.openbmc_project.Configuration.MAX20734",
49 "xyz.openbmc_project.Configuration.MAX20796",
50 "xyz.openbmc_project.Configuration.MAX34451",
51 "xyz.openbmc_project.Configuration.pmbus"};
Cheng C Yang209ec562019-03-12 16:37:44 +080052
Alex Qiu6690d8f2020-01-22 17:56:33 -080053static std::vector<std::string> pmbusNames = {
Manikandan Elumalai68b14e62020-06-03 17:09:52 +053054 "adm1272", "adm1278", "isl68137", "ina219", "ina230",
55 "max16601", "max20730", "max20734", "max20796", "max34451",
56 "pmbus", "pxe1610", "raa228228"};
Josh Lehan0830c7b2019-10-08 16:35:09 -070057
Cheng C Yang209ec562019-03-12 16:37:44 +080058namespace fs = std::filesystem;
59
Yong Libf8b1da2020-04-15 16:32:50 +080060static boost::container::flat_map<std::string, std::shared_ptr<PSUSensor>>
Cheng C Yang916360b2019-05-07 18:47:16 +080061 sensors;
Cheng C Yang58b2b532019-05-31 00:19:45 +080062static boost::container::flat_map<std::string, std::unique_ptr<PSUCombineEvent>>
63 combineEvents;
Cheng C Yang916360b2019-05-07 18:47:16 +080064static boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
65 pwmSensors;
66static boost::container::flat_map<std::string, std::string> sensorTable;
67static boost::container::flat_map<std::string, PSUProperty> labelMatch;
68static boost::container::flat_map<std::string, std::string> pwmTable;
Cheng C Yang58b2b532019-05-31 00:19:45 +080069static boost::container::flat_map<std::string, std::vector<std::string>>
70 eventMatch;
Cheng C Yang202a1ff2020-01-09 09:34:22 +080071static boost::container::flat_map<
72 std::string,
73 boost::container::flat_map<std::string, std::vector<std::string>>>
74 groupEventMatch;
Cheng C Yang58b2b532019-05-31 00:19:45 +080075static boost::container::flat_map<std::string, std::vector<std::string>>
76 limitEventMatch;
77
Josh Lehan74d9bd92019-10-31 08:51:58 -070078static std::vector<PSUProperty> psuProperties;
79
Cheng C Yang58b2b532019-05-31 00:19:45 +080080// Function CheckEvent will check each attribute from eventMatch table in the
81// sysfs. If the attributes exists in sysfs, then store the complete path
82// of the attribute into eventPathList.
83void checkEvent(
84 const std::string& directory,
85 const boost::container::flat_map<std::string, std::vector<std::string>>&
86 eventMatch,
87 boost::container::flat_map<std::string, std::vector<std::string>>&
88 eventPathList)
89{
90 for (const auto& match : eventMatch)
91 {
92 const std::vector<std::string>& eventAttrs = match.second;
93 const std::string& eventName = match.first;
94 for (const auto& eventAttr : eventAttrs)
95 {
96 auto eventPath = directory + "/" + eventAttr;
97
98 std::ifstream eventFile(eventPath);
99 if (!eventFile.good())
100 {
101 continue;
102 }
103
104 eventPathList[eventName].push_back(eventPath);
105 }
106 }
107}
108
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800109// Check Group Events which contains more than one targets in each combine
110// events.
111void checkGroupEvent(
112 const std::string& directory,
113 const boost::container::flat_map<
114 std::string,
115 boost::container::flat_map<std::string, std::vector<std::string>>>&
116 groupEventMatch,
117 boost::container::flat_map<
118 std::string,
119 boost::container::flat_map<std::string, std::vector<std::string>>>&
120 groupEventPathList)
121{
122 for (const auto& match : groupEventMatch)
123 {
124 const std::string& groupEventName = match.first;
125 const boost::container::flat_map<std::string, std::vector<std::string>>
126 events = match.second;
127 boost::container::flat_map<std::string, std::vector<std::string>>
128 pathList;
129 for (const auto& match : events)
130 {
131 const std::string& eventName = match.first;
132 const std::vector<std::string>& eventAttrs = match.second;
133 for (const auto& eventAttr : eventAttrs)
134 {
135 auto eventPath = directory + "/" + eventAttr;
136 std::ifstream eventFile(eventPath);
137 if (!eventFile.good())
138 {
139 continue;
140 }
141
142 pathList[eventName].push_back(eventPath);
143 }
144 }
145 groupEventPathList[groupEventName] = pathList;
146 }
147}
148
Cheng C Yang58b2b532019-05-31 00:19:45 +0800149// Function checkEventLimits will check all the psu related xxx_input attributes
150// in sysfs to see if xxx_crit_alarm xxx_lcrit_alarm xxx_max_alarm
151// xxx_min_alarm exist, then store the existing paths of the alarm attributes
152// to eventPathList.
153void checkEventLimits(
154 const std::string& sensorPathStr,
155 const boost::container::flat_map<std::string, std::vector<std::string>>&
156 limitEventMatch,
157 boost::container::flat_map<std::string, std::vector<std::string>>&
158 eventPathList)
159{
160 for (const auto& limitMatch : limitEventMatch)
161 {
162 const std::vector<std::string>& limitEventAttrs = limitMatch.second;
163 const std::string& eventName = limitMatch.first;
164 for (const auto& limitEventAttr : limitEventAttrs)
165 {
166 auto limitEventPath =
167 boost::replace_all_copy(sensorPathStr, "input", limitEventAttr);
168 std::ifstream eventFile(limitEventPath);
169 if (!eventFile.good())
170 {
171 continue;
172 }
173 eventPathList[eventName].push_back(limitEventPath);
174 }
175 }
176}
Cheng C Yang916360b2019-05-07 18:47:16 +0800177
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530178static void
179 checkPWMSensor(const fs::path& sensorPath, std::string& labelHead,
180 const std::string& interfacePath,
181 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
182 sdbusplus::asio::object_server& objectServer,
183 const std::string& psuName)
Cheng C Yang916360b2019-05-07 18:47:16 +0800184{
185 for (const auto& pwmName : pwmTable)
186 {
187 if (pwmName.first != labelHead)
188 {
189 continue;
190 }
191
192 const std::string& sensorPathStr = sensorPath.string();
193 const std::string& pwmPathStr =
194 boost::replace_all_copy(sensorPathStr, "input", "target");
195 std::ifstream pwmFile(pwmPathStr);
196 if (!pwmFile.good())
197 {
198 continue;
199 }
200
201 auto findPWMSensor = pwmSensors.find(psuName + labelHead);
202 if (findPWMSensor != pwmSensors.end())
203 {
204 continue;
205 }
206
207 pwmSensors[psuName + labelHead] = std::make_unique<PwmSensor>(
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530208 "Pwm_" + psuName + "_" + pwmName.second, pwmPathStr, dbusConnection,
209 objectServer, interfacePath + "_" + pwmName.second, "PSU");
Cheng C Yang916360b2019-05-07 18:47:16 +0800210 }
211}
212
213void createSensors(boost::asio::io_service& io,
214 sdbusplus::asio::object_server& objectServer,
215 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
Cheng C Yang209ec562019-03-12 16:37:44 +0800216{
217
218 ManagedObjectType sensorConfigs;
Josh Lehan49cfba92019-10-08 16:50:42 -0700219 int numCreated = 0;
Cheng C Yang209ec562019-03-12 16:37:44 +0800220 bool useCache = false;
221
Cheng C Yang58b2b532019-05-31 00:19:45 +0800222 // TODO may need only modify the ones that need to be changed.
223 sensors.clear();
Cheng C Yang209ec562019-03-12 16:37:44 +0800224 for (const char* type : sensorTypes)
225 {
226 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
227 useCache))
228 {
229 std::cerr << "error get sensor config from entity manager\n";
230 return;
231 }
232 useCache = true;
233 }
234
235 std::vector<fs::path> pmbusPaths;
236 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
237 {
238 std::cerr << "No PSU sensors in system\n";
239 return;
240 }
241
242 boost::container::flat_set<std::string> directories;
243 for (const auto& pmbusPath : pmbusPaths)
244 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800245 boost::container::flat_map<std::string, std::vector<std::string>>
246 eventPathList;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800247 boost::container::flat_map<
248 std::string,
249 boost::container::flat_map<std::string, std::vector<std::string>>>
250 groupEventPathList;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800251
252 std::ifstream nameFile(pmbusPath);
253 if (!nameFile.good())
254 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700255 std::cerr << "Failure finding pmbus path " << pmbusPath << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800256 continue;
257 }
258
259 std::string pmbusName;
260 std::getline(nameFile, pmbusName);
261 nameFile.close();
Vijay Khemka996bad12019-05-28 15:15:16 -0700262
263 if (std::find(pmbusNames.begin(), pmbusNames.end(), pmbusName) ==
264 pmbusNames.end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800265 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700266 // To avoid this error message, add your driver name to
267 // the pmbusNames vector at the top of this file.
268 std::cerr << "Driver name " << pmbusName
269 << " not found in sensor whitelist\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800270 continue;
271 }
272
273 const std::string* psuName;
Cheng C Yang209ec562019-03-12 16:37:44 +0800274 auto directory = pmbusPath.parent_path();
275
276 auto ret = directories.insert(directory.string());
277 if (!ret.second)
278 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700279 std::cerr << "Duplicate path " << directory.string() << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800280 continue; // check if path has already been searched
Cheng C Yang209ec562019-03-12 16:37:44 +0800281 }
282
James Feistb6c0b912019-07-09 12:21:44 -0700283 fs::path device = directory / "device";
Cheng C Yang209ec562019-03-12 16:37:44 +0800284 std::string deviceName = fs::canonical(device).stem();
285 auto findHyphen = deviceName.find("-");
286 if (findHyphen == std::string::npos)
287 {
288 std::cerr << "found bad device" << deviceName << "\n";
289 continue;
290 }
291 std::string busStr = deviceName.substr(0, findHyphen);
292 std::string addrStr = deviceName.substr(findHyphen + 1);
293
294 size_t bus = 0;
295 size_t addr = 0;
296
297 try
298 {
299 bus = std::stoi(busStr);
300 addr = std::stoi(addrStr, 0, 16);
301 }
James Feistb6c0b912019-07-09 12:21:44 -0700302 catch (std::invalid_argument&)
Cheng C Yang209ec562019-03-12 16:37:44 +0800303 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700304 std::cerr << "Error parsing bus " << busStr << " addr " << addrStr
305 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800306 continue;
307 }
308
Cheng C Yang209ec562019-03-12 16:37:44 +0800309 const std::pair<std::string, boost::container::flat_map<
310 std::string, BasicVariantType>>*
311 baseConfig = nullptr;
312 const SensorData* sensorData = nullptr;
313 const std::string* interfacePath = nullptr;
314 const char* sensorType = nullptr;
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800315 size_t thresholdConfSize = 0;
Cheng C Yang209ec562019-03-12 16:37:44 +0800316
317 for (const std::pair<sdbusplus::message::object_path, SensorData>&
318 sensor : sensorConfigs)
319 {
320 sensorData = &(sensor.second);
321 for (const char* type : sensorTypes)
322 {
323 auto sensorBase = sensorData->find(type);
324 if (sensorBase != sensorData->end())
325 {
326 baseConfig = &(*sensorBase);
327 sensorType = type;
328 break;
329 }
330 }
331 if (baseConfig == nullptr)
332 {
333 std::cerr << "error finding base configuration for "
334 << deviceName << "\n";
335 continue;
336 }
337
338 auto configBus = baseConfig->second.find("Bus");
339 auto configAddress = baseConfig->second.find("Address");
340
341 if (configBus == baseConfig->second.end() ||
342 configAddress == baseConfig->second.end())
343 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800344 std::cerr << "error finding necessary entry in configuration\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800345 continue;
346 }
347
Cheng C Yang58b2b532019-05-31 00:19:45 +0800348 const uint64_t* confBus;
349 const uint64_t* confAddr;
350 if (!(confBus = std::get_if<uint64_t>(&(configBus->second))) ||
351 !(confAddr = std::get_if<uint64_t>(&(configAddress->second))))
352 {
353 std::cerr
Josh Lehan49cfba92019-10-08 16:50:42 -0700354 << "Cannot get bus or address, invalid configuration\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800355 continue;
356 }
357
358 if ((*confBus != bus) || (*confAddr != addr))
Cheng C Yang209ec562019-03-12 16:37:44 +0800359 {
Josh Lehan432d1ed2019-10-16 12:23:31 -0700360 std::cerr << "Configuration skipping " << *confBus << "-"
361 << *confAddr << " because not " << bus << "-" << addr
362 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800363 continue;
364 }
365
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800366 std::vector<thresholds::Threshold> confThresholds;
367 if (!parseThresholdsFromConfig(*sensorData, confThresholds))
368 {
369 std::cerr << "error populating totoal thresholds\n";
370 }
371 thresholdConfSize = confThresholds.size();
372
Cheng C Yang209ec562019-03-12 16:37:44 +0800373 interfacePath = &(sensor.first.str);
374 break;
375 }
376 if (interfacePath == nullptr)
377 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700378 // To avoid this error message, add your export map entry,
379 // from Entity Manager, to sensorTypes at the top of this file.
Cheng C Yang209ec562019-03-12 16:37:44 +0800380 std::cerr << "failed to find match for " << deviceName << "\n";
381 continue;
382 }
383
Cheng C Yange50345b2019-04-02 17:26:15 +0800384 auto findPSUName = baseConfig->second.find("Name");
385 if (findPSUName == baseConfig->second.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800386 {
387 std::cerr << "could not determine configuration name for "
388 << deviceName << "\n";
389 continue;
390 }
391
Cheng C Yang58b2b532019-05-31 00:19:45 +0800392 if (!(psuName = std::get_if<std::string>(&(findPSUName->second))))
393 {
394 std::cerr << "Cannot find psu name, invalid configuration\n";
395 continue;
396 }
397 checkEvent(directory.string(), eventMatch, eventPathList);
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800398 checkGroupEvent(directory.string(), groupEventMatch,
399 groupEventPathList);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800400
Vijay Khemka996bad12019-05-28 15:15:16 -0700401 /* Check if there are more sensors in the same interface */
402 int i = 1;
403 std::vector<std::string> psuNames;
404 do
405 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700406 // Individual string fields: Name, Name1, Name2, Name3, ...
Vijay Khemka996bad12019-05-28 15:15:16 -0700407 psuNames.push_back(std::get<std::string>(findPSUName->second));
408 findPSUName = baseConfig->second.find("Name" + std::to_string(i++));
409 } while (findPSUName != baseConfig->second.end());
410
Cheng C Yange50345b2019-04-02 17:26:15 +0800411 std::vector<fs::path> sensorPaths;
James Feistb6c0b912019-07-09 12:21:44 -0700412 if (!findFiles(directory, R"(\w\d+_input$)", sensorPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800413 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800414 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800415 continue;
416 }
417
Vijay Khemka996bad12019-05-28 15:15:16 -0700418 /* Find array of labels to be exposed if it is defined in config */
419 std::vector<std::string> findLabels;
420 auto findLabelObj = baseConfig->second.find("Labels");
421 if (findLabelObj != baseConfig->second.end())
422 {
423 findLabels =
424 std::get<std::vector<std::string>>(findLabelObj->second);
425 }
426
Jason Ling5747fab2019-10-02 16:46:23 -0700427 std::regex sensorNameRegEx("([A-Za-z]+)[0-9]*_");
428 std::smatch matches;
429
Cheng C Yange50345b2019-04-02 17:26:15 +0800430 for (const auto& sensorPath : sensorPaths)
Cheng C Yang209ec562019-03-12 16:37:44 +0800431 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800432 std::string labelHead;
433 std::string sensorPathStr = sensorPath.string();
434 std::string sensorNameStr = sensorPath.filename();
Jason Ling5747fab2019-10-02 16:46:23 -0700435 std::string sensorNameSubStr{""};
436 if (std::regex_search(sensorNameStr, matches, sensorNameRegEx))
437 {
Josh Lehan06494452019-10-31 09:49:16 -0700438 // hwmon *_input filename without number:
439 // in, curr, power, temp, ...
Jason Ling5747fab2019-10-02 16:46:23 -0700440 sensorNameSubStr = matches[1];
441 }
442 else
443 {
Josh Lehan06494452019-10-31 09:49:16 -0700444 std::cerr << "Could not extract the alpha prefix from "
Jason Ling5747fab2019-10-02 16:46:23 -0700445 << sensorNameStr;
446 continue;
447 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800448
Cheng C Yangecba9de2019-09-12 23:41:50 +0800449 auto labelPath =
450 boost::replace_all_copy(sensorPathStr, "input", "label");
451 std::ifstream labelFile(labelPath);
452 if (!labelFile.good())
Cheng C Yang209ec562019-03-12 16:37:44 +0800453 {
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800454 if constexpr (DEBUG)
455 {
456 std::cerr << "Input file " << sensorPath
457 << " has no corresponding label file\n";
458 }
Josh Lehan06494452019-10-31 09:49:16 -0700459 // hwmon *_input filename with number:
460 // temp1, temp2, temp3, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800461 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800462 }
463 else
464 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800465 std::string label;
466 std::getline(labelFile, label);
467 labelFile.close();
Cheng C Yange50345b2019-04-02 17:26:15 +0800468 auto findSensor = sensors.find(label);
469 if (findSensor != sensors.end())
470 {
471 continue;
472 }
473
Josh Lehan06494452019-10-31 09:49:16 -0700474 // hwmon corresponding *_label file contents:
475 // vin1, vout1, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800476 labelHead = label.substr(0, label.find(" "));
477 }
478
Josh Lehan74d9bd92019-10-31 08:51:58 -0700479 if constexpr (DEBUG)
480 {
481 std::cerr << "Sensor type=\"" << sensorNameSubStr
482 << "\" label=\"" << labelHead << "\"\n";
483 }
484
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530485 checkPWMSensor(sensorPath, labelHead, *interfacePath,
486 dbusConnection, objectServer, psuNames[0]);
Cheng C Yang916360b2019-05-07 18:47:16 +0800487
Vijay Khemka996bad12019-05-28 15:15:16 -0700488 if (!findLabels.empty())
489 {
490 /* Check if this labelHead is enabled in config file */
491 if (std::find(findLabels.begin(), findLabels.end(),
492 labelHead) == findLabels.end())
493 {
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800494 if constexpr (DEBUG)
495 {
496 std::cerr << "could not find " << labelHead
497 << " in the Labels list\n";
498 }
Vijay Khemka996bad12019-05-28 15:15:16 -0700499 continue;
500 }
501 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800502
Cheng C Yange50345b2019-04-02 17:26:15 +0800503 auto findProperty = labelMatch.find(labelHead);
504 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800505 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700506 if constexpr (DEBUG)
507 {
508 std::cerr << "Could not find matching default property for "
509 << labelHead << "\n";
510 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800511 continue;
512 }
513
Josh Lehan74d9bd92019-10-31 08:51:58 -0700514 // Protect the hardcoded labelMatch list from changes,
515 // by making a copy and modifying that instead.
516 // Avoid bleedthrough of one device's customizations to
517 // the next device, as each should be independently customizable.
518 psuProperties.push_back(findProperty->second);
519 auto psuProperty = psuProperties.rbegin();
520
521 // Use label head as prefix for reading from config file,
522 // example if temp1: temp1_Name, temp1_Scale, temp1_Min, ...
523 std::string keyName = labelHead + "_Name";
524 std::string keyScale = labelHead + "_Scale";
525 std::string keyMin = labelHead + "_Min";
526 std::string keyMax = labelHead + "_Max";
527
528 bool customizedName = false;
529 auto findCustomName = baseConfig->second.find(keyName);
530 if (findCustomName != baseConfig->second.end())
Josh Lehan432d1ed2019-10-16 12:23:31 -0700531 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700532 try
533 {
534 psuProperty->labelTypeName = std::visit(
535 VariantToStringVisitor(), findCustomName->second);
536 }
537 catch (std::invalid_argument&)
538 {
539 std::cerr << "Unable to parse " << keyName << "\n";
540 continue;
541 }
542
543 // All strings are valid, including empty string
544 customizedName = true;
545 }
546
547 bool customizedScale = false;
548 auto findCustomScale = baseConfig->second.find(keyScale);
549 if (findCustomScale != baseConfig->second.end())
550 {
551 try
552 {
553 psuProperty->sensorScaleFactor = std::visit(
554 VariantToUnsignedIntVisitor(), findCustomScale->second);
555 }
556 catch (std::invalid_argument&)
557 {
558 std::cerr << "Unable to parse " << keyScale << "\n";
559 continue;
560 }
561
562 // Avoid later division by zero
563 if (psuProperty->sensorScaleFactor > 0)
564 {
565 customizedScale = true;
566 }
567 else
568 {
569 std::cerr << "Unable to accept " << keyScale << "\n";
570 continue;
571 }
572 }
573
574 auto findCustomMin = baseConfig->second.find(keyMin);
575 if (findCustomMin != baseConfig->second.end())
576 {
577 try
578 {
579 psuProperty->minReading = std::visit(
580 VariantToDoubleVisitor(), findCustomMin->second);
581 }
582 catch (std::invalid_argument&)
583 {
584 std::cerr << "Unable to parse " << keyMin << "\n";
585 continue;
586 }
587 }
588
589 auto findCustomMax = baseConfig->second.find(keyMax);
590 if (findCustomMax != baseConfig->second.end())
591 {
592 try
593 {
594 psuProperty->maxReading = std::visit(
595 VariantToDoubleVisitor(), findCustomMax->second);
596 }
597 catch (std::invalid_argument&)
598 {
599 std::cerr << "Unable to parse " << keyMax << "\n";
600 continue;
601 }
602 }
603
604 if (!(psuProperty->minReading < psuProperty->maxReading))
605 {
606 std::cerr << "Min must be less than Max\n";
607 continue;
608 }
609
610 // If the sensor name is being customized by config file,
611 // then prefix/suffix composition becomes not necessary,
612 // and in fact not wanted, because it gets in the way.
613 std::string psuNameFromIndex;
614 if (!customizedName)
615 {
616 /* Find out sensor name index for this label */
617 std::regex rgx("[A-Za-z]+([0-9]+)");
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500618 size_t nameIndex{0};
Josh Lehan74d9bd92019-10-31 08:51:58 -0700619 if (std::regex_search(labelHead, matches, rgx))
620 {
621 nameIndex = std::stoi(matches[1]);
622
623 // Decrement to preserve alignment, because hwmon
624 // human-readable filenames and labels use 1-based
625 // numbering, but the "Name", "Name1", "Name2", etc. naming
626 // convention (the psuNames vector) uses 0-based numbering.
627 if (nameIndex > 0)
628 {
629 --nameIndex;
630 }
631 }
632 else
633 {
634 nameIndex = 0;
635 }
636
637 if (psuNames.size() <= nameIndex)
638 {
639 std::cerr << "Could not pair " << labelHead
640 << " with a Name field\n";
641 continue;
642 }
643
644 psuNameFromIndex = psuNames[nameIndex];
645
646 if constexpr (DEBUG)
647 {
648 std::cerr << "Sensor label head " << labelHead
649 << " paired with " << psuNameFromIndex
650 << " at index " << nameIndex << "\n";
651 }
Josh Lehan432d1ed2019-10-16 12:23:31 -0700652 }
653
Cheng C Yang58b2b532019-05-31 00:19:45 +0800654 checkEventLimits(sensorPathStr, limitEventMatch, eventPathList);
655
Josh Lehan74d9bd92019-10-31 08:51:58 -0700656 // Similarly, if sensor scaling factor is being customized,
657 // then the below power-of-10 constraint becomes unnecessary,
658 // as config should be able to specify an arbitrary divisor.
659 unsigned int factor = psuProperty->sensorScaleFactor;
660 if (!customizedScale)
Vijay Khemka53ca4442019-07-23 11:03:55 -0700661 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700662 // Preserve existing usage of hardcoded labelMatch table below
663 factor = std::pow(10.0, factor);
Vijay Khemka53ca4442019-07-23 11:03:55 -0700664
Josh Lehan74d9bd92019-10-31 08:51:58 -0700665 /* Change first char of substring to uppercase */
666 char firstChar = sensorNameSubStr[0] - 0x20;
667 std::string strScaleFactor =
668 firstChar + sensorNameSubStr.substr(1) + "ScaleFactor";
669
670 // Preserve existing configs by accepting earlier syntax,
671 // example CurrScaleFactor, PowerScaleFactor, ...
672 auto findScaleFactor = baseConfig->second.find(strScaleFactor);
673 if (findScaleFactor != baseConfig->second.end())
674 {
675 factor = std::visit(VariantToIntVisitor(),
676 findScaleFactor->second);
677 }
678
679 if constexpr (DEBUG)
680 {
681 std::cerr << "Sensor scaling factor " << factor
682 << " string " << strScaleFactor << "\n";
683 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700684 }
685
Vijay Khemka996bad12019-05-28 15:15:16 -0700686 std::vector<thresholds::Threshold> sensorThresholds;
Joshi, Mansi14f0ad82019-11-21 10:52:30 +0530687 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds,
688 &labelHead))
Cheng C Yange50345b2019-04-02 17:26:15 +0800689 {
James Feist17ab6e02019-06-25 12:28:13 -0700690 std::cerr << "error populating thresholds for "
691 << sensorNameSubStr << "\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800692 }
693
694 auto findSensorType = sensorTable.find(sensorNameSubStr);
695 if (findSensorType == sensorTable.end())
696 {
Jason Ling5747fab2019-10-02 16:46:23 -0700697 std::cerr << sensorNameSubStr
Josh Lehan06494452019-10-31 09:49:16 -0700698 << " is not a recognized sensor type\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800699 continue;
700 }
701
Josh Lehan49cfba92019-10-08 16:50:42 -0700702 if constexpr (DEBUG)
703 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700704 std::cerr << "Sensor properties: Name \""
705 << psuProperty->labelTypeName << "\" Scale "
706 << psuProperty->sensorScaleFactor << " Min "
707 << psuProperty->minReading << " Max "
708 << psuProperty->maxReading << "\n";
709 }
710
711 std::string sensorName = psuProperty->labelTypeName;
712 if (customizedName)
713 {
714 if (sensorName.empty())
715 {
716 // Allow selective disabling of an individual sensor,
717 // by customizing its name to an empty string.
718 std::cerr << "Sensor disabled, empty string\n";
719 continue;
720 }
721 }
722 else
723 {
724 // Sensor name not customized, do prefix/suffix composition,
725 // preserving default behavior by using psuNameFromIndex.
726 sensorName =
727 psuNameFromIndex + " " + psuProperty->labelTypeName;
728 }
729
730 if constexpr (DEBUG)
731 {
732 std::cerr << "Sensor name \"" << sensorName << "\" path \""
733 << sensorPathStr << "\" type \"" << sensorType
734 << "\"\n";
Josh Lehan49cfba92019-10-08 16:50:42 -0700735 }
736
Yong Libf8b1da2020-04-15 16:32:50 +0800737 sensors[sensorName] = std::make_shared<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800738 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800739 sensorName, std::move(sensorThresholds), *interfacePath,
Josh Lehan74d9bd92019-10-31 08:51:58 -0700740 findSensorType->second, factor, psuProperty->maxReading,
Cheng C Yang6b1247a2020-03-09 23:48:39 +0800741 psuProperty->minReading, labelHead, thresholdConfSize);
Yong Libf8b1da2020-04-15 16:32:50 +0800742 sensors[sensorName]->setupRead();
Josh Lehan74d9bd92019-10-31 08:51:58 -0700743 ++numCreated;
744 if constexpr (DEBUG)
745 {
746 std::cerr << "Created " << numCreated << " sensors so far\n";
747 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800748 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800749
750 // OperationalStatus event
Cheng C Yang92498eb2019-09-26 21:59:25 +0800751 combineEvents[*psuName + "OperationalStatus"] = nullptr;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800752 combineEvents[*psuName + "OperationalStatus"] =
Yong Li3046a022020-04-03 13:01:02 +0800753 std::make_unique<PSUCombineEvent>(
754 objectServer, dbusConnection, io, *psuName, eventPathList,
755 groupEventPathList, "OperationalStatus");
Cheng C Yang209ec562019-03-12 16:37:44 +0800756 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700757
758 if constexpr (DEBUG)
759 {
760 std::cerr << "Created total of " << numCreated << " sensors\n";
761 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800762 return;
763}
764
Cheng C Yang916360b2019-05-07 18:47:16 +0800765void propertyInitialize(void)
Cheng C Yang209ec562019-03-12 16:37:44 +0800766{
Cheng C Yange50345b2019-04-02 17:26:15 +0800767 sensorTable = {{"power", "power/"},
768 {"curr", "current/"},
769 {"temp", "temperature/"},
770 {"in", "voltage/"},
771 {"fan", "fan_tach/"}};
772
773 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
774 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700775 {"pout2", PSUProperty("Output Power", 3000, 0, 6)},
776 {"pout3", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700777 {"power1", PSUProperty("Output Power", 3000, 0, 6)},
Cheng C Yangbdbde962019-04-26 22:50:34 +0800778 {"vin", PSUProperty("Input Voltage", 300, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700779 {"vout1", PSUProperty("Output Voltage", 255, 0, 3)},
780 {"vout2", PSUProperty("Output Voltage", 255, 0, 3)},
781 {"vout3", PSUProperty("Output Voltage", 255, 0, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700782 {"vout4", PSUProperty("Output Voltage", 255, 0, 3)},
783 {"vout5", PSUProperty("Output Voltage", 255, 0, 3)},
784 {"vout6", PSUProperty("Output Voltage", 255, 0, 3)},
785 {"vout7", PSUProperty("Output Voltage", 255, 0, 3)},
786 {"vout8", PSUProperty("Output Voltage", 255, 0, 3)},
787 {"vout9", PSUProperty("Output Voltage", 255, 0, 3)},
788 {"vout10", PSUProperty("Output Voltage", 255, 0, 3)},
789 {"vout11", PSUProperty("Output Voltage", 255, 0, 3)},
790 {"vout12", PSUProperty("Output Voltage", 255, 0, 3)},
791 {"vout13", PSUProperty("Output Voltage", 255, 0, 3)},
792 {"vout14", PSUProperty("Output Voltage", 255, 0, 3)},
793 {"vout15", PSUProperty("Output Voltage", 255, 0, 3)},
794 {"vout16", PSUProperty("Output Voltage", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700795 {"in1", PSUProperty("Output Voltage", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800796 {"iin", PSUProperty("Input Current", 20, 0, 3)},
797 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700798 {"iout2", PSUProperty("Output Current", 255, 0, 3)},
799 {"iout3", PSUProperty("Output Current", 255, 0, 3)},
Peter Lundgren48b44232020-01-30 16:02:11 -0800800 {"iout4", PSUProperty("Output Current", 255, 0, 3)},
801 {"iout5", PSUProperty("Output Current", 255, 0, 3)},
802 {"iout6", PSUProperty("Output Current", 255, 0, 3)},
803 {"iout7", PSUProperty("Output Current", 255, 0, 3)},
804 {"iout8", PSUProperty("Output Current", 255, 0, 3)},
805 {"iout9", PSUProperty("Output Current", 255, 0, 3)},
806 {"iout10", PSUProperty("Output Current", 255, 0, 3)},
807 {"iout11", PSUProperty("Output Current", 255, 0, 3)},
808 {"iout12", PSUProperty("Output Current", 255, 0, 3)},
809 {"iout13", PSUProperty("Output Current", 255, 0, 3)},
810 {"iout14", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700811 {"curr1", PSUProperty("Output Current", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800812 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700813 {"temp2", PSUProperty("Temperature", 127, -128, 3)},
814 {"temp3", PSUProperty("Temperature", 127, -128, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700815 {"temp4", PSUProperty("Temperature", 127, -128, 3)},
816 {"temp5", PSUProperty("Temperature", 127, -128, 3)},
Alex Qiuf5709b42020-01-29 13:29:50 -0800817 {"temp6", PSUProperty("Temperature", 127, -128, 3)},
Cheng C Yang8dbb3952019-05-23 00:03:12 +0800818 {"fan1", PSUProperty("Fan Speed 1", 30000, 0, 0)},
819 {"fan2", PSUProperty("Fan Speed 2", 30000, 0, 0)}};
Cheng C Yang916360b2019-05-07 18:47:16 +0800820
821 pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
Cheng C Yang58b2b532019-05-31 00:19:45 +0800822
823 limitEventMatch = {{"PredictiveFailure", {"max_alarm", "min_alarm"}},
824 {"Failure", {"crit_alarm", "lcrit_alarm"}}};
825
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800826 eventMatch = {{"PredictiveFailure", {"power1_alarm"}},
827 {"Failure", {"in2_alarm"}},
828 {"ACLost", {"in1_beep"}},
829 {"ConfigureError", {"in1_fault"}}};
830
831 groupEventMatch = {{"FanFault",
832 {{"fan1", {"fan1_alarm", "fan1_fault"}},
833 {"fan2", {"fan2_alarm", "fan2_fault"}}}}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800834}
835
James Feistb6c0b912019-07-09 12:21:44 -0700836int main()
Cheng C Yang209ec562019-03-12 16:37:44 +0800837{
838 boost::asio::io_service io;
839 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
840
841 systemBus->request_name("xyz.openbmc_project.PSUSensor");
842 sdbusplus::asio::object_server objectServer(systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800843 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yang209ec562019-03-12 16:37:44 +0800844
Cheng C Yang916360b2019-05-07 18:47:16 +0800845 propertyInitialize();
Cheng C Yang209ec562019-03-12 16:37:44 +0800846
Cheng C Yang916360b2019-05-07 18:47:16 +0800847 io.post([&]() { createSensors(io, objectServer, systemBus); });
Cheng C Yang209ec562019-03-12 16:37:44 +0800848 boost::asio::deadline_timer filterTimer(io);
849 std::function<void(sdbusplus::message::message&)> eventHandler =
850 [&](sdbusplus::message::message& message) {
851 if (message.is_method_error())
852 {
853 std::cerr << "callback method error\n";
854 return;
855 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800856 filterTimer.expires_from_now(boost::posix_time::seconds(3));
Cheng C Yang209ec562019-03-12 16:37:44 +0800857 filterTimer.async_wait([&](const boost::system::error_code& ec) {
858 if (ec == boost::asio::error::operation_aborted)
859 {
860 return;
861 }
862 else if (ec)
863 {
864 std::cerr << "timer error\n";
865 }
Cheng C Yang916360b2019-05-07 18:47:16 +0800866 createSensors(io, objectServer, systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800867 });
868 };
869
870 for (const char* type : sensorTypes)
871 {
872 auto match = std::make_unique<sdbusplus::bus::match::match>(
873 static_cast<sdbusplus::bus::bus&>(*systemBus),
874 "type='signal',member='PropertiesChanged',path_namespace='" +
875 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
876 eventHandler);
877 matches.emplace_back(std::move(match));
878 }
879 io.run();
880}