Add Set Package and Channel Mask to ncsi-netlink utility

The NCSI driver has added two commands for setting the Package Mask
and the Channel Mask within a package. The ncsi-netlink utility does
not support these new commands.

Add the ability to set the package and the channel mask values.

Tested:
Instrumented the NCSI kernel driver to print mask values.
Issued 'ncsi-netlink -x 3 -j 0x3' and saw the NCSI driver print the
new package mask.
Issued 'ncsi-netlink -x 3 -p 0 -k 0x3' and saw the NCSI driver
print the new channel mask.

Change-Id: Icd2188e789de43f631fe26d9e751d564ba5f822f
Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com>
diff --git a/src/argument.cpp b/src/argument.cpp
index 6bc40bc..16d855c 100644
--- a/src/argument.cpp
+++ b/src/argument.cpp
@@ -15,9 +15,7 @@
  */
 #include "argument.hpp"
 
-#include <algorithm>
 #include <iostream>
-#include <iterator>
 
 namespace phosphor
 {
@@ -76,6 +74,8 @@
            "    --set   | -s      Set a specific package/channel.\n"
            "    --clear | -r      Clear all the settings on the interface.\n"
            "    --oem-payload=<hex data...> | -o <hex data...> Send an OEM command with payload.\n"
+           "    --pmask=<mask> | -j <mask> Bitmask to enable/disable packages\n"
+           "    --cmask=<mask> | -k <mask> Bitmask to enable/disable channels\n"
            "\n"
            "Example commands:\n"
            "    1) Retrieve topology information:\n"
@@ -88,6 +88,10 @@
            "         ncsi-netlink -x 3 -p 0 -r\n"
            "    5) Send NCSI Command\n"
            "         ncsi-netlink -x 3 -p 0 -c 0 -o 50000001572100\n"
+           "    6) Set Package Mask\n"
+           "         ncsi-netlink -x 3 -j 1\n"
+           "    7) Set Channel Mask\n"
+           "         ncsi-netlink -x 3 -p 0 -k 1\n"
            "\n";
 }
 
@@ -100,10 +104,12 @@
     {"channel", required_argument, NULL, 'c'},
     {"index", required_argument, NULL, 'x'},
     {"help", no_argument, NULL, 'h'},
+    {"pmask", required_argument, NULL, 'j'},
+    {"cmask", required_argument, NULL, 'k'},
     {0, 0, 0, 0},
 };
 
-const char* ArgumentParser::optionStr = "irsx:o:p:c:h?";
+const char* ArgumentParser::optionStr = "irsj:k:x:o:p:c:h?";
 
 const std::string ArgumentParser::trueString = "true";
 const std::string ArgumentParser::emptyString = "";
diff --git a/src/ncsi_netlink_main.cpp b/src/ncsi_netlink_main.cpp
index 7808ef7..6657c60 100644
--- a/src/ncsi_netlink_main.cpp
+++ b/src/ncsi_netlink_main.cpp
@@ -157,6 +157,46 @@
     {
         return ncsi::clearInterface(indexInt);
     }
+    else if (!(options)["pmask"].empty())
+    {
+        unsigned int mask{};
+        try
+        {
+            size_t lastChar{};
+            mask = std::stoul((options)["pmask"], &lastChar, 0);
+            if (lastChar < (options["pmask"].size()))
+            {
+                exitWithError("Package mask value is not valid", argv);
+            }
+        }
+        catch (const std::exception& e)
+        {
+            exitWithError("Package mask value is not valid", argv);
+        }
+        return ncsi::setPackageMask(indexInt, mask);
+    }
+    else if (!(options)["cmask"].empty())
+    {
+        if (packageInt == DEFAULT_VALUE)
+        {
+            exitWithError("Package is not specified", argv);
+        }
+        unsigned int mask{};
+        try
+        {
+            size_t lastChar{};
+            mask = stoul((options)["cmask"], &lastChar, 0);
+            if (lastChar < (options["cmask"].size()))
+            {
+                exitWithError("Channel mask value is not valid", argv);
+            }
+        }
+        catch (const std::exception& e)
+        {
+            exitWithError("Channel mask value is not valid", argv);
+        }
+        return ncsi::setChannelMask(indexInt, packageInt, mask);
+    }
     else
     {
         exitWithError("No Command specified", argv);
diff --git a/src/ncsi_util.cpp b/src/ncsi_util.cpp
index 839c80d..c3d3794 100644
--- a/src/ncsi_util.cpp
+++ b/src/ncsi_util.cpp
@@ -9,8 +9,6 @@
 #include <stdplus/numeric/str.hpp>
 #include <stdplus/str/buf.hpp>
 
-#include <iomanip>
-#include <iostream>
 #include <vector>
 
 namespace phosphor
@@ -344,7 +342,29 @@
         return ret;
     }
 
