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