blob: 72d78215469ab0a8e47f5ccaf890d8943ffb0488 [file] [log] [blame]
Patrick Venture043d3232018-08-31 10:10:53 -07001#include "config.h"
2
3#include "sensor.hpp"
4
5#include "env.hpp"
Patrick Ventureb28f4322018-09-14 10:19:14 -07006#include "gpio_handle.hpp"
Patrick Venture043d3232018-08-31 10:10:53 -07007#include "hwmon.hpp"
8#include "sensorset.hpp"
9#include "sysfs.hpp"
10
Patrick Williamse8771fd2023-05-10 07:51:06 -050011#include <phosphor-logging/elog-errors.hpp>
12#include <xyz/openbmc_project/Common/error.hpp>
13#include <xyz/openbmc_project/Sensor/Device/error.hpp>
14
Brandon Kim86dcac82019-06-18 17:48:51 -070015#include <cassert>
William A. Kennington III2227bd52019-06-19 11:32:22 -070016#include <chrono>
James Feistee73f5b2018-08-01 16:31:42 -070017#include <cmath>
Matthew Barthcb3daaf2018-05-07 15:03:16 -050018#include <cstring>
Patrick Venture9e997b42019-03-08 13:42:10 -080019#include <filesystem>
Patrick Williams64129932024-02-13 21:10:17 -060020#include <format>
Brandon Kim6d50c3e2019-08-09 15:38:53 -070021#include <future>
Patrick Ventureb28f4322018-09-14 10:19:14 -070022#include <thread>
Matthew Barth35819382018-04-18 14:53:01 -050023
Matthew Barth35819382018-04-18 14:53:01 -050024namespace sensor
25{
26
Matthew Barthcb3daaf2018-05-07 15:03:16 -050027using namespace phosphor::logging;
Patrick Ventureb28f4322018-09-14 10:19:14 -070028using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Matthew Barthcb3daaf2018-05-07 15:03:16 -050029
James Feistee73f5b2018-08-01 16:31:42 -070030// todo: this can be simplified once we move to the double interface
Matthew Barth2e41b132018-05-07 14:15:45 -050031Sensor::Sensor(const SensorSet::key_type& sensor,
Patrick Venture2864b062018-12-19 08:13:41 -080032 const hwmonio::HwmonIOInterface* ioAccess,
33 const std::string& devPath) :
Patrick Venture12659aa2018-12-19 13:58:43 -080034 _sensor(sensor),
Brandon Kim86dcac82019-06-18 17:48:51 -070035 _ioAccess(ioAccess), _devPath(devPath), _scale(0), _hasFaultFile(false)
Matthew Barth9c431062018-05-07 13:55:29 -050036{
Patrick Ventureb28f4322018-09-14 10:19:14 -070037 auto chip = env::getEnv("GPIOCHIP", sensor);
38 auto access = env::getEnv("GPIO", sensor);
39 if (!access.empty() && !chip.empty())
40 {
Patrick Venture12659aa2018-12-19 13:58:43 -080041 _handle = gpio::BuildGpioHandle(chip, access);
Patrick Ventureb28f4322018-09-14 10:19:14 -070042
Patrick Venture12659aa2018-12-19 13:58:43 -080043 if (!_handle)
Patrick Ventureb28f4322018-09-14 10:19:14 -070044 {
45 log<level::ERR>("Unable to set up gpio locking");
46 elog<InternalFailure>();
47 }
48 }
49
Matthew Barthac473092018-05-07 14:41:46 -050050 auto gain = env::getEnv("GAIN", sensor);
51 if (!gain.empty())
52 {
Patrick Venture12659aa2018-12-19 13:58:43 -080053 _sensorAdjusts.gain = std::stod(gain);
Matthew Barthac473092018-05-07 14:41:46 -050054 }
55
56 auto offset = env::getEnv("OFFSET", sensor);
57 if (!offset.empty())
58 {
Patrick Venture12659aa2018-12-19 13:58:43 -080059 _sensorAdjusts.offset = std::stoi(offset);
Matthew Barthac473092018-05-07 14:41:46 -050060 }
61 auto senRmRCs = env::getEnv("REMOVERCS", sensor);
62 // Add sensor removal return codes defined per sensor
63 addRemoveRCs(senRmRCs);
Matthew Barth9c431062018-05-07 13:55:29 -050064}
65
Matthew Barthcb3daaf2018-05-07 15:03:16 -050066void Sensor::addRemoveRCs(const std::string& rcList)
67{
68 if (rcList.empty())
69 {
70 return;
71 }
72
73 // Convert to a char* for strtok
Patrick Venture043d3232018-08-31 10:10:53 -070074 std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1);
Matthew Barthcb3daaf2018-05-07 15:03:16 -050075 auto rmRC = std::strtok(&rmRCs[0], ", ");
76 while (rmRC != nullptr)
77 {
78 try
79 {
Patrick Venture12659aa2018-12-19 13:58:43 -080080 _sensorAdjusts.rmRCs.insert(std::stoi(rmRC));
Matthew Barthcb3daaf2018-05-07 15:03:16 -050081 }
82 catch (const std::logic_error& le)
83 {
84 // Unable to convert to int, continue to next token
Patrick Venture12659aa2018-12-19 13:58:43 -080085 std::string name = _sensor.first + "_" + _sensor.second;
Matthew Barthcb3daaf2018-05-07 15:03:16 -050086 log<level::INFO>("Unable to convert sensor removal return code",
87 entry("SENSOR=%s", name.c_str()),
88 entry("RC=%s", rmRC),
89 entry("EXCEPTION=%s", le.what()));
90 }
91 rmRC = std::strtok(nullptr, ", ");
92 }
93}
94
James Feistee73f5b2018-08-01 16:31:42 -070095SensorValueType Sensor::adjustValue(SensorValueType value)
Matthew Barthcb3daaf2018-05-07 15:03:16 -050096{
97// Because read doesn't have an out pointer to store errors.
98// let's assume negative values are errors if they have this
99// set.
Matt Spinlerd8cacfd2021-04-26 09:56:21 -0500100#if NEGATIVE_ERRNO_ON_FAIL
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500101 if (value < 0)
102 {
103 return value;
104 }
105#endif
106
107 // Adjust based on gain and offset
Patrick Venture12659aa2018-12-19 13:58:43 -0800108 value = static_cast<decltype(value)>(static_cast<double>(value) *
109 _sensorAdjusts.gain +
110 _sensorAdjusts.offset);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500111
James Feistee73f5b2018-08-01 16:31:42 -0700112 if constexpr (std::is_same<SensorValueType, double>::value)
113 {
Patrick Venture12659aa2018-12-19 13:58:43 -0800114 value *= std::pow(10, _scale);
James Feistee73f5b2018-08-01 16:31:42 -0700115 }
116
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500117 return value;
118}
119
Patrick Venture043d3232018-08-31 10:10:53 -0700120std::shared_ptr<ValueObject> Sensor::addValue(const RetryIO& retryIO,
Brandon Kim6d50c3e2019-08-09 15:38:53 -0700121 ObjectInfo& info,
122 TimedoutMap& timedoutMap)
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500123{
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500124 // Get the initial value for the value interface.
Patrick Williamsad6043f2022-07-22 19:26:56 -0500125 auto& bus = *std::get<sdbusplus::bus_t*>(info);
Patrick Venture62067232019-06-19 17:39:33 -0700126 auto& obj = std::get<InterfaceMap>(info);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500127 auto& objPath = std::get<std::string>(info);
128
James Feistee73f5b2018-08-01 16:31:42 -0700129 SensorValueType val = 0;
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500130
Brandon Kim86dcac82019-06-18 17:48:51 -0700131 auto& statusIface = std::any_cast<std::shared_ptr<StatusObject>&>(
132 obj[InterfaceType::STATUS]);
133 // As long as addStatus is called before addValue, statusIface
134 // should never be nullptr
135 assert(statusIface);
136
137 // Only read the input value if the status is functional
138 if (statusIface->functional())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500139 {
Matt Spinlerd8cacfd2021-04-26 09:56:21 -0500140#if UPDATE_FUNCTIONAL_ON_FAIL
Brandon Kim79205b22019-06-20 12:18:24 -0700141 try
142#endif
143 {
144 // RAII object for GPIO unlock / lock
William A. Kennington III2227bd52019-06-19 11:32:22 -0700145 auto locker = gpioUnlock(getGpio());
Patrick Ventureb28f4322018-09-14 10:19:14 -0700146
Brandon Kim6d50c3e2019-08-09 15:38:53 -0700147 // For sensors with attribute ASYNC_READ_TIMEOUT,
148 // spawn a thread with timeout
149 auto asyncReadTimeout = env::getEnv("ASYNC_READ_TIMEOUT", _sensor);
150 if (!asyncReadTimeout.empty())
151 {
152 std::chrono::milliseconds asyncTimeout{
153 std::stoi(asyncReadTimeout)};
154 val = asyncRead(_sensor, _ioAccess, asyncTimeout, timedoutMap,
155 _sensor.first, _sensor.second,
Brandon Kim79205b22019-06-20 12:18:24 -0700156 hwmon::entry::cinput, std::get<size_t>(retryIO),
157 std::get<std::chrono::milliseconds>(retryIO));
Brandon Kim6d50c3e2019-08-09 15:38:53 -0700158 }
159 else
160 {
161 // Retry for up to a second if device is busy
162 // or has a transient error.
163 val = _ioAccess->read(
164 _sensor.first, _sensor.second, hwmon::entry::cinput,
165 std::get<size_t>(retryIO),
166 std::get<std::chrono::milliseconds>(retryIO));
167 }
Brandon Kim79205b22019-06-20 12:18:24 -0700168 }
Matt Spinlerd8cacfd2021-04-26 09:56:21 -0500169#if UPDATE_FUNCTIONAL_ON_FAIL
Brandon Kim79205b22019-06-20 12:18:24 -0700170 catch (const std::system_error& e)
171 {
172 // Catch the exception here and update the functional property.
173 // By catching the exception, it will not propagate it up the stack
174 // and thus the code will skip the "Remove RCs" check in
175 // MainLoop::getObject and will not exit on failure.
176 statusIface->functional(false);
177 }
178#endif
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500179 }
180
Patrick Williamsd273b1e2022-03-30 21:55:44 -0500181 auto iface = std::make_shared<ValueObject>(bus, objPath.c_str(),
182 ValueObject::action::defer_emit);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500183
184 hwmon::Attributes attrs;
Patrick Venture12659aa2018-12-19 13:58:43 -0800185 if (hwmon::getAttributes(_sensor.first, attrs))
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500186 {
187 iface->unit(hwmon::getUnit(attrs));
James Feistee73f5b2018-08-01 16:31:42 -0700188
Patrick Venture12659aa2018-12-19 13:58:43 -0800189 _scale = hwmon::getScale(attrs);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500190 }
191
Matt Spinler7ab1b252020-07-22 15:11:02 -0500192 val = adjustValue(val);
193 iface->value(val);
194
Patrick Venture12659aa2018-12-19 13:58:43 -0800195 auto maxValue = env::getEnv("MAXVALUE", _sensor);
Patrick Venture043d3232018-08-31 10:10:53 -0700196 if (!maxValue.empty())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500197 {
198 iface->maxValue(std::stoll(maxValue));
199 }
Patrick Venture12659aa2018-12-19 13:58:43 -0800200 auto minValue = env::getEnv("MINVALUE", _sensor);
Patrick Venture043d3232018-08-31 10:10:53 -0700201 if (!minValue.empty())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500202 {
203 iface->minValue(std::stoll(minValue));
204 }
205
206 obj[InterfaceType::VALUE] = iface;
207 return iface;
208}
209
Matthew Barth2e41b132018-05-07 14:15:45 -0500210std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info)
Matthew Barth35819382018-04-18 14:53:01 -0500211{
Patrick Venture9e997b42019-03-08 13:42:10 -0800212 namespace fs = std::filesystem;
Matthew Barth35819382018-04-18 14:53:01 -0500213
214 std::shared_ptr<StatusObject> iface = nullptr;
Matthew Barth35819382018-04-18 14:53:01 -0500215 auto& objPath = std::get<std::string>(info);
Patrick Venture62067232019-06-19 17:39:33 -0700216 auto& obj = std::get<InterfaceMap>(info);
Matthew Barth35819382018-04-18 14:53:01 -0500217
218 // Check if fault sysfs file exists
Patrick Venture12659aa2018-12-19 13:58:43 -0800219 std::string faultName = _sensor.first;
220 std::string faultID = _sensor.second;
Matthew Barth35819382018-04-18 14:53:01 -0500221 std::string entry = hwmon::entry::fault;
222
Brandon Kim86dcac82019-06-18 17:48:51 -0700223 bool functional = true;
Patrick Williamse8771fd2023-05-10 07:51:06 -0500224 auto sysfsFullPath = sysfs::make_sysfs_path(_ioAccess->path(), faultName,
225 faultID, entry);
Matthew Barth35819382018-04-18 14:53:01 -0500226 if (fs::exists(sysfsFullPath))
227 {
Brandon Kim86dcac82019-06-18 17:48:51 -0700228 _hasFaultFile = true;
Matthew Barth35819382018-04-18 14:53:01 -0500229 try
230 {
Patrick Venture12659aa2018-12-19 13:58:43 -0800231 uint32_t fault = _ioAccess->read(faultName, faultID, entry,
232 hwmonio::retries, hwmonio::delay);
Matthew Barth35819382018-04-18 14:53:01 -0500233 if (fault != 0)
234 {
235 functional = false;
236 }
237 }
238 catch (const std::system_error& e)
239 {
Patrick Venture043d3232018-08-31 10:10:53 -0700240 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::
241 Error;
242 using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure;
Matthew Barth35819382018-04-18 14:53:01 -0500243
Patrick Venture12659aa2018-12-19 13:58:43 -0800244 report<ReadFailure>(
245 metadata::CALLOUT_ERRNO(e.code().value()),
246 metadata::CALLOUT_DEVICE_PATH(_devPath.c_str()));
Matthew Barth35819382018-04-18 14:53:01 -0500247
Patrick Williams64129932024-02-13 21:10:17 -0600248 log<level::INFO>(std::format("Failing sysfs file: {} errno {}",
Matt Spinler6a391de2020-07-08 13:03:10 -0500249 sysfsFullPath, e.code().value())
250 .c_str());
Matthew Barth35819382018-04-18 14:53:01 -0500251 }
Matthew Barth35819382018-04-18 14:53:01 -0500252 }
253
Patrick Williamsad6043f2022-07-22 19:26:56 -0500254 auto& bus = *std::get<sdbusplus::bus_t*>(info);
Brandon Kim86dcac82019-06-18 17:48:51 -0700255
Patrick Williamsd273b1e2022-03-30 21:55:44 -0500256 iface = std::make_shared<StatusObject>(
257 bus, objPath.c_str(), StatusObject::action::emit_no_signals);
Brandon Kim86dcac82019-06-18 17:48:51 -0700258 // Set functional property
259 iface->functional(functional);
260
261 obj[InterfaceType::STATUS] = iface;
262
Matthew Barth35819382018-04-18 14:53:01 -0500263 return iface;
264}
265
George Liuc9d61612022-10-12 14:31:39 +0800266std::shared_ptr<AccuracyObject> Sensor::addAccuracy(ObjectInfo& info,
267 double accuracy)
268{
269 auto& objPath = std::get<std::string>(info);
270 auto& obj = std::get<InterfaceMap>(info);
271
272 auto& bus = *std::get<sdbusplus::bus_t*>(info);
273 auto iface = std::make_shared<AccuracyObject>(
274 bus, objPath.c_str(), AccuracyObject::action::emit_no_signals);
275
276 iface->accuracy(accuracy);
277 obj[InterfaceType::ACCURACY] = iface;
278
279 return iface;
280}
281
Lakshmi Yadlapati47fb49a2023-10-19 14:47:08 -0500282std::shared_ptr<PriorityObject> Sensor::addPriority(ObjectInfo& info,
283 size_t priority)
284{
285 auto& objPath = std::get<std::string>(info);
286 auto& obj = std::get<InterfaceMap>(info);
287
288 auto& bus = *std::get<sdbusplus::bus_t*>(info);
289 auto iface = std::make_shared<PriorityObject>(
290 bus, objPath.c_str(), PriorityObject::action::emit_no_signals);
291
292 iface->priority(priority);
293 obj[InterfaceType::PRIORITY] = iface;
294
295 return iface;
296}
297
William A. Kennington III2227bd52019-06-19 11:32:22 -0700298void gpioLock(const gpioplus::HandleInterface*&& handle)
Brandon Kimdb76d492019-06-17 11:53:04 -0700299{
William A. Kennington III2227bd52019-06-19 11:32:22 -0700300 handle->setValues({0});
Brandon Kimdb76d492019-06-17 11:53:04 -0700301}
302
William A. Kennington III2227bd52019-06-19 11:32:22 -0700303std::optional<GpioLocker> gpioUnlock(const gpioplus::HandleInterface* handle)
Brandon Kimdb76d492019-06-17 11:53:04 -0700304{
William A. Kennington III2227bd52019-06-19 11:32:22 -0700305 if (handle == nullptr)
Patrick Ventureb28f4322018-09-14 10:19:14 -0700306 {
William A. Kennington III2227bd52019-06-19 11:32:22 -0700307 return std::nullopt;
Patrick Ventureb28f4322018-09-14 10:19:14 -0700308 }
Patrick Ventureb28f4322018-09-14 10:19:14 -0700309
William A. Kennington III2227bd52019-06-19 11:32:22 -0700310 handle->setValues({1});
311 // Default pause needed to guarantee sensors are ready
312 std::this_thread::sleep_for(std::chrono::milliseconds(500));
313 return GpioLocker(std::move(handle));
Patrick Ventureb28f4322018-09-14 10:19:14 -0700314}
315
Brandon Kim6d50c3e2019-08-09 15:38:53 -0700316SensorValueType asyncRead(const SensorSet::key_type& sensorSetKey,
317 const hwmonio::HwmonIOInterface* ioAccess,
318 std::chrono::milliseconds asyncTimeout,
319 TimedoutMap& timedoutMap, const std::string& type,
320 const std::string& id, const std::string& sensor,
321 const size_t retries,
322 const std::chrono::milliseconds delay)
323{
324 // Default async read timeout
325 bool valueIsValid = false;
326 std::future<int64_t> asyncThread;
327
328 auto asyncIter = timedoutMap.find(sensorSetKey);
329 if (asyncIter == timedoutMap.end())
330 {
331 // If sensor not found in timedoutMap, spawn an async thread
Patrick Williamse8771fd2023-05-10 07:51:06 -0500332 asyncThread = std::async(std::launch::async,
333 &hwmonio::HwmonIOInterface::read, ioAccess,
334 type, id, sensor, retries, delay);
Brandon Kim6d50c3e2019-08-09 15:38:53 -0700335 valueIsValid = true;
336 }
337 else
338 {
339 // If we already have the async thread in the timedoutMap, it means this
340 // sensor has already timed out in the previous reads. No need to wait
341 // on subsequent reads - proceed to check the future_status to see when
342 // the async thread finishes
343 asyncTimeout = std::chrono::seconds(0);
344 asyncThread = std::move(asyncIter->second);
345 }
346
347 // TODO: This is still not a true asynchronous read as it still blocks the
348 // main thread for asyncTimeout amount of time. To make this completely
349 // asynchronous, schedule a read and register a callback to update the
350 // sensor value
351 std::future_status status = asyncThread.wait_for(asyncTimeout);
352 switch (status)
353 {
354 case std::future_status::ready:
355 // Read has finished
356 if (valueIsValid)
357 {
358 return asyncThread.get();
359 // Good sensor reads should skip the code below
360 }
361 // Async read thread has completed but had previously timed out (was
362 // found in the timedoutMap). Erase from timedoutMap and throw to
363 // allow retry in the next read cycle. Not returning the read value
364 // as the sensor reading may be bad / corrupted if it took so long.
365 timedoutMap.erase(sensorSetKey);
366 throw AsyncSensorReadTimeOut();
367 default:
368 // Read timed out so add the thread to the timedoutMap (if the entry
369 // already exists, operator[] updates it).
370 //
371 // Keeping the timed out futures in a map is required to prevent
372 // their destructor from being called when returning from this
373 // stack. The destructor will otherwise block until the read
374 // completes due to the limitation of std::async.
375 timedoutMap[sensorSetKey] = std::move(asyncThread);
376 throw AsyncSensorReadTimeOut();
377 }
378}
379
Matthew Barth35819382018-04-18 14:53:01 -0500380} // namespace sensor