frudevice: use a single I2C transaction to read from EEPROM with 16-bit
offset
Current code use 2 I2C transactions to read 16-bit EEPROM: the first I2C
transaction to write one bye of the 16-bit offset to EEPROM; the second
I2C transaction to write the other byteof the offset and read data from
EEPROM.
Tested:
Verified this new EEPROM read method reading from 16-bit offset EEPROM
correctly.
Change-Id: I5628de4bd6b2da4d4dfc02f87d1551d7e93062cd
Signed-off-by: Xiang Liu <xiangliu@google.com>
diff --git a/src/FruDevice.cpp b/src/FruDevice.cpp
index 68f1659..3ae71b1 100644
--- a/src/FruDevice.cpp
+++ b/src/FruDevice.cpp
@@ -86,17 +86,19 @@
bool validateHeader(const std::array<uint8_t, I2C_SMBUS_BLOCK_MAX>& blockData);
-using ReadBlockFunc = std::function<int64_t(int flag, int file, uint16_t offset,
- uint8_t length, uint8_t* outBuf)>;
+using ReadBlockFunc =
+ std::function<int64_t(int flag, int file, uint16_t address, uint16_t offset,
+ uint8_t length, uint8_t* outBuf)>;
// Read and validate FRU contents, given the flag required for raw i2c, the open
// file handle, a read method, and a helper string for failures.
-std::vector<char> readFruContents(int flag, int file, ReadBlockFunc readBlock,
+std::vector<char> readFruContents(int flag, int file, uint16_t address,
+ ReadBlockFunc readBlock,
const std::string& errorHelp)
{
std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData;
- if (readBlock(flag, file, 0x0, 0x8, blockData.data()) < 0)
+ if (readBlock(flag, file, address, 0x0, 0x8, blockData.data()) < 0)
{
std::cerr << "failed to read " << errorHelp << "\n";
return {};
@@ -137,8 +139,8 @@
areaOffset *= 8;
- if (readBlock(flag, file, static_cast<uint16_t>(areaOffset), 0x2,
- blockData.data()) < 0)
+ if (readBlock(flag, file, address, static_cast<uint16_t>(areaOffset),
+ 0x2, blockData.data()) < 0)
{
std::cerr << "failed to read " << errorHelp << "\n";
return {};
@@ -166,7 +168,8 @@
{
// In multi-area, the area offset points to the 0th record, each
// record has 3 bytes of the header we care about.
- if (readBlock(flag, file, static_cast<uint16_t>(areaOffset), 0x3,
+ if (readBlock(flag, file, address,
+ static_cast<uint16_t>(areaOffset), 0x3,
blockData.data()) < 0)
{
std::cerr << "failed to read " << errorHelp << "\n";
@@ -196,7 +199,7 @@
{
int requestLength = std::min(I2C_SMBUS_BLOCK_MAX, fruLength);
- if (readBlock(flag, file, static_cast<uint16_t>(readOffset),
+ if (readBlock(flag, file, address, static_cast<uint16_t>(readOffset),
static_cast<uint8_t>(requestLength),
blockData.data()) < 0)
{
@@ -238,6 +241,7 @@
}
static int64_t readFromEeprom(int flag __attribute__((unused)), int fd,
+ uint16_t address __attribute__((unused)),
uint16_t offset, uint8_t len, uint8_t* buf)
{
auto result = lseek(fd, offset, SEEK_SET);
@@ -324,27 +328,52 @@
return 0;
}
-static int64_t readBlockData(int flag, int file, uint16_t offset, uint8_t len,
- uint8_t* buf)
+// Issue an I2C transaction to first write to_slave_buf_len bytes,then read
+// from_slave_buf_len bytes.
+static int i2c_smbus_write_then_read(int file, uint16_t address,
+ uint8_t* toSlaveBuf, uint8_t toSlaveBufLen,
+ uint8_t* fromSlaveBuf,
+ uint8_t fromSlaveBufLen)
{
- uint8_t lowAddr = static_cast<uint8_t>(offset);
- uint8_t highAddr = static_cast<uint8_t>(offset >> 8);
+ if (toSlaveBuf == NULL || toSlaveBufLen == 0 || fromSlaveBuf == NULL ||
+ fromSlaveBufLen == 0)
+ {
+ return -1;
+ }
+#define SMBUS_IOCTL_WRITE_THEN_READ_MSG_COUNT 2
+ struct i2c_msg msgs[SMBUS_IOCTL_WRITE_THEN_READ_MSG_COUNT];
+ struct i2c_rdwr_ioctl_data rdwr;
+
+ msgs[0].addr = address;
+ msgs[0].flags = 0;
+ msgs[0].len = toSlaveBufLen;
+ msgs[0].buf = toSlaveBuf;
+ msgs[1].addr = address;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = fromSlaveBufLen;
+ msgs[1].buf = fromSlaveBuf;
+
+ rdwr.msgs = msgs;
+ rdwr.nmsgs = SMBUS_IOCTL_WRITE_THEN_READ_MSG_COUNT;
+
+ int ret = ioctl(file, I2C_RDWR, &rdwr);
+
+ return (ret == SMBUS_IOCTL_WRITE_THEN_READ_MSG_COUNT) ? ret : -1;
+}
+
+static int64_t readBlockData(int flag, int file, uint16_t address,
+ uint16_t offset, uint8_t len, uint8_t* buf)
+{
if (flag == 0)
{
- return i2c_smbus_read_i2c_block_data(file, lowAddr, len, buf);
+ return i2c_smbus_read_i2c_block_data(file, static_cast<uint8_t>(offset),
+ 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, lowAddr);
- if (ret < 0)
- {
- return ret;
- }
-
- return i2c_smbus_read_i2c_block_data(file, highAddr, len, buf);
+ offset = htobe16(offset);
+ return i2c_smbus_write_then_read(
+ file, address, reinterpret_cast<uint8_t*>(&offset), 2, buf, len);
}
bool validateHeader(const std::array<uint8_t, I2C_SMBUS_BLOCK_MAX>& blockData)
@@ -406,8 +435,8 @@
std::string errorMessage = "eeprom at " + std::to_string(bus) +
" address " + std::to_string(address);
- std::vector<char> device =
- readFruContents(0, file, readFromEeprom, errorMessage);
+ std::vector<char> device = readFruContents(
+ 0, file, static_cast<uint16_t>(address), readFromEeprom, errorMessage);
close(file);
return device;
@@ -555,7 +584,8 @@
std::string errorMessage =
"bus " + std::to_string(bus) + " address " + std::to_string(ii);
std::vector<char> device =
- readFruContents(flag, file, readBlockData, errorMessage);
+ readFruContents(flag, file, static_cast<uint16_t>(ii),
+ readBlockData, errorMessage);
if (device.empty())
{
continue;