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