i2c: Implement read function

Implement I2CDevice::read() by invoking i2c_smbus_read_xxx() APIs.
The code is referenced from i2c-tools' i2cget.c:

 https://github.com/ev3dev/i2c-tools/blob/ev3dev-stretch/tools/i2cget.c

Tested: Verify on Witherspoon that it reads the PSU ppgrade mode status
        register (1 byte) and CRC16 register (2 bytes) correctly.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I8759b6a35229f81120acf77f08429f7f79458b8b
diff --git a/tools/i2c/i2c.cpp b/tools/i2c/i2c.cpp
index aa565b4..da121d8 100644
--- a/tools/i2c/i2c.cpp
+++ b/tools/i2c/i2c.cpp
@@ -1,11 +1,19 @@
 #include "i2c.hpp"
 
 #include <fcntl.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <cassert>
 #include <cerrno>
 
+extern "C" {
+#include <i2c/smbus.h>
+#include <linux/i2c-dev.h>
+#include <linux/i2c.h>
+}
+
 namespace i2c
 {
 
@@ -16,6 +24,11 @@
     {
         throw I2CException("Failed to open", busStr, devAddr, errno);
     }
+
+    if (ioctl(fd, I2C_SLAVE, devAddr) < 0)
+    {
+        throw I2CException("Failed to set I2C_SLAVE", busStr, devAddr, errno);
+    }
 }
 
 void I2CDevice::close()
@@ -23,32 +36,98 @@
     ::close(fd);
 }
 
+void I2CDevice::checkReadFuncs(int type)
+{
+    unsigned long funcs;
+
+    /* Check adapter functionality */
+    if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
+    {
+        throw I2CException("Failed to get funcs", busStr, devAddr, errno);
+    }
+
+    switch (type)
+    {
+        case I2C_SMBUS_BYTE:
+            if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
+            {
+                throw I2CException("Missing SMBUS_READ_BYTE", busStr, devAddr);
+            }
+            break;
+        case I2C_SMBUS_BYTE_DATA:
+            if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
+            {
+                throw I2CException("Missing SMBUS_READ_BYTE_DATA", busStr,
+                                   devAddr);
+            }
+            break;
+
+        case I2C_SMBUS_WORD_DATA:
+            if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
+            {
+                throw I2CException("Missing SMBUS_READ_WORD_DATA", busStr,
+                                   devAddr);
+            }
+            break;
+        case I2C_SMBUS_BLOCK_DATA:
+            if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
+            {
+                throw I2CException("Missing SMBUS_READ_BLOCK_DATA", busStr,
+                                   devAddr);
+            }
+            break;
+        default:
+            fprintf(stderr, "Unexpected read size type: %d\n", type);
+            assert(false);
+            break;
+    }
+}
+
 void I2CDevice::read(uint8_t& data)
 {
-    // TODO
-    (void)data;
+    checkReadFuncs(I2C_SMBUS_BYTE);
+
+    int ret = i2c_smbus_read_byte(fd);
+    if (ret < 0)
+    {
+        throw I2CException("Failed to read byte", busStr, devAddr, errno);
+    }
+    data = static_cast<uint8_t>(ret);
 }
 
 void I2CDevice::read(uint8_t addr, uint8_t& data)
 {
-    // TODO
-    (void)addr;
-    (void)data;
+    checkReadFuncs(I2C_SMBUS_BYTE_DATA);
+
+    int ret = i2c_smbus_read_byte_data(fd, addr);
+    if (ret < 0)
+    {
+        throw I2CException("Failed to read byte data", busStr, devAddr, errno);
+    }
+    data = static_cast<uint8_t>(ret);
 }
 
 void I2CDevice::read(uint8_t addr, uint16_t& data)
 {
-    // TODO
-    (void)addr;
-    (void)data;
+    checkReadFuncs(I2C_SMBUS_WORD_DATA);
+    int ret = i2c_smbus_read_word_data(fd, addr);
+    if (ret < 0)
+    {
+        throw I2CException("Failed to read word data", busStr, devAddr, errno);
+    }
+    data = static_cast<uint16_t>(ret);
 }
 
 void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data)
 {
-    // TODO
-    (void)addr;
-    (void)size;
-    (void)data;
+    checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
+
+    int ret = i2c_smbus_read_block_data(fd, addr, data);
+    if (ret < 0)
+    {
+        throw I2CException("Failed to read block data", busStr, devAddr, errno);
+    }
+    size = static_cast<uint8_t>(ret);
 }
 
 void I2CDevice::write(uint8_t data)