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 auto valueResponseMsg = propertyReadBus.call(pimMsg); 90 91 PropertyMap propMap; 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