blob: 7a0bbf47f3249f9fa0be349da42ea0cb994b145f [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
27 if (ioctl(fd, I2C_FUNCS, &cachedFuncs) < 0)
28 {
29 throw I2CException("Failed to get funcs", busStr, devAddr, errno);
30 }
Lei YU92e89eb2019-11-06 18:08:25 +080031 }
32
Shawn McCarney38ed88d2019-12-11 12:26:09 -060033 return cachedFuncs;
34}
35
36void I2CDevice::checkReadFuncs(int type)
37{
38 unsigned long funcs = getFuncs();
Lei YU92e89eb2019-11-06 18:08:25 +080039 switch (type)
40 {
41 case I2C_SMBUS_BYTE:
42 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
43 {
44 throw I2CException("Missing SMBUS_READ_BYTE", busStr, devAddr);
45 }
46 break;
47 case I2C_SMBUS_BYTE_DATA:
48 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
49 {
50 throw I2CException("Missing SMBUS_READ_BYTE_DATA", busStr,
51 devAddr);
52 }
53 break;
54
55 case I2C_SMBUS_WORD_DATA:
56 if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
57 {
58 throw I2CException("Missing SMBUS_READ_WORD_DATA", busStr,
59 devAddr);
60 }
61 break;
62 case I2C_SMBUS_BLOCK_DATA:
63 if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
64 {
65 throw I2CException("Missing SMBUS_READ_BLOCK_DATA", busStr,
66 devAddr);
67 }
68 break;
Lei YU1d103422019-11-29 14:00:02 +080069 case I2C_SMBUS_I2C_BLOCK_DATA:
70 if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
71 {
72 throw I2CException("Missing I2C_FUNC_SMBUS_READ_I2C_BLOCK",
73 busStr, devAddr);
74 }
75 break;
Lei YU92e89eb2019-11-06 18:08:25 +080076 default:
77 fprintf(stderr, "Unexpected read size type: %d\n", type);
78 assert(false);
79 break;
80 }
81}
82
Lei YU34fb8bd2019-11-07 14:24:20 +080083void I2CDevice::checkWriteFuncs(int type)
84{
Shawn McCarney38ed88d2019-12-11 12:26:09 -060085 unsigned long funcs = getFuncs();
Lei YU34fb8bd2019-11-07 14:24:20 +080086 switch (type)
87 {
88 case I2C_SMBUS_BYTE:
89 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
90 {
91 throw I2CException("Missing SMBUS_WRITE_BYTE", busStr, devAddr);
92 }
93 break;
94 case I2C_SMBUS_BYTE_DATA:
95 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
96 {
97 throw I2CException("Missing SMBUS_WRITE_BYTE_DATA", busStr,
98 devAddr);
99 }
100 break;
101
102 case I2C_SMBUS_WORD_DATA:
103 if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
104 {
105 throw I2CException("Missing SMBUS_WRITE_WORD_DATA", busStr,
106 devAddr);
107 }
108 break;
109 case I2C_SMBUS_BLOCK_DATA:
110 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
111 {
112 throw I2CException("Missing SMBUS_WRITE_BLOCK_DATA", busStr,
113 devAddr);
114 }
115 break;
Lei YU1d103422019-11-29 14:00:02 +0800116 case I2C_SMBUS_I2C_BLOCK_DATA:
117 if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
118 {
119 throw I2CException("Missing I2C_FUNC_SMBUS_WRITE_I2C_BLOCK",
120 busStr, devAddr);
121 }
122 break;
Lei YU34fb8bd2019-11-07 14:24:20 +0800123 default:
124 fprintf(stderr, "Unexpected read size type: %d\n", type);
125 assert(false);
126 }
127}
128
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600129void I2CDevice::open()
130{
131 if (isOpen())
132 {
133 throw I2CException("Device already open", busStr, devAddr);
134 }
135
136 fd = ::open(busStr.c_str(), O_RDWR);
137 if (fd == -1)
138 {
139 throw I2CException("Failed to open", busStr, devAddr, errno);
140 }
141
142 if (ioctl(fd, I2C_SLAVE, devAddr) < 0)
143 {
144 // Close device since setting slave address failed
145 closeWithoutException();
146
147 throw I2CException("Failed to set I2C_SLAVE", busStr, devAddr, errno);
148 }
149}
150
151void I2CDevice::close()
152{
153 checkIsOpen();
154 if (::close(fd) == -1)
155 {
156 throw I2CException("Failed to close", busStr, devAddr, errno);
157 }
158 fd = INVALID_FD;
Shawn McCarney38ed88d2019-12-11 12:26:09 -0600159 cachedFuncs = NO_FUNCS;
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600160}
161
Lei YUab1327c2019-11-04 16:53:39 +0800162void I2CDevice::read(uint8_t& data)
163{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600164 checkIsOpen();
Lei YU92e89eb2019-11-06 18:08:25 +0800165 checkReadFuncs(I2C_SMBUS_BYTE);
166
167 int ret = i2c_smbus_read_byte(fd);
168 if (ret < 0)
169 {
170 throw I2CException("Failed to read byte", busStr, devAddr, errno);
171 }
172 data = static_cast<uint8_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800173}
174
175void I2CDevice::read(uint8_t addr, uint8_t& data)
176{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600177 checkIsOpen();
Lei YU92e89eb2019-11-06 18:08:25 +0800178 checkReadFuncs(I2C_SMBUS_BYTE_DATA);
179
180 int ret = i2c_smbus_read_byte_data(fd, addr);
181 if (ret < 0)
182 {
183 throw I2CException("Failed to read byte data", busStr, devAddr, errno);
184 }
185 data = static_cast<uint8_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800186}
187
188void I2CDevice::read(uint8_t addr, uint16_t& data)
189{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600190 checkIsOpen();
Lei YU92e89eb2019-11-06 18:08:25 +0800191 checkReadFuncs(I2C_SMBUS_WORD_DATA);
192 int ret = i2c_smbus_read_word_data(fd, addr);
193 if (ret < 0)
194 {
195 throw I2CException("Failed to read word data", busStr, devAddr, errno);
196 }
197 data = static_cast<uint16_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800198}
199
Lei YU1d103422019-11-29 14:00:02 +0800200void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data, Mode mode)
Lei YUab1327c2019-11-04 16:53:39 +0800201{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600202 checkIsOpen();
Lei YU8429e882020-12-14 13:16:07 +0800203 int ret = -1;
Lei YU1d103422019-11-29 14:00:02 +0800204 switch (mode)
205 {
206 case Mode::SMBUS:
207 checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
208 ret = i2c_smbus_read_block_data(fd, addr, data);
209 break;
210 case Mode::I2C:
211 checkReadFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
212 ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
213 if (ret != size)
214 {
215 throw I2CException("Failed to read i2c block data", busStr,
216 devAddr, errno);
217 }
218 break;
219 }
Lei YU92e89eb2019-11-06 18:08:25 +0800220 if (ret < 0)
221 {
222 throw I2CException("Failed to read block data", busStr, devAddr, errno);
223 }
224 size = static_cast<uint8_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800225}
226
227void I2CDevice::write(uint8_t data)
228{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600229 checkIsOpen();
Lei YU34fb8bd2019-11-07 14:24:20 +0800230 checkWriteFuncs(I2C_SMBUS_BYTE);
231
232 if (i2c_smbus_write_byte(fd, data) < 0)
233 {
234 throw I2CException("Failed to write byte", busStr, devAddr, errno);
235 }
Lei YUab1327c2019-11-04 16:53:39 +0800236}
237
238void I2CDevice::write(uint8_t addr, uint8_t data)
239{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600240 checkIsOpen();
Lei YU34fb8bd2019-11-07 14:24:20 +0800241 checkWriteFuncs(I2C_SMBUS_BYTE_DATA);
242
243 if (i2c_smbus_write_byte_data(fd, addr, data) < 0)
244 {
245 throw I2CException("Failed to write byte data", busStr, devAddr, errno);
246 }
Lei YUab1327c2019-11-04 16:53:39 +0800247}
248
249void I2CDevice::write(uint8_t addr, uint16_t data)
250{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600251 checkIsOpen();
Lei YU34fb8bd2019-11-07 14:24:20 +0800252 checkWriteFuncs(I2C_SMBUS_WORD_DATA);
253
254 if (i2c_smbus_write_word_data(fd, addr, data) < 0)
255 {
256 throw I2CException("Failed to write word data", busStr, devAddr, errno);
257 }
Lei YUab1327c2019-11-04 16:53:39 +0800258}
259
Lei YU1d103422019-11-29 14:00:02 +0800260void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data,
261 Mode mode)
Lei YUab1327c2019-11-04 16:53:39 +0800262{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600263 checkIsOpen();
Lei YU8429e882020-12-14 13:16:07 +0800264 int ret = -1;
Lei YU1d103422019-11-29 14:00:02 +0800265 switch (mode)
266 {
267 case Mode::SMBUS:
268 checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
269 ret = i2c_smbus_write_block_data(fd, addr, size, data);
270 break;
271 case Mode::I2C:
272 checkWriteFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
273 ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
274 break;
275 }
276 if (ret < 0)
Lei YU34fb8bd2019-11-07 14:24:20 +0800277 {
278 throw I2CException("Failed to write block data", busStr, devAddr,
279 errno);
280 }
Lei YUab1327c2019-11-04 16:53:39 +0800281}
282
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600283std::unique_ptr<I2CInterface> I2CDevice::create(uint8_t busId, uint8_t devAddr,
284 InitialState initialState)
Lei YUab1327c2019-11-04 16:53:39 +0800285{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600286 std::unique_ptr<I2CDevice> dev(new I2CDevice(busId, devAddr, initialState));
Lei YUab1327c2019-11-04 16:53:39 +0800287 return dev;
288}
289
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600290std::unique_ptr<I2CInterface> create(uint8_t busId, uint8_t devAddr,
291 I2CInterface::InitialState initialState)
Lei YUab1327c2019-11-04 16:53:39 +0800292{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600293 return I2CDevice::create(busId, devAddr, initialState);
Lei YUab1327c2019-11-04 16:53:39 +0800294}
295
296} // namespace i2c