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