Add serial uart mux interface class

1. This change adds the multi-host serial uart mux interface class.

In a multi-host system when the host position is changed,
then the serial uart mux is configured to enable  the
respective host's serial console which is accessed via
OCP debug card.

2. Introduced two new methods in gpio.cpp
   setGpioState - set state for gpio fd based on polarity
   getGpiostate - get state for gpio fd based on polarity

3. Updated the readme file about details on the new gpio
configs

design : https://github.com/openbmc/docs/blob/master/designs/multihost-phosphor-buttons.md

Testing : This change is verified in yosemiteV2 platform.

Signed-off-by: Naveen Moses <naveen.mosess@hcl.com>
Change-Id: I861d70570650d7dfcab842a35bdcf63a9fdd3bd0
diff --git a/src/gpio.cpp b/src/gpio.cpp
index 9f8b98d..0a16ce5 100644
--- a/src/gpio.cpp
+++ b/src/gpio.cpp
@@ -30,9 +30,59 @@
 #include <fstream>
 
 const std::string gpioDev = "/sys/class/gpio";
-
 namespace fs = std::filesystem;
+std::unordered_map<GpioPolarity, GPIOBufferValue> GpioValueMap = {
+    {GpioPolarity::activeLow, {'0', '1'}},
+    {GpioPolarity::activeHigh, {'1', '0'}}};
 
+void setGpioState(int fd, GpioPolarity polarity, GpioState state)
+{
+    char writeBuffer;
+
+    if (state == GpioState::assert)
+    {
+        writeBuffer = GpioValueMap[polarity].assert;
+    }
+    else
+    {
+        writeBuffer = GpioValueMap[polarity].deassert;
+    }
+
+    auto result = ::write(fd, &writeBuffer, sizeof(writeBuffer));
+    if (result < 0)
+    {
+        lg2::error("GPIO write error {GPIOFD} : {ERRORNO}", "GPIOFD", fd,
+                   "ERRORNO", errno);
+    }
+    return;
+}
+GpioState getGpioState(int fd, GpioPolarity polarity)
+{
+    int result = -1;
+    char readBuffer = '0';
+
+    result = ::lseek(fd, 0, SEEK_SET);
+
+    if (result < 0)
+    {
+        lg2::error("GPIO lseek error {GPIOFD}: {ERROR}", "GPIOFD", fd, "ERROR",
+                   errno);
+        return GpioState::invalid;
+    }
+
+    result = ::read(fd, &readBuffer, sizeof(readBuffer));
+    if (result < 0)
+    {
+        lg2::error("GPIO read error {GPIOFD}: {ERRORNO}", "GPIOFD", fd,
+                   "ERRORNO", errno);
+        throw std::runtime_error("GPIO read failed");
+    }
+    // read the gpio state for the io event received
+    GpioState gpioState = (readBuffer == GpioValueMap[polarity].assert)
+                              ? (GpioState::assert)
+                              : (GpioState::deassert);
+    return gpioState;
+}
 void closeGpio(int fd)
 {
     if (fd > 0)
diff --git a/src/hostSelector_switch.cpp b/src/hostSelector_switch.cpp
index 66b79c8..efbccd2 100644
--- a/src/hostSelector_switch.cpp
+++ b/src/hostSelector_switch.cpp
@@ -61,7 +61,7 @@
                 IOError();
         }
         GpioState gpioState =
-            (buf == '0') ? (GpioState::low) : (GpioState::high);
+            (buf == '0') ? (GpioState::deassert) : (GpioState::assert);
         setHostSelectorValue(config.gpios[index].fd, gpioState);
         size_t hsPosMapped = getMappedHSConfig(hostSelectorPosition);
         if (hsPosMapped != INVALID_INDEX)
@@ -83,7 +83,7 @@
 
     auto clr_bit = [](size_t& val, size_t n) { val &= ~(0xff & (1 << n)); };
 
-    auto bit_op = (state == GpioState::low) ? set_bit : clr_bit;
+    auto bit_op = (state == GpioState::deassert) ? set_bit : clr_bit;
 
     bit_op(hostSelectorPosition, pos);
     return;
@@ -120,7 +120,8 @@
     }
 
     // read the gpio state for the io event received
-    GpioState gpioState = (buf == '0') ? (GpioState::low) : (GpioState::high);
+    GpioState gpioState =
+        (buf == '0') ? (GpioState::deassert) : (GpioState::assert);
 
     setHostSelectorValue(fd, gpioState);
 
diff --git a/src/main.cpp b/src/main.cpp
index 16ae7b0..ecb9402 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -47,7 +47,6 @@
         bus, "/xyz/openbmc_project/Chassis/Buttons"};
 
     bus.request_name("xyz.openbmc_project.Chassis.Buttons");
-    //
     std::vector<std::unique_ptr<ButtonIface>> buttonInterfaces;
 
     std::ifstream gpios{gpioDefFile};
