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>
109af82a5cSLei YU
11d1bc4cecSBrandon Wyman extern "C"
12d1bc4cecSBrandon Wyman {
1392e89eb5SLei YU #include <i2c/smbus.h>
1492e89eb5SLei YU #include <linux/i2c-dev.h>
1592e89eb5SLei YU #include <linux/i2c.h>
1692e89eb5SLei YU }
1792e89eb5SLei YU
18ab1327c3SLei YU namespace i2c
19ab1327c3SLei YU {
20ab1327c3SLei YU
getFuncs()2138ed88d6SShawn McCarney unsigned long I2CDevice::getFuncs()
2292e89eb5SLei YU {
2338ed88d6SShawn McCarney // If functionality has not been cached
2438ed88d6SShawn McCarney if (cachedFuncs == NO_FUNCS)
2538ed88d6SShawn McCarney {
2638ed88d6SShawn McCarney // Get functionality from adapter
27*770de580SShawn McCarney int ret = 0, retries = 0;
28*770de580SShawn McCarney do
29*770de580SShawn McCarney {
30*770de580SShawn McCarney ret = ioctl(fd, I2C_FUNCS, &cachedFuncs);
31*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
32*770de580SShawn McCarney
33*770de580SShawn McCarney if (ret < 0)
3492e89eb5SLei YU {
3592e89eb5SLei YU throw I2CException("Failed to get funcs", busStr, devAddr, errno);
3692e89eb5SLei YU }
3738ed88d6SShawn McCarney }
3892e89eb5SLei YU
3938ed88d6SShawn McCarney return cachedFuncs;
4038ed88d6SShawn McCarney }
4138ed88d6SShawn McCarney
checkReadFuncs(int type)4238ed88d6SShawn McCarney void I2CDevice::checkReadFuncs(int type)
4338ed88d6SShawn McCarney {
4438ed88d6SShawn McCarney unsigned long funcs = getFuncs();
4592e89eb5SLei YU switch (type)
4692e89eb5SLei YU {
4792e89eb5SLei YU case I2C_SMBUS_BYTE:
4892e89eb5SLei YU if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
4992e89eb5SLei YU {
5092e89eb5SLei YU throw I2CException("Missing SMBUS_READ_BYTE", busStr, devAddr);
5192e89eb5SLei YU }
5292e89eb5SLei YU break;
5392e89eb5SLei YU case I2C_SMBUS_BYTE_DATA:
5492e89eb5SLei YU if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
5592e89eb5SLei YU {
5692e89eb5SLei YU throw I2CException("Missing SMBUS_READ_BYTE_DATA", busStr,
5792e89eb5SLei YU devAddr);
5892e89eb5SLei YU }
5992e89eb5SLei YU break;
6092e89eb5SLei YU case I2C_SMBUS_WORD_DATA:
6192e89eb5SLei YU if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
6292e89eb5SLei YU {
6392e89eb5SLei YU throw I2CException("Missing SMBUS_READ_WORD_DATA", busStr,
6492e89eb5SLei YU devAddr);
6592e89eb5SLei YU }
6692e89eb5SLei YU break;
6792e89eb5SLei YU case I2C_SMBUS_BLOCK_DATA:
6892e89eb5SLei YU if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
6992e89eb5SLei YU {
7092e89eb5SLei YU throw I2CException("Missing SMBUS_READ_BLOCK_DATA", busStr,
7192e89eb5SLei YU devAddr);
7292e89eb5SLei YU }
7392e89eb5SLei YU break;
741d103428SLei YU case I2C_SMBUS_I2C_BLOCK_DATA:
751d103428SLei YU if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
761d103428SLei YU {
771d103428SLei YU throw I2CException("Missing I2C_FUNC_SMBUS_READ_I2C_BLOCK",
781d103428SLei YU busStr, devAddr);
791d103428SLei YU }
801d103428SLei YU break;
8192e89eb5SLei YU default:
8292e89eb5SLei YU fprintf(stderr, "Unexpected read size type: %d\n", type);
8392e89eb5SLei YU assert(false);
8492e89eb5SLei YU break;
8592e89eb5SLei YU }
8692e89eb5SLei YU }
8792e89eb5SLei YU
checkWriteFuncs(int type)8834fb8bdaSLei YU void I2CDevice::checkWriteFuncs(int type)
8934fb8bdaSLei YU {
9038ed88d6SShawn McCarney unsigned long funcs = getFuncs();
9134fb8bdaSLei YU switch (type)
9234fb8bdaSLei YU {
9334fb8bdaSLei YU case I2C_SMBUS_BYTE:
9434fb8bdaSLei YU if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
9534fb8bdaSLei YU {
9634fb8bdaSLei YU throw I2CException("Missing SMBUS_WRITE_BYTE", busStr, devAddr);
9734fb8bdaSLei YU }
9834fb8bdaSLei YU break;
9934fb8bdaSLei YU case I2C_SMBUS_BYTE_DATA:
10034fb8bdaSLei YU if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
10134fb8bdaSLei YU {
10234fb8bdaSLei YU throw I2CException("Missing SMBUS_WRITE_BYTE_DATA", busStr,
10334fb8bdaSLei YU devAddr);
10434fb8bdaSLei YU }
10534fb8bdaSLei YU break;
10634fb8bdaSLei YU case I2C_SMBUS_WORD_DATA:
10734fb8bdaSLei YU if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
10834fb8bdaSLei YU {
10934fb8bdaSLei YU throw I2CException("Missing SMBUS_WRITE_WORD_DATA", busStr,
11034fb8bdaSLei YU devAddr);
11134fb8bdaSLei YU }
11234fb8bdaSLei YU break;
11334fb8bdaSLei YU case I2C_SMBUS_BLOCK_DATA:
11434fb8bdaSLei YU if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
11534fb8bdaSLei YU {
11634fb8bdaSLei YU throw I2CException("Missing SMBUS_WRITE_BLOCK_DATA", busStr,
11734fb8bdaSLei YU devAddr);
11834fb8bdaSLei YU }
11934fb8bdaSLei YU break;
1201d103428SLei YU case I2C_SMBUS_I2C_BLOCK_DATA:
1211d103428SLei YU if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
1221d103428SLei YU {
1231d103428SLei YU throw I2CException("Missing I2C_FUNC_SMBUS_WRITE_I2C_BLOCK",
1241d103428SLei YU busStr, devAddr);
1251d103428SLei YU }
1261d103428SLei YU break;
12734fb8bdaSLei YU default:
128*770de580SShawn McCarney fprintf(stderr, "Unexpected write size type: %d\n", type);
12934fb8bdaSLei YU assert(false);
13034fb8bdaSLei YU }
13134fb8bdaSLei YU }
13234fb8bdaSLei YU
open()133d45a9a6dSShawn McCarney void I2CDevice::open()
134d45a9a6dSShawn McCarney {
135d45a9a6dSShawn McCarney if (isOpen())
136d45a9a6dSShawn McCarney {
137d45a9a6dSShawn McCarney throw I2CException("Device already open", busStr, devAddr);
138d45a9a6dSShawn McCarney }
139d45a9a6dSShawn McCarney
140*770de580SShawn McCarney int retries = 0;
141*770de580SShawn McCarney do
142*770de580SShawn McCarney {
143d45a9a6dSShawn McCarney fd = ::open(busStr.c_str(), O_RDWR);
144*770de580SShawn McCarney } while ((fd == -1) && (++retries <= maxRetries));
145*770de580SShawn McCarney
146d45a9a6dSShawn McCarney if (fd == -1)
147d45a9a6dSShawn McCarney {
148d45a9a6dSShawn McCarney throw I2CException("Failed to open", busStr, devAddr, errno);
149d45a9a6dSShawn McCarney }
150d45a9a6dSShawn McCarney
151*770de580SShawn McCarney retries = 0;
152*770de580SShawn McCarney int ret = 0;
153*770de580SShawn McCarney do
154*770de580SShawn McCarney {
155*770de580SShawn McCarney ret = ioctl(fd, I2C_SLAVE, devAddr);
156*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
157*770de580SShawn McCarney
158*770de580SShawn McCarney if (ret < 0)
159d45a9a6dSShawn McCarney {
160d45a9a6dSShawn McCarney // Close device since setting slave address failed
161d45a9a6dSShawn McCarney closeWithoutException();
162d45a9a6dSShawn McCarney
163d45a9a6dSShawn McCarney throw I2CException("Failed to set I2C_SLAVE", busStr, devAddr, errno);
164d45a9a6dSShawn McCarney }
165d45a9a6dSShawn McCarney }
166d45a9a6dSShawn McCarney
close()167d45a9a6dSShawn McCarney void I2CDevice::close()
168d45a9a6dSShawn McCarney {
169d45a9a6dSShawn McCarney checkIsOpen();
170*770de580SShawn McCarney
171*770de580SShawn McCarney int ret = 0, retries = 0;
172*770de580SShawn McCarney do
173*770de580SShawn McCarney {
174*770de580SShawn McCarney ret = ::close(fd);
175*770de580SShawn McCarney } while ((ret == -1) && (++retries <= maxRetries));
176*770de580SShawn McCarney
177*770de580SShawn McCarney if (ret == -1)
178d45a9a6dSShawn McCarney {
179d45a9a6dSShawn McCarney throw I2CException("Failed to close", busStr, devAddr, errno);
180d45a9a6dSShawn McCarney }
181*770de580SShawn McCarney
182d45a9a6dSShawn McCarney fd = INVALID_FD;
18338ed88d6SShawn McCarney cachedFuncs = NO_FUNCS;
184d45a9a6dSShawn McCarney }
185d45a9a6dSShawn McCarney
read(uint8_t & data)186ab1327c3SLei YU void I2CDevice::read(uint8_t& data)
187ab1327c3SLei YU {
188d45a9a6dSShawn McCarney checkIsOpen();
18992e89eb5SLei YU checkReadFuncs(I2C_SMBUS_BYTE);
19092e89eb5SLei YU
191*770de580SShawn McCarney int ret = 0, retries = 0;
192*770de580SShawn McCarney do
193*770de580SShawn McCarney {
194*770de580SShawn McCarney ret = i2c_smbus_read_byte(fd);
195*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
196*770de580SShawn McCarney
19792e89eb5SLei YU if (ret < 0)
19892e89eb5SLei YU {
19992e89eb5SLei YU throw I2CException("Failed to read byte", busStr, devAddr, errno);
20092e89eb5SLei YU }
201*770de580SShawn McCarney
20292e89eb5SLei YU data = static_cast<uint8_t>(ret);
203ab1327c3SLei YU }
204ab1327c3SLei YU
read(uint8_t addr,uint8_t & data)205ab1327c3SLei YU void I2CDevice::read(uint8_t addr, uint8_t& data)
206ab1327c3SLei YU {
207d45a9a6dSShawn McCarney checkIsOpen();
20892e89eb5SLei YU checkReadFuncs(I2C_SMBUS_BYTE_DATA);
20992e89eb5SLei YU
210*770de580SShawn McCarney int ret = 0, retries = 0;
211*770de580SShawn McCarney do
212*770de580SShawn McCarney {
213*770de580SShawn McCarney ret = i2c_smbus_read_byte_data(fd, addr);
214*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
215*770de580SShawn McCarney
21692e89eb5SLei YU if (ret < 0)
21792e89eb5SLei YU {
21892e89eb5SLei YU throw I2CException("Failed to read byte data", busStr, devAddr, errno);
21992e89eb5SLei YU }
220*770de580SShawn McCarney
22192e89eb5SLei YU data = static_cast<uint8_t>(ret);
222ab1327c3SLei YU }
223ab1327c3SLei YU
read(uint8_t addr,uint16_t & data)224ab1327c3SLei YU void I2CDevice::read(uint8_t addr, uint16_t& data)
225ab1327c3SLei YU {
226d45a9a6dSShawn McCarney checkIsOpen();
22792e89eb5SLei YU checkReadFuncs(I2C_SMBUS_WORD_DATA);
228*770de580SShawn McCarney
229*770de580SShawn McCarney int ret = 0, retries = 0;
230*770de580SShawn McCarney do
231*770de580SShawn McCarney {
232*770de580SShawn McCarney ret = i2c_smbus_read_word_data(fd, addr);
233*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
234*770de580SShawn McCarney
23592e89eb5SLei YU if (ret < 0)
23692e89eb5SLei YU {
23792e89eb5SLei YU throw I2CException("Failed to read word data", busStr, devAddr, errno);
23892e89eb5SLei YU }
239*770de580SShawn McCarney
24092e89eb5SLei YU data = static_cast<uint16_t>(ret);
241ab1327c3SLei YU }
242ab1327c3SLei YU
read(uint8_t addr,uint8_t & size,uint8_t * data,Mode mode)2431d103428SLei YU void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data, Mode mode)
244ab1327c3SLei YU {
245d45a9a6dSShawn McCarney checkIsOpen();
246*770de580SShawn McCarney
247*770de580SShawn McCarney int ret = -1, retries = 0;
2481d103428SLei YU switch (mode)
2491d103428SLei YU {
2501d103428SLei YU case Mode::SMBUS:
25192e89eb5SLei YU checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
252*770de580SShawn McCarney do
253*770de580SShawn McCarney {
2541d103428SLei YU ret = i2c_smbus_read_block_data(fd, addr, data);
255*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
2561d103428SLei YU break;
2571d103428SLei YU case Mode::I2C:
2581d103428SLei YU checkReadFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
259*770de580SShawn McCarney do
260*770de580SShawn McCarney {
2611d103428SLei YU ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
262*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
2631d103428SLei YU if (ret != size)
2641d103428SLei YU {
2651d103428SLei YU throw I2CException("Failed to read i2c block data", busStr,
2661d103428SLei YU devAddr, errno);
2671d103428SLei YU }
2681d103428SLei YU break;
2691d103428SLei YU }
270*770de580SShawn McCarney
27192e89eb5SLei YU if (ret < 0)
27292e89eb5SLei YU {
27392e89eb5SLei YU throw I2CException("Failed to read block data", busStr, devAddr, errno);
27492e89eb5SLei YU }
275*770de580SShawn McCarney
27692e89eb5SLei YU size = static_cast<uint8_t>(ret);
277ab1327c3SLei YU }
278ab1327c3SLei YU
write(uint8_t data)279ab1327c3SLei YU void I2CDevice::write(uint8_t data)
280ab1327c3SLei YU {
281d45a9a6dSShawn McCarney checkIsOpen();
28234fb8bdaSLei YU checkWriteFuncs(I2C_SMBUS_BYTE);
28334fb8bdaSLei YU
284*770de580SShawn McCarney int ret = 0, retries = 0;
285*770de580SShawn McCarney do
286*770de580SShawn McCarney {
287*770de580SShawn McCarney ret = i2c_smbus_write_byte(fd, data);
288*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
289*770de580SShawn McCarney
290*770de580SShawn McCarney if (ret < 0)
29134fb8bdaSLei YU {
29234fb8bdaSLei YU throw I2CException("Failed to write byte", busStr, devAddr, errno);
29334fb8bdaSLei YU }
294ab1327c3SLei YU }
295ab1327c3SLei YU
write(uint8_t addr,uint8_t data)296ab1327c3SLei YU void I2CDevice::write(uint8_t addr, uint8_t data)
297ab1327c3SLei YU {
298d45a9a6dSShawn McCarney checkIsOpen();
29934fb8bdaSLei YU checkWriteFuncs(I2C_SMBUS_BYTE_DATA);
30034fb8bdaSLei YU
301*770de580SShawn McCarney int ret = 0, retries = 0;
302*770de580SShawn McCarney do
303*770de580SShawn McCarney {
304*770de580SShawn McCarney ret = i2c_smbus_write_byte_data(fd, addr, data);
305*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
306*770de580SShawn McCarney
307*770de580SShawn McCarney if (ret < 0)
30834fb8bdaSLei YU {
30934fb8bdaSLei YU throw I2CException("Failed to write byte data", busStr, devAddr, errno);
31034fb8bdaSLei YU }
311ab1327c3SLei YU }
312ab1327c3SLei YU
write(uint8_t addr,uint16_t data)313ab1327c3SLei YU void I2CDevice::write(uint8_t addr, uint16_t data)
314ab1327c3SLei YU {
315d45a9a6dSShawn McCarney checkIsOpen();
31634fb8bdaSLei YU checkWriteFuncs(I2C_SMBUS_WORD_DATA);
31734fb8bdaSLei YU
318*770de580SShawn McCarney int ret = 0, retries = 0;
319*770de580SShawn McCarney do
320*770de580SShawn McCarney {
321*770de580SShawn McCarney ret = i2c_smbus_write_word_data(fd, addr, data);
322*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
323*770de580SShawn McCarney
324*770de580SShawn McCarney if (ret < 0)
32534fb8bdaSLei YU {
32634fb8bdaSLei YU throw I2CException("Failed to write word data", busStr, devAddr, errno);
32734fb8bdaSLei YU }
328ab1327c3SLei YU }
329ab1327c3SLei YU
write(uint8_t addr,uint8_t size,const uint8_t * data,Mode mode)3301d103428SLei YU void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data,
3311d103428SLei YU Mode mode)
332ab1327c3SLei YU {
333d45a9a6dSShawn McCarney checkIsOpen();
334*770de580SShawn McCarney
335*770de580SShawn McCarney int ret = -1, retries = 0;
3361d103428SLei YU switch (mode)
3371d103428SLei YU {
3381d103428SLei YU case Mode::SMBUS:
33934fb8bdaSLei YU checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
340*770de580SShawn McCarney do
341*770de580SShawn McCarney {
3421d103428SLei YU ret = i2c_smbus_write_block_data(fd, addr, size, data);
343*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
3441d103428SLei YU break;
3451d103428SLei YU case Mode::I2C:
3461d103428SLei YU checkWriteFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
347*770de580SShawn McCarney do
348*770de580SShawn McCarney {
3491d103428SLei YU ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
350*770de580SShawn McCarney } while ((ret < 0) && (++retries <= maxRetries));
3511d103428SLei YU break;
3521d103428SLei YU }
353*770de580SShawn McCarney
3541d103428SLei YU if (ret < 0)
35534fb8bdaSLei YU {
35634fb8bdaSLei YU throw I2CException("Failed to write block data", busStr, devAddr,
35734fb8bdaSLei YU errno);
35834fb8bdaSLei YU }
359ab1327c3SLei YU }
360ab1327c3SLei YU
create(uint8_t busId,uint8_t devAddr,InitialState initialState,int maxRetries)361d45a9a6dSShawn McCarney std::unique_ptr<I2CInterface> I2CDevice::create(uint8_t busId, uint8_t devAddr,
362*770de580SShawn McCarney InitialState initialState,
363*770de580SShawn McCarney int maxRetries)
364ab1327c3SLei YU {
365*770de580SShawn McCarney std::unique_ptr<I2CDevice> dev(
366*770de580SShawn McCarney new I2CDevice(busId, devAddr, initialState, maxRetries));
367ab1327c3SLei YU return dev;
368ab1327c3SLei YU }
369ab1327c3SLei YU
create(uint8_t busId,uint8_t devAddr,I2CInterface::InitialState initialState,int maxRetries)370d45a9a6dSShawn McCarney std::unique_ptr<I2CInterface> create(uint8_t busId, uint8_t devAddr,
371*770de580SShawn McCarney I2CInterface::InitialState initialState,
372*770de580SShawn McCarney int maxRetries)
373ab1327c3SLei YU {
374*770de580SShawn McCarney return I2CDevice::create(busId, devAddr, initialState, maxRetries);
375ab1327c3SLei YU }
376ab1327c3SLei YU
377ab1327c3SLei YU } // namespace i2c
378