blob: 09aeca6dc196b565e25a8ee64d77493f9c37ddc8 [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
Brandon Kim86dcac82019-06-18 17:48:51 -070013#include <cassert>
William A. Kennington III2227bd52019-06-19 11:32:22 -070014#include <chrono>
James Feistee73f5b2018-08-01 16:31:42 -070015#include <cmath>
Matthew Barthcb3daaf2018-05-07 15:03:16 -050016#include <cstring>
Patrick Venture9e997b42019-03-08 13:42:10 -080017#include <filesystem>
Matthew Barth35819382018-04-18 14:53:01 -050018#include <phosphor-logging/elog-errors.hpp>
Patrick Ventureb28f4322018-09-14 10:19:14 -070019#include <thread>
20#include <xyz/openbmc_project/Common/error.hpp>
Matthew Barth35819382018-04-18 14:53:01 -050021#include <xyz/openbmc_project/Sensor/Device/error.hpp>
22
Matthew Barth35819382018-04-18 14:53:01 -050023namespace sensor
24{
25
Matthew Barthcb3daaf2018-05-07 15:03:16 -050026using namespace phosphor::logging;
Patrick Ventureb28f4322018-09-14 10:19:14 -070027using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Matthew Barthcb3daaf2018-05-07 15:03:16 -050028
James Feistee73f5b2018-08-01 16:31:42 -070029// todo: this can be simplified once we move to the double interface
Matthew Barth2e41b132018-05-07 14:15:45 -050030Sensor::Sensor(const SensorSet::key_type& sensor,
Patrick Venture2864b062018-12-19 08:13:41 -080031 const hwmonio::HwmonIOInterface* ioAccess,
32 const std::string& devPath) :
Patrick Venture12659aa2018-12-19 13:58:43 -080033 _sensor(sensor),
Brandon Kim86dcac82019-06-18 17:48:51 -070034 _ioAccess(ioAccess), _devPath(devPath), _scale(0), _hasFaultFile(false)
Matthew Barth9c431062018-05-07 13:55:29 -050035{
Patrick Ventureb28f4322018-09-14 10:19:14 -070036 auto chip = env::getEnv("GPIOCHIP", sensor);
37 auto access = env::getEnv("GPIO", sensor);
38 if (!access.empty() && !chip.empty())
39 {
Patrick Venture12659aa2018-12-19 13:58:43 -080040 _handle = gpio::BuildGpioHandle(chip, access);
Patrick Ventureb28f4322018-09-14 10:19:14 -070041
Patrick Venture12659aa2018-12-19 13:58:43 -080042 if (!_handle)
Patrick Ventureb28f4322018-09-14 10:19:14 -070043 {
44 log<level::ERR>("Unable to set up gpio locking");
45 elog<InternalFailure>();
46 }
47 }
48
Matthew Barthac473092018-05-07 14:41:46 -050049 auto gain = env::getEnv("GAIN", sensor);
50 if (!gain.empty())
51 {
Patrick Venture12659aa2018-12-19 13:58:43 -080052 _sensorAdjusts.gain = std::stod(gain);
Matthew Barthac473092018-05-07 14:41:46 -050053 }
54
55 auto offset = env::getEnv("OFFSET", sensor);
56 if (!offset.empty())
57 {
Patrick Venture12659aa2018-12-19 13:58:43 -080058 _sensorAdjusts.offset = std::stoi(offset);
Matthew Barthac473092018-05-07 14:41:46 -050059 }
60 auto senRmRCs = env::getEnv("REMOVERCS", sensor);
61 // Add sensor removal return codes defined per sensor
62 addRemoveRCs(senRmRCs);
Matthew Barth9c431062018-05-07 13:55:29 -050063}
64
Matthew Barthcb3daaf2018-05-07 15:03:16 -050065void Sensor::addRemoveRCs(const std::string& rcList)
66{
67 if (rcList.empty())
68 {
69 return;
70 }
71
72 // Convert to a char* for strtok
Patrick Venture043d3232018-08-31 10:10:53 -070073 std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1);
Matthew Barthcb3daaf2018-05-07 15:03:16 -050074 auto rmRC = std::strtok(&rmRCs[0], ", ");
75 while (rmRC != nullptr)
76 {
77 try
78 {
Patrick Venture12659aa2018-12-19 13:58:43 -080079 _sensorAdjusts.rmRCs.insert(std::stoi(rmRC));
Matthew Barthcb3daaf2018-05-07 15:03:16 -050080 }
81 catch (const std::logic_error& le)
82 {
83 // Unable to convert to int, continue to next token
Patrick Venture12659aa2018-12-19 13:58:43 -080084 std::string name = _sensor.first + "_" + _sensor.second;
Matthew Barthcb3daaf2018-05-07 15:03:16 -050085 log<level::INFO>("Unable to convert sensor removal return code",
86 entry("SENSOR=%s", name.c_str()),
87 entry("RC=%s", rmRC),
88 entry("EXCEPTION=%s", le.what()));
89 }
90 rmRC = std::strtok(nullptr, ", ");
91 }
92}
93
James Feistee73f5b2018-08-01 16:31:42 -070094SensorValueType Sensor::adjustValue(SensorValueType value)
Matthew Barthcb3daaf2018-05-07 15:03:16 -050095{
96// Because read doesn't have an out pointer to store errors.
97// let's assume negative values are errors if they have this
98// set.
99#ifdef NEGATIVE_ERRNO_ON_FAIL
100 if (value < 0)
101 {
102 return value;
103 }
104#endif
105
106 // Adjust based on gain and offset
Patrick Venture12659aa2018-12-19 13:58:43 -0800107 value = static_cast<decltype(value)>(static_cast<double>(value) *
108 _sensorAdjusts.gain +
109 _sensorAdjusts.offset);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500110
James Feistee73f5b2018-08-01 16:31:42 -0700111 if constexpr (std::is_same<SensorValueType, double>::value)
112 {
Patrick Venture12659aa2018-12-19 13:58:43 -0800113 value *= std::pow(10, _scale);
James Feistee73f5b2018-08-01 16:31:42 -0700114 }
115
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500116 return value;
117}
118
Patrick Venture043d3232018-08-31 10:10:53 -0700119std::shared_ptr<ValueObject> Sensor::addValue(const RetryIO& retryIO,
120 ObjectInfo& info)
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500121{
122 static constexpr bool deferSignals = true;
123
124 // Get the initial value for the value interface.
125 auto& bus = *std::get<sdbusplus::bus::bus*>(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 {
Brandon Kim79205b22019-06-20 12:18:24 -0700140#ifdef UPDATE_FUNCTIONAL_ON_FAIL
141 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 Kim79205b22019-06-20 12:18:24 -0700147 // Retry for up to a second if device is busy
148 // or has a transient error.
149 val =
150 _ioAccess->read(_sensor.first, _sensor.second,
151 hwmon::entry::cinput, std::get<size_t>(retryIO),
152 std::get<std::chrono::milliseconds>(retryIO));
Brandon Kim79205b22019-06-20 12:18:24 -0700153 }
154#ifdef UPDATE_FUNCTIONAL_ON_FAIL
155 catch (const std::system_error& e)
156 {
157 // Catch the exception here and update the functional property.
158 // By catching the exception, it will not propagate it up the stack
159 // and thus the code will skip the "Remove RCs" check in
160 // MainLoop::getObject and will not exit on failure.
161 statusIface->functional(false);
162 }
163#endif
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500164 }
165
Patrick Venture043d3232018-08-31 10:10:53 -0700166 auto iface =
167 std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500168
169 hwmon::Attributes attrs;
Patrick Venture12659aa2018-12-19 13:58:43 -0800170 if (hwmon::getAttributes(_sensor.first, attrs))
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500171 {
172 iface->unit(hwmon::getUnit(attrs));
James Feistee73f5b2018-08-01 16:31:42 -0700173
Patrick Venture12659aa2018-12-19 13:58:43 -0800174 _scale = hwmon::getScale(attrs);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500175 }
176
Matt Spinler7ab1b252020-07-22 15:11:02 -0500177 val = adjustValue(val);
178 iface->value(val);
179
Patrick Venture12659aa2018-12-19 13:58:43 -0800180 auto maxValue = env::getEnv("MAXVALUE", _sensor);
Patrick Venture043d3232018-08-31 10:10:53 -0700181 if (!maxValue.empty())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500182 {
183 iface->maxValue(std::stoll(maxValue));
184 }
Patrick Venture12659aa2018-12-19 13:58:43 -0800185 auto minValue = env::getEnv("MINVALUE", _sensor);
Patrick Venture043d3232018-08-31 10:10:53 -0700186 if (!minValue.empty())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500187 {
188 iface->minValue(std::stoll(minValue));
189 }
190
191 obj[InterfaceType::VALUE] = iface;
192 return iface;
193}
194
Matthew Barth2e41b132018-05-07 14:15:45 -0500195std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info)
Matthew Barth35819382018-04-18 14:53:01 -0500196{
Patrick Venture9e997b42019-03-08 13:42:10 -0800197 namespace fs = std::filesystem;
Matthew Barth35819382018-04-18 14:53:01 -0500198
199 std::shared_ptr<StatusObject> iface = nullptr;
Matthew Barth35819382018-04-18 14:53:01 -0500200 auto& objPath = std::get<std::string>(info);
Patrick Venture62067232019-06-19 17:39:33 -0700201 auto& obj = std::get<InterfaceMap>(info);
Matthew Barth35819382018-04-18 14:53:01 -0500202
203 // Check if fault sysfs file exists
Patrick Venture12659aa2018-12-19 13:58:43 -0800204 std::string faultName = _sensor.first;
205 std::string faultID = _sensor.second;
Matthew Barth35819382018-04-18 14:53:01 -0500206 std::string entry = hwmon::entry::fault;
207
Brandon Kim86dcac82019-06-18 17:48:51 -0700208 bool functional = true;
Patrick Venture043d3232018-08-31 10:10:53 -0700209 auto sysfsFullPath =
Patrick Venture12659aa2018-12-19 13:58:43 -0800210 sysfs::make_sysfs_path(_ioAccess->path(), faultName, faultID, entry);
Matthew Barth35819382018-04-18 14:53:01 -0500211 if (fs::exists(sysfsFullPath))
212 {
Brandon Kim86dcac82019-06-18 17:48:51 -0700213 _hasFaultFile = true;
Matthew Barth35819382018-04-18 14:53:01 -0500214 try
215 {
Patrick Venture12659aa2018-12-19 13:58:43 -0800216 uint32_t fault = _ioAccess->read(faultName, faultID, entry,
217 hwmonio::retries, hwmonio::delay);
Matthew Barth35819382018-04-18 14:53:01 -0500218 if (fault != 0)
219 {
220 functional = false;
221 }
222 }
223 catch (const std::system_error& e)
224 {
Patrick Venture043d3232018-08-31 10:10:53 -0700225 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::
226 Error;
227 using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure;
Matthew Barth35819382018-04-18 14:53:01 -0500228
Patrick Venture12659aa2018-12-19 13:58:43 -0800229 report<ReadFailure>(
230 metadata::CALLOUT_ERRNO(e.code().value()),
231 metadata::CALLOUT_DEVICE_PATH(_devPath.c_str()));
Matthew Barth35819382018-04-18 14:53:01 -0500232
Matt Spinler6a391de2020-07-08 13:03:10 -0500233 log<level::INFO>(fmt::format("Failing sysfs file: {} errno {}",
234 sysfsFullPath, e.code().value())
235 .c_str());
Matthew Barth35819382018-04-18 14:53:01 -0500236 }
Matthew Barth35819382018-04-18 14:53:01 -0500237 }
238
Brandon Kim86dcac82019-06-18 17:48:51 -0700239 static constexpr bool deferSignals = true;
240 auto& bus = *std::get<sdbusplus::bus::bus*>(info);
241
242 iface = std::make_shared<StatusObject>(bus, objPath.c_str(), deferSignals);
243 // Set functional property
244 iface->functional(functional);
245
246 obj[InterfaceType::STATUS] = iface;
247
Matthew Barth35819382018-04-18 14:53:01 -0500248 return iface;
249}
250
William A. Kennington III2227bd52019-06-19 11:32:22 -0700251void gpioLock(const gpioplus::HandleInterface*&& handle)
Brandon Kimdb76d492019-06-17 11:53:04 -0700252{
William A. Kennington III2227bd52019-06-19 11:32:22 -0700253 handle->setValues({0});
Brandon Kimdb76d492019-06-17 11:53:04 -0700254}
255
William A. Kennington III2227bd52019-06-19 11:32:22 -0700256std::optional<GpioLocker> gpioUnlock(const gpioplus::HandleInterface* handle)
Brandon Kimdb76d492019-06-17 11:53:04 -0700257{
William A. Kennington III2227bd52019-06-19 11:32:22 -0700258 if (handle == nullptr)
Patrick Ventureb28f4322018-09-14 10:19:14 -0700259 {
William A. Kennington III2227bd52019-06-19 11:32:22 -0700260 return std::nullopt;
Patrick Ventureb28f4322018-09-14 10:19:14 -0700261 }
Patrick Ventureb28f4322018-09-14 10:19:14 -0700262
William A. Kennington III2227bd52019-06-19 11:32:22 -0700263 handle->setValues({1});
264 // Default pause needed to guarantee sensors are ready
265 std::this_thread::sleep_for(std::chrono::milliseconds(500));
266 return GpioLocker(std::move(handle));
Patrick Ventureb28f4322018-09-14 10:19:14 -0700267}
268
Matthew Barth35819382018-04-18 14:53:01 -0500269} // namespace sensor