Move i2c WR api into libipmid

Move the low-level i2c write-read api into libipmid,
to allow provider libraries access to i2c without duplicating this code.

Tested:
I2c master write read command still works:
ipmitool i2c bus=2 0x9c 8 0

Signed-off-by: Yong Li <yong.b.li@linux.intel.com>
Change-Id: I0d5f82cf46ecf871eebb47aae25537b5da1f2e6a
diff --git a/libipmid/utils.cpp b/libipmid/utils.cpp
index b8ba9a7..cc4b763 100644
--- a/libipmid/utils.cpp
+++ b/libipmid/utils.cpp
@@ -1,6 +1,12 @@
 #include <arpa/inet.h>
 #include <dirent.h>
+#include <fcntl.h>
+#include <linux/i2c-dev.h>
+#include <linux/i2c.h>
 #include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <algorithm>
 #include <chrono>
@@ -541,4 +547,63 @@
 }
 
 } // namespace network
+
+ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t slaveAddr,
+                      std::vector<uint8_t> writeData,
+                      std::vector<uint8_t>& readBuf)
+{
+    // Open the i2c device, for low-level combined data write/read
+    int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
+    if (i2cDev < 0)
+    {
+        log<level::ERR>("Failed to open i2c bus",
+                        phosphor::logging::entry("BUS=%s", i2cBus.c_str()));
+        return ipmi::ccInvalidFieldRequest;
+    }
+
+    const size_t writeCount = writeData.size();
+    const size_t readCount = readBuf.size();
+    int msgCount = 0;
+    i2c_msg i2cmsg[2] = {0};
+    if (writeCount)
+    {
+        // Data will be writtern to the slave address
+        i2cmsg[msgCount].addr = slaveAddr;
+        i2cmsg[msgCount].flags = 0x00;
+        i2cmsg[msgCount].len = writeCount;
+        i2cmsg[msgCount].buf = writeData.data();
+        msgCount++;
+    }
+    if (readCount)
+    {
+        // Data will be read into the buffer from the slave address
+        i2cmsg[msgCount].addr = slaveAddr;
+        i2cmsg[msgCount].flags = I2C_M_RD;
+        i2cmsg[msgCount].len = readCount;
+        i2cmsg[msgCount].buf = readBuf.data();
+        msgCount++;
+    }
+
+    i2c_rdwr_ioctl_data msgReadWrite = {0};
+    msgReadWrite.msgs = i2cmsg;
+    msgReadWrite.nmsgs = msgCount;
+
+    // Perform the combined write/read
+    int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
+    ::close(i2cDev);
+
+    if (ret < 0)
+    {
+        log<level::ERR>("I2C WR Failed!",
+                        phosphor::logging::entry("RET=%d", ret));
+        return ipmi::ccUnspecifiedError;
+    }
+    if (readCount)
+    {
+        readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
+    }
+
+    return ipmi::ccSuccess;
+}
+
 } // namespace ipmi