blob: 0dbfe9adb52575ee65218f492ea5fe8bbdb96450 [file] [log] [blame]
Lei YUab1327c2019-11-04 16:53:39 +08001#include "i2c.hpp"
2
Lei YU9af82a52019-11-06 14:51:22 +08003#include <fcntl.h>
Lei YU92e89eb2019-11-06 18:08:25 +08004#include <sys/ioctl.h>
Lei YU9af82a52019-11-06 14:51:22 +08005#include <sys/stat.h>
6#include <unistd.h>
7
Lei YU92e89eb2019-11-06 18:08:25 +08008#include <cassert>
Lei YU9af82a52019-11-06 14:51:22 +08009#include <cerrno>
10
Brandon Wymand1bc4ce2019-12-13 14:20:34 -060011extern "C"
12{
Lei YU92e89eb2019-11-06 18:08:25 +080013#include <i2c/smbus.h>
14#include <linux/i2c-dev.h>
15#include <linux/i2c.h>
16}
17
Lei YUab1327c2019-11-04 16:53:39 +080018namespace i2c
19{
20
Shawn McCarney38ed88d2019-12-11 12:26:09 -060021unsigned long I2CDevice::getFuncs()
Lei YU92e89eb2019-11-06 18:08:25 +080022{
Shawn McCarney38ed88d2019-12-11 12:26:09 -060023 // If functionality has not been cached
24 if (cachedFuncs == NO_FUNCS)
Lei YU92e89eb2019-11-06 18:08:25 +080025 {
Shawn McCarney38ed88d2019-12-11 12:26:09 -060026 // Get functionality from adapter
Shawn McCarney770de582021-11-05 02:28:35 -050027 int ret = 0, retries = 0;
28 do
29 {
30 ret = ioctl(fd, I2C_FUNCS, &cachedFuncs);
31 } while ((ret < 0) && (++retries <= maxRetries));
32
33 if (ret < 0)
Shawn McCarney38ed88d2019-12-11 12:26:09 -060034 {
35 throw I2CException("Failed to get funcs", busStr, devAddr, errno);
36 }
Lei YU92e89eb2019-11-06 18:08:25 +080037 }
38
Shawn McCarney38ed88d2019-12-11 12:26:09 -060039 return cachedFuncs;
40}
41
42void I2CDevice::checkReadFuncs(int type)
43{
44 unsigned long funcs = getFuncs();
Lei YU92e89eb2019-11-06 18:08:25 +080045 switch (type)
46 {
47 case I2C_SMBUS_BYTE:
48 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
49 {
50 throw I2CException("Missing SMBUS_READ_BYTE", busStr, devAddr);
51 }
52 break;
53 case I2C_SMBUS_BYTE_DATA:
54 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
55 {
56 throw I2CException("Missing SMBUS_READ_BYTE_DATA", busStr,
57 devAddr);
58 }
59 break;
Lei YU92e89eb2019-11-06 18:08:25 +080060 case I2C_SMBUS_WORD_DATA:
61 if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
62 {
63 throw I2CException("Missing SMBUS_READ_WORD_DATA", busStr,
64 devAddr);
65 }
66 break;
67 case I2C_SMBUS_BLOCK_DATA:
68 if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
69 {
70 throw I2CException("Missing SMBUS_READ_BLOCK_DATA", busStr,
71 devAddr);
72 }
73 break;
Lei YU1d103422019-11-29 14:00:02 +080074 case I2C_SMBUS_I2C_BLOCK_DATA:
75 if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
76 {
77 throw I2CException("Missing I2C_FUNC_SMBUS_READ_I2C_BLOCK",
78 busStr, devAddr);
79 }
80 break;
Lei YU92e89eb2019-11-06 18:08:25 +080081 default:
82 fprintf(stderr, "Unexpected read size type: %d\n", type);
83 assert(false);
84 break;
85 }
86}
87
Lei YU34fb8bd2019-11-07 14:24:20 +080088void I2CDevice::checkWriteFuncs(int type)
89{
Shawn McCarney38ed88d2019-12-11 12:26:09 -060090 unsigned long funcs = getFuncs();
Lei YU34fb8bd2019-11-07 14:24:20 +080091 switch (type)
92 {
93 case I2C_SMBUS_BYTE:
94 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
95 {
96 throw I2CException("Missing SMBUS_WRITE_BYTE", busStr, devAddr);
97 }
98 break;
99 case I2C_SMBUS_BYTE_DATA:
100 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
101 {
102 throw I2CException("Missing SMBUS_WRITE_BYTE_DATA", busStr,
103 devAddr);
104 }
105 break;
Lei YU34fb8bd2019-11-07 14:24:20 +0800106 case I2C_SMBUS_WORD_DATA:
107 if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
108 {
109 throw I2CException("Missing SMBUS_WRITE_WORD_DATA", busStr,
110 devAddr);
111 }
112 break;
113 case I2C_SMBUS_BLOCK_DATA:
114 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
115 {
116 throw I2CException("Missing SMBUS_WRITE_BLOCK_DATA", busStr,
117 devAddr);
118 }
119 break;
Lei YU1d103422019-11-29 14:00:02 +0800120 case I2C_SMBUS_I2C_BLOCK_DATA:
121 if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
122 {
123 throw I2CException("Missing I2C_FUNC_SMBUS_WRITE_I2C_BLOCK",
124 busStr, devAddr);
125 }
126 break;
Lei YU34fb8bd2019-11-07 14:24:20 +0800127 default:
Shawn McCarney770de582021-11-05 02:28:35 -0500128 fprintf(stderr, "Unexpected write size type: %d\n", type);
Lei YU34fb8bd2019-11-07 14:24:20 +0800129 assert(false);
130 }
131}
132
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600133void I2CDevice::open()
134{
135 if (isOpen())
136 {
137 throw I2CException("Device already open", busStr, devAddr);
138 }
139
Shawn McCarney770de582021-11-05 02:28:35 -0500140 int retries = 0;
141 do
142 {
143 fd = ::open(busStr.c_str(), O_RDWR);
144 } while ((fd == -1) && (++retries <= maxRetries));
145
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600146 if (fd == -1)
147 {
148 throw I2CException("Failed to open", busStr, devAddr, errno);
149 }
150
Shawn McCarney770de582021-11-05 02:28:35 -0500151 retries = 0;
152 int ret = 0;
153 do
154 {
155 ret = ioctl(fd, I2C_SLAVE, devAddr);
156 } while ((ret < 0) && (++retries <= maxRetries));
157
158 if (ret < 0)
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600159 {
160 // Close device since setting slave address failed
161 closeWithoutException();
162
163 throw I2CException("Failed to set I2C_SLAVE", busStr, devAddr, errno);
164 }
165}
166
167void I2CDevice::close()
168{
169 checkIsOpen();
Shawn McCarney770de582021-11-05 02:28:35 -0500170
171 int ret = 0, retries = 0;
172 do
173 {
174 ret = ::close(fd);
175 } while ((ret == -1) && (++retries <= maxRetries));
176
177 if (ret == -1)
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600178 {
179 throw I2CException("Failed to close", busStr, devAddr, errno);
180 }
Shawn McCarney770de582021-11-05 02:28:35 -0500181
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600182 fd = INVALID_FD;
Shawn McCarney38ed88d2019-12-11 12:26:09 -0600183 cachedFuncs = NO_FUNCS;
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600184}
185
Lei YUab1327c2019-11-04 16:53:39 +0800186void I2CDevice::read(uint8_t& data)
187{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600188 checkIsOpen();
Lei YU92e89eb2019-11-06 18:08:25 +0800189 checkReadFuncs(I2C_SMBUS_BYTE);
190
Shawn McCarney770de582021-11-05 02:28:35 -0500191 int ret = 0, retries = 0;
192 do
193 {
194 ret = i2c_smbus_read_byte(fd);
195 } while ((ret < 0) && (++retries <= maxRetries));
196
Lei YU92e89eb2019-11-06 18:08:25 +0800197 if (ret < 0)
198 {
199 throw I2CException("Failed to read byte", busStr, devAddr, errno);
200 }
Shawn McCarney770de582021-11-05 02:28:35 -0500201
Lei YU92e89eb2019-11-06 18:08:25 +0800202 data = static_cast<uint8_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800203}
204
205void I2CDevice::read(uint8_t addr, uint8_t& data)
206{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600207 checkIsOpen();
Lei YU92e89eb2019-11-06 18:08:25 +0800208 checkReadFuncs(I2C_SMBUS_BYTE_DATA);
209
Shawn McCarney770de582021-11-05 02:28:35 -0500210 int ret = 0, retries = 0;
211 do
212 {
213 ret = i2c_smbus_read_byte_data(fd, addr);
214 } while ((ret < 0) && (++retries <= maxRetries));
215
Lei YU92e89eb2019-11-06 18:08:25 +0800216 if (ret < 0)
217 {
218 throw I2CException("Failed to read byte data", busStr, devAddr, errno);
219 }
Shawn McCarney770de582021-11-05 02:28:35 -0500220
Lei YU92e89eb2019-11-06 18:08:25 +0800221 data = static_cast<uint8_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800222}
223
224void I2CDevice::read(uint8_t addr, uint16_t& data)
225{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600226 checkIsOpen();
Lei YU92e89eb2019-11-06 18:08:25 +0800227 checkReadFuncs(I2C_SMBUS_WORD_DATA);
Shawn McCarney770de582021-11-05 02:28:35 -0500228
229 int ret = 0, retries = 0;
230 do
231 {
232 ret = i2c_smbus_read_word_data(fd, addr);
233 } while ((ret < 0) && (++retries <= maxRetries));
234
Lei YU92e89eb2019-11-06 18:08:25 +0800235 if (ret < 0)
236 {
237 throw I2CException("Failed to read word data", busStr, devAddr, errno);
238 }
Shawn McCarney770de582021-11-05 02:28:35 -0500239
Lei YU92e89eb2019-11-06 18:08:25 +0800240 data = static_cast<uint16_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800241}
242
Lei YU1d103422019-11-29 14:00:02 +0800243void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data, Mode mode)
Lei YUab1327c2019-11-04 16:53:39 +0800244{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600245 checkIsOpen();
Shawn McCarney770de582021-11-05 02:28:35 -0500246
247 int ret = -1, retries = 0;
Lei YU1d103422019-11-29 14:00:02 +0800248 switch (mode)
249 {
250 case Mode::SMBUS:
251 checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
Shawn McCarney770de582021-11-05 02:28:35 -0500252 do
253 {
254 ret = i2c_smbus_read_block_data(fd, addr, data);
255 } while ((ret < 0) && (++retries <= maxRetries));
Lei YU1d103422019-11-29 14:00:02 +0800256 break;
257 case Mode::I2C:
258 checkReadFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
Shawn McCarney770de582021-11-05 02:28:35 -0500259 do
260 {
261 ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
262 } while ((ret < 0) && (++retries <= maxRetries));
Lei YU1d103422019-11-29 14:00:02 +0800263 if (ret != size)
264 {
265 throw I2CException("Failed to read i2c block data", busStr,
266 devAddr, errno);
267 }
268 break;
269 }
Shawn McCarney770de582021-11-05 02:28:35 -0500270
Lei YU92e89eb2019-11-06 18:08:25 +0800271 if (ret < 0)
272 {
273 throw I2CException("Failed to read block data", busStr, devAddr, errno);
274 }
Shawn McCarney770de582021-11-05 02:28:35 -0500275
Lei YU92e89eb2019-11-06 18:08:25 +0800276 size = static_cast<uint8_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800277}
278
279void I2CDevice::write(uint8_t data)
280{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600281 checkIsOpen();
Lei YU34fb8bd2019-11-07 14:24:20 +0800282 checkWriteFuncs(I2C_SMBUS_BYTE);
283
Shawn McCarney770de582021-11-05 02:28:35 -0500284 int ret = 0, retries = 0;
285 do
286 {
287 ret = i2c_smbus_write_byte(fd, data);
288 } while ((ret < 0) && (++retries <= maxRetries));
289
290 if (ret < 0)
Lei YU34fb8bd2019-11-07 14:24:20 +0800291 {
292 throw I2CException("Failed to write byte", busStr, devAddr, errno);
293 }
Lei YUab1327c2019-11-04 16:53:39 +0800294}
295
296void I2CDevice::write(uint8_t addr, uint8_t data)
297{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600298 checkIsOpen();
Lei YU34fb8bd2019-11-07 14:24:20 +0800299 checkWriteFuncs(I2C_SMBUS_BYTE_DATA);
300
Shawn McCarney770de582021-11-05 02:28:35 -0500301 int ret = 0, retries = 0;
302 do
303 {
304 ret = i2c_smbus_write_byte_data(fd, addr, data);
305 } while ((ret < 0) && (++retries <= maxRetries));
306
307 if (ret < 0)
Lei YU34fb8bd2019-11-07 14:24:20 +0800308 {
309 throw I2CException("Failed to write byte data", busStr, devAddr, errno);
310 }
Lei YUab1327c2019-11-04 16:53:39 +0800311}
312
313void I2CDevice::write(uint8_t addr, uint16_t data)
314{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600315 checkIsOpen();
Lei YU34fb8bd2019-11-07 14:24:20 +0800316 checkWriteFuncs(I2C_SMBUS_WORD_DATA);
317
Shawn McCarney770de582021-11-05 02:28:35 -0500318 int ret = 0, retries = 0;
319 do
320 {
321 ret = i2c_smbus_write_word_data(fd, addr, data);
322 } while ((ret < 0) && (++retries <= maxRetries));
323
324 if (ret < 0)
Lei YU34fb8bd2019-11-07 14:24:20 +0800325 {
326 throw I2CException("Failed to write word data", busStr, devAddr, errno);
327 }
Lei YUab1327c2019-11-04 16:53:39 +0800328}
329
Lei YU1d103422019-11-29 14:00:02 +0800330void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data,
331 Mode mode)
Lei YUab1327c2019-11-04 16:53:39 +0800332{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600333 checkIsOpen();
Shawn McCarney770de582021-11-05 02:28:35 -0500334
335 int ret = -1, retries = 0;
Lei YU1d103422019-11-29 14:00:02 +0800336 switch (mode)
337 {
338 case Mode::SMBUS:
339 checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
Shawn McCarney770de582021-11-05 02:28:35 -0500340 do
341 {
342 ret = i2c_smbus_write_block_data(fd, addr, size, data);
343 } while ((ret < 0) && (++retries <= maxRetries));
Lei YU1d103422019-11-29 14:00:02 +0800344 break;
345 case Mode::I2C:
346 checkWriteFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
Shawn McCarney770de582021-11-05 02:28:35 -0500347 do
348 {
349 ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
350 } while ((ret < 0) && (++retries <= maxRetries));
Lei YU1d103422019-11-29 14:00:02 +0800351 break;
352 }
Shawn McCarney770de582021-11-05 02:28:35 -0500353
Lei YU1d103422019-11-29 14:00:02 +0800354 if (ret < 0)
Lei YU34fb8bd2019-11-07 14:24:20 +0800355 {
356 throw I2CException("Failed to write block data", busStr, devAddr,
357 errno);
358 }
Lei YUab1327c2019-11-04 16:53:39 +0800359}
360
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600361std::unique_ptr<I2CInterface> I2CDevice::create(uint8_t busId, uint8_t devAddr,
Shawn McCarney770de582021-11-05 02:28:35 -0500362 InitialState initialState,
363 int maxRetries)
Lei YUab1327c2019-11-04 16:53:39 +0800364{
Shawn McCarney770de582021-11-05 02:28:35 -0500365 std::unique_ptr<I2CDevice> dev(
366 new I2CDevice(busId, devAddr, initialState, maxRetries));
Lei YUab1327c2019-11-04 16:53:39 +0800367 return dev;
368}
369
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600370std::unique_ptr<I2CInterface> create(uint8_t busId, uint8_t devAddr,
Shawn McCarney770de582021-11-05 02:28:35 -0500371 I2CInterface::InitialState initialState,
372 int maxRetries)
Lei YUab1327c2019-11-04 16:53:39 +0800373{
Shawn McCarney770de582021-11-05 02:28:35 -0500374 return I2CDevice::create(busId, devAddr, initialState, maxRetries);
Lei YUab1327c2019-11-04 16:53:39 +0800375}
376
377} // namespace i2c