blob: ae1efb9d3d7d6a725afacbf7b6f224c6f31a606d [file] [log] [blame]
James Feist6714a252018-09-10 15:26:18 -07001/*
2// Copyright (c) 2017 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
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103017#include "ADCSensor.hpp"
Ed Tanouseacbfdd2024-04-04 12:00:24 -070018#include "Thresholds.hpp"
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103019#include "Utils.hpp"
20#include "VariantVisitors.hpp"
21
James Feistb83bb2b2019-05-31 12:31:23 -070022#include <boost/algorithm/string/case_conv.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070023#include <boost/asio/error.hpp>
24#include <boost/asio/io_context.hpp>
25#include <boost/asio/post.hpp>
26#include <boost/asio/steady_timer.hpp>
27#include <boost/container/flat_map.hpp>
James Feist6714a252018-09-10 15:26:18 -070028#include <boost/container/flat_set.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070029#include <gpiod.hpp>
George Liud4bc41f2025-02-20 09:21:38 +080030#include <phosphor-logging/lg2.hpp>
James Feist38fb5982020-05-28 10:09:54 -070031#include <sdbusplus/asio/connection.hpp>
32#include <sdbusplus/asio/object_server.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070033#include <sdbusplus/bus.hpp>
James Feist38fb5982020-05-28 10:09:54 -070034#include <sdbusplus/bus/match.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070035#include <sdbusplus/message.hpp>
36#include <sdbusplus/message/native_types.hpp>
James Feist38fb5982020-05-28 10:09:54 -070037
Ed Tanouseacbfdd2024-04-04 12:00:24 -070038#include <array>
39#include <chrono>
40#include <cstddef>
James Feist24f02f22019-04-15 11:05:39 -070041#include <filesystem>
James Feist6714a252018-09-10 15:26:18 -070042#include <fstream>
Patrick Venture96e97db2019-10-31 13:44:38 -070043#include <functional>
44#include <memory>
Zhu, Yungea5b1bbc2019-04-09 19:49:36 -040045#include <optional>
James Feist6714a252018-09-10 15:26:18 -070046#include <regex>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070047#include <stdexcept>
Patrick Venture96e97db2019-10-31 13:44:38 -070048#include <string>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070049#include <utility>
Patrick Venture96e97db2019-10-31 13:44:38 -070050#include <variant>
51#include <vector>
James Feist6714a252018-09-10 15:26:18 -070052
Jeff Lind9cd7042020-11-20 15:49:28 +080053static constexpr float pollRateDefault = 0.5;
Zev Weissf72eb832021-06-25 05:55:08 +000054static constexpr float gpioBridgeSetupTimeDefault = 0.02;
James Feist6714a252018-09-10 15:26:18 -070055
Zev Weiss054aad82022-08-18 01:37:34 -070056static constexpr auto sensorTypes{std::to_array<const char*>({"ADC"})};
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070057static std::regex inputRegex(R"(in(\d+)_input)");
James Feist6714a252018-09-10 15:26:18 -070058
James Feistb83bb2b2019-05-31 12:31:23 -070059static boost::container::flat_map<size_t, bool> cpuPresence;
60
Matt Spinler6ad74d92022-03-01 10:56:46 -060061enum class UpdateType
62{
63 init,
64 cpuPresenceChange
65};
66
James Feist08aec6f2019-01-30 16:17:04 -080067// filter out adc from any other voltage sensor
Ed Tanous2e466962025-01-30 10:59:49 -080068bool isAdc(const std::filesystem::path& parentPath)
James Feist08aec6f2019-01-30 16:17:04 -080069{
Ed Tanous2e466962025-01-30 10:59:49 -080070 std::filesystem::path namePath = parentPath / "name";
James Feist08aec6f2019-01-30 16:17:04 -080071
72 std::ifstream nameFile(namePath);
73 if (!nameFile.good())
74 {
George Liud4bc41f2025-02-20 09:21:38 +080075 lg2::error("Failure reading '{PATH}'", "PATH", namePath.string());
James Feist08aec6f2019-01-30 16:17:04 -080076 return false;
77 }
78
79 std::string name;
80 std::getline(nameFile, name);
81
82 return name == "iio_hwmon";
83}
84
James Feist6714a252018-09-10 15:26:18 -070085void createSensors(
Ed Tanous1f978632023-02-28 18:16:39 -080086 boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
Yong Li1afda6b2020-04-09 19:24:13 +080087 boost::container::flat_map<std::string, std::shared_ptr<ADCSensor>>&
James Feist6714a252018-09-10 15:26:18 -070088 sensors,
89 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
James Feist5591cf082020-07-15 16:44:54 -070090 const std::shared_ptr<boost::container::flat_set<std::string>>&
Matt Spinler6ad74d92022-03-01 10:56:46 -060091 sensorsChanged,
92 UpdateType updateType)
James Feist6714a252018-09-10 15:26:18 -070093{
James Feistc71c1192019-09-18 14:31:33 -070094 auto getter = std::make_shared<GetSensorConfiguration>(
95 dbusConnection,
Matt Spinler6ad74d92022-03-01 10:56:46 -060096 [&io, &objectServer, &sensors, &dbusConnection, sensorsChanged,
97 updateType](const ManagedObjectType& sensorConfigurations) {
Patrick Williams2aaf7172024-08-16 15:20:40 -040098 bool firstScan = sensorsChanged == nullptr;
Ed Tanous2e466962025-01-30 10:59:49 -080099 std::vector<std::filesystem::path> paths;
100 if (!findFiles(std::filesystem::path("/sys/class/hwmon"),
101 R"(in\d+_input)", paths))
James Feist08aec6f2019-01-30 16:17:04 -0800102 {
George Liud4bc41f2025-02-20 09:21:38 +0800103 lg2::error("No adc sensors in system");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400104 return;
James Feistc71c1192019-09-18 14:31:33 -0700105 }
106
Patrick Williams2aaf7172024-08-16 15:20:40 -0400107 // iterate through all found adc sensors, and try to match them with
108 // configuration
109 for (auto& path : paths)
James Feistc71c1192019-09-18 14:31:33 -0700110 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400111 if (!isAdc(path.parent_path()))
James Feist08aec6f2019-01-30 16:17:04 -0800112 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400113 continue;
114 }
115 std::smatch match;
116 std::string pathStr = path.string();
117
118 std::regex_search(pathStr, match, inputRegex);
119 std::string indexStr = *(match.begin() + 1);
120
121 // convert to 0 based
122 size_t index = std::stoul(indexStr) - 1;
123
124 const SensorData* sensorData = nullptr;
125 const std::string* interfacePath = nullptr;
126 const std::pair<std::string, SensorBaseConfigMap>*
127 baseConfiguration = nullptr;
128 for (const auto& [path, cfgData] : sensorConfigurations)
129 {
130 // clear it out each loop
131 baseConfiguration = nullptr;
132
133 // find base configuration
134 for (const char* type : sensorTypes)
Jae Hyun Yoo12bbae02019-07-22 17:24:09 -0700135 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400136 auto sensorBase =
137 cfgData.find(configInterfaceName(type));
138 if (sensorBase != cfgData.end())
Ed Tanousbb679322022-05-16 16:10:00 -0700139 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400140 baseConfiguration = &(*sensorBase);
141 break;
Ed Tanousbb679322022-05-16 16:10:00 -0700142 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400143 }
144 if (baseConfiguration == nullptr)
145 {
146 continue;
147 }
148 auto findIndex = baseConfiguration->second.find("Index");
149 if (findIndex == baseConfiguration->second.end())
150 {
George Liud4bc41f2025-02-20 09:21:38 +0800151 lg2::error(
152 "Base configuration missing Index: '{INTERFACE}'",
153 "INTERFACE", baseConfiguration->first);
Patrick Williams2aaf7172024-08-16 15:20:40 -0400154 continue;
Ed Tanousbb679322022-05-16 16:10:00 -0700155 }
156
Patrick Williams2aaf7172024-08-16 15:20:40 -0400157 unsigned int number = std::visit(
158 VariantToUnsignedIntVisitor(), findIndex->second);
159
160 if (number != index)
161 {
162 continue;
163 }
164
165 sensorData = &cfgData;
166 interfacePath = &path.str;
Ed Tanousbb679322022-05-16 16:10:00 -0700167 break;
168 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400169 if (sensorData == nullptr)
170 {
Alexander Hansen89be6142025-09-18 15:36:16 +0200171 lg2::debug("failed to find match for '{PATH}'", "PATH",
172 path.string());
Patrick Williams2aaf7172024-08-16 15:20:40 -0400173 continue;
174 }
Ed Tanousbb679322022-05-16 16:10:00 -0700175
Patrick Williams2aaf7172024-08-16 15:20:40 -0400176 if (baseConfiguration == nullptr)
177 {
George Liud4bc41f2025-02-20 09:21:38 +0800178 lg2::error("error finding base configuration for '{PATH}'",
179 "PATH", path.string());
Patrick Williams2aaf7172024-08-16 15:20:40 -0400180 continue;
181 }
182
183 auto findSensorName = baseConfiguration->second.find("Name");
184 if (findSensorName == baseConfiguration->second.end())
185 {
George Liud4bc41f2025-02-20 09:21:38 +0800186 lg2::error(
187 "could not determine configuration name for '{PATH}'",
188 "PATH", path.string());
Patrick Williams2aaf7172024-08-16 15:20:40 -0400189 continue;
190 }
191 std::string sensorName =
192 std::get<std::string>(findSensorName->second);
193
194 // on rescans, only update sensors we were signaled by
195 auto findSensor = sensors.find(sensorName);
196 if (!firstScan && findSensor != sensors.end())
197 {
198 bool found = false;
199 for (auto it = sensorsChanged->begin();
200 it != sensorsChanged->end(); it++)
201 {
202 if (findSensor->second &&
203 it->ends_with(findSensor->second->name))
204 {
205 sensorsChanged->erase(it);
206 findSensor->second = nullptr;
207 found = true;
208 break;
209 }
210 }
211 if (!found)
212 {
213 continue;
214 }
215 }
216
217 auto findCPU = baseConfiguration->second.find("CPURequired");
218 if (findCPU != baseConfiguration->second.end())
219 {
220 size_t index =
221 std::visit(VariantToIntVisitor(), findCPU->second);
222 auto presenceFind = cpuPresence.find(index);
223 if (presenceFind == cpuPresence.end())
224 {
225 continue; // no such cpu
226 }
227 if (!presenceFind->second)
228 {
229 continue; // cpu not installed
230 }
231 }
232 else if (updateType == UpdateType::cpuPresenceChange)
233 {
234 continue;
235 }
236
237 std::vector<thresholds::Threshold> sensorThresholds;
238 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds))
239 {
George Liud4bc41f2025-02-20 09:21:38 +0800240 lg2::error("error populating thresholds for '{NAME}'",
241 "NAME", sensorName);
Patrick Williams2aaf7172024-08-16 15:20:40 -0400242 }
243
244 auto findScaleFactor =
245 baseConfiguration->second.find("ScaleFactor");
246 float scaleFactor = 1.0;
247 if (findScaleFactor != baseConfiguration->second.end())
248 {
249 scaleFactor = std::visit(VariantToFloatVisitor(),
250 findScaleFactor->second);
251 // scaleFactor is used in division
252 if (scaleFactor == 0.0F)
253 {
254 scaleFactor = 1.0;
255 }
256 }
257
258 float pollRate =
259 getPollRate(baseConfiguration->second, pollRateDefault);
260 PowerState readState = getPowerState(baseConfiguration->second);
261
262 auto& sensor = sensors[sensorName];
263 sensor = nullptr;
264
265 std::optional<BridgeGpio> bridgeGpio;
266 for (const auto& [key, cfgMap] : *sensorData)
267 {
268 if (key.find("BridgeGpio") != std::string::npos)
269 {
270 auto findName = cfgMap.find("Name");
271 if (findName != cfgMap.end())
272 {
273 std::string gpioName = std::visit(
274 VariantToStringVisitor(), findName->second);
275
276 int polarity = gpiod::line::ACTIVE_HIGH;
277 auto findPolarity = cfgMap.find("Polarity");
278 if (findPolarity != cfgMap.end())
279 {
280 if (std::string("Low") ==
281 std::visit(VariantToStringVisitor(),
282 findPolarity->second))
283 {
284 polarity = gpiod::line::ACTIVE_LOW;
285 }
286 }
287
288 float setupTime = gpioBridgeSetupTimeDefault;
289 auto findSetupTime = cfgMap.find("SetupTime");
290 if (findSetupTime != cfgMap.end())
291 {
292 setupTime = std::visit(VariantToFloatVisitor(),
293 findSetupTime->second);
294 }
295
296 bridgeGpio =
297 BridgeGpio(gpioName, polarity, setupTime);
298 }
299
300 break;
301 }
302 }
303
304 sensor = std::make_shared<ADCSensor>(
305 path.string(), objectServer, dbusConnection, io, sensorName,
306 std::move(sensorThresholds), scaleFactor, pollRate,
307 readState, *interfacePath, std::move(bridgeGpio));
308 sensor->setupRead();
309 }
310 });
James Feistc71c1192019-09-18 14:31:33 -0700311
312 getter->getConfiguration(
313 std::vector<std::string>{sensorTypes.begin(), sensorTypes.end()});
James Feist6714a252018-09-10 15:26:18 -0700314}
315
James Feistb6c0b912019-07-09 12:21:44 -0700316int main()
James Feist6714a252018-09-10 15:26:18 -0700317{
Ed Tanous1f978632023-02-28 18:16:39 -0800318 boost::asio::io_context io;
James Feist6714a252018-09-10 15:26:18 -0700319 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
Ed Tanous14ed5e92022-07-12 15:50:23 -0700320 sdbusplus::asio::object_server objectServer(systemBus, true);
321 objectServer.add_manager("/xyz/openbmc_project/sensors");
322
James Feist6714a252018-09-10 15:26:18 -0700323 systemBus->request_name("xyz.openbmc_project.ADCSensor");
Yong Li1afda6b2020-04-09 19:24:13 +0800324 boost::container::flat_map<std::string, std::shared_ptr<ADCSensor>> sensors;
James Feist5591cf082020-07-15 16:44:54 -0700325 auto sensorsChanged =
326 std::make_shared<boost::container::flat_set<std::string>>();
James Feist6714a252018-09-10 15:26:18 -0700327
Ed Tanous83db50c2023-03-01 10:20:24 -0800328 boost::asio::post(io, [&]() {
Matt Spinler6ad74d92022-03-01 10:56:46 -0600329 createSensors(io, objectServer, sensors, systemBus, nullptr,
330 UpdateType::init);
James Feist6714a252018-09-10 15:26:18 -0700331 });
332
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700333 boost::asio::steady_timer filterTimer(io);
Patrick Williams92f8f512022-07-22 19:26:55 -0500334 std::function<void(sdbusplus::message_t&)> eventHandler =
335 [&](sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400336 if (message.is_method_error())
337 {
George Liud4bc41f2025-02-20 09:21:38 +0800338 lg2::error("callback method error");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400339 return;
340 }
341 sensorsChanged->insert(message.get_path());
342 // this implicitly cancels the timer
343 filterTimer.expires_after(std::chrono::seconds(1));
Ed Tanousbb679322022-05-16 16:10:00 -0700344
Patrick Williams2aaf7172024-08-16 15:20:40 -0400345 filterTimer.async_wait([&](const boost::system::error_code& ec) {
346 if (ec == boost::asio::error::operation_aborted)
347 {
348 /* we were canceled*/
349 return;
350 }
351 if (ec)
352 {
George Liud4bc41f2025-02-20 09:21:38 +0800353 lg2::error("timer error");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400354 return;
355 }
356 createSensors(io, objectServer, sensors, systemBus,
357 sensorsChanged, UpdateType::init);
358 });
359 };
James Feist6714a252018-09-10 15:26:18 -0700360
Matt Spinlerd0c21d52022-11-17 13:55:48 -0600361 boost::asio::steady_timer cpuFilterTimer(io);
Patrick Williams92f8f512022-07-22 19:26:55 -0500362 std::function<void(sdbusplus::message_t&)> cpuPresenceHandler =
363 [&](sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400364 std::string path = message.get_path();
365 boost::to_lower(path);
James Feistb83bb2b2019-05-31 12:31:23 -0700366
Patrick Williams2aaf7172024-08-16 15:20:40 -0400367 sdbusplus::message::object_path cpuPath(path);
368 std::string cpuName = cpuPath.filename();
369 if (!cpuName.starts_with("cpu"))
James Feistb83bb2b2019-05-31 12:31:23 -0700370 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400371 return; // not interested
372 }
373 size_t index = 0;
374 try
375 {
376 index = std::stoi(path.substr(path.size() - 1));
377 }
378 catch (const std::invalid_argument&)
379 {
George Liud4bc41f2025-02-20 09:21:38 +0800380 lg2::error("Found invalid path: '{PATH}'", "PATH", path);
James Feistb83bb2b2019-05-31 12:31:23 -0700381 return;
382 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400383
384 std::string objectName;
385 boost::container::flat_map<std::string, std::variant<bool>> values;
386 message.read(objectName, values);
387 auto findPresence = values.find("Present");
388 if (findPresence != values.end())
James Feistb83bb2b2019-05-31 12:31:23 -0700389 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400390 cpuPresence[index] = std::get<bool>(findPresence->second);
James Feistb83bb2b2019-05-31 12:31:23 -0700391 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400392
393 // this implicitly cancels the timer
394 cpuFilterTimer.expires_after(std::chrono::seconds(1));
395
396 cpuFilterTimer.async_wait([&](const boost::system::error_code& ec) {
397 if (ec == boost::asio::error::operation_aborted)
398 {
399 /* we were canceled*/
400 return;
401 }
402 if (ec)
403 {
George Liud4bc41f2025-02-20 09:21:38 +0800404 lg2::error("timer error");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400405 return;
406 }
407 createSensors(io, objectServer, sensors, systemBus, nullptr,
408 UpdateType::cpuPresenceChange);
409 });
410 };
James Feistb83bb2b2019-05-31 12:31:23 -0700411
Zev Weiss214d9712022-08-12 12:54:31 -0700412 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
413 setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler);
Patrick Williams92f8f512022-07-22 19:26:55 -0500414 matches.emplace_back(std::make_unique<sdbusplus::bus::match_t>(
415 static_cast<sdbusplus::bus_t&>(*systemBus),
James Feistb83bb2b2019-05-31 12:31:23 -0700416 "type='signal',member='PropertiesChanged',path_namespace='" +
417 std::string(cpuInventoryPath) +
418 "',arg0namespace='xyz.openbmc_project.Inventory.Item'",
419 cpuPresenceHandler));
James Feist6714a252018-09-10 15:26:18 -0700420
Bruce Lee1263c3d2021-06-04 15:16:33 +0800421 setupManufacturingModeMatch(*systemBus);
James Feist6714a252018-09-10 15:26:18 -0700422 io.run();
423}