1 /**
2  * Copyright 2017 Google Inc.
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 
17 #include "manualcmds.hpp"
18 
19 #include "dbus_mode.hpp"
20 #include "manual_messages.hpp"
21 
22 #include <ipmid/api.h>
23 
24 #include <sdbusplus/bus.hpp>
25 #include <sdbusplus/message.hpp>
26 
27 #include <map>
28 #include <string>
29 #include <tuple>
30 #include <variant>
31 
32 namespace pid_control
33 {
34 namespace ipmi
35 {
36 
37 static constexpr auto objectPath = "/xyz/openbmc_project/settings/fanctrl/zone";
38 static constexpr auto busName = "xyz.openbmc_project.State.FanCtrl";
39 static constexpr auto intf = "xyz.openbmc_project.Control.Mode";
40 static constexpr auto manualProperty = "Manual";
41 static constexpr auto failsafeProperty = "FailSafe";
42 static constexpr auto propertiesintf = "org.freedesktop.DBus.Properties";
43 
44 using Property = std::string;
45 using Value = std::variant<bool>;
46 using PropertyMap = std::map<Property, Value>;
47 
48 /* The following was copied directly from my manual thread handler. */
49 static std::string getControlPath(int8_t zone)
50 {
51     return std::string(objectPath) + std::to_string(zone);
52 }
53 
54 static ipmi_ret_t getFailsafeModeState(const uint8_t* reqBuf, uint8_t* replyBuf,
55                                        size_t* dataLen)
56 {
57     bool current;
58 
59     if (*dataLen < sizeof(struct FanCtrlRequest))
60     {
61         return IPMI_CC_INVALID;
62     }
63 
64     const auto request =
65         reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]);
66 
67     ipmi_ret_t rc =
68         getFanCtrlProperty(request->zone, &current, failsafeProperty);
69     if (rc)
70     {
71         return rc;
72     }
73 
74     *replyBuf = (uint8_t)current;
75     *dataLen = sizeof(uint8_t);
76     return IPMI_CC_OK;
77 }
78 
79 /*
80  * <method name="GetAll">
81  *   <arg name="interface" direction="in" type="s"/>
82  *   <arg name="properties" direction="out" type="a{sv}"/>
83  * </method>
84  */
85 static ipmi_ret_t getManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf,
86                                      size_t* dataLen)
87 {
88     bool current;
89 
90     if (*dataLen < sizeof(struct FanCtrlRequest))
91     {
92         return IPMI_CC_INVALID;
93     }
94 
95     const auto request =
96         reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]);
97 
98     ipmi_ret_t rc = getFanCtrlProperty(request->zone, &current, manualProperty);
99     if (rc)
100     {
101         return rc;
102     }
103 
104     *replyBuf = (uint8_t)current;
105     *dataLen = sizeof(uint8_t);
106     return IPMI_CC_OK;
107 }
108 
109 /*
110  * <method name="Set">
111  *   <arg name="interface" direction="in" type="s"/>
112  *   <arg name="property" direction="in" type="s"/>
113  *   <arg name="value" direction="in" type="v"/>
114  * </method>
115  */
116 static ipmi_ret_t setManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf,
117                                      size_t* dataLen)
118 {
119     if (*dataLen < sizeof(struct FanCtrlRequestSet))
120     {
121         return IPMI_CC_INVALID;
122     }
123 
124     using Value = std::variant<bool>;
125 
126     const auto request =
127         reinterpret_cast<const struct FanCtrlRequestSet*>(&reqBuf[0]);
128 
129     /* 0 is false, 1 is true */
130     bool setValue = static_cast<bool>(request->value);
131     Value v{setValue};
132 
133     auto PropertyWriteBus = sdbusplus::bus::new_system();
134 
135     std::string path = getControlPath(request->zone);
136 
137     auto pimMsg = PropertyWriteBus.new_method_call(busName, path.c_str(),
138                                                    propertiesintf, "Set");
139     pimMsg.append(intf);
140     pimMsg.append(manualProperty);
141     pimMsg.append(v);
142 
143     ipmi_ret_t rc = IPMI_CC_OK;
144 
145     try
146     {
147         PropertyWriteBus.call_noreply(pimMsg);
148     }
149     catch (const sdbusplus::exception::SdBusError& ex)
150     {
151         rc = IPMI_CC_INVALID;
152     }
153     /* TODO(venture): Should sanity check the result. */
154 
155     return rc;
156 }
157 
158 /* Three command packages: get, set true, set false */
159 ipmi_ret_t manualModeControl(ipmi_cmd_t cmd, const uint8_t* reqBuf,
160                              uint8_t* replyCmdBuf, size_t* dataLen)
161 {
162     // FanCtrlRequest is the smaller of the requests, so it's at a minimum.
163     if (*dataLen < sizeof(struct FanCtrlRequest))
164     {
165         return IPMI_CC_INVALID;
166     }
167 
168     const auto request =
169         reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]);
170 
171     ipmi_ret_t rc = IPMI_CC_OK;
172 
173     switch (request->command)
174     {
175         case getControlState:
176             return getManualModeState(reqBuf, replyCmdBuf, dataLen);
177         case setControlState:
178             return setManualModeState(reqBuf, replyCmdBuf, dataLen);
179         case getFailsafeState:
180             return getFailsafeModeState(reqBuf, replyCmdBuf, dataLen);
181         default:
182             rc = IPMI_CC_INVALID;
183     }
184 
185     return rc;
186 }
187 
188 } // namespace ipmi
189 } // namespace pid_control
190