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 "host-ipmid/oemopenbmc.hpp" 20 #include "host-ipmid/oemrouter.hpp" 21 22 #include <map> 23 #include <sdbusplus/bus.hpp> 24 #include <sdbusplus/message.hpp> 25 #include <string> 26 #include <tuple> 27 28 #include "host-ipmid/ipmid-api.h" 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 { 39 uint8_t command; 40 uint8_t zone; 41 } __attribute__((packed)); 42 43 struct FanCtrlRequestSet 44 { 45 uint8_t command; 46 uint8_t zone; 47 uint8_t value; 48 } __attribute__((packed)); 49 50 static constexpr auto objectPath = "/xyz/openbmc_project/settings/fanctrl/zone"; 51 static constexpr auto busName = "xyz.openbmc_project.State.FanCtrl"; 52 static constexpr auto intf = "xyz.openbmc_project.Control.Mode"; 53 static constexpr auto manualProperty = "Manual"; 54 static constexpr auto failsafeProperty = "FailSafe"; 55 static constexpr auto propertiesintf = "org.freedesktop.DBus.Properties"; 56 57 using Property = std::string; 58 using Value = sdbusplus::message::variant<bool>; 59 using PropertyMap = std::map<Property, Value>; 60 61 /* The following was copied directly from my manual thread handler. */ 62 static std::string GetControlPath(int8_t zone) 63 { 64 return std::string(objectPath) + std::to_string(zone); 65 } 66 67 /* 68 * busctl call xyz.openbmc_project.State.FanCtrl \ 69 * /xyz/openbmc_project/settings/fanctrl/zone1 \ 70 * org.freedesktop.DBus.Properties \ 71 * GetAll \ 72 * s \ 73 * xyz.openbmc_project.Control.Mode 74 * a{sv} 2 "Manual" b false "FailSafe" b false 75 */ 76 77 static ipmi_ret_t GetFanCtrlProperty(uint8_t zoneId, bool* value, 78 const std::string& property) 79 { 80 std::string path = GetControlPath(zoneId); 81 82 auto propertyReadBus = sdbusplus::bus::new_default(); 83 auto pimMsg = propertyReadBus.new_method_call(busName, path.c_str(), 84 propertiesintf, "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 GetFailsafeModeState(const uint8_t* reqBuf, uint8_t* replyBuf, 107 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, ¤t, 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 GetManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf, 138 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, ¤t, 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 SetManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf, 170 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, path.c_str(), 192 propertiesintf, "Set"); 193 pimMsg.append(intf); 194 pimMsg.append(manualProperty); 195 pimMsg.append(v); 196 auto responseMsg = PropertyWriteBus.call(pimMsg); 197 if (responseMsg.is_method_error()) 198 { 199 rc = IPMI_CC_INVALID; 200 } 201 /* TODO(venture): Should sanity check the result. */ 202 203 return rc; 204 } 205 206 /* Three command packages: get, set true, set false */ 207 static ipmi_ret_t ManualModeControl(ipmi_cmd_t cmd, const uint8_t* reqBuf, 208 uint8_t* replyCmdBuf, size_t* dataLen) 209 { 210 ipmi_ret_t rc = IPMI_CC_OK; 211 // FanCtrlRequest is the smaller of the requests, so it's at a minimum. 212 if (*dataLen < sizeof(struct FanCtrlRequest)) 213 { 214 return IPMI_CC_INVALID; 215 } 216 217 const auto request = 218 reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]); 219 220 switch (request->command) 221 { 222 case GET_CONTROL_STATE: 223 return GetManualModeState(reqBuf, replyCmdBuf, dataLen); 224 case SET_CONTROL_STATE: 225 return SetManualModeState(reqBuf, replyCmdBuf, dataLen); 226 case GET_FAILSAFE_STATE: 227 return GetFailsafeModeState(reqBuf, replyCmdBuf, dataLen); 228 default: 229 rc = IPMI_CC_INVALID; 230 } 231 232 return rc; 233 } 234 235 void setupGlobalOemFanControl() __attribute__((constructor)); 236 237 void setupGlobalOemFanControl() 238 { 239 oem::Router* router = oem::mutableRouter(); 240 241 fprintf(stderr, 242 "Registering OEM:[%#08X], Cmd:[%#04X] for Manual Zone Control\n", 243 oem::obmcOemNumber, oem::Cmd::fanManualCmd); 244 245 router->registerHandler(oem::obmcOemNumber, oem::Cmd::fanManualCmd, 246 ManualModeControl); 247 } 248