xref: /openbmc/phosphor-hwmon/sensor.cpp (revision 4cbdfef205ba4011018a3a287dd238e00509a757)
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 <cstring>
12 #include <experimental/filesystem>
13 #include <phosphor-logging/elog-errors.hpp>
14 #include <thread>
15 #include <xyz/openbmc_project/Common/error.hpp>
16 #include <xyz/openbmc_project/Sensor/Device/error.hpp>
17 
18 namespace sensor
19 {
20 
21 using namespace phosphor::logging;
22 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
23 
24 Sensor::Sensor(const SensorSet::key_type& sensor,
25                const hwmonio::HwmonIO& ioAccess, const std::string& devPath) :
26     sensor(sensor),
27     ioAccess(ioAccess), devPath(devPath)
28 {
29     auto chip = env::getEnv("GPIOCHIP", sensor);
30     auto access = env::getEnv("GPIO", sensor);
31     if (!access.empty() && !chip.empty())
32     {
33         handle = gpio::BuildGpioHandle(chip, access);
34 
35         if (!handle)
36         {
37             log<level::ERR>("Unable to set up gpio locking");
38             elog<InternalFailure>();
39         }
40     }
41 
42     auto gain = env::getEnv("GAIN", sensor);
43     if (!gain.empty())
44     {
45         sensorAdjusts.gain = std::stod(gain);
46     }
47 
48     auto offset = env::getEnv("OFFSET", sensor);
49     if (!offset.empty())
50     {
51         sensorAdjusts.offset = std::stoi(offset);
52     }
53     auto senRmRCs = env::getEnv("REMOVERCS", sensor);
54     // Add sensor removal return codes defined per sensor
55     addRemoveRCs(senRmRCs);
56 }
57 
58 void Sensor::addRemoveRCs(const std::string& rcList)
59 {
60     if (rcList.empty())
61     {
62         return;
63     }
64 
65     // Convert to a char* for strtok
66     std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1);
67     auto rmRC = std::strtok(&rmRCs[0], ", ");
68     while (rmRC != nullptr)
69     {
70         try
71         {
72             sensorAdjusts.rmRCs.insert(std::stoi(rmRC));
73         }
74         catch (const std::logic_error& le)
75         {
76             // Unable to convert to int, continue to next token
77             std::string name = sensor.first + "_" + sensor.second;
78             log<level::INFO>("Unable to convert sensor removal return code",
79                              entry("SENSOR=%s", name.c_str()),
80                              entry("RC=%s", rmRC),
81                              entry("EXCEPTION=%s", le.what()));
82         }
83         rmRC = std::strtok(nullptr, ", ");
84     }
85 }
86 
87 int64_t Sensor::adjustValue(int64_t value)
88 {
89 // Because read doesn't have an out pointer to store errors.
90 // let's assume negative values are errors if they have this
91 // set.
92 #ifdef NEGATIVE_ERRNO_ON_FAIL
93     if (value < 0)
94     {
95         return value;
96     }
97 #endif
98 
99     // Adjust based on gain and offset
100     value = static_cast<decltype(value)>(
101         static_cast<double>(value) * sensorAdjusts.gain + sensorAdjusts.offset);
102 
103     return value;
104 }
105 
106 std::shared_ptr<ValueObject> Sensor::addValue(const RetryIO& retryIO,
107                                               ObjectInfo& info)
108 {
109     static constexpr bool deferSignals = true;
110 
111     // Get the initial value for the value interface.
112     auto& bus = *std::get<sdbusplus::bus::bus*>(info);
113     auto& obj = std::get<Object>(info);
114     auto& objPath = std::get<std::string>(info);
115 
116     int64_t val = 0;
117     std::shared_ptr<StatusObject> statusIface = nullptr;
118     auto it = obj.find(InterfaceType::STATUS);
119     if (it != obj.end())
120     {
121         statusIface = std::any_cast<std::shared_ptr<StatusObject>>(it->second);
122     }
123 
124     // If there's no fault file or the sensor has a fault file and
125     // its status is functional, read the input value.
126     if (!statusIface || (statusIface && statusIface->functional()))
127     {
128         unlockGpio();
129 
130         // Retry for up to a second if device is busy
131         // or has a transient error.
132         val = ioAccess.read(sensor.first, sensor.second, hwmon::entry::cinput,
133                             std::get<size_t>(retryIO),
134                             std::get<std::chrono::milliseconds>(retryIO));
135 
136         lockGpio();
137         val = adjustValue(val);
138     }
139 
140     auto iface =
141         std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals);
142     iface->value(val);
143 
144     hwmon::Attributes attrs;
145     if (hwmon::getAttributes(sensor.first, attrs))
146     {
147         iface->unit(hwmon::getUnit(attrs));
148         iface->scale(hwmon::getScale(attrs));
149     }
150 
151     auto maxValue = env::getEnv("MAXVALUE", sensor);
152     if (!maxValue.empty())
153     {
154         iface->maxValue(std::stoll(maxValue));
155     }
156     auto minValue = env::getEnv("MINVALUE", sensor);
157     if (!minValue.empty())
158     {
159         iface->minValue(std::stoll(minValue));
160     }
161 
162     obj[InterfaceType::VALUE] = iface;
163     return iface;
164 }
165 
166 std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info)
167 {
168     namespace fs = std::experimental::filesystem;
169 
170     std::shared_ptr<StatusObject> iface = nullptr;
171     auto& objPath = std::get<std::string>(info);
172     auto& obj = std::get<Object>(info);
173 
174     // Check if fault sysfs file exists
175     std::string faultName = sensor.first;
176     std::string faultID = sensor.second;
177     std::string entry = hwmon::entry::fault;
178 
179     auto sysfsFullPath =
180         sysfs::make_sysfs_path(ioAccess.path(), faultName, faultID, entry);
181     if (fs::exists(sysfsFullPath))
182     {
183         bool functional = true;
184         try
185         {
186             uint32_t fault = ioAccess.read(faultName, faultID, entry,
187                                            hwmonio::retries, hwmonio::delay);
188             if (fault != 0)
189             {
190                 functional = false;
191             }
192         }
193         catch (const std::system_error& e)
194         {
195             using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::
196                 Error;
197             using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure;
198 
199             report<ReadFailure>(metadata::CALLOUT_ERRNO(e.code().value()),
200                                 metadata::CALLOUT_DEVICE_PATH(devPath.c_str()));
201 
202             log<level::INFO>(
203                 "Logging failing sysfs file",
204                 phosphor::logging::entry("FILE=%s", sysfsFullPath.c_str()));
205         }
206 
207         static constexpr bool deferSignals = true;
208         auto& bus = *std::get<sdbusplus::bus::bus*>(info);
209 
210         iface =
211             std::make_shared<StatusObject>(bus, objPath.c_str(), deferSignals);
212         // Set functional property
213         iface->functional(functional);
214 
215         obj[InterfaceType::STATUS] = iface;
216     }
217 
218     return iface;
219 }
220 
221 void Sensor::unlockGpio()
222 {
223     if (handle)
224     {
225         handle->setValues({1});
226         std::this_thread::sleep_for(pause);
227     }
228 }
229 
230 void Sensor::lockGpio()
231 {
232     if (handle)
233     {
234         handle->setValues({0});
235     }
236 }
237 
238 } // namespace sensor
239