Add retry support to I2CInterface
Enhance the I2CInterface class hierarchy to support retrying failed I2C
operations.
Add an optional parameter to i2c::create() to specify the maximum number
of retries to perform for an I2C operation. Default to 0 retries so
that existing code will not be affected.
Testing:
* Tested each modified function
* When I2C operation succeeds
* When I2C operation fails
* When no retries are done
* When retries are done
* When retried operation succeeds
* When retried operation fails
* See complete test plan at
https://gist.github.com/smccarney/8d203a40d9984402ac495dc3d689c12d
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: I2b0e782e5caaafa9908ef5625687377b959b38ff
diff --git a/tools/i2c/i2c.cpp b/tools/i2c/i2c.cpp
index 7a0bbf4..0dbfe9a 100644
--- a/tools/i2c/i2c.cpp
+++ b/tools/i2c/i2c.cpp
@@ -24,7 +24,13 @@
if (cachedFuncs == NO_FUNCS)
{
// Get functionality from adapter
- if (ioctl(fd, I2C_FUNCS, &cachedFuncs) < 0)
+ int ret = 0, retries = 0;
+ do
+ {
+ ret = ioctl(fd, I2C_FUNCS, &cachedFuncs);
+ } while ((ret < 0) && (++retries <= maxRetries));
+
+ if (ret < 0)
{
throw I2CException("Failed to get funcs", busStr, devAddr, errno);
}
@@ -51,7 +57,6 @@
devAddr);
}
break;
-
case I2C_SMBUS_WORD_DATA:
if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
{
@@ -98,7 +103,6 @@
devAddr);
}
break;
-
case I2C_SMBUS_WORD_DATA:
if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
{
@@ -121,7 +125,7 @@
}
break;
default:
- fprintf(stderr, "Unexpected read size type: %d\n", type);
+ fprintf(stderr, "Unexpected write size type: %d\n", type);
assert(false);
}
}
@@ -133,13 +137,25 @@
throw I2CException("Device already open", busStr, devAddr);
}
- fd = ::open(busStr.c_str(), O_RDWR);
+ int retries = 0;
+ do
+ {
+ fd = ::open(busStr.c_str(), O_RDWR);
+ } while ((fd == -1) && (++retries <= maxRetries));
+
if (fd == -1)
{
throw I2CException("Failed to open", busStr, devAddr, errno);
}
- if (ioctl(fd, I2C_SLAVE, devAddr) < 0)
+ retries = 0;
+ int ret = 0;
+ do
+ {
+ ret = ioctl(fd, I2C_SLAVE, devAddr);
+ } while ((ret < 0) && (++retries <= maxRetries));
+
+ if (ret < 0)
{
// Close device since setting slave address failed
closeWithoutException();
@@ -151,10 +167,18 @@
void I2CDevice::close()
{
checkIsOpen();
- if (::close(fd) == -1)
+
+ int ret = 0, retries = 0;
+ do
+ {
+ ret = ::close(fd);
+ } while ((ret == -1) && (++retries <= maxRetries));
+
+ if (ret == -1)
{
throw I2CException("Failed to close", busStr, devAddr, errno);
}
+
fd = INVALID_FD;
cachedFuncs = NO_FUNCS;
}
@@ -164,11 +188,17 @@
checkIsOpen();
checkReadFuncs(I2C_SMBUS_BYTE);
- int ret = i2c_smbus_read_byte(fd);
+ int ret = 0, retries = 0;
+ do
+ {
+ ret = i2c_smbus_read_byte(fd);
+ } while ((ret < 0) && (++retries <= maxRetries));
+
if (ret < 0)
{
throw I2CException("Failed to read byte", busStr, devAddr, errno);
}
+
data = static_cast<uint8_t>(ret);
}
@@ -177,11 +207,17 @@
checkIsOpen();
checkReadFuncs(I2C_SMBUS_BYTE_DATA);
- int ret = i2c_smbus_read_byte_data(fd, addr);
+ int ret = 0, retries = 0;
+ do
+ {
+ ret = i2c_smbus_read_byte_data(fd, addr);
+ } while ((ret < 0) && (++retries <= maxRetries));
+
if (ret < 0)
{
throw I2CException("Failed to read byte data", busStr, devAddr, errno);
}
+
data = static_cast<uint8_t>(ret);
}
@@ -189,27 +225,41 @@
{
checkIsOpen();
checkReadFuncs(I2C_SMBUS_WORD_DATA);
- int ret = i2c_smbus_read_word_data(fd, addr);
+
+ int ret = 0, retries = 0;
+ do
+ {
+ ret = i2c_smbus_read_word_data(fd, addr);
+ } while ((ret < 0) && (++retries <= maxRetries));
+
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, Mode mode)
{
checkIsOpen();
- int ret = -1;
+
+ int ret = -1, retries = 0;
switch (mode)
{
case Mode::SMBUS:
checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
- ret = i2c_smbus_read_block_data(fd, addr, data);
+ do
+ {
+ ret = i2c_smbus_read_block_data(fd, addr, data);
+ } while ((ret < 0) && (++retries <= maxRetries));
break;
case Mode::I2C:
checkReadFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
- ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
+ do
+ {
+ ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
+ } while ((ret < 0) && (++retries <= maxRetries));
if (ret != size)
{
throw I2CException("Failed to read i2c block data", busStr,
@@ -217,10 +267,12 @@
}
break;
}
+
if (ret < 0)
{
throw I2CException("Failed to read block data", busStr, devAddr, errno);
}
+
size = static_cast<uint8_t>(ret);
}
@@ -229,7 +281,13 @@
checkIsOpen();
checkWriteFuncs(I2C_SMBUS_BYTE);
- if (i2c_smbus_write_byte(fd, data) < 0)
+ int ret = 0, retries = 0;
+ do
+ {
+ ret = i2c_smbus_write_byte(fd, data);
+ } while ((ret < 0) && (++retries <= maxRetries));
+
+ if (ret < 0)
{
throw I2CException("Failed to write byte", busStr, devAddr, errno);
}
@@ -240,7 +298,13 @@
checkIsOpen();
checkWriteFuncs(I2C_SMBUS_BYTE_DATA);
- if (i2c_smbus_write_byte_data(fd, addr, data) < 0)
+ int ret = 0, retries = 0;
+ do
+ {
+ ret = i2c_smbus_write_byte_data(fd, addr, data);
+ } while ((ret < 0) && (++retries <= maxRetries));
+
+ if (ret < 0)
{
throw I2CException("Failed to write byte data", busStr, devAddr, errno);
}
@@ -251,7 +315,13 @@
checkIsOpen();
checkWriteFuncs(I2C_SMBUS_WORD_DATA);
- if (i2c_smbus_write_word_data(fd, addr, data) < 0)
+ int ret = 0, retries = 0;
+ do
+ {
+ ret = i2c_smbus_write_word_data(fd, addr, data);
+ } while ((ret < 0) && (++retries <= maxRetries));
+
+ if (ret < 0)
{
throw I2CException("Failed to write word data", busStr, devAddr, errno);
}
@@ -261,18 +331,26 @@
Mode mode)
{
checkIsOpen();
- int ret = -1;
+
+ int ret = -1, retries = 0;
switch (mode)
{
case Mode::SMBUS:
checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
- ret = i2c_smbus_write_block_data(fd, addr, size, data);
+ do
+ {
+ ret = i2c_smbus_write_block_data(fd, addr, size, data);
+ } while ((ret < 0) && (++retries <= maxRetries));
break;
case Mode::I2C:
checkWriteFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
- ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
+ do
+ {
+ ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
+ } while ((ret < 0) && (++retries <= maxRetries));
break;
}
+
if (ret < 0)
{
throw I2CException("Failed to write block data", busStr, devAddr,
@@ -281,16 +359,19 @@
}
std::unique_ptr<I2CInterface> I2CDevice::create(uint8_t busId, uint8_t devAddr,
- InitialState initialState)
+ InitialState initialState,
+ int maxRetries)
{
- std::unique_ptr<I2CDevice> dev(new I2CDevice(busId, devAddr, initialState));
+ std::unique_ptr<I2CDevice> dev(
+ new I2CDevice(busId, devAddr, initialState, maxRetries));
return dev;
}
std::unique_ptr<I2CInterface> create(uint8_t busId, uint8_t devAddr,
- I2CInterface::InitialState initialState)
+ I2CInterface::InitialState initialState,
+ int maxRetries)
{
- return I2CDevice::create(busId, devAddr, initialState);
+ return I2CDevice::create(busId, devAddr, initialState, maxRetries);
}
} // namespace i2c