blob: d0e4484103d460242f52d0a6b71f83675fa6e529 [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
175static void checkPWMSensor(const fs::path& sensorPath, std::string& labelHead,
176 const std::string& interfacePath,
177 sdbusplus::asio::object_server& objectServer,
Patrick Venturea2b2d842019-10-11 07:48:47 -0700178 const std::string& psuName)
Cheng C Yang916360b2019-05-07 18:47:16 +0800179{
180 for (const auto& pwmName : pwmTable)
181 {
182 if (pwmName.first != labelHead)
183 {
184 continue;
185 }
186
187 const std::string& sensorPathStr = sensorPath.string();
188 const std::string& pwmPathStr =
189 boost::replace_all_copy(sensorPathStr, "input", "target");
190 std::ifstream pwmFile(pwmPathStr);
191 if (!pwmFile.good())
192 {
193 continue;
194 }
195
196 auto findPWMSensor = pwmSensors.find(psuName + labelHead);
197 if (findPWMSensor != pwmSensors.end())
198 {
199 continue;
200 }
201
202 pwmSensors[psuName + labelHead] = std::make_unique<PwmSensor>(
203 "Pwm_" + psuName + "_" + pwmName.second, pwmPathStr, objectServer,
Cheng C Yang15266a92019-06-12 08:42:52 +0800204 interfacePath + "_" + pwmName.second);
Cheng C Yang916360b2019-05-07 18:47:16 +0800205 }
206}
207
208void createSensors(boost::asio::io_service& io,
209 sdbusplus::asio::object_server& objectServer,
210 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
Cheng C Yang209ec562019-03-12 16:37:44 +0800211{
212
213 ManagedObjectType sensorConfigs;
Josh Lehan49cfba92019-10-08 16:50:42 -0700214 int numCreated = 0;
Cheng C Yang209ec562019-03-12 16:37:44 +0800215 bool useCache = false;
216
Cheng C Yang58b2b532019-05-31 00:19:45 +0800217 // TODO may need only modify the ones that need to be changed.
218 sensors.clear();
Cheng C Yang92498eb2019-09-26 21:59:25 +0800219 combineEvents.clear();
Cheng C Yang209ec562019-03-12 16:37:44 +0800220 for (const char* type : sensorTypes)
221 {
222 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
223 useCache))
224 {
225 std::cerr << "error get sensor config from entity manager\n";
226 return;
227 }
228 useCache = true;
229 }
230
231 std::vector<fs::path> pmbusPaths;
232 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
233 {
234 std::cerr << "No PSU sensors in system\n";
235 return;
236 }
237
238 boost::container::flat_set<std::string> directories;
239 for (const auto& pmbusPath : pmbusPaths)
240 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800241 boost::container::flat_map<std::string, std::vector<std::string>>
242 eventPathList;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800243 boost::container::flat_map<
244 std::string,
245 boost::container::flat_map<std::string, std::vector<std::string>>>
246 groupEventPathList;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800247
248 std::ifstream nameFile(pmbusPath);
249 if (!nameFile.good())
250 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700251 std::cerr << "Failure finding pmbus path " << pmbusPath << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800252 continue;
253 }
254
255 std::string pmbusName;
256 std::getline(nameFile, pmbusName);
257 nameFile.close();
Vijay Khemka996bad12019-05-28 15:15:16 -0700258
259 if (std::find(pmbusNames.begin(), pmbusNames.end(), pmbusName) ==
260 pmbusNames.end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800261 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700262 // To avoid this error message, add your driver name to
263 // the pmbusNames vector at the top of this file.
264 std::cerr << "Driver name " << pmbusName
265 << " not found in sensor whitelist\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800266 continue;
267 }
268
269 const std::string* psuName;
Cheng C Yang209ec562019-03-12 16:37:44 +0800270 auto directory = pmbusPath.parent_path();
271
272 auto ret = directories.insert(directory.string());
273 if (!ret.second)
274 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700275 std::cerr << "Duplicate path " << directory.string() << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800276 continue; // check if path has already been searched
Cheng C Yang209ec562019-03-12 16:37:44 +0800277 }
278
James Feistb6c0b912019-07-09 12:21:44 -0700279 fs::path device = directory / "device";
Cheng C Yang209ec562019-03-12 16:37:44 +0800280 std::string deviceName = fs::canonical(device).stem();
281 auto findHyphen = deviceName.find("-");
282 if (findHyphen == std::string::npos)
283 {
284 std::cerr << "found bad device" << deviceName << "\n";
285 continue;
286 }
287 std::string busStr = deviceName.substr(0, findHyphen);
288 std::string addrStr = deviceName.substr(findHyphen + 1);
289
290 size_t bus = 0;
291 size_t addr = 0;
292
293 try
294 {
295 bus = std::stoi(busStr);
296 addr = std::stoi(addrStr, 0, 16);
297 }
James Feistb6c0b912019-07-09 12:21:44 -0700298 catch (std::invalid_argument&)
Cheng C Yang209ec562019-03-12 16:37:44 +0800299 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700300 std::cerr << "Error parsing bus " << busStr << " addr " << addrStr
301 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800302 continue;
303 }
304
Cheng C Yang209ec562019-03-12 16:37:44 +0800305 const std::pair<std::string, boost::container::flat_map<
306 std::string, BasicVariantType>>*
307 baseConfig = nullptr;
308 const SensorData* sensorData = nullptr;
309 const std::string* interfacePath = nullptr;
310 const char* sensorType = nullptr;
311
312 for (const std::pair<sdbusplus::message::object_path, SensorData>&
313 sensor : sensorConfigs)
314 {
315 sensorData = &(sensor.second);
316 for (const char* type : sensorTypes)
317 {
318 auto sensorBase = sensorData->find(type);
319 if (sensorBase != sensorData->end())
320 {
321 baseConfig = &(*sensorBase);
322 sensorType = type;
323 break;
324 }
325 }
326 if (baseConfig == nullptr)
327 {
328 std::cerr << "error finding base configuration for "
329 << deviceName << "\n";
330 continue;
331 }
332
333 auto configBus = baseConfig->second.find("Bus");
334 auto configAddress = baseConfig->second.find("Address");
335
336 if (configBus == baseConfig->second.end() ||
337 configAddress == baseConfig->second.end())
338 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800339 std::cerr << "error finding necessary entry in configuration\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800340 continue;
341 }
342
Cheng C Yang58b2b532019-05-31 00:19:45 +0800343 const uint64_t* confBus;
344 const uint64_t* confAddr;
345 if (!(confBus = std::get_if<uint64_t>(&(configBus->second))) ||
346 !(confAddr = std::get_if<uint64_t>(&(configAddress->second))))
347 {
348 std::cerr
Josh Lehan49cfba92019-10-08 16:50:42 -0700349 << "Cannot get bus or address, invalid configuration\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800350 continue;
351 }
352
353 if ((*confBus != bus) || (*confAddr != addr))
Cheng C Yang209ec562019-03-12 16:37:44 +0800354 {
Josh Lehan432d1ed2019-10-16 12:23:31 -0700355 std::cerr << "Configuration skipping " << *confBus << "-"
356 << *confAddr << " because not " << bus << "-" << addr
357 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800358 continue;
359 }
360
361 interfacePath = &(sensor.first.str);
362 break;
363 }
364 if (interfacePath == nullptr)
365 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700366 // To avoid this error message, add your export map entry,
367 // from Entity Manager, to sensorTypes at the top of this file.
Cheng C Yang209ec562019-03-12 16:37:44 +0800368 std::cerr << "failed to find match for " << deviceName << "\n";
369 continue;
370 }
371
Cheng C Yange50345b2019-04-02 17:26:15 +0800372 auto findPSUName = baseConfig->second.find("Name");
373 if (findPSUName == baseConfig->second.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800374 {
375 std::cerr << "could not determine configuration name for "
376 << deviceName << "\n";
377 continue;
378 }
379
Cheng C Yang58b2b532019-05-31 00:19:45 +0800380 if (!(psuName = std::get_if<std::string>(&(findPSUName->second))))
381 {
382 std::cerr << "Cannot find psu name, invalid configuration\n";
383 continue;
384 }
385 checkEvent(directory.string(), eventMatch, eventPathList);
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800386 checkGroupEvent(directory.string(), groupEventMatch,
387 groupEventPathList);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800388
Vijay Khemka996bad12019-05-28 15:15:16 -0700389 /* Check if there are more sensors in the same interface */
390 int i = 1;
391 std::vector<std::string> psuNames;
392 do
393 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700394 // Individual string fields: Name, Name1, Name2, Name3, ...
Vijay Khemka996bad12019-05-28 15:15:16 -0700395 psuNames.push_back(std::get<std::string>(findPSUName->second));
396 findPSUName = baseConfig->second.find("Name" + std::to_string(i++));
397 } while (findPSUName != baseConfig->second.end());
398
Cheng C Yange50345b2019-04-02 17:26:15 +0800399 std::vector<fs::path> sensorPaths;
James Feistb6c0b912019-07-09 12:21:44 -0700400 if (!findFiles(directory, R"(\w\d+_input$)", sensorPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800401 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800402 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800403 continue;
404 }
405
Vijay Khemka996bad12019-05-28 15:15:16 -0700406 /* Find array of labels to be exposed if it is defined in config */
407 std::vector<std::string> findLabels;
408 auto findLabelObj = baseConfig->second.find("Labels");
409 if (findLabelObj != baseConfig->second.end())
410 {
411 findLabels =
412 std::get<std::vector<std::string>>(findLabelObj->second);
413 }
414
Jason Ling5747fab2019-10-02 16:46:23 -0700415 std::regex sensorNameRegEx("([A-Za-z]+)[0-9]*_");
416 std::smatch matches;
417
Cheng C Yange50345b2019-04-02 17:26:15 +0800418 for (const auto& sensorPath : sensorPaths)
Cheng C Yang209ec562019-03-12 16:37:44 +0800419 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800420 std::string labelHead;
421 std::string sensorPathStr = sensorPath.string();
422 std::string sensorNameStr = sensorPath.filename();
Jason Ling5747fab2019-10-02 16:46:23 -0700423 std::string sensorNameSubStr{""};
424 if (std::regex_search(sensorNameStr, matches, sensorNameRegEx))
425 {
Josh Lehan06494452019-10-31 09:49:16 -0700426 // hwmon *_input filename without number:
427 // in, curr, power, temp, ...
Jason Ling5747fab2019-10-02 16:46:23 -0700428 sensorNameSubStr = matches[1];
429 }
430 else
431 {
Josh Lehan06494452019-10-31 09:49:16 -0700432 std::cerr << "Could not extract the alpha prefix from "
Jason Ling5747fab2019-10-02 16:46:23 -0700433 << sensorNameStr;
434 continue;
435 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800436
Cheng C Yangecba9de2019-09-12 23:41:50 +0800437 auto labelPath =
438 boost::replace_all_copy(sensorPathStr, "input", "label");
439 std::ifstream labelFile(labelPath);
440 if (!labelFile.good())
Cheng C Yang209ec562019-03-12 16:37:44 +0800441 {
Josh Lehan432d1ed2019-10-16 12:23:31 -0700442 std::cerr << "Input file " << sensorPath
443 << " has no corresponding label file\n";
Josh Lehan06494452019-10-31 09:49:16 -0700444
445 // hwmon *_input filename with number:
446 // temp1, temp2, temp3, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800447 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800448 }
449 else
450 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800451 std::string label;
452 std::getline(labelFile, label);
453 labelFile.close();
Cheng C Yange50345b2019-04-02 17:26:15 +0800454 auto findSensor = sensors.find(label);
455 if (findSensor != sensors.end())
456 {
457 continue;
458 }
459
Josh Lehan06494452019-10-31 09:49:16 -0700460 // hwmon corresponding *_label file contents:
461 // vin1, vout1, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800462 labelHead = label.substr(0, label.find(" "));
463 }
464
Josh Lehan74d9bd92019-10-31 08:51:58 -0700465 if constexpr (DEBUG)
466 {
467 std::cerr << "Sensor type=\"" << sensorNameSubStr
468 << "\" label=\"" << labelHead << "\"\n";
469 }
470
Cheng C Yang916360b2019-05-07 18:47:16 +0800471 checkPWMSensor(sensorPath, labelHead, *interfacePath, objectServer,
Vijay Khemka996bad12019-05-28 15:15:16 -0700472 psuNames[0]);
Cheng C Yang916360b2019-05-07 18:47:16 +0800473
Vijay Khemka996bad12019-05-28 15:15:16 -0700474 if (!findLabels.empty())
475 {
476 /* Check if this labelHead is enabled in config file */
477 if (std::find(findLabels.begin(), findLabels.end(),
478 labelHead) == findLabels.end())
479 {
Josh Lehan06494452019-10-31 09:49:16 -0700480 std::cerr << "could not find " << labelHead
Jason Ling5747fab2019-10-02 16:46:23 -0700481 << " in the Labels list\n";
Vijay Khemka996bad12019-05-28 15:15:16 -0700482 continue;
483 }
484 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800485
Cheng C Yange50345b2019-04-02 17:26:15 +0800486 auto findProperty = labelMatch.find(labelHead);
487 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800488 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700489 if constexpr (DEBUG)
490 {
491 std::cerr << "Could not find matching default property for "
492 << labelHead << "\n";
493 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800494 continue;
495 }
496
Josh Lehan74d9bd92019-10-31 08:51:58 -0700497 // Protect the hardcoded labelMatch list from changes,
498 // by making a copy and modifying that instead.
499 // Avoid bleedthrough of one device's customizations to
500 // the next device, as each should be independently customizable.
501 psuProperties.push_back(findProperty->second);
502 auto psuProperty = psuProperties.rbegin();
503
504 // Use label head as prefix for reading from config file,
505 // example if temp1: temp1_Name, temp1_Scale, temp1_Min, ...
506 std::string keyName = labelHead + "_Name";
507 std::string keyScale = labelHead + "_Scale";
508 std::string keyMin = labelHead + "_Min";
509 std::string keyMax = labelHead + "_Max";
510
511 bool customizedName = false;
512 auto findCustomName = baseConfig->second.find(keyName);
513 if (findCustomName != baseConfig->second.end())
Josh Lehan432d1ed2019-10-16 12:23:31 -0700514 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700515 try
516 {
517 psuProperty->labelTypeName = std::visit(
518 VariantToStringVisitor(), findCustomName->second);
519 }
520 catch (std::invalid_argument&)
521 {
522 std::cerr << "Unable to parse " << keyName << "\n";
523 continue;
524 }
525
526 // All strings are valid, including empty string
527 customizedName = true;
528 }
529
530 bool customizedScale = false;
531 auto findCustomScale = baseConfig->second.find(keyScale);
532 if (findCustomScale != baseConfig->second.end())
533 {
534 try
535 {
536 psuProperty->sensorScaleFactor = std::visit(
537 VariantToUnsignedIntVisitor(), findCustomScale->second);
538 }
539 catch (std::invalid_argument&)
540 {
541 std::cerr << "Unable to parse " << keyScale << "\n";
542 continue;
543 }
544
545 // Avoid later division by zero
546 if (psuProperty->sensorScaleFactor > 0)
547 {
548 customizedScale = true;
549 }
550 else
551 {
552 std::cerr << "Unable to accept " << keyScale << "\n";
553 continue;
554 }
555 }
556
557 auto findCustomMin = baseConfig->second.find(keyMin);
558 if (findCustomMin != baseConfig->second.end())
559 {
560 try
561 {
562 psuProperty->minReading = std::visit(
563 VariantToDoubleVisitor(), findCustomMin->second);
564 }
565 catch (std::invalid_argument&)
566 {
567 std::cerr << "Unable to parse " << keyMin << "\n";
568 continue;
569 }
570 }
571
572 auto findCustomMax = baseConfig->second.find(keyMax);
573 if (findCustomMax != baseConfig->second.end())
574 {
575 try
576 {
577 psuProperty->maxReading = std::visit(
578 VariantToDoubleVisitor(), findCustomMax->second);
579 }
580 catch (std::invalid_argument&)
581 {
582 std::cerr << "Unable to parse " << keyMax << "\n";
583 continue;
584 }
585 }
586
587 if (!(psuProperty->minReading < psuProperty->maxReading))
588 {
589 std::cerr << "Min must be less than Max\n";
590 continue;
591 }
592
593 // If the sensor name is being customized by config file,
594 // then prefix/suffix composition becomes not necessary,
595 // and in fact not wanted, because it gets in the way.
596 std::string psuNameFromIndex;
597 if (!customizedName)
598 {
599 /* Find out sensor name index for this label */
600 std::regex rgx("[A-Za-z]+([0-9]+)");
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500601 size_t nameIndex{0};
Josh Lehan74d9bd92019-10-31 08:51:58 -0700602 if (std::regex_search(labelHead, matches, rgx))
603 {
604 nameIndex = std::stoi(matches[1]);
605
606 // Decrement to preserve alignment, because hwmon
607 // human-readable filenames and labels use 1-based
608 // numbering, but the "Name", "Name1", "Name2", etc. naming
609 // convention (the psuNames vector) uses 0-based numbering.
610 if (nameIndex > 0)
611 {
612 --nameIndex;
613 }
614 }
615 else
616 {
617 nameIndex = 0;
618 }
619
620 if (psuNames.size() <= nameIndex)
621 {
622 std::cerr << "Could not pair " << labelHead
623 << " with a Name field\n";
624 continue;
625 }
626
627 psuNameFromIndex = psuNames[nameIndex];
628
629 if constexpr (DEBUG)
630 {
631 std::cerr << "Sensor label head " << labelHead
632 << " paired with " << psuNameFromIndex
633 << " at index " << nameIndex << "\n";
634 }
Josh Lehan432d1ed2019-10-16 12:23:31 -0700635 }
636
Cheng C Yang58b2b532019-05-31 00:19:45 +0800637 checkEventLimits(sensorPathStr, limitEventMatch, eventPathList);
638
Josh Lehan74d9bd92019-10-31 08:51:58 -0700639 // Similarly, if sensor scaling factor is being customized,
640 // then the below power-of-10 constraint becomes unnecessary,
641 // as config should be able to specify an arbitrary divisor.
642 unsigned int factor = psuProperty->sensorScaleFactor;
643 if (!customizedScale)
Vijay Khemka53ca4442019-07-23 11:03:55 -0700644 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700645 // Preserve existing usage of hardcoded labelMatch table below
646 factor = std::pow(10.0, factor);
Vijay Khemka53ca4442019-07-23 11:03:55 -0700647
Josh Lehan74d9bd92019-10-31 08:51:58 -0700648 /* Change first char of substring to uppercase */
649 char firstChar = sensorNameSubStr[0] - 0x20;
650 std::string strScaleFactor =
651 firstChar + sensorNameSubStr.substr(1) + "ScaleFactor";
652
653 // Preserve existing configs by accepting earlier syntax,
654 // example CurrScaleFactor, PowerScaleFactor, ...
655 auto findScaleFactor = baseConfig->second.find(strScaleFactor);
656 if (findScaleFactor != baseConfig->second.end())
657 {
658 factor = std::visit(VariantToIntVisitor(),
659 findScaleFactor->second);
660 }
661
662 if constexpr (DEBUG)
663 {
664 std::cerr << "Sensor scaling factor " << factor
665 << " string " << strScaleFactor << "\n";
666 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700667 }
668
Vijay Khemka996bad12019-05-28 15:15:16 -0700669 std::vector<thresholds::Threshold> sensorThresholds;
670
Joshi, Mansi14f0ad82019-11-21 10:52:30 +0530671 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds,
672 &labelHead))
Cheng C Yange50345b2019-04-02 17:26:15 +0800673 {
James Feist17ab6e02019-06-25 12:28:13 -0700674 std::cerr << "error populating thresholds for "
675 << sensorNameSubStr << "\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800676 }
677
678 auto findSensorType = sensorTable.find(sensorNameSubStr);
679 if (findSensorType == sensorTable.end())
680 {
Jason Ling5747fab2019-10-02 16:46:23 -0700681 std::cerr << sensorNameSubStr
Josh Lehan06494452019-10-31 09:49:16 -0700682 << " is not a recognized sensor type\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800683 continue;
684 }
685
Josh Lehan49cfba92019-10-08 16:50:42 -0700686 if constexpr (DEBUG)
687 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700688 std::cerr << "Sensor properties: Name \""
689 << psuProperty->labelTypeName << "\" Scale "
690 << psuProperty->sensorScaleFactor << " Min "
691 << psuProperty->minReading << " Max "
692 << psuProperty->maxReading << "\n";
693 }
694
695 std::string sensorName = psuProperty->labelTypeName;
696 if (customizedName)
697 {
698 if (sensorName.empty())
699 {
700 // Allow selective disabling of an individual sensor,
701 // by customizing its name to an empty string.
702 std::cerr << "Sensor disabled, empty string\n";
703 continue;
704 }
705 }
706 else
707 {
708 // Sensor name not customized, do prefix/suffix composition,
709 // preserving default behavior by using psuNameFromIndex.
710 sensorName =
711 psuNameFromIndex + " " + psuProperty->labelTypeName;
712 }
713
714 if constexpr (DEBUG)
715 {
716 std::cerr << "Sensor name \"" << sensorName << "\" path \""
717 << sensorPathStr << "\" type \"" << sensorType
718 << "\"\n";
Josh Lehan49cfba92019-10-08 16:50:42 -0700719 }
720
Cheng C Yang58b2b532019-05-31 00:19:45 +0800721 sensors[sensorName] = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800722 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800723 sensorName, std::move(sensorThresholds), *interfacePath,
Josh Lehan74d9bd92019-10-31 08:51:58 -0700724 findSensorType->second, factor, psuProperty->maxReading,
725 psuProperty->minReading);
726
727 ++numCreated;
728 if constexpr (DEBUG)
729 {
730 std::cerr << "Created " << numCreated << " sensors so far\n";
731 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800732 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800733
734 // OperationalStatus event
Cheng C Yang92498eb2019-09-26 21:59:25 +0800735 combineEvents[*psuName + "OperationalStatus"] = nullptr;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800736 combineEvents[*psuName + "OperationalStatus"] =
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800737 std::make_unique<PSUCombineEvent>(objectServer, io, *psuName,
738 eventPathList, groupEventPathList,
739 "OperationalStatus");
Cheng C Yang209ec562019-03-12 16:37:44 +0800740 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700741
742 if constexpr (DEBUG)
743 {
744 std::cerr << "Created total of " << numCreated << " sensors\n";
745 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800746 return;
747}
748
Cheng C Yang916360b2019-05-07 18:47:16 +0800749void propertyInitialize(void)
Cheng C Yang209ec562019-03-12 16:37:44 +0800750{
Cheng C Yange50345b2019-04-02 17:26:15 +0800751 sensorTable = {{"power", "power/"},
752 {"curr", "current/"},
753 {"temp", "temperature/"},
754 {"in", "voltage/"},
755 {"fan", "fan_tach/"}};
756
757 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
758 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700759 {"pout2", PSUProperty("Output Power", 3000, 0, 6)},
760 {"pout3", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700761 {"power1", PSUProperty("Output Power", 3000, 0, 6)},
Cheng C Yangbdbde962019-04-26 22:50:34 +0800762 {"vin", PSUProperty("Input Voltage", 300, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700763 {"vout1", PSUProperty("Output Voltage", 255, 0, 3)},
764 {"vout2", PSUProperty("Output Voltage", 255, 0, 3)},
765 {"vout3", PSUProperty("Output Voltage", 255, 0, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700766 {"vout4", PSUProperty("Output Voltage", 255, 0, 3)},
767 {"vout5", PSUProperty("Output Voltage", 255, 0, 3)},
768 {"vout6", PSUProperty("Output Voltage", 255, 0, 3)},
769 {"vout7", PSUProperty("Output Voltage", 255, 0, 3)},
770 {"vout8", PSUProperty("Output Voltage", 255, 0, 3)},
771 {"vout9", PSUProperty("Output Voltage", 255, 0, 3)},
772 {"vout10", PSUProperty("Output Voltage", 255, 0, 3)},
773 {"vout11", PSUProperty("Output Voltage", 255, 0, 3)},
774 {"vout12", PSUProperty("Output Voltage", 255, 0, 3)},
775 {"vout13", PSUProperty("Output Voltage", 255, 0, 3)},
776 {"vout14", PSUProperty("Output Voltage", 255, 0, 3)},
777 {"vout15", PSUProperty("Output Voltage", 255, 0, 3)},
778 {"vout16", PSUProperty("Output Voltage", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700779 {"in1", PSUProperty("Output Voltage", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800780 {"iin", PSUProperty("Input Current", 20, 0, 3)},
781 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700782 {"iout2", PSUProperty("Output Current", 255, 0, 3)},
783 {"iout3", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700784 {"curr1", PSUProperty("Output Current", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800785 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700786 {"temp2", PSUProperty("Temperature", 127, -128, 3)},
787 {"temp3", PSUProperty("Temperature", 127, -128, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700788 {"temp4", PSUProperty("Temperature", 127, -128, 3)},
789 {"temp5", PSUProperty("Temperature", 127, -128, 3)},
Cheng C Yang8dbb3952019-05-23 00:03:12 +0800790 {"fan1", PSUProperty("Fan Speed 1", 30000, 0, 0)},
791 {"fan2", PSUProperty("Fan Speed 2", 30000, 0, 0)}};
Cheng C Yang916360b2019-05-07 18:47:16 +0800792
793 pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
Cheng C Yang58b2b532019-05-31 00:19:45 +0800794
795 limitEventMatch = {{"PredictiveFailure", {"max_alarm", "min_alarm"}},
796 {"Failure", {"crit_alarm", "lcrit_alarm"}}};
797
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800798 eventMatch = {{"PredictiveFailure", {"power1_alarm"}},
799 {"Failure", {"in2_alarm"}},
800 {"ACLost", {"in1_beep"}},
801 {"ConfigureError", {"in1_fault"}}};
802
803 groupEventMatch = {{"FanFault",
804 {{"fan1", {"fan1_alarm", "fan1_fault"}},
805 {"fan2", {"fan2_alarm", "fan2_fault"}}}}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800806}
807
James Feistb6c0b912019-07-09 12:21:44 -0700808int main()
Cheng C Yang209ec562019-03-12 16:37:44 +0800809{
810 boost::asio::io_service io;
811 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
812
813 systemBus->request_name("xyz.openbmc_project.PSUSensor");
814 sdbusplus::asio::object_server objectServer(systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800815 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yang209ec562019-03-12 16:37:44 +0800816
Cheng C Yang916360b2019-05-07 18:47:16 +0800817 propertyInitialize();
Cheng C Yang209ec562019-03-12 16:37:44 +0800818
Cheng C Yang916360b2019-05-07 18:47:16 +0800819 io.post([&]() { createSensors(io, objectServer, systemBus); });
Cheng C Yang209ec562019-03-12 16:37:44 +0800820 boost::asio::deadline_timer filterTimer(io);
821 std::function<void(sdbusplus::message::message&)> eventHandler =
822 [&](sdbusplus::message::message& message) {
823 if (message.is_method_error())
824 {
825 std::cerr << "callback method error\n";
826 return;
827 }
828 filterTimer.expires_from_now(boost::posix_time::seconds(1));
829 filterTimer.async_wait([&](const boost::system::error_code& ec) {
830 if (ec == boost::asio::error::operation_aborted)
831 {
832 return;
833 }
834 else if (ec)
835 {
836 std::cerr << "timer error\n";
837 }
Cheng C Yang916360b2019-05-07 18:47:16 +0800838 createSensors(io, objectServer, systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800839 });
840 };
841
842 for (const char* type : sensorTypes)
843 {
844 auto match = std::make_unique<sdbusplus::bus::match::match>(
845 static_cast<sdbusplus::bus::bus&>(*systemBus),
846 "type='signal',member='PropertiesChanged',path_namespace='" +
847 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
848 eventHandler);
849 matches.emplace_back(std::move(match));
850 }
851 io.run();
852}