i2c: Implement open and close

Implement open() and close() function, and invoke open() on creating
I2CDevice, and invoke close() in destructor.

Tested: Manually write test codet and run on Witherspoon that it opens
        and closes the i2c device correctly.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I709fcc80474a4a0cef067748a78256ceb76430a5
diff --git a/tools/i2c/i2c.cpp b/tools/i2c/i2c.cpp
index fdf0732..aa565b4 100644
--- a/tools/i2c/i2c.cpp
+++ b/tools/i2c/i2c.cpp
@@ -1,8 +1,28 @@
 #include "i2c.hpp"
 
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <cerrno>
+
 namespace i2c
 {
 
+void I2CDevice::open()
+{
+    fd = ::open(busStr.c_str(), O_RDWR);
+    if (fd == -1)
+    {
+        throw I2CException("Failed to open", busStr, devAddr, errno);
+    }
+}
+
+void I2CDevice::close()
+{
+    ::close(fd);
+}
+
 void I2CDevice::read(uint8_t& data)
 {
     // TODO
diff --git a/tools/i2c/i2c.hpp b/tools/i2c/i2c.hpp
index f9cc09e..45bb024 100644
--- a/tools/i2c/i2c.hpp
+++ b/tools/i2c/i2c.hpp
@@ -10,15 +10,43 @@
   private:
     I2CDevice() = delete;
 
-    explicit I2CDevice(uint8_t busId, uint8_t devAddr)
+    /** @brief Constructor
+     *
+     * Construct I2CDevice object from the bus id and device address
+     *
+     * @param[in] busId - The i2c bus ID
+     * @param[in] devAddr - The device address of the I2C device
+     */
+    explicit I2CDevice(uint8_t busId, uint8_t devAddr) :
+        busId(busId), devAddr(devAddr)
     {
-        // TODO
-        (void)busId;
-        (void)devAddr;
+        busStr = "/dev/i2c-" + std::to_string(busId);
+        open();
     }
 
+    /** @brief Open the i2c device */
+    void open();
+
+    /** @brief Close the i2c device */
+    void close();
+
+    /** @brief The I2C bus ID */
+    uint8_t busId;
+
+    /** @brief The i2c device address in the bus */
+    uint8_t devAddr;
+
+    /** @brief The file descriptor of the opened i2c device */
+    int fd;
+
+    /** @brief The i2c bus path in /dev */
+    std::string busStr;
+
   public:
-    virtual ~I2CDevice() = default;
+    ~I2CDevice()
+    {
+        close();
+    }
 
     /** @copydoc I2CInterface::read(uint8_t&) */
     void read(uint8_t& data) override;
diff --git a/tools/i2c/meson.build b/tools/i2c/meson.build
index 1f4e7d1..258ac77 100644
--- a/tools/i2c/meson.build
+++ b/tools/i2c/meson.build
@@ -3,3 +3,5 @@
     'i2c.cpp',
     link_args : '-li2c',
 )
+
+libi2c_inc = include_directories('.')