blob: 141b2ea3a6a677f9584cd4f9fcccef81804c4f8a [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;
Cheng C Yang202a1ff2020-01-09 09:34:22 +080064static boost::container::flat_map<
65 std::string,
66 boost::container::flat_map<std::string, std::vector<std::string>>>
67 groupEventMatch;
Cheng C Yang58b2b532019-05-31 00:19:45 +080068static boost::container::flat_map<std::string, std::vector<std::string>>
69 limitEventMatch;
70
Josh Lehan74d9bd92019-10-31 08:51:58 -070071static std::vector<PSUProperty> psuProperties;
72
Cheng C Yang58b2b532019-05-31 00:19:45 +080073// Function CheckEvent will check each attribute from eventMatch table in the
74// sysfs. If the attributes exists in sysfs, then store the complete path
75// of the attribute into eventPathList.
76void checkEvent(
77 const std::string& directory,
78 const boost::container::flat_map<std::string, std::vector<std::string>>&
79 eventMatch,
80 boost::container::flat_map<std::string, std::vector<std::string>>&
81 eventPathList)
82{
83 for (const auto& match : eventMatch)
84 {
85 const std::vector<std::string>& eventAttrs = match.second;
86 const std::string& eventName = match.first;
87 for (const auto& eventAttr : eventAttrs)
88 {
89 auto eventPath = directory + "/" + eventAttr;
90
91 std::ifstream eventFile(eventPath);
92 if (!eventFile.good())
93 {
94 continue;
95 }
96
97 eventPathList[eventName].push_back(eventPath);
98 }
99 }
100}
101
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800102// Check Group Events which contains more than one targets in each combine
103// events.
104void checkGroupEvent(
105 const std::string& directory,
106 const boost::container::flat_map<
107 std::string,
108 boost::container::flat_map<std::string, std::vector<std::string>>>&
109 groupEventMatch,
110 boost::container::flat_map<
111 std::string,
112 boost::container::flat_map<std::string, std::vector<std::string>>>&
113 groupEventPathList)
114{
115 for (const auto& match : groupEventMatch)
116 {
117 const std::string& groupEventName = match.first;
118 const boost::container::flat_map<std::string, std::vector<std::string>>
119 events = match.second;
120 boost::container::flat_map<std::string, std::vector<std::string>>
121 pathList;
122 for (const auto& match : events)
123 {
124 const std::string& eventName = match.first;
125 const std::vector<std::string>& eventAttrs = match.second;
126 for (const auto& eventAttr : eventAttrs)
127 {
128 auto eventPath = directory + "/" + eventAttr;
129 std::ifstream eventFile(eventPath);
130 if (!eventFile.good())
131 {
132 continue;
133 }
134
135 pathList[eventName].push_back(eventPath);
136 }
137 }
138 groupEventPathList[groupEventName] = pathList;
139 }
140}
141
Cheng C Yang58b2b532019-05-31 00:19:45 +0800142// Function checkEventLimits will check all the psu related xxx_input attributes
143// in sysfs to see if xxx_crit_alarm xxx_lcrit_alarm xxx_max_alarm
144// xxx_min_alarm exist, then store the existing paths of the alarm attributes
145// to eventPathList.
146void checkEventLimits(
147 const std::string& sensorPathStr,
148 const boost::container::flat_map<std::string, std::vector<std::string>>&
149 limitEventMatch,
150 boost::container::flat_map<std::string, std::vector<std::string>>&
151 eventPathList)
152{
153 for (const auto& limitMatch : limitEventMatch)
154 {
155 const std::vector<std::string>& limitEventAttrs = limitMatch.second;
156 const std::string& eventName = limitMatch.first;
157 for (const auto& limitEventAttr : limitEventAttrs)
158 {
159 auto limitEventPath =
160 boost::replace_all_copy(sensorPathStr, "input", limitEventAttr);
161 std::ifstream eventFile(limitEventPath);
162 if (!eventFile.good())
163 {
164 continue;
165 }
166 eventPathList[eventName].push_back(limitEventPath);
167 }
168 }
169}
Cheng C Yang916360b2019-05-07 18:47:16 +0800170
171static void checkPWMSensor(const fs::path& sensorPath, std::string& labelHead,
172 const std::string& interfacePath,
173 sdbusplus::asio::object_server& objectServer,
Patrick Venturea2b2d842019-10-11 07:48:47 -0700174 const std::string& psuName)
Cheng C Yang916360b2019-05-07 18:47:16 +0800175{
176 for (const auto& pwmName : pwmTable)
177 {
178 if (pwmName.first != labelHead)
179 {
180 continue;
181 }
182
183 const std::string& sensorPathStr = sensorPath.string();
184 const std::string& pwmPathStr =
185 boost::replace_all_copy(sensorPathStr, "input", "target");
186 std::ifstream pwmFile(pwmPathStr);
187 if (!pwmFile.good())
188 {
189 continue;
190 }
191
192 auto findPWMSensor = pwmSensors.find(psuName + labelHead);
193 if (findPWMSensor != pwmSensors.end())
194 {
195 continue;
196 }
197
198 pwmSensors[psuName + labelHead] = std::make_unique<PwmSensor>(
199 "Pwm_" + psuName + "_" + pwmName.second, pwmPathStr, objectServer,
Cheng C Yang15266a92019-06-12 08:42:52 +0800200 interfacePath + "_" + pwmName.second);
Cheng C Yang916360b2019-05-07 18:47:16 +0800201 }
202}
203
204void createSensors(boost::asio::io_service& io,
205 sdbusplus::asio::object_server& objectServer,
206 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
Cheng C Yang209ec562019-03-12 16:37:44 +0800207{
208
209 ManagedObjectType sensorConfigs;
Josh Lehan49cfba92019-10-08 16:50:42 -0700210 int numCreated = 0;
Cheng C Yang209ec562019-03-12 16:37:44 +0800211 bool useCache = false;
212
Cheng C Yang58b2b532019-05-31 00:19:45 +0800213 // TODO may need only modify the ones that need to be changed.
214 sensors.clear();
Cheng C Yang92498eb2019-09-26 21:59:25 +0800215 combineEvents.clear();
Cheng C Yang209ec562019-03-12 16:37:44 +0800216 for (const char* type : sensorTypes)
217 {
218 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
219 useCache))
220 {
221 std::cerr << "error get sensor config from entity manager\n";
222 return;
223 }
224 useCache = true;
225 }
226
227 std::vector<fs::path> pmbusPaths;
228 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
229 {
230 std::cerr << "No PSU sensors in system\n";
231 return;
232 }
233
234 boost::container::flat_set<std::string> directories;
235 for (const auto& pmbusPath : pmbusPaths)
236 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800237 boost::container::flat_map<std::string, std::vector<std::string>>
238 eventPathList;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800239 boost::container::flat_map<
240 std::string,
241 boost::container::flat_map<std::string, std::vector<std::string>>>
242 groupEventPathList;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800243
244 std::ifstream nameFile(pmbusPath);
245 if (!nameFile.good())
246 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700247 std::cerr << "Failure finding pmbus path " << pmbusPath << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800248 continue;
249 }
250
251 std::string pmbusName;
252 std::getline(nameFile, pmbusName);
253 nameFile.close();
Vijay Khemka996bad12019-05-28 15:15:16 -0700254
255 if (std::find(pmbusNames.begin(), pmbusNames.end(), pmbusName) ==
256 pmbusNames.end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800257 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700258 // To avoid this error message, add your driver name to
259 // the pmbusNames vector at the top of this file.
260 std::cerr << "Driver name " << pmbusName
261 << " not found in sensor whitelist\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800262 continue;
263 }
264
265 const std::string* psuName;
Cheng C Yang209ec562019-03-12 16:37:44 +0800266 auto directory = pmbusPath.parent_path();
267
268 auto ret = directories.insert(directory.string());
269 if (!ret.second)
270 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700271 std::cerr << "Duplicate path " << directory.string() << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800272 continue; // check if path has already been searched
Cheng C Yang209ec562019-03-12 16:37:44 +0800273 }
274
James Feistb6c0b912019-07-09 12:21:44 -0700275 fs::path device = directory / "device";
Cheng C Yang209ec562019-03-12 16:37:44 +0800276 std::string deviceName = fs::canonical(device).stem();
277 auto findHyphen = deviceName.find("-");
278 if (findHyphen == std::string::npos)
279 {
280 std::cerr << "found bad device" << deviceName << "\n";
281 continue;
282 }
283 std::string busStr = deviceName.substr(0, findHyphen);
284 std::string addrStr = deviceName.substr(findHyphen + 1);
285
286 size_t bus = 0;
287 size_t addr = 0;
288
289 try
290 {
291 bus = std::stoi(busStr);
292 addr = std::stoi(addrStr, 0, 16);
293 }
James Feistb6c0b912019-07-09 12:21:44 -0700294 catch (std::invalid_argument&)
Cheng C Yang209ec562019-03-12 16:37:44 +0800295 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700296 std::cerr << "Error parsing bus " << busStr << " addr " << addrStr
297 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800298 continue;
299 }
300
Cheng C Yang209ec562019-03-12 16:37:44 +0800301 const std::pair<std::string, boost::container::flat_map<
302 std::string, BasicVariantType>>*
303 baseConfig = nullptr;
304 const SensorData* sensorData = nullptr;
305 const std::string* interfacePath = nullptr;
306 const char* sensorType = nullptr;
307
308 for (const std::pair<sdbusplus::message::object_path, SensorData>&
309 sensor : sensorConfigs)
310 {
311 sensorData = &(sensor.second);
312 for (const char* type : sensorTypes)
313 {
314 auto sensorBase = sensorData->find(type);
315 if (sensorBase != sensorData->end())
316 {
317 baseConfig = &(*sensorBase);
318 sensorType = type;
319 break;
320 }
321 }
322 if (baseConfig == nullptr)
323 {
324 std::cerr << "error finding base configuration for "
325 << deviceName << "\n";
326 continue;
327 }
328
329 auto configBus = baseConfig->second.find("Bus");
330 auto configAddress = baseConfig->second.find("Address");
331
332 if (configBus == baseConfig->second.end() ||
333 configAddress == baseConfig->second.end())
334 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800335 std::cerr << "error finding necessary entry in configuration\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800336 continue;
337 }
338
Cheng C Yang58b2b532019-05-31 00:19:45 +0800339 const uint64_t* confBus;
340 const uint64_t* confAddr;
341 if (!(confBus = std::get_if<uint64_t>(&(configBus->second))) ||
342 !(confAddr = std::get_if<uint64_t>(&(configAddress->second))))
343 {
344 std::cerr
Josh Lehan49cfba92019-10-08 16:50:42 -0700345 << "Cannot get bus or address, invalid configuration\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800346 continue;
347 }
348
349 if ((*confBus != bus) || (*confAddr != addr))
Cheng C Yang209ec562019-03-12 16:37:44 +0800350 {
Josh Lehan432d1ed2019-10-16 12:23:31 -0700351 std::cerr << "Configuration skipping " << *confBus << "-"
352 << *confAddr << " because not " << bus << "-" << addr
353 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800354 continue;
355 }
356
357 interfacePath = &(sensor.first.str);
358 break;
359 }
360 if (interfacePath == nullptr)
361 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700362 // To avoid this error message, add your export map entry,
363 // from Entity Manager, to sensorTypes at the top of this file.
Cheng C Yang209ec562019-03-12 16:37:44 +0800364 std::cerr << "failed to find match for " << deviceName << "\n";
365 continue;
366 }
367
Cheng C Yange50345b2019-04-02 17:26:15 +0800368 auto findPSUName = baseConfig->second.find("Name");
369 if (findPSUName == baseConfig->second.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800370 {
371 std::cerr << "could not determine configuration name for "
372 << deviceName << "\n";
373 continue;
374 }
375
Cheng C Yang58b2b532019-05-31 00:19:45 +0800376 if (!(psuName = std::get_if<std::string>(&(findPSUName->second))))
377 {
378 std::cerr << "Cannot find psu name, invalid configuration\n";
379 continue;
380 }
381 checkEvent(directory.string(), eventMatch, eventPathList);
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800382 checkGroupEvent(directory.string(), groupEventMatch,
383 groupEventPathList);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800384
Vijay Khemka996bad12019-05-28 15:15:16 -0700385 /* Check if there are more sensors in the same interface */
386 int i = 1;
387 std::vector<std::string> psuNames;
388 do
389 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700390 // Individual string fields: Name, Name1, Name2, Name3, ...
Vijay Khemka996bad12019-05-28 15:15:16 -0700391 psuNames.push_back(std::get<std::string>(findPSUName->second));
392 findPSUName = baseConfig->second.find("Name" + std::to_string(i++));
393 } while (findPSUName != baseConfig->second.end());
394
Cheng C Yange50345b2019-04-02 17:26:15 +0800395 std::vector<fs::path> sensorPaths;
James Feistb6c0b912019-07-09 12:21:44 -0700396 if (!findFiles(directory, R"(\w\d+_input$)", sensorPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800397 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800398 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800399 continue;
400 }
401
Vijay Khemka996bad12019-05-28 15:15:16 -0700402 /* Find array of labels to be exposed if it is defined in config */
403 std::vector<std::string> findLabels;
404 auto findLabelObj = baseConfig->second.find("Labels");
405 if (findLabelObj != baseConfig->second.end())
406 {
407 findLabels =
408 std::get<std::vector<std::string>>(findLabelObj->second);
409 }
410
Jason Ling5747fab2019-10-02 16:46:23 -0700411 std::regex sensorNameRegEx("([A-Za-z]+)[0-9]*_");
412 std::smatch matches;
413
Cheng C Yange50345b2019-04-02 17:26:15 +0800414 for (const auto& sensorPath : sensorPaths)
Cheng C Yang209ec562019-03-12 16:37:44 +0800415 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800416 std::string labelHead;
417 std::string sensorPathStr = sensorPath.string();
418 std::string sensorNameStr = sensorPath.filename();
Jason Ling5747fab2019-10-02 16:46:23 -0700419 std::string sensorNameSubStr{""};
420 if (std::regex_search(sensorNameStr, matches, sensorNameRegEx))
421 {
Josh Lehan06494452019-10-31 09:49:16 -0700422 // hwmon *_input filename without number:
423 // in, curr, power, temp, ...
Jason Ling5747fab2019-10-02 16:46:23 -0700424 sensorNameSubStr = matches[1];
425 }
426 else
427 {
Josh Lehan06494452019-10-31 09:49:16 -0700428 std::cerr << "Could not extract the alpha prefix from "
Jason Ling5747fab2019-10-02 16:46:23 -0700429 << sensorNameStr;
430 continue;
431 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800432
Cheng C Yangecba9de2019-09-12 23:41:50 +0800433 auto labelPath =
434 boost::replace_all_copy(sensorPathStr, "input", "label");
435 std::ifstream labelFile(labelPath);
436 if (!labelFile.good())
Cheng C Yang209ec562019-03-12 16:37:44 +0800437 {
Josh Lehan432d1ed2019-10-16 12:23:31 -0700438 std::cerr << "Input file " << sensorPath
439 << " has no corresponding label file\n";
Josh Lehan06494452019-10-31 09:49:16 -0700440
441 // hwmon *_input filename with number:
442 // temp1, temp2, temp3, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800443 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800444 }
445 else
446 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800447 std::string label;
448 std::getline(labelFile, label);
449 labelFile.close();
Cheng C Yange50345b2019-04-02 17:26:15 +0800450 auto findSensor = sensors.find(label);
451 if (findSensor != sensors.end())
452 {
453 continue;
454 }
455
Josh Lehan06494452019-10-31 09:49:16 -0700456 // hwmon corresponding *_label file contents:
457 // vin1, vout1, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800458 labelHead = label.substr(0, label.find(" "));
459 }
460
Josh Lehan74d9bd92019-10-31 08:51:58 -0700461 if constexpr (DEBUG)
462 {
463 std::cerr << "Sensor type=\"" << sensorNameSubStr
464 << "\" label=\"" << labelHead << "\"\n";
465 }
466
Cheng C Yang916360b2019-05-07 18:47:16 +0800467 checkPWMSensor(sensorPath, labelHead, *interfacePath, objectServer,
Vijay Khemka996bad12019-05-28 15:15:16 -0700468 psuNames[0]);
Cheng C Yang916360b2019-05-07 18:47:16 +0800469
Vijay Khemka996bad12019-05-28 15:15:16 -0700470 if (!findLabels.empty())
471 {
472 /* Check if this labelHead is enabled in config file */
473 if (std::find(findLabels.begin(), findLabels.end(),
474 labelHead) == findLabels.end())
475 {
Josh Lehan06494452019-10-31 09:49:16 -0700476 std::cerr << "could not find " << labelHead
Jason Ling5747fab2019-10-02 16:46:23 -0700477 << " in the Labels list\n";
Vijay Khemka996bad12019-05-28 15:15:16 -0700478 continue;
479 }
480 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800481
Cheng C Yange50345b2019-04-02 17:26:15 +0800482 auto findProperty = labelMatch.find(labelHead);
483 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800484 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700485 if constexpr (DEBUG)
486 {
487 std::cerr << "Could not find matching default property for "
488 << labelHead << "\n";
489 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800490 continue;
491 }
492
Josh Lehan74d9bd92019-10-31 08:51:58 -0700493 // Protect the hardcoded labelMatch list from changes,
494 // by making a copy and modifying that instead.
495 // Avoid bleedthrough of one device's customizations to
496 // the next device, as each should be independently customizable.
497 psuProperties.push_back(findProperty->second);
498 auto psuProperty = psuProperties.rbegin();
499
500 // Use label head as prefix for reading from config file,
501 // example if temp1: temp1_Name, temp1_Scale, temp1_Min, ...
502 std::string keyName = labelHead + "_Name";
503 std::string keyScale = labelHead + "_Scale";
504 std::string keyMin = labelHead + "_Min";
505 std::string keyMax = labelHead + "_Max";
506
507 bool customizedName = false;
508 auto findCustomName = baseConfig->second.find(keyName);
509 if (findCustomName != baseConfig->second.end())
Josh Lehan432d1ed2019-10-16 12:23:31 -0700510 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700511 try
512 {
513 psuProperty->labelTypeName = std::visit(
514 VariantToStringVisitor(), findCustomName->second);
515 }
516 catch (std::invalid_argument&)
517 {
518 std::cerr << "Unable to parse " << keyName << "\n";
519 continue;
520 }
521
522 // All strings are valid, including empty string
523 customizedName = true;
524 }
525
526 bool customizedScale = false;
527 auto findCustomScale = baseConfig->second.find(keyScale);
528 if (findCustomScale != baseConfig->second.end())
529 {
530 try
531 {
532 psuProperty->sensorScaleFactor = std::visit(
533 VariantToUnsignedIntVisitor(), findCustomScale->second);
534 }
535 catch (std::invalid_argument&)
536 {
537 std::cerr << "Unable to parse " << keyScale << "\n";
538 continue;
539 }
540
541 // Avoid later division by zero
542 if (psuProperty->sensorScaleFactor > 0)
543 {
544 customizedScale = true;
545 }
546 else
547 {
548 std::cerr << "Unable to accept " << keyScale << "\n";
549 continue;
550 }
551 }
552
553 auto findCustomMin = baseConfig->second.find(keyMin);
554 if (findCustomMin != baseConfig->second.end())
555 {
556 try
557 {
558 psuProperty->minReading = std::visit(
559 VariantToDoubleVisitor(), findCustomMin->second);
560 }
561 catch (std::invalid_argument&)
562 {
563 std::cerr << "Unable to parse " << keyMin << "\n";
564 continue;
565 }
566 }
567
568 auto findCustomMax = baseConfig->second.find(keyMax);
569 if (findCustomMax != baseConfig->second.end())
570 {
571 try
572 {
573 psuProperty->maxReading = std::visit(
574 VariantToDoubleVisitor(), findCustomMax->second);
575 }
576 catch (std::invalid_argument&)
577 {
578 std::cerr << "Unable to parse " << keyMax << "\n";
579 continue;
580 }
581 }
582
583 if (!(psuProperty->minReading < psuProperty->maxReading))
584 {
585 std::cerr << "Min must be less than Max\n";
586 continue;
587 }
588
589 // If the sensor name is being customized by config file,
590 // then prefix/suffix composition becomes not necessary,
591 // and in fact not wanted, because it gets in the way.
592 std::string psuNameFromIndex;
593 if (!customizedName)
594 {
595 /* Find out sensor name index for this label */
596 std::regex rgx("[A-Za-z]+([0-9]+)");
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500597 size_t nameIndex{0};
Josh Lehan74d9bd92019-10-31 08:51:58 -0700598 if (std::regex_search(labelHead, matches, rgx))
599 {
600 nameIndex = std::stoi(matches[1]);
601
602 // Decrement to preserve alignment, because hwmon
603 // human-readable filenames and labels use 1-based
604 // numbering, but the "Name", "Name1", "Name2", etc. naming
605 // convention (the psuNames vector) uses 0-based numbering.
606 if (nameIndex > 0)
607 {
608 --nameIndex;
609 }
610 }
611 else
612 {
613 nameIndex = 0;
614 }
615
616 if (psuNames.size() <= nameIndex)
617 {
618 std::cerr << "Could not pair " << labelHead
619 << " with a Name field\n";
620 continue;
621 }
622
623 psuNameFromIndex = psuNames[nameIndex];
624
625 if constexpr (DEBUG)
626 {
627 std::cerr << "Sensor label head " << labelHead
628 << " paired with " << psuNameFromIndex
629 << " at index " << nameIndex << "\n";
630 }
Josh Lehan432d1ed2019-10-16 12:23:31 -0700631 }
632
Cheng C Yang58b2b532019-05-31 00:19:45 +0800633 checkEventLimits(sensorPathStr, limitEventMatch, eventPathList);
634
Josh Lehan74d9bd92019-10-31 08:51:58 -0700635 // Similarly, if sensor scaling factor is being customized,
636 // then the below power-of-10 constraint becomes unnecessary,
637 // as config should be able to specify an arbitrary divisor.
638 unsigned int factor = psuProperty->sensorScaleFactor;
639 if (!customizedScale)
Vijay Khemka53ca4442019-07-23 11:03:55 -0700640 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700641 // Preserve existing usage of hardcoded labelMatch table below
642 factor = std::pow(10.0, factor);
Vijay Khemka53ca4442019-07-23 11:03:55 -0700643
Josh Lehan74d9bd92019-10-31 08:51:58 -0700644 /* Change first char of substring to uppercase */
645 char firstChar = sensorNameSubStr[0] - 0x20;
646 std::string strScaleFactor =
647 firstChar + sensorNameSubStr.substr(1) + "ScaleFactor";
648
649 // Preserve existing configs by accepting earlier syntax,
650 // example CurrScaleFactor, PowerScaleFactor, ...
651 auto findScaleFactor = baseConfig->second.find(strScaleFactor);
652 if (findScaleFactor != baseConfig->second.end())
653 {
654 factor = std::visit(VariantToIntVisitor(),
655 findScaleFactor->second);
656 }
657
658 if constexpr (DEBUG)
659 {
660 std::cerr << "Sensor scaling factor " << factor
661 << " string " << strScaleFactor << "\n";
662 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700663 }
664
Vijay Khemka996bad12019-05-28 15:15:16 -0700665 std::vector<thresholds::Threshold> sensorThresholds;
666
Joshi, Mansi14f0ad82019-11-21 10:52:30 +0530667 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds,
668 &labelHead))
Cheng C Yange50345b2019-04-02 17:26:15 +0800669 {
James Feist17ab6e02019-06-25 12:28:13 -0700670 std::cerr << "error populating thresholds for "
671 << sensorNameSubStr << "\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800672 }
673
674 auto findSensorType = sensorTable.find(sensorNameSubStr);
675 if (findSensorType == sensorTable.end())
676 {
Jason Ling5747fab2019-10-02 16:46:23 -0700677 std::cerr << sensorNameSubStr
Josh Lehan06494452019-10-31 09:49:16 -0700678 << " is not a recognized sensor type\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800679 continue;
680 }
681
Josh Lehan49cfba92019-10-08 16:50:42 -0700682 if constexpr (DEBUG)
683 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700684 std::cerr << "Sensor properties: Name \""
685 << psuProperty->labelTypeName << "\" Scale "
686 << psuProperty->sensorScaleFactor << " Min "
687 << psuProperty->minReading << " Max "
688 << psuProperty->maxReading << "\n";
689 }
690
691 std::string sensorName = psuProperty->labelTypeName;
692 if (customizedName)
693 {
694 if (sensorName.empty())
695 {
696 // Allow selective disabling of an individual sensor,
697 // by customizing its name to an empty string.
698 std::cerr << "Sensor disabled, empty string\n";
699 continue;
700 }
701 }
702 else
703 {
704 // Sensor name not customized, do prefix/suffix composition,
705 // preserving default behavior by using psuNameFromIndex.
706 sensorName =
707 psuNameFromIndex + " " + psuProperty->labelTypeName;
708 }
709
710 if constexpr (DEBUG)
711 {
712 std::cerr << "Sensor name \"" << sensorName << "\" path \""
713 << sensorPathStr << "\" type \"" << sensorType
714 << "\"\n";
Josh Lehan49cfba92019-10-08 16:50:42 -0700715 }
716
Cheng C Yang58b2b532019-05-31 00:19:45 +0800717 sensors[sensorName] = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800718 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800719 sensorName, std::move(sensorThresholds), *interfacePath,
Josh Lehan74d9bd92019-10-31 08:51:58 -0700720 findSensorType->second, factor, psuProperty->maxReading,
721 psuProperty->minReading);
722
723 ++numCreated;
724 if constexpr (DEBUG)
725 {
726 std::cerr << "Created " << numCreated << " sensors so far\n";
727 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800728 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800729
730 // OperationalStatus event
Cheng C Yang92498eb2019-09-26 21:59:25 +0800731 combineEvents[*psuName + "OperationalStatus"] = nullptr;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800732 combineEvents[*psuName + "OperationalStatus"] =
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800733 std::make_unique<PSUCombineEvent>(objectServer, io, *psuName,
734 eventPathList, groupEventPathList,
735 "OperationalStatus");
Cheng C Yang209ec562019-03-12 16:37:44 +0800736 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700737
738 if constexpr (DEBUG)
739 {
740 std::cerr << "Created total of " << numCreated << " sensors\n";
741 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800742 return;
743}
744
Cheng C Yang916360b2019-05-07 18:47:16 +0800745void propertyInitialize(void)
Cheng C Yang209ec562019-03-12 16:37:44 +0800746{
Cheng C Yange50345b2019-04-02 17:26:15 +0800747 sensorTable = {{"power", "power/"},
748 {"curr", "current/"},
749 {"temp", "temperature/"},
750 {"in", "voltage/"},
751 {"fan", "fan_tach/"}};
752
753 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
754 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700755 {"pout2", PSUProperty("Output Power", 3000, 0, 6)},
756 {"pout3", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700757 {"power1", PSUProperty("Output Power", 3000, 0, 6)},
Cheng C Yangbdbde962019-04-26 22:50:34 +0800758 {"vin", PSUProperty("Input Voltage", 300, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700759 {"vout1", PSUProperty("Output Voltage", 255, 0, 3)},
760 {"vout2", PSUProperty("Output Voltage", 255, 0, 3)},
761 {"vout3", PSUProperty("Output Voltage", 255, 0, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700762 {"vout4", PSUProperty("Output Voltage", 255, 0, 3)},
763 {"vout5", PSUProperty("Output Voltage", 255, 0, 3)},
764 {"vout6", PSUProperty("Output Voltage", 255, 0, 3)},
765 {"vout7", PSUProperty("Output Voltage", 255, 0, 3)},
766 {"vout8", PSUProperty("Output Voltage", 255, 0, 3)},
767 {"vout9", PSUProperty("Output Voltage", 255, 0, 3)},
768 {"vout10", PSUProperty("Output Voltage", 255, 0, 3)},
769 {"vout11", PSUProperty("Output Voltage", 255, 0, 3)},
770 {"vout12", PSUProperty("Output Voltage", 255, 0, 3)},
771 {"vout13", PSUProperty("Output Voltage", 255, 0, 3)},
772 {"vout14", PSUProperty("Output Voltage", 255, 0, 3)},
773 {"vout15", PSUProperty("Output Voltage", 255, 0, 3)},
774 {"vout16", PSUProperty("Output Voltage", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700775 {"in1", PSUProperty("Output Voltage", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800776 {"iin", PSUProperty("Input Current", 20, 0, 3)},
777 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700778 {"iout2", PSUProperty("Output Current", 255, 0, 3)},
779 {"iout3", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700780 {"curr1", PSUProperty("Output Current", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800781 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700782 {"temp2", PSUProperty("Temperature", 127, -128, 3)},
783 {"temp3", PSUProperty("Temperature", 127, -128, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700784 {"temp4", PSUProperty("Temperature", 127, -128, 3)},
785 {"temp5", PSUProperty("Temperature", 127, -128, 3)},
Cheng C Yang8dbb3952019-05-23 00:03:12 +0800786 {"fan1", PSUProperty("Fan Speed 1", 30000, 0, 0)},
787 {"fan2", PSUProperty("Fan Speed 2", 30000, 0, 0)}};
Cheng C Yang916360b2019-05-07 18:47:16 +0800788
789 pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
Cheng C Yang58b2b532019-05-31 00:19:45 +0800790
791 limitEventMatch = {{"PredictiveFailure", {"max_alarm", "min_alarm"}},
792 {"Failure", {"crit_alarm", "lcrit_alarm"}}};
793
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800794 eventMatch = {{"PredictiveFailure", {"power1_alarm"}},
795 {"Failure", {"in2_alarm"}},
796 {"ACLost", {"in1_beep"}},
797 {"ConfigureError", {"in1_fault"}}};
798
799 groupEventMatch = {{"FanFault",
800 {{"fan1", {"fan1_alarm", "fan1_fault"}},
801 {"fan2", {"fan2_alarm", "fan2_fault"}}}}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800802}
803
James Feistb6c0b912019-07-09 12:21:44 -0700804int main()
Cheng C Yang209ec562019-03-12 16:37:44 +0800805{
806 boost::asio::io_service io;
807 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
808
809 systemBus->request_name("xyz.openbmc_project.PSUSensor");
810 sdbusplus::asio::object_server objectServer(systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800811 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yang209ec562019-03-12 16:37:44 +0800812
Cheng C Yang916360b2019-05-07 18:47:16 +0800813 propertyInitialize();
Cheng C Yang209ec562019-03-12 16:37:44 +0800814
Cheng C Yang916360b2019-05-07 18:47:16 +0800815 io.post([&]() { createSensors(io, objectServer, systemBus); });
Cheng C Yang209ec562019-03-12 16:37:44 +0800816 boost::asio::deadline_timer filterTimer(io);
817 std::function<void(sdbusplus::message::message&)> eventHandler =
818 [&](sdbusplus::message::message& message) {
819 if (message.is_method_error())
820 {
821 std::cerr << "callback method error\n";
822 return;
823 }
824 filterTimer.expires_from_now(boost::posix_time::seconds(1));
825 filterTimer.async_wait([&](const boost::system::error_code& ec) {
826 if (ec == boost::asio::error::operation_aborted)
827 {
828 return;
829 }
830 else if (ec)
831 {
832 std::cerr << "timer error\n";
833 }
Cheng C Yang916360b2019-05-07 18:47:16 +0800834 createSensors(io, objectServer, systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800835 });
836 };
837
838 for (const char* type : sensorTypes)
839 {
840 auto match = std::make_unique<sdbusplus::bus::match::match>(
841 static_cast<sdbusplus::bus::bus&>(*systemBus),
842 "type='signal',member='PropertiesChanged',path_namespace='" +
843 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
844 eventHandler);
845 matches.emplace_back(std::move(match));
846 }
847 io.run();
848}