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.FanCtrl.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.FanCtrl.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, ¤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 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, ¤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 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 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 } 257