Add Host selector button interface

This change includes new button interface
for the host selector switch.

The  button handler code is adapted to support
both single host and multiple host power control
dbus events.

design : https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/45544

Signed-off-by: Naveen Moses <naveen.mosess@hcl.com>
Change-Id: Icbfb22baaee057fd255c3ab0cba129693b913a9d
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ff6638b..f6a9801 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,23 +10,32 @@
 set(POWER_DBUS_OBJECT_NAME "xyz/openbmc_project/Chassis/Buttons/Power")
 set(RESET_DBUS_OBJECT_NAME "xyz/openbmc_project/Chassis/Buttons/Reset")
 set(ID_DBUS_OBJECT_NAME "xyz/openbmc_project/Chassis/Buttons/ID")
+set(HS_DBUS_OBJECT_NAME "xyz/openbmc_project/Chassis/Buttons/HostSelector")
+
 set(GPIO_BASE_LABEL_NAME "1e780000.gpio")
 set(LONG_PRESS_TIME_MS 3000)
 set(CHASSIS_STATE_OBJECT_NAME "xyz/openbmc_project/state/chassis")
+set(CHASSISSYSTEM_STATE_OBJECT_NAME "xyz/openbmc_project/state/chassis_system")
+
 set(HOST_STATE_OBJECT_NAME "xyz/openbmc_project/state/host")
 set(ID_LED_GROUP "enclosure_identify" CACHE STRING "The identify LED group name")
 
 add_definitions(-DPOWER_DBUS_OBJECT_NAME="/${POWER_DBUS_OBJECT_NAME}0")
 add_definitions(-DRESET_DBUS_OBJECT_NAME="/${RESET_DBUS_OBJECT_NAME}0")
 add_definitions(-DID_DBUS_OBJECT_NAME="/${ID_DBUS_OBJECT_NAME}0")
+add_definitions(-DHS_DBUS_OBJECT_NAME="/${HS_DBUS_OBJECT_NAME}")
+
 add_definitions(-DGPIO_BASE_LABEL_NAME="${GPIO_BASE_LABEL_NAME}")
 add_definitions(-DLONG_PRESS_TIME_MS=${LONG_PRESS_TIME_MS})
-add_definitions(-DHOST_STATE_OBJECT_NAME="/${HOST_STATE_OBJECT_NAME}0")
-add_definitions(-DCHASSIS_STATE_OBJECT_NAME="/${CHASSIS_STATE_OBJECT_NAME}0")
+add_definitions(-DHOST_STATE_OBJECT_NAME="/${HOST_STATE_OBJECT_NAME}")
+add_definitions(-DCHASSIS_STATE_OBJECT_NAME="/${CHASSIS_STATE_OBJECT_NAME}")
+add_definitions(-DCHASSISSYSTEM_STATE_OBJECT_NAME="/${CHASSISSYSTEM_STATE_OBJECT_NAME}")
 
 set(SRC_FILES src/power_button.cpp
     src/reset_button.cpp
     src/id_button.cpp
+    src/hostSelector_switch.cpp
+
     src/main.cpp
     src/gpio.cpp
 )
diff --git a/inc/button_handler.hpp b/inc/button_handler.hpp
index 7340c2f..b3c65f5 100644
--- a/inc/button_handler.hpp
+++ b/inc/button_handler.hpp
@@ -7,7 +7,12 @@
 {
 namespace button
 {
-
+enum class PowerEvent
+{
+    powerPressed,
+    longPowerPressed,
+    resetPressed
+};
 /**
  * @class Handler
  *
@@ -80,14 +85,9 @@
      *
      * @return true if powered on, false else
      */
-    bool poweredOn() const;
+    bool poweredOn(size_t hostNumber) const;
 
-    /**
-     * @brief Returns the service name for an object
-     *
-     * @param[in] path - the object path
-     * @param[in] interface - the interface name
-     *
+    /*
      * @return std::string - the D-Bus service name if found, else
      *                       an empty string
      */
@@ -95,6 +95,32 @@
                            const std::string& interface) const;
 
     /**
+     * @brief gets the valid host selector value in multi host
+     * system
+     *
+     * @return size_t throws exception if host selector position is
+     * invalid or not available.
+     */
+
+    size_t getHostSelectorValue();
+
+    /**
+     * @brief checks if the system has multi host
+     * based on the host selector property availability
+     *
+     * @return bool returns true if multi host system
+     * else returns false.
+     */
+    bool isMultiHost();
+    /**
+     * @brief trigger the power ctrl event based on the
+     *  button press event type.
+     *
+     * @return void
+     */
+    void handlePowerEvent(PowerEvent powerEventType);
+
+    /**
      * @brief sdbusplus connection object
      */
     sdbusplus::bus::bus& bus;
diff --git a/inc/button_interface.hpp b/inc/button_interface.hpp
index 8648797..f37a29c 100644
--- a/inc/button_interface.hpp
+++ b/inc/button_interface.hpp
@@ -50,8 +50,9 @@
 
             ButtonIface* buttonIface = static_cast<ButtonIface*>(userdata);
             buttonIface->handleEvent(es, fd, revents);
-            return 0;
         }
