xref: /openbmc/phosphor-power/tools/i2c/i2c.cpp (revision 4c94bc7c840d2bfda6ac7acec811f80fdf3f3b7a)
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 
11 extern "C" {
12 #include <i2c/smbus.h>
13 #include <linux/i2c-dev.h>
14 #include <linux/i2c.h>
15 }
16 
17 namespace i2c
18 {
19 
20 unsigned long I2CDevice::getFuncs()
21 {
22     // If functionality has not been cached
23     if (cachedFuncs == NO_FUNCS)
24     {
25         // Get functionality from adapter
26         if (ioctl(fd, I2C_FUNCS, &cachedFuncs) < 0)
27         {
28             throw I2CException("Failed to get funcs", busStr, devAddr, errno);
29         }
30     }
31 
32     return cachedFuncs;
33 }
34 
35 void I2CDevice::checkReadFuncs(int type)
36 {
37     unsigned long funcs = getFuncs();
38     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;
68         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;
75         default:
76             fprintf(stderr, "Unexpected read size type: %d\n", type);
77             assert(false);
78             break;
79     }
80 }
81 
82 void I2CDevice::checkWriteFuncs(int type)
83 {
84     unsigned long funcs = getFuncs();
85     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;
115         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;
122         default:
123             fprintf(stderr, "Unexpected read size type: %d\n", type);
124             assert(false);
125     }
126 }
127 
128 void 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 
150 void 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;
158     cachedFuncs = NO_FUNCS;
159 }
160 
161 void I2CDevice::read(uint8_t& data)
162 {
163     checkIsOpen();
164     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);
172 }
173 
174 void I2CDevice::read(uint8_t addr, uint8_t& data)
175 {
176     checkIsOpen();
177     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);
185 }
186 
187 void I2CDevice::read(uint8_t addr, uint16_t& data)
188 {
189     checkIsOpen();
190     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);
197 }
198 
199 void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data, Mode mode)
200 {
201     checkIsOpen();
202     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     }
219     if (ret < 0)
220     {
221         throw I2CException("Failed to read block data", busStr, devAddr, errno);
222     }
223     size = static_cast<uint8_t>(ret);
224 }
225 
226 void I2CDevice::write(uint8_t data)
227 {
228     checkIsOpen();
229     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     }
235 }
236 
237 void I2CDevice::write(uint8_t addr, uint8_t data)
238 {
239     checkIsOpen();
240     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     }
246 }
247 
248 void I2CDevice::write(uint8_t addr, uint16_t data)
249 {
250     checkIsOpen();
251     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     }
257 }
258 
259 void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data,
260                       Mode mode)
261 {
262     checkIsOpen();
263     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)
276     {
277         throw I2CException("Failed to write block data", busStr, devAddr,
278                            errno);
279     }
280 }
281 
282 std::unique_ptr<I2CInterface> I2CDevice::create(uint8_t busId, uint8_t devAddr,
283                                                 InitialState initialState)
284 {
285     std::unique_ptr<I2CDevice> dev(new I2CDevice(busId, devAddr, initialState));
286     return dev;
287 }
288 
289 std::unique_ptr<I2CInterface> create(uint8_t busId, uint8_t devAddr,
290                                      I2CInterface::InitialState initialState)
291 {
292     return I2CDevice::create(busId, devAddr, initialState);
293 }
294 
295 } // namespace i2c
296