Add attention event handling priority logic

All active attention events are gathered and prioritized and the highest
priority event is handled.

Signed-off-by: Ben Tyner <ben.tyner@ibm.com>
Change-Id: Icad55dacb23232801fdbb5995ef8ee3edc7e90f4
diff --git a/attn/attention.cpp b/attn/attention.cpp
new file mode 100644
index 0000000..c13bf64
--- /dev/null
+++ b/attn/attention.cpp
@@ -0,0 +1,50 @@
+#include <attention.hpp>
+
+namespace attn
+{
+
+/** @brief Main constructor. */
+Attention::Attention(AttentionType i_type, int (*i_handler)(Attention*),
+                     pdbg_target* i_target, bool i_breakpoints) :
+    iv_type(i_type),
+    iv_handler(i_handler), iv_target(i_target)
+
+{
+    // set attention handler configuration flags
+    if (true == i_breakpoints)
+    {
+        iv_flags |= enableBreakpoints;
+    }
+}
+
+/** @brief Get attention priority */
+int Attention::getPriority() const
+{
+    return iv_type;
+}
+
+/** @brief Get configuration flags */
+uint32_t Attention::getFlags() const
+{
+    return iv_flags;
+}
+
+/** @brief Set configuration flags */
+void Attention::setFlags(uint32_t i_flags)
+{
+    iv_flags = i_flags;
+}
+
+/* @brief Call attention handler function */
+int Attention::handle()
+{
+    return iv_handler(this);
+}
+
+/** @brief less than operator */
+bool Attention::operator<(const Attention& right) const
+{
+    return (getPriority() < right.getPriority());
+}
+
+} // namespace attn
diff --git a/attn/attention.hpp b/attn/attention.hpp
new file mode 100644
index 0000000..28e7c76
--- /dev/null
+++ b/attn/attention.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include <libpdbg.h>
+
+namespace attn
+{
+
+/** @brief attention handler configuration flags */
+inline constexpr uint32_t enableBreakpoints = 1;
+
+/**
+ * @brief These objects contain information about an active attention.
+ *
+ * An Attention object is created for each active attention. These objects
+ * carry with them various configuration and status information as well
+ * the attention handler function to call for handling the attention. Each
+ * Attention object also carries a priority value. This priority is used
+ * to determine which attention event(s) to handle when there are more than
+ * one active event.
+ */
+class Attention
+{
+  public:
+    /** @brief types of attentions to be handled (by priority low to high) */
+    enum AttentionType
+    {
+        Special   = 0,
+        Checkstop = 1,
+        Vital     = 2
+    };
+
+    /** @brief Default constructor. */
+    Attention() = delete;
+
+    /** @brief Main constructors */
+    Attention(AttentionType i_type, int (*i_handler)(Attention*),
+              pdbg_target* i_target, bool i_breakpoints);
+
+    /** @brief Destructor */
+    ~Attention() = default;
+
+    /** @brief Get attention priority */
+    int getPriority() const;
+
+    /** @brief Get configuration flags */
+    uint32_t getFlags() const;
+
+    /** @brief Set configuration flags */
+    void setFlags(uint32_t i_flags);
+
+    /* @brief Call attention handler function */
+    int handle();
+
+    /** @brief Copy constructor. */
+    Attention(const Attention&) = default;
+
+    /** @brief Assignment operator. */
+    Attention& operator=(const Attention&) = default;
+
+    /** @brief less than operator */
+    bool operator<(const Attention& right) const;
+
+  private:
+    AttentionType iv_type;         // attention type
+    int (*iv_handler)(Attention*); // handler function
+    pdbg_target* iv_target;        // handler function target
+    uint32_t iv_flags = 0;         // configuration flags
+};
+
+} // namespace attn
diff --git a/attn/attn_handler.cpp b/attn/attn_handler.cpp
index 4a5382d..af62771 100644
--- a/attn/attn_handler.cpp
+++ b/attn/attn_handler.cpp
@@ -1,45 +1,55 @@
-#include <libpdbg.h>
-
 #include <analyzer/analyzer_main.hpp>
