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