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)
diff --git a/tools/i2c/i2c.hpp b/tools/i2c/i2c.hpp
index 45bb024..7fe064f 100644
--- a/tools/i2c/i2c.hpp
+++ b/tools/i2c/i2c.hpp
@@ -42,6 +42,17 @@
/** @brief The i2c bus path in /dev */
std::string busStr;
+ /** @brief Check i2c adapter read functionality
+ *
+ * Check if the i2c adapter has the functionality specified by the SMBus
+ * transaction type
+ *
+ * @param[in] type - The SMBus transaction type defined in linux/i2c.h
+ *
+ * @throw I2CException if the function is not supported
+ */
+ void checkReadFuncs(int type);
+
public:
~I2CDevice()
{
diff --git a/tools/i2c/meson.build b/tools/i2c/meson.build
index 258ac77..71a5aab 100644
--- a/tools/i2c/meson.build
+++ b/tools/i2c/meson.build
@@ -5,3 +5,7 @@
)
libi2c_inc = include_directories('.')
+libi2c_dep = declare_dependency(
+ link_with: libi2c_dev,
+ include_directories : libi2c_inc,
+ link_args : '-li2c')