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