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