blob: bad5b89d41e393339254ef4809d8280b078affd2 [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
Lei YU92e89eb2019-11-06 18:08:25 +080011extern "C" {
12#include <i2c/smbus.h>
13#include <linux/i2c-dev.h>
14#include <linux/i2c.h>
15}
16
Lei YUab1327c2019-11-04 16:53:39 +080017namespace i2c
18{
19
Shawn McCarney38ed88d2019-12-11 12:26:09 -060020unsigned long I2CDevice::getFuncs()
Lei YU92e89eb2019-11-06 18:08:25 +080021{
Shawn McCarney38ed88d2019-12-11 12:26:09 -060022 // If functionality has not been cached
23 if (cachedFuncs == NO_FUNCS)
Lei YU92e89eb2019-11-06 18:08:25 +080024 {
Shawn McCarney38ed88d2019-12-11 12:26:09 -060025 // Get functionality from adapter
26 if (ioctl(fd, I2C_FUNCS, &cachedFuncs) < 0)
27 {
28 throw I2CException("Failed to get funcs", busStr, devAddr, errno);
29 }
Lei YU92e89eb2019-11-06 18:08:25 +080030 }
31
Shawn McCarney38ed88d2019-12-11 12:26:09 -060032 return cachedFuncs;
33}
34
35void I2CDevice::checkReadFuncs(int type)
36{
37 unsigned long funcs = getFuncs();
Lei YU92e89eb2019-11-06 18:08:25 +080038 switch (type)
39 {
40 case I2C_SMBUS_BYTE:
41 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
42 {
43 throw I2CException("Missing SMBUS_READ_BYTE", busStr, devAddr);
44 }
45 break;
46 case I2C_SMBUS_BYTE_DATA:
47 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
48 {
49 throw I2CException("Missing SMBUS_READ_BYTE_DATA", busStr,
50 devAddr);
51 }
52 break;
53
54 case I2C_SMBUS_WORD_DATA:
55 if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
56 {
57 throw I2CException("Missing SMBUS_READ_WORD_DATA", busStr,
58 devAddr);
59 }
60 break;
61 case I2C_SMBUS_BLOCK_DATA:
62 if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
63 {
64 throw I2CException("Missing SMBUS_READ_BLOCK_DATA", busStr,
65 devAddr);
66 }
67 break;
Lei YU1d103422019-11-29 14:00:02 +080068 case I2C_SMBUS_I2C_BLOCK_DATA:
69 if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
70 {
71 throw I2CException("Missing I2C_FUNC_SMBUS_READ_I2C_BLOCK",
72 busStr, devAddr);
73 }
74 break;
Lei YU92e89eb2019-11-06 18:08:25 +080075 default:
76 fprintf(stderr, "Unexpected read size type: %d\n", type);
77 assert(false);
78 break;
79 }
80}
81
Lei YU34fb8bd2019-11-07 14:24:20 +080082void I2CDevice::checkWriteFuncs(int type)
83{
Shawn McCarney38ed88d2019-12-11 12:26:09 -060084 unsigned long funcs = getFuncs();
Lei YU34fb8bd2019-11-07 14:24:20 +080085 switch (type)
86 {
87 case I2C_SMBUS_BYTE:
88 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
89 {
90 throw I2CException("Missing SMBUS_WRITE_BYTE", busStr, devAddr);
91 }
92 break;
93 case I2C_SMBUS_BYTE_DATA:
94 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
95 {
96 throw I2CException("Missing SMBUS_WRITE_BYTE_DATA", busStr,
97 devAddr);
98 }
99 break;
100
101 case I2C_SMBUS_WORD_DATA:
102 if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
103 {
104 throw I2CException("Missing SMBUS_WRITE_WORD_DATA", busStr,
105 devAddr);
106 }
107 break;
108 case I2C_SMBUS_BLOCK_DATA:
109 if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
110 {
111 throw I2CException("Missing SMBUS_WRITE_BLOCK_DATA", busStr,
112 devAddr);
113 }
114 break;
Lei YU1d103422019-11-29 14:00:02 +0800115 case I2C_SMBUS_I2C_BLOCK_DATA:
116 if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
117 {
118 throw I2CException("Missing I2C_FUNC_SMBUS_WRITE_I2C_BLOCK",
119 busStr, devAddr);
120 }
121 break;
Lei YU34fb8bd2019-11-07 14:24:20 +0800122 default:
123 fprintf(stderr, "Unexpected read size type: %d\n", type);
124 assert(false);
125 }
126}
127
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600128void I2CDevice::open()
129{
130 if (isOpen())
131 {
132 throw I2CException("Device already open", busStr, devAddr);
133 }
134
135 fd = ::open(busStr.c_str(), O_RDWR);
136 if (fd == -1)
137 {
138 throw I2CException("Failed to open", busStr, devAddr, errno);
139 }
140
141 if (ioctl(fd, I2C_SLAVE, devAddr) < 0)
142 {
143 // Close device since setting slave address failed
144 closeWithoutException();
145
146 throw I2CException("Failed to set I2C_SLAVE", busStr, devAddr, errno);
147 }
148}
149
150void I2CDevice::close()
151{
152 checkIsOpen();
153 if (::close(fd) == -1)
154 {
155 throw I2CException("Failed to close", busStr, devAddr, errno);
156 }
157 fd = INVALID_FD;
Shawn McCarney38ed88d2019-12-11 12:26:09 -0600158 cachedFuncs = NO_FUNCS;
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600159}
160
Lei YUab1327c2019-11-04 16:53:39 +0800161void I2CDevice::read(uint8_t& data)
162{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600163 checkIsOpen();
Lei YU92e89eb2019-11-06 18:08:25 +0800164 checkReadFuncs(I2C_SMBUS_BYTE);
165
166 int ret = i2c_smbus_read_byte(fd);
167 if (ret < 0)
168 {
169 throw I2CException("Failed to read byte", busStr, devAddr, errno);
170 }
171 data = static_cast<uint8_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800172}
173
174void I2CDevice::read(uint8_t addr, uint8_t& data)
175{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600176 checkIsOpen();
Lei YU92e89eb2019-11-06 18:08:25 +0800177 checkReadFuncs(I2C_SMBUS_BYTE_DATA);
178
179 int ret = i2c_smbus_read_byte_data(fd, addr);
180 if (ret < 0)
181 {
182 throw I2CException("Failed to read byte data", busStr, devAddr, errno);
183 }
184 data = static_cast<uint8_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800185}
186
187void I2CDevice::read(uint8_t addr, uint16_t& data)
188{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600189 checkIsOpen();
Lei YU92e89eb2019-11-06 18:08:25 +0800190 checkReadFuncs(I2C_SMBUS_WORD_DATA);
191 int ret = i2c_smbus_read_word_data(fd, addr);
192 if (ret < 0)
193 {
194 throw I2CException("Failed to read word data", busStr, devAddr, errno);
195 }
196 data = static_cast<uint16_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800197}
198
Lei YU1d103422019-11-29 14:00:02 +0800199void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data, Mode mode)
Lei YUab1327c2019-11-04 16:53:39 +0800200{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600201 checkIsOpen();
Lei YU1d103422019-11-29 14:00:02 +0800202 int ret;
203 switch (mode)
204 {
205 case Mode::SMBUS:
206 checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
207 ret = i2c_smbus_read_block_data(fd, addr, data);
208 break;
209 case Mode::I2C:
210 checkReadFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
211 ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
212 if (ret != size)
213 {
214 throw I2CException("Failed to read i2c block data", busStr,
215 devAddr, errno);
216 }
217 break;
218 }
Lei YU92e89eb2019-11-06 18:08:25 +0800219 if (ret < 0)
220 {
221 throw I2CException("Failed to read block data", busStr, devAddr, errno);
222 }
223 size = static_cast<uint8_t>(ret);
Lei YUab1327c2019-11-04 16:53:39 +0800224}
225
226void I2CDevice::write(uint8_t data)
227{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600228 checkIsOpen();
Lei YU34fb8bd2019-11-07 14:24:20 +0800229 checkWriteFuncs(I2C_SMBUS_BYTE);
230
231 if (i2c_smbus_write_byte(fd, data) < 0)
232 {
233 throw I2CException("Failed to write byte", busStr, devAddr, errno);
234 }
Lei YUab1327c2019-11-04 16:53:39 +0800235}
236
237void I2CDevice::write(uint8_t addr, uint8_t data)
238{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600239 checkIsOpen();
Lei YU34fb8bd2019-11-07 14:24:20 +0800240 checkWriteFuncs(I2C_SMBUS_BYTE_DATA);
241
242 if (i2c_smbus_write_byte_data(fd, addr, data) < 0)
243 {
244 throw I2CException("Failed to write byte data", busStr, devAddr, errno);
245 }
Lei YUab1327c2019-11-04 16:53:39 +0800246}
247
248void I2CDevice::write(uint8_t addr, uint16_t data)
249{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600250 checkIsOpen();
Lei YU34fb8bd2019-11-07 14:24:20 +0800251 checkWriteFuncs(I2C_SMBUS_WORD_DATA);
252
253 if (i2c_smbus_write_word_data(fd, addr, data) < 0)
254 {
255 throw I2CException("Failed to write word data", busStr, devAddr, errno);
256 }
Lei YUab1327c2019-11-04 16:53:39 +0800257}
258
Lei YU1d103422019-11-29 14:00:02 +0800259void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data,
260 Mode mode)
Lei YUab1327c2019-11-04 16:53:39 +0800261{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600262 checkIsOpen();
Lei YU1d103422019-11-29 14:00:02 +0800263 int ret;
264 switch (mode)
265 {
266 case Mode::SMBUS:
267 checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
268 ret = i2c_smbus_write_block_data(fd, addr, size, data);
269 break;
270 case Mode::I2C:
271 checkWriteFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
272 ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
273 break;
274 }
275 if (ret < 0)
Lei YU34fb8bd2019-11-07 14:24:20 +0800276 {
277 throw I2CException("Failed to write block data", busStr, devAddr,
278 errno);
279 }
Lei YUab1327c2019-11-04 16:53:39 +0800280}
281
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600282std::unique_ptr<I2CInterface> I2CDevice::create(uint8_t busId, uint8_t devAddr,
283 InitialState initialState)
Lei YUab1327c2019-11-04 16:53:39 +0800284{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600285 std::unique_ptr<I2CDevice> dev(new I2CDevice(busId, devAddr, initialState));
Lei YUab1327c2019-11-04 16:53:39 +0800286 return dev;
287}
288
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600289std::unique_ptr<I2CInterface> create(uint8_t busId, uint8_t devAddr,
290 I2CInterface::InitialState initialState)
Lei YUab1327c2019-11-04 16:53:39 +0800291{
Shawn McCarneyd45a9a62019-12-10 18:35:44 -0600292 return I2CDevice::create(busId, devAddr, initialState);
Lei YUab1327c2019-11-04 16:53:39 +0800293}
294
295} // namespace i2c