blob: 667dcca61ce30dc4944f041c6ef4624cd813af3d [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*/
16
Cheng C Yang209ec562019-03-12 16:37:44 +080017#include <PSUSensor.hpp>
18#include <Utils.hpp>
19#include <boost/algorithm/string/predicate.hpp>
20#include <boost/algorithm/string/replace.hpp>
21#include <boost/container/flat_set.hpp>
James Feist24f02f22019-04-15 11:05:39 -070022#include <filesystem>
Cheng C Yang209ec562019-03-12 16:37:44 +080023#include <fstream>
24#include <regex>
25#include <sdbusplus/asio/connection.hpp>
26#include <sdbusplus/asio/object_server.hpp>
27
28static constexpr std::array<const char*, 1> sensorTypes = {
29 "xyz.openbmc_project.Configuration.pmbus"};
30
31namespace fs = std::filesystem;
32
Cheng C Yang916360b2019-05-07 18:47:16 +080033static boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>>
34 sensors;
35static boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
36 pwmSensors;
37static boost::container::flat_map<std::string, std::string> sensorTable;
38static boost::container::flat_map<std::string, PSUProperty> labelMatch;
39static boost::container::flat_map<std::string, std::string> pwmTable;
40
41static void checkPWMSensor(const fs::path& sensorPath, std::string& labelHead,
42 const std::string& interfacePath,
43 sdbusplus::asio::object_server& objectServer,
44 std::string psuName)
45{
46 for (const auto& pwmName : pwmTable)
47 {
48 if (pwmName.first != labelHead)
49 {
50 continue;
51 }
52
53 const std::string& sensorPathStr = sensorPath.string();
54 const std::string& pwmPathStr =
55 boost::replace_all_copy(sensorPathStr, "input", "target");
56 std::ifstream pwmFile(pwmPathStr);
57 if (!pwmFile.good())
58 {
59 continue;
60 }
61
62 auto findPWMSensor = pwmSensors.find(psuName + labelHead);
63 if (findPWMSensor != pwmSensors.end())
64 {
65 continue;
66 }
67
68 pwmSensors[psuName + labelHead] = std::make_unique<PwmSensor>(
69 "Pwm_" + psuName + "_" + pwmName.second, pwmPathStr, objectServer,
70 interfacePath + "/" + psuName + " " + pwmName.second);
71 }
72}
73
74void createSensors(boost::asio::io_service& io,
75 sdbusplus::asio::object_server& objectServer,
76 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
Cheng C Yang209ec562019-03-12 16:37:44 +080077{
78
79 ManagedObjectType sensorConfigs;
80 bool useCache = false;
81
82 for (const char* type : sensorTypes)
83 {
84 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
85 useCache))
86 {
87 std::cerr << "error get sensor config from entity manager\n";
88 return;
89 }
90 useCache = true;
91 }
92
93 std::vector<fs::path> pmbusPaths;
94 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
95 {
96 std::cerr << "No PSU sensors in system\n";
97 return;
98 }
99
100 boost::container::flat_set<std::string> directories;
101 for (const auto& pmbusPath : pmbusPaths)
102 {
103 const std::string pathStr = pmbusPath.string();
104 auto directory = pmbusPath.parent_path();
105
106 auto ret = directories.insert(directory.string());
107 if (!ret.second)
108 {
109 continue; // check if path i1 already searched
110 }
111
112 auto device = fs::path(directory / "device");
113 std::string deviceName = fs::canonical(device).stem();
114 auto findHyphen = deviceName.find("-");
115 if (findHyphen == std::string::npos)
116 {
117 std::cerr << "found bad device" << deviceName << "\n";
118 continue;
119 }
120 std::string busStr = deviceName.substr(0, findHyphen);
121 std::string addrStr = deviceName.substr(findHyphen + 1);
122
123 size_t bus = 0;
124 size_t addr = 0;
125
126 try
127 {
128 bus = std::stoi(busStr);
129 addr = std::stoi(addrStr, 0, 16);
130 }
131 catch (std::invalid_argument)
132 {
133 continue;
134 }
135
136 std::ifstream nameFile(pmbusPath);
137 if (!nameFile.good())
138 {
139 std::cerr << "Failure reading " << pmbusPath << "\n";
140 continue;
141 }
142
143 std::string pmbusName;
144 std::getline(nameFile, pmbusName);
145 nameFile.close();
146 if (pmbusName != "pmbus")
147 {
148 continue;
149 }
150
151 const std::pair<std::string, boost::container::flat_map<
152 std::string, BasicVariantType>>*
153 baseConfig = nullptr;
154 const SensorData* sensorData = nullptr;
155 const std::string* interfacePath = nullptr;
156 const char* sensorType = nullptr;
157
158 for (const std::pair<sdbusplus::message::object_path, SensorData>&
159 sensor : sensorConfigs)
160 {
161 sensorData = &(sensor.second);
162 for (const char* type : sensorTypes)
163 {
164 auto sensorBase = sensorData->find(type);
165 if (sensorBase != sensorData->end())
166 {
167 baseConfig = &(*sensorBase);
168 sensorType = type;
169 break;
170 }
171 }
172 if (baseConfig == nullptr)
173 {
174 std::cerr << "error finding base configuration for "
175 << deviceName << "\n";
176 continue;
177 }
178
179 auto configBus = baseConfig->second.find("Bus");
180 auto configAddress = baseConfig->second.find("Address");
181
182 if (configBus == baseConfig->second.end() ||
183 configAddress == baseConfig->second.end())
184 {
185 std::cerr << "error finding necessary entry in configuration";
186 continue;
187 }
188
189 if (std::get<uint64_t>(configBus->second) != bus ||
190 std::get<uint64_t>(configAddress->second) != addr)
191 {
192 continue;
193 }
194
195 interfacePath = &(sensor.first.str);
196 break;
197 }
198 if (interfacePath == nullptr)
199 {
200 std::cerr << "failed to find match for " << deviceName << "\n";
201 continue;
202 }
203
Cheng C Yange50345b2019-04-02 17:26:15 +0800204 auto findPSUName = baseConfig->second.find("Name");
205 if (findPSUName == baseConfig->second.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800206 {
207 std::cerr << "could not determine configuration name for "
208 << deviceName << "\n";
209 continue;
210 }
211
Cheng C Yange50345b2019-04-02 17:26:15 +0800212 std::vector<fs::path> sensorPaths;
213 if (!findFiles(fs::path(directory), R"(\w\d+_input$)", sensorPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800214 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800215 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800216 continue;
217 }
218
Cheng C Yange50345b2019-04-02 17:26:15 +0800219 for (const auto& sensorPath : sensorPaths)
Cheng C Yang209ec562019-03-12 16:37:44 +0800220 {
Cheng C Yang209ec562019-03-12 16:37:44 +0800221
Cheng C Yange50345b2019-04-02 17:26:15 +0800222 std::string labelHead;
223 std::string sensorPathStr = sensorPath.string();
224 std::string sensorNameStr = sensorPath.filename();
225 std::string sensorNameSubStr =
226 sensorNameStr.substr(0, sensorNameStr.find("_") - 1);
227
228 std::string labelPathStr =
229 boost::replace_all_copy(sensorNameStr, "input", "label");
230 std::vector<fs::path> labelPaths;
231 if (!findFiles(fs::path(directory), labelPathStr, labelPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800232 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800233 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800234 continue;
235 }
236
Cheng C Yange50345b2019-04-02 17:26:15 +0800237 if (labelPaths.empty())
Cheng C Yang209ec562019-03-12 16:37:44 +0800238 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800239 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800240 }
241 else
242 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800243 auto labelPath =
244 boost::replace_all_copy(sensorPathStr, "input", "label");
245 std::ifstream labelFile(labelPath);
246 if (!labelFile.good())
247 {
248 std::cerr << "Failure reading " << sensorPath << "\n";
249 continue;
250 }
251 std::string label;
252 std::getline(labelFile, label);
253 labelFile.close();
Cheng C Yang209ec562019-03-12 16:37:44 +0800254
Cheng C Yange50345b2019-04-02 17:26:15 +0800255 auto findSensor = sensors.find(label);
256 if (findSensor != sensors.end())
257 {
258 continue;
259 }
260
261 labelHead = label.substr(0, label.find(" "));
262 }
263
Cheng C Yang916360b2019-05-07 18:47:16 +0800264 checkPWMSensor(sensorPath, labelHead, *interfacePath, objectServer,
265 std::get<std::string>(findPSUName->second));
266
Cheng C Yange50345b2019-04-02 17:26:15 +0800267 std::vector<thresholds::Threshold> sensorThresholds;
268
269 parseThresholdsFromConfig(*sensorData, sensorThresholds,
270 &labelHead);
271
272 auto findProperty = labelMatch.find(labelHead);
273 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800274 {
Cheng C Yang209ec562019-03-12 16:37:44 +0800275 continue;
276 }
277
Cheng C Yange50345b2019-04-02 17:26:15 +0800278 unsigned int factor =
279 std::pow(10, findProperty->second.sensorScaleFactor);
280 if (sensorThresholds.empty())
281 {
282 if (!parseThresholdsFromAttr(sensorThresholds, sensorPathStr,
283 factor))
284 {
285 std::cerr << "error populating thresholds\n";
286 }
287 }
288
289 auto findSensorType = sensorTable.find(sensorNameSubStr);
290 if (findSensorType == sensorTable.end())
291 {
292 std::cerr << "Cannot find PSU sensorType\n";
293 continue;
294 }
295
296 std::string sensorName =
297 std::get<std::string>(findPSUName->second) + " " +
298 findProperty->second.labelTypeName;
299
James Feistbfaf2382019-04-19 13:57:17 -0700300 auto& newSensor = sensors[sensorName];
301 newSensor = nullptr; // destroy old one if it exists
302 newSensor = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800303 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800304 sensorName, std::move(sensorThresholds), *interfacePath,
Cheng C Yange50345b2019-04-02 17:26:15 +0800305 findSensorType->second, factor, findProperty->second.maxReading,
306 findProperty->second.minReading);
Cheng C Yang209ec562019-03-12 16:37:44 +0800307 }
308 }
309 return;
310}
311
Cheng C Yang916360b2019-05-07 18:47:16 +0800312void propertyInitialize(void)
Cheng C Yang209ec562019-03-12 16:37:44 +0800313{
Cheng C Yange50345b2019-04-02 17:26:15 +0800314 sensorTable = {{"power", "power/"},
315 {"curr", "current/"},
316 {"temp", "temperature/"},
317 {"in", "voltage/"},
318 {"fan", "fan_tach/"}};
319
320 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
321 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
Cheng C Yangbdbde962019-04-26 22:50:34 +0800322 {"vin", PSUProperty("Input Voltage", 300, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800323 {"iin", PSUProperty("Input Current", 20, 0, 3)},
324 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
325 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
326 {"fan1", PSUProperty("Fan Speed 1", 10000, 0, 0)},
327 {"fan2", PSUProperty("Fan Speed 2", 10000, 0, 0)}};
Cheng C Yang916360b2019-05-07 18:47:16 +0800328
329 pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800330}
331
332int main(int argc, char** argv)
333{
334 boost::asio::io_service io;
335 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
336
337 systemBus->request_name("xyz.openbmc_project.PSUSensor");
338 sdbusplus::asio::object_server objectServer(systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800339 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yang209ec562019-03-12 16:37:44 +0800340
Cheng C Yang916360b2019-05-07 18:47:16 +0800341 propertyInitialize();
Cheng C Yang209ec562019-03-12 16:37:44 +0800342
Cheng C Yang916360b2019-05-07 18:47:16 +0800343 io.post([&]() { createSensors(io, objectServer, systemBus); });
Cheng C Yang209ec562019-03-12 16:37:44 +0800344 boost::asio::deadline_timer filterTimer(io);
345 std::function<void(sdbusplus::message::message&)> eventHandler =
346 [&](sdbusplus::message::message& message) {
347 if (message.is_method_error())
348 {
349 std::cerr << "callback method error\n";
350 return;
351 }
352 filterTimer.expires_from_now(boost::posix_time::seconds(1));
353 filterTimer.async_wait([&](const boost::system::error_code& ec) {
354 if (ec == boost::asio::error::operation_aborted)
355 {
356 return;
357 }
358 else if (ec)
359 {
360 std::cerr << "timer error\n";
361 }
Cheng C Yang916360b2019-05-07 18:47:16 +0800362 createSensors(io, objectServer, systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800363 });
364 };
365
366 for (const char* type : sensorTypes)
367 {
368 auto match = std::make_unique<sdbusplus::bus::match::match>(
369 static_cast<sdbusplus::bus::bus&>(*systemBus),
370 "type='signal',member='PropertiesChanged',path_namespace='" +
371 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
372 eventHandler);
373 matches.emplace_back(std::move(match));
374 }
375 io.run();
376}