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