GCC Code Coverage Report


Directory: ./
File: util/GPIOUtils.h
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 28 59 47.5%
Functions: 19 48 39.6%
Branches: 1 2 50.0%

Line Branch Exec Source
1 #pragma once
2 /*
3 * This file is part of the Falcon Player (FPP) and is Copyright (C)
4 * 2013-2022 by the Falcon Player Developers.
5 *
6 * The Falcon Player (FPP) is free software, and is covered under
7 * multiple Open Source licenses. Please see the included 'LICENSES'
8 * file for descriptions of what files are covered by each license.
9 *
10 * This source file is covered under the LGPL v2.1 as described in the
11 * included LICENSE.LGPL file.
12 */
13
14 // PLUGIN ABI: native plugins compile against this header and are loaded with
15 // dlopen(), so parts of it are baked into binaries FPP did not build.
16 // - PinCapabilities' vtable: plugins call ptr(), configPin(), getValue()
17 // and setValue() virtually. Inserting a virtual anywhere but after the
18 // last one shifts every later slot (10.2's isAcquired() turned an old
19 // plugin's ptr() into getPWMRegisterAddress()). Plugins do not subclass
20 // it, so appending after the last virtual is safe.
21 // - PinCapabilities' data members (plugins read name).
22 // Any such change must bump FPP_PLUGIN_API_VERSION in Plugin.h. Without the
23 // bump, a plugin built against the previous header still loads and silently
24 // uses the old layout - FPP 10.2 shipped exactly that and crash-looped fppd.
25 // See .claude/PLUGIN-ABI.md for which plugins use what.
26
27 #include <list>
28 #include "fpp-json-fwd.h"
29 #include <string>
30 #include <vector>
31
32 #if __has_include(<gpiod.hpp>)
33 #include <gpiod.hpp>
34 #define HASGPIOD
35 #if __has_include(<gpiodcxx/chip.hpp>)
36 #define IS_GPIOD_CXX_V2
37 #endif
38 #endif
39
40 class PinCapabilitiesProvider;
41
42 class PinCapabilities {
43 public:
44 static void InitGPIO(const std::string& processName, PinCapabilitiesProvider* provider);
45 // Re-scan the kernel gpiochips and register lines for any chip that has
46 // appeared since InitGPIO() ran. I2C GPIO expanders instantiated by a cape
47 // device-tree overlay can probe a bit after boot, so a chip (and its named
48 // lines) may not exist yet during the initial scan. Safe to call repeatedly.
49 static void RescanGPIO();
50 static void SetMultiPinValue(const std::list<const PinCapabilities*>& pins, int v);
51
52 50018 PinCapabilities(const std::string& n, uint32_t k) :
53 50018 name(n),
54 50018 pwm(-1),
55 50018 subPwm(-1),
56 50018 i2cBus(-1),
57 50018 gpioIdx(0),
58 50018 gpio(k) {}
59 213548 virtual ~PinCapabilities() {}
60
61 std::string name;
62
63 uint8_t gpioIdx;
64 uint8_t gpio;
65
66 int8_t pwm;
67 int8_t subPwm;
68
69 int8_t i2cBus;
70
71 std::string uart;
72
73 virtual Json::Value toJSON() const;
74
75 virtual void releasePin() const = 0;
76 virtual int configPin(const std::string& mode = "gpio",
77 bool directionOut = true,
78 const std::string& desc = "") const = 0;
79 ✗ virtual bool supportsPullUp() const { return true; }
80 ✗ virtual bool supportsPullDown() const { return true; }
81 11 virtual bool supportsGpiod() const { return true; }
82
83 virtual bool getValue() const = 0;
84 virtual void setValue(bool i) const = 0;
85
86 // Whether configPin() actually got hold of the line. getValue() has to be
87 // callable from paths that are not exception safe, so a pin it could not
88 // acquire reads a flat 0 rather than reporting an error -- and for an
89 // active-low input, 0 is "pressed". A caller that would otherwise sit in a
90 // polling loop on a pin the kernel owns should ask this first and leave the
91 // input alone. Providers that cannot fail to acquire say so by default.
92 485 virtual bool isAcquired() const { return true; }
93
94 virtual bool setupPWM(int maxValueNS = 25500) const = 0;
95 virtual void setPWMValue(int valueNS) const = 0;
96
97 virtual bool supportPWM() const = 0;
98
99 ✗ virtual int getPWMRegisterAddress() const { return 0; }
100
101 862 virtual const PinCapabilities* ptr() const { return this; }
102
103 ✗ virtual bool isGPIOD() const { return false; }
104 ✗ virtual void releaseGPIOD() const {}
105
106 // on some platforms (beagles), the gpiod index may not match how
107 // the gpio's are layed out memory. Allow subclasses to remap
108 // the indexes
109 ✗ virtual int mappedGPIOIdx() const { return gpioIdx; }
110 ✗ virtual int mappedGPIO() const { return gpio; }
111
112 ✗ virtual int requestEventFile(bool risingEdge, bool fallingEdge) const {
113 ✗ return -1;
114 }
115 ✗ virtual int readEventFromFile() const {
116 ✗ return -1;
117 }
118
119 static const PinCapabilities& getPinByName(const std::string& n);
120 static const PinCapabilities& getPinByUART(const std::string& n);
121 static std::vector<std::string> getPinNames();
122
123 // [[deprecated("Use Pin Names instead")]]
124 static const PinCapabilities& getPinByGPIO(int chip, int gpio);
125
126 [[deprecated("Use Pin Names (getPinByName(...)) instead")]]
127 static const PinCapabilities& getPinByGPIO(int gpio) {
128 return getPinByGPIO(0, gpio);
129 }
130
131 protected:
132 static void enableOledScreen(int i2cBus, bool enable);
133 static PinCapabilitiesProvider* pinProvider;
134 };
135
136 template<class T>
137 class PinCapabilitiesFluent : public PinCapabilities {
138 public:
139 61478 PinCapabilitiesFluent(const std::string& n, uint8_t k) :
140 61478 PinCapabilities(n, k) {}
141 213570 virtual ~PinCapabilitiesFluent() {}
142
143 2684 T& setGPIO(int chip, int pin) {
144 2684 gpioIdx = chip;
145 2684 gpio = pin;
146 2684 return *(static_cast<T*>(this));
147 }
148 252 T& setPwm(int p, int sub) {
149 252 pwm = p;
150 252 subPwm = sub;
151 252 return *(static_cast<T*>(this));
152 }
153 T& setI2C(int i2c) {
154 i2cBus = i2c;
155 return *(static_cast<T*>(this));
156 }
157 T& setUART(const std::string& u) {
158 uart = u;
159 return *(static_cast<T*>(this));
160 }
161 };
162
163 class GPIODCapabilities : public PinCapabilitiesFluent<GPIODCapabilities> {
164 public:
165 10665 GPIODCapabilities(const std::string& n, uint32_t kg, const std::string& gn = "") :
166
1/2
✓ Branch 0 taken 10665 times.
✗ Branch 1 not taken.
10665 PinCapabilitiesFluent(n, kg), gpioName(gn) {}
167
168 virtual void releasePin() const override;
169 virtual int configPin(const std::string& mode = "gpio",
170 bool directionOut = true,
171 const std::string& desc = "") const override;
172 virtual bool getValue() const override;
173 virtual void setValue(bool i) const override;
174 virtual bool isAcquired() const override;
175
176 virtual bool supportsPullUp() const override;
177 virtual bool supportsPullDown() const override;
178
179 ✗ virtual bool setupPWM(int maxValueNS = 25500) const override { return false; }
180 ✗ virtual void setPWMValue(int valueNS) const override {}
181
182 ✗ virtual int getPWMRegisterAddress() const override { return 0; };
183 ✗ virtual bool supportPWM() const override { return false; };
184
185 ✗ virtual bool isGPIOD() const override { return true; }
186 virtual void releaseGPIOD() const override;
187
188 virtual int requestEventFile(bool risingEdge, bool fallingEdge) const override;
189 virtual int readEventFromFile() const override;
190
191 std::string gpioName;
192 #ifdef HASGPIOD
193 #ifdef IS_GPIOD_CXX_V2
194 mutable std::shared_ptr<gpiod::chip> chip = nullptr;
195 mutable std::shared_ptr<gpiod::line_request> request = nullptr;
196 mutable gpiod::line::bias lastBias = gpiod::line::bias::UNKNOWN;
197 #else
198 mutable gpiod::chip* chip = nullptr;
199 mutable gpiod::line line;
200 mutable decltype(gpiod::line_request::flags) lastRequestFlags = {};
201 #endif
202 mutable int lastRequestType = 0;
203 mutable bool lastValue = false;
204 mutable bool openDrain = false;
205 mutable std::string lastDesc;
206 #endif
207 };
208
209 class PinCapabilitiesProvider {
210 public:
211 1900 PinCapabilitiesProvider() {}
212 3734 virtual ~PinCapabilitiesProvider() {}
213
214 virtual void Init() = 0;
215
216 virtual const PinCapabilities& getPinByName(const std::string& n) = 0;
217 virtual const PinCapabilities& getPinByGPIO(int chip, int gpio) = 0;
218 virtual const PinCapabilities& getPinByUART(const std::string& n) = 0;
219 virtual std::vector<std::string> getPinNames() = 0;
220 };
221
222 // No platform information on how to control pins
223 class NoPinCapabilities : public PinCapabilitiesFluent<NoPinCapabilities> {
224 public:
225 2362 NoPinCapabilities(const std::string& n, uint32_t kg) :
226 2362 PinCapabilitiesFluent(n, kg) {}
227 ✗ virtual void releasePin() const override {}
228 ✗ virtual int configPin(const std::string& mode = "gpio",
229 bool directionOut = true,
230 ✗ const std::string& desc = "") const override { return 0; }
231
232 ✗ virtual bool getValue() const override { return false; }
233 ✗ virtual void setValue(bool i) const override {}
234
235 ✗ virtual bool setupPWM(int maxValueNS = 25500) const override { return false; }
236 ✗ virtual void setPWMValue(int valueNS) const override {}
237
238 ✗ virtual int getPWMRegisterAddress() const override { return 0; };
239 ✗ virtual bool supportPWM() const override { return false; };
240
241 ✗ virtual const PinCapabilities* ptr() const override { return nullptr; }
242 };
243
244 class NoPinCapabilitiesProvider : public PinCapabilitiesProvider {
245 public:
246 ✗ NoPinCapabilitiesProvider() {}
247 ✗ virtual ~NoPinCapabilitiesProvider() {}
248
249 ✗ void Init() {}
250 const NoPinCapabilities& getPinByName(const std::string& name);
251 const NoPinCapabilities& getPinByGPIO(int chip, int gpio);
252 const NoPinCapabilities& getPinByUART(const std::string& n);
253 ✗ std::vector<std::string> getPinNames() {
254 ✗ return std::vector<std::string>();
255 }
256 };
257