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 <ipmid/api.h> 18 19 #include <ipmid/iana.hpp> 20 #include <ipmid/oemopenbmc.hpp> 21 #include <ipmid/oemrouter.hpp> 22 #include <map> 23 #include <sdbusplus/bus.hpp> 24 #include <sdbusplus/message.hpp> 25 #include <string> 26 #include <tuple> 27 #include <variant> 28 29 enum ManualSubCmd 30 { 31 GET_CONTROL_STATE = 0, 32 SET_CONTROL_STATE = 1, 33 GET_FAILSAFE_STATE = 2, 34 }; 35 36 struct FanCtrlRequest 37 { 38 uint8_t command; 39 uint8_t zone; 40 } __attribute__((packed)); 41 42 struct FanCtrlRequestSet 43 { 44 uint8_t command; 45 uint8_t zone; 46 uint8_t value; 47 } __attribute__((packed)); 48 49 static constexpr auto objectPath = "/xyz/openbmc_project/settings/fanctrl/zone"; 50 static constexpr auto busName = "xyz.openbmc_project.State.FanCtrl"; 51 static constexpr auto intf = "xyz.openbmc_project.Control.Mode"; 52 static constexpr auto manualProperty = "Manual"; 53 static constexpr auto failsafeProperty = "FailSafe"; 54 static constexpr auto propertiesintf = "org.freedesktop.DBus.Properties"; 55 56 using Property = std::string; 57 using Value = std::variant<bool>; 58 using PropertyMap = std::map<Property, Value>; 59 60 /* The following was copied directly from my manual thread handler. */ 61 static std::string getControlPath(int8_t zone) 62 { 63 return std::string(objectPath) + std::to_string(zone); 64 } 65 66 /* 67 * busctl call xyz.openbmc_project.State.FanCtrl \ 68 * /xyz/openbmc_project/settings/fanctrl/zone1 \ 69 * org.freedesktop.DBus.Properties \ 70 * GetAll \ 71 * s \ 72 * xyz.openbmc_project.Control.Mode 73 * a{sv} 2 "Manual" b false "FailSafe" b false 74 */ 75 76 static ipmi_ret_t getFanCtrlProperty(uint8_t zoneId, bool* value, 77 const std::string& property) 78 { 79 std::string path = getControlPath(zoneId); 80 81 auto propertyReadBus = sdbusplus::bus::new_system(); 82 auto pimMsg = propertyReadBus.new_method_call(busName, path.c_str(), 83 propertiesintf, "GetAll"); 84 pimMsg.append(intf); 85 86 try 87 { 88 PropertyMap propMap; 89 90 /* a method could error but the call not error. */ 91 auto valueResponseMsg = propertyReadBus.call(pimMsg); 92 93 valueResponseMsg.read(propMap); 94 95 *value = std::get<bool>(propMap[property]); 96 } 97 catch (const sdbusplus::exception::SdBusError& ex) 98 { 99 return IPMI_CC_INVALID; 100 } 101 102 return IPMI_CC_OK; 103 } 104 105 static ipmi_ret_t getFailsafeModeState(const uint8_t* reqBuf, uint8_t* replyBuf, 106 size_t* dataLen) 107 { 108 ipmi_ret_t rc = IPMI_CC_OK; 109 bool current; 110 111 if (*dataLen < sizeof(struct FanCtrlRequest)) 112 { 113 return IPMI_CC_INVALID; 114 } 115 116 const auto request = 117 reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]); 118 119 rc = getFanCtrlProperty(request->zone, ¤t, failsafeProperty); 120 if (rc) 121 { 122 return rc; 123 } 124 125 *replyBuf = (uint8_t)current; 126 *dataLen = sizeof(uint8_t); 127 return rc; 128 } 129 130 /* 131 * <method name="GetAll"> 132 * <arg name="interface" direction="in" type="s"/> 133 * <arg name="properties" direction="out" type="a{sv}"/> 134 * </method> 135 */ 136 static ipmi_ret_t getManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf, 137 size_t* dataLen) 138 { 139 ipmi_ret_t rc = IPMI_CC_OK; 140 bool current; 141 142 if (*dataLen < sizeof(struct FanCtrlRequest)) 143 { 144 return IPMI_CC_INVALID; 145 } 146 147 const auto request = 148 reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]); 149 150 rc = getFanCtrlProperty(request->zone, ¤t, manualProperty); 151 if (rc) 152 { 153 return rc; 154 } 155 156 *replyBuf = (uint8_t)current; 157 *dataLen = sizeof(uint8_t); 158 return rc; 159 } 160 161 /* 162 * <method name="Set"> 163 * <arg name="interface" direction="in" type="s"/> 164 * <arg name="property" direction="in" type="s"/> 165 * <arg name="value" direction="in" type="v"/> 166 * </method> 167 */ 168 static ipmi_ret_t setManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf, 169 size_t* dataLen) 170 { 171 ipmi_ret_t rc = IPMI_CC_OK; 172 if (*dataLen < sizeof(struct FanCtrlRequestSet)) 173 { 174 return IPMI_CC_INVALID; 175 } 176 177 using Value = std::variant<bool>; 178 179 const auto request = 180 reinterpret_cast<const struct FanCtrlRequestSet*>(&reqBuf[0]); 181 182 /* 0 is false, 1 is true */ 183 bool setValue = static_cast<bool>(request->value); 184 Value v{setValue}; 185 186 auto PropertyWriteBus = sdbusplus::bus::new_system(); 187 188 std::string path = getControlPath(request->zone); 189 190 auto pimMsg = PropertyWriteBus.new_method_call(busName, path.c_str(), 191 propertiesintf, "Set"); 192 pimMsg.append(intf); 193 pimMsg.append(manualProperty); 194 pimMsg.append(v); 195 196 try 197 { 198 PropertyWriteBus.call_noreply(pimMsg); 199 } 200 catch (const sdbusplus::exception::SdBusError& ex) 201 { 202 rc = IPMI_CC_INVALID; 203 } 204 /* TODO(venture): Should sanity check the result. */ 205 206 return rc; 207 } 208 209 /* Three command packages: get, set true, set false */ 210 static ipmi_ret_t manualModeControl(ipmi_cmd_t cmd, const uint8_t* reqBuf, 211 uint8_t* replyCmdBuf, size_t* dataLen) 212 { 213 ipmi_ret_t rc = IPMI_CC_OK; 214 // FanCtrlRequest is the smaller of the requests, so it's at a minimum. 215 if (*dataLen < sizeof(struct FanCtrlRequest)) 216 { 217 return IPMI_CC_INVALID; 218 } 219 220 const auto request = 221 reinterpret_cast<const struct FanCtrlRequest*>(&reqBuf[0]); 222 223 switch (request->command) 224 { 225 case GET_CONTROL_STATE: 226 return getManualModeState(reqBuf, replyCmdBuf, dataLen); 227 case SET_CONTROL_STATE: 228 return setManualModeState(reqBuf, replyCmdBuf, dataLen); 229 case GET_FAILSAFE_STATE: 230 return getFailsafeModeState(reqBuf, replyCmdBuf, dataLen); 231 default: 232 rc = IPMI_CC_INVALID; 233 } 234 235 return rc; 236 } 237 238 void setupGlobalOemFanControl() __attribute__((constructor)); 239 240 void setupGlobalOemFanControl() 241 { 242 oem::Router* router = oem::mutableRouter(); 243 244 fprintf(stderr, 245 "Registering OEM:[%#08X], Cmd:[%#04X] for Manual Zone Control\n", 246 oem::obmcOemNumber, oem::Cmd::fanManualCmd); 247 248 router->registerHandler(oem::obmcOemNumber, oem::Cmd::fanManualCmd, 249 manualModeControl); 250 } 251