+#include <attention.hpp>
 #include <bp_handler.hpp>
 #include <logging.hpp>
 #include <ti_handler.hpp>
 
+#include <algorithm>
 #include <iomanip>
 #include <sstream>
+#include <vector>
 
 namespace attn
 {
 
+/** @brief Return codes */
+static constexpr int RC_SUCCESS     = 0;
+static constexpr int RC_NOT_SUCCESS = 1;
+
 /**
  * @brief Handle SBE vital attention
  *
+ * @param i_attention Attention object
  * @return 0 = success
  */
-int handleVital();
+int handleVital(Attention* i_attention);
 
 /**
  * @brief Handle checkstop attention
  *
+ * @param i_attention Attention object
  * @return 0 = success
  */
-int handleCheckstop();
+int handleCheckstop(Attention* i_attention);
 
 /**
  * @brief Handle special attention
  *
- * @param i_breakpoints true = breakpoint special attn handling enabled
+ * @param i_attention Attention object
  * @return 0 = success
  */
-int handleSpecial(bool i_breakpoints);
+int handleSpecial(Attention* i_attention);
 
 /**
  * @brief The main attention handler logic
  *
  * @param i_breakpoints true = breakpoint special attn handling enabled
  */
-void attnHandler(bool i_breakpoints)
+void attnHandler(const bool i_breakpoints)
 {
+    // Vector of active attentions to be handled
+    std::vector<Attention> active_attentions;
+
     uint32_t isr_val, isr_mask;
     uint32_t proc;
 
@@ -56,7 +66,7 @@
             log<level::INFO>(ss.str().c_str());
 
             // get active attentions on processor
-            if (0 != fsi_read(target, 0x1007, &isr_val))
+            if (RC_SUCCESS != fsi_read(target, 0x1007, &isr_val))
             {
                 std::stringstream ss; // log message stream
                 ss << "Error! cfam read 0x1007 FAILED" << std::endl;
@@ -71,7 +81,7 @@
                 log<level::INFO>(ss.str().c_str());
 
                 // get interrupt enabled special attentions mask
-                if (0 != fsi_read(target, 0x100d, &isr_mask))
+                if (RC_SUCCESS != fsi_read(target, 0x100d, &isr_mask))
                 {
                     std::stringstream ss; // log message stream
                     ss << "Error! cfam read 0x100d FAILED" << std::endl;
@@ -88,43 +98,66 @@
                     // bit 0 on "left": bit 30 = SBE vital attention
                     if (isr_val & isr_mask & 0x00000002)
                     {
-                        handleVital();
+                        active_attentions.emplace_back(Attention::Vital,
+                                                       handleVital, target,
+                                                       i_breakpoints);
                     }
 
                     // bit 0 on "left": bit 1 = checkstop
                     if (isr_val & isr_mask & 0x40000000)
                     {
-                        if (0 == handleCheckstop())
-                        {
-                            break;
-                        }
+                        active_attentions.emplace_back(Attention::Checkstop,
+                                                       handleCheckstop, target,
+                                                       i_breakpoints);
                     }
 
                     // bit 0 on "left": bit 2 = special attention
                     if (isr_val & isr_mask & 0x20000000)
                     {
-                        handleSpecial(i_breakpoints);
+                        active_attentions.emplace_back(Attention::Special,
+                                                       handleSpecial, target,
+                                                       i_breakpoints);
                     }
                 } // cfam 0x100d valid
             }     // cfam 0x1007 valid
         }         // fsi target enabled
     }             // next processor
 