+
+        return 0;
     }
 
     std::string getFormFactorType() const
diff --git a/inc/gpio.hpp b/inc/gpio.hpp
index ebaf3cf..a5b943a 100644
--- a/inc/gpio.hpp
+++ b/inc/gpio.hpp
@@ -15,6 +15,7 @@
 */
 #pragma once
 
+#include <nlohmann/json.hpp>
 #include <sdbusplus/bus.hpp>
 #include <string>
 #include <vector>
@@ -30,9 +31,9 @@
 // this struct represents button interface
 struct buttonConfig
 {
-    std::string formFactorName;  // name of the button interface
-    std::vector<gpioInfo> gpios; // holds single or group gpio config
-                                 // corresponding to button interface
+    std::string formFactorName;   // name of the button interface
+    std::vector<gpioInfo> gpios;  // holds single or group gpio config
+    nlohmann::json extraJsonInfo; // corresponding to button interface
 };
 
 /**
@@ -53,3 +54,5 @@
 
 uint32_t getGpioNum(const std::string& gpioPin);
 void closeGpio(int fd);
+// global json object which holds gpio_defs.json configs
+extern nlohmann::json gpioDefs;
diff --git a/inc/hostSelector_switch.hpp b/inc/hostSelector_switch.hpp
new file mode 100644
index 0000000..acdae19
--- /dev/null
+++ b/inc/hostSelector_switch.hpp
@@ -0,0 +1,76 @@
+#pragma once
+#include "button_factory.hpp"
+#include "button_interface.hpp"
+#include "common.hpp"
+#include "gpio.hpp"
+#include "xyz/openbmc_project/Chassis/Buttons/HostSelector/server.hpp"
+#include "xyz/openbmc_project/Chassis/Common/error.hpp"
+
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+
+static constexpr std::string_view HOST_SELECTOR = "HOST_SELECTOR";
+
+static constexpr auto INVALID_INDEX = std::numeric_limits<size_t>::max();
+
+enum class GpioState
+{
+    low,
+    high
+};
+
+class HostSelector final : public sdbusplus::server::object_t<
+                               sdbusplus::xyz::openbmc_project::Chassis::
+                                   Buttons::server::HostSelector>,
+                           public ButtonIface
+{
+  public:
+    HostSelector(sdbusplus::bus::bus& bus, const char* path, EventPtr& event,
+                 buttonConfig& buttonCfg) :
+        sdbusplus::server::object_t<sdbusplus::xyz::openbmc_project::Chassis::
+                                        Buttons::server::HostSelector>(
+            bus, path, action::defer_emit),
+        ButtonIface(bus, event, buttonCfg)
+    {
+        init();
+        // read and store the host selector position Map
+        hsPosMap = buttonCfg.extraJsonInfo.at("host_selector_map")
+                       .get<std::map<std::string, int>>();
+        maxPosition(buttonCfg.extraJsonInfo["max_position"], true);
+        gpioLineCount = buttonCfg.gpios.size();
+        setInitialHostSelectorValue();
+        emit_object_added();
+    }
+
+    ~HostSelector()
+    {
+        deInit();
+    }
+
+    static constexpr std::string_view getFormFactorName()
+    {
+        return HOST_SELECTOR;
+    }
+
+    static const char* getDbusObjectPath()
+    {
+        return HS_DBUS_OBJECT_NAME;
+    }
+    void handleEvent(sd_event_source* es, int fd, uint32_t revents) override;
+    size_t getMappedHSConfig(size_t hsPosition);
+    size_t getGpioIndex(int fd);
+    void setInitialHostSelectorValue(void);
+    void setHostSelectorValue(int fd, GpioState state);
+
+  protected:
+    size_t hostSelectorPosition = 0;
+    size_t gpioLineCount;
+
+    // map of read Host selector switch value and corresponding host number
+    // value.
+    std::map<std::string, int> hsPosMap;
+};
diff --git a/src/button_handler.cpp b/src/button_handler.cpp
index 6362f82..55f5db5 100644
--- a/src/button_handler.cpp
+++ b/src/button_handler.cpp
@@ -5,7 +5,6 @@
 #include <phosphor-logging/log.hpp>
 #include <xyz/openbmc_project/State/Chassis/server.hpp>
 #include <xyz/openbmc_project/State/Host/server.hpp>
-
 namespace phosphor
 {
 namespace button
@@ -15,18 +14,24 @@
 using namespace sdbusplus::xyz::openbmc_project::State::server;
 using namespace phosphor::logging;
 
-constexpr auto propertyIface = "org.freedesktop.DBus.Properties";
 constexpr auto chassisIface = "xyz.openbmc_project.State.Chassis";
 constexpr auto hostIface = "xyz.openbmc_project.State.Host";
 constexpr auto powerButtonIface = "xyz.openbmc_project.Chassis.Buttons.Power";
 constexpr auto idButtonIface = "xyz.openbmc_project.Chassis.Buttons.ID";
 constexpr auto resetButtonIface = "xyz.openbmc_project.Chassis.Buttons.Reset";
-constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
 constexpr auto ledGroupIface = "xyz.openbmc_project.Led.Group";
+constexpr auto ledGroupBasePath = "/xyz/openbmc_project/led/groups/";
+constexpr auto hostSelectorIface =
+    "xyz.openbmc_project.Chassis.Buttons.HostSelector";
+constexpr auto debugHostSelectorIface =
+    "xyz.openbmc_project.Chassis.Buttons.DebugHostSelector";
+
+constexpr auto propertyIface = "org.freedesktop.DBus.Properties";
+constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
 
 constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
 constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
-constexpr auto ledGroupBasePath = "/xyz/openbmc_project/led/groups/";
+constexpr auto BMC_POSITION = 0;
 
 Handler::Handler(sdbusplus::bus::bus& bus) : bus(bus)
 {
@@ -97,7 +102,11 @@
         // The button wasn't implemented
     }
 }
-
+bool Handler::isMultiHost()
+{
+    // return true in case host selector object is available
+    return (!getService(HS_DBUS_OBJECT_NAME, hostSelectorIface).empty());
+}
 std::string Handler::getService(const std::string& path,
                                 const std::string& interface) const
 {
@@ -111,12 +120,43 @@
 
     return objectData.begin()->first;
 }
-
-bool Handler::poweredOn() const
+size_t Handler::getHostSelectorValue()
 {
-    auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
+    auto HSService = getService(HS_DBUS_OBJECT_NAME, hostSelectorIface);
+
+    if (HSService.empty())
+    {
+        log<level::INFO>("Host Selector dbus object not available");
+        throw std::invalid_argument("Host selector dbus object not available");
+    }
+
+    try
+    {
+        auto method = bus.new_method_call(
+            HSService.c_str(), HS_DBUS_OBJECT_NAME, propertyIface, "Get");
+        method.append(hostSelectorIface, "Position");
+        auto result = bus.call(method);
+
+        std::variant<size_t> HSPositionVariant;
+        result.read(HSPositionVariant);
+
+        auto position = std::get<size_t>(HSPositionVariant);
+        return position;
+    }
+    catch (const sdbusplus::exception::exception& e)
+    {
+        log<level::ERR>("Error reading Host selector Position",
+                        entry("ERROR=%s", e.what()));
+        throw;
+    }
+}
+bool Handler::poweredOn(size_t hostNumber) const
+{
+    auto chassisObjectName =
+        CHASSIS_STATE_OBJECT_NAME + std::to_string(hostNumber);
+    auto service = getService(chassisObjectName.c_str(), chassisIface);
     auto method = bus.new_method_call(
-        service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Get");
+        service.c_str(), chassisObjectName.c_str(), propertyIface, "Get");
     method.append(chassisIface, "CurrentPowerState");
     auto result = bus.call(method);
 
@@ -127,27 +167,118 @@
            Chassis::convertPowerStateFromString(std::get<std::string>(state));
 }
 
-void Handler::powerPressed(sdbusplus::message::message& msg)
+void Handler::handlePowerEvent(PowerEvent powerEventType)
 {
-    auto transition = Host::Transition::On;
+    std::string objPathName;
+    std::string dbusIfaceName;
+    std::string transitionName;
+    std::variant<Host::Transition, Chassis::Transition> transition;
 
-    try
+    size_t hostNumber = 0;
+    auto isMultiHostSystem = isMultiHost();
+    if (isMultiHostSystem)
     {
-        if (poweredOn())
-        {
-            transition = Host::Transition::Off;
+        hostNumber = getHostSelectorValue();
+        log<level::INFO>("Multi host system detected : ",
+                         entry("POSITION=%d", hostNumber));
+    }
+
+    std::string hostNumStr = std::to_string(hostNumber);
+
+    // ignore  power and reset button events if BMC is selected.
+    if (isMultiHostSystem && (hostNumber == BMC_POSITION) &&
+        (powerEventType != PowerEvent::longPowerPressed))
+    {
+        log<level::INFO>("handlePowerEvent : BMC selected on multihost system."
+                         "ignoring power and reset button events...");
+        return;
+    }
+
+    switch (powerEventType)
+    {
+        case PowerEvent::powerPressed: {
+            objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
+            dbusIfaceName = hostIface;
+            transitionName = "RequestedHostTransition";
+
+            transition = Host::Transition::On;
+
+            if (poweredOn(hostNumber))
+            {
+                transition = Host::Transition::Off;
+            }
+            log<level::INFO>("handlePowerEvent : handle power button press ");
+
+            break;
+        }
+        case PowerEvent::longPowerPressed: {
+            dbusIfaceName = chassisIface;
+            transitionName = "RequestedPowerTransition";
+            objPathName = CHASSIS_STATE_OBJECT_NAME + hostNumStr;
+            transition = Chassis::Transition::Off;
+
+            /*  multi host system :
+                    hosts (1 to N) - host shutdown
+                    bmc (0) - sled cycle
+                single host system :
+                    host(0) - host shutdown
+            */
+            if (isMultiHostSystem && (hostNumber == BMC_POSITION))
+            {
+#if CHASSIS_SYSTEM_RESET_ENABLED
+                objPathName = CHASSISSYSTEM_STATE_OBJECT_NAME + hostNumStr;
+                transition = Chassis::Transition::PowerCycle;
+#else
+                return;
+#endif
+            }
+            else if (!poweredOn(hostNumber))
+            {
+                log<level::INFO>(
+                    "Power is off so ignoring long power button press");
+                return;
+            }
+            log<level::INFO>(
+                "handlePowerEvent : handle long power button press");
+
+            break;
         }
 
