i2c: Implement write function
Implement I2CDevice::write() by invoking i2c_smbus_write_xxx() APIs.
The code is referenced from i2c-tools' i2cset.c:
https://github.com/ev3dev/i2c-tools/blob/ev3dev-stretch/tools/i2cset.c
Tested: Verify on Witherspoon that it writes the PSU unlock upgrade
command and boot flag successfully.
Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I9fb014c787ef3ebb2f7793a0d012b1d652ef069f
diff --git a/tools/i2c/i2c.cpp b/tools/i2c/i2c.cpp
index da121d8..76756be 100644
--- a/tools/i2c/i2c.cpp
+++ b/tools/i2c/i2c.cpp
@@ -83,6 +83,52 @@
}
}
+void I2CDevice::checkWriteFuncs(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_WRITE_BYTE))
+ {
+ throw I2CException("Missing SMBUS_WRITE_BYTE", busStr, devAddr);
+ }
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ {
+ throw I2CException("Missing SMBUS_WRITE_BYTE_DATA", busStr,
+ devAddr);
+ }
+ break;
+
+ case I2C_SMBUS_WORD_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
+ {
+ throw I2CException("Missing SMBUS_WRITE_WORD_DATA", busStr,
+ devAddr);
+ }
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
+ {
+ throw I2CException("Missing SMBUS_WRITE_BLOCK_DATA", busStr,
+ devAddr);
+ }
+ break;
+ default:
+ fprintf(stderr, "Unexpected read size type: %d\n", type);
+ assert(false);
+ }
+}
+
void I2CDevice::read(uint8_t& data)
{
checkReadFuncs(I2C_SMBUS_BYTE);
@@ -132,30 +178,43 @@
void I2CDevice::write(uint8_t data)
{
- // TODO
- (void)data;
+ checkWriteFuncs(I2C_SMBUS_BYTE);
+
+ if (i2c_smbus_write_byte(fd, data) < 0)
+ {
+ throw I2CException("Failed to write byte", busStr, devAddr, errno);
+ }
}
void I2CDevice::write(uint8_t addr, uint8_t data)
{
- // TODO
- (void)addr;
- (void)data;
+ checkWriteFuncs(I2C_SMBUS_BYTE_DATA);
+
+ if (i2c_smbus_write_byte_data(fd, addr, data) < 0)
+ {
+ throw I2CException("Failed to write byte data", busStr, devAddr, errno);
+ }
}
void I2CDevice::write(uint8_t addr, uint16_t data)
{
- // TODO
- (void)addr;
- (void)data;
+ checkWriteFuncs(I2C_SMBUS_WORD_DATA);
+
+ if (i2c_smbus_write_word_data(fd, addr, data) < 0)
+ {
+ throw I2CException("Failed to write word data", busStr, devAddr, errno);
+ }
}
void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data)
{
- // TODO
- (void)addr;
- (void)size;
- (void)data;
+ checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
+
+ if (i2c_smbus_write_block_data(fd, addr, size, data) < 0)
+ {
+ throw I2CException("Failed to write block data", busStr, devAddr,
+ errno);
+ }
}
std::unique_ptr<I2CInterface> I2CDevice::create(uint8_t busId, uint8_t devAddr)
diff --git a/tools/i2c/i2c.hpp b/tools/i2c/i2c.hpp
index 7fe064f..befeaba 100644
--- a/tools/i2c/i2c.hpp
+++ b/tools/i2c/i2c.hpp
@@ -53,6 +53,17 @@
*/
void checkReadFuncs(int type);
+ /** @brief Check i2c adapter write 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 checkWriteFuncs(int type);
+
public:
~I2CDevice()
{
diff --git a/tools/power-utils/test/test_updater.cpp b/tools/power-utils/test/test_updater.cpp
index d2dfd11..e1b6a2e 100644
--- a/tools/power-utils/test/test_updater.cpp
+++ b/tools/power-utils/test/test_updater.cpp
@@ -24,6 +24,7 @@
using ::testing::_;
using ::testing::An;
+using ::testing::Pointee;
namespace updater
{
@@ -95,10 +96,10 @@
updater = std::make_unique<Updater>(psuInventoryPath, devPath, imageDir);
updater->createI2CDevice();
auto& i2c = getMockedI2c();
- EXPECT_CALL(i2c, read(An<uint8_t&>()));
- EXPECT_CALL(i2c, read(_, An<uint8_t&>()));
- EXPECT_CALL(i2c, read(_, An<uint16_t&>()));
- EXPECT_CALL(i2c, read(_, An<uint8_t&>(), _));
+
+ EXPECT_CALL(i2c, write(0xf0, 12, _));
+ EXPECT_CALL(i2c, write(0xf1, An<uint8_t>()));
+ EXPECT_CALL(i2c, read(0xf1, An<uint8_t&>()));
updater->doUpdate();
}
diff --git a/tools/power-utils/updater.cpp b/tools/power-utils/updater.cpp
index 2668030..4c6ed72 100644
--- a/tools/power-utils/updater.cpp
+++ b/tools/power-utils/updater.cpp
@@ -21,8 +21,10 @@
#include "types.hpp"
#include "utility.hpp"
+#include <chrono>
#include <fstream>
#include <phosphor-logging/log.hpp>
+#include <thread>
using namespace phosphor::logging;
namespace util = phosphor::power::util;
@@ -242,23 +244,26 @@
int Updater::doUpdate()
{
- // TODO
- uint8_t data;
- uint8_t size;
- uint16_t word;
- std::vector<uint8_t> blockData(32);
+ using namespace std::chrono;
- i2c->read(data);
- printf("Read byte 0x%02x\n", data);
+ uint8_t data;
+ uint8_t unlockData[12] = {0x45, 0x43, 0x44, 0x31, 0x36, 0x30,
+ 0x33, 0x30, 0x30, 0x30, 0x34, 0x01};
+ uint8_t bootFlag = 0x01;
+ static_assert(sizeof(unlockData) == 12);
+
+ i2c->write(0xf0, sizeof(unlockData), unlockData);
+ printf("Unlock PSU\n");
+
+ std::this_thread::sleep_for(milliseconds(5));
+
+ i2c->write(0xf1, bootFlag);
+ printf("Set boot flag ret\n");
+
+ std::this_thread::sleep_for(seconds(3));
i2c->read(0xf1, data);
- printf("First read of 0x%02x, 0x%02x\n", 0xf1, data);
-
- i2c->read(0xbd, word);
- printf("Read word of 0x%02x, 0x%04x\n", 0xbd, word);
-
- i2c->read(0x00, size, blockData.data()); // This throws on the device
- printf("Read block data, size: %d\n", size);
+ printf("Read of 0x%02x, 0x%02x\n", 0xf1, data);
return 0;
}