Add GPIO pin description

Added functionality to read gpio pin code description from
gpio_desc.json file stored in /usr/share/ipmi-providers/.
This file is platform specific and can be overwritten for
different platforms. This gpio pin description will be
requested by lcd debug card for displaying in screen.

Tested: verified with lcd debug card screen.

Change-Id: I32275a5d218cbfdd20cd919768688ab6cc67b091
Signed-off-by: Vijay Khemka <vijaykhemka@fb.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d54c71d..62e3e4d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -74,7 +74,7 @@
 target_link_libraries (zfboemcmds phosphor_logging)
 
 set (PACKAGE_DIR /usr/share/ipmi-providers/)
-set (CONFIG_FILES post_desc.json)
+set (CONFIG_FILES post_desc.json gpio_desc.json)
 
 install (TARGETS zfboemcmds DESTINATION lib/ipmid-providers)
 install (FILES ${CONFIG_FILES} DESTINATION ${PACKAGE_DIR})
diff --git a/gpio_desc.json b/gpio_desc.json
new file mode 100644
index 0000000..97cf81b
--- /dev/null
+++ b/gpio_desc.json
@@ -0,0 +1,13 @@
+
+{
+ "GpioDesc": [
+  [ "0x10", 0, 2, "FM_DBG_RST_BTN" ],
+  [ "0x11", 0, 1, "FM_PWR_BTN" ],
+  [ "0x12", 1, 0, "SYS_PWROK" ],
+  [ "0x13", 0, 0, "RST_PLTRST" ],
+  [ "0x14", 1, 0, "DSW_PWROK" ],
+  [ "0x15", 0, 0, "FM_CATERR_MSMI" ],
+  [ "0x16", 0, 0, "FM_SLPS3" ],
+  [ "0x17", 0, 3, "FM_UART_SWITCH" ]
+]
+}
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
index b628c7b..9fbcc90 100644
--- a/src/oemcommands.cpp
+++ b/src/oemcommands.cpp
@@ -46,6 +46,8 @@
 
 int plat_udbg_get_post_desc(uint8_t, uint8_t *, uint8_t, uint8_t *, uint8_t *,
                             uint8_t *);
