xref: /openbmc/phosphor-pid-control/ipmi/manualcmds.cpp (revision 40be36ac31a4756498a1f297324129fe1271b726)
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 <stdint.h>
18  
19  #include <map>
20  #include <string>
21  #include <tuple>
22  
23  #include <sdbusplus/bus.hpp>
24  #include <sdbusplus/message.hpp>
25  
26  #include "host-ipmid/ipmid-api.h"
27  #include "host-ipmid/oemopenbmc.hpp"
28  #include "host-ipmid/oemrouter.hpp"
29  
30  enum ManualSubCmd
31  {
32      GET_CONTROL_STATE = 0,
33      SET_CONTROL_STATE = 1,
34      GET_FAILSAFE_STATE = 2,
35  };
36  
37  struct FanCtrlRequest {
38      uint8_t command;
39      uint8_t zone;
40  } __attribute__((packed));
41  
42  struct FanCtrlRequestSet {
43      uint8_t command;
44      uint8_t zone;
45      uint8_t value;
46  } __attribute__((packed));
47  
48  static constexpr auto objectPath = "/xyz/openbmc_project/settings/fanctrl/zone";
49  static constexpr auto busName = "xyz.openbmc_project.State.FanCtrl";
50  static constexpr auto intf = "xyz.openbmc_project.Control.Mode";
51  static constexpr auto manualProperty = "Manual";
52  static constexpr auto failsafeProperty = "FailSafe";
53  static constexpr auto propertiesintf = "org.freedesktop.DBus.Properties";
54  
55  using Property = std::string;
56  using Value = sdbusplus::message::variant<bool>;
57  using PropertyMap = std::map<Property, Value>;
58  
59  /* The following was copied directly from my manual thread handler. */
60  static std::string GetControlPath(int8_t zone)
61  {
62      return std::string(objectPath) + std::to_string(zone);
63  }
64  
65  /*
66   * busctl call xyz.openbmc_project.State.FanCtrl \
67   *     /xyz/openbmc_project/settings/fanctrl/zone1 \
68   *     org.freedesktop.DBus.Properties \
69   *     GetAll \
70   *     s \
71   *     xyz.openbmc_project.Control.Mode
72   * a{sv} 2 "Manual" b false "FailSafe" b false
73   */
74  
75  static ipmi_ret_t
76  GetFanCtrlProperty(uint8_t zoneId, bool *value, const std::string &property)
77  {
78      std::string path = GetControlPath(zoneId);
79  
80      auto propertyReadBus = sdbusplus::bus::new_default();
81      auto pimMsg = propertyReadBus.new_method_call(busName,
82                    path.c_str(),
83                    propertiesintf,
84                    "GetAll");
85      pimMsg.append(intf);
86  
87      auto valueResponseMsg = propertyReadBus.call(pimMsg);
88      if (valueResponseMsg.is_method_error())
89      {
90          return IPMI_CC_INVALID;
91      }
92  
93      PropertyMap propMap;
94      valueResponseMsg.read(propMap);
95  
96      if (propMap.size() != 2)
97      {
98          return IPMI_CC_INVALID;
99      }
100  
101      *value = sdbusplus::message::variant_ns::get<bool>(propMap[property]);
102  
103      return IPMI_CC_OK;
104  }
105  
106  static ipmi_ret_t
107  GetFailsafeModeState(const uint8_t* reqBuf, uint8_t* replyBuf, size_t* dataLen)
108  {
109      ipmi_ret_t rc = IPMI_CC_OK;
110      bool current;
111  
112      if (*dataLen < sizeof(struct FanCtrlRequest))
113      {
114          return IPMI_CC_INVALID;
115      }
116  
117      const auto request =
118              reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]);
119  
120      rc = GetFanCtrlProperty(request->zone, &current, failsafeProperty);
121      if (rc)
122      {
123          return rc;
124      }
125  
126      *replyBuf = (uint8_t)current;
127      *dataLen = sizeof(uint8_t);
128      return rc;
129  }
130  
131  /*
132   * <method name="GetAll">
133   *   <arg name="interface" direction="in" type="s"/>
134   *   <arg name="properties" direction="out" type="a{sv}"/>
135   * </method>
136   */
137  static ipmi_ret_t
138  GetManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf, size_t* dataLen)
139  {
140      ipmi_ret_t rc = IPMI_CC_OK;
141      bool current;
142  
143      if (*dataLen < sizeof(struct FanCtrlRequest))
144      {
145          return IPMI_CC_INVALID;
146      }
147  
148      const auto request =
149              reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]);
150  
151      rc = GetFanCtrlProperty(request->zone, &current, manualProperty);
152      if (rc)
153      {
154          return rc;
155      }
156  
157      *replyBuf = (uint8_t)current;
158      *dataLen = sizeof(uint8_t);
159      return rc;
160  }
161  
162  /*
163   * <method name="Set">
164   *   <arg name="interface" direction="in" type="s"/>
165   *   <arg name="property" direction="in" type="s"/>
166   *   <arg name="value" direction="in" type="v"/>
167   * </method>
168   */
169  static ipmi_ret_t
170  SetManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf, size_t* dataLen)
171  {
172      ipmi_ret_t rc = IPMI_CC_OK;
173      if (*dataLen < sizeof(struct FanCtrlRequestSet))
174      {
175          return IPMI_CC_INVALID;
176      }
177  
178      using Value = sdbusplus::message::variant<bool>;
179  
180      const auto request =
181              reinterpret_cast<const struct FanCtrlRequestSet*>(&reqBuf[0]);
182  
183      /* 0 is false, 1 is true */
184      bool setValue = static_cast<bool>(request->value);
185      Value v {setValue};
186  
187      auto PropertyWriteBus = sdbusplus::bus::new_default();
188  
189      std::string path = GetControlPath(request->zone);
190  
191      auto pimMsg = PropertyWriteBus.new_method_call(busName,
192                    path.c_str(),
193                    propertiesintf,
194                    "Set");
195      pimMsg.append(intf);
196      pimMsg.append(manualProperty);
197      pimMsg.append(v);
198      auto responseMsg = PropertyWriteBus.call(pimMsg);
199      if (responseMsg.is_method_error())
200      {
201          rc = IPMI_CC_INVALID;
202      }
203      /* TODO(venture): Should sanity check the result. */
204  
205      return rc;
206  }
207  
208  /* Three command packages: get, set true, set false */
209  static ipmi_ret_t
210  ManualModeControl(
211      ipmi_cmd_t cmd,
212      const uint8_t* reqBuf,
213      uint8_t* replyCmdBuf,
214      size_t* dataLen)
215  {
216      ipmi_ret_t rc = IPMI_CC_OK;
217      // FanCtrlRequest is the smaller of the requests, so it's at a minimum.
218      if (*dataLen < sizeof(struct FanCtrlRequest))
219      {
220          return IPMI_CC_INVALID;
221      }
222  
223      const auto request =
224              reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]);
225  
226      switch (request->command)
227      {
228          case GET_CONTROL_STATE:
229              return GetManualModeState(reqBuf, replyCmdBuf, dataLen);
230          case SET_CONTROL_STATE:
231              return SetManualModeState(reqBuf, replyCmdBuf, dataLen);
232          case GET_FAILSAFE_STATE:
233              return GetFailsafeModeState(reqBuf, replyCmdBuf, dataLen);
234          default:
235              rc = IPMI_CC_INVALID;
236      }
237  
238      return rc;
239  }
240  
241  void setupGlobalOemFanControl() __attribute__((constructor));
242  
243  void setupGlobalOemFanControl()
244  {
245      oem::Router* router = oem::mutableRouter();
246  
247      fprintf(stderr,
248              "Registering OEM:[%#08X], Cmd:[%#04X] for Manual Zone Control\n",
249              oem::obmcOemNumber,
250              oem::Cmd::fanManualCmd);
251  
252      router->registerHandler(
253          oem::obmcOemNumber,
254          oem::Cmd::fanManualCmd,
255          ManualModeControl);
256  }
257