1 /**
2  * Copyright © 2017 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "tach.hpp"
17 
18 #include "logging.hpp"
19 #include "rpolicy.hpp"
20 
21 #include <phosphor-logging/log.hpp>
22 
23 #include <format>
24 #include <string>
25 #include <tuple>
26 #include <vector>
27 
28 namespace phosphor
29 {
30 namespace fan
31 {
32 namespace presence
33 {
34 
35 using namespace phosphor::logging;
36 using namespace std::literals::string_literals;
37 
38 static const auto tachNamespace = "/xyz/openbmc_project/sensors/fan_tach/"s;
39 static const auto tachIface = "xyz.openbmc_project.Sensor.Value"s;
40 static const auto tachProperty = "Value"s;
41 
42 Tach::Tach(const std::vector<std::string>& sensors) : currentState(false)
43 {
44     // Initialize state.
45     for (const auto& s : sensors)
46     {
47         state.emplace_back(s, nullptr, 0);
48     }
49 }
50 
51 bool Tach::start()
52 {
53     for (size_t i = 0; i < state.size(); ++i)
54     {
55         auto& s = state[i];
56         auto tachPath = tachNamespace + std::get<std::string>(s);
57 
58         // Register for signal callbacks.
59         std::get<1>(s) = std::make_unique<sdbusplus::bus::match_t>(
60             util::SDBusPlus::getBus(),
61             sdbusplus::bus::match::rules::propertiesChanged(tachPath,
62                                                             tachIface),
63             [this, i](auto& msg) { this->propertiesChanged(i, msg); });
64 
65         // Get an initial tach speed.
66         try
67         {
68             std::get<double>(s) = util::SDBusPlus::getProperty<double>(
69                 tachPath, tachIface, tachProperty);
70         }
71         catch (const std::exception&)
72         {
73             // Assume not spinning.
74 
75             std::get<double>(s) = 0;
76             log<level::INFO>(
77                 std::format("Unable to read fan tach sensor {}", tachPath)
78                     .c_str());
79         }
80     }
81 
82     // Set the initial state of the sensor.
83     currentState = std::any_of(state.begin(), state.end(), [](const auto& s) {
84         return std::get<double>(s) != 0;
85     });
86 
87     return currentState;
88 }
89 
90 void Tach::stop()
91 {
92     for (auto& s : state)
93     {
94         // De-register signal callbacks.
95         std::get<1>(s) = nullptr;
96     }
97 }
98 
99 bool Tach::present()
100 {
101     // Live query the tach readings.
102     std::vector<double> values;
103     for (const auto& s : state)
104     {
105         values.push_back(util::SDBusPlus::getProperty<double>(
106             tachNamespace + std::get<std::string>(s), tachIface, tachProperty));
107     }
108 
109     return std::any_of(values.begin(), values.end(),
110                        [](const auto& v) { return v != 0; });
111 }
112 
113 void Tach::propertiesChanged(size_t sensor, sdbusplus::message_t& msg)
114 {
115     std::string iface;
116     util::Properties<double> properties;
117     msg.read(iface, properties);
118 
119     propertiesChanged(sensor, properties);
120 }
121 
122 void Tach::propertiesChanged(size_t sensor,
123                              const util::Properties<double>& props)
124 {
125     // Find the Value property containing the speed.
126     auto it = props.find(tachProperty);
127     if (it != props.end())
128     {
129         auto& s = state[sensor];
130         std::get<double>(s) = std::get<double>(it->second);
131 
132         auto newState =
133             std::any_of(state.begin(), state.end(),
134                         [](const auto& s) { return std::get<double>(s) != 0; });
135 
136         if (currentState != newState)
137         {
138             getPolicy().stateChanged(newState, *this);
139             currentState = newState;
140         }
141     }
142 }
143 
144 void Tach::logConflict(const std::string& fanInventoryPath) const
145 {
146     getLogger().log(std::format(
147         "Tach sensor presence detect for fan {} said not present but "
148         "other methods indicated present",
149         fanInventoryPath));
150 
151     // Let the code that monitors fan faults create the event
152     // logs for stopped rotors.
153 }
154 
155 } // namespace presence
156 } // namespace fan
157 } // namespace phosphor
158