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