Add calls to the SBE FIFO device driver to perform the SBE
chip operations.

Change-Id: I2f1204efc8196e9d331789e7910f90537834ddde
Signed-off-by: Murulidhar Nataraju <murulidhar@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index cfaa17b..2c02937 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,8 @@
     sbe_interfaces.hpp
 
 noinst_HEADERS = \
-    sbe_chipOp_handler.hpp
+    sbe_chipOp_handler.hpp \
+    file.hpp
 
 libsbeinterfacedir = ${libdir}/sbe-interface
 libsbeinterface_LTLIBRARIES = libsbeinterface.la
diff --git a/file.hpp b/file.hpp
new file mode 100644
index 0000000..33df65f
--- /dev/null
+++ b/file.hpp
@@ -0,0 +1,72 @@
+#pragma once
+
+#include <unistd.h>
+namespace openpower
+{
+namespace sbe
+{
+namespace internal
+{
+
+/** @class FileDescriptor
+ *  @brief Provide RAII file descriptor
+ */
+class FileDescriptor
+{
+    private:
+        /** @brief File descriptor for the SBE FIFO device */
+        int fd = -1;
+
+    public:
+        FileDescriptor() = delete;
+        FileDescriptor(const FileDescriptor&) = delete;
+        FileDescriptor& operator=(const FileDescriptor&) = delete;
+        FileDescriptor(FileDescriptor&&) = delete;
+        FileDescriptor& operator=(FileDescriptor&&) = delete;
+
+        /** @brief Opens the input file and saves the file descriptor
+         *
+         *  @param[in] devPath - Path of the file
+         *  @para,[in] accessModes - File access modes
+         */
+        FileDescriptor(const char* devPath, int accessModes)
+        {
+            fd = open(devPath, accessModes);
+            if (fd < 0)
+            {
+                //TODO:use elog infrastructure
+                std::ostringstream errMsg;
+                errMsg << "Opening the device with device path:" <<
+                       devPath << " and access modes:" << accessModes <<
+                       ",Failed with errno" << errno;
+                throw std::runtime_error(errMsg.str().c_str());
+            }
+
+        }
+
+        /** @brief Saves File descriptor and uses it to do file operation
+         *
+         *  @param[in] fd - File descriptor
+         */
+        FileDescriptor(int fd) : fd(fd)
+        {
+            // Nothing
+        }
+
+        ~FileDescriptor()
+        {
+            if (fd >= 0)
+            {
+                close(fd);
+            }
+        }
+
+        int operator()()
+        {
+            return fd;
+        }
+};
+
+} // namespace internal
+} // namespace sbe
+} // namespace openpower
diff --git a/sbe_chipOp_handler.cpp b/sbe_chipOp_handler.cpp
index f662690..05d506f 100644
--- a/sbe_chipOp_handler.cpp
+++ b/sbe_chipOp_handler.cpp
@@ -1,49 +1,120 @@
-#include <stdexcept>
 #include <array>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <poll.h>
+#include <endian.h>
 #include <sbe_chipOp_handler.hpp>
