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