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