-    if (cmd.operation != DEFAULT_VALUE)
+    if ((cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK) ||
+        (cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK))
+    {
+        if (cmd.payload.size() != sizeof(unsigned int))
+        {
+            lg2::error("Package/Channel mask must be 32-bits");
+            return -EINVAL;
+        }
+        int maskAttr =
+            cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK
+                ? NCSI_ATTR_PACKAGE_MASK
+                : NCSI_ATTR_CHANNEL_MASK;
+        ret = nla_put_u32(
+            msg.get(), maskAttr,
+            *(reinterpret_cast<const unsigned int*>(cmd.payload.data())));
+        if (ret < 0)
+        {
+            lg2::error("Failed to set the mask attribute, RC : {RC}", "RC",
+                       ret);
+            return ret;
+        }
+    }
+    else if (cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SEND_CMD)
     {
         std::vector<unsigned char> pl(
             sizeof(NCSIPacketHeader) + cmd.payload.size());
@@ -457,6 +477,36 @@
     }
 }
 
+int setPackageMask(int ifindex, unsigned int mask)
+{
+    lg2::debug(
+        "Set Package Mask , INTERFACE_INDEX: {INTERFACE_INDEX} MASK: {MASK}",
+        "INTERFACE_INDEX", lg2::hex, ifindex, "MASK", lg2::hex, mask);
+    auto payload = std::span<const unsigned char>(
+        reinterpret_cast<const unsigned char*>(&mask),
+        reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
+    return internal::applyCmd(
+        ifindex, internal::Command(ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK,
+                                   0, payload));
+}
+
+int setChannelMask(int ifindex, int package, unsigned int mask)
+{
+    lg2::debug(
+        "Set Channel Mask , INTERFACE_INDEX: {INTERFACE_INDEX}, PACKAGE : {PACKAGE} MASK: {MASK}",
+        "INTERFACE_INDEX", lg2::hex, ifindex, "PACKAGE", lg2::hex, package,
+        "MASK", lg2::hex, mask);
+    auto payload = std::span<const unsigned char>(
+        reinterpret_cast<const unsigned char*>(&mask),
+        reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
+    return internal::applyCmd(
+        ifindex,
+        internal::Command(ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK, 0,
+                          payload),
+        package);
+    return 0;
+}
+
 } // namespace ncsi
 } // namespace network
 } // namespace phosphor
diff --git a/src/ncsi_util.hpp b/src/ncsi_util.hpp
index 750f0f1..dfa39e5 100644
--- a/src/ncsi_util.hpp
+++ b/src/ncsi_util.hpp
@@ -58,6 +58,23 @@
  */
 int getInfo(int ifindex, int package);
 
+/* @brief  This function assigns a mask controlling responses to AEN from a
+ * package.
+ * @param[in] ifindex - Interface Index.
+ * @param[in] mask - A 32-bit mask integer
+ * @returns 0 on success and negative value for failure.
+ */
+int setPackageMask(int ifindex, unsigned int mask);
+
+/* @brief  This function sets the AEN mask for the channels inside the selected
+ * package.
+ * @param[in] ifindex - Interface Index.
+ * @param[in] package - NCSI Package.
+ * @param[in] mask - A 32-bit mask integer
+ * @returns 0 on success and negative value for failure.
+ */
+int setChannelMask(int ifindex, int package, unsigned int mask);
+
 } // namespace ncsi
 } // namespace network
 } // namespace phosphor