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 try 88 { 89 PropertyMap propMap; 90 91 /* a method could error but the call not error. */ 92 auto valueResponseMsg = propertyReadBus.call(pimMsg); 93 if (valueResponseMsg.is_method_error()) 94 { 95 return IPMI_CC_INVALID; 96 } 97 98 valueResponseMsg.read(propMap); 99 100 *value = sdbusplus::message::variant_ns::get<bool>(propMap[property]); 101 } 102 catch (const sdbusplus::exception::SdBusError& ex) 103 { 104 return IPMI_CC_INVALID; 105 } 106 107 return IPMI_CC_OK; 108 } 109 110 static ipmi_ret_t GetFailsafeModeState(const uint8_t* reqBuf, uint8_t* replyBuf, 111 size_t* dataLen) 112 { 113 ipmi_ret_t rc = IPMI_CC_OK; 114 bool current; 115 116 if (*dataLen < sizeof(struct FanCtrlRequest)) 117 { 118 return IPMI_CC_INVALID; 119 } 120 121 const auto request = 122 reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]); 123 124 rc = GetFanCtrlProperty(request->zone, ¤t, failsafeProperty); 125 if (rc) 126 { 127 return rc; 128 } 129 130 *replyBuf = (uint8_t)current; 131 *dataLen = sizeof(uint8_t); 132 return rc; 133 } 134 135 /* 136 * <method name="GetAll"> 137 * <arg name="interface" direction="in" type="s"/> 138 * <arg name="properties" direction="out" type="a{sv}"/> 139 * </method> 140 */ 141 static ipmi_ret_t GetManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf, 142 size_t* dataLen) 143 { 144 ipmi_ret_t rc = IPMI_CC_OK; 145 bool current; 146 147 if (*dataLen < sizeof(struct FanCtrlRequest)) 148 { 149 return IPMI_CC_INVALID; 150 } 151 152 const auto request = 153 reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]); 154 155 rc = GetFanCtrlProperty(request->zone, ¤t, manualProperty); 156 if (rc) 157 { 158 return rc; 159 } 160 161 *replyBuf = (uint8_t)current; 162 *dataLen = sizeof(uint8_t); 163 return rc; 164 } 165 166 /* 167 * <method name="Set"> 168 * <arg name="interface" direction="in" type="s"/> 169 * <arg name="property" direction="in" type="s"/> 170 * <arg name="value" direction="in" type="v"/> 171 * </method> 172 */ 173 static ipmi_ret_t SetManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf, 174 size_t* dataLen) 175 { 176 ipmi_ret_t rc = IPMI_CC_OK; 177 if (*dataLen < sizeof(struct FanCtrlRequestSet)) 178 { 179 return IPMI_CC_INVALID; 180 } 181 182 using Value = sdbusplus::message::variant<bool>; 183 184 const auto request = 185 reinterpret_cast<const struct FanCtrlRequestSet*>(&reqBuf[0]); 186 187 /* 0 is false, 1 is true */ 188 bool setValue = static_cast<bool>(request->value); 189 Value v{setValue}; 190 191 auto PropertyWriteBus = sdbusplus::bus::new_default(); 192 193 std::string path = GetControlPath(request->zone); 194 195 auto pimMsg = PropertyWriteBus.new_method_call(busName, path.c_str(), 196 propertiesintf, "Set"); 197 pimMsg.append(intf); 198 pimMsg.append(manualProperty); 199 pimMsg.append(v); 200 201 try 202 { 203 PropertyWriteBus.call_noreply(pimMsg); 204 } 205 catch (const sdbusplus::exception::SdBusError& ex) 206 { 207 rc = IPMI_CC_INVALID; 208 } 209 /* TODO(venture): Should sanity check the result. */ 210 211 return rc; 212 } 213 214 /* Three command packages: get, set true, set false */ 215 static ipmi_ret_t ManualModeControl(ipmi_cmd_t cmd, const uint8_t* reqBuf, 216 uint8_t* replyCmdBuf, size_t* dataLen) 217 { 218 ipmi_ret_t rc = IPMI_CC_OK; 219 // FanCtrlRequest is the smaller of the requests, so it's at a minimum. 220 if (*dataLen < sizeof(struct FanCtrlRequest)) 221 { 222 return IPMI_CC_INVALID; 223 } 224 225 const auto request = 226 reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]); 227 228 switch (request->command) 229 { 230 case GET_CONTROL_STATE: 231 return GetManualModeState(reqBuf, replyCmdBuf, dataLen); 232 case SET_CONTROL_STATE: 233 return SetManualModeState(reqBuf, replyCmdBuf, dataLen); 234 case GET_FAILSAFE_STATE: 235 return GetFailsafeModeState(reqBuf, replyCmdBuf, dataLen); 236 default: 237 rc = IPMI_CC_INVALID; 238 } 239 240 return rc; 241 } 242 243 void setupGlobalOemFanControl() __attribute__((constructor)); 244 245 void setupGlobalOemFanControl() 246 { 247 oem::Router* router = oem::mutableRouter(); 248 249 fprintf(stderr, 250 "Registering OEM:[%#08X], Cmd:[%#04X] for Manual Zone Control\n", 251 oem::obmcOemNumber, oem::Cmd::fanManualCmd); 252 253 router->registerHandler(oem::obmcOemNumber, oem::Cmd::fanManualCmd, 254 ManualModeControl); 255 } 256