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