+int plat_udbg_get_gpio_desc(uint8_t, uint8_t *, uint8_t *, uint8_t *, uint8_t *,
+                            uint8_t *);
 ipmi_ret_t plat_udbg_get_frame_data(uint8_t, uint8_t, uint8_t *, uint8_t *,
                                     uint8_t *);
 ipmi_ret_t plat_udbg_control_panel(uint8_t, uint8_t, uint8_t, uint8_t *,
@@ -481,11 +483,31 @@
     uint8_t *req = reinterpret_cast<uint8_t *>(request);
     uint8_t *res = reinterpret_cast<uint8_t *>(response);
 
-    phosphor::logging::log<phosphor::logging::level::INFO>(
-        "Get GPIO Description Event");
+    uint8_t index = 0;
+    uint8_t next = 0;
+    uint8_t level = 0;
+    uint8_t pinDef = 0;
+    uint8_t descLen = 0;
+    int ret;
 
-    std::memcpy(res, req, SIZE_IANA_ID + 1); // IANA ID
-    *data_len = SIZE_IANA_ID + 1;
+    index = req[3];
+
+    ret = plat_udbg_get_gpio_desc(index, &next, &level, &pinDef, &descLen,
+                                  &res[8]);
+    if (ret)
+    {
+        memcpy(res, req, SIZE_IANA_ID); // IANA ID
+        *data_len = SIZE_IANA_ID;
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+    memcpy(res, req, SIZE_IANA_ID); // IANA ID
+    res[3] = index;
+    res[4] = next;
+    res[5] = level;
+    res[6] = pinDef;
+    res[7] = descLen;
+    *data_len = SIZE_IANA_ID + 5 + descLen;
 
     return IPMI_CC_OK;
 }
diff --git a/src/usb-dbg.cpp b/src/usb-dbg.cpp
index d61c9b2..630b7b7 100644
--- a/src/usb-dbg.cpp
+++ b/src/usb-dbg.cpp
@@ -29,6 +29,7 @@
 {
 
 #define JSON_POST_DATA_FILE "/usr/share/ipmi-providers/post_desc.json"
+#define JSON_GPIO_DATA_FILE "/usr/share/ipmi-providers/gpio_desc.json"
 #define ETH_INTF_NAME "eth0"
 
 #define ESCAPE "\x1B"
@@ -45,6 +46,13 @@
 #define FRU_ALL 0
 #define MAX_VALUE_LEN 64
 
+#define DEBUG_GPIO_KEY "GpioDesc"
+#define GPIO_ARRAY_SIZE 4
+#define GPIO_PIN_INDEX 0
+#define GPIO_LEVEL_INDEX 1
+#define GPIO_DEF_INDEX 2
+#define GPIO_DESC_INDEX 3
+
 /* Used for systems which do not specifically have a
  * phase, and we want to ignore the phase provided by the
  * debug card */
@@ -53,14 +61,6 @@
 ipmi_ret_t getNetworkData(uint8_t lan_param, char *data);
 int8_t getFruData(std::string &serial, std::string &name);
 
-typedef struct _gpio_desc
-{
-    uint8_t pin;
-    uint8_t level;
-    uint8_t def;
-    char desc[32];
-} gpio_desc_t;
-
 typedef struct _sensor_desc
 {
     char name[16];
@@ -587,9 +587,76 @@
 
 /* Need to implement this */
 int plat_udbg_get_gpio_desc(uint8_t index, uint8_t *next, uint8_t *level,
-                            uint8_t *def, uint8_t *count, uint8_t *buffer)
+                            uint8_t *def, uint8_t *length, uint8_t *buffer)
 {
-    return 0;
+    nlohmann::json gpioObj;
+    std::string gpioPin;
+
+    /* Get gpio data stored in json file */
+    std::ifstream file(JSON_GPIO_DATA_FILE);
+    if (file)
+    {
+        file >> gpioObj;
+        file.close();
+    }
+    else
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "GPIO pin description file not found",
+            phosphor::logging::entry("GPIO_PIN_DETAILS_FILE=%s",
+                                     JSON_GPIO_DATA_FILE));
+        return -1;
+    }
+
+    if (gpioObj.find(DEBUG_GPIO_KEY) == gpioObj.end())
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "GPIO pin details not available",
+            phosphor::logging::entry("GPIO_JSON_KEY=%d", DEBUG_GPIO_KEY));
+        return -1;
+    }
+
+    auto obj = gpioObj[DEBUG_GPIO_KEY];
+    int objSize = obj.size();
+
+    for (int i = 0; i < objSize; i++)
+    {
+        if (obj[i].size() != GPIO_ARRAY_SIZE)
+        {
+            phosphor::logging::log<phosphor::logging::level::ERR>(
+                "Size of gpio array is incorrect",
+                phosphor::logging::entry("EXPECTED_SIZE=%d", GPIO_ARRAY_SIZE));
+            return -1;
+        }
+
+        gpioPin = obj[i][GPIO_PIN_INDEX];
+        if (index == stoul(gpioPin, nullptr, 16))
+        {
+            if (objSize != i + 1)
+            {
+                gpioPin = obj[i + 1][GPIO_PIN_INDEX];
+                *next = stoul(gpioPin, nullptr, 16);
+            }
+            else
+            {
+                *next = 0xff;
+            }
+
+            *level = obj[i][GPIO_LEVEL_INDEX];
+            *def = obj[i][GPIO_DEF_INDEX];
+            std::string gpioDesc = obj[i][GPIO_DESC_INDEX];
+            *length = gpioDesc.size();
+            memcpy(buffer, gpioDesc.data(), *length);
+            buffer[*length] = '\0';
+
+            return 0;
+        }
+    }
+
+    phosphor::logging::log<phosphor::logging::level::ERR>(
+        "GPIO pin description data not available",
+        phosphor::logging::entry("GPIO_PIN=0x%x", index));
+    return -1;
 }
 
 static int udbg_get_cri_sel(uint8_t frame, uint8_t page, uint8_t *next,