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