xref: /openbmc/phosphor-hwmon/sensor.cpp (revision 79205b2c1466f8ee7ab11dc976d33ac0771355ea)
1 #include "config.h"
2 
3 #include "sensor.hpp"
4 
5 #include "env.hpp"
6 #include "gpio_handle.hpp"
7 #include "hwmon.hpp"
8 #include "sensorset.hpp"
9 #include "sysfs.hpp"
10 
11 #include <cassert>
12 #include <cmath>
13 #include <cstring>
14 #include <filesystem>
15 #include <phosphor-logging/elog-errors.hpp>
16 #include <thread>
17 #include <xyz/openbmc_project/Common/error.hpp>
18 #include <xyz/openbmc_project/Sensor/Device/error.hpp>
19 
20 namespace sensor
21 {
22 
23 using namespace phosphor::logging;
24 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
25 
26 // todo: this can be deleted once we move to double
27 // helper class to set the scale on the value iface only when it's available
28 template <typename T>
29 void setScale(T& iface, int64_t value, double)
30 {
31 }
32 template <typename T>
33 void setScale(T& iface, int64_t value, int64_t)
34 {
35     iface->scale(value);
36 }
37 
38 // todo: this can be simplified once we move to the double interface
39 Sensor::Sensor(const SensorSet::key_type& sensor,
40                const hwmonio::HwmonIOInterface* ioAccess,
41                const std::string& devPath) :
42     _sensor(sensor),
43     _ioAccess(ioAccess), _devPath(devPath), _scale(0), _hasFaultFile(false)
44 {
45     auto chip = env::getEnv("GPIOCHIP", sensor);
46     auto access = env::getEnv("GPIO", sensor);
47     if (!access.empty() && !chip.empty())
48     {
49         _handle = gpio::BuildGpioHandle(chip, access);
50 
51         if (!_handle)
52         {
53             log<level::ERR>("Unable to set up gpio locking");
54             elog<InternalFailure>();
55         }
56     }
57 
58     auto gain = env::getEnv("GAIN", sensor);
59     if (!gain.empty())
60     {
61         _sensorAdjusts.gain = std::stod(gain);
62     }
63 
64     auto offset = env::getEnv("OFFSET", sensor);
65     if (!offset.empty())
66     {
67         _sensorAdjusts.offset = std::stoi(offset);
68     }
69     auto senRmRCs = env::getEnv("REMOVERCS", sensor);
70     // Add sensor removal return codes defined per sensor
71     addRemoveRCs(senRmRCs);
72 }
73 
74 void Sensor::addRemoveRCs(const std::string& rcList)
75 {
76     if (rcList.empty())
77     {
78         return;
79     }
80 
81     // Convert to a char* for strtok
82     std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1);
83     auto rmRC = std::strtok(&rmRCs[0], ", ");
84     while (rmRC != nullptr)
85     {
86         try
87         {
88             _sensorAdjusts.rmRCs.insert(std::stoi(rmRC));
89         }
90         catch (const std::logic_error& le)
91         {
92             // Unable to convert to int, continue to next token
93             std::string name = _sensor.first + "_" + _sensor.second;
94             log<level::INFO>("Unable to convert sensor removal return code",
95                              entry("SENSOR=%s", name.c_str()),
96                              entry("RC=%s", rmRC),
97                              entry("EXCEPTION=%s", le.what()));
98         }
99         rmRC = std::strtok(nullptr, ", ");
100     }
101 }
102 
103 SensorValueType Sensor::adjustValue(SensorValueType value)
104 {
105 // Because read doesn't have an out pointer to store errors.
106 // let's assume negative values are errors if they have this
107 // set.
108 #ifdef NEGATIVE_ERRNO_ON_FAIL
109     if (value < 0)
110     {
111         return value;
112     }
113 #endif
114 
115     // Adjust based on gain and offset
116     value = static_cast<decltype(value)>(static_cast<double>(value) *
117                                              _sensorAdjusts.gain +
118                                          _sensorAdjusts.offset);
119 
120     if constexpr (std::is_same<SensorValueType, double>::value)
121     {
122         value *= std::pow(10, _scale);
123     }
124 
125     return value;
126 }
127 
128 std::shared_ptr<ValueObject> Sensor::addValue(const RetryIO& retryIO,
129                                               ObjectInfo& info)
130 {
131     static constexpr bool deferSignals = true;
132 
133     // Get the initial value for the value interface.
134     auto& bus = *std::get<sdbusplus::bus::bus*>(info);
135     auto& obj = std::get<InterfaceMap>(info);
136     auto& objPath = std::get<std::string>(info);
137 
138     SensorValueType val = 0;
139 
140     auto& statusIface = std::any_cast<std::shared_ptr<StatusObject>&>(
141         obj[InterfaceType::STATUS]);
142     // As long as addStatus is called before addValue, statusIface
143     // should never be nullptr
144     assert(statusIface);
145 
146     // Only read the input value if the status is functional
147     if (statusIface->functional())
148     {
149 #ifdef UPDATE_FUNCTIONAL_ON_FAIL
150         try
151 #endif
152         {
153             // RAII object for GPIO unlock / lock
154             GpioLock gpioLock(getGpio());
155 
156             // Retry for up to a second if device is busy
157             // or has a transient error.
158             val =
159                 _ioAccess->read(_sensor.first, _sensor.second,
160                                 hwmon::entry::cinput, std::get<size_t>(retryIO),
161                                 std::get<std::chrono::milliseconds>(retryIO));
162 
163             val = adjustValue(val);
164         }
165 #ifdef UPDATE_FUNCTIONAL_ON_FAIL
166         catch (const std::system_error& e)
167         {
168             // Catch the exception here and update the functional property.
169             // By catching the exception, it will not propagate it up the stack
170             // and thus the code will skip the "Remove RCs" check in
171             // MainLoop::getObject and will not exit on failure.
172             statusIface->functional(false);
173         }
174 #endif
175     }
176 
177     auto iface =
178         std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals);
179     iface->value(val);
180 
181     hwmon::Attributes attrs;
182     if (hwmon::getAttributes(_sensor.first, attrs))
183     {
184         iface->unit(hwmon::getUnit(attrs));
185 
186         setScale(iface, hwmon::getScale(attrs), val);
187 
188         _scale = hwmon::getScale(attrs);
189     }
190 
191     auto maxValue = env::getEnv("MAXVALUE", _sensor);
192     if (!maxValue.empty())
193     {
194         iface->maxValue(std::stoll(maxValue));
195     }
196     auto minValue = env::getEnv("MINVALUE", _sensor);
197     if (!minValue.empty())
198     {
199         iface->minValue(std::stoll(minValue));
200     }
201 
202     obj[InterfaceType::VALUE] = iface;
203     return iface;
204 }
205 
206 std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info)
207 {
208     namespace fs = std::filesystem;
209 
210     std::shared_ptr<StatusObject> iface = nullptr;
211     auto& objPath = std::get<std::string>(info);
212     auto& obj = std::get<InterfaceMap>(info);
213 
214     // Check if fault sysfs file exists
215     std::string faultName = _sensor.first;
216     std::string faultID = _sensor.second;
217     std::string entry = hwmon::entry::fault;
218 
219     bool functional = true;
220     auto sysfsFullPath =
221         sysfs::make_sysfs_path(_ioAccess->path(), faultName, faultID, entry);
222     if (fs::exists(sysfsFullPath))
223     {
224         _hasFaultFile = true;
225         try
226         {
227             uint32_t fault = _ioAccess->read(faultName, faultID, entry,
228                                              hwmonio::retries, hwmonio::delay);
229             if (fault != 0)
230             {
231                 functional = false;
232             }
233         }
234         catch (const std::system_error& e)
235         {
236             using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::
237                 Error;
238             using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure;
239 
240             report<ReadFailure>(
241                 metadata::CALLOUT_ERRNO(e.code().value()),
242                 metadata::CALLOUT_DEVICE_PATH(_devPath.c_str()));
243 
244             log<level::INFO>(
245                 "Logging failing sysfs file",
246                 phosphor::logging::entry("FILE=%s", sysfsFullPath.c_str()));
247         }
248     }
249 
250     static constexpr bool deferSignals = true;
251     auto& bus = *std::get<sdbusplus::bus::bus*>(info);
252 
253     iface = std::make_shared<StatusObject>(bus, objPath.c_str(), deferSignals);
254     // Set functional property
255     iface->functional(functional);
256 
257     obj[InterfaceType::STATUS] = iface;
258 
259     return iface;
260 }
261 
262 GpioLock::GpioLock(const gpioplus::HandleInterface* handle) : _handle(handle)
263 {
264     unlockGpio();
265 }
266 
267 GpioLock::~GpioLock()
268 {
269     lockGpio();
270 }
271 
272 void GpioLock::unlockGpio()
273 {
274     if (_handle)
275     {
276         _handle->setValues({1});
277         std::this_thread::sleep_for(_pause);
278     }
279 }
280 
281 void GpioLock::lockGpio()
282 {
283     if (_handle)
284     {
285         _handle->setValues({0});
286     }
287 }
288 
289 } // namespace sensor
290