xref: /openbmc/phosphor-power/tools/i2c/i2c.cpp (revision a3ff7e71)
1 #include "i2c.hpp"
2 
3 #include <fcntl.h>
4 #include <sys/ioctl.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 
8 #include <cassert>
9 #include <cerrno>
10 #include <cstring>
11 #include <format>
12 
13 extern "C"
14 {
15 #include <i2c/smbus.h>
16 #include <linux/i2c-dev.h>
17 #include <linux/i2c.h>
18 }
19 
20 namespace i2c
21 {
22 
23 // Maximum number of data bytes in a block read/block write/block process call
24 // in SMBus 3.0. The maximum was 32 data bytes in SMBus 2.0 and earlier.
25 constexpr uint8_t I2C_SMBUS3_BLOCK_MAX = 255;
26 
getFuncs()27 unsigned long I2CDevice::getFuncs()
28 {
29     // If functionality has not been cached
30     if (cachedFuncs == NO_FUNCS)
31     {
32         // Get functionality from adapter
33         int ret = 0, retries = 0;
34         do
35         {
36             ret = ioctl(fd, I2C_FUNCS, &cachedFuncs);
37         } while ((ret < 0) && (++retries <= maxRetries));
38 
39         if (ret < 0)
40         {
41             cachedFuncs = NO_FUNCS;
42             throw I2CException("Failed to get funcs", busStr, devAddr, errno);
43         }
44     }
45 
46     return cachedFuncs;
47 }
48 
checkReadFuncs(int type)49 void I2CDevice::checkReadFuncs(int type)
50 {
51     unsigned long funcs = getFuncs();
52     switch (type)
53     {
54         case I2C_SMBUS_BYTE:
55             if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
56             {
57                 throw I2CException("Missing SMBUS_READ_BYTE", busStr, devAddr);
58             }
59             break;
60         case I2C_SMBUS_BYTE_DATA:
61             if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
62             {
63                 throw I2CException("Missing SMBUS_READ_BYTE_DATA", busStr,
64                                    devAddr);
65             }
66             break;
67         case I2C_SMBUS_WORD_DATA:
68             if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
69             {
70                 throw I2CException("Missing SMBUS_READ_WORD_DATA", busStr,
71                                    devAddr);
72             }
73             break;
74         case I2C_SMBUS_BLOCK_DATA:
75             if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
76             {
77                 throw I2CException("Missing SMBUS_READ_BLOCK_DATA", busStr,
78                                    devAddr);
79             }
80             break;
81         case I2C_SMBUS_I2C_BLOCK_DATA:
82             if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
83             {
84                 throw I2CException("Missing I2C_FUNC_SMBUS_READ_I2C_BLOCK",
85                                    busStr, devAddr);
86             }
87             break;
88         default:
89             fprintf(stderr, "Unexpected read size type: %d\n", type);
90             assert(false);
91             break;
92     }
93 }
94 
checkWriteFuncs(int type)95 void I2CDevice::checkWriteFuncs(int type)
96 {
97     unsigned long funcs = getFuncs();
98     switch (type)
99     {
100         case I2C_SMBUS_BYTE:
101             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
102             {
103                 throw I2CException("Missing SMBUS_WRITE_BYTE", busStr, devAddr);
104             }
105             break;
106         case I2C_SMBUS_BYTE_DATA:
107             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
108             {
109                 throw I2CException("Missing SMBUS_WRITE_BYTE_DATA", busStr,
110                                    devAddr);
111             }
112             break;
113         case I2C_SMBUS_WORD_DATA:
114             if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
115             {
116                 throw I2CException("Missing SMBUS_WRITE_WORD_DATA", busStr,
117                                    devAddr);
118             }
119             break;
120         case I2C_SMBUS_BLOCK_DATA:
121             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
122             {
123                 throw I2CException("Missing SMBUS_WRITE_BLOCK_DATA", busStr,
124                                    devAddr);
125             }
126             break;
127         case I2C_SMBUS_I2C_BLOCK_DATA:
128             if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
129             {
130                 throw I2CException("Missing I2C_FUNC_SMBUS_WRITE_I2C_BLOCK",
131                                    busStr, devAddr);
132             }
133             break;
134         case I2C_SMBUS_PROC_CALL:
135             if (!(funcs & I2C_FUNC_SMBUS_PROC_CALL))
136             {
137                 throw I2CException("Missing I2C_FUNC_SMBUS_PROC_CALL", busStr,
138                                    devAddr);
139             }
140             break;
141         case I2C_SMBUS_BLOCK_PROC_CALL:
142             if (!(funcs & I2C_FUNC_SMBUS_BLOCK_PROC_CALL))
143             {
144                 throw I2CException("Missing I2C_FUNC_SMBUS_BLOCK_PROC_CALL",
145                                    busStr, devAddr);
146             }
147             break;
148         default:
149             fprintf(stderr, "Unexpected write size type: %d\n", type);
150             assert(false);
151     }
152 }
153 
processCallSMBus(uint8_t addr,uint8_t writeSize,const uint8_t * writeData,uint8_t & readSize,uint8_t * readData)154 void I2CDevice::processCallSMBus(uint8_t addr, uint8_t writeSize,
155                                  const uint8_t* writeData, uint8_t& readSize,
156                                  uint8_t* readData)
157 {
158     int ret = 0, retries = 0;
159     uint8_t buffer[I2C_SMBUS_BLOCK_MAX];
160     do
161     {
162         // Copy write data to buffer. Buffer is also used by SMBus function to
163         // store the data read from the device.
164         std::memcpy(buffer, writeData, writeSize);
165         ret = i2c_smbus_block_process_call(fd, addr, writeSize, buffer);
166     } while ((ret < 0) && (++retries <= maxRetries));
167 
168     if (ret < 0)
169     {
170         throw I2CException("Failed to execute block process call", busStr,
171                            devAddr, errno);
172     }
173 
174     readSize = static_cast<uint8_t>(ret);
175     std::memcpy(readData, buffer, readSize);
176 }
177 
processCallI2C(uint8_t addr,uint8_t writeSize,const uint8_t * writeData,uint8_t & readSize,uint8_t * readData)178 void I2CDevice::processCallI2C(uint8_t addr, uint8_t writeSize,
179                                const uint8_t* writeData, uint8_t& readSize,
180                                uint8_t* readData)
181 {
182     // Buffer for block write. Linux supports SMBus 3.0 max size for block
183     // write. Buffer will contain register address, byte count, and data bytes.
184     constexpr uint16_t writeBufferSize = I2C_SMBUS3_BLOCK_MAX + 2;
185     uint8_t writeBuffer[writeBufferSize];
186 
187     // Buffer for block read. Linux supports smaller SMBus 2.0 max size for
188     // block read. After ioctl() buffer will contain byte count and data bytes.
189     constexpr uint16_t readBufferSize = I2C_SMBUS_BLOCK_MAX + 1;
190     uint8_t readBuffer[readBufferSize];
191 
192     // i2c_msg and i2c_rdwr_ioctl_data structs required for ioctl()
193     constexpr unsigned int numMessages = 2;
194     struct i2c_msg messages[numMessages];
195     struct i2c_rdwr_ioctl_data readWriteData;
196     readWriteData.msgs = messages;
197     readWriteData.nmsgs = numMessages;
198 
199     int ret = 0, retries = 0;
200     do
201     {
202         // Initialize write buffer with reg addr, byte count, and data bytes
203         writeBuffer[0] = addr;
204         writeBuffer[1] = writeSize;
205         std::memcpy(&(writeBuffer[2]), writeData, writeSize);
206 
207         // Initialize first i2c_msg to perform block write
208         messages[0].addr = devAddr;
209         messages[0].flags = 0;
210         messages[0].len = writeSize + 2; // 2 == reg addr + byte count
211         messages[0].buf = writeBuffer;
212 
213         // Initialize read buffer. Set first byte to number of "extra bytes"
214         // that will be read in addition to data bytes. Set to 1 since only
215         // extra byte is the byte count.
216         readBuffer[0] = 1;
217 
218         // Initialize second i2c_msg to perform block read. Linux requires the
219         // len field to be set to the buffer size.
220         messages[1].addr = devAddr;
221         messages[1].flags = I2C_M_RD | I2C_M_RECV_LEN;
222         messages[1].len = readBufferSize;
223         messages[1].buf = readBuffer;
224 
225         // Call ioctl() to send the I2C messages
226         ret = ioctl(fd, I2C_RDWR, &readWriteData);
227     } while ((ret != numMessages) && (++retries <= maxRetries));
228 
229     if (ret < 0)
230     {
231         throw I2CException("Failed to execute I2C block process call", busStr,
232                            devAddr, errno);
233     }
234     else if (ret != numMessages)
235     {
236         throw I2CException(
237             std::format(
238                 "Failed to execute I2C block process call: {} messages sent",
239                 ret),
240             busStr, devAddr);
241     }
242 
243     // Read size is in first byte; copy remaining data bytes to readData param
244     readSize = readBuffer[0];
245     std::memcpy(readData, &(readBuffer[1]), readSize);
246 }
247 
open()248 void I2CDevice::open()
249 {
250     if (isOpen())
251     {
252         throw I2CException("Device already open", busStr, devAddr);
253     }
254 
255     int retries = 0;
256     do
257     {
258         fd = ::open(busStr.c_str(), O_RDWR);
259     } while ((fd == -1) && (++retries <= maxRetries));
260 
261     if (fd == -1)
262     {
263         throw I2CException("Failed to open", busStr, devAddr, errno);
264     }
265 
266     retries = 0;
267     int ret = 0;
268     do
269     {
270         ret = ioctl(fd, I2C_SLAVE, devAddr);
271     } while ((ret < 0) && (++retries <= maxRetries));
272 
273     if (ret < 0)
274     {
275         // Close device since setting slave address failed
276         closeWithoutException();
277 
278         throw I2CException("Failed to set I2C_SLAVE", busStr, devAddr, errno);
279     }
280 }
281 
close()282 void I2CDevice::close()
283 {
284     checkIsOpen();
285 
286     int ret = 0, retries = 0;
287     do
288     {
289         ret = ::close(fd);
290     } while ((ret == -1) && (++retries <= maxRetries));
291 
292     if (ret == -1)
293     {
294         throw I2CException("Failed to close", busStr, devAddr, errno);
295     }
296 
297     fd = INVALID_FD;
298     cachedFuncs = NO_FUNCS;
299 }
300 
read(uint8_t & data)301 void I2CDevice::read(uint8_t& data)
302 {
303     checkIsOpen();
304     checkReadFuncs(I2C_SMBUS_BYTE);
305 
306     int ret = 0, retries = 0;
307     do
308     {
309         ret = i2c_smbus_read_byte(fd);
310     } while ((ret < 0) && (++retries <= maxRetries));
311 
312     if (ret < 0)
313     {
314         throw I2CException("Failed to read byte", busStr, devAddr, errno);
315     }
316 
317     data = static_cast<uint8_t>(ret);
318 }
319 
read(uint8_t addr,uint8_t & data)320 void I2CDevice::read(uint8_t addr, uint8_t& data)
321 {
322     checkIsOpen();
323     checkReadFuncs(I2C_SMBUS_BYTE_DATA);
324 
325     int ret = 0, retries = 0;
326     do
327     {
328         ret = i2c_smbus_read_byte_data(fd, addr);
329     } while ((ret < 0) && (++retries <= maxRetries));
330 
331     if (ret < 0)
332     {
333         throw I2CException("Failed to read byte data", busStr, devAddr, errno);
334     }
335 
336     data = static_cast<uint8_t>(ret);
337 }
338 
read(uint8_t addr,uint16_t & data)339 void I2CDevice::read(uint8_t addr, uint16_t& data)
340 {
341     checkIsOpen();
342     checkReadFuncs(I2C_SMBUS_WORD_DATA);
343 
344     int ret = 0, retries = 0;
345     do
346     {
347         ret = i2c_smbus_read_word_data(fd, addr);
348     } while ((ret < 0) && (++retries <= maxRetries));
349 
350     if (ret < 0)
351     {
352         throw I2CException("Failed to read word data", busStr, devAddr, errno);
353     }
354 
355     data = static_cast<uint16_t>(ret);
356 }
357 
read(uint8_t addr,uint8_t & size,uint8_t * data,Mode mode)358 void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data, Mode mode)
359 {
360     checkIsOpen();
361 
362     int ret = -1, retries = 0;
363     switch (mode)
364     {
365         case Mode::SMBUS:
366             checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
367             do
368             {
369                 ret = i2c_smbus_read_block_data(fd, addr, data);
370             } while ((ret < 0) && (++retries <= maxRetries));
371             break;
372         case Mode::I2C:
373             checkReadFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
374             do
375             {
376                 ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
377             } while ((ret < 0) && (++retries <= maxRetries));
378             if (ret != size)
379             {
380                 throw I2CException("Failed to read i2c block data", busStr,
381                                    devAddr, errno);
382             }
383             break;
384     }
385 
386     if (ret < 0)
387     {
388         throw I2CException("Failed to read block data", busStr, devAddr, errno);
389     }
390 
391     size = static_cast<uint8_t>(ret);
392 }
393 
write(uint8_t data)394 void I2CDevice::write(uint8_t data)
395 {
396     checkIsOpen();
397     checkWriteFuncs(I2C_SMBUS_BYTE);
398 
399     int ret = 0, retries = 0;
400     do
401     {
402         ret = i2c_smbus_write_byte(fd, data);
403     } while ((ret < 0) && (++retries <= maxRetries));
404 
405     if (ret < 0)
406     {
407         throw I2CException("Failed to write byte", busStr, devAddr, errno);
408     }
409 }
410 
write(uint8_t addr,uint8_t data)411 void I2CDevice::write(uint8_t addr, uint8_t data)
412 {
413     checkIsOpen();
414     checkWriteFuncs(I2C_SMBUS_BYTE_DATA);
415 
416     int ret = 0, retries = 0;
417     do
418     {
419         ret = i2c_smbus_write_byte_data(fd, addr, data);
420     } while ((ret < 0) && (++retries <= maxRetries));
421 
422     if (ret < 0)
423     {
424         throw I2CException("Failed to write byte data", busStr, devAddr, errno);
425     }
426 }
427 
write(uint8_t addr,uint16_t data)428 void I2CDevice::write(uint8_t addr, uint16_t data)
429 {
430     checkIsOpen();
431     checkWriteFuncs(I2C_SMBUS_WORD_DATA);
432 
433     int ret = 0, retries = 0;
434     do
435     {
436         ret = i2c_smbus_write_word_data(fd, addr, data);
437     } while ((ret < 0) && (++retries <= maxRetries));
438 
439     if (ret < 0)
440     {
441         throw I2CException("Failed to write word data", busStr, devAddr, errno);
442     }
443 }
444 
write(uint8_t addr,uint8_t size,const uint8_t * data,Mode mode)445 void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data,
446                       Mode mode)
447 {
448     checkIsOpen();
449 
450     int ret = -1, retries = 0;
451     switch (mode)
452     {
453         case Mode::SMBUS:
454             checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
455             do
456             {
457                 ret = i2c_smbus_write_block_data(fd, addr, size, data);
458             } while ((ret < 0) && (++retries <= maxRetries));
459             break;
460         case Mode::I2C:
461             checkWriteFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
462             do
463             {
464                 ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
465             } while ((ret < 0) && (++retries <= maxRetries));
466             break;
467     }
468 
469     if (ret < 0)
470     {
471         throw I2CException("Failed to write block data", busStr, devAddr,
472                            errno);
473     }
474 }
475 
processCall(uint8_t addr,uint16_t writeData,uint16_t & readData)476 void I2CDevice::processCall(uint8_t addr, uint16_t writeData,
477                             uint16_t& readData)
478 {
479     checkIsOpen();
480     checkWriteFuncs(I2C_SMBUS_PROC_CALL);
481 
482     int ret = 0, retries = 0;
483     do
484     {
485         ret = i2c_smbus_process_call(fd, addr, writeData);
486     } while ((ret < 0) && (++retries <= maxRetries));
487 
488     if (ret < 0)
489     {
490         throw I2CException("Failed to execute process call", busStr, devAddr,
491                            errno);
492     }
493 
494     readData = static_cast<uint16_t>(ret);
495 }
496 
processCall(uint8_t addr,uint8_t writeSize,const uint8_t * writeData,uint8_t & readSize,uint8_t * readData)497 void I2CDevice::processCall(uint8_t addr, uint8_t writeSize,
498                             const uint8_t* writeData, uint8_t& readSize,
499                             uint8_t* readData)
500 {
501     checkIsOpen();
502     unsigned long funcs = getFuncs();
503 
504     if ((funcs & I2C_FUNC_SMBUS_BLOCK_PROC_CALL) &&
505         (writeSize <= I2C_SMBUS_BLOCK_MAX))
506     {
507         // Use standard SMBus function which supports smaller SMBus 2.0 maximum
508         processCallSMBus(addr, writeSize, writeData, readSize, readData);
509     }
510     else if (funcs & I2C_FUNC_I2C)
511     {
512         // Use lower level I2C ioctl which supports larger SMBus 3.0 maximum
513         processCallI2C(addr, writeSize, writeData, readSize, readData);
514     }
515     else
516     {
517         throw I2CException(
518             std::format(
519                 "Block process call unsupported: writeSize={:d}, funcs={}",
520                 writeSize, funcs),
521             busStr, devAddr);
522     }
523 }
524 
create(uint8_t busId,uint8_t devAddr,InitialState initialState,int maxRetries)525 std::unique_ptr<I2CInterface> I2CDevice::create(
526     uint8_t busId, uint8_t devAddr, InitialState initialState, int maxRetries)
527 {
528     std::unique_ptr<I2CDevice> dev(
529         new I2CDevice(busId, devAddr, initialState, maxRetries));
530     return dev;
531 }
532 
533 std::unique_ptr<I2CInterface>
create(uint8_t busId,uint8_t devAddr,I2CInterface::InitialState initialState,int maxRetries)534     create(uint8_t busId, uint8_t devAddr,
535            I2CInterface::InitialState initialState, int maxRetries)
536 {
537     return I2CDevice::create(busId, devAddr, initialState, maxRetries);
538 }
539 
540 } // namespace i2c
541