/** * Copyright 2017 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "manualcmds.hpp" #include "manual_messages.hpp" #include #include #include #include #include #include #include namespace pid_control { namespace ipmi { static constexpr auto objectPath = "/xyz/openbmc_project/settings/fanctrl/zone"; static constexpr auto busName = "xyz.openbmc_project.State.FanCtrl"; static constexpr auto intf = "xyz.openbmc_project.Control.Mode"; static constexpr auto manualProperty = "Manual"; static constexpr auto failsafeProperty = "FailSafe"; static constexpr auto propertiesintf = "org.freedesktop.DBus.Properties"; using Property = std::string; using Value = std::variant; using PropertyMap = std::map; /* The following was copied directly from my manual thread handler. */ static std::string getControlPath(int8_t zone) { return std::string(objectPath) + std::to_string(zone); } /* * busctl call xyz.openbmc_project.State.FanCtrl \ * /xyz/openbmc_project/settings/fanctrl/zone1 \ * org.freedesktop.DBus.Properties \ * GetAll \ * s \ * xyz.openbmc_project.Control.Mode * a{sv} 2 "Manual" b false "FailSafe" b false */ static ipmi_ret_t getFanCtrlProperty(uint8_t zoneId, bool* value, const std::string& property) { std::string path = getControlPath(zoneId); auto propertyReadBus = sdbusplus::bus::new_system(); auto pimMsg = propertyReadBus.new_method_call(busName, path.c_str(), propertiesintf, "GetAll"); pimMsg.append(intf); try { PropertyMap propMap; /* a method could error but the call not error. */ auto valueResponseMsg = propertyReadBus.call(pimMsg); valueResponseMsg.read(propMap); *value = std::get(propMap[property]); } catch (const sdbusplus::exception::SdBusError& ex) { return IPMI_CC_INVALID; } return IPMI_CC_OK; } static ipmi_ret_t getFailsafeModeState(const uint8_t* reqBuf, uint8_t* replyBuf, size_t* dataLen) { bool current; if (*dataLen < sizeof(struct FanCtrlRequest)) { return IPMI_CC_INVALID; } const auto request = reinterpret_cast(&reqBuf[0]); ipmi_ret_t rc = getFanCtrlProperty(request->zone, ¤t, failsafeProperty); if (rc) { return rc; } *replyBuf = (uint8_t)current; *dataLen = sizeof(uint8_t); return IPMI_CC_OK; } /* * * * * */ static ipmi_ret_t getManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf, size_t* dataLen) { bool current; if (*dataLen < sizeof(struct FanCtrlRequest)) { return IPMI_CC_INVALID; } const auto request = reinterpret_cast(&reqBuf[0]); ipmi_ret_t rc = getFanCtrlProperty(request->zone, ¤t, manualProperty); if (rc) { return rc; } *replyBuf = (uint8_t)current; *dataLen = sizeof(uint8_t); return IPMI_CC_OK; } /* * * * * * */ static ipmi_ret_t setManualModeState(const uint8_t* reqBuf, uint8_t* replyBuf, size_t* dataLen) { if (*dataLen < sizeof(struct FanCtrlRequestSet)) { return IPMI_CC_INVALID; } using Value = std::variant; const auto request = reinterpret_cast(&reqBuf[0]); /* 0 is false, 1 is true */ bool setValue = static_cast(request->value); Value v{setValue}; auto PropertyWriteBus = sdbusplus::bus::new_system(); std::string path = getControlPath(request->zone); auto pimMsg = PropertyWriteBus.new_method_call(busName, path.c_str(), propertiesintf, "Set"); pimMsg.append(intf); pimMsg.append(manualProperty); pimMsg.append(v); ipmi_ret_t rc = IPMI_CC_OK; try { PropertyWriteBus.call_noreply(pimMsg); } catch (const sdbusplus::exception::SdBusError& ex) { rc = IPMI_CC_INVALID; } /* TODO(venture): Should sanity check the result. */ return rc; } /* Three command packages: get, set true, set false */ ipmi_ret_t manualModeControl(ipmi_cmd_t cmd, const uint8_t* reqBuf, uint8_t* replyCmdBuf, size_t* dataLen) { // FanCtrlRequest is the smaller of the requests, so it's at a minimum. if (*dataLen < sizeof(struct FanCtrlRequest)) { return IPMI_CC_INVALID; } const auto request = reinterpret_cast(&reqBuf[0]); ipmi_ret_t rc = IPMI_CC_OK; switch (request->command) { case getControlState: return getManualModeState(reqBuf, replyCmdBuf, dataLen); case setControlState: return setManualModeState(reqBuf, replyCmdBuf, dataLen); case getFailsafeState: return getFailsafeModeState(reqBuf, replyCmdBuf, dataLen); default: rc = IPMI_CC_INVALID; } return rc; } } // namespace ipmi } // namespace pid_control