-    return; // checked all processors
+    // convert to heap, highest priority is at front
+    if (!std::is_heap(active_attentions.begin(), active_attentions.end()))
+    {
+        std::make_heap(active_attentions.begin(), active_attentions.end());
+    }
+
+    // call the attention handler until one is handled or all were attempted
+    while (false == active_attentions.empty())
+    {
+        // handle highest priority attention, done if successful
+        if (RC_SUCCESS == active_attentions.front().handle())
+        {
+            break;
+        }
+
+        // move attention to back of vector
+        std::pop_heap(active_attentions.begin(), active_attentions.end());
+
+        // remove attention from vector
+        active_attentions.pop_back();
+    }
 }
 
 /**
  * @brief Handle SBE vital attention
  */
-int handleVital()
+int handleVital(Attention* i_attention)
 {
-    int rc = 1; // vital attention handling not yet supported
+    int rc = RC_NOT_SUCCESS; // vital attention handling not yet supported
 
     std::stringstream ss; // log message stream
     ss << "vital" << std::endl;
     log<level::INFO>(ss.str().c_str());
 
-    if (0 != rc)
+    if (RC_SUCCESS != rc)
     {
         std::stringstream ss; // log message stream
         ss << "vital NOT handled" << std::endl;
@@ -137,9 +170,9 @@
 /**
  * @brief Handle checkstop attention
  */
-int handleCheckstop()
+int handleCheckstop(Attention* i_attention)
 {
-    int rc = 0; // checkstop handling supported
+    int rc = RC_SUCCESS; // checkstop handling supported
 
     std::stringstream ss; // log message stream
     ss << "checkstop" << std::endl;
@@ -154,12 +187,10 @@
 
 /**
  * @brief Handle special attention
- *
- * @param i_breakpoints true = breakpoint special attn handling enabled
  */
-int handleSpecial(bool i_breakpoints)
+int handleSpecial(Attention* i_attention)
 {
-    int rc = 0; // special attention handling supported
+    int rc = RC_SUCCESS; // special attention handling supported
 
     std::stringstream ss; // log message stream
 
@@ -168,7 +199,7 @@
     // Right now we always handle breakpoint special attentions if breakpoint
     // attn handling is enabled. This will eventually check if breakpoint attn
     // handing is enabled AND there is a breakpoint pending.
-    if (true == i_breakpoints)
+    if (0 != (i_attention->getFlags() & enableBreakpoints))
     {
         ss << "breakpoint" << std::endl;
         log<level::INFO>(ss.str().c_str());
diff --git a/attn/attn_handler.hpp b/attn/attn_handler.hpp
index 256145b..5bfe995 100644
--- a/attn/attn_handler.hpp
+++ b/attn/attn_handler.hpp
@@ -23,6 +23,6 @@
  *
  * @param i_breakpoints true = breakpoint special attn handling enabled
  */
-void attnHandler(bool i_breakpoints);
+void attnHandler(const bool i_breakpoints);
 
 } // namespace attn
diff --git a/attn/meson.build b/attn/meson.build
index 6a81213..40a3875 100644
--- a/attn/meson.build
+++ b/attn/meson.build
@@ -39,7 +39,8 @@
 
 # gather attention sources to be used here and elsewhere if needed
 attn_src = files('attn_main.cpp', 'attn_handler.cpp', 'attn_monitor.cpp',
-                 'bp_handler.cpp', 'ti_handler.cpp', logging_src)
+                 'bp_handler.cpp', 'ti_handler.cpp', logging_src,
+                 'attention.cpp')
 
 # Create attention handler library
 attn = static_library('attn_handler',
diff --git a/test/end2end/main.cpp b/test/end2end/main.cpp
index 3540f6d..3232903 100644
--- a/test/end2end/main.cpp
+++ b/test/end2end/main.cpp
@@ -12,7 +12,7 @@
     pdbg_targets_init(nullptr);
 
     // exercise attention gpio event path
-    attn::attnHandler(false);
+    attn::attnHandler(false); // false = breakpoint handling disabled
 
     return rc;
 }