xref: /openbmc/phosphor-hwmon/sensor.cpp (revision 99b95815)
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 <cmath>
12 #include <cstring>
13 #include <experimental/filesystem>
14 #include <phosphor-logging/elog-errors.hpp>
15 #include <thread>
16 #include <xyz/openbmc_project/Common/error.hpp>
17 #include <xyz/openbmc_project/Sensor/Device/error.hpp>
18 
19 namespace sensor
20 {
21 
22 using namespace phosphor::logging;
23 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
24 
25 // 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
27 template <typename T>
28 void setScale(T& iface, int64_t value, double)
29 {
30 }
31 template <typename T>
32 void 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
38 Sensor::Sensor(const SensorSet::key_type& sensor,
39                const hwmonio::HwmonIOInterface* ioAccess,
40                const std::string& devPath) :
41     _sensor(sensor),
42     _ioAccess(ioAccess), _devPath(devPath)
43 {
44     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 
57     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);
71 }
72 
73 void Sensor::addRemoveRCs(const std::string& rcList)
74 {
75     if (rcList.empty())
76     {
77         return;
78     }
79 
80     // Convert to a char* for strtok
81     std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1);
82     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 
102 SensorValueType Sensor::adjustValue(SensorValueType value)
103 {
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)>(static_cast<double>(value) *
116                                              _sensorAdjusts.gain +
117                                          _sensorAdjusts.offset);
118 
119     if constexpr (std::is_same<SensorValueType, double>::value)
120     {
121         value *= std::pow(10, _scale);
122     }
123 
124     return value;
125 }
126 
127 std::shared_ptr<ValueObject> Sensor::addValue(const RetryIO& retryIO,
128                                               ObjectInfo& info)
129 {
130     static constexpr bool deferSignals = true;
131 
132     // Get the initial value for the value interface.
133     auto& bus = *std::get<sdbusplus::bus::bus*>(info);
134     auto& obj = std::get<Object>(info);
135     auto& objPath = std::get<std::string>(info);
136 
137     SensorValueType val = 0;
138     std::shared_ptr<StatusObject> statusIface = nullptr;
139     auto it = obj.find(InterfaceType::STATUS);
140     if (it != obj.end())
141     {
142         statusIface = std::any_cast<std::shared_ptr<StatusObject>>(it->second);
143     }
144 
145     // If there's no fault file or the sensor has a fault file and
146     // its status is functional, read the input value.
147     if (!statusIface || (statusIface && statusIface->functional()))
148     {
149         unlockGpio();
150 
151         // Retry for up to a second if device is busy
152         // or has a transient error.
153         val = _ioAccess->read(_sensor.first, _sensor.second,
154                               hwmon::entry::cinput, std::get<size_t>(retryIO),
155                               std::get<std::chrono::milliseconds>(retryIO));
156 
157         lockGpio();
158         val = adjustValue(val);
159     }
160 
161     auto iface =
162         std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals);
163     iface->value(val);
164 
165     hwmon::Attributes attrs;
166     if (hwmon::getAttributes(_sensor.first, attrs))
167     {
168         iface->unit(hwmon::getUnit(attrs));
169 
170         setScale(iface, hwmon::getScale(attrs), val);
171 
172         _scale = hwmon::getScale(attrs);
173     }
174 
175     auto maxValue = env::getEnv("MAXVALUE", _sensor);
176     if (!maxValue.empty())
177     {
178         iface->maxValue(std::stoll(maxValue));
179     }
180     auto minValue = env::getEnv("MINVALUE", _sensor);
181     if (!minValue.empty())
182     {
183         iface->minValue(std::stoll(minValue));
184     }
185 
186     obj[InterfaceType::VALUE] = iface;
187     return iface;
188 }
189 
190 std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info)
191 {
192     namespace fs = std::experimental::filesystem;
193 
194     std::shared_ptr<StatusObject> iface = nullptr;
195     auto& objPath = std::get<std::string>(info);
196     auto& obj = std::get<Object>(info);
197 
198     // Check if fault sysfs file exists
199     std::string faultName = _sensor.first;
200     std::string faultID = _sensor.second;
201     std::string entry = hwmon::entry::fault;
202 
203     auto sysfsFullPath =
204         sysfs::make_sysfs_path(_ioAccess->path(), faultName, faultID, entry);
205     if (fs::exists(sysfsFullPath))
206     {
207         bool functional = true;
208         try
209         {
210             uint32_t fault = _ioAccess->read(faultName, faultID, entry,
211                                              hwmonio::retries, hwmonio::delay);
212             if (fault != 0)
213             {
214                 functional = false;
215             }
216         }
217         catch (const std::system_error& e)
218         {
219             using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::
220                 Error;
221             using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure;
222 
223             report<ReadFailure>(
224                 metadata::CALLOUT_ERRNO(e.code().value()),
225                 metadata::CALLOUT_DEVICE_PATH(_devPath.c_str()));
226 
227             log<level::INFO>(
228                 "Logging failing sysfs file",
229                 phosphor::logging::entry("FILE=%s", sysfsFullPath.c_str()));
230         }
231 
232         static constexpr bool deferSignals = true;
233         auto& bus = *std::get<sdbusplus::bus::bus*>(info);
234 
235         iface =
236             std::make_shared<StatusObject>(bus, objPath.c_str(), deferSignals);
237         // Set functional property
238         iface->functional(functional);
239 
240         obj[InterfaceType::STATUS] = iface;
241     }
242 
243     return iface;
244 }
245 
246 void Sensor::unlockGpio()
247 {
248     if (_handle)
249     {
250         _handle->setValues({1});
251         std::this_thread::sleep_for(_pause);
252     }
253 }
254 
255 void Sensor::lockGpio()
256 {
257     if (_handle)
258     {
259         _handle->setValues({0});
260     }
261 }
262 
263 } // namespace sensor
264