Create framework for IPMI OEM extension commands

IPMI has four commands that accept "OEM Parameters". The existing IPMI
command handlers do not account for these OEM extensions. This commit
adds OEM Parameters support for the Set/Get LAN Configuration
Parameters commands.

Tested:

ipmitool raw 0xc 1 3 0xc0 0 ;; received 0x80 return code
ipmitool raw 0xc 2 3 0 0    ;; received 0x80 return code

Change-Id: I81135b6d3269cec98ffd7754a03201a74c436c11
Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com>
diff --git a/Makefile.am b/Makefile.am
index cd0b64e..02583fe 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -79,6 +79,12 @@
 	$(AM_V_GEN)@FRUGEN@ -o $(top_builddir) generate-cpp
 
 providers_LTLIBRARIES += libipmi20.la
+if FEATURE_TRANSPORT_OEM
+libipmi20_la_TRANSPORTOEM = transporthandler_oem.cpp
+else
+libipmi20_la_TRANSPORTOEM =
+endif
+
 libipmi20_la_SOURCES = \
 	app/channel.cpp \
 	app/watchdog.cpp \
@@ -100,6 +106,7 @@
 	read_fru_data.cpp \
 	sensordatahandler.cpp \
 	user_channel/channelcommands.cpp \
+	$(libipmi20_la_TRANSPORTOEM) \
 	$(libipmi20_BUILT_LIST)
 
 check_PROGRAMS =
diff --git a/configure.ac b/configure.ac
index b759320..04c48b8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -229,6 +229,21 @@
 )
 AM_CONDITIONAL(FEATURE_LIBUSERLAYER, [test "x$enable_libuserlayer" != "xno"])
 
+# When enable-transport-oem flag is set, the transporthandler_oem.cpp contents
+# are compiled and added to the project. The transporthandler_oem.cpp file is
+# copied from your own customization layer in the
+# phosphor-ipmi-host_%.bbappend file. It is not necessary to create this file
+# unless OEM Parameter extensions are required.
+AC_ARG_ENABLE([transport_oem],
+    [  --enable-transport-oem   Enable/disable OEM Parameter extensions],
+    [case "${enableval}" in
+      yes) transport_oem=true ;;
+      no) transport_oem=false ;;
+      *) AC_MSG_ERROR([bad value ${enableval} for --enable-transport_oem]) ;;
+      esac],[transport_oem=false]
+      )
+AM_CONDITIONAL([FEATURE_TRANSPORT_OEM], [test x$transport_oem = xtrue])
+
 # Create configured output
 AC_CONFIG_FILES([
     Makefile
diff --git a/transporthandler.cpp b/transporthandler.cpp
index 777171c..a88e0a2 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -164,6 +164,9 @@
     CiphersuiteEntries = 23,
 };
 
+static constexpr uint8_t oemCmdStart = 192;
+static constexpr uint8_t oemCmdEnd = 255;
+
 /** @brief IPMI IP Origin Types */
 enum class IPSrc : uint8_t
 {
@@ -1067,6 +1070,60 @@
     return setStatus[channel] = SetStatus::Complete;
 }
 
+/**
+ * Define placeholder command handlers for the OEM Extension bytes for the Set
+ * LAN Configuration Parameters and Get LAN Configuration Parameters
+ * commands. Using "weak" linking allows the placeholder setLanOem/getLanOem
+ * functions below to be overridden.
+ * To create handlers for your own proprietary command set:
+ *   Create/modify a phosphor-ipmi-host Bitbake append file within your Yocto
+ *   recipe
+ *   Create C++ file(s) that define IPMI handler functions matching the
+ *     function names below (i.e. setLanOem). The default name for the
+ *     transport IPMI commands is transporthandler_oem.cpp.
+ *   Add:
+ *      EXTRA_OECONF_append = " --enable-transport-oem=yes"
+ *   Create a do_compile_prepend()/do_install_append method in your
+ *   bbappend file to copy the file to the build directory.
+ *   Add:
+ *   PROJECT_SRC_DIR := "${THISDIR}/${PN}"
+ *   # Copy the "strong" functions into the working directory, overriding the
+ *   # placeholder functions.
+ *   do_compile_prepend(){
+ *      cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S}
+ *   }
+ *
+ *   # Clean up after complilation has completed
+ *   do_install_append(){
+ *      rm -f ${S}/transporthandler_oem.cpp
+ *   }
+ *
+ */
+
+/**
+ * Define the placeholder OEM commands as having weak linkage. Create
+ * setLanOem, and getLanOem functions in the transporthandler_oem.cpp
+ * file. The functions defined there must not have the "weak" attribute
+ * applied to them.
+ */
+RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
+    __attribute__((weak));
+RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
+                                    uint8_t set, uint8_t block)
+    __attribute__((weak));
+
+RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
+{
+    req.trailingOk = true;
+    return response(ccParamNotSupported);
+}
+
+RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
+                                    uint8_t set, uint8_t block)
+{
+    return response(ccParamNotSupported);
+}
+
 RspType<> setLan(uint4_t channelBits, uint4_t, uint8_t parameter,
                  message::Payload& req)
 {
@@ -1234,6 +1291,11 @@
         }
     }
 
+    if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
+    {
+        return setLanOem(channel, parameter, req);
+    }
+
     req.trailingOk = true;
     return response(ccParamNotSupported);
 }
@@ -1394,6 +1456,11 @@
         }
     }
 
+    if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
+    {
+        return getLanOem(channel, parameter, set, block);
+    }
+
     return response(ccParamNotSupported);
 }