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