Add routines to start and stop the sd_event timer

Change-Id: I738be7b70554125e544aa59fe1770e909d3dffb1
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/configure.ac b/configure.ac
index 68e0dc9..7112ca7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -90,6 +90,10 @@
     AS_IF([test "x$SOFTOFF_OBJPATH" == "x"],
           [SOFTOFF_OBJPATH="/xyz/openbmc_project/ipmi/internal/softpoweroff"])
     [AC_DEFINE_UNQUOTED([SOFTOFF_OBJPATH], ["$SOFTOFF_OBJPATH"], [The SoftPowerOff Dbus root])]
+
+    # Timeouts in SECONDS for SoftPowerOff protocol
+    [AC_ARG_VAR(IPMI_SMS_ATN_ACK_TIMEOUT_SECS, [Initial timeout for host to ack SMS_ATN from BMC])]
+    [AC_DEFINE_UNQUOTED([IPMI_SMS_ATN_ACK_TIMEOUT_SECS], [3], [Initial timeout for host to ack SMS_ATN from BMC])]
 )
 
 # Create configured output
diff --git a/softoff/softoff.cpp b/softoff/softoff.cpp
index 11298ab..49b6f26 100644
--- a/softoff/softoff.cpp
+++ b/softoff/softoff.cpp
@@ -13,7 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <chrono>
+#include <phosphor-logging/log.hpp>
 #include "softoff.hpp"
+#include "config.h"
 namespace phosphor
 {
 namespace ipmi
@@ -22,6 +25,7 @@
 /** @brief Send the SMS_ATN to host if value is set */
 void SoftPowerOff::sendSMSAttn()
 {
+    using namespace std::chrono;
     auto method = bus.new_method_call(HOST_IPMI_BUS,
                                       HOST_IPMI_OBJ,
                                       HOST_IPMI_INTF,
@@ -31,6 +35,14 @@
     // BT returns '0' on success and bus_error on failure.
     bus.call_noreply(method);
 
+    // Start the timer
+    auto time = duration_cast<microseconds>(
+            seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS));
+    auto r = timer.startTimer(time);
+    if (r < 0)
+    {
+        throw std::runtime_error("Error starting timer");
+    }
     return;
 }
 
diff --git a/softoff/timer.cpp b/softoff/timer.cpp
index 9e722f1..c626536 100644
--- a/softoff/timer.cpp
+++ b/softoff/timer.cpp
@@ -1,3 +1,4 @@
+#include <chrono>
 #include <phosphor-logging/log.hpp>
 #include "timer.hpp"
 namespace phosphor
@@ -19,7 +20,7 @@
     // Add infinite expiration time
     auto r = sd_event_add_time(timeEvent, &eventSource,
                                CLOCK_MONOTONIC, // Time base
-                               UINT64_MAX,      // Expire time - way long enough time
+                               UINT64_MAX,      // Expire time - way long time
                                0,               // Use default event accuracy
                                timeoutHandler,  // Callback handler on timeout
                                this);           // User data
@@ -32,13 +33,13 @@
     }
 
     // Disable the timer for now
-    r = sd_event_source_set_enabled(eventSource, SD_EVENT_OFF);
+    r = setTimer(SD_EVENT_OFF);
     if (r < 0)
     {
         log<level::ERR>("Failure to disable timer",
                 entry("ERROR=%s", strerror(-r)));
 
-        throw std::runtime_error("Setting initial timer value failed");
+        throw std::runtime_error("Disabling the timer failed");
     }
     return;
 }
@@ -54,5 +55,48 @@
     return 0;
 }
 
+// Gets the time from steady_clock
+std::chrono::microseconds Timer::getTime()
+{
+    using namespace std::chrono;
+    auto usec = steady_clock::now().time_since_epoch();
+    return duration_cast<microseconds>(usec);
+}
+
+// Enables or disables the timer
+int Timer::setTimer(int action)
+{
+    return sd_event_source_set_enabled(eventSource, action);
+}
+
+// Sets the time and arms the timer
+int Timer::startTimer(std::chrono::microseconds timeValue)
+{
+    // Disable the timer
+    setTimer(SD_EVENT_OFF);
+
+    // Get the current MONOTONIC time and add the delta
+    auto expireTime = getTime() + timeValue;
+
+    // Set the time
+    auto r = sd_event_source_set_time(eventSource, expireTime.count());
+    if (r < 0)
+    {
+        log<level::ERR>("Failure to set timer",
+                entry("ERROR=%s", strerror(-r)));
+        return r;
+    }
+
+    // A ONESHOT timer means that when the timer goes off,
+    // its moves to disabled state.
+    r = setTimer(SD_EVENT_ONESHOT);
+    if (r < 0)
+    {
+        log<level::ERR>("Failure to start timer",
+                entry("ERROR=%s", strerror(-r)));
+    }
+    return r;
+}
+
 } // namespace ipmi
 } // namespace phosphor
diff --git a/softoff/timer.hpp b/softoff/timer.hpp
index 7c3bac2..9d597f8 100644
--- a/softoff/timer.hpp
+++ b/softoff/timer.hpp
@@ -38,11 +38,19 @@
             }
         }
 
-        inline auto isExpired()
+        inline auto isExpired() const
         {
             return expired;
         }
 
+        /** @brief Starts the timer with specified expiration value.
+         *  input is an offset from the current steady_clock
+         */
+        int startTimer(std::chrono::microseconds usec);
+
+        /** @brief Enables / disables the timer */
+        int setTimer(int action);
+
     private:
         /** @brief the sd_event structure */
         sd_event* timeEvent = nullptr;
@@ -76,6 +84,9 @@
          */
         static int timeoutHandler(sd_event_source* eventSource,
                                   uint64_t usec, void* userData);
+
+        /** @brief Gets the current time from steady clock */
+        static std::chrono::microseconds getTime();
 };
 
 } // namespace ipmi