blob: 2a2148c3cb5bee2744a31d6a5303c8b8036fd3ce [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
James Feistee73f5b2018-08-01 16:31:42 -070011#include <cmath>
Matthew Barthcb3daaf2018-05-07 15:03:16 -050012#include <cstring>
Matthew Barth35819382018-04-18 14:53:01 -050013#include <experimental/filesystem>
Matthew Barth35819382018-04-18 14:53:01 -050014#include <phosphor-logging/elog-errors.hpp>
Patrick Ventureb28f4322018-09-14 10:19:14 -070015#include <thread>
16#include <xyz/openbmc_project/Common/error.hpp>
Matthew Barth35819382018-04-18 14:53:01 -050017#include <xyz/openbmc_project/Sensor/Device/error.hpp>
18
Matthew Barth35819382018-04-18 14:53:01 -050019namespace sensor
20{
21
Matthew Barthcb3daaf2018-05-07 15:03:16 -050022using namespace phosphor::logging;
Patrick Ventureb28f4322018-09-14 10:19:14 -070023using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Matthew Barthcb3daaf2018-05-07 15:03:16 -050024
James Feistee73f5b2018-08-01 16:31:42 -070025// todo: this can be deleted once we move to double
26// helper class to set the scale on the value iface only when it's available
27template <typename T>
28void setScale(T& iface, int64_t value, double)
29{
30}
31template <typename T>
32void setScale(T& iface, int64_t value, int64_t)
33{
34 iface->scale(value);
35}
36
37// todo: this can be simplified once we move to the double interface
Matthew Barth2e41b132018-05-07 14:15:45 -050038Sensor::Sensor(const SensorSet::key_type& sensor,
Patrick Venture2864b062018-12-19 08:13:41 -080039 const hwmonio::HwmonIOInterface* ioAccess,
40 const std::string& devPath) :
Matthew Barth2e41b132018-05-07 14:15:45 -050041 sensor(sensor),
Patrick Venture043d3232018-08-31 10:10:53 -070042 ioAccess(ioAccess), devPath(devPath)
Matthew Barth9c431062018-05-07 13:55:29 -050043{
Patrick Ventureb28f4322018-09-14 10:19:14 -070044 auto chip = env::getEnv("GPIOCHIP", sensor);
45 auto access = env::getEnv("GPIO", sensor);
46 if (!access.empty() && !chip.empty())
47 {
48 handle = gpio::BuildGpioHandle(chip, access);
49
50 if (!handle)
51 {
52 log<level::ERR>("Unable to set up gpio locking");
53 elog<InternalFailure>();
54 }
55 }
56
Matthew Barthac473092018-05-07 14:41:46 -050057 auto gain = env::getEnv("GAIN", sensor);
58 if (!gain.empty())
59 {
60 sensorAdjusts.gain = std::stod(gain);
61 }
62
63 auto offset = env::getEnv("OFFSET", sensor);
64 if (!offset.empty())
65 {
66 sensorAdjusts.offset = std::stoi(offset);
67 }
68 auto senRmRCs = env::getEnv("REMOVERCS", sensor);
69 // Add sensor removal return codes defined per sensor
70 addRemoveRCs(senRmRCs);
Matthew Barth9c431062018-05-07 13:55:29 -050071}
72
Matthew Barthcb3daaf2018-05-07 15:03:16 -050073void Sensor::addRemoveRCs(const std::string& rcList)
74{
75 if (rcList.empty())
76 {
77 return;
78 }
79
80 // Convert to a char* for strtok
Patrick Venture043d3232018-08-31 10:10:53 -070081 std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1);
Matthew Barthcb3daaf2018-05-07 15:03:16 -050082 auto rmRC = std::strtok(&rmRCs[0], ", ");
83 while (rmRC != nullptr)
84 {
85 try
86 {
87 sensorAdjusts.rmRCs.insert(std::stoi(rmRC));
88 }
89 catch (const std::logic_error& le)
90 {
91 // Unable to convert to int, continue to next token
92 std::string name = sensor.first + "_" + sensor.second;
93 log<level::INFO>("Unable to convert sensor removal return code",
94 entry("SENSOR=%s", name.c_str()),
95 entry("RC=%s", rmRC),
96 entry("EXCEPTION=%s", le.what()));
97 }
98 rmRC = std::strtok(nullptr, ", ");
99 }
100}
101
James Feistee73f5b2018-08-01 16:31:42 -0700102SensorValueType Sensor::adjustValue(SensorValueType value)
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500103{
104// Because read doesn't have an out pointer to store errors.
105// let's assume negative values are errors if they have this
106// set.
107#ifdef NEGATIVE_ERRNO_ON_FAIL
108 if (value < 0)
109 {
110 return value;
111 }
112#endif
113
114 // Adjust based on gain and offset
115 value = static_cast<decltype(value)>(
Patrick Venture043d3232018-08-31 10:10:53 -0700116 static_cast<double>(value) * sensorAdjusts.gain + sensorAdjusts.offset);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500117
James Feistee73f5b2018-08-01 16:31:42 -0700118 if constexpr (std::is_same<SensorValueType, double>::value)
119 {
120 value *= std::pow(10, scale);
121 }
122
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500123 return value;
124}
125
Patrick Venture043d3232018-08-31 10:10:53 -0700126std::shared_ptr<ValueObject> Sensor::addValue(const RetryIO& retryIO,
127 ObjectInfo& info)
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500128{
129 static constexpr bool deferSignals = true;
130
131 // Get the initial value for the value interface.
132 auto& bus = *std::get<sdbusplus::bus::bus*>(info);
133 auto& obj = std::get<Object>(info);
134 auto& objPath = std::get<std::string>(info);
135
James Feistee73f5b2018-08-01 16:31:42 -0700136 SensorValueType val = 0;
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500137 std::shared_ptr<StatusObject> statusIface = nullptr;
138 auto it = obj.find(InterfaceType::STATUS);
139 if (it != obj.end())
140 {
William A. Kennington III4cbdfef2018-10-18 19:19:51 -0700141 statusIface = std::any_cast<std::shared_ptr<StatusObject>>(it->second);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500142 }
143
144 // If there's no fault file or the sensor has a fault file and
145 // its status is functional, read the input value.
146 if (!statusIface || (statusIface && statusIface->functional()))
147 {
Patrick Ventureb28f4322018-09-14 10:19:14 -0700148 unlockGpio();
149
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500150 // Retry for up to a second if device is busy
151 // or has a transient error.
Patrick Venture2864b062018-12-19 08:13:41 -0800152 val = ioAccess->read(sensor.first, sensor.second, hwmon::entry::cinput,
153 std::get<size_t>(retryIO),
154 std::get<std::chrono::milliseconds>(retryIO));
Patrick Ventureb28f4322018-09-14 10:19:14 -0700155
156 lockGpio();
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500157 val = adjustValue(val);
158 }
159
Patrick Venture043d3232018-08-31 10:10:53 -0700160 auto iface =
161 std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500162 iface->value(val);
163
164 hwmon::Attributes attrs;
165 if (hwmon::getAttributes(sensor.first, attrs))
166 {
167 iface->unit(hwmon::getUnit(attrs));
James Feistee73f5b2018-08-01 16:31:42 -0700168
169 setScale(iface, hwmon::getScale(attrs), val);
170
171 scale = hwmon::getScale(attrs);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500172 }
173
174 auto maxValue = env::getEnv("MAXVALUE", sensor);
Patrick Venture043d3232018-08-31 10:10:53 -0700175 if (!maxValue.empty())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500176 {
177 iface->maxValue(std::stoll(maxValue));
178 }
179 auto minValue = env::getEnv("MINVALUE", sensor);
Patrick Venture043d3232018-08-31 10:10:53 -0700180 if (!minValue.empty())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500181 {
182 iface->minValue(std::stoll(minValue));
183 }
184
185 obj[InterfaceType::VALUE] = iface;
186 return iface;
187}
188
Matthew Barth2e41b132018-05-07 14:15:45 -0500189std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info)
Matthew Barth35819382018-04-18 14:53:01 -0500190{
191 namespace fs = std::experimental::filesystem;
192
193 std::shared_ptr<StatusObject> iface = nullptr;
Matthew Barth35819382018-04-18 14:53:01 -0500194 auto& objPath = std::get<std::string>(info);
195 auto& obj = std::get<Object>(info);
196
197 // Check if fault sysfs file exists
198 std::string faultName = sensor.first;
199 std::string faultID = sensor.second;
200 std::string entry = hwmon::entry::fault;
201
Patrick Venture043d3232018-08-31 10:10:53 -0700202 auto sysfsFullPath =
Patrick Venture2864b062018-12-19 08:13:41 -0800203 sysfs::make_sysfs_path(ioAccess->path(), faultName, faultID, entry);
Matthew Barth35819382018-04-18 14:53:01 -0500204 if (fs::exists(sysfsFullPath))
205 {
206 bool functional = true;
Matthew Barth35819382018-04-18 14:53:01 -0500207 try
208 {
Patrick Venture2864b062018-12-19 08:13:41 -0800209 uint32_t fault = ioAccess->read(faultName, faultID, entry,
210 hwmonio::retries, hwmonio::delay);
Matthew Barth35819382018-04-18 14:53:01 -0500211 if (fault != 0)
212 {
213 functional = false;
214 }
215 }
216 catch (const std::system_error& e)
217 {
Patrick Venture043d3232018-08-31 10:10:53 -0700218 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::
219 Error;
220 using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure;
Matthew Barth35819382018-04-18 14:53:01 -0500221
Patrick Venture043d3232018-08-31 10:10:53 -0700222 report<ReadFailure>(metadata::CALLOUT_ERRNO(e.code().value()),
223 metadata::CALLOUT_DEVICE_PATH(devPath.c_str()));
Matthew Barth35819382018-04-18 14:53:01 -0500224
Patrick Venture043d3232018-08-31 10:10:53 -0700225 log<level::INFO>(
226 "Logging failing sysfs file",
227 phosphor::logging::entry("FILE=%s", sysfsFullPath.c_str()));
Matthew Barth35819382018-04-18 14:53:01 -0500228 }
229
Patrick Venture685efa12018-10-12 18:00:13 -0700230 static constexpr bool deferSignals = true;
231 auto& bus = *std::get<sdbusplus::bus::bus*>(info);
232
Patrick Venture043d3232018-08-31 10:10:53 -0700233 iface =
234 std::make_shared<StatusObject>(bus, objPath.c_str(), deferSignals);
Matthew Barth35819382018-04-18 14:53:01 -0500235 // Set functional property
236 iface->functional(functional);
237
238 obj[InterfaceType::STATUS] = iface;
239 }
240
241 return iface;
242}
243
Patrick Ventureb28f4322018-09-14 10:19:14 -0700244void Sensor::unlockGpio()
245{
246 if (handle)
247 {
248 handle->setValues({1});
249 std::this_thread::sleep_for(pause);
250 }
251}
252
253void Sensor::lockGpio()
254{
255 if (handle)
256 {
257 handle->setValues({0});
258 }
259}
260
Matthew Barth35819382018-04-18 14:53:01 -0500261} // namespace sensor