blob: 5c97e0c0e268d816ba921e7673ef749d9fc225e5 [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 Venture043d3232018-08-31 10:10:53 -070039 const hwmonio::HwmonIO& ioAccess, const std::string& devPath) :
Matthew Barth2e41b132018-05-07 14:15:45 -050040 sensor(sensor),
Patrick Venture043d3232018-08-31 10:10:53 -070041 ioAccess(ioAccess), devPath(devPath)
Matthew Barth9c431062018-05-07 13:55:29 -050042{
Patrick Ventureb28f4322018-09-14 10:19:14 -070043 auto chip = env::getEnv("GPIOCHIP", sensor);
44 auto access = env::getEnv("GPIO", sensor);
45 if (!access.empty() && !chip.empty())
46 {
47 handle = gpio::BuildGpioHandle(chip, access);
48
49 if (!handle)
50 {
51 log<level::ERR>("Unable to set up gpio locking");
52 elog<InternalFailure>();
53 }
54 }
55
Matthew Barthac473092018-05-07 14:41:46 -050056 auto gain = env::getEnv("GAIN", sensor);
57 if (!gain.empty())
58 {
59 sensorAdjusts.gain = std::stod(gain);
60 }
61
62 auto offset = env::getEnv("OFFSET", sensor);
63 if (!offset.empty())
64 {
65 sensorAdjusts.offset = std::stoi(offset);
66 }
67 auto senRmRCs = env::getEnv("REMOVERCS", sensor);
68 // Add sensor removal return codes defined per sensor
69 addRemoveRCs(senRmRCs);
Matthew Barth9c431062018-05-07 13:55:29 -050070}
71
Matthew Barthcb3daaf2018-05-07 15:03:16 -050072void Sensor::addRemoveRCs(const std::string& rcList)
73{
74 if (rcList.empty())
75 {
76 return;
77 }
78
79 // Convert to a char* for strtok
Patrick Venture043d3232018-08-31 10:10:53 -070080 std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1);
Matthew Barthcb3daaf2018-05-07 15:03:16 -050081 auto rmRC = std::strtok(&rmRCs[0], ", ");
82 while (rmRC != nullptr)
83 {
84 try
85 {
86 sensorAdjusts.rmRCs.insert(std::stoi(rmRC));
87 }
88 catch (const std::logic_error& le)
89 {
90 // Unable to convert to int, continue to next token
91 std::string name = sensor.first + "_" + sensor.second;
92 log<level::INFO>("Unable to convert sensor removal return code",
93 entry("SENSOR=%s", name.c_str()),
94 entry("RC=%s", rmRC),
95 entry("EXCEPTION=%s", le.what()));
96 }
97 rmRC = std::strtok(nullptr, ", ");
98 }
99}
100
James Feistee73f5b2018-08-01 16:31:42 -0700101SensorValueType Sensor::adjustValue(SensorValueType value)
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500102{
103// Because read doesn't have an out pointer to store errors.
104// let's assume negative values are errors if they have this
105// set.
106#ifdef NEGATIVE_ERRNO_ON_FAIL
107 if (value < 0)
108 {
109 return value;
110 }
111#endif
112
113 // Adjust based on gain and offset
114 value = static_cast<decltype(value)>(
Patrick Venture043d3232018-08-31 10:10:53 -0700115 static_cast<double>(value) * sensorAdjusts.gain + sensorAdjusts.offset);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500116
James Feistee73f5b2018-08-01 16:31:42 -0700117 if constexpr (std::is_same<SensorValueType, double>::value)
118 {
119 value *= std::pow(10, scale);
120 }
121
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500122 return value;
123}
124
Patrick Venture043d3232018-08-31 10:10:53 -0700125std::shared_ptr<ValueObject> Sensor::addValue(const RetryIO& retryIO,
126 ObjectInfo& info)
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500127{
128 static constexpr bool deferSignals = true;
129
130 // Get the initial value for the value interface.
131 auto& bus = *std::get<sdbusplus::bus::bus*>(info);
132 auto& obj = std::get<Object>(info);
133 auto& objPath = std::get<std::string>(info);
134
James Feistee73f5b2018-08-01 16:31:42 -0700135 SensorValueType val = 0;
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500136 std::shared_ptr<StatusObject> statusIface = nullptr;
137 auto it = obj.find(InterfaceType::STATUS);
138 if (it != obj.end())
139 {
William A. Kennington III4cbdfef2018-10-18 19:19:51 -0700140 statusIface = std::any_cast<std::shared_ptr<StatusObject>>(it->second);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500141 }
142
143 // If there's no fault file or the sensor has a fault file and
144 // its status is functional, read the input value.
145 if (!statusIface || (statusIface && statusIface->functional()))
146 {
Patrick Ventureb28f4322018-09-14 10:19:14 -0700147 unlockGpio();
148
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500149 // Retry for up to a second if device is busy
150 // or has a transient error.
Patrick Venture043d3232018-08-31 10:10:53 -0700151 val = ioAccess.read(sensor.first, sensor.second, hwmon::entry::cinput,
152 std::get<size_t>(retryIO),
153 std::get<std::chrono::milliseconds>(retryIO));
Patrick Ventureb28f4322018-09-14 10:19:14 -0700154
155 lockGpio();
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500156 val = adjustValue(val);
157 }
158
Patrick Venture043d3232018-08-31 10:10:53 -0700159 auto iface =
160 std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500161 iface->value(val);
162
163 hwmon::Attributes attrs;
164 if (hwmon::getAttributes(sensor.first, attrs))
165 {
166 iface->unit(hwmon::getUnit(attrs));
James Feistee73f5b2018-08-01 16:31:42 -0700167
168 setScale(iface, hwmon::getScale(attrs), val);
169
170 scale = hwmon::getScale(attrs);
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500171 }
172
173 auto maxValue = env::getEnv("MAXVALUE", sensor);
Patrick Venture043d3232018-08-31 10:10:53 -0700174 if (!maxValue.empty())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500175 {
176 iface->maxValue(std::stoll(maxValue));
177 }
178 auto minValue = env::getEnv("MINVALUE", sensor);
Patrick Venture043d3232018-08-31 10:10:53 -0700179 if (!minValue.empty())
Matthew Barthcb3daaf2018-05-07 15:03:16 -0500180 {
181 iface->minValue(std::stoll(minValue));
182 }
183
184 obj[InterfaceType::VALUE] = iface;
185 return iface;
186}
187
Matthew Barth2e41b132018-05-07 14:15:45 -0500188std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info)
Matthew Barth35819382018-04-18 14:53:01 -0500189{
190 namespace fs = std::experimental::filesystem;
191
192 std::shared_ptr<StatusObject> iface = nullptr;
Matthew Barth35819382018-04-18 14:53:01 -0500193 auto& objPath = std::get<std::string>(info);
194 auto& obj = std::get<Object>(info);
195
196 // Check if fault sysfs file exists
197 std::string faultName = sensor.first;
198 std::string faultID = sensor.second;
199 std::string entry = hwmon::entry::fault;
200
Patrick Venture043d3232018-08-31 10:10:53 -0700201 auto sysfsFullPath =
202 sysfs::make_sysfs_path(ioAccess.path(), faultName, faultID, entry);
Matthew Barth35819382018-04-18 14:53:01 -0500203 if (fs::exists(sysfsFullPath))
204 {
205 bool functional = true;
Matthew Barth35819382018-04-18 14:53:01 -0500206 try
207 {
Patrick Venture685efa12018-10-12 18:00:13 -0700208 uint32_t fault = ioAccess.read(faultName, faultID, entry,
209 hwmonio::retries, hwmonio::delay);
Matthew Barth35819382018-04-18 14:53:01 -0500210 if (fault != 0)
211 {
212 functional = false;
213 }
214 }
215 catch (const std::system_error& e)
216 {
Patrick Venture043d3232018-08-31 10:10:53 -0700217 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::
218 Error;
219 using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure;
Matthew Barth35819382018-04-18 14:53:01 -0500220
Patrick Venture043d3232018-08-31 10:10:53 -0700221 report<ReadFailure>(metadata::CALLOUT_ERRNO(e.code().value()),
222 metadata::CALLOUT_DEVICE_PATH(devPath.c_str()));
Matthew Barth35819382018-04-18 14:53:01 -0500223
Patrick Venture043d3232018-08-31 10:10:53 -0700224 log<level::INFO>(
225 "Logging failing sysfs file",
226 phosphor::logging::entry("FILE=%s", sysfsFullPath.c_str()));
Matthew Barth35819382018-04-18 14:53:01 -0500227 }
228
Patrick Venture685efa12018-10-12 18:00:13 -0700229 static constexpr bool deferSignals = true;
230 auto& bus = *std::get<sdbusplus::bus::bus*>(info);
231
Patrick Venture043d3232018-08-31 10:10:53 -0700232 iface =
233 std::make_shared<StatusObject>(bus, objPath.c_str(), deferSignals);
Matthew Barth35819382018-04-18 14:53:01 -0500234 // Set functional property
235 iface->functional(functional);
236
237 obj[InterfaceType::STATUS] = iface;
238 }
239
240 return iface;
241}
242
Patrick Ventureb28f4322018-09-14 10:19:14 -0700243void Sensor::unlockGpio()
244{
245 if (handle)
246 {
247 handle->setValues({1});
248 std::this_thread::sleep_for(pause);
249 }
250}
251
252void Sensor::lockGpio()
253{
254 if (handle)
255 {
256 handle->setValues({0});
257 }
258}
259
Matthew Barth35819382018-04-18 14:53:01 -0500260} // namespace sensor