Define the Manager for handling host bound commands
Since some of the host bound commands are valid only for
OpenPower systems, a manager is needed, which can handle
commands from OpenBmc common code and also OpenPower
implementations.
Change-Id: Icf6566e701921ecea4c8cff1c16e498385303396
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/host-cmd-manager.cpp b/host-cmd-manager.cpp
new file mode 100644
index 0000000..5b75b55
--- /dev/null
+++ b/host-cmd-manager.cpp
@@ -0,0 +1,147 @@
+#include <chrono>
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <systemintfcmds.h>
+#include <utils.hpp>
+#include <config.h>
+#include <host-cmd-manager.hpp>
+
+namespace phosphor
+{
+namespace host
+{
+namespace command
+{
+
+constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
+constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
+constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
+
+// For throwing exceptions
+using namespace phosphor::logging;
+using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
+ Error::InternalFailure;
+
+// Called as part of READ_MSG_DATA command
+IpmiCmdData Manager::getNextCommand()
+{
+ // Stop the timer. Don't have to Err failure doing so.
+ auto r = timer.setTimer(SD_EVENT_OFF);
+ if (r < 0)
+ {
+ log<level::ERR>("Failure to STOP the timer",
+ entry("ERROR=%s", strerror(-r)));
+ }
+
+ if(this->workQueue.empty())
+ {
+ // Just return a heartbeat in this case. A spurious SMS_ATN was
+ // asserted for the host (probably from a previous boot).
+ log<level::INFO>("Control Host work queue is empty!");
+
+ return std::make_pair(CMD_HEARTBEAT, 0x00);
+ }
+
+ // Pop the processed entry off the queue
+ auto command = this->workQueue.front();
+ this->workQueue.pop();
+
+ // IPMI command is the first element in pair
+ auto ipmiCmdData = std::get<0>(command);
+
+ // Now, call the user registered functions so that
+ // implementation specific CommandComplete signals
+ // can be sent. `true` indicating Success.
+ std::get<CallBack>(command)(ipmiCmdData, true);
+
+ // Check for another entry in the queue and kick it off
+ this->checkQueueAndAlertHost();
+
+ // Tuple of command and data
+ return ipmiCmdData;
+}
+
+// Called when initial timer goes off post sending SMS_ATN
+void Manager::hostTimeout()
+{
+ log<level::ERR>("Host control timeout hit!");
+
+ // Dequeue all entries and send fail signal
+ while(!this->workQueue.empty())
+ {
+ auto command = this->workQueue.front();
+ this->workQueue.pop();
+
+ // IPMI command is the first element in pair
+ auto ipmiCmdData = std::get<0>(command);
+
+ // Call the implementation specific Command Failure.
+ // `false` indicating Failure
+ std::get<CallBack>(command)(ipmiCmdData, false);
+ }
+}
+
+// Called for alerting the host
+void Manager::checkQueueAndAlertHost()
+{
+ if (this->workQueue.size() >= 1)
+ {
+ log<level::INFO>("Asserting SMS Attention");
+
+ std::string IPMI_PATH("/org/openbmc/HostIpmi/1");
+ std::string IPMI_INTERFACE("org.openbmc.HostIpmi");
+
+ auto host = ::ipmi::getService(this->bus,IPMI_INTERFACE,IPMI_PATH);
+
+ // Start the timer for this transaction
+ auto time = std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS));
+
+ auto r = timer.startTimer(time);
+ if (r < 0)
+ {
+ log<level::ERR>("Error starting timer for control host");
+ return;
+ }
+
+ auto method = this->bus.new_method_call(host.c_str(),
+ IPMI_PATH.c_str(),
+ IPMI_INTERFACE.c_str(),
+ "setAttention");
+ auto reply = this->bus.call(method);
+
+ if (reply.is_method_error())
+ {
+ log<level::ERR>("Error in setting SMS attention");
+ elog<InternalFailure>();
+ }
+ log<level::INFO>("SMS Attention asserted");
+ }
+}
+
+// Called by specific implementations that provide commands
+void Manager::execute(CommandHandler command)
+{
+ log<level::INFO>("Pushing cmd on to queue",
+ entry("COMMAND=%d", std::get<0>(command).first));
+
+ this->workQueue.emplace(command);
+
+ // Alert host if this is only command in queue otherwise host will
+ // be notified of next message after processing the current one
+ if (this->workQueue.size() == 1)
+ {
+ this->checkQueueAndAlertHost();
+ }
+ else
+ {
+ log<level::INFO>("Command in process, no attention");
+ }
+
+ return;
+}
+
+} // namespace command
+} // namespace host
+} // namepsace phosphor