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