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