xref: /openbmc/phosphor-power/tools/i2c/i2c.cpp (revision 92261f88729b618b1c31d20f28328a8aff73b83b)
1ab1327c3SLei YU #include "i2c.hpp"
2ab1327c3SLei YU 
39af82a5cSLei YU #include <fcntl.h>
492e89eb5SLei YU #include <sys/ioctl.h>
59af82a5cSLei YU #include <sys/stat.h>
69af82a5cSLei YU #include <unistd.h>
79af82a5cSLei YU 
892e89eb5SLei YU #include <cassert>
99af82a5cSLei YU #include <cerrno>
10a3ff7e71SShawn McCarney #include <cstring>
11a3ff7e71SShawn McCarney #include <format>
129af82a5cSLei YU 
13d1bc4cecSBrandon Wyman extern "C"
14d1bc4cecSBrandon Wyman {
1592e89eb5SLei YU #include <i2c/smbus.h>
1692e89eb5SLei YU #include <linux/i2c-dev.h>
1792e89eb5SLei YU #include <linux/i2c.h>
1892e89eb5SLei YU }
1992e89eb5SLei YU 
20ab1327c3SLei YU namespace i2c
21ab1327c3SLei YU {
22ab1327c3SLei YU 
23a3ff7e71SShawn McCarney // Maximum number of data bytes in a block read/block write/block process call
24a3ff7e71SShawn McCarney // in SMBus 3.0. The maximum was 32 data bytes in SMBus 2.0 and earlier.
25a3ff7e71SShawn McCarney constexpr uint8_t I2C_SMBUS3_BLOCK_MAX = 255;
26a3ff7e71SShawn McCarney 
getFuncs()2738ed88d6SShawn McCarney unsigned long I2CDevice::getFuncs()
2892e89eb5SLei YU {
2938ed88d6SShawn McCarney     // If functionality has not been cached
3038ed88d6SShawn McCarney     if (cachedFuncs == NO_FUNCS)
3138ed88d6SShawn McCarney     {
3238ed88d6SShawn McCarney         // Get functionality from adapter
33770de580SShawn McCarney         int ret = 0, retries = 0;
34770de580SShawn McCarney         do
35770de580SShawn McCarney         {
36770de580SShawn McCarney             ret = ioctl(fd, I2C_FUNCS, &cachedFuncs);
37770de580SShawn McCarney         } while ((ret < 0) && (++retries <= maxRetries));
38770de580SShawn McCarney 
39770de580SShawn McCarney         if (ret < 0)
4092e89eb5SLei YU         {
41a3ff7e71SShawn McCarney             cachedFuncs = NO_FUNCS;
4292e89eb5SLei YU             throw I2CException("Failed to get funcs", busStr, devAddr, errno);
4392e89eb5SLei YU         }
4438ed88d6SShawn McCarney     }
4592e89eb5SLei YU 
4638ed88d6SShawn McCarney     return cachedFuncs;
4738ed88d6SShawn McCarney }
4838ed88d6SShawn McCarney 
checkReadFuncs(int type)4938ed88d6SShawn McCarney void I2CDevice::checkReadFuncs(int type)
5038ed88d6SShawn McCarney {
5138ed88d6SShawn McCarney     unsigned long funcs = getFuncs();
5292e89eb5SLei YU     switch (type)
5392e89eb5SLei YU     {
5492e89eb5SLei YU         case I2C_SMBUS_BYTE:
5592e89eb5SLei YU             if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
5692e89eb5SLei YU             {
5792e89eb5SLei YU                 throw I2CException("Missing SMBUS_READ_BYTE", busStr, devAddr);
5892e89eb5SLei YU             }
5992e89eb5SLei YU             break;
6092e89eb5SLei YU         case I2C_SMBUS_BYTE_DATA:
6192e89eb5SLei YU             if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
6292e89eb5SLei YU             {
6392e89eb5SLei YU                 throw I2CException("Missing SMBUS_READ_BYTE_DATA", busStr,
6492e89eb5SLei YU                                    devAddr);
6592e89eb5SLei YU             }
6692e89eb5SLei YU             break;
6792e89eb5SLei YU         case I2C_SMBUS_WORD_DATA:
6892e89eb5SLei YU             if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
6992e89eb5SLei YU             {
7092e89eb5SLei YU                 throw I2CException("Missing SMBUS_READ_WORD_DATA", busStr,
7192e89eb5SLei YU                                    devAddr);
7292e89eb5SLei YU             }
7392e89eb5SLei YU             break;
7492e89eb5SLei YU         case I2C_SMBUS_BLOCK_DATA:
7592e89eb5SLei YU             if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
7692e89eb5SLei YU             {
7792e89eb5SLei YU                 throw I2CException("Missing SMBUS_READ_BLOCK_DATA", busStr,
7892e89eb5SLei YU                                    devAddr);
7992e89eb5SLei YU             }
8092e89eb5SLei YU             break;
811d103428SLei YU         case I2C_SMBUS_I2C_BLOCK_DATA:
821d103428SLei YU             if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
831d103428SLei YU             {
841d103428SLei YU                 throw I2CException("Missing I2C_FUNC_SMBUS_READ_I2C_BLOCK",
851d103428SLei YU                                    busStr, devAddr);
861d103428SLei YU             }
871d103428SLei YU             break;
8892e89eb5SLei YU         default:
8992e89eb5SLei YU             fprintf(stderr, "Unexpected read size type: %d\n", type);
9092e89eb5SLei YU             assert(false);
9192e89eb5SLei YU             break;
9292e89eb5SLei YU     }
9392e89eb5SLei YU }
9492e89eb5SLei YU 
checkWriteFuncs(int type)9534fb8bdaSLei YU void I2CDevice::checkWriteFuncs(int type)
9634fb8bdaSLei YU {
9738ed88d6SShawn McCarney     unsigned long funcs = getFuncs();
9834fb8bdaSLei YU     switch (type)
9934fb8bdaSLei YU     {
10034fb8bdaSLei YU         case I2C_SMBUS_BYTE:
10134fb8bdaSLei YU             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
10234fb8bdaSLei YU             {
10334fb8bdaSLei YU                 throw I2CException("Missing SMBUS_WRITE_BYTE", busStr, devAddr);
10434fb8bdaSLei YU             }
10534fb8bdaSLei YU             break;
10634fb8bdaSLei YU         case I2C_SMBUS_BYTE_DATA:
10734fb8bdaSLei YU             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
10834fb8bdaSLei YU             {
10934fb8bdaSLei YU                 throw I2CException("Missing SMBUS_WRITE_BYTE_DATA", busStr,
11034fb8bdaSLei YU                                    devAddr);
11134fb8bdaSLei YU             }
11234fb8bdaSLei YU             break;
11334fb8bdaSLei YU         case I2C_SMBUS_WORD_DATA:
11434fb8bdaSLei YU             if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
11534fb8bdaSLei YU             {
11634fb8bdaSLei YU                 throw I2CException("Missing SMBUS_WRITE_WORD_DATA", busStr,
11734fb8bdaSLei YU                                    devAddr);
11834fb8bdaSLei YU             }
11934fb8bdaSLei YU             break;
12034fb8bdaSLei YU         case I2C_SMBUS_BLOCK_DATA:
12134fb8bdaSLei YU             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
12234fb8bdaSLei YU             {
12334fb8bdaSLei YU                 throw I2CException("Missing SMBUS_WRITE_BLOCK_DATA", busStr,
12434fb8bdaSLei YU                                    devAddr);
12534fb8bdaSLei YU             }
12634fb8bdaSLei YU             break;
1271d103428SLei YU         case I2C_SMBUS_I2C_BLOCK_DATA:
1281d103428SLei YU             if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
1291d103428SLei YU             {
1301d103428SLei YU                 throw I2CException("Missing I2C_FUNC_SMBUS_WRITE_I2C_BLOCK",
1311d103428SLei YU                                    busStr, devAddr);
1321d103428SLei YU             }
1331d103428SLei YU             break;
134a3ff7e71SShawn McCarney         case I2C_SMBUS_PROC_CALL:
135a3ff7e71SShawn McCarney             if (!(funcs & I2C_FUNC_SMBUS_PROC_CALL))
136a3ff7e71SShawn McCarney             {
137a3ff7e71SShawn McCarney                 throw I2CException("Missing I2C_FUNC_SMBUS_PROC_CALL", busStr,
138a3ff7e71SShawn McCarney                                    devAddr);
139a3ff7e71SShawn McCarney             }
140a3ff7e71SShawn McCarney             break;
141a3ff7e71SShawn McCarney         case I2C_SMBUS_BLOCK_PROC_CALL:
142a3ff7e71SShawn McCarney             if (!(funcs & I2C_FUNC_SMBUS_BLOCK_PROC_CALL))
143a3ff7e71SShawn McCarney             {
144a3ff7e71SShawn McCarney                 throw I2CException("Missing I2C_FUNC_SMBUS_BLOCK_PROC_CALL",
145a3ff7e71SShawn McCarney                                    busStr, devAddr);
146a3ff7e71SShawn McCarney             }
147a3ff7e71SShawn McCarney             break;
14834fb8bdaSLei YU         default:
149770de580SShawn McCarney             fprintf(stderr, "Unexpected write size type: %d\n", type);
15034fb8bdaSLei YU             assert(false);
15134fb8bdaSLei YU     }
15234fb8bdaSLei YU }
15334fb8bdaSLei YU 
processCallSMBus(uint8_t addr,uint8_t writeSize,const uint8_t * writeData,uint8_t & readSize,uint8_t * readData)154a3ff7e71SShawn McCarney void I2CDevice::processCallSMBus(uint8_t addr, uint8_t writeSize,
155a3ff7e71SShawn McCarney                                  const uint8_t* writeData, uint8_t& readSize,
156a3ff7e71SShawn McCarney                                  uint8_t* readData)
157a3ff7e71SShawn McCarney {
158a3ff7e71SShawn McCarney     int ret = 0, retries = 0;
159a3ff7e71SShawn McCarney     uint8_t buffer[I2C_SMBUS_BLOCK_MAX];
160a3ff7e71SShawn McCarney     do
161a3ff7e71SShawn McCarney     {
162a3ff7e71SShawn McCarney         // Copy write data to buffer. Buffer is also used by SMBus function to
163a3ff7e71SShawn McCarney         // store the data read from the device.
164a3ff7e71SShawn McCarney         std::memcpy(buffer, writeData, writeSize);
165a3ff7e71SShawn McCarney         ret = i2c_smbus_block_process_call(fd, addr, writeSize, buffer);
166a3ff7e71SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
167a3ff7e71SShawn McCarney 
168a3ff7e71SShawn McCarney     if (ret < 0)
169a3ff7e71SShawn McCarney     {
170a3ff7e71SShawn McCarney         throw I2CException("Failed to execute block process call", busStr,
171a3ff7e71SShawn McCarney                            devAddr, errno);
172a3ff7e71SShawn McCarney     }
173a3ff7e71SShawn McCarney 
174a3ff7e71SShawn McCarney     readSize = static_cast<uint8_t>(ret);
175a3ff7e71SShawn McCarney     std::memcpy(readData, buffer, readSize);
176a3ff7e71SShawn McCarney }
177a3ff7e71SShawn McCarney 
processCallI2C(uint8_t addr,uint8_t writeSize,const uint8_t * writeData,uint8_t & readSize,uint8_t * readData)178a3ff7e71SShawn McCarney void I2CDevice::processCallI2C(uint8_t addr, uint8_t writeSize,
179a3ff7e71SShawn McCarney                                const uint8_t* writeData, uint8_t& readSize,
180a3ff7e71SShawn McCarney                                uint8_t* readData)
181a3ff7e71SShawn McCarney {
182a3ff7e71SShawn McCarney     // Buffer for block write. Linux supports SMBus 3.0 max size for block
183a3ff7e71SShawn McCarney     // write. Buffer will contain register address, byte count, and data bytes.
184a3ff7e71SShawn McCarney     constexpr uint16_t writeBufferSize = I2C_SMBUS3_BLOCK_MAX + 2;
185a3ff7e71SShawn McCarney     uint8_t writeBuffer[writeBufferSize];
186a3ff7e71SShawn McCarney 
187a3ff7e71SShawn McCarney     // Buffer for block read. Linux supports smaller SMBus 2.0 max size for
188a3ff7e71SShawn McCarney     // block read. After ioctl() buffer will contain byte count and data bytes.
189a3ff7e71SShawn McCarney     constexpr uint16_t readBufferSize = I2C_SMBUS_BLOCK_MAX + 1;
190a3ff7e71SShawn McCarney     uint8_t readBuffer[readBufferSize];
191a3ff7e71SShawn McCarney 
192a3ff7e71SShawn McCarney     // i2c_msg and i2c_rdwr_ioctl_data structs required for ioctl()
193a3ff7e71SShawn McCarney     constexpr unsigned int numMessages = 2;
194a3ff7e71SShawn McCarney     struct i2c_msg messages[numMessages];
195a3ff7e71SShawn McCarney     struct i2c_rdwr_ioctl_data readWriteData;
196a3ff7e71SShawn McCarney     readWriteData.msgs = messages;
197a3ff7e71SShawn McCarney     readWriteData.nmsgs = numMessages;
198a3ff7e71SShawn McCarney 
199a3ff7e71SShawn McCarney     int ret = 0, retries = 0;
200a3ff7e71SShawn McCarney     do
201a3ff7e71SShawn McCarney     {
202a3ff7e71SShawn McCarney         // Initialize write buffer with reg addr, byte count, and data bytes
203a3ff7e71SShawn McCarney         writeBuffer[0] = addr;
204a3ff7e71SShawn McCarney         writeBuffer[1] = writeSize;
205a3ff7e71SShawn McCarney         std::memcpy(&(writeBuffer[2]), writeData, writeSize);
206a3ff7e71SShawn McCarney 
207a3ff7e71SShawn McCarney         // Initialize first i2c_msg to perform block write
208a3ff7e71SShawn McCarney         messages[0].addr = devAddr;
209a3ff7e71SShawn McCarney         messages[0].flags = 0;
210a3ff7e71SShawn McCarney         messages[0].len = writeSize + 2; // 2 == reg addr + byte count
211a3ff7e71SShawn McCarney         messages[0].buf = writeBuffer;
212a3ff7e71SShawn McCarney 
213a3ff7e71SShawn McCarney         // Initialize read buffer. Set first byte to number of "extra bytes"
214a3ff7e71SShawn McCarney         // that will be read in addition to data bytes. Set to 1 since only
215a3ff7e71SShawn McCarney         // extra byte is the byte count.
216a3ff7e71SShawn McCarney         readBuffer[0] = 1;
217a3ff7e71SShawn McCarney 
218a3ff7e71SShawn McCarney         // Initialize second i2c_msg to perform block read. Linux requires the
219a3ff7e71SShawn McCarney         // len field to be set to the buffer size.
220a3ff7e71SShawn McCarney         messages[1].addr = devAddr;
221a3ff7e71SShawn McCarney         messages[1].flags = I2C_M_RD | I2C_M_RECV_LEN;
222a3ff7e71SShawn McCarney         messages[1].len = readBufferSize;
223a3ff7e71SShawn McCarney         messages[1].buf = readBuffer;
224a3ff7e71SShawn McCarney 
225a3ff7e71SShawn McCarney         // Call ioctl() to send the I2C messages
226a3ff7e71SShawn McCarney         ret = ioctl(fd, I2C_RDWR, &readWriteData);
227a3ff7e71SShawn McCarney     } while ((ret != numMessages) && (++retries <= maxRetries));
228a3ff7e71SShawn McCarney 
229a3ff7e71SShawn McCarney     if (ret < 0)
230a3ff7e71SShawn McCarney     {
231a3ff7e71SShawn McCarney         throw I2CException("Failed to execute I2C block process call", busStr,
232a3ff7e71SShawn McCarney                            devAddr, errno);
233a3ff7e71SShawn McCarney     }
234a3ff7e71SShawn McCarney     else if (ret != numMessages)
235a3ff7e71SShawn McCarney     {
236a3ff7e71SShawn McCarney         throw I2CException(
237a3ff7e71SShawn McCarney             std::format(
238a3ff7e71SShawn McCarney                 "Failed to execute I2C block process call: {} messages sent",
239a3ff7e71SShawn McCarney                 ret),
240a3ff7e71SShawn McCarney             busStr, devAddr);
241a3ff7e71SShawn McCarney     }
242a3ff7e71SShawn McCarney 
243a3ff7e71SShawn McCarney     // Read size is in first byte; copy remaining data bytes to readData param
244a3ff7e71SShawn McCarney     readSize = readBuffer[0];
245a3ff7e71SShawn McCarney     std::memcpy(readData, &(readBuffer[1]), readSize);
246a3ff7e71SShawn McCarney }
247a3ff7e71SShawn McCarney 
open()248d45a9a6dSShawn McCarney void I2CDevice::open()
249d45a9a6dSShawn McCarney {
250d45a9a6dSShawn McCarney     if (isOpen())
251d45a9a6dSShawn McCarney     {
252d45a9a6dSShawn McCarney         throw I2CException("Device already open", busStr, devAddr);
253d45a9a6dSShawn McCarney     }
254d45a9a6dSShawn McCarney 
255770de580SShawn McCarney     int retries = 0;
256770de580SShawn McCarney     do
257770de580SShawn McCarney     {
258d45a9a6dSShawn McCarney         fd = ::open(busStr.c_str(), O_RDWR);
259770de580SShawn McCarney     } while ((fd == -1) && (++retries <= maxRetries));
260770de580SShawn McCarney 
261d45a9a6dSShawn McCarney     if (fd == -1)
262d45a9a6dSShawn McCarney     {
263d45a9a6dSShawn McCarney         throw I2CException("Failed to open", busStr, devAddr, errno);
264d45a9a6dSShawn McCarney     }
265d45a9a6dSShawn McCarney 
266770de580SShawn McCarney     retries = 0;
267770de580SShawn McCarney     int ret = 0;
268770de580SShawn McCarney     do
269770de580SShawn McCarney     {
270770de580SShawn McCarney         ret = ioctl(fd, I2C_SLAVE, devAddr);
271770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
272770de580SShawn McCarney 
273770de580SShawn McCarney     if (ret < 0)
274d45a9a6dSShawn McCarney     {
275d45a9a6dSShawn McCarney         // Close device since setting slave address failed
276d45a9a6dSShawn McCarney         closeWithoutException();
277d45a9a6dSShawn McCarney 
278d45a9a6dSShawn McCarney         throw I2CException("Failed to set I2C_SLAVE", busStr, devAddr, errno);
279d45a9a6dSShawn McCarney     }
280d45a9a6dSShawn McCarney }
281d45a9a6dSShawn McCarney 
close()282d45a9a6dSShawn McCarney void I2CDevice::close()
283d45a9a6dSShawn McCarney {
284d45a9a6dSShawn McCarney     checkIsOpen();
285770de580SShawn McCarney 
286770de580SShawn McCarney     int ret = 0, retries = 0;
287770de580SShawn McCarney     do
288770de580SShawn McCarney     {
289770de580SShawn McCarney         ret = ::close(fd);
290770de580SShawn McCarney     } while ((ret == -1) && (++retries <= maxRetries));
291770de580SShawn McCarney 
292770de580SShawn McCarney     if (ret == -1)
293d45a9a6dSShawn McCarney     {
294d45a9a6dSShawn McCarney         throw I2CException("Failed to close", busStr, devAddr, errno);
295d45a9a6dSShawn McCarney     }
296770de580SShawn McCarney 
297d45a9a6dSShawn McCarney     fd = INVALID_FD;
29838ed88d6SShawn McCarney     cachedFuncs = NO_FUNCS;
299d45a9a6dSShawn McCarney }
300d45a9a6dSShawn McCarney 
read(uint8_t & data)301ab1327c3SLei YU void I2CDevice::read(uint8_t& data)
302ab1327c3SLei YU {
303d45a9a6dSShawn McCarney     checkIsOpen();
30492e89eb5SLei YU     checkReadFuncs(I2C_SMBUS_BYTE);
30592e89eb5SLei YU 
306770de580SShawn McCarney     int ret = 0, retries = 0;
307770de580SShawn McCarney     do
308770de580SShawn McCarney     {
309770de580SShawn McCarney         ret = i2c_smbus_read_byte(fd);
310770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
311770de580SShawn McCarney 
31292e89eb5SLei YU     if (ret < 0)
31392e89eb5SLei YU     {
31492e89eb5SLei YU         throw I2CException("Failed to read byte", busStr, devAddr, errno);
31592e89eb5SLei YU     }
316770de580SShawn McCarney 
31792e89eb5SLei YU     data = static_cast<uint8_t>(ret);
318ab1327c3SLei YU }
319ab1327c3SLei YU 
read(uint8_t addr,uint8_t & data)320ab1327c3SLei YU void I2CDevice::read(uint8_t addr, uint8_t& data)
321ab1327c3SLei YU {
322d45a9a6dSShawn McCarney     checkIsOpen();
32392e89eb5SLei YU     checkReadFuncs(I2C_SMBUS_BYTE_DATA);
32492e89eb5SLei YU 
325770de580SShawn McCarney     int ret = 0, retries = 0;
326770de580SShawn McCarney     do
327770de580SShawn McCarney     {
328770de580SShawn McCarney         ret = i2c_smbus_read_byte_data(fd, addr);
329770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
330770de580SShawn McCarney 
33192e89eb5SLei YU     if (ret < 0)
33292e89eb5SLei YU     {
33392e89eb5SLei YU         throw I2CException("Failed to read byte data", busStr, devAddr, errno);
33492e89eb5SLei YU     }
335770de580SShawn McCarney 
33692e89eb5SLei YU     data = static_cast<uint8_t>(ret);
337ab1327c3SLei YU }
338ab1327c3SLei YU 
read(uint8_t addr,uint16_t & data)339ab1327c3SLei YU void I2CDevice::read(uint8_t addr, uint16_t& data)
340ab1327c3SLei YU {
341d45a9a6dSShawn McCarney     checkIsOpen();
34292e89eb5SLei YU     checkReadFuncs(I2C_SMBUS_WORD_DATA);
343770de580SShawn McCarney 
344770de580SShawn McCarney     int ret = 0, retries = 0;
345770de580SShawn McCarney     do
346770de580SShawn McCarney     {
347770de580SShawn McCarney         ret = i2c_smbus_read_word_data(fd, addr);
348770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
349770de580SShawn McCarney 
35092e89eb5SLei YU     if (ret < 0)
35192e89eb5SLei YU     {
35292e89eb5SLei YU         throw I2CException("Failed to read word data", busStr, devAddr, errno);
35392e89eb5SLei YU     }
354770de580SShawn McCarney 
35592e89eb5SLei YU     data = static_cast<uint16_t>(ret);
356ab1327c3SLei YU }
357ab1327c3SLei YU 
read(uint8_t addr,uint8_t & size,uint8_t * data,Mode mode)3581d103428SLei YU void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data, Mode mode)
359ab1327c3SLei YU {
360d45a9a6dSShawn McCarney     checkIsOpen();
361770de580SShawn McCarney 
362770de580SShawn McCarney     int ret = -1, retries = 0;
3631d103428SLei YU     switch (mode)
3641d103428SLei YU     {
3651d103428SLei YU         case Mode::SMBUS:
36692e89eb5SLei YU             checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
367770de580SShawn McCarney             do
368770de580SShawn McCarney             {
3691d103428SLei YU                 ret = i2c_smbus_read_block_data(fd, addr, data);
370770de580SShawn McCarney             } while ((ret < 0) && (++retries <= maxRetries));
3711d103428SLei YU             break;
3721d103428SLei YU         case Mode::I2C:
3731d103428SLei YU             checkReadFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
374770de580SShawn McCarney             do
375770de580SShawn McCarney             {
3761d103428SLei YU                 ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
377770de580SShawn McCarney             } while ((ret < 0) && (++retries <= maxRetries));
3781d103428SLei YU             if (ret != size)
3791d103428SLei YU             {
3801d103428SLei YU                 throw I2CException("Failed to read i2c block data", busStr,
3811d103428SLei YU                                    devAddr, errno);
3821d103428SLei YU             }
3831d103428SLei YU             break;
3841d103428SLei YU     }
385770de580SShawn McCarney 
38692e89eb5SLei YU     if (ret < 0)
38792e89eb5SLei YU     {
38892e89eb5SLei YU         throw I2CException("Failed to read block data", busStr, devAddr, errno);
38992e89eb5SLei YU     }
390770de580SShawn McCarney 
39192e89eb5SLei YU     size = static_cast<uint8_t>(ret);
392ab1327c3SLei YU }
393ab1327c3SLei YU 
write(uint8_t data)394ab1327c3SLei YU void I2CDevice::write(uint8_t data)
395ab1327c3SLei YU {
396d45a9a6dSShawn McCarney     checkIsOpen();
39734fb8bdaSLei YU     checkWriteFuncs(I2C_SMBUS_BYTE);
39834fb8bdaSLei YU 
399770de580SShawn McCarney     int ret = 0, retries = 0;
400770de580SShawn McCarney     do
401770de580SShawn McCarney     {
402770de580SShawn McCarney         ret = i2c_smbus_write_byte(fd, data);
403770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
404770de580SShawn McCarney 
405770de580SShawn McCarney     if (ret < 0)
40634fb8bdaSLei YU     {
40734fb8bdaSLei YU         throw I2CException("Failed to write byte", busStr, devAddr, errno);
40834fb8bdaSLei YU     }
409ab1327c3SLei YU }
410ab1327c3SLei YU 
write(uint8_t addr,uint8_t data)411ab1327c3SLei YU void I2CDevice::write(uint8_t addr, uint8_t data)
412ab1327c3SLei YU {
413d45a9a6dSShawn McCarney     checkIsOpen();
41434fb8bdaSLei YU     checkWriteFuncs(I2C_SMBUS_BYTE_DATA);
41534fb8bdaSLei YU 
416770de580SShawn McCarney     int ret = 0, retries = 0;
417770de580SShawn McCarney     do
418770de580SShawn McCarney     {
419770de580SShawn McCarney         ret = i2c_smbus_write_byte_data(fd, addr, data);
420770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
421770de580SShawn McCarney 
422770de580SShawn McCarney     if (ret < 0)
42334fb8bdaSLei YU     {
42434fb8bdaSLei YU         throw I2CException("Failed to write byte data", busStr, devAddr, errno);
42534fb8bdaSLei YU     }
426ab1327c3SLei YU }
427ab1327c3SLei YU 
write(uint8_t addr,uint16_t data)428ab1327c3SLei YU void I2CDevice::write(uint8_t addr, uint16_t data)
429ab1327c3SLei YU {
430d45a9a6dSShawn McCarney     checkIsOpen();
43134fb8bdaSLei YU     checkWriteFuncs(I2C_SMBUS_WORD_DATA);
43234fb8bdaSLei YU 
433770de580SShawn McCarney     int ret = 0, retries = 0;
434770de580SShawn McCarney     do
435770de580SShawn McCarney     {
436770de580SShawn McCarney         ret = i2c_smbus_write_word_data(fd, addr, data);
437770de580SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
438770de580SShawn McCarney 
439770de580SShawn McCarney     if (ret < 0)
44034fb8bdaSLei YU     {
44134fb8bdaSLei YU         throw I2CException("Failed to write word data", busStr, devAddr, errno);
44234fb8bdaSLei YU     }
443ab1327c3SLei YU }
444ab1327c3SLei YU 
write(uint8_t addr,uint8_t size,const uint8_t * data,Mode mode)4451d103428SLei YU void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data,
4461d103428SLei YU                       Mode mode)
447ab1327c3SLei YU {
448d45a9a6dSShawn McCarney     checkIsOpen();
449770de580SShawn McCarney 
450770de580SShawn McCarney     int ret = -1, retries = 0;
4511d103428SLei YU     switch (mode)
4521d103428SLei YU     {
4531d103428SLei YU         case Mode::SMBUS:
45434fb8bdaSLei YU             checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
455770de580SShawn McCarney             do
456770de580SShawn McCarney             {
4571d103428SLei YU                 ret = i2c_smbus_write_block_data(fd, addr, size, data);
458770de580SShawn McCarney             } while ((ret < 0) && (++retries <= maxRetries));
4591d103428SLei YU             break;
4601d103428SLei YU         case Mode::I2C:
4611d103428SLei YU             checkWriteFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
462770de580SShawn McCarney             do
463770de580SShawn McCarney             {
4641d103428SLei YU                 ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
465770de580SShawn McCarney             } while ((ret < 0) && (++retries <= maxRetries));
4661d103428SLei YU             break;
4671d103428SLei YU     }
468770de580SShawn McCarney 
4691d103428SLei YU     if (ret < 0)
47034fb8bdaSLei YU     {
47134fb8bdaSLei YU         throw I2CException("Failed to write block data", busStr, devAddr,
47234fb8bdaSLei YU                            errno);
47334fb8bdaSLei YU     }
474ab1327c3SLei YU }
475ab1327c3SLei YU 
processCall(uint8_t addr,uint16_t writeData,uint16_t & readData)476a3ff7e71SShawn McCarney void I2CDevice::processCall(uint8_t addr, uint16_t writeData,
477a3ff7e71SShawn McCarney                             uint16_t& readData)
478a3ff7e71SShawn McCarney {
479a3ff7e71SShawn McCarney     checkIsOpen();
480a3ff7e71SShawn McCarney     checkWriteFuncs(I2C_SMBUS_PROC_CALL);
481a3ff7e71SShawn McCarney 
482a3ff7e71SShawn McCarney     int ret = 0, retries = 0;
483a3ff7e71SShawn McCarney     do
484a3ff7e71SShawn McCarney     {
485a3ff7e71SShawn McCarney         ret = i2c_smbus_process_call(fd, addr, writeData);
486a3ff7e71SShawn McCarney     } while ((ret < 0) && (++retries <= maxRetries));
487a3ff7e71SShawn McCarney 
488a3ff7e71SShawn McCarney     if (ret < 0)
489a3ff7e71SShawn McCarney     {
490a3ff7e71SShawn McCarney         throw I2CException("Failed to execute process call", busStr, devAddr,
491a3ff7e71SShawn McCarney                            errno);
492a3ff7e71SShawn McCarney     }
493a3ff7e71SShawn McCarney 
494a3ff7e71SShawn McCarney     readData = static_cast<uint16_t>(ret);
495a3ff7e71SShawn McCarney }
496a3ff7e71SShawn McCarney 
processCall(uint8_t addr,uint8_t writeSize,const uint8_t * writeData,uint8_t & readSize,uint8_t * readData)497a3ff7e71SShawn McCarney void I2CDevice::processCall(uint8_t addr, uint8_t writeSize,
498a3ff7e71SShawn McCarney                             const uint8_t* writeData, uint8_t& readSize,
499a3ff7e71SShawn McCarney                             uint8_t* readData)
500a3ff7e71SShawn McCarney {
501a3ff7e71SShawn McCarney     checkIsOpen();
502a3ff7e71SShawn McCarney     unsigned long funcs = getFuncs();
503a3ff7e71SShawn McCarney 
504a3ff7e71SShawn McCarney     if ((funcs & I2C_FUNC_SMBUS_BLOCK_PROC_CALL) &&
505a3ff7e71SShawn McCarney         (writeSize <= I2C_SMBUS_BLOCK_MAX))
506a3ff7e71SShawn McCarney     {
507a3ff7e71SShawn McCarney         // Use standard SMBus function which supports smaller SMBus 2.0 maximum
508a3ff7e71SShawn McCarney         processCallSMBus(addr, writeSize, writeData, readSize, readData);
509a3ff7e71SShawn McCarney     }
510a3ff7e71SShawn McCarney     else if (funcs & I2C_FUNC_I2C)
511a3ff7e71SShawn McCarney     {
512a3ff7e71SShawn McCarney         // Use lower level I2C ioctl which supports larger SMBus 3.0 maximum
513a3ff7e71SShawn McCarney         processCallI2C(addr, writeSize, writeData, readSize, readData);
514a3ff7e71SShawn McCarney     }
515a3ff7e71SShawn McCarney     else
516a3ff7e71SShawn McCarney     {
517a3ff7e71SShawn McCarney         throw I2CException(
518a3ff7e71SShawn McCarney             std::format(
519a3ff7e71SShawn McCarney                 "Block process call unsupported: writeSize={:d}, funcs={}",
520a3ff7e71SShawn McCarney                 writeSize, funcs),
521a3ff7e71SShawn McCarney             busStr, devAddr);
522a3ff7e71SShawn McCarney     }
523a3ff7e71SShawn McCarney }
524a3ff7e71SShawn McCarney 
create(uint8_t busId,uint8_t devAddr,InitialState initialState,int maxRetries)525f5402197SPatrick Williams std::unique_ptr<I2CInterface> I2CDevice::create(
526f5402197SPatrick Williams     uint8_t busId, uint8_t devAddr, InitialState initialState, int maxRetries)
527ab1327c3SLei YU {
528770de580SShawn McCarney     std::unique_ptr<I2CDevice> dev(
529770de580SShawn McCarney         new I2CDevice(busId, devAddr, initialState, maxRetries));
530ab1327c3SLei YU     return dev;
531ab1327c3SLei YU }
532ab1327c3SLei YU 
create(uint8_t busId,uint8_t devAddr,I2CInterface::InitialState initialState,int maxRetries)533*92261f88SPatrick Williams std::unique_ptr<I2CInterface> create(uint8_t busId, uint8_t devAddr,
534*92261f88SPatrick Williams                                      I2CInterface::InitialState initialState,
535*92261f88SPatrick Williams                                      int maxRetries)
536ab1327c3SLei YU {
537770de580SShawn McCarney     return I2CDevice::create(busId, devAddr, initialState, maxRetries);
538ab1327c3SLei YU }
539ab1327c3SLei YU 
540ab1327c3SLei YU } // namespace i2c
541