| 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 GPL v2 as described in the | ||
| 10 | * included LICENSE.GPL file. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include "fpp-pch.h" | ||
| 14 | |||
| 15 | #include <fcntl.h> | ||
| 16 | |||
| 17 | #include <filesystem> | ||
| 18 | #include <fstream> | ||
| 19 | #include <mutex> | ||
| 20 | #include <map> | ||
| 21 | #include <set> | ||
| 22 | |||
| 23 | #include "PiFaceUtils.h" | ||
| 24 | #include "PiGPIOUtils.h" | ||
| 25 | #include "Warnings.h" | ||
| 26 | #include "common_mini.h" | ||
| 27 | #include "log.h" | ||
| 28 | |||
| 29 | 341 | static bool isPi5() { | |
| 30 | 126 | static bool pi5 = [] { | |
| 31 |
2/4✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
|
63 | std::string model = GetFileContents("/proc/device-tree/model"); |
| 32 |
5/12✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 62 times.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
|
190 | return startsWith(model, "Raspberry Pi 5") || |
| 33 |
8/16✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 62 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 62 times.
✓ Branch 10 taken 63 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
|
128 | startsWith(model, "Raspberry Pi Compute Module 5"); |
| 34 |
4/8✓ Branch 0 taken 63 times.
✓ Branch 1 taken 278 times.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
404 | }(); |
| 35 | 341 | return pi5; | |
| 36 | } | ||
| 37 | |||
| 38 | // Kernel 6.18 enabled strict pinmux mode for both pinctrl-rp1 and pinctrl-bcm2835 | ||
| 39 | // (raspberrypi/linux "pinctrl: rp1: enable strict pinmux mode", raspberrypi/linux#5870). | ||
| 40 | // A header pin that a peripheral node claims in the device tree -- i2c1 on P1-3/5, spi0 | ||
| 41 | // on P1-19/21/23, uart0 on P1-8/10, i2s on P1-12/35/38/40 -- is now reported to gpiolib | ||
| 42 | // as in use by the kernel, and every gpiod request against it fails with EINVAL. Through | ||
| 43 | // FPP 9.x (kernel 6.12) those drivers were non-strict, so re-muxing the pad with | ||
| 44 | // "pinctrl set" and then requesting it worked; on 6.18 it does not, because "pinctrl set" | ||
| 45 | // writes FUNCSEL behind the pinctrl driver's back and leaves the kernel's claim in place. | ||
| 46 | // | ||
| 47 | // The supported way to get the pin back is to make the owning device release it, which | ||
| 48 | // unbinding its driver does. That is safe when nothing has attached to the bus (the common | ||
| 49 | // case for buttons wired straight to the header on a board with no cape), and decidedly | ||
| 50 | // unsafe when something has: a child driver's remove path can wedge in the kernel and take | ||
| 51 | // the whole bus with it. So we unbind only when the owner has no child driver bound other | ||
| 52 | // than the ones known to detach cleanly, and warn with the offending driver named otherwise. | ||
| 53 | // | ||
| 54 | // On a non-strict kernel the debugfs format has no "device " prefix, so findPinMuxOwner() | ||
| 55 | // finds nothing and none of this engages -- which is exactly right, since a non-strict | ||
| 56 | // controller hands the pin over without help. | ||
| 57 | static constexpr const char* PINMUX_DEBUG_DIR = "/sys/kernel/debug/pinctrl"; | ||
| 58 | |||
| 59 | struct PinMuxOwner { | ||
| 60 | std::string device; // platform device holding the mux claim, e.g. "1f00074000.i2c" | ||
| 61 | std::string function; // mux function it selected, e.g. "i2c1" | ||
| 62 | bool hog = false; | ||
| 63 | }; | ||
| 64 | |||
| 65 | // The pin controllers that drive the 40-pin header. Other pinctrl instances (the bcm2712 | ||
| 66 | // "aon" controller, expanders) enumerate overlapping pin numbers and must not be consulted. | ||
| 67 | 5 | static bool isHeaderPinctrlDir(const std::string& dir) { | |
| 68 |
6/26✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✓ Branch 14 taken 5 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 5 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
|
20 | return endsWith(dir, "-pinctrl-rp1") || endsWith(dir, "-pinctrl-bcm2711") || |
| 69 |
3/16✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 5 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 5 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
|
10 | endsWith(dir, "-pinctrl-bcm2835"); |
| 70 | } | ||
| 71 | |||
| 72 | // debugfs files report a size of 0, so they have to be streamed rather than slurped. | ||
| 73 | 63 | static bool findPinMuxOwner(int gpio, PinMuxOwner& owner) { | |
| 74 |
2/4✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
|
63 | const std::string prefix = "pin " + std::to_string(gpio) + " ("; |
| 75 | 63 | std::error_code ec; | |
| 76 |
2/4✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
|
63 | std::filesystem::directory_iterator dir(PINMUX_DEBUG_DIR, ec); |
| 77 |
2/2✓ Branch 0 taken 58 times.
✓ Branch 1 taken 5 times.
|
63 | if (ec) { |
| 78 | // Without debugfs there is no way to find out which device holds a pin, so a | ||
| 79 | // claimed pin can only be reported as unavailable by the failing request itself. | ||
| 80 | static bool warned = false; | ||
| 81 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 23 times.
|
58 | if (!warned) { |
| 82 | 35 | warned = true; | |
| 83 |
1/2✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
|
35 | LogWarn(VB_GPIO, "%s is not readable; pins claimed by a kernel driver cannot be freed for GPIO use\n", |
| 84 | PINMUX_DEBUG_DIR); | ||
| 85 | } | ||
| 86 | 58 | return false; | |
| 87 | } | ||
| 88 |
1/4✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
|
5 | for (const auto& entry : dir) { |
| 89 |
4/8✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
|
5 | if (!isHeaderPinctrlDir(entry.path().filename().string())) { |
| 90 | ✗ | continue; | |
| 91 | } | ||
| 92 |
3/6✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
|
5 | std::ifstream in(entry.path().string() + "/pinmux-pins"); |
| 93 | 5 | std::string line; | |
| 94 |
3/6✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 64 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 64 times.
✗ Branch 5 not taken.
|
64 | while (std::getline(in, line)) { |
| 95 |
3/4✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 59 times.
✓ Branch 3 taken 5 times.
|
64 | if (!startsWith(line, prefix)) { |
| 96 | 59 | continue; | |
| 97 | } | ||
| 98 | // strict format: "pin 2 (gpio2): device 1f00074000.i2c function i2c1 group gpio2" | ||
| 99 | 5 | size_t dpos = line.find("): device "); | |
| 100 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
|
5 | if (dpos == std::string::npos) { |
| 101 | 5 | return false; // UNCLAIMED, GPIO-owned, or a non-strict kernel | |
| 102 | } | ||
| 103 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | std::string rest = line.substr(dpos + 10); |
| 104 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | owner.device = rest.substr(0, rest.find(' ')); |
| 105 | 3 | owner.hog = rest.find(" (HOG)") != std::string::npos; | |
| 106 | 3 | size_t fpos = rest.find(" function "); | |
| 107 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (fpos != std::string::npos) { |
| 108 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | std::string fn = rest.substr(fpos + 10); |
| 109 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | owner.function = fn.substr(0, fn.find(' ')); |
| 110 | 3 | } | |
| 111 | 3 | return true; | |
| 112 | 3 | } | |
| 113 |
4/8✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
|
20 | } |
| 114 | ✗ | return false; | |
| 115 | 63 | } | |
| 116 | |||
| 117 | // A child that is still driver-bound means the bus is in use; removing the controller then | ||
| 118 | // runs that driver's remove path, which is where an unbind can hang. spidev is the one | ||
| 119 | // child FPP creates itself and is known to detach cleanly. | ||
| 120 | 3 | static bool findBlockingChildDriver(const std::string& devPath, std::string& blocker) { | |
| 121 |
4/8✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
3 | static const std::set<std::string> safeChildDrivers = { "spidev" }; |
| 122 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | const std::string ownDriver = devPath + "/driver"; |
| 123 | 3 | std::error_code ec; | |
| 124 | 3 | auto end = std::filesystem::recursive_directory_iterator(); | |
| 125 | 3 | for (auto it = std::filesystem::recursive_directory_iterator( | |
| 126 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | devPath, std::filesystem::directory_options::skip_permission_denied, ec); |
| 127 |
2/4✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
|
48 | it != end; it.increment(ec)) { |
| 128 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (ec) { |
| 129 | ✗ | break; | |
| 130 | } | ||
| 131 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (it.depth() >= 5) { |
| 132 | ✗ | it.disable_recursion_pending(); | |
| 133 | } | ||
| 134 | 48 | const std::filesystem::path& p = it->path(); | |
| 135 |
12/24✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 45 times.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✓ Branch 10 taken 3 times.
✓ Branch 11 taken 45 times.
✓ Branch 12 taken 48 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 48 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 45 times.
✓ Branch 17 taken 3 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
|
48 | if (p.filename() != "driver" || p.string() == ownDriver) { |
| 136 | 45 | continue; | |
| 137 | } | ||
| 138 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | std::filesystem::path target = std::filesystem::read_symlink(p, ec); |
| 139 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (ec) { |
| 140 | ✗ | ec.clear(); | |
| 141 | ✗ | continue; | |
| 142 | } | ||
| 143 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | std::string driver = target.filename().string(); |
| 144 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | if (!safeChildDrivers.count(driver)) { |
| 145 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | blocker = driver; |
| 146 | 3 | return true; | |
| 147 | } | ||
| 148 |
3/7✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
|
9 | } |
| 149 | ✗ | return false; | |
| 150 | 3 | } | |
| 151 | |||
| 152 | // Hand a pin back from the peripheral that claimed it in the device tree so FPP can | ||
| 153 | // use it itself - as a GPIO, or re-muxed to another function such as DPI (a pixel | ||
| 154 | // string output on the SPI header pins, say, with spi0 still enabled in config.txt). | ||
| 155 | // "use" names what the caller wants the pin for, for the messages. Returns true if | ||
| 156 | // the pin is usable when it returns. | ||
| 157 | 64 | static bool releasePinFromKernelDriver(int gpio, const std::string& pinName, const std::string& use = "GPIO") { | |
| 158 | // configPin() is reachable from the REST API as well as from startup, so the | ||
| 159 | // once-per-pin bookkeeping needs to be serialized. | ||
| 160 | static std::mutex handledLock; | ||
| 161 | // What became of each pin already dealt with this boot: true if it was | ||
| 162 | // freed, false if it couldn't be (and was warned about). A later caller | ||
| 163 | // gets the same answer; this used to say "freed" to every later caller, | ||
| 164 | // even for a pin that is still claimed. | ||
| 165 |
3/4✓ Branch 0 taken 39 times.
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
|
64 | static std::map<int, bool> handled; |
| 166 |
1/2✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
|
64 | std::unique_lock<std::mutex> lock(handledLock); |
| 167 |
1/2✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
|
64 | auto done = handled.find(gpio); |
| 168 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 63 times.
|
64 | if (done != handled.end()) { |
| 169 | 1 | return done->second; | |
| 170 | } | ||
| 171 | |||
| 172 | 63 | PinMuxOwner owner; | |
| 173 |
3/4✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
✓ Branch 3 taken 3 times.
|
63 | if (!findPinMuxOwner(gpio, owner)) { |
| 174 | 60 | return true; // unclaimed, or a kernel that doesn't enforce the claim | |
| 175 | } | ||
| 176 |
4/8✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
|
3 | if (owner.hog || owner.function == "gpio") { |
| 177 | // A hog belongs to the pin controller itself, and a pin muxed as the GPIO | ||
| 178 | // function stays requestable under strict mode via .function_is_gpio. | ||
| 179 | ✗ | return true; | |
| 180 | } | ||
| 181 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | handled[gpio] = false; // until it is shown to be free below |
| 182 | |||
| 183 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | const std::string devPath = "/sys/bus/platform/devices/" + owner.device; |
| 184 | 3 | std::error_code ec; | |
| 185 |
3/6✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | std::filesystem::path driverLink = std::filesystem::read_symlink(devPath + "/driver", ec); |
| 186 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (ec) { |
| 187 | ✗ | std::string w = "Pin " + pinName + " is claimed by " + owner.device + " (" + owner.function + | |
| 188 | ✗ | ") and cannot be used for " + use + "; no driver to unbind"; | |
| 189 | ✗ | WarningHolder::AddWarning(51, w); | |
| 190 | ✗ | LogWarn(VB_GPIO, "%s\n", w.c_str()); | |
| 191 | ✗ | return false; | |
| 192 | ✗ | } | |
| 193 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | std::string driver = driverLink.filename().string(); |
| 194 | |||
| 195 | 3 | std::string blocker; | |
| 196 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | if (findBlockingChildDriver(devPath, blocker)) { |
| 197 |
5/10✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
|
6 | std::string w = "Pin " + pinName + " is claimed by " + owner.function + " (" + owner.device + |
| 198 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
6 | "), which is in use by the " + blocker + |
| 199 |
5/10✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
|
3 | " driver. Disable " + owner.function + " in config.txt to use this pin for " + use + "."; |
| 200 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | WarningHolder::AddWarning(51, w); |
| 201 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | LogWarn(VB_GPIO, "%s\n", w.c_str()); |
| 202 | 3 | return false; | |
| 203 | 3 | } | |
| 204 | |||
| 205 | ✗ | LogInfo(VB_GPIO, "Pin %s is claimed by %s (%s); unbinding %s to free it for %s use\n", | |
| 206 | pinName.c_str(), owner.function.c_str(), owner.device.c_str(), driver.c_str(), use.c_str()); | ||
| 207 | ✗ | std::ofstream unbind("/sys/bus/platform/drivers/" + driver + "/unbind"); | |
| 208 | ✗ | unbind << owner.device; | |
| 209 | ✗ | unbind.close(); | |
| 210 | |||
| 211 | ✗ | PinMuxOwner check; | |
| 212 | ✗ | if (findPinMuxOwner(gpio, check)) { | |
| 213 | ✗ | std::string w = "Pin " + pinName + " is claimed by " + check.function + " (" + check.device + | |
| 214 | ✗ | ") and could not be freed for " + use + " use"; | |
| 215 | ✗ | WarningHolder::AddWarning(51, w); | |
| 216 | ✗ | LogWarn(VB_GPIO, "%s\n", w.c_str()); | |
| 217 | ✗ | return false; | |
| 218 | ✗ | } | |
| 219 | ✗ | handled[gpio] = true; | |
| 220 | ✗ | return true; | |
| 221 | 64 | } | |
| 222 | |||
| 223 | class PiGPIODCapabilities : public GPIODCapabilities { | ||
| 224 | public: | ||
| 225 | 1764 | PiGPIODCapabilities(const std::string& n, uint32_t kg) : | |
| 226 |
3/6✓ Branch 0 taken 1764 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1764 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1764 times.
✗ Branch 5 not taken.
|
8820 | GPIODCapabilities(n, kg) { |
| 227 |
1/2✓ Branch 0 taken 1764 times.
✗ Branch 1 not taken.
|
1764 | gpioIdx = getPinctrlRpiChip(); |
| 228 |
1/2✓ Branch 0 taken 1764 times.
✗ Branch 1 not taken.
|
1764 | gpioName = pinctrlRpiChipName; |
| 229 | 1764 | gpio = kg; | |
| 230 | 1764 | } | |
| 231 | |||
| 232 | 7938 | virtual ~PiGPIODCapabilities() { | |
| 233 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3969 times.
|
7938 | if (dutyFile != nullptr) { |
| 234 | ✗ | fclose(dutyFile); | |
| 235 | } | ||
| 236 | 7938 | } | |
| 237 | |||
| 238 | 1386 | PiGPIODCapabilities& setResetMode(const std::string& mode) { | |
| 239 | 1386 | resetMode = mode; | |
| 240 | 1386 | return *this; | |
| 241 | } | ||
| 242 | 252 | PiGPIODCapabilities& setPwm(int p, int sub) { | |
| 243 | 252 | GPIODCapabilities::setPwm(p, sub); | |
| 244 | 252 | return *this; | |
| 245 | } | ||
| 246 | 126 | PiGPIODCapabilities& setUART(const std::string& u, const std::string& um) { | |
| 247 | 126 | uart = u; | |
| 248 | 126 | uartMode = um; | |
| 249 | 126 | return *this; | |
| 250 | } | ||
| 251 | // The pin is a data line (MISO/MOSI/SCLK) of SPI bus b when muxed to alt. Only | ||
| 252 | // for a bus the device tree enables with no pins claimed (fpp-spi1-nopins), the | ||
| 253 | // same way setUART() pairs with fpp-uart0-nopins. | ||
| 254 | 189 | PiGPIODCapabilities& setSPI(int b, const std::string& alt) { | |
| 255 | 189 | spiBus = b; | |
| 256 | 189 | spiMode = alt; | |
| 257 | 189 | return *this; | |
| 258 | } | ||
| 259 | 33 | virtual void releasePin() const override { | |
| 260 | char buf[256]; | ||
| 261 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 7 times.
|
33 | if (dpiConfigured) { |
| 262 | // A pixel data (or latch) line. Returning it to a plain input leaves | ||
| 263 | // it floating - or on the Pi 5, at "function none" with a weak | ||
| 264 | // pull-down - and the hat's buffer then amplifies whatever noise it | ||
| 265 | // picks up into random colours on the string for the rest of the | ||
| 266 | // shutdown/reboot (issue #2895). Park it as a driven-low output | ||
| 267 | // instead: pinctrl writes the pad registers directly, so this | ||
| 268 | // survives fppd exiting, and low is idle for WS281x. It holds until | ||
| 269 | // the next boot resets the pad; fppd's Init() then muxes the pin back | ||
| 270 | // to DPI only after a blank frame is already in the framebuffer. | ||
| 271 | 26 | snprintf(buf, 256, "/usr/bin/pinctrl set %d op dl", gpio); | |
| 272 | 26 | dpiConfigured = false; | |
| 273 | } else { | ||
| 274 | 7 | snprintf(buf, 256, "/usr/bin/pinctrl set %d %s", gpio, resetMode.c_str()); | |
| 275 | } | ||
| 276 |
1/2✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
|
33 | system(buf); |
| 277 |
1/2✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
|
33 | GPIODCapabilities::releasePin(); |
| 278 | 33 | } | |
| 279 | 64 | virtual int configPin(const std::string& mode = "gpio", | |
| 280 | bool directionOut = true, | ||
| 281 | const std::string& desc = "") const override { | ||
| 282 | // see https://datasheets.raspberrypi.com/rp1/rp1-peripherals.pdf | ||
| 283 | // 1-3: https://elinux.org/RPi_BCM2835_GPIOs | ||
| 284 | // 4/5: https://elinux.org/RPi_BCM2711_GPIOs | ||
| 285 | |||
| 286 | // A data line of a bus the device tree enables with no pins claimed | ||
| 287 | // (fpp-spi1-nopins), so the spi controller never owns it here. Anything | ||
| 288 | // else holding the pin -- i2s on P1-35/38/40 under an audio overlay, say | ||
| 289 | // -- has to let go before a "pinctrl set" to the SPI alt sticks, and a | ||
| 290 | // pin that cannot be freed is refused rather than half-muxed. | ||
| 291 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
|
64 | if (mode == "spi") { |
| 292 | ✗ | if (spiBus == -1) { | |
| 293 | ✗ | LogWarn(VB_GPIO, "Pin %s has no SPI function\n", name.c_str()); | |
| 294 | ✗ | return -1; | |
| 295 | } | ||
| 296 | ✗ | if (!releasePinFromKernelDriver(gpio, name, "SPI")) { | |
| 297 | ✗ | return -1; | |
| 298 | } | ||
| 299 | char buf[256]; | ||
| 300 | ✗ | snprintf(buf, 256, "/usr/bin/pinctrl set %d %s", gpio, spiMode.c_str()); | |
| 301 | ✗ | system(buf); | |
| 302 | ✗ | return 0; | |
| 303 | } | ||
| 304 | |||
| 305 | // Under strict pinmux a pin the device tree handed to a peripheral has to be | ||
| 306 | // reclaimed from that driver before any gpiod request on it can succeed, and | ||
| 307 | // before a "pinctrl set" to another alt function sticks: the pinctrl driver | ||
| 308 | // reasserts its claim, so a string output on a pin spi0 owns silently stayed | ||
| 309 | // SPI. See releasePinFromKernelDriver() above. | ||
| 310 |
15/28✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 64 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 29 times.
✓ Branch 5 taken 35 times.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 26 times.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 2 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 64 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 64 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 64 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
|
192 | if (startsWith(mode, "gpio") || mode == "dpi" || mode == "pwm" || mode == "uart") { |
| 311 |
11/16✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 38 times.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 37 times.
✓ Branch 8 taken 37 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 35 times.
✓ Branch 12 taken 64 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 64 times.
✗ Branch 15 not taken.
|
192 | releasePinFromKernelDriver(gpio, name, mode == "dpi" ? "DPI" : mode == "pwm" ? "PWM" : mode == "uart" ? "UART" : "GPIO"); |
| 312 | } | ||
| 313 | |||
| 314 |
5/6✓ Branch 0 taken 1 times.
✓ Branch 1 taken 63 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 63 times.
|
64 | if (mode == "pwm" && pwm != -1) { |
| 315 | // alt3 is pwm | ||
| 316 | char buf[256]; | ||
| 317 | 1 | snprintf(buf, 256, "/usr/bin/pinctrl set %d a3", gpio); | |
| 318 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | system(buf); |
| 319 | 1 | return 0; | |
| 320 | } | ||
| 321 | |||
| 322 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 37 times.
|
63 | if (mode == "dpi") { |
| 323 | // Pi 5 (RP1): DPI is alt1, Pi 4 and earlier (BCM2711/BCM2835): DPI is alt2 | ||
| 324 |
3/4✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 2 times.
|
26 | const char* alt = isPi5() ? "a1" : "a2"; |
| 325 | char buf[256]; | ||
| 326 | 26 | snprintf(buf, 256, "/usr/bin/pinctrl set %d %s", gpio, alt); | |
| 327 |
1/2✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
|
26 | system(buf); |
| 328 | 26 | dpiConfigured = true; | |
| 329 | 26 | return 0; | |
| 330 | } | ||
| 331 |
6/8✓ Branch 0 taken 2 times.
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 35 times.
|
37 | if (mode == "uart" && !uart.empty() && !uartMode.empty()) { |
| 332 | char buf[256]; | ||
| 333 | 2 | snprintf(buf, 256, "/usr/bin/pinctrl set %d %s", gpio, uartMode.c_str()); | |
| 334 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | system(buf); |
| 335 | 2 | return 0; | |
| 336 | } | ||
| 337 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 31 times.
|
35 | if (mode == "gpio_od") { |
| 338 | // Open-drain (bit-banged I2C): set the pad to GPIO function with the | ||
| 339 | // input buffer + pull-up enabled via pinctrl, then create the | ||
| 340 | // open-drain gpiod request that actually drives/senses the line. | ||
| 341 | char buf[256]; | ||
| 342 | 4 | snprintf(buf, 256, "/usr/bin/pinctrl set %d ip pu", gpio); | |
| 343 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | system(buf); |
| 344 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
12 | return GPIODCapabilities::configPin("gpio_od", false, desc); |
| 345 | } | ||
| 346 | // Pins whose resetMode is an alt-function sentinel ("a0"/"a3" -- the | ||
| 347 | // i2c/uart/spi header pins the device tree hands to a peripheral) come out of | ||
| 348 | // reset muxed away from GPIO, so push the pad back to the gpio function with | ||
| 349 | // pinctrl first. (gpio_pu/gpio_pd are left out: pinctrl's bare direction set | ||
| 350 | // carries no bias, and those pins are fine going straight to the request.) | ||
| 351 | // | ||
| 352 | // This must NOT return early. A gpio* mode only becomes usable once | ||
| 353 | // GPIODCapabilities::configPin() has requested the line: without a live | ||
| 354 | // request setValue()/getValue() are silent no-ops and requestEventFile() has | ||
| 355 | // no lastBias/lastDesc to work from. Returning here instead is a bug that has | ||
| 356 | // now surfaced three times -- P1-35/GPIO19 floating on Pi5 (the "no" sentinel, | ||
| 357 | // #2768), pull-up/down inputs coming up floating on any pin left at the | ||
| 358 | // class-default "a0", and a plain "gpio" OUTPUT on P1-10/GPIO15 (uart0 RX, | ||
| 359 | // which some Pi capes use as a port enable pin) muxing correctly but never | ||
| 360 | // driving, leaving that port permanently dark (#2979). | ||
| 361 |
13/24✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 31 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 19 times.
✓ Branch 9 taken 12 times.
✓ Branch 10 taken 19 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 11 times.
✓ Branch 13 taken 8 times.
✓ Branch 14 taken 31 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 31 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 5 times.
✓ Branch 19 taken 26 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
|
104 | if (startsWith(mode, "gpio") && mode != "gpio_pu" && mode != "gpio_pd" && |
| 362 |
6/8✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 6 times.
|
11 | (resetMode == "a0" || resetMode == "a3")) { |
| 363 | char buf[256]; | ||
| 364 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | snprintf(buf, 256, "/usr/bin/pinctrl set %d %s", gpio, directionOut ? "op" : "ip"); |
| 365 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | system(buf); |
| 366 | } | ||
| 367 | |||
| 368 |
3/6✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 31 times.
|
31 | if (mode == "pwm" || mode == "uart") { |
| 369 | ✗ | return 0; | |
| 370 | } | ||
| 371 | 31 | return GPIODCapabilities::configPin(mode, directionOut, desc); | |
| 372 | } | ||
| 373 | |||
| 374 | 1 | virtual bool supportPWM() const override { return pwm != -1; } | |
| 375 | 1 | virtual bool setupPWM(int maxValue = 25500) const override { | |
| 376 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if (pwm != -1 && dutyFile != nullptr) { |
| 377 | // Already set up. Each call opened another duty_cycle stream and | ||
| 378 | // leaked the last; reopening instead would close a FILE* that | ||
| 379 | // setPWMValue() on an output thread may be writing to. The pin | ||
| 380 | // itself is muxed again, though: an output reload closes the old | ||
| 381 | // output first, and its releasePin() put the pin back to its reset | ||
| 382 | // mode, so the new output's PWM never reached it. A caller that | ||
| 383 | // wants another period (SetupExtGPIO vs the GPIO output) gets it. | ||
| 384 | ✗ | configPin("pwm", true, "PWM"); | |
| 385 | ✗ | if (maxValue != pwmPeriod) { | |
| 386 | char name[128]; | ||
| 387 | // duty_cycle may not exceed period: zero it before a shorter one | ||
| 388 | ✗ | fprintf(dutyFile, "0"); | |
| 389 | ✗ | fflush(dutyFile); | |
| 390 | ✗ | snprintf(name, sizeof(name), "/sys/class/pwm/pwmchip0/pwm%d/period", subPwm); | |
| 391 | ✗ | FILE* f = fopen(name, "w"); | |
| 392 | ✗ | if (f) { | |
| 393 | ✗ | fprintf(f, "%d", maxValue); | |
| 394 | ✗ | fclose(f); | |
| 395 | ✗ | pwmPeriod = maxValue; | |
| 396 | } | ||
| 397 | } | ||
| 398 | ✗ | return true; | |
| 399 | } | ||
| 400 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (pwm != -1) { |
| 401 |
3/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
4 | configPin("pwm", true, "PWM"); |
| 402 | char dir_name[128]; | ||
| 403 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | FILE* dir = fopen("/sys/class/pwm/pwmchip0/export", "w"); |
| 404 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (!dir) { |
| 405 | 1 | return false; | |
| 406 | } | ||
| 407 | ✗ | fprintf(dir, "%d", subPwm); | |
| 408 | ✗ | fclose(dir); | |
| 409 | |||
| 410 | ✗ | snprintf(dir_name, sizeof(dir_name), "/sys/class/pwm/pwmchip0/pwm%d/period", subPwm); | |
| 411 | ✗ | dir = fopen(dir_name, "w"); | |
| 412 | ✗ | if (!dir) { | |
| 413 | ✗ | return false; | |
| 414 | } | ||
| 415 | ✗ | fprintf(dir, "%d", maxValue); | |
| 416 | ✗ | fclose(dir); | |
| 417 | ✗ | pwmPeriod = maxValue; | |
| 418 | |||
| 419 | ✗ | snprintf(dir_name, sizeof(dir_name), "/sys/class/pwm/pwmchip0/pwm%d/duty_cycle", subPwm); | |
| 420 | ✗ | dutyFile = fopen(dir_name, "w"); | |
| 421 | ✗ | if (!dutyFile) { | |
| 422 | ✗ | return false; | |
| 423 | } | ||
| 424 | ✗ | fprintf(dutyFile, "0"); | |
| 425 | ✗ | fflush(dutyFile); | |
| 426 | |||
| 427 | ✗ | snprintf(dir_name, sizeof(dir_name), "/sys/class/pwm/pwmchip0/pwm%d/enable", subPwm); | |
| 428 | ✗ | dir = fopen(dir_name, "w"); | |
| 429 | ✗ | if (!dir) { | |
| 430 | ✗ | return false; | |
| 431 | } | ||
| 432 | ✗ | fprintf(dir, "1"); | |
| 433 | ✗ | fclose(dir); | |
| 434 | ✗ | return true; | |
| 435 | } | ||
| 436 | ✗ | return false; | |
| 437 | } | ||
| 438 | 69 | virtual void setPWMValue(int valueNS) const override { | |
| 439 | // dutyFile is only opened by a setupPWM() that succeeded; every caller | ||
| 440 | // (the GPIO output, the ExtGPIO command) reaches here regardless. | ||
| 441 |
2/4✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 69 times.
|
69 | if (pwm != -1 && dutyFile) { |
| 442 | ✗ | fprintf(dutyFile, "%d", valueNS); | |
| 443 | ✗ | fflush(dutyFile); | |
| 444 | } | ||
| 445 | 69 | } | |
| 446 | |||
| 447 | 1764 | static int getPinctrlRpiChip() { | |
| 448 |
2/2✓ Branch 0 taken 63 times.
✓ Branch 1 taken 1701 times.
|
1764 | if (pinctrlRpiChip == -1) { |
| 449 | 63 | pinctrlRpiChip = 0; // Default fallback | |
| 450 | #ifdef IS_GPIOD_CXX_V2 | ||
| 451 | 63 | int has54lineChip = -1; | |
| 452 | 63 | std::string has54lineChipName; | |
| 453 |
5/8✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5694 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5754 times.
✓ Branch 7 taken 3 times.
|
5757 | for (const auto& entry : std::filesystem::directory_iterator("/dev/")) { |
| 454 |
6/10✓ Branch 0 taken 5754 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5754 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5754 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5754 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 75 times.
✓ Branch 9 taken 5679 times.
|
17262 | if (startsWith(entry.path().filename().string(), "gpiochip")) { |
| 455 | try { | ||
| 456 |
3/6✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 75 times.
✗ Branch 5 not taken.
|
75 | auto chip = gpiod::chip(entry.path().string()); |
| 457 |
1/2✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
|
75 | auto info = chip.get_info(); |
| 458 |
14/26✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 60 times.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 15 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 15 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 15 times.
✓ Branch 12 taken 15 times.
✓ Branch 13 taken 60 times.
✓ Branch 14 taken 15 times.
✓ Branch 15 taken 60 times.
✓ Branch 16 taken 75 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 60 times.
✓ Branch 19 taken 15 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
|
75 | if (info.label() == "pinctrl-rp1" || info.label() == "pinctrl-bcm2711" || info.label() == "pinctrl-bcm2835") { |
| 459 |
4/8✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 60 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 60 times.
✗ Branch 7 not taken.
|
60 | pinctrlRpiChip = std::stoi(entry.path().filename().string().substr(8)); |
| 460 | 60 | pinctrlRpiChipName = info.name(); | |
| 461 | 60 | return pinctrlRpiChip; | |
| 462 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | } else if (info.num_lines() == 54) { |
| 463 | ✗ | has54lineChip = std::stoi(entry.path().filename().string().substr(8)); | |
| 464 | ✗ | has54lineChipName = info.name(); | |
| 465 | } | ||
| 466 |
4/4✓ Branch 0 taken 15 times.
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 60 times.
|
135 | } catch (...) { |
| 467 | // Chip doesn't exist, continue | ||
| 468 | ✗ | } | |
| 469 | } | ||
| 470 |
6/6✓ Branch 0 taken 3 times.
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 60 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 60 times.
|
183 | } |
| 471 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (has54lineChip != -1) { |
| 472 | ✗ | pinctrlRpiChip = has54lineChip; | |
| 473 | ✗ | pinctrlRpiChipName = has54lineChipName; | |
| 474 | ✗ | return pinctrlRpiChip; | |
| 475 | } | ||
| 476 | #else | ||
| 477 | for (auto& a : gpiod::make_chip_iter()) { | ||
| 478 | std::string label = a.label(); | ||
| 479 | if (label == "pinctrl-rp1" || label == "pinctrl-bcm2711" || label == "pinctrl-bcm2835") { | ||
| 480 | pinctrlRpiChipName = a.name(); | ||
| 481 | return pinctrlRpiChip; | ||
| 482 | } | ||
| 483 | pinctrlRpiChip++; | ||
| 484 | } | ||
| 485 | // didn't find it by name, try finding the chip with 54 lines | ||
| 486 | pinctrlRpiChip = 0; | ||
| 487 | for (auto& a : gpiod::make_chip_iter()) { | ||
| 488 | if (a.num_lines() == 54) { | ||
| 489 | pinctrlRpiChipName = a.name(); | ||
| 490 | return pinctrlRpiChip; | ||
| 491 | } | ||
| 492 | pinctrlRpiChip++; | ||
| 493 | } | ||
| 494 | #endif | ||
| 495 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 60 times.
|
63 | } |
| 496 | 1704 | return pinctrlRpiChip; | |
| 497 | } | ||
| 498 | mutable FILE* dutyFile = nullptr; | ||
| 499 | mutable int pwmPeriod = 0; // ns, as last written to the period file | ||
| 500 | static int pinctrlRpiChip; | ||
| 501 | static std::string pinctrlRpiChipName; | ||
| 502 | std::string resetMode = "a0"; | ||
| 503 | mutable bool dpiConfigured = false; // muxed for DPI; released as output-low, see releasePin() | ||
| 504 | std::string uartMode; | ||
| 505 | int spiBus = -1; // logical SPI bus this pin is a data line of, -1 if none | ||
| 506 | std::string spiMode; | ||
| 507 | }; | ||
| 508 | int PiGPIODCapabilities::pinctrlRpiChip = -1; | ||
| 509 | std::string PiGPIODCapabilities::pinctrlRpiChipName; | ||
| 510 | static std::vector<PiGPIODCapabilities> PI_PINS; | ||
| 511 | |||
| 512 | 63 | void PiGPIOPinProvider::Init() { | |
| 513 |
4/6✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
|
126 | std::string ipOrNo = isPi5() ? "no" : "ip"; |
| 514 |
4/6✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
|
126 | std::string i2c = isPi5() ? "a3" : "a0"; |
| 515 | // SPI1 sits on the I2S pins; RP1 puts it on alt0 where the older SoCs use alt4 | ||
| 516 |
4/6✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
|
189 | std::string spi1 = isPi5() ? "a0" : "a4"; |
| 517 |
4/8✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-3", 2).setResetMode(i2c)); |
| 518 |
4/8✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-5", 3).setResetMode(i2c)); |
| 519 |
3/6✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-7", 4)); |
| 520 |
9/16✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 62 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 63 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 63 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 63 times.
✗ Branch 15 not taken.
|
378 | PI_PINS.push_back(PiGPIODCapabilities("P1-8", 14).setUART("ttyAMA0-tx", isPi5() ? "a4" : "a0")); |
| 521 |
9/16✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 62 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 63 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 63 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 63 times.
✗ Branch 15 not taken.
|
378 | PI_PINS.push_back(PiGPIODCapabilities("P1-10", 15).setUART("ttyAMA0-rx", isPi5() ? "a4" : "a0")); |
| 522 |
4/8✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-11", 17).setResetMode(ipOrNo)); |
| 523 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-12", 18).setPwm(0, 0).setResetMode(ipOrNo)); |
| 524 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-13", 27).setPwm(0, 1).setResetMode(ipOrNo)); |
| 525 |
4/8✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-15", 22).setResetMode(ipOrNo)); |
| 526 |
4/8✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-16", 23).setResetMode(ipOrNo)); |
| 527 |
4/8✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-18", 24).setResetMode(ipOrNo)); |
| 528 |
3/6✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-19", 10)); |
| 529 |
3/6✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-21", 9)); |
| 530 |
4/8✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-22", 25).setResetMode(ipOrNo)); |
| 531 |
3/6✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-23", 11)); |
| 532 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
252 | PI_PINS.push_back(PiGPIODCapabilities("P1-24", 8).setResetMode("op")); |
| 533 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
252 | PI_PINS.push_back(PiGPIODCapabilities("P1-26", 7).setResetMode("op")); |
| 534 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
252 | PI_PINS.push_back(PiGPIODCapabilities("P1-27", 0).setResetMode("ip")); |
| 535 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
252 | PI_PINS.push_back(PiGPIODCapabilities("P1-28", 1).setResetMode("ip")); |
| 536 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
252 | PI_PINS.push_back(PiGPIODCapabilities("P1-29", 5).setResetMode("ip")); |
| 537 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
252 | PI_PINS.push_back(PiGPIODCapabilities("P1-31", 6).setResetMode("ip")); |
| 538 |
6/12✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 63 times.
✗ Branch 11 not taken.
|
252 | PI_PINS.push_back(PiGPIODCapabilities("P1-32", 12).setResetMode("ip").setPwm(0, 0)); |
| 539 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
252 | PI_PINS.push_back(PiGPIODCapabilities("P1-33", 13).setResetMode("ip")); |
| 540 |
6/12✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 63 times.
✗ Branch 11 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-35", 19).setResetMode(ipOrNo).setPwm(0, 1).setSPI(1, spi1)); |
| 541 |
4/8✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-36", 16).setResetMode(ipOrNo)); |
| 542 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
252 | PI_PINS.push_back(PiGPIODCapabilities("P1-37", 26).setResetMode("ip")); |
| 543 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
126 | PI_PINS.push_back(PiGPIODCapabilities("P1-38", 20).setResetMode(ipOrNo).setSPI(1, spi1)); |
| 544 |
5/10✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 63 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 63 times.
✗ Branch 9 not taken.
|
63 | PI_PINS.push_back(PiGPIODCapabilities("P1-40", 21).setResetMode(ipOrNo).setSPI(1, spi1)); |
| 545 | |||
| 546 |
1/2✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
|
63 | PiFacePinCapabilities::Init(); |
| 547 | 63 | } | |
| 548 | 183 | const PinCapabilities& PiGPIOPinProvider::getPinByName(const std::string& name) { | |
| 549 |
2/2✓ Branch 0 taken 2873 times.
✓ Branch 1 taken 12 times.
|
2885 | for (auto& a : PI_PINS) { |
| 550 |
2/2✓ Branch 0 taken 171 times.
✓ Branch 1 taken 2702 times.
|
2873 | if (a.name == name) { |
| 551 | 171 | return a; | |
| 552 | } | ||
| 553 | } | ||
| 554 | 12 | return PiFacePinCapabilities::getPinByName(name); | |
| 555 | } | ||
| 556 | 3 | const PinCapabilities& PiGPIOPinProvider::getPinByGPIO(int chip, int gpio) { | |
| 557 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (chip == 0) { |
| 558 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
48 | for (auto& a : PI_PINS) { |
| 559 |
3/4✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 45 times.
|
48 | if (a.gpioIdx == chip && a.gpio == gpio) { |
| 560 | 3 | return a; | |
| 561 | } | ||
| 562 | } | ||
| 563 | } | ||
| 564 | ✗ | return PiFacePinCapabilities::getPinByGPIO(chip, gpio); | |
| 565 | } | ||
| 566 | |||
| 567 | 56 | std::vector<std::string> PiGPIOPinProvider::getPinNames() { | |
| 568 | 56 | std::vector<std::string> ret; | |
| 569 |
2/2✓ Branch 0 taken 1568 times.
✓ Branch 1 taken 56 times.
|
1624 | for (auto& a : PI_PINS) { |
| 570 |
1/2✓ Branch 0 taken 1568 times.
✗ Branch 1 not taken.
|
1568 | ret.push_back(a.name); |
| 571 | } | ||
| 572 |
1/2✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
|
56 | PiFacePinCapabilities::getPinNames(ret); |
| 573 | 56 | return ret; | |
| 574 | ✗ | } | |
| 575 | 2 | const PinCapabilities& PiGPIOPinProvider::getPinByUART(const std::string& n) { | |
| 576 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | for (auto& a : PI_PINS) { |
| 577 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
|
8 | if (a.uart == n) { |
| 578 | 2 | return a; | |
| 579 | } | ||
| 580 | } | ||
| 581 | ✗ | return PiFacePinCapabilities::getPinByUART(n); | |
| 582 | } | ||
| 583 |