GCC Code Coverage Report


Directory: ./
File: util/I2CUtils.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 79 148 53.4%
Functions: 12 18 66.7%
Branches: 34 86 39.5%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the LGPL v2.1 as described in the
10 * included LICENSE.LGPL file.
11 */
12
13 #include "fpp-pch.h"
14 #include <cstring>
15
16 #include <fcntl.h>
17 #include <thread>
18 #include <unistd.h>
19
20 #include "../config.h"
21
22 #ifdef HAS_I2C
23 #include <linux/i2c-dev.h>
24 #include <linux/i2c.h>
25 #else
26
27 #endif
28 #include <sys/ioctl.h>
29
30 #include "I2CUtils.h"
31
32 // some of this is based off the pigpio.c file which is public domain
33
34 3 I2CUtils::I2CUtils(int bus, int address) {
35 3 file = -1;
36 char dev[64];
37 3 snprintf(dev, sizeof(dev), "/dev/i2c-%d", bus);
38
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 Init(dev, address, false);
39 3 }
40 3 I2CUtils::I2CUtils(int bus, int address, bool skipDetect) {
41 3 file = -1;
42 char dev[64];
43 3 snprintf(dev, sizeof(dev), "/dev/i2c-%d", bus);
44
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 Init(dev, address, skipDetect);
45 3 }
46 6 I2CUtils::I2CUtils(const char* bus, int address) {
47 6 file = -1;
48 char dev[64];
49 6 snprintf(dev, sizeof(dev), "/dev/%s", bus);
50
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 Init(dev, address, false);
51 6 }
52 12 void I2CUtils::Init(const char* dev, int addr, bool skipDetect) {
53 12 address = addr;
54 12 file = -1;
55 12 funcs = -1;
56 #ifdef HAS_I2C
57
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if ((file = open(dev, O_RDWR)) < 0) {
58 ✗ if (system("/sbin/modprobe i2c_dev") == -1) { /* ignore errors */
59 }
60
61 ✗ std::this_thread::sleep_for(std::chrono::milliseconds(50));
62 ✗ if ((file = open(dev, O_RDWR)) < 0) {
63 ✗ file = -1;
64 ✗ return;
65 }
66 }
67
68
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (ioctl(file, I2C_SLAVE, address) < 0) {
69 ✗ close(file);
70 ✗ file = -1;
71 ✗ return;
72 }
73
74
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (ioctl(file, I2C_FUNCS, &funcs) < 0) {
75 ✗ funcs = -1;
76 }
77
78 // read a byte to make sure the device actually exists on the i2c bus
79
4/6
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
12 if (!skipDetect && readByte() == -1) {
80 ✗ close(file);
81 ✗ file = -1;
82 }
83 #endif
84 }
85 11 I2CUtils::~I2CUtils() {
86
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (file != -1) {
87 11 close(file);
88 11 file = -1;
89 }
90 11 }
91
92 ✗ int I2CUtils::readDevice(uint8_t* buf, int count) {
93 ✗ if (file != -1) {
94 ✗ return read(file, buf, count);
95 }
96 ✗ return -1;
97 }
98 ✗ int I2CUtils::writeDevice(uint8_t* buf, int count) {
99 ✗ if (file != -1) {
100 ✗ return write(file, buf, count);
101 }
102 ✗ return -1;
103 }
104
105 #ifdef HAS_I2C
106 143 inline int fpp_smbus_access(int file, char rw, uint8_t cmd, int size, union i2c_smbus_data* data) {
107 struct i2c_smbus_ioctl_data args;
108 143 args.read_write = rw;
109 143 args.command = cmd;
110 143 args.size = size;
111 143 args.data = data;
112
113 143 return ioctl(file, I2C_SMBUS, &args);
114 }
115 9 int I2CUtils::readByte() {
116
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 if (file != -1) {
117 union i2c_smbus_data data;
118 9 memset(&data, 0, sizeof(data));
119 9 int status = fpp_smbus_access(file, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (status < 0) {
121 ✗ return -1;
122 }
123 9 return 0xFF & data.byte;
124 }
125 ✗ return -1;
126 }
127 3 int I2CUtils::writeByte(unsigned val) {
128
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (file != -1) {
129 3 val = val & 0xFF;
130 3 int status = fpp_smbus_access(file, I2C_SMBUS_WRITE, val, I2C_SMBUS_BYTE, NULL);
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (status < 0) {
132 ✗ return -1;
133 }
134 3 return status;
135 }
136 ✗ return -1;
137 }
138
139 18 int I2CUtils::readByteData(int reg) {
140
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (file != -1) {
141 union i2c_smbus_data data;
142 18 memset(&data, 0, sizeof(data));
143 18 int status = fpp_smbus_access(file, I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, &data);
144
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 15 times.
18 if (status < 0) {
145 3 return -1;
146 }
147 15 return 0xFF & data.byte;
148 }
149 ✗ return -1;
150 }
151 105 int I2CUtils::writeByteData(int reg, unsigned val) {
152
1/2
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
105 if (file != -1) {
153 union i2c_smbus_data data;
154 105 data.byte = val & 0xFF;
155 105 int status = fpp_smbus_access(file, I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, &data);
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
105 if (status < 0) {
157 ✗ return -1;
158 }
159 105 return status;
160 }
161 ✗ return -1;
162 }
163 ✗ int I2CUtils::readWordData(int reg) {
164 ✗ if (file != -1) {
165 union i2c_smbus_data data;
166 ✗ memset(&data, 0, sizeof(data));
167 ✗ int status = fpp_smbus_access(file, I2C_SMBUS_READ, reg, I2C_SMBUS_WORD_DATA, &data);
168 ✗ if (status < 0) {
169 ✗ return -1;
170 }
171 ✗ return 0xFFFF & data.word;
172 }
173 ✗ return -1;
174 }
175 ✗ int I2CUtils::writeWordData(int reg, unsigned val) {
176 ✗ if (file != -1) {
177 union i2c_smbus_data data;
178 ✗ data.word = val & 0xFFFF;
179 ✗ int status = fpp_smbus_access(file, I2C_SMBUS_WRITE, reg, I2C_SMBUS_WORD_DATA, &data);
180 ✗ if (status < 0) {
181 ✗ return -1;
182 }
183 ✗ return status;
184 }
185 ✗ return -1;
186 }
187
188 ✗ int I2CUtils::writeBlockData(int reg, const uint8_t* buf, int count) {
189 ✗ if (file != -1) {
190 union i2c_smbus_data data;
191 ✗ if (count > 32)
192 ✗ count = 32; // max for SMBus
193 ✗ data.block[0] = count;
194 ✗ for (int x = 0; x < count; x++) {
195 ✗ data.block[x + 1] = buf[x];
196 }
197 ✗ int status = fpp_smbus_access(file, I2C_SMBUS_WRITE, reg, I2C_SMBUS_I2C_BLOCK_DATA, &data);
198 ✗ if (status < 0) {
199 ✗ return -1;
200 }
201 ✗ return status;
202 }
203 ✗ return -1;
204 }
205 ✗ int I2CUtils::writeI2CBlockData(int reg, const uint8_t* buf, int count) {
206 ✗ if (file != -1) {
207 union i2c_smbus_data data;
208 ✗ if (count > 32)
209 ✗ count = 32; // max
210 ✗ data.block[0] = count;
211 ✗ for (int x = 0; x < count; x++) {
212 ✗ data.block[x + 1] = buf[x];
213 }
214 ✗ int status = fpp_smbus_access(file, I2C_SMBUS_WRITE, reg, I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
215 ✗ if (status < 0) {
216 ✗ return -1;
217 }
218 ✗ return status;
219 }
220 ✗ return -1;
221 }
222
223 8 int I2CUtils::readI2CBlockData(int reg, uint8_t* buf, int count) {
224
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (file != -1) {
225 union i2c_smbus_data data;
226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (count > 32)
227 ✗ count = 32; // max
228 8 data.block[0] = count;
229
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 8 times.
52 for (int x = 0; x < count; x++) {
230 44 data.block[x + 1] = buf[x];
231 }
232 8 int status = fpp_smbus_access(file, I2C_SMBUS_READ, reg, I2C_SMBUS_I2C_BLOCK_DATA, &data);
233
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if (status < 0) {
234 6 return -1;
235 }
236
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
10 for (int i = 0; i < count; i++) {
237 // Skip the first byte, which is the length of the rest of the block.
238 8 buf[i] = data.block[i + 1];
239 }
240 2 return count;
241 }
242 ✗ return -1;
243 }
244
245 53 int I2CUtils::writeRawI2CBlockData(int reg, const uint8_t* buf, int count) {
246
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 if (file != -1) {
247 uint8_t bytes[257];
248 // A raw write goes out as one message, so a count that doesn't fit is
249 // refused rather than cut short like the SMBus calls above - half a
250 // register block is worse than none. It used to overrun the buffer.
251 // No log: output plugins call this every frame.
252
4/4
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 51 times.
53 if (count < 0 || count > (int)sizeof(bytes) - 1) {
253 2 return -1;
254 }
255 51 bytes[0] = reg;
256 51 memcpy(bytes + 1, buf, count);
257 51 ioctl(file, I2C_SLAVE, address);
258
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 return write(file, bytes, count + 1);
259 }
260 ✗ return -1;
261 }
262
263 #else
264 int I2CUtils::readByte() { return -1; }
265 int I2CUtils::writeByte(unsigned val) { return -1; }
266 int I2CUtils::readByteData(int reg) { return -1; }
267 int I2CUtils::writeByteData(int reg, unsigned val) { return -1; }
268 int I2CUtils::readWordData(int reg) { return -1; }
269 int I2CUtils::writeWordData(int reg, unsigned val) { return -1; }
270 int I2CUtils::writeBlockData(int reg, const uint8_t* buf, int count) { return -1; }
271 int I2CUtils::writeI2CBlockData(int reg, const uint8_t* buf, int count) { return -1; }
272 int I2CUtils::readI2CBlockData(int reg, uint8_t* buf, int count) { return -1; }
273 int I2CUtils::writeRawI2CBlockData(int reg, const uint8_t* buf, int count) { return -1; }
274
275 #endif
276