| 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 |