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/Makefile.am b/Makefile.am
index 18b198c..ea44248 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,8 +5,10 @@
 
 ipmid_SOURCES = \
 	ipmid.cpp \
-	settings.cpp
-
+	settings.cpp \
+	host-cmd-manager.cpp \
+	timer.cpp \
+	utils.cpp
 nodist_ipmid_SOURCES = ipmiwhitelist.cpp
 
 BUILT_SOURCES = \
@@ -18,11 +20,9 @@
 CLEANFILES = $(BUILT_SOURCES)
 
 #TODO - Make this path a configure option (bitbake parameter)
-ipmid_CPPFLAGS = \
-	-DHOST_IPMI_LIB_PATH=\"/usr/lib/host-ipmid/\" \
-	$(PHOSPHOR_LOGGING_CFLAGS) \
-	$(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
-
+ipmid_CPPFLAGS = -DHOST_IPMI_LIB_PATH=\"/usr/lib/host-ipmid/\" \
+                 $(PHOSPHOR_LOGGING_CFLAGS) \
+                 $(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
 ipmid_LDFLAGS = \
 	$(SYSTEMD_LIBS) \
 	$(libmapper_LIBS) \
@@ -30,6 +30,7 @@
 	$(PHOSPHOR_LOGGING_LIBS) \
 	$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
 	-export-dynamic
+
 # TODO: Rather than use -export-dynamic, we should use -export-symbol to have a
 #       selective list of symbols.
 
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
diff --git a/host-cmd-manager.hpp b/host-cmd-manager.hpp
new file mode 100644
index 0000000..0c938e5
--- /dev/null
+++ b/host-cmd-manager.hpp
@@ -0,0 +1,88 @@
+#pragma once
+
+#include <tuple>
+#include <queue>
+#include <sdbusplus/bus.hpp>
+#include <timer.hpp>
+#include <host-cmd-utils.hpp>
+
+namespace phosphor
+{
+namespace host
+{
+namespace command
+{
+
+/** @class
+ *  @brief Manages commands that are to be sent to Host
+ */
+class Manager
+{
+    public:
+        Manager() = delete;
+        ~Manager() = default;
+        Manager(const Manager&) = delete;
+        Manager& operator=(const Manager&) = delete;
+        Manager(Manager&&) = delete;
+        Manager& operator=(Manager&&) = delete;
+
+        /** @brief Constructs Manager object
+         *
+         *  @param[in] bus   - dbus handler
+         *  @param[in] event - pointer to sd_event
+         */
+        Manager(sdbusplus::bus::bus& bus, sd_event* event) :
+            bus(bus),
+            timer(event, std::bind(&Manager::hostTimeout, this))
+        {
+            // Nothing to do here.
+        }
+
+        /** @brief  Extracts the next entry in the queue and returns
+         *          Command and data part of it.
+         *
+         *  @detail Also calls into the registered handlers so that they can now
+         *          send the CommandComplete signal since the interface contract
+         *          is that we emit this signal once the message has been
+         *          passed to the host (which is required when calling this)
+         *
+         *          Also, if the queue has more commands, then it will alert the
+         *          host
+         */
+        IpmiCmdData getNextCommand();
+
+        /** @brief  Pushes the command onto the queue.
+         *
+         *  @detail If the queue is empty, then it alerts the Host. If not,
+         *          then it returns and the API documented above will handle
+         *          the commands in Queue.
+         *
+         *  @param[in] command - tuple of <IPMI command, data, callback>
+         */
+        void execute(CommandHandler command);
+
+    private:
+        /** @brief Check if anything in queue and alert host if so */
+        void checkQueueAndAlertHost();
+
+        /** @brief  Call back interface on message timeouts to host.
+         *
+         *  @detail When this happens, a failure message would be sent
+         *          to all the commands that are in the Queue and queue
+         *          will be purged
+         */
+        void hostTimeout();
+
+        /** @brief Reference to the dbus handler */
+        sdbusplus::bus::bus& bus;
+
+        /** @brief Queue to store the requested commands */
+        std::queue<CommandHandler> workQueue{};
+
+        /** @brief Timer for commands to host */
+        phosphor::ipmi::Timer timer;
+};
+
+} // namespace command
+} // namespace host
+} // namespace phosphor
diff --git a/host-cmd-utils.hpp b/host-cmd-utils.hpp
new file mode 100644
index 0000000..f564e2a
--- /dev/null
+++ b/host-cmd-utils.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <unistd.h>
+#include <tuple>
+
+namespace phosphor
+{
+namespace host
+{
+namespace command
+{
+    /** @detail After sending SMS_ATN to the Host, Host comes down and
+     *          asks why an 'SMS_ATN` was sent.
+     *          BMC then sends 'There is a Message to be Read` as reponse.
+     *          Host then comes down asks for Message and the specified
+     *          commands and data would go as data conforming to IPMI spec.
+     *
+     *          Refer: 6.13.2 Send Message Command From System Interface
+     *          in IPMI V2.0 spec.
+     */
+
+    /** @brief IPMI command */
+    using IPMIcmd = uint8_t;
+
+    /** @brief Data associated with command */
+    using Data = uint8_t;
+
+    /** @brief <IPMI command, Data> to be sent as payload when Host asks for
+     *          the message that can be associated with the previous SMS_ATN
+     */
+    using IpmiCmdData = std::pair<IPMIcmd, Data>;
+
+    /** @detail Implementation specific callback function to be invoked
+     *          conveying the status of the executed command. Specific
+     *          implementations may then broadcast an agreed signal
+     */
+    using CallBack = std::function<void(IpmiCmdData, bool)>;
+
+    /** @detail Tuple encapsulating above 2 to enable using Manager by
+     *          different implementations. Users of Manager will supply
+     *          <Ipmi command, Data> along with the callback handler.
+     *          Manager will invoke the handler onveying the status of
+     *          the command.
+     */
+    using CommandHandler = std::tuple<IpmiCmdData, CallBack>;
+
+} // namespace command
+} // namespace host
+} // namespace phosphor