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 <fmt/format.h>
22 
23 #include <phosphor-logging/log.hpp>
24 
25 #include <string>
26 #include <tuple>
27 #include <vector>
28 
29 namespace phosphor
30 {
31 namespace fan
32 {
33 namespace presence
34 {
35 
36 using namespace phosphor::logging;
37 using namespace std::literals::string_literals;
38 
39 static const auto tachNamespace = "/xyz/openbmc_project/sensors/fan_tach/"s;
40 static const auto tachIface = "xyz.openbmc_project.Sensor.Value"s;
41 static const auto tachProperty = "Value"s;
42 
43 Tach::Tach(const std::vector<std::string>& sensors) : currentState(false)
44 {
45     // Initialize state.
46     for (const auto& s : sensors)
47     {
48         state.emplace_back(s, nullptr, 0);
49     }
50 }
51 
52 bool Tach::start()
53 {
54     for (size_t i = 0; i < state.size(); ++i)
55     {
56         auto& s = state[i];
57         auto tachPath = tachNamespace + std::get<std::string>(s);
58 
59         // Register for signal callbacks.
60         std::get<1>(s) = std::make_unique<sdbusplus::bus::match::match>(
61             util::SDBusPlus::getBus(),
62             sdbusplus::bus::match::rules::propertiesChanged(tachPath,
63                                                             tachIface),
64             [this, i](auto& msg) { this->propertiesChanged(i, msg); });
65 
66         // Get an initial tach speed.
67         try
68         {
69             std::get<double>(s) = util::SDBusPlus::getProperty<double>(
70                 tachPath, tachIface, tachProperty);
71         }
72         catch (std::exception&)
73         {
74             // Assume not spinning.
75 
76             std::get<double>(s) = 0;
77             log<level::INFO>(
78                 fmt::format("Unable to read fan tach sensor {}", tachPath)
79                     .c_str());
80         }
81     }
82 
83     // Set the initial state of the sensor.
84     currentState = std::any_of(state.begin(), state.end(), [](const auto& s) {
85         return std::get<double>(s) != 0;
86     });
87 
88     return currentState;
89 }
90 
91 void Tach::stop()
92 {
93     for (auto& s : state)
94     {
95         // De-register signal callbacks.
96         std::get<1>(s) = nullptr;
97     }
98 }
99 
100 bool Tach::present()
101 {
102     // Live query the tach readings.
103     std::vector<double> values;
104     for (const auto& s : state)
105     {
106         values.push_back(util::SDBusPlus::getProperty<double>(
107             tachNamespace + std::get<std::string>(s), tachIface, tachProperty));
108     }
109 
110     return std::any_of(values.begin(), values.end(),
111                        [](const auto& v) { return v != 0; });
112 }
113 
114 void Tach::propertiesChanged(size_t sensor, sdbusplus::message::message& msg)
115 {
116     std::string iface;
117     util::Properties<double> properties;
118     msg.read(iface, properties);
119 
120     propertiesChanged(sensor, properties);
121 }
122 
123 void Tach::propertiesChanged(size_t sensor,
124                              const util::Properties<double>& props)
125 {
126     // Find the Value property containing the speed.
127     auto it = props.find(tachProperty);
128     if (it != props.end())
129     {
130         auto& s = state[sensor];
131         std::get<double>(s) = std::get<double>(it->second);
132 
133         auto newState =
134             std::any_of(state.begin(), state.end(),
135                         [](const auto& s) { return std::get<double>(s) != 0; });
136 
137         if (currentState != newState)
138         {
139             getPolicy().stateChanged(newState, *this);
140             currentState = newState;
141         }
142     }
143 }
144 
145 void Tach::logConflict(const std::string& fanInventoryPath) const
146 {
147     getLogger().log(fmt::format(
148         "Tach sensor presence detect for fan {} said not present but "
149         "other methods indicated present",
150         fanInventoryPath));
151 
152     // Let the code that monitors fan faults create the event
153     // logs for stopped rotors.
154 }
155 
156 } // namespace presence
157 } // namespace fan
158 } // namespace phosphor
159