xref: /openbmc/phosphor-hwmon/targets.hpp (revision ad6043f6)
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_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