PEL: SRC FRU callout structure

This class represents a single FRU callout in the SRC section of a PEL.
When there are multiple callouts, there will be multiple instances of
this class created.  Technically, the callout isn't always a FRU, such
as it could be a maintenance procedure name, but the spec still refers
to this section as the FRU callout section.

The callout priority and location code are in this structure.

There can also be up to one each of three types of substructures
in a single callout:
* FRU Identity  (must be first if present)
* Power Controlling Enclosure (PCE)
* Manufacturing Replaceable Unit (MRU)

This commit just provides support for creating this object from a
flattened PEL, such as one that comes down from the host.  A future
commit will add support for creating a callout for BMC created event
logs.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I49535285e3cbaa15dfe031648cfaf262380a1cf7
diff --git a/extensions/openpower-pels/callout.cpp b/extensions/openpower-pels/callout.cpp
new file mode 100644
index 0000000..53fe85d
--- /dev/null
+++ b/extensions/openpower-pels/callout.cpp
@@ -0,0 +1,102 @@
+#include "callout.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+namespace openpower
+{
+namespace pels
+{
+namespace src
+{
+
+using namespace phosphor::logging;
+
+Callout::Callout(Stream& pel)
+{
+    pel >> _size >> _flags >> _priority >> _locationCodeSize;
+
+    if (_locationCodeSize)
+    {
+        _locationCode.resize(_locationCodeSize);
+        pel >> _locationCode;
+    }
+
+    size_t currentSize = 4 + _locationCodeSize;
+
+    // Read in the substructures until the end of this structure.
+    // Any stream overflows will throw an exception up to the SRC constructor
+    while (_size > currentSize)
+    {
+        // Peek the type
+        uint16_t type = 0;
+        pel >> type;
+        pel.offset(pel.offset() - 2);
+
+        switch (type)
+        {
+            case FRUIdentity::substructureType:
+            {
+                _fruIdentity = std::make_unique<FRUIdentity>(pel);
+                currentSize += _fruIdentity->flattenedSize();
+                break;
+            }
+            case PCEIdentity::substructureType:
+            {
+                _pceIdentity = std::make_unique<PCEIdentity>(pel);
+                currentSize += _pceIdentity->flattenedSize();
+                break;
+            }
+            case MRU::substructureType:
+            {
+                _mru = std::make_unique<MRU>(pel);
+                currentSize += _mru->flattenedSize();
+                break;
+            }
+            default:
+                log<level::ERR>("Invalid Callout subsection type",
+                                entry("CALLOUT_TYPE=0x%X", type));
+                throw std::runtime_error("Invalid Callout subsection type");
+                break;
+        }
+    }
+}
+
+size_t Callout::flattenedSize()
+{
+    size_t size = sizeof(_size) + sizeof(_flags) + sizeof(_priority) +
+                  sizeof(_locationCodeSize) + _locationCodeSize;
+
+    size += _fruIdentity ? _fruIdentity->flattenedSize() : 0;
+    size += _pceIdentity ? _pceIdentity->flattenedSize() : 0;
+    size += _mru ? _mru->flattenedSize() : 0;
+
+    return size;
+}
+
+void Callout::flatten(Stream& pel)
+{
+    pel << _size << _flags << _priority << _locationCodeSize;
+
+    if (_locationCodeSize)
+    {
+        pel << _locationCode;
+    }
+
+    if (_fruIdentity)
+    {
+        _fruIdentity->flatten(pel);
+    }
+
+    if (_pceIdentity)
+    {
+        _pceIdentity->flatten(pel);
+    }
+    if (_mru)
+    {
+        _mru->flatten(pel);
+    }
+}
+
+} // namespace src
+} // namespace pels
+} // namespace openpower