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