-        log<level::INFO>("Handling power button press");
+        case PowerEvent::resetPressed: {
 
-        std::variant<std::string> state = convertForMessage(transition);
+            objPathName = HOST_STATE_OBJECT_NAME + hostNumStr;
+            dbusIfaceName = hostIface;
+            transitionName = "RequestedHostTransition";
 
-        auto service = getService(HOST_STATE_OBJECT_NAME, hostIface);
-        auto method = bus.new_method_call(
-            service.c_str(), HOST_STATE_OBJECT_NAME, propertyIface, "Set");
-        method.append(hostIface, "RequestedHostTransition", state);
+            if (!poweredOn(hostNumber))
+            {
+                log<level::INFO>("Power is off so ignoring reset button press");
+                return;
+            }
 
-        bus.call(method);
+            log<level::INFO>("Handling reset button press");
+            transition = Host::Transition::Reboot;
+            break;
+        }
+        default: {
+            log<level::ERR>(
+                "Invalid power event. skipping...",
+                entry("EVENT=%d", static_cast<int>(powerEventType)));
+
+            return;
+        }
+    }
+    auto service = getService(objPathName.c_str(), dbusIfaceName);
+    auto method = bus.new_method_call(service.c_str(), objPathName.c_str(),
+                                      propertyIface, "Set");
+    method.append(dbusIfaceName, transitionName, transition);
+    bus.call(method);
+}
+void Handler::powerPressed(sdbusplus::message::message& msg)
+{
+    try
+    {
+        handlePowerEvent(PowerEvent::powerPressed);
     }
     catch (const sdbusplus::exception::exception& e)
     {
@@ -155,29 +286,11 @@
                         entry("ERROR=%s", e.what()));
     }
 }
