#include "config.h" #include "sensor.hpp" #include "env.hpp" #include "gpio_handle.hpp" #include "hwmon.hpp" #include "sensorset.hpp" #include "sysfs.hpp" #include #include #include #include #include #include #include namespace sensor { using namespace phosphor::logging; using namespace sdbusplus::xyz::openbmc_project::Common::Error; // todo: this can be deleted once we move to double // helper class to set the scale on the value iface only when it's available template void setScale(T& iface, int64_t value, double) { } template void setScale(T& iface, int64_t value, int64_t) { iface->scale(value); } // todo: this can be simplified once we move to the double interface Sensor::Sensor(const SensorSet::key_type& sensor, const hwmonio::HwmonIOInterface* ioAccess, const std::string& devPath) : _sensor(sensor), _ioAccess(ioAccess), _devPath(devPath), _scale(0) { auto chip = env::getEnv("GPIOCHIP", sensor); auto access = env::getEnv("GPIO", sensor); if (!access.empty() && !chip.empty()) { _handle = gpio::BuildGpioHandle(chip, access); if (!_handle) { log("Unable to set up gpio locking"); elog(); } } auto gain = env::getEnv("GAIN", sensor); if (!gain.empty()) { _sensorAdjusts.gain = std::stod(gain); } auto offset = env::getEnv("OFFSET", sensor); if (!offset.empty()) { _sensorAdjusts.offset = std::stoi(offset); } auto senRmRCs = env::getEnv("REMOVERCS", sensor); // Add sensor removal return codes defined per sensor addRemoveRCs(senRmRCs); } void Sensor::addRemoveRCs(const std::string& rcList) { if (rcList.empty()) { return; } // Convert to a char* for strtok std::vector rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1); auto rmRC = std::strtok(&rmRCs[0], ", "); while (rmRC != nullptr) { try { _sensorAdjusts.rmRCs.insert(std::stoi(rmRC)); } catch (const std::logic_error& le) { // Unable to convert to int, continue to next token std::string name = _sensor.first + "_" + _sensor.second; log("Unable to convert sensor removal return code", entry("SENSOR=%s", name.c_str()), entry("RC=%s", rmRC), entry("EXCEPTION=%s", le.what())); } rmRC = std::strtok(nullptr, ", "); } } SensorValueType Sensor::adjustValue(SensorValueType value) { // Because read doesn't have an out pointer to store errors. // let's assume negative values are errors if they have this // set. #ifdef NEGATIVE_ERRNO_ON_FAIL if (value < 0) { return value; } #endif // Adjust based on gain and offset value = static_cast(static_cast(value) * _sensorAdjusts.gain + _sensorAdjusts.offset); if constexpr (std::is_same::value) { value *= std::pow(10, _scale); } return value; } std::shared_ptr Sensor::addValue(const RetryIO& retryIO, ObjectInfo& info) { static constexpr bool deferSignals = true; // Get the initial value for the value interface. auto& bus = *std::get(info); auto& obj = std::get(info); auto& objPath = std::get(info); SensorValueType val = 0; std::shared_ptr statusIface = nullptr; auto it = obj.find(InterfaceType::STATUS); if (it != obj.end()) { statusIface = std::any_cast>(it->second); } // If there's no fault file or the sensor has a fault file and // its status is functional, read the input value. if (!statusIface || (statusIface && statusIface->functional())) { unlockGpio(); // Retry for up to a second if device is busy // or has a transient error. val = _ioAccess->read(_sensor.first, _sensor.second, hwmon::entry::cinput, std::get(retryIO), std::get(retryIO)); lockGpio(); val = adjustValue(val); } auto iface = std::make_shared(bus, objPath.c_str(), deferSignals); iface->value(val); hwmon::Attributes attrs; if (hwmon::getAttributes(_sensor.first, attrs)) { iface->unit(hwmon::getUnit(attrs)); setScale(iface, hwmon::getScale(attrs), val); _scale = hwmon::getScale(attrs); } auto maxValue = env::getEnv("MAXVALUE", _sensor); if (!maxValue.empty()) { iface->maxValue(std::stoll(maxValue)); } auto minValue = env::getEnv("MINVALUE", _sensor); if (!minValue.empty()) { iface->minValue(std::stoll(minValue)); } obj[InterfaceType::VALUE] = iface; return iface; } std::shared_ptr Sensor::addStatus(ObjectInfo& info) { namespace fs = std::filesystem; std::shared_ptr iface = nullptr; auto& objPath = std::get(info); auto& obj = std::get(info); // Check if fault sysfs file exists std::string faultName = _sensor.first; std::string faultID = _sensor.second; std::string entry = hwmon::entry::fault; auto sysfsFullPath = sysfs::make_sysfs_path(_ioAccess->path(), faultName, faultID, entry); if (fs::exists(sysfsFullPath)) { bool functional = true; try { uint32_t fault = _ioAccess->read(faultName, faultID, entry, hwmonio::retries, hwmonio::delay); if (fault != 0) { functional = false; } } catch (const std::system_error& e) { using namespace sdbusplus::xyz::openbmc_project::Sensor::Device:: Error; using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure; report( metadata::CALLOUT_ERRNO(e.code().value()), metadata::CALLOUT_DEVICE_PATH(_devPath.c_str())); log( "Logging failing sysfs file", phosphor::logging::entry("FILE=%s", sysfsFullPath.c_str())); } static constexpr bool deferSignals = true; auto& bus = *std::get(info); iface = std::make_shared(bus, objPath.c_str(), deferSignals); // Set functional property iface->functional(functional); obj[InterfaceType::STATUS] = iface; } return iface; } void Sensor::unlockGpio() { if (_handle) { _handle->setValues({1}); std::this_thread::sleep_for(_pause); } } void Sensor::lockGpio() { if (_handle) { _handle->setValues({0}); } } } // namespace sensor