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 <algorithm>
17 #include <phosphor-logging/log.hpp>
18 #include <phosphor-logging/elog.hpp>
19 #include <phosphor-logging/elog-errors.hpp>
20 #include <xyz/openbmc_project/Common/error.hpp>
21 #include <unistd.h>
22 #include "manager.hpp"
23 #include "utility.hpp"
24 
25 namespace phosphor
26 {
27 namespace fan
28 {
29 namespace control
30 {
31 
32 using namespace phosphor::logging;
33 
34 constexpr auto SYSTEMD_SERVICE   = "org.freedesktop.systemd1";
35 constexpr auto SYSTEMD_OBJ_PATH  = "/org/freedesktop/systemd1";
36 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
37 constexpr auto FAN_CONTROL_READY_TARGET = "obmc-fan-control-ready@0.target";
38 
39 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
40 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
41 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
42 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
43 
44 
45 /**
46  * Get the current value of the D-Bus property under the specified path
47  * and interface.
48  *
49  * @param[in] bus          - The D-Bus bus object
50  * @param[in] path         - The D-Bus path
51  * @param[in] interface    - The D-Bus interface
52  * @param[in] propertyName - The D-Bus property
53  * @param[out] value       - The D-Bus property's value
54  */
55 template <typename T>
56 void getProperty(sdbusplus::bus::bus& bus,
57                  const std::string& path,
58                  const std::string& interface,
59                  const std::string& propertyName,
60                  T& value)
61 {
62     sdbusplus::message::variant<T> property;
63     std::string service = phosphor::fan::util::getService(path, interface, bus);
64 
65     auto method = bus.new_method_call(service.c_str(),
66                                       path.c_str(),
67                                       PROPERTY_INTERFACE,
68                                       "Get");
69 
70     method.append(interface, propertyName);
71     auto reply = bus.call(method);
72 
73     if (reply.is_method_error())
74     {
75         log<level::ERR>("Error in call response for retrieving property");
76         elog<InternalFailure>();
77     }
78     reply.read(property);
79     value = sdbusplus::message::variant_ns::get<T>(property);
80 }
81 
82 
83 /**
84  * Check if a condition is true. Conditions are used to determine
85  * which fan zone to use.
86  *
87  * @param[in] bus       - The D-Bus bus object
88  * @param[in] condition - The condition to check if true
89  * @return result       - True if the condition is true
90  */
91 bool checkCondition(sdbusplus::bus::bus& bus, const auto& c)
92 {
93     auto& type = std::get<conditionTypePos>(c);
94     auto& properties = std::get<conditionPropertyListPos>(c);
95 
96     for (auto& p : properties)
97     {
98         bool value = std::get<propertyValuePos>(p);
99         bool propertyValue;
100 
101         // TODO openbmc/openbmc#1769: Support more types than just getProperty.
102         if (type.compare("getProperty") == 0)
103         {
104             getProperty(bus,
105                         std::get<propertyPathPos>(p),
106                         std::get<propertyInterfacePos>(p),
107                         std::get<propertyNamePos>(p),
108                         propertyValue);
109 
110             if (value != propertyValue)
111             {
112                 return false;
113             }
114         }
115     }
116     return true;
117 }
118 
119 
120 //Note: Future code will check 'mode' before starting control algorithm
121 Manager::Manager(sdbusplus::bus::bus& bus,
122                  phosphor::fan::event::EventPtr& events,
123                  Mode mode) :
124     _bus(bus)
125 {
126     //Create the appropriate Zone objects based on the
127     //actual system configuration.
128 
129     //Find the 1 ZoneGroup that meets all of its conditions
130     for (auto& group : _zoneLayouts)
131     {
132         auto& conditions = std::get<conditionListPos>(group);
133 
134         if (std::all_of(conditions.begin(), conditions.end(),
135                         [&bus](const auto& condition)
136         {
137             return checkCondition(bus, condition);
138         }))
139         {
140             //Create a Zone object for each zone in this group
141             auto& zones = std::get<zoneListPos>(group);
142 
143             for (auto& z : zones)
144             {
145                 _zones.emplace(std::get<zoneNumPos>(z),
146                                std::make_unique<Zone>(mode, _bus, events, z));
147             }
148 
149             break;
150         }
151     }
152 
153 }
154 
155 
156 void Manager::doInit()
157 {
158     for (auto& z : _zones)
159     {
160         z.second->setFullSpeed();
161     }
162 
163     auto delay = _powerOnDelay;
164     while (delay > 0)
165     {
166         delay = sleep(delay);
167     }
168 
169     startFanControlReadyTarget();
170 }
171 
172 
173 void Manager::startFanControlReadyTarget()
174 {
175     auto method = _bus.new_method_call(SYSTEMD_SERVICE,
176                                        SYSTEMD_OBJ_PATH,
177                                        SYSTEMD_INTERFACE,
178                                        "StartUnit");
179 
180     method.append(FAN_CONTROL_READY_TARGET);
181     method.append("replace");
182 
183     auto response = _bus.call(method);
184     if (response.is_method_error())
185     {
186         log<level::ERR>("Failed to start fan control ready target");
187         elog<InternalFailure>();
188     }
189 }
190 
191 } // namespace control
192 } // namespace fan
193 } // namespace phosphor
194