Read channel configuration from JSON file

Read channel information from /usr/share/ipmbbridge/ipmb-channels.json
allowing platforms to override this information.

Signed-off-by: Amithash Prasad <amithash@fb.com>
Change-Id: I161cf72840ff211c270fe56a86e6d3ebd65f170c
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 107f041..80377f5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,6 +40,8 @@
 link_directories (${EXTERNAL_INSTALL_LOCATION}/lib)
 
 set (SERVICE_FILES ${PROJECT_SOURCE_DIR}/ipmb.service)
+set (CONFIG_FILES ${PROJECT_SOURCE_DIR}/ipmb-channels.json)
 
 install (TARGETS ${PROJECT_NAME} DESTINATION bin)
 install (FILES ${SERVICE_FILES} DESTINATION /lib/systemd/system/)
+install (FILES ${CONFIG_FILES} DESTINATION /usr/share/ipmbbridge/)
diff --git a/ipmb-channels.json b/ipmb-channels.json
new file mode 100644
index 0000000..ee4a373
--- /dev/null
+++ b/ipmb-channels.json
@@ -0,0 +1,18 @@
+{
+  "channels": [
+    {
+      "type": "me",
+      "master-path": "/dev/i2c-5",
+      "slave-path": "/sys/bus/i2c/devices/5-1010/slave-mqueue",
+      "bmc-addr": 32,
+      "remote-addr": 44
+    },
+    {
+      "type": "ipmb",
+      "master-path": "/dev/i2c-0",
+      "slave-path": "/sys/bus/i2c/devices/0-1010/slave-mqueue",
+      "bmc-addr": 32,
+      "remote-addr": 88
+    }
+  ]
+}
diff --git a/ipmbbridged.cpp b/ipmbbridged.cpp
index 72c2e1d..b37c867 100644
--- a/ipmbbridged.cpp
+++ b/ipmbbridged.cpp
@@ -18,8 +18,11 @@
 #include "ipmbdefines.hpp"
 #include "ipmbutils.hpp"
 
+#include <fstream>
+#include <nlohmann/json.hpp>
 #include <phosphor-logging/log.hpp>
 #include <tuple>
+#include <unordered_map>
 
 extern "C" {
 #include <i2c/smbus.h>
@@ -37,19 +40,10 @@
 boost::asio::io_service io;
 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
 
-/**
- * @brief Channel configuration table
- * TODO : move to user configuration as JSON file
- */
-static const std::vector<IpmbChannelConfig> ipmbChannelsConfig = {
-    // ME channel
-    {ipmbChannelType::me, "/sys/bus/i2c/devices/5-1010/slave-mqueue",
-     "/dev/i2c-5", 0x20, 0x2C}, // 8 bit addresses
-    // IPMB header channel
-    {ipmbChannelType::ipmb, "/sys/bus/i2c/devices/0-1010/slave-mqueue",
-     "/dev/i2c-0", 0x20, 0x58}}; // 8 bit addresses
-
 static std::list<IpmbChannel> ipmbChannels;
+static const std::unordered_map<std::string, ipmbChannelType>
+    ipmbChannelTypeMap = {{"me", ipmbChannelType::me},
+                          {"ipmb", ipmbChannelType::ipmb}};
 
 /**
  * @brief Ipmb request class methods
@@ -615,22 +609,50 @@
     std::shared_ptr<IpmbCommandFilter> commandFilter =
         std::make_shared<IpmbCommandFilter>();
 
-    for (const auto &channelConfig : ipmbChannelsConfig)
+    constexpr const char *configFilePath =
+        "/usr/share/ipmbbridge/ipmb-channels.json";
+    std::ifstream configFile(configFilePath);
+    if (!configFile.is_open())
     {
-        auto channel = ipmbChannels.emplace(ipmbChannels.end(), io,
-                                            channelConfig.ipmbBmcSlaveAddress,
-                                            channelConfig.ipmbRqSlaveAddress,
-                                            channelConfig.type, commandFilter);
-
-        if (channel->ipmbChannelInit(channelConfig.ipmbI2cSlave,
-                                     channelConfig.ipmbI2cMaster) < 0)
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "initializeChannels: Cannot open config path");
+        return -1;
+    }
+    try
+    {
+        auto data = nlohmann::json::parse(configFile, nullptr);
+        for (const auto &channelConfig : data["channels"])
         {
-            phosphor::logging::log<phosphor::logging::level::ERR>(
-                "initializeChannels: channel initialization failed");
-            return -1;
+            const std::string &typeConfig = channelConfig["type"];
+            const std::string &masterPath = channelConfig["master-path"];
+            const std::string &slavePath = channelConfig["slave-path"];
+            uint8_t bmcAddr = channelConfig["bmc-addr"];
+            uint8_t reqAddr = channelConfig["remote-addr"];
+            ipmbChannelType type = ipmbChannelTypeMap.at(typeConfig);
+
+            auto channel = ipmbChannels.emplace(ipmbChannels.end(), io, bmcAddr,
+                                                reqAddr, type, commandFilter);
+            if (channel->ipmbChannelInit(slavePath.c_str(),
+                                         masterPath.c_str()) < 0)
+            {
+                phosphor::logging::log<phosphor::logging::level::ERR>(
+                    "initializeChannels: channel initialization failed");
+                return -1;
+            }
         }
     }
-
+    catch (nlohmann::json::exception &e)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "initializeChannels: Error parsing config file");
+        return -1;
+    }
+    catch (std::out_of_range &e)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "initializeChannels: Error invalid type");
+        return -1;
+    }
     return 0;
 }
 
diff --git a/ipmbbridged.hpp b/ipmbbridged.hpp
index c139391..317fdab 100644
--- a/ipmbbridged.hpp
+++ b/ipmbbridged.hpp
@@ -145,18 +145,6 @@
     me = 1
 };
 
-/**
- * @brief Channel configuration structure
- */
-struct IpmbChannelConfig
-{
-    ipmbChannelType type;
-    const char *ipmbI2cSlave;
-    const char *ipmbI2cMaster;
-    uint8_t ipmbBmcSlaveAddress;
-    uint8_t ipmbRqSlaveAddress;
-};
-
 // TODO w/a to differentiate channel origin of incoming IPMI response:
 // extracting channel number from 2 oldest bits of seq
 constexpr ipmbChannelType getChannelFromSeq(const uint8_t &seq)