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