-
 void Handler::longPowerPressed(sdbusplus::message::message& msg)
 {
     try
     {
-        if (!poweredOn())
-        {
-            log<level::INFO>(
-                "Power is off so ignoring long power button press");
-            return;
-        }
-
-        log<level::INFO>("Handling long power button press");
-
-        std::variant<std::string> state =
-            convertForMessage(Chassis::Transition::Off);
-
-        auto service = getService(CHASSIS_STATE_OBJECT_NAME, chassisIface);
-        auto method = bus.new_method_call(
-            service.c_str(), CHASSIS_STATE_OBJECT_NAME, propertyIface, "Set");
-        method.append(chassisIface, "RequestedPowerTransition", state);
-
-        bus.call(method);
+        handlePowerEvent(PowerEvent::longPowerPressed);
     }
     catch (const sdbusplus::exception::exception& e)
     {
@@ -190,24 +303,7 @@
 {
     try
     {
-        if (!poweredOn())
-        {
-            log<level::INFO>("Power is off so ignoring reset button press");
-            return;
-        }
-
-        log<level::INFO>("Handling reset button press");
-
-        std::variant<std::string> state =
-            convertForMessage(Host::Transition::Reboot);
-
-        auto service = getService(HOST_STATE_OBJECT_NAME, hostIface);
-        auto method = bus.new_method_call(
-            service.c_str(), HOST_STATE_OBJECT_NAME, propertyIface, "Set");
-
-        method.append(hostIface, "RequestedHostTransition", state);
-
-        bus.call(method);
+        handlePowerEvent(PowerEvent::resetPressed);
     }
     catch (const sdbusplus::exception::exception& e)
     {
diff --git a/src/hostSelector_switch.cpp b/src/hostSelector_switch.cpp
new file mode 100644
index 0000000..c3ad83f
--- /dev/null
+++ b/src/hostSelector_switch.cpp
@@ -0,0 +1,133 @@
+
+#include "hostSelector_switch.hpp"
+
+// add the button iface class to registry
+static ButtonIFRegister<HostSelector> buttonRegister;
+using namespace phosphor::logging;
+size_t HostSelector::getMappedHSConfig(size_t hsPosition)
+{
+    size_t adjustedPosition = INVALID_INDEX; // set bmc as default value
+    std::string hsPosStr;
+    hsPosStr = std::to_string(hsPosition);
+
+    if (hsPosMap.find(hsPosStr) != hsPosMap.end())
+    {
+        adjustedPosition = hsPosMap[hsPosStr];
+    }
+    else
+    {
+        log<level::DEBUG>(
+            "getMappedHSConfig : no valid value in map.",
+            entry("FORM_FACTOR_TYPE=%s", (getFormFactorType()).c_str()));
+    }
+    return adjustedPosition;
+}
+
+size_t HostSelector::getGpioIndex(int fd)
+{
+    for (size_t index = 0; index < gpioLineCount; index++)
+    {
+        if (config.gpios[index].fd == fd)
+        {
+            return index;
+        }
+    }
+    return INVALID_INDEX;
+}
+void HostSelector::setInitialHostSelectorValue()
+{
+    char buf;
+    for (int index = 0; index < gpioLineCount; index++)
+    {
+        auto result = ::lseek(config.gpios[index].fd, 0, SEEK_SET);
+
+        if (result < 0)
+        {
+            log<level::ERR>(
+                "gpio fd lseek error!",
+                entry("FORM_FACTOR_TYPE=%s", (getFormFactorType()).c_str()));
+            throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+                IOError();
+        }
+
+        result = ::read(config.gpios[index].fd, &buf, sizeof(buf));
+        if (result < 0)
+        {
+            log<level::ERR>(
+                "gpio fd read error!",
+                entry("FORM_FACTOR_TYPE=%s", (getFormFactorType()).c_str()));
+            throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+                IOError();
+        }
+        GpioState gpioState =
+            (buf == '0') ? (GpioState::low) : (GpioState::high);
+        setHostSelectorValue(config.gpios[index].fd, gpioState);
+        size_t hsPosMapped = getMappedHSConfig(hostSelectorPosition);
+        if (hsPosMapped != INVALID_INDEX)
+        {
+            position(hsPosMapped, true);
+        }
+    }
+}
+
+void HostSelector::setHostSelectorValue(int fd, GpioState state)
+{
+    size_t pos = getGpioIndex(fd);
+
+    if (pos == INVALID_INDEX)
+    {
+        return;
+    }
+    auto set_bit = [](size_t& val, size_t n) { val |= 0xff & (1 << n); };
+
+    auto clr_bit = [](size_t& val, size_t n) { val &= ~(0xff & (1 << n)); };
+
+    auto bit_op = (state == GpioState::low) ? set_bit : clr_bit;
+
+    bit_op(hostSelectorPosition, pos);
+    return;
+}
+/**
+ * @brief This method is called from sd-event provided callback function
+ * callbackHandler if platform specific event handling is needed then a
+ * derived class instance with its specific event handling logic along with
+ * init() function can be created to override the default event handling
+ */
+
+void HostSelector::handleEvent(sd_event_source* es, int fd, uint32_t revents)
+{
+    int n = -1;
+    char buf = '0';
+
+    n = ::lseek(fd, 0, SEEK_SET);
+
+    if (n < 0)
+    {
+        log<level::ERR>(
+            "gpio fd lseek error!",
+            entry("FORM_FACTOR_TYPE=%s", (getFormFactorType()).c_str()));
+        return;
+    }
+
+    n = ::read(fd, &buf, sizeof(buf));
+    if (n < 0)
+    {
+        log<level::ERR>(
+            "gpio fd read error!",
+            entry("FORM_FACTOR_TYPE=%s", (getFormFactorType()).c_str()));
+        throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+            IOError();
+    }
+
+    // read the gpio state for the io event received
+    GpioState gpioState = (buf == '0') ? (GpioState::low) : (GpioState::high);
+
+    setHostSelectorValue(fd, gpioState);
+
+    size_t hsPosMapped = getMappedHSConfig(hostSelectorPosition);
+
+    if (hsPosMapped != INVALID_INDEX)
+    {
+        position(hsPosMapped);
+    }
+}
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index cfa224e..e1efd3e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -22,6 +22,9 @@
 #include <phosphor-logging/elog-errors.hpp>
 static constexpr auto gpioDefFile = "/etc/default/obmc/gpio/gpio_defs.json";
 
+using namespace phosphor::logging;
+nlohmann::json gpioDefs;
+
 int main(int argc, char* argv[])
 {
     int ret = 0;
@@ -49,8 +52,8 @@
     std::vector<std::unique_ptr<ButtonIface>> buttonInterfaces;
 
     std::ifstream gpios{gpioDefFile};
-    auto json = nlohmann::json::parse(gpios, nullptr, true);
-    auto gpioDefs = json["gpio_definitions"];
+    auto gpioDefJson = nlohmann::json::parse(gpios, nullptr, true);
+    gpioDefs = gpioDefJson["gpio_definitions"];
 
     // load gpio config from gpio defs json file and create button interface
     // objects based on the button form factor type
@@ -60,6 +63,7 @@
         std::string formFactorName = gpioConfig["name"];
         buttonConfig buttonCfg;
         buttonCfg.formFactorName = formFactorName;
+        buttonCfg.extraJsonInfo = gpioConfig;
 
         /* The folloing code checks if the gpio config read
         from json file is single gpio config or group gpio config,