xref: /openbmc/phosphor-pid-control/ipmi/manualcmds.cpp (revision 7af157b10ef4c1a0d09be0e310825909ca630cd0)
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 <host-ipmid/ipmid-api.h>
18  
19  #include <host-ipmid/iana.hpp>
20  #include <host-ipmid/oemopenbmc.hpp>
21  #include <host-ipmid/oemrouter.hpp>
22  #include <map>
23  #include <sdbusplus/bus.hpp>
24  #include <sdbusplus/message.hpp>
25  #include <string>
26  #include <tuple>
27  
28  enum ManualSubCmd
29  {
30      GET_CONTROL_STATE = 0,
31      SET_CONTROL_STATE = 1,
32      GET_FAILSAFE_STATE = 2,
33  };
34  
35  struct FanCtrlRequest
36  {
37      uint8_t command;
38      uint8_t zone;
39  } __attribute__((packed));
40  
41  struct FanCtrlRequestSet
42  {
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 getFanCtrlProperty(uint8_t zoneId, bool* value,
76                                       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, path.c_str(),
82                                                    propertiesintf, "GetAll");
83      pimMsg.append(intf);
84  
85      try
86      {
87          PropertyMap propMap;
88  
89          /* a method could error but the call not error. */
90          auto valueResponseMsg = propertyReadBus.call(pimMsg);
91  
92          valueResponseMsg.read(propMap);
93  
94          *value = sdbusplus::message::variant_ns::get<bool>(propMap[property]);
95      }
96      catch (const sdbusplus::exception::SdBusError& ex)
97      {
98          return IPMI_CC_INVALID;
99      }
100  
101      return IPMI_CC_OK;
102  }
103  
104  static ipmi_ret_t getFailsafeModeState(const uint8_t* reqBuf, uint8_t* replyBuf,
105                                         size_t* dataLen)
106  {
107      ipmi_ret_t rc = IPMI_CC_OK;
108      bool current;
109  
110      if (*dataLen < sizeof(struct FanCtrlRequest))
111      {
112          return IPMI_CC_INVALID;
113      }
114  
115      const auto request =
116          reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]);
117  
118      rc = getFanCtrlProperty(request->zone, &current, failsafeProperty);
119      if (rc)
120      {
121          return rc;
122      }
123  
124      *replyBuf = (uint8_t)current;
125      *dataLen = sizeof(uint8_t);
126      return rc;
127  }
128  
129  /*
130   * <method name="GetAll">
131   *   <arg name="interface" direction="in" type="s"/>
132   *   <arg name="properties" direction="out" type="a{sv}"/>
133   * </method>
134   */
135  static ipmi_ret_t getManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf,
136                                       size_t* dataLen)
137  {
138      ipmi_ret_t rc = IPMI_CC_OK;
139      bool current;
140  
141      if (*dataLen < sizeof(struct FanCtrlRequest))
142      {
143          return IPMI_CC_INVALID;
144      }
145  
146      const auto request =
147          reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]);
148  
149      rc = getFanCtrlProperty(request->zone, &current, manualProperty);
150      if (rc)
151      {
152          return rc;
153      }
154  
155      *replyBuf = (uint8_t)current;
156      *dataLen = sizeof(uint8_t);
157      return rc;
158  }
159  
160  /*
161   * <method name="Set">
162   *   <arg name="interface" direction="in" type="s"/>
163   *   <arg name="property" direction="in" type="s"/>
164   *   <arg name="value" direction="in" type="v"/>
165   * </method>
166   */
167  static ipmi_ret_t setManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf,
168                                       size_t* dataLen)
169  {
170      ipmi_ret_t rc = IPMI_CC_OK;
171      if (*dataLen < sizeof(struct FanCtrlRequestSet))
172      {
173          return IPMI_CC_INVALID;
174      }
175  
176      using Value = sdbusplus::message::variant<bool>;
177  
178      const auto request =
179          reinterpret_cast<const struct FanCtrlRequestSet*>(&reqBuf[0]);
180  
181      /* 0 is false, 1 is true */
182      bool setValue = static_cast<bool>(request->value);
183      Value v{setValue};
184  
185      auto PropertyWriteBus = sdbusplus::bus::new_default();
186  
187      std::string path = getControlPath(request->zone);
188  
189      auto pimMsg = PropertyWriteBus.new_method_call(busName, path.c_str(),
190                                                     propertiesintf, "Set");
191      pimMsg.append(intf);
192      pimMsg.append(manualProperty);
193      pimMsg.append(v);
194  
195      try
196      {
197          PropertyWriteBus.call_noreply(pimMsg);
198      }
199      catch (const sdbusplus::exception::SdBusError& ex)
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 manualModeControl(ipmi_cmd_t cmd, const uint8_t* reqBuf,
210                                      uint8_t* replyCmdBuf, size_t* dataLen)
211  {
212      ipmi_ret_t rc = IPMI_CC_OK;
213      // FanCtrlRequest is the smaller of the requests, so it's at a minimum.
214      if (*dataLen < sizeof(struct FanCtrlRequest))
215      {
216          return IPMI_CC_INVALID;
217      }
218  
219      const auto request =
220          reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]);
221  
222      switch (request->command)
223      {
224          case GET_CONTROL_STATE:
225              return getManualModeState(reqBuf, replyCmdBuf, dataLen);
226          case SET_CONTROL_STATE:
227              return setManualModeState(reqBuf, replyCmdBuf, dataLen);
228          case GET_FAILSAFE_STATE:
229              return getFailsafeModeState(reqBuf, replyCmdBuf, dataLen);
230          default:
231              rc = IPMI_CC_INVALID;
232      }
233  
234      return rc;
235  }
236  
237  void setupGlobalOemFanControl() __attribute__((constructor));
238  
239  void setupGlobalOemFanControl()
240  {
241      oem::Router* router = oem::mutableRouter();
242  
243      fprintf(stderr,
244              "Registering OEM:[%#08X], Cmd:[%#04X] for Manual Zone Control\n",
245              oem::obmcOemNumber, oem::Cmd::fanManualCmd);
246  
247      router->registerHandler(oem::obmcOemNumber, oem::Cmd::fanManualCmd,
248                              manualModeControl);
249  }
250