+#include <file.hpp>
 namespace openpower
 {
 namespace sbe
 {
 namespace internal
 {
-/*! \union sbeRespHeader_t
- *  Defines the breakup of the SBE FIFO chip operation response header.
- */
-union sbeRespHeader_t
-{
-    uint32_t commandWord; /**<Entire 32 bit command word */
-    struct
-    {
-        uint8_t command: 8; /**<command value for which response is obtained */
-        uint8_t commandClass: 8; /**< Class of the command */
-        uint16_t magic: 16; /**< Magic code obtained in the response */
-    };
-} __attribute__((packed));
-
 
 constexpr uint16_t MAGIC_CODE = 0xC0DE;
 constexpr auto SBE_OPERATION_SUCCESSFUL = 0;
 constexpr auto LENGTH_OF_DISTANCE_HEADER_IN_WORDS = 0x1;
 constexpr auto LENGTH_OF_RESP_HEADER_IN_WORDS = 0x2;
-constexpr auto SBEI_SBE_RESPONSE_SIZE_IN_WORDS = ((sizeof(sbeRespHeader_t) + \
-        sizeof(sbe_word_t)) / 4);
 constexpr auto DISTANCE_TO_RESP_CODE = 0x1;
-
-std::vector<sbe_word_t> write(const char* devPath,
-                              const sbe_word_t* cmdBuffer,
-                              size_t cmdBufLen,
-                              size_t respBufLen)
+constexpr auto MAX_FFDC_LEN_IN_WORDS = 5120;
+constexpr auto WORD_SIZE = 4;
+constexpr auto MAGIC_CODE_BITS = 16;
+std::vector<sbe_word_t> writeToFifo(const char* devPath,
+                                    const sbe_word_t* cmdBuffer,
+                                    size_t cmdBufLen,
+                                    size_t respBufLen)
 {
-    //TODO: Add support for reading and writing from the FIFO device
-    std::vector<sbe_word_t> response(respBufLen);
+    size_t len = 0;
+    std::vector<sbe_word_t> response;
+    std::ostringstream errMsg;
+
+    //Open the device and obtain the file descriptor associated with it.
+    FileDescriptor fileFd(devPath, (O_RDWR | O_NONBLOCK));
+
+    //Wait for FIFO device and perform write operation
+    struct pollfd poll_fd = {};
+    poll_fd.fd = fileFd();
+    poll_fd.events = POLLOUT | POLLERR;
+
+    int rc = 0;
+    if ((rc = poll(&poll_fd, 1, -1)) < 0)
+    {
+        //TODO:use elog infrastructure
+        errMsg << "Waiting for FIFO device:" << devPath << "to write failed"
+               << "rc=" << rc << "errno=" << errno;
+        throw std::runtime_error(errMsg.str().c_str());
+    }
+    if (poll_fd.revents & POLLERR)
+    {
+        //TODO:use elog infrastructure
+        errMsg << "POLLERR while waiting for writeable FIFO,errno:" << errno;
+        throw std::runtime_error(errMsg.str().c_str());
+    }
+    auto bytesToWrite = (cmdBufLen * WORD_SIZE);
+    //Perform the write operation
+    len = write(fileFd(), cmdBuffer, bytesToWrite);
+    if (len < 0)
+    {
+        //TODO:use elog infrastructure
+        errMsg << "Failed to write to FIFO device:" << devPath << " Length "
+               "returned= " << len << " errno=" << errno;
+        throw std::runtime_error(errMsg.str().c_str());
+    }
+    //Wait for FIFO device and perform read operation
+    poll_fd.fd = fileFd();
+    poll_fd.events = POLLIN | POLLERR;
+    if ((rc = poll(&poll_fd, 1, -1) < 0))
+    {
+        //TODO:use elog infrastructure
+        errMsg << "Waiting for FIFO device:" << devPath << "to read failed"
+               << " rc=" << rc << " and errno=" << errno;
+        throw std::runtime_error(errMsg.str().c_str());
+    }
+    if (poll_fd.revents & POLLERR)
+    {
+        //TODO:use elog infrastructure
+        errMsg << "POLLERR while waiting for readable FIFO,errno:" << errno;
+        throw std::runtime_error(errMsg.str().c_str());
+    }
+    //Derive the total read length which should include the FFDC, which SBE
+    //returns in case of failure.
+    size_t totalReadLen = respBufLen + MAX_FFDC_LEN_IN_WORDS;
+    //Create a temporary buffer
+    std::vector<sbe_word_t> buffer(totalReadLen);
+
+    auto bytesToRead = (totalReadLen * WORD_SIZE);
+    len = read(fileFd(), buffer.data(), bytesToRead);
+    if (len < 0)
+    {
+        //TODO:use elog infrastructure
+        errMsg << "Failed to read the FIFO device:" << devPath << "bytes read ="
+               << len << " errno=" << errno;
+        throw std::runtime_error(errMsg.str().c_str());
+    }
+    else if (len != bytesToRead)
+    {
+        //TODO:use elog infrastructure
+        errMsg << "Exepcted " << bytesToRead << " bytes to be read from FIFO"
+               " device:" << devPath << ", but bytes read are " << len <<
+               " errno= " << errno;
+        throw std::runtime_error(errMsg.str().c_str());
+    }
+
+    //Extract the valid number of words read.
+    for (auto i = 0; i < (rc / WORD_SIZE); ++i)
+    {
+        response.push_back(be32toh(buffer[i]));
+    }
+
+    //Closing of the file descriptor will be handled when the FileDescriptor
+    //object will go out of scope.
     return response;
 }
 
-std::vector<sbe_word_t> parseResponse(
-    const std::vector<sbe_word_t>& sbeDataBuf)
+void parseResponse(std::vector<sbe_word_t>& sbeDataBuf)
 {
-
     //Number of 32-bit words obtained from the SBE
     size_t lengthObtained = sbeDataBuf.size();
 
@@ -62,22 +133,25 @@
     }
 
     //Fetch the response header contents
-    sbeRespHeader_t l_respHeader{};
     auto iter = sbeDataBuf.begin();
     std::advance(iter, (lengthObtained - distanceToStatusHeader));
-    l_respHeader.commandWord = *iter;
+
+    //First header word will have 2 bytes of MAGIC CODE followed by
+    //Command class and command type
+    //|  MAGIC BYTES:0xCODE | COMMAND-CLASS | COMMAND-TYPE|
+    sbe_word_t l_magicCode = (*iter >> MAGIC_CODE_BITS);
 
     //Fetch the primary and secondary response code
     std::advance(iter, DISTANCE_TO_RESP_CODE);
     auto l_priSecResp = *iter;
 
     //Validate the magic code obtained in the response
-    if (l_respHeader.magic != MAGIC_CODE)
+    if (l_magicCode != MAGIC_CODE)
     {
         //TODO:use elog infrastructure
         std::ostringstream errMsg;
         errMsg << "Invalid MAGIC keyword in the response header (" <<
-               l_respHeader.magic << "),expected keyword " << MAGIC_CODE;
+               l_magicCode << "),expected keyword " << MAGIC_CODE;
         throw std::runtime_error(errMsg.str().c_str());
     }
 
@@ -86,7 +160,7 @@
     {
         //Extract the SBE FFDC and throw it to the caller
         size_t ffdcLen = (distanceToStatusHeader -
-                          SBEI_SBE_RESPONSE_SIZE_IN_WORDS -
+                          LENGTH_OF_RESP_HEADER_IN_WORDS -
                           LENGTH_OF_DISTANCE_HEADER_IN_WORDS);
         if (ffdcLen)
         {
@@ -105,12 +179,15 @@
                << ".Length of FFDC data of obtained:" << ffdcLen;
         throw std::runtime_error(errMsg.str().c_str());
     }
-    return sbeDataBuf;
 
+    //In case of success, remove the response header content and send only the
+    //data.Response header will be towards the end of the buffer.
+    auto respLen = (lengthObtained - distanceToStatusHeader);
+    iter = sbeDataBuf.begin();
+    std::advance(iter,respLen);
+    sbeDataBuf.erase(iter, sbeDataBuf.end());
 }
 
 }
 }
 }
