GCC Code Coverage Report


Directory: ./
File: non-gpl/FalconV5Support/FalconV5Support.h
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 6 6 100.0%
Functions: 6 6 100.0%
Branches: 0 0 -%

Line Branch Exec Source
1 #pragma once
2 /*
3 * This file is part of the Falcon Player (FPP) and is Copyright (C)
4 * 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 CC-BY-ND as described in the
11 * included LICENSE.CC-BY-ND file. This file may be modified for
12 * personal use, but modified copies MAY NOT be redistributed in any form.
13 */
14
15 #include <atomic>
16 #include <cstdint>
17 #include <functional>
18 #include "fpp-json-fwd.h"
19 #include <list>
20 #include <map>
21 #include <mutex>
22 #include <vector>
23
24 class PixelString;
25 class FalconV5Listener;
26 class PinCapabilities;
27 class PRUControl;
28
29 class FalconV5Support {
30 public:
31 FalconV5Support();
32 ~FalconV5Support();
33
34 class ReceiverChain {
35 public:
36 ReceiverChain(PixelString* p1, PixelString* p2, PixelString* p3, PixelString* p4, int grp = 0, int m = 0, bool sendOnly = false);
37 bool generateConfigPacket(uint8_t* packet) const;
38 bool generateNumberPackets(uint8_t* packet, uint8_t* packet2) const;
39 bool generateQueryPacket(uint8_t* packet, int receiver) const;
40 bool generateResetFusesPacket(uint8_t* packet) const;
41 bool generateToggleEFusePacket(uint8_t* packet, int receiver, int port) const;
42 bool generateResetEFusePacket(uint8_t* packet, int receiver, int port) const;
43 bool generateSetFusesPacket(uint8_t* packet, bool on) const;
44
45 296 const std::array<const PixelString*, 4>& getPixelStrings() const { return strings; };
46 18 uint32_t getReceiverCount() const { return numReceivers; }
47
48 // Falcon V4 handling: only the config packet is sent, no queries
49 // are scheduled and no responses are expected
50 54 bool isSendOnly() const { return sendOnly; }
51
52 240 bool hasMoreQueries() const { return curReceiverQuery < numReceivers; }
53 239 void resetQueryCount() { curReceiverQuery = 0; }
54 bool generateQueryPacket(uint8_t* packet);
55 void handleQueryResponse(Json::Value& json);
56 bool generatePixelCountPacket(uint8_t* packet) const;
57
58 private:
59 std::array<const PixelString*, 4> strings;
60 uint32_t group;
61 uint32_t mux;
62 uint32_t numReceivers;
63 bool sendOnly;
64
65 uint32_t curReceiverQuery = 0;
66 };
67
68 ReceiverChain* addReceiverChain(PixelString* p1, PixelString* p2, PixelString* p3, PixelString* p4, int group, int mux, bool sendOnly = false);
69 72 const std::list<ReceiverChain*>& getReceiverChains() const { return receiverChains; };
70
71 void addListeners(const Json::Value& config);
72 void setCurrentMux(int i);
73
74 // Copies a newly published listener capture out of the PRU shared RAM
75 // into a staging buffer. Safe to call from any thread, meant to be
76 // called often (the string driver's pump thread polls it every few
77 // hundred microseconds) so the capture is taken well before the next
78 // listen window can overwrite it; processListenerData() also calls it
79 // for platforms without a pump thread.
80 void stageListenerData();
81 // Decodes the staged capture (if any) and delivers the receiver status
82 // to the OutputMonitor. Output thread only.
83 void processListenerData();
84
85 bool generateDynamicPacket(std::vector<std::array<uint8_t, 64>>& packets, bool& listen);
86
87 // fraction of frames that carry a query packet and open a listen window
88 // once the receivers are being polled: each mux cycles through its
89 // receivers one frame at a time and then idles one frame; 0 without
90 // listeners or queryable chains
91 float queryFrameShare() const;
92
93 void sendCountPixelPackets();
94
95 private:
96 std::list<ReceiverChain*> receiverChains;
97 std::list<FalconV5Listener*> listeners;
98 std::list<const PinCapabilities*> muxPins;
99 PRUControl* pru = nullptr;
100 uint32_t lastCaptures = 0;
101 uint32_t tornCaptures = 0;
102 // staged capture: stageListenerData() fills stageBuf under stageLock,
103 // processListenerData() swaps it with workBuf under the lock and decodes
104 // workBuf outside it
105 std::mutex stageLock;
106 std::vector<uint8_t> stageBuf;
107 std::vector<uint8_t> workBuf;
108 uint32_t stageLen = 0;
109 bool stageValid = false;
110
111 std::vector<std::map<int, std::list<ReceiverChain*>>> queryData;
112 std::vector<int> maxCount;
113 int curMux = 0;
114 int curCount = 0;
115 bool triggerPixelCount = false;
116
117 // A pending smart-receiver eFuse action. Published by the OutputMonitor
118 // callback (API/MQTT/GPIO/scheduler threads) and consumed by
119 // generateDynamicPacket() on the output thread, so it has to be a single
120 // atomic word: as three separate fields the consumer could pair a new
121 // port with the previous command, and the std::string it replaces was
122 // being assigned and compared concurrently, which is a torn read of a
123 // heap pointer. Kept to 4 bytes so the atomic is lock-free on 32-bit ARM
124 // as well.
125 enum class ToggleCommand : uint8_t {
126 None = 0,
127 Toggle,
128 Reset
129 };
130 struct PendingToggle {
131 int16_t port = -1;
132 uint8_t index = 0;
133 ToggleCommand cmd = ToggleCommand::None;
134 };
135 // compare_exchange compares the object representation, so padding here
136 // would make the exchange in generateDynamicPacket() fail at random.
137 static_assert(sizeof(PendingToggle) == 4, "PendingToggle must be a single unpadded word");
138 static_assert(std::atomic<PendingToggle>::is_always_lock_free, "PendingToggle atomic must be lock-free");
139 std::atomic<PendingToggle> pendingToggle{ PendingToggle() };
140 };
141