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 <cassert> 12 #include <cmath> 13 #include <cstring> 14 #include <filesystem> 15 #include <phosphor-logging/elog-errors.hpp> 16 #include <thread> 17 #include <xyz/openbmc_project/Common/error.hpp> 18 #include <xyz/openbmc_project/Sensor/Device/error.hpp> 19 20 namespace sensor 21 { 22 23 using namespace phosphor::logging; 24 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 25 26 // todo: this can be deleted once we move to double 27 // helper class to set the scale on the value iface only when it's available 28 template <typename T> 29 void setScale(T& iface, int64_t value, double) 30 { 31 } 32 template <typename T> 33 void setScale(T& iface, int64_t value, int64_t) 34 { 35 iface->scale(value); 36 } 37 38 // todo: this can be simplified once we move to the double interface 39 Sensor::Sensor(const SensorSet::key_type& sensor, 40 const hwmonio::HwmonIOInterface* ioAccess, 41 const std::string& devPath) : 42 _sensor(sensor), 43 _ioAccess(ioAccess), _devPath(devPath), _scale(0), _hasFaultFile(false) 44 { 45 auto chip = env::getEnv("GPIOCHIP", sensor); 46 auto access = env::getEnv("GPIO", sensor); 47 if (!access.empty() && !chip.empty()) 48 { 49 _handle = gpio::BuildGpioHandle(chip, access); 50 51 if (!_handle) 52 { 53 log<level::ERR>("Unable to set up gpio locking"); 54 elog<InternalFailure>(); 55 } 56 } 57 58 auto gain = env::getEnv("GAIN", sensor); 59 if (!gain.empty()) 60 { 61 _sensorAdjusts.gain = std::stod(gain); 62 } 63 64 auto offset = env::getEnv("OFFSET", sensor); 65 if (!offset.empty()) 66 { 67 _sensorAdjusts.offset = std::stoi(offset); 68 } 69 auto senRmRCs = env::getEnv("REMOVERCS", sensor); 70 // Add sensor removal return codes defined per sensor 71 addRemoveRCs(senRmRCs); 72 } 73 74 void Sensor::addRemoveRCs(const std::string& rcList) 75 { 76 if (rcList.empty()) 77 { 78 return; 79 } 80 81 // Convert to a char* for strtok 82 std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1); 83 auto rmRC = std::strtok(&rmRCs[0], ", "); 84 while (rmRC != nullptr) 85 { 86 try 87 { 88 _sensorAdjusts.rmRCs.insert(std::stoi(rmRC)); 89 } 90 catch (const std::logic_error& le) 91 { 92 // Unable to convert to int, continue to next token 93 std::string name = _sensor.first + "_" + _sensor.second; 94 log<level::INFO>("Unable to convert sensor removal return code", 95 entry("SENSOR=%s", name.c_str()), 96 entry("RC=%s", rmRC), 97 entry("EXCEPTION=%s", le.what())); 98 } 99 rmRC = std::strtok(nullptr, ", "); 100 } 101 } 102 103 SensorValueType Sensor::adjustValue(SensorValueType value) 104 { 105 // Because read doesn't have an out pointer to store errors. 106 // let's assume negative values are errors if they have this 107 // set. 108 #ifdef NEGATIVE_ERRNO_ON_FAIL 109 if (value < 0) 110 { 111 return value; 112 } 113 #endif 114 115 // Adjust based on gain and offset 116 value = static_cast<decltype(value)>(static_cast<double>(value) * 117 _sensorAdjusts.gain + 118 _sensorAdjusts.offset); 119 120 if constexpr (std::is_same<SensorValueType, double>::value) 121 { 122 value *= std::pow(10, _scale); 123 } 124 125 return value; 126 } 127 128 std::shared_ptr<ValueObject> Sensor::addValue(const RetryIO& retryIO, 129 ObjectInfo& info) 130 { 131 static constexpr bool deferSignals = true; 132 133 // Get the initial value for the value interface. 134 auto& bus = *std::get<sdbusplus::bus::bus*>(info); 135 auto& obj = std::get<InterfaceMap>(info); 136 auto& objPath = std::get<std::string>(info); 137 138 SensorValueType val = 0; 139 140 auto& statusIface = std::any_cast<std::shared_ptr<StatusObject>&>( 141 obj[InterfaceType::STATUS]); 142 // As long as addStatus is called before addValue, statusIface 143 // should never be nullptr 144 assert(statusIface); 145 146 // Only read the input value if the status is functional 147 if (statusIface->functional()) 148 { 149 #ifdef UPDATE_FUNCTIONAL_ON_FAIL 150 try 151 #endif 152 { 153 // RAII object for GPIO unlock / lock 154 GpioLock gpioLock(getGpio()); 155 156 // Retry for up to a second if device is busy 157 // or has a transient error. 158 val = 159 _ioAccess->read(_sensor.first, _sensor.second, 160 hwmon::entry::cinput, std::get<size_t>(retryIO), 161 std::get<std::chrono::milliseconds>(retryIO)); 162 163 val = adjustValue(val); 164 } 165 #ifdef UPDATE_FUNCTIONAL_ON_FAIL 166 catch (const std::system_error& e) 167 { 168 // Catch the exception here and update the functional property. 169 // By catching the exception, it will not propagate it up the stack 170 // and thus the code will skip the "Remove RCs" check in 171 // MainLoop::getObject and will not exit on failure. 172 statusIface->functional(false); 173 } 174 #endif 175 } 176 177 auto iface = 178 std::make_shared<ValueObject>(bus, objPath.c_str(), deferSignals); 179 iface->value(val); 180 181 hwmon::Attributes attrs; 182 if (hwmon::getAttributes(_sensor.first, attrs)) 183 { 184 iface->unit(hwmon::getUnit(attrs)); 185 186 setScale(iface, hwmon::getScale(attrs), val); 187 188 _scale = hwmon::getScale(attrs); 189 } 190 191 auto maxValue = env::getEnv("MAXVALUE", _sensor); 192 if (!maxValue.empty()) 193 { 194 iface->maxValue(std::stoll(maxValue)); 195 } 196 auto minValue = env::getEnv("MINVALUE", _sensor); 197 if (!minValue.empty()) 198 { 199 iface->minValue(std::stoll(minValue)); 200 } 201 202 obj[InterfaceType::VALUE] = iface; 203 return iface; 204 } 205 206 std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info) 207 { 208 namespace fs = std::filesystem; 209 210 std::shared_ptr<StatusObject> iface = nullptr; 211 auto& objPath = std::get<std::string>(info); 212 auto& obj = std::get<InterfaceMap>(info); 213 214 // Check if fault sysfs file exists 215 std::string faultName = _sensor.first; 216 std::string faultID = _sensor.second; 217 std::string entry = hwmon::entry::fault; 218 219 bool functional = true; 220 auto sysfsFullPath = 221 sysfs::make_sysfs_path(_ioAccess->path(), faultName, faultID, entry); 222 if (fs::exists(sysfsFullPath)) 223 { 224 _hasFaultFile = true; 225 try 226 { 227 uint32_t fault = _ioAccess->read(faultName, faultID, entry, 228 hwmonio::retries, hwmonio::delay); 229 if (fault != 0) 230 { 231 functional = false; 232 } 233 } 234 catch (const std::system_error& e) 235 { 236 using namespace sdbusplus::xyz::openbmc_project::Sensor::Device:: 237 Error; 238 using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure; 239 240 report<ReadFailure>( 241 metadata::CALLOUT_ERRNO(e.code().value()), 242 metadata::CALLOUT_DEVICE_PATH(_devPath.c_str())); 243 244 log<level::INFO>( 245 "Logging failing sysfs file", 246 phosphor::logging::entry("FILE=%s", sysfsFullPath.c_str())); 247 } 248 } 249 250 static constexpr bool deferSignals = true; 251 auto& bus = *std::get<sdbusplus::bus::bus*>(info); 252 253 iface = std::make_shared<StatusObject>(bus, objPath.c_str(), deferSignals); 254 // Set functional property 255 iface->functional(functional); 256 257 obj[InterfaceType::STATUS] = iface; 258 259 return iface; 260 } 261 262 GpioLock::GpioLock(const gpioplus::HandleInterface* handle) : _handle(handle) 263 { 264 unlockGpio(); 265 } 266 267 GpioLock::~GpioLock() 268 { 269 lockGpio(); 270 } 271 272 void GpioLock::unlockGpio() 273 { 274 if (_handle) 275 { 276 _handle->setValues({1}); 277 std::this_thread::sleep_for(_pause); 278 } 279 } 280 281 void GpioLock::lockGpio() 282 { 283 if (_handle) 284 { 285 _handle->setValues({0}); 286 } 287 } 288 289 } // namespace sensor 290