blob: 2d2d53b712b9363fc4c851f6bcb4576cf0a3f387 [file] [log] [blame]
Patrick Venture391b8b02018-03-08 08:31:13 -08001/**
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
30enum ManualSubCmd
31{
32 GET_CONTROL_STATE = 0,
33 SET_CONTROL_STATE = 1,
34 GET_FAILSAFE_STATE = 2,
35};
36
37struct FanCtrlRequest {
38 uint8_t command;
39 uint8_t zone;
40} __attribute__((packed));
41
42struct FanCtrlRequestSet {
43 uint8_t command;
44 uint8_t zone;
45 uint8_t value;
46} __attribute__((packed));
47
48static constexpr auto objectPath = "/xyz/openbmc_project/settings/fanctrl/zone";
49static constexpr auto busName = "xyz.openbmc_project.State.FanCtrl";
50static constexpr auto intf = "xyz.openbmc_project.Control.FanCtrl.Mode";
51static constexpr auto manualProperty = "Manual";
52static constexpr auto failsafeProperty = "FailSafe";
53static constexpr auto propertiesintf = "org.freedesktop.DBus.Properties";
54
55using Property = std::string;
56using Value = sdbusplus::message::variant<bool>;
57using PropertyMap = std::map<Property, Value>;
58
59/* The following was copied directly from my manual thread handler. */
60static 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.FanCtrl.Mode
72 * a{sv} 2 "Manual" b false "FailSafe" b false
73 */
74
75static ipmi_ret_t
76GetFanCtrlProperty(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
106static ipmi_ret_t
107GetFailsafeModeState(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 */
137static ipmi_ret_t
138GetManualModeState(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 */
169static ipmi_ret_t
170SetManualModeState(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 */
209static ipmi_ret_t
210ManualModeControl(
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
241void setupGlobalOemFanControl() __attribute__((constructor));
242
243void setupGlobalOemFanControl()
244{
245 ipmid::OemRouter* oemRouter = ipmid::mutableOemRouter();
246
247 fprintf(stderr,
248 "Registering OEM:[%#08X], Cmd:[%#04X] for Manual Zone Control\n",
249 ipmid::oem::openbmc::obmcOemNumber,
250 ipmid::oem::openbmc::OemCmd::fanManualCmd);
251
252 oemRouter->registerHandler(
253 ipmid::oem::openbmc::obmcOemNumber,
254 ipmid::oem::openbmc::OemCmd::fanManualCmd,
255 ManualModeControl);
256}