Add OemRouter facility.

OemRouter adds a facility to register OEM Group Message handlers,
then dispatch matching messages to the registered handler.
Added as a core source so that any dynamic provider can register its
messages without requiring any specific load order.

Includes code fixes for x86 portability.

Change-Id: I47b8fe7873e3c7fdf35a00d3c8a7e17d30c398c4
Signed-off-by: Peter Hanson <peterh@google.com>
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/host-ipmid/ipmid-api.h b/host-ipmid/ipmid-api.h
index 5a2e81d..1bec851 100644
--- a/host-ipmid/ipmid-api.h
+++ b/host-ipmid/ipmid-api.h
@@ -93,6 +93,7 @@
     NETFUN_STORAGE   =   0x0a,
     NETFUN_TRANSPORT =   0x0c,
     NETFUN_GRPEXT    =   0x2c,
+    NETFUN_OEM_GROUP =   0x2e,
     NETFUN_NONE      =   0x30,
     NETFUN_OEM       =   0x32,
     NETFUN_IBM_OEM   =   0x3A
diff --git a/host-ipmid/oemopenbmc.hpp b/host-ipmid/oemopenbmc.hpp
new file mode 100644
index 0000000..c3bfaa4
--- /dev/null
+++ b/host-ipmid/oemopenbmc.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "host-ipmid/ipmid-api.h"
+#include "host-ipmid/oemrouter.hpp"
+
+namespace oem
+{
+
+/*
+ * This is the OpenBMC IANA OEM Number
+ */
+constexpr Number obmcOemNumber = 49871;
+
+/*
+ * OpenBMC OEM Extension IPMI Command codes.
+ */
+enum Cmd
+{
+    gpioCmd = 1,
+    i2cCmd = 2,
+    flashCmd = 3,
+    fanManualCmd = 4,
+};
+
+}  // namespace oem
diff --git a/host-ipmid/oemrouter.hpp b/host-ipmid/oemrouter.hpp
new file mode 100644
index 0000000..bc2b88c
--- /dev/null
+++ b/host-ipmid/oemrouter.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <functional>
+#include <vector>
+
+#include "host-ipmid/ipmid-api.h"
+
+namespace oem
+{
+constexpr size_t groupMagicSize = 3;
+
+using Group = std::array<uint8_t, groupMagicSize>;
+using Number = uint32_t;  // smallest standard size >= 24.
+
+// Handler signature includes ipmi cmd to support wildcard cmd match.
+// Buffers and lengths exclude the OemGroup bytes in the IPMI message.
+// dataLen supplies length of reqBuf upon call, and should be set to the
+// length of replyBuf upon return - conventional in this code base.
+using Handler = std::function<ipmi_ret_t(
+                       ipmi_cmd_t,     // cmd byte
+                       const uint8_t*, // reqBuf
+                       uint8_t*,       // replyBuf
+                       size_t*)>;      // dataLen
+
+/// Router Interface class.
+/// @brief Abstract Router Interface
+class Router
+{
+    public:
+        virtual ~Router() {}
+
+        /// Enable message routing to begin.
+        virtual void activate() = 0;
+
+        /// Register a handler for given OEMNumber & cmd.
+        /// Use IPMI_CMD_WILDCARD to catch any unregistered cmd
+        /// for the given OEMNumber.
+        ///
+        /// @param[in] oen - the OEM Number.
+        /// @param[in] cmd - the Command.
+        /// @param[in] handler - the handler to call given that OEN and
+        ///                      command.
+        virtual void registerHandler(Number oen, ipmi_cmd_t cmd,
+                                     Handler handler) = 0;
+};
+
+/// Expose mutable Router for configuration & activation.
+///
+/// @returns pointer to OEM Router to use.
+Router* mutableRouter();
+
+/// Convert a group to an OEN.
+///
+/// @param[in] oeg - request buffer for IPMI command.
+/// @return the OEN.
+constexpr Number toOemNumber(const uint8_t oeg[groupMagicSize])
+{
+    return (oeg[2] << 16) | (oeg[1] << 8) | oeg[0];
+}
+
+/// Given a Group convert to an OEN.
+///
+/// @param[in] oeg - OEM Group reference.
+/// @return the OEN.
+constexpr Number toOemNumber(const Group& oeg)
+{
+    return (oeg[2] << 16) | (oeg[1] << 8) | oeg[0];
+}
+
+/// Given an OEN, conver to the OEM Group.
+///
+/// @param[in] oen - the OEM Number.
+/// @return the OEM Group.
+constexpr Group toOemGroup(Number oen)
+{
+    return Group { static_cast<uint8_t>(oen),
+                      static_cast<uint8_t>(oen >> 8),
+                      static_cast<uint8_t>(oen >> 16) };
+}
+
+}  // namespace oem