@@ -78,6 +77,10 @@
                 gpioInfo gpioCfg;
                 gpioCfg.number = getGpioNum(config["pin"]);
                 gpioCfg.direction = config["direction"];
+                gpioCfg.name = config["name"];
+                gpioCfg.polarity = (config["polarity"] == "active_high")
+                                       ? GpioPolarity::activeHigh
+                                       : GpioPolarity::activeLow;
                 buttonCfg.gpios.push_back(gpioCfg);
             }
         }
diff --git a/src/serial_uart_mux.cpp b/src/serial_uart_mux.cpp
new file mode 100644
index 0000000..00724dd
--- /dev/null
+++ b/src/serial_uart_mux.cpp
@@ -0,0 +1,112 @@
+#include "serial_uart_mux.hpp"
+
+#include "xyz/openbmc_project/Chassis/Buttons/HostSelector/client.hpp"
+#include "xyz/openbmc_project/Chassis/Buttons/HostSelector/server.hpp"
+
+#include <error.h>
+
+#include <phosphor-logging/lg2.hpp>
+namespace sdbusRule = sdbusplus::bus::match::rules;
+// add the button iface class to registry
+static ButtonIFRegister<SerialUartMux> buttonRegister;
+namespace HostSelectorServerObj =
+    sdbusplus::xyz::openbmc_project::Chassis::Buttons::server;
+namespace HostSelectorClientObj =
+    sdbusplus::xyz::openbmc_project::Chassis::Buttons::client::HostSelector;
+
+constexpr std::string_view SERIAL_UART_RX_GPIO = "serial_uart_rx";
+void SerialUartMux::init()
+{
+    try
+    {
+        // when Host Selector Position is changed call the handler
+
+        std::string matchPattern = sdbusRule::propertiesChanged(
+            HS_DBUS_OBJECT_NAME, HostSelectorClientObj::interface);
+
+        hostPositionChanged = std::make_unique<sdbusplus::bus::match_t>(
+            bus, matchPattern,
+            std::bind(std::mem_fn(&SerialUartMux::hostSelectorPositionChanged),
+                      this, std::placeholders::_1));
+    }
+    catch (const std::exception& e)
+    {
+        lg2::error(
+            "Failed binding to matching function : {BUTTON_TYPE},Exception : {ERROR}",
+            "BUTTON_TYPE", getFormFactorName(), "ERROR", e);
+        throw sdbusplus::xyz::openbmc_project::Chassis::Common::Error::
+            IOError();
+    }
+}
+// check the debug card present pin
+bool SerialUartMux::isOCPDebugCardPresent()
+{
+    auto gpioState =
+        getGpioState(debugCardPresentGpio.fd, debugCardPresentGpio.polarity);
+    return (gpioState == GpioState::assert);
+}
+// set the serial uart MUX to select the console w.r.t host selector position
+void SerialUartMux::configSerialConsoleMux(size_t position)
+{
+    auto debugCardPresent = isOCPDebugCardPresent();
+
+    if (debugCardPresent)
+    {
+        lg2::info("Debug card is present ");
+    }
+    else
+    {
+        lg2::info("Debug card not present ");
+    }
+
+    for (size_t uartMuxSel = 0; uartMuxSel < gpioLineCount; uartMuxSel++)
+    {
+        auto gpioState = GpioState::invalid;
+        gpioInfo gpioConfig = config.gpios[uartMuxSel];
+
+        if (gpioConfig.name == SERIAL_UART_RX_GPIO)
+        {
+            gpioState =
+                debugCardPresent ? GpioState::assert : GpioState::deassert;
+        }
+        else
+        {
+            gpioState = (serialUartMuxMap[position] & (0x1 << uartMuxSel))
+                            ? GpioState::assert
+                            : GpioState::deassert;
+        }
+        setGpioState(gpioConfig.fd, gpioConfig.polarity, gpioState);
+    }
+}
+
+void SerialUartMux::hostSelectorPositionChanged(sdbusplus::message_t& msg)
+{
+    std::string interface;
+    std::map<std::string,
+             HostSelectorServerObj::HostSelector::PropertiesVariant>
+        propertiesChanged;
+    lg2::info("hostSelectorPositionChanged callback : {BUTTON_TYPE}",
+              "BUTTON_TYPE", getFormFactorName());
+
+    try
+    {
+        msg.read(interface, propertiesChanged);
+        for (auto& property : propertiesChanged)
+        {
+            auto propertyName = property.first;
+            if (propertyName == "Position")
+            {
+                size_t hostPosition = std::get<size_t>(property.second);
+                lg2::debug("property changed : {VALUE}", "VALUE", hostPosition);
+                configSerialConsoleMux(hostPosition);
+                return;
+            }
+        }
+    }
+    catch (const std::exception& e)
+    {
+        lg2::error("exception while reading dbus property : {ERROR}", "ERROR",
+                   e.what());
+        return;
+    }
+}