1 #pragma once 2 3 #include "env.hpp" 4 #include "fan_pwm.hpp" 5 #include "fan_speed.hpp" 6 #include "hwmonio.hpp" 7 8 #include <fmt/format.h> 9 10 #include <phosphor-logging/elog-errors.hpp> 11 #include <phosphor-logging/log.hpp> 12 #include <xyz/openbmc_project/Sensor/Device/error.hpp> 13 14 #include <filesystem> 15 #include <memory> 16 17 enum class targetType 18 { 19 DEFAULT, 20 RPM, 21 PWM 22 }; 23 24 static constexpr auto RPM_TARGET = "RPM"; 25 static constexpr auto PWM_TARGET = "PWM"; 26 27 /** @class Targets 28 * @brief Target type traits. 29 * 30 * @tparam T - The target type. 31 */ 32 template <typename T> 33 struct Targets 34 { 35 static void fail() 36 { 37 static_assert(sizeof(Targets) == -1, "Unsupported Target type"); 38 } 39 }; 40 41 /**@brief Targets specialization for fan speed. */ 42 template <> 43 struct Targets<hwmon::FanSpeed> 44 { 45 static constexpr InterfaceType type = InterfaceType::FAN_SPEED; 46 }; 47 48 template <> 49 struct Targets<hwmon::FanPwm> 50 { 51 static constexpr InterfaceType type = InterfaceType::FAN_PWM; 52 }; 53 54 /** @brief addTarget 55 * 56 * Creates the target type interface 57 * 58 * @tparam T - The target type 59 * 60 * @param[in] sensor - A sensor type and name 61 * @param[in] ioAccess - hwmon sysfs access object 62 * @param[in] devPath - The /sys/devices sysfs path 63 * @param[in] info - The sdbusplus server connection and interfaces 64 * 65 * @return A shared pointer to the target interface object 66 * Will be empty if no interface was created 67 */ 68 template <typename T> 69 std::shared_ptr<T> addTarget(const SensorSet::key_type& sensor, 70 const hwmonio::HwmonIOInterface* ioAccess, 71 const std::string& devPath, ObjectInfo& info) 72 { 73 std::shared_ptr<T> target; 74 namespace fs = std::filesystem; 75 76 auto& obj = std::get<InterfaceMap>(info); 77 auto& objPath = std::get<std::string>(info); 78 auto type = Targets<T>::type; 79 80 // Check if target sysfs file exists 81 std::string sysfsFullPath; 82 std::string targetName = sensor.first; 83 std::string targetId = sensor.second; 84 std::string entry = hwmon::entry::target; 85 86 using namespace std::literals; 87 const std::string pwm = "pwm"s; 88 const std::string empty = ""s; 89 90 if (InterfaceType::FAN_PWM == type) 91 { 92 targetName = pwm; 93 // If PWM_TARGET is set, use the specified pwm id 94 auto id = env::getEnv("PWM_TARGET", sensor); 95 if (!id.empty()) 96 { 97 targetId = id; 98 } 99 entry = empty; 100 } 101 102 sysfsFullPath = sysfs::make_sysfs_path(ioAccess->path(), targetName, 103 targetId, entry); 104 if (fs::exists(sysfsFullPath)) 105 { 106 auto useTarget = true; 107 auto tmEnv = env::getEnv("TARGET_MODE"); 108 if (!tmEnv.empty()) 109 { 110 std::string mode{tmEnv}; 111 std::transform(mode.begin(), mode.end(), mode.begin(), toupper); 112 113 if (mode == RPM_TARGET) 114 { 115 if (type != InterfaceType::FAN_SPEED) 116 { 117 useTarget = false; 118 } 119 } 120 else if (mode == PWM_TARGET) 121 { 122 if (type != InterfaceType::FAN_PWM) 123 { 124 useTarget = false; 125 } 126 } 127 else 128 { 129 using namespace phosphor::logging; 130 log<level::ERR>( 131 "Invalid TARGET_MODE env var found", 132 phosphor::logging::entry("TARGET_MODE=%s", tmEnv.c_str()), 133 phosphor::logging::entry("DEVPATH=%s", devPath.c_str())); 134 } 135 } 136 137 if (useTarget) 138 { 139 uint32_t targetSpeed = 0; 140 141 try 142 { 143 targetSpeed = ioAccess->read(targetName, targetId, entry, 144 hwmonio::retries, hwmonio::delay); 145 } 146 catch (const std::system_error& e) 147 { 148 using namespace phosphor::logging; 149 using namespace sdbusplus::xyz::openbmc_project::Sensor:: 150 Device::Error; 151 using metadata = 152 xyz::openbmc_project::Sensor::Device::ReadFailure; 153 154 report<ReadFailure>( 155 metadata::CALLOUT_ERRNO(e.code().value()), 156 metadata::CALLOUT_DEVICE_PATH(devPath.c_str())); 157 158 log<level::INFO>(fmt::format("Failing sysfs file: {} errno: {}", 159 sysfsFullPath, e.code().value()) 160 .c_str()); 161 } 162 163 static constexpr bool deferSignals = true; 164 auto& bus = *std::get<sdbusplus::bus_t*>(info); 165 166 // ioAccess->path() is a path like: /sys/class/hwmon/hwmon1 167 // NOTE: When unit-testing, the target won't have an inject-ible 168 // ioAccess: fan_pwm/fan_speed. 169 target = std::make_shared<T>( 170 std::move(std::make_unique<hwmonio::HwmonIO>(ioAccess->path())), 171 devPath, targetId, bus, objPath.c_str(), deferSignals, 172 targetSpeed); 173 obj[type] = target; 174 } 175 } 176 177 return target; 178 } 179