-
-
diff --git a/sbe_chipOp_handler.hpp b/sbe_chipOp_handler.hpp
index 565c6fd..c30a466 100644
--- a/sbe_chipOp_handler.hpp
+++ b/sbe_chipOp_handler.hpp
@@ -5,6 +5,7 @@
 #include <sstream>
 #include <algorithm>
 #include <vector>
+#include <sbe_interfaces.hpp>
 
 namespace openpower
 {
@@ -31,25 +32,26 @@
  *
  * @return Response buffer returned by the SBE for the input command.
  */
-std::vector<sbe_word_t> write(const char* devPath,
-                              const sbe_word_t* cmdBuffer,
-                              size_t cmdBufLen,
-                              size_t respBufLen);
+std::vector<sbe_word_t> writeToFifo(const char* devPath,
+                                    const sbe_word_t* cmdBuffer,
+                                    size_t cmdBufLen,
+                                    size_t respBufLen);
 
 /**
- * @brief Helper function for invokeSBEChipOperation(), to parse the data
- * obtained from the SBE. The header and SBE response will be verified and on
- * success the required data will be returned to the caller. SBE interface
- * failure will be conveyed via respective exceptions.
+ * @brief Helper function for invokeSBEChipOperation(), to parse and validate
+ * the data obtained from the SBE. Input buffer will be validated and on failure
+ * the FFDC content will be extracted and returned to the caller via
+ * respective exception. On success the input buffer will be modified to have
+ * only valid response data after removing the header content.
  *
  * Exceptions thrown for:
  * - SBE Internal failures
  *
- * @param[in] SBE data obtained from the SBE FIFO device
- * @return Valid chip operation response obtained by SBE.
+ * @param[in/out] On input  - SBE data obtained from the SBE FIFO device.
+ *                On output - Chip operation data after removing the response
+ *                            header.
  */
-std::vector<sbe_word_t> parseResponse(
-    const std::vector<sbe_word_t>& sbeDataBuf);
+void parseResponse(std::vector<sbe_word_t>& sbeDataBuf);
 
 }//end of internal namespace
 
