xref: /openbmc/phosphor-power/tools/i2c/i2c.cpp (revision d45a9a6d)
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 void I2CDevice::checkReadFuncs(int type)
21 {
22     unsigned long funcs;
23 
24     /* Check adapter functionality */
25     if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
26     {
27         throw I2CException("Failed to get funcs", busStr, devAddr, errno);
28     }
29 
30     switch (type)
31     {
32         case I2C_SMBUS_BYTE:
33             if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
34             {
35                 throw I2CException("Missing SMBUS_READ_BYTE", busStr, devAddr);
36             }
37             break;
38         case I2C_SMBUS_BYTE_DATA:
39             if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
40             {
41                 throw I2CException("Missing SMBUS_READ_BYTE_DATA", busStr,
42                                    devAddr);
43             }
44             break;
45 
46         case I2C_SMBUS_WORD_DATA:
47             if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
48             {
49                 throw I2CException("Missing SMBUS_READ_WORD_DATA", busStr,
50                                    devAddr);
51             }
52             break;
53         case I2C_SMBUS_BLOCK_DATA:
54             if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
55             {
56                 throw I2CException("Missing SMBUS_READ_BLOCK_DATA", busStr,
57                                    devAddr);
58             }
59             break;
60         case I2C_SMBUS_I2C_BLOCK_DATA:
61             if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
62             {
63                 throw I2CException("Missing I2C_FUNC_SMBUS_READ_I2C_BLOCK",
64                                    busStr, devAddr);
65             }
66             break;
67         default:
68             fprintf(stderr, "Unexpected read size type: %d\n", type);
69             assert(false);
70             break;
71     }
72 }
73 
74 void I2CDevice::checkWriteFuncs(int type)
75 {
76     unsigned long funcs;
77 
78     /* Check adapter functionality */
79     if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
80     {
81         throw I2CException("Failed to get funcs", busStr, devAddr, errno);
82     }
83 
84     switch (type)
85     {
86         case I2C_SMBUS_BYTE:
87             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
88             {
89                 throw I2CException("Missing SMBUS_WRITE_BYTE", busStr, devAddr);
90             }
91             break;
92         case I2C_SMBUS_BYTE_DATA:
93             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
94             {
95                 throw I2CException("Missing SMBUS_WRITE_BYTE_DATA", busStr,
96                                    devAddr);
97             }
98             break;
99 
100         case I2C_SMBUS_WORD_DATA:
101             if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
102             {
103                 throw I2CException("Missing SMBUS_WRITE_WORD_DATA", busStr,
104                                    devAddr);
105             }
106             break;
107         case I2C_SMBUS_BLOCK_DATA:
108             if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
109             {
110                 throw I2CException("Missing SMBUS_WRITE_BLOCK_DATA", busStr,
111                                    devAddr);
112             }
113             break;
114         case I2C_SMBUS_I2C_BLOCK_DATA:
115             if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
116             {
117                 throw I2CException("Missing I2C_FUNC_SMBUS_WRITE_I2C_BLOCK",
118                                    busStr, devAddr);
119             }
120             break;
121         default:
122             fprintf(stderr, "Unexpected read size type: %d\n", type);
123             assert(false);
124     }
125 }
126 
127 void I2CDevice::open()
128 {
129     if (isOpen())
130     {
131         throw I2CException("Device already open", busStr, devAddr);
132     }
133 
134     fd = ::open(busStr.c_str(), O_RDWR);
135     if (fd == -1)
136     {
137         throw I2CException("Failed to open", busStr, devAddr, errno);
138     }
139 
140     if (ioctl(fd, I2C_SLAVE, devAddr) < 0)
141     {
142         // Close device since setting slave address failed
143         closeWithoutException();
144 
145         throw I2CException("Failed to set I2C_SLAVE", busStr, devAddr, errno);
146     }
147 }
148 
149 void I2CDevice::close()
150 {
151     checkIsOpen();
152     if (::close(fd) == -1)
153     {
154         throw I2CException("Failed to close", busStr, devAddr, errno);
155     }
156     fd = INVALID_FD;
157 }
158 
159 void I2CDevice::read(uint8_t& data)
160 {
161     checkIsOpen();
162     checkReadFuncs(I2C_SMBUS_BYTE);
163 
164     int ret = i2c_smbus_read_byte(fd);
165     if (ret < 0)
166     {
167         throw I2CException("Failed to read byte", busStr, devAddr, errno);
168     }
169     data = static_cast<uint8_t>(ret);
170 }
171 
172 void I2CDevice::read(uint8_t addr, uint8_t& data)
173 {
174     checkIsOpen();
175     checkReadFuncs(I2C_SMBUS_BYTE_DATA);
176 
177     int ret = i2c_smbus_read_byte_data(fd, addr);
178     if (ret < 0)
179     {
180         throw I2CException("Failed to read byte data", busStr, devAddr, errno);
181     }
182     data = static_cast<uint8_t>(ret);
183 }
184 
185 void I2CDevice::read(uint8_t addr, uint16_t& data)
186 {
187     checkIsOpen();
188     checkReadFuncs(I2C_SMBUS_WORD_DATA);
189     int ret = i2c_smbus_read_word_data(fd, addr);
190     if (ret < 0)
191     {
192         throw I2CException("Failed to read word data", busStr, devAddr, errno);
193     }
194     data = static_cast<uint16_t>(ret);
195 }
196 
197 void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data, Mode mode)
198 {
199     checkIsOpen();
200     int ret;
201     switch (mode)
202     {
203         case Mode::SMBUS:
204             checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
205             ret = i2c_smbus_read_block_data(fd, addr, data);
206             break;
207         case Mode::I2C:
208             checkReadFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
209             ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
210             if (ret != size)
211             {
212                 throw I2CException("Failed to read i2c block data", busStr,
213                                    devAddr, errno);
214             }
215             break;
216     }
217     if (ret < 0)
218     {
219         throw I2CException("Failed to read block data", busStr, devAddr, errno);
220     }
221     size = static_cast<uint8_t>(ret);
222 }
223 
224 void I2CDevice::write(uint8_t data)
225 {
226     checkIsOpen();
227     checkWriteFuncs(I2C_SMBUS_BYTE);
228 
229     if (i2c_smbus_write_byte(fd, data) < 0)
230     {
231         throw I2CException("Failed to write byte", busStr, devAddr, errno);
232     }
233 }
234 
235 void I2CDevice::write(uint8_t addr, uint8_t data)
236 {
237     checkIsOpen();
238     checkWriteFuncs(I2C_SMBUS_BYTE_DATA);
239 
240     if (i2c_smbus_write_byte_data(fd, addr, data) < 0)
241     {
242         throw I2CException("Failed to write byte data", busStr, devAddr, errno);
243     }
244 }
245 
246 void I2CDevice::write(uint8_t addr, uint16_t data)
247 {
248     checkIsOpen();
249     checkWriteFuncs(I2C_SMBUS_WORD_DATA);
250 
251     if (i2c_smbus_write_word_data(fd, addr, data) < 0)
252     {
253         throw I2CException("Failed to write word data", busStr, devAddr, errno);
254     }
255 }
256 
257 void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data,
258                       Mode mode)
259 {
260     checkIsOpen();
261     int ret;
262     switch (mode)
263     {
264         case Mode::SMBUS:
265             checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
266             ret = i2c_smbus_write_block_data(fd, addr, size, data);
267             break;
268         case Mode::I2C:
269             checkWriteFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
270             ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
271             break;
272     }
273     if (ret < 0)
274     {
275         throw I2CException("Failed to write block data", busStr, devAddr,
276                            errno);
277     }
278 }
279 
280 std::unique_ptr<I2CInterface> I2CDevice::create(uint8_t busId, uint8_t devAddr,
281                                                 InitialState initialState)
282 {
283     std::unique_ptr<I2CDevice> dev(new I2CDevice(busId, devAddr, initialState));
284     return dev;
285 }
286 
287 std::unique_ptr<I2CInterface> create(uint8_t busId, uint8_t devAddr,
288                                      I2CInterface::InitialState initialState)
289 {
290     return I2CDevice::create(busId, devAddr, initialState);
291 }
292 
293 } // namespace i2c
294