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