@@ -74,25 +76,26 @@
                                    std::array<sbe_word_t, S2>& chipOpData)
 {
     //Write and read from the FIFO device.
-    auto sbeFifoResp = internal::write(devPath, request.data(), request.size(),
-                                       chipOpData.size());
+    auto sbeFifoResp = internal::writeToFifo(devPath, request.data(),
+                       request.size(), chipOpData.size());
 
     //Parse the obtained data
-    auto response = internal::parseResponse(sbeFifoResp);
-
-    if (response.size() != chipOpData.size())
+    internal::parseResponse(sbeFifoResp);
+    //Above interface would have stripped the SBE header content from the input
+    //response buffer.
+    if (sbeFifoResp.size() > chipOpData.size())
     {
         //TODO:use elog infrastructure
         std::ostringstream errMsg;
-        errMsg << "Obtained chip operation response length (" << response.size()
-               << "from SBE is not equal to the expected length of data (" <<
-               chipOpData.size();
+        errMsg << "Obtained chip operation response length (" <<
+               sbeFifoResp.size() << "from SBE is greater than maximum expected"
+               " lenght:" << chipOpData.size();
 
         throw std::runtime_error(errMsg.str().c_str());
     }
 
     //Move the contents of response buffer into the output buffer.
-    std::move(response.begin(), response.end(), chipOpData.begin());
+    std::move(sbeFifoResp.begin(), sbeFifoResp.end(), chipOpData.begin());
 }
 
 }
diff --git a/sbe_interfaces.cpp b/sbe_interfaces.cpp
index 28f0bb9..8575dad 100755
--- a/sbe_interfaces.cpp
+++ b/sbe_interfaces.cpp
@@ -1,6 +1,7 @@
 #include <iostream>
 #include <stdexcept>
 #include <array>
+#include <endian.h>
 #include "sbe_interfaces.hpp"
 #include "sbe_chipOp_handler.hpp"
 
@@ -46,13 +47,15 @@
         throw std::runtime_error("NULL FIFO device path");
     }
 
-    //Build SCOM read request command
+    //Build SCOM read request command.
+    //Handle byte order mismatch ,SBE is big endian and BMC is
+    //little endian.
     std::array<sbe_word_t, READ_CMD_LENGTH> command =
     {
         static_cast<sbe_word_t>(READ_CMD_LENGTH),
-        READ_OPCODE,
-        upper(address),
-        lower(address)
+        htobe32(READ_OPCODE),
+        htobe32(upper(address)),
+        htobe32(lower(address))
     };
 
     //Buffer to hold the response data along with the SBE header
@@ -79,14 +82,16 @@
     }
 
     //Build SCOM write request command
+    //Handle byte order mismatch, SBE is big endian and BMC is
+    //little endian.
     std::array<sbe_word_t, WRITE_CMD_LENGTH> command =
     {
         static_cast<sbe_word_t>(WRITE_CMD_LENGTH),
-        WRITE_OPCODE,
-        upper(address),
-        lower(address),
-        upper(data),
-        lower(data)
+        htobe32(WRITE_OPCODE),
+        htobe32(upper(address)),
+        htobe32(lower(address)),
+        htobe32(upper(data)),
+        htobe32(lower(data))
     };
 
     //Buffer to hold the SBE response status