blob: 0b3ca66e4f0021c2dd7d96783b7cf042bacedabb [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
Matt Spinler5e034af2020-06-24 15:21:53 -050011#include <fmt/format.h>
12
Patrick Williamse8771fd2023-05-10 07:51:06 -050013#include <phosphor-logging/elog-errors.hpp>
14#include <xyz/openbmc_project/Common/error.hpp>
15#include <xyz/openbmc_project/Sensor/Device/error.hpp>
16
Brandon Kim86dcac82019-06-18 17:48:51 -070017#include <cassert>
William A. Kennington III2227bd52019-06-19 11:32:22 -070018#include <chrono>
James Feistee73f5b2018-08-01 16:31:42 -070019#include <cmath>
Matthew Barthcb3daaf2018-05-07 15:03:16 -050020#include <cstring>
Patrick Venture9e997b42019-03-08 13:42:10 -080021#include <filesystem>
Brandon Kim6d50c3e2019-08-09 15:38:53 -070022#include <future>
Patrick Ventureb28f4322018-09-14 10:19:14 -070023#include <thread>
Matthew Barth35819382018-04-18 14:53:01 -050024
Matthew Barth35819382018-04-18 14:53:01 -050025namespace sensor
26{
27
Matthew Barthcb3daaf2018-05-07 15:03:16 -050028using namespace phosphor::logging;
Patrick Ventureb28f4322018-09-14 10:19:14 -070029using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Matthew Barthcb3daaf2018-05-07 15:03:16 -050030
James Feistee73f5b2018-08-01 16:31:42 -070031// todo: this can be simplified once we move to the double interface
Matthew Barth2e41b132018-05-07 14:15:45 -050032Sensor::Sensor(const SensorSet::key_type& sensor,
Patrick Venture2864b062018-12-19 08:13:41 -080033 const hwmonio::HwmonIOInterface* ioAccess,
34 const std::string& devPath) :
Patrick Venture12659aa2018-12-19 13:58:43 -080035 _sensor(sensor),
Brandon Kim86dcac82019-06-18 17:48:51 -070036 _ioAccess(ioAccess), _devPath(devPath), _scale(0), _hasFaultFile(false)
Matthew Barth9c431062018-05-07 13:55:29 -050037{
Patrick Ventureb28f4322018-09-14 10:19:14 -070038 auto chip = env::getEnv("GPIOCHIP", sensor);
39 auto access = env::getEnv("GPIO", sensor);
40 if (!access.empty() && !chip.empty())
41 {
Patrick Venture12659aa2018-12-19 13:58:43 -080042 _handle = gpio::BuildGpioHandle(chip, access);
Patrick Ventureb28f4322018-09-14 10:19:14 -070043
Patrick Venture12659aa2018-12-19 13:58:43 -080044 if (!_handle)
Patrick Ventureb28f4322018-09-14 10:19:14 -070045 {
46 log<level::ERR>("Unable to set up gpio locking");
47 elog<InternalFailure>();
48 }
49 }
50
Matthew Barthac473092018-05-07 14:41:46 -050051 auto gain = env::getEnv("GAIN", sensor);
52 if (!gain.empty())
53 {
Patrick Venture12659aa2018-12-19 13:58:43 -080054 _sensorAdjusts.gain = std::stod(gain);
Matthew Barthac473092018-05-07 14:41:46 -050055 }
56
57 auto offset = env::getEnv("OFFSET", sensor);
58 if (!offset.empty())
59 {
Patrick Venture12659aa2018-12-19 13:58:43 -080060 _sensorAdjusts.offset = std::stoi(offset);
Matthew Barthac473092018-05-07 14:41:46 -050061 }
62 auto senRmRCs = env::getEnv("REMOVERCS", sensor);
63 // Add sensor removal return codes defined per sensor
64 addRemoveRCs(senRmRCs);
Matthew Barth9c431062018-05-07 13:55:29 -050065}
66
Matthew Barthcb3daaf2018-05-07 15:03:16 -050067void Sensor::addRemoveRCs(const std::string& rcList)
68{
69 if (rcList.empty())
70 {
71 return;
72 }
73
74 // Convert to a char* for strtok
Patrick Venture043d3232018-08-31 10:10:53 -070075 std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1);
Matthew Barthcb3daaf2018-05-07 15:03:16 -050076 auto rmRC = std::strtok(&rmRCs[0], ", ");
77 while (rmRC != nullptr)
78 {
79 try
80 {
Patrick Venture12659aa2018-12-19 13:58:43 -080081 _sensorAdjusts.rmRCs.insert(std::stoi(rmRC));
Matthew Barthcb3daaf2018-05-07 15:03:16 -050082 }
83 catch (const std::logic_error& le)
84 {
85 // Unable to convert to int, continue to next token
Patrick Venture12659aa2018-12-19 13:58:43 -080086 std::string name = _sensor.first + "_" + _sensor.second;
Matthew Barthcb3daaf2018-05-07 15:03:16 -050087 log<level::INFO>("Unable to convert sensor removal return code",
88 entry("SENSOR=%s", name.c_str()),
89 entry("RC=%s", rmRC),
90 entry("EXCEPTION=%s", le.what()));
91 }
92 rmRC = std::strtok(nullptr, ", ");
93 }
94}
95
James Feistee73f5b2018-08-01 16:31:42 -070096SensorValueType Sensor::adjustValue(SensorValueType value)
Matthew Barthcb3daaf2018-05-07 15:03:16 -050097{
98// Because read doesn't have an out pointer to store errors.
99// let's assume negative values are errors if they have this
100// set.
Matt Spinlerd8cacfd2021-04-26 09:56:21 -0500101#if NEGATIVE_ERRNO_ON_FAIL
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500102 if (value < 0)
103 {
104 return value;
105 }
106#endif
107
108 // Adjust based on gain and offset
Patrick Venture12659aa2018-12-19 13:58:43 -0800109 value = static_cast<decltype(value)>(static_cast<double>(value) *
110 _sensorAdjusts.gain +
111 _sensorAdjusts.offset);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500112
James Feistee73f5b2018-08-01 16:31:42 -0700113 if constexpr (std::is_same<SensorValueType, double>::value)
114 {
Patrick Venture12659aa2018-12-19 13:58:43 -0800115 value *= std::pow(10, _scale);
James Feistee73f5b2018-08-01 16:31:42 -0700116 }
117
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500118 return value;
119}
120
Patrick Venture043d3232018-08-31 10:10:53 -0700121std::shared_ptr<ValueObject> Sensor::addValue(const RetryIO& retryIO,
Brandon Kim6d50c3e2019-08-09 15:38:53 -0700122 ObjectInfo& info,
123 TimedoutMap& timedoutMap)
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500124{
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500125 // Get the initial value for the value interface.
Patrick Williamsad6043f2022-07-22 19:26:56 -0500126 auto& bus = *std::get<sdbusplus::bus_t*>(info);
Patrick Venture62067232019-06-19 17:39:33 -0700127 auto& obj = std::get<InterfaceMap>(info);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500128 auto& objPath = std::get<std::string>(info);
129
James Feistee73f5b2018-08-01 16:31:42 -0700130 SensorValueType val = 0;
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500131
Brandon Kim86dcac82019-06-18 17:48:51 -0700132 auto& statusIface = std::any_cast<std::shared_ptr<StatusObject>&>(
133 obj[InterfaceType::STATUS]);
134 // As long as addStatus is called before addValue, statusIface
135 // should never be nullptr
136 assert(statusIface);
137
138 // Only read the input value if the status is functional
139 if (statusIface->functional())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500140 {
Matt Spinlerd8cacfd2021-04-26 09:56:21 -0500141#if UPDATE_FUNCTIONAL_ON_FAIL
Brandon Kim79205b22019-06-20 12:18:24 -0700142 try
143#endif
144 {
145 // RAII object for GPIO unlock / lock
William A. Kennington III2227bd52019-06-19 11:32:22 -0700146 auto locker = gpioUnlock(getGpio());
Patrick Ventureb28f4322018-09-14 10:19:14 -0700147
Brandon Kim6d50c3e2019-08-09 15:38:53 -0700148 // For sensors with attribute ASYNC_READ_TIMEOUT,
149 // spawn a thread with timeout
150 auto asyncReadTimeout = env::getEnv("ASYNC_READ_TIMEOUT", _sensor);
151 if (!asyncReadTimeout.empty())
152 {
153 std::chrono::milliseconds asyncTimeout{
154 std::stoi(asyncReadTimeout)};
155 val = asyncRead(_sensor, _ioAccess, asyncTimeout, timedoutMap,
156 _sensor.first, _sensor.second,
Brandon Kim79205b22019-06-20 12:18:24 -0700157 hwmon::entry::cinput, std::get<size_t>(retryIO),
158 std::get<std::chrono::milliseconds>(retryIO));
Brandon Kim6d50c3e2019-08-09 15:38:53 -0700159 }
160 else
161 {
162 // Retry for up to a second if device is busy
163 // or has a transient error.
164 val = _ioAccess->read(
165 _sensor.first, _sensor.second, hwmon::entry::cinput,
166 std::get<size_t>(retryIO),
167 std::get<std::chrono::milliseconds>(retryIO));
168 }
Brandon Kim79205b22019-06-20 12:18:24 -0700169 }
Matt Spinlerd8cacfd2021-04-26 09:56:21 -0500170#if UPDATE_FUNCTIONAL_ON_FAIL
Brandon Kim79205b22019-06-20 12:18:24 -0700171 catch (const std::system_error& e)
172 {
173 // Catch the exception here and update the functional property.
174 // By catching the exception, it will not propagate it up the stack
175 // and thus the code will skip the "Remove RCs" check in
176 // MainLoop::getObject and will not exit on failure.
177 statusIface->functional(false);
178 }
179#endif
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500180 }
181
Patrick Williamsd273b1e2022-03-30 21:55:44 -0500182 auto iface = std::make_shared<ValueObject>(bus, objPath.c_str(),
183 ValueObject::action::defer_emit);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500184
185 hwmon::Attributes attrs;
Patrick Venture12659aa2018-12-19 13:58:43 -0800186 if (hwmon::getAttributes(_sensor.first, attrs))
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500187 {
188 iface->unit(hwmon::getUnit(attrs));
James Feistee73f5b2018-08-01 16:31:42 -0700189
Patrick Venture12659aa2018-12-19 13:58:43 -0800190 _scale = hwmon::getScale(attrs);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500191 }
192
Matt Spinler7ab1b252020-07-22 15:11:02 -0500193 val = adjustValue(val);
194 iface->value(val);
195
Patrick Venture12659aa2018-12-19 13:58:43 -0800196 auto maxValue = env::getEnv("MAXVALUE", _sensor);
Patrick Venture043d3232018-08-31 10:10:53 -0700197 if (!maxValue.empty())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500198 {
199 iface->maxValue(std::stoll(maxValue));
200 }
Patrick Venture12659aa2018-12-19 13:58:43 -0800201 auto minValue = env::getEnv("MINVALUE", _sensor);
Patrick Venture043d3232018-08-31 10:10:53 -0700202 if (!minValue.empty())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500203 {
204 iface->minValue(std::stoll(minValue));
205 }
206
207 obj[InterfaceType::VALUE] = iface;
208 return iface;
209}
210
Matthew Barth2e41b132018-05-07 14:15:45 -0500211std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info)
Matthew Barth35819382018-04-18 14:53:01 -0500212{
Patrick Venture9e997b42019-03-08 13:42:10 -0800213 namespace fs = std::filesystem;
Matthew Barth35819382018-04-18 14:53:01 -0500214
215 std::shared_ptr<StatusObject> iface = nullptr;
Matthew Barth35819382018-04-18 14:53:01 -0500216 auto& objPath = std::get<std::string>(info);
Patrick Venture62067232019-06-19 17:39:33 -0700217 auto& obj = std::get<InterfaceMap>(info);
Matthew Barth35819382018-04-18 14:53:01 -0500218
219 // Check if fault sysfs file exists
Patrick Venture12659aa2018-12-19 13:58:43 -0800220 std::string faultName = _sensor.first;
221 std::string faultID = _sensor.second;
Matthew Barth35819382018-04-18 14:53:01 -0500222 std::string entry = hwmon::entry::fault;
223
Brandon Kim86dcac82019-06-18 17:48:51 -0700224 bool functional = true;
Patrick Williamse8771fd2023-05-10 07:51:06 -0500225 auto sysfsFullPath = sysfs::make_sysfs_path(_ioAccess->path(), faultName,
226 faultID, entry);
Matthew Barth35819382018-04-18 14:53:01 -0500227 if (fs::exists(sysfsFullPath))
228 {
Brandon Kim86dcac82019-06-18 17:48:51 -0700229 _hasFaultFile = true;
Matthew Barth35819382018-04-18 14:53:01 -0500230 try
231 {
Patrick Venture12659aa2018-12-19 13:58:43 -0800232 uint32_t fault = _ioAccess->read(faultName, faultID, entry,
233 hwmonio::retries, hwmonio::delay);
Matthew Barth35819382018-04-18 14:53:01 -0500234 if (fault != 0)
235 {
236 functional = false;
237 }
238 }
239 catch (const std::system_error& e)
240 {
Patrick Venture043d3232018-08-31 10:10:53 -0700241 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::
242 Error;
243 using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure;
Matthew Barth35819382018-04-18 14:53:01 -0500244
Patrick Venture12659aa2018-12-19 13:58:43 -0800245 report<ReadFailure>(
246 metadata::CALLOUT_ERRNO(e.code().value()),
247 metadata::CALLOUT_DEVICE_PATH(_devPath.c_str()));
Matthew Barth35819382018-04-18 14:53:01 -0500248
Matt Spinler6a391de2020-07-08 13:03:10 -0500249 log<level::INFO>(fmt::format("Failing sysfs file: {} errno {}",
250 sysfsFullPath, e.code().value())
251 .c_str());
Matthew Barth35819382018-04-18 14:53:01 -0500252 }
Matthew Barth35819382018-04-18 14:53:01 -0500253 }
254
Patrick Williamsad6043f2022-07-22 19:26:56 -0500255 auto& bus = *std::get<sdbusplus::bus_t*>(info);
Brandon Kim86dcac82019-06-18 17:48:51 -0700256
Patrick Williamsd273b1e2022-03-30 21:55:44 -0500257 iface = std::make_shared<StatusObject>(
258 bus, objPath.c_str(), StatusObject::action::emit_no_signals);
Brandon Kim86dcac82019-06-18 17:48:51 -0700259 // Set functional property
260 iface->functional(functional);
261
262 obj[InterfaceType::STATUS] = iface;
263
Matthew Barth35819382018-04-18 14:53:01 -0500264 return iface;
265}
266
George Liuc9d61612022-10-12 14:31:39 +0800267std::shared_ptr<AccuracyObject> Sensor::addAccuracy(ObjectInfo& info,
268 double accuracy)
269{
270 auto& objPath = std::get<std::string>(info);
271 auto& obj = std::get<InterfaceMap>(info);
272
273 auto& bus = *std::get<sdbusplus::bus_t*>(info);
274 auto iface = std::make_shared<AccuracyObject>(
275 bus, objPath.c_str(), AccuracyObject::action::emit_no_signals);
276
277 iface->accuracy(accuracy);
278 obj[InterfaceType::ACCURACY] = iface;
279
280 return iface;
281}
282
Lakshmi Yadlapati47fb49a2023-10-19 14:47:08 -0500283std::shared_ptr<PriorityObject> Sensor::addPriority(ObjectInfo& info,
284 size_t priority)
285{
286 auto& objPath = std::get<std::string>(info);
287 auto& obj = std::get<InterfaceMap>(info);
288
289 auto& bus = *std::get<sdbusplus::bus_t*>(info);
290 auto iface = std::make_shared<PriorityObject>(
291 bus, objPath.c_str(), PriorityObject::action::emit_no_signals);
292
293 iface->priority(priority);
294 obj[InterfaceType::PRIORITY] = iface;
295
296 return iface;
297}
298
William A. Kennington III2227bd52019-06-19 11:32:22 -0700299void gpioLock(const gpioplus::HandleInterface*&& handle)
Brandon Kimdb76d492019-06-17 11:53:04 -0700300{
William A. Kennington III2227bd52019-06-19 11:32:22 -0700301 handle->setValues({0});
Brandon Kimdb76d492019-06-17 11:53:04 -0700302}
303
William A. Kennington III2227bd52019-06-19 11:32:22 -0700304std::optional<GpioLocker> gpioUnlock(const gpioplus::HandleInterface* handle)
Brandon Kimdb76d492019-06-17 11:53:04 -0700305{
William A. Kennington III2227bd52019-06-19 11:32:22 -0700306 if (handle == nullptr)
Patrick Ventureb28f4322018-09-14 10:19:14 -0700307 {
William A. Kennington III2227bd52019-06-19 11:32:22 -0700308 return std::nullopt;
Patrick Ventureb28f4322018-09-14 10:19:14 -0700309 }
Patrick Ventureb28f4322018-09-14 10:19:14 -0700310
William A. Kennington III2227bd52019-06-19 11:32:22 -0700311 handle->setValues({1});
312 // Default pause needed to guarantee sensors are ready
313 std::this_thread::sleep_for(std::chrono::milliseconds(500));
314 return GpioLocker(std::move(handle));
Patrick Ventureb28f4322018-09-14 10:19:14 -0700315}
316
Brandon Kim6d50c3e2019-08-09 15:38:53 -0700317SensorValueType asyncRead(const SensorSet::key_type& sensorSetKey,
318 const hwmonio::HwmonIOInterface* ioAccess,
319 std::chrono::milliseconds asyncTimeout,
320 TimedoutMap& timedoutMap, const std::string& type,
321 const std::string& id, const std::string& sensor,
322 const size_t retries,
323 const std::chrono::milliseconds delay)
324{
325 // Default async read timeout
326 bool valueIsValid = false;
327 std::future<int64_t> asyncThread;
328
329 auto asyncIter = timedoutMap.find(sensorSetKey);
330 if (asyncIter == timedoutMap.end())
331 {
332 // If sensor not found in timedoutMap, spawn an async thread
Patrick Williamse8771fd2023-05-10 07:51:06 -0500333 asyncThread = std::async(std::launch::async,
334 &hwmonio::HwmonIOInterface::read, ioAccess,
335 type, id, sensor, retries, delay);
Brandon Kim6d50c3e2019-08-09 15:38:53 -0700336 valueIsValid = true;
337 }
338 else
339 {
340 // If we already have the async thread in the timedoutMap, it means this
341 // sensor has already timed out in the previous reads. No need to wait
342 // on subsequent reads - proceed to check the future_status to see when
343 // the async thread finishes
344 asyncTimeout = std::chrono::seconds(0);
345 asyncThread = std::move(asyncIter->second);
346 }
347
348 // TODO: This is still not a true asynchronous read as it still blocks the
349 // main thread for asyncTimeout amount of time. To make this completely
350 // asynchronous, schedule a read and register a callback to update the
351 // sensor value
352 std::future_status status = asyncThread.wait_for(asyncTimeout);
353 switch (status)
354 {
355 case std::future_status::ready:
356 // Read has finished
357 if (valueIsValid)
358 {
359 return asyncThread.get();
360 // Good sensor reads should skip the code below
361 }
362 // Async read thread has completed but had previously timed out (was
363 // found in the timedoutMap). Erase from timedoutMap and throw to
364 // allow retry in the next read cycle. Not returning the read value
365 // as the sensor reading may be bad / corrupted if it took so long.
366 timedoutMap.erase(sensorSetKey);
367 throw AsyncSensorReadTimeOut();
368 default:
369 // Read timed out so add the thread to the timedoutMap (if the entry
370 // already exists, operator[] updates it).
371 //
372 // Keeping the timed out futures in a map is required to prevent
373 // their destructor from being called when returning from this
374 // stack. The destructor will otherwise block until the read
375 // completes due to the limitation of std::async.
376 timedoutMap[sensorSetKey] = std::move(asyncThread);
377 throw AsyncSensorReadTimeOut();
378 }
379}
380
Matthew Barth35819382018-04-18 14:53:01 -0500381} // namespace sensor