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