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 <phosphor-logging/elog-errors.hpp>
9 #include <phosphor-logging/log.hpp>
10 #include <xyz/openbmc_project/Sensor/Device/error.hpp>
11
12 #include <filesystem>
13 #include <format>
14 #include <memory>
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 {
failTargets34 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>
addTarget(const SensorSet::key_type & sensor,const hwmonio::HwmonIOInterface * ioAccess,const std::string & devPath,ObjectInfo & info)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>(std::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_t*>(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