Add 16 bit addressing EEPROM handling

Adding 16 bit eeprom addressing for reading data. First checking if
device is 8 bit or 16 bit by reading first 8 byte header and comparing
each byte. If all bytes are same then it is 8 bit or if address is auto
incremented then it is 16 bit.

Tested: Build Facebook TiogaPass board and load on the
target hardware. Ensure that Fru information are updated.

Change-Id: I1ec848764afb012562c1231f45e42f37e22334b8
Signed-off-by: Vijay Khemka <vijaykhemka@fb.com>
diff --git a/src/FruDevice.cpp b/src/FruDevice.cpp
index f879afd..fd5f5ae 100644
--- a/src/FruDevice.cpp
+++ b/src/FruDevice.cpp
@@ -63,12 +63,62 @@
         "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device"));
 }
 
+static int isDevice16Bit(int file)
+{
+    /* Get first byte */
+    int byte1 = i2c_smbus_read_byte_data(file, 0);
+    if (byte1 < 0)
+    {
+        return byte1;
+    }
+    /* Read 7 more bytes, it will read same first byte in case of
+     * 8 bit but it will read next byte in case of 16 bit
+     */
+    for (int i = 0; i < 7; i++)
+    {
+        int byte2 = i2c_smbus_read_byte_data(file, 0);
+        if (byte2 < 0)
+        {
+            return byte2;
+        }
+        if (byte2 != byte1)
+        {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int read_block_data(int flag, int file, uint16_t offset, uint8_t len,
+                           uint8_t *buf)
+{
+    uint8_t low_addr = offset & 0xFF;
+    uint8_t high_addr = (offset >> 8) & 0xFF;
+
+    if (flag == 0)
+    {
+        return i2c_smbus_read_i2c_block_data(file, low_addr, len, buf);
+    }
+
+    /* This is for 16 bit addressing EEPROM device. First an offset
+     * needs to be written before read data from a offset
+     */
+    int ret = i2c_smbus_write_byte_data(file, 0, low_addr);
+    if (ret < 0)
+    {
+        return ret;
+    }
+
+    return i2c_smbus_read_i2c_block_data(file, high_addr, len, buf);
+}
+
 int get_bus_frus(int file, int first, int last, int bus,
                  std::shared_ptr<DeviceMap> devices)
 {
 
     std::future<int> future = std::async(std::launch::async, [&]() {
         std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> block_data;
+
         for (int ii = first; ii <= last; ii++)
         {
 
@@ -90,8 +140,17 @@
                 std::cout << "something at bus " << bus << "addr " << ii
                           << "\n";
             }
-            if (i2c_smbus_read_i2c_block_data(file, 0x0, 0x8,
-                                              block_data.data()) < 0)
+
+            /* Check for Device type if it is 8 bit or 16 bit */
+            int flag = isDevice16Bit(file);
+            if (flag < 0)
+            {
+                std::cerr << "failed to read bus " << bus << " address " << ii
+                          << "\n";
+                continue;
+            }
+
+            if (read_block_data(flag, file, 0x0, 0x8, block_data.data()) < 0)
             {
                 std::cerr << "failed to read bus " << bus << " address " << ii
                           << "\n";
@@ -117,8 +176,8 @@
                     if (area_offset != 0)
                     {
                         area_offset *= 8;
-                        if (i2c_smbus_read_i2c_block_data(
-                                file, area_offset, 0x8, block_data.data()) < 0)
+                        if (read_block_data(flag, file, area_offset, 0x8,
+                                            block_data.data()) < 0)
                         {
                             std::cerr << "failed to read bus " << bus
                                       << " address " << ii << "\n";
@@ -133,9 +192,8 @@
                         while (length > 0)
                         {
                             auto to_get = std::min(0x20, length);
-                            if (i2c_smbus_read_i2c_block_data(
-                                    file, area_offset, to_get,
-                                    block_data.data()) < 0)
+                            if (read_block_data(flag, file, area_offset, to_get,
+                                                block_data.data()) < 0)
                             {
                                 std::cerr << "failed to read bus " << bus
                                           << " address " << ii << "\n";