GCC Code Coverage Report


Directory: ./
File: channeloutput/BBBSerial.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 190 223 85.2%
Functions: 11 11 100.0%
Branches: 170 364 46.7%

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 "fpp-json.h"
16
17 #include <sys/wait.h>
18 #include <fstream>
19 #include <thread>
20 #include <unistd.h>
21
22 #include "../Warnings.h"
23 #include "../common.h"
24 #include "../log.h"
25
26 #define BBB_PRU 0
27 // #define USING_PRU_RAM
28
29 // FPP includes
30 #include "BBBSerial.h"
31 #include "ChannelOutputSetup.h"
32
33 #include "../non-gpl/CapeUtils/CapeUtils.h"
34 #include "util/BBBUtils.h"
35
36 // reserve the TOP 84K for DMX/PixelNet data
37 #define DDR_RESERVED 84 * 1024
38
39 static const std::string PRU_STALLED_WARNING = "BBBSerial: PRU is not consuming frames - serial output is stalled";
40
41 #include "Plugin.h"
42 class BBBSerialPlugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin {
43 public:
44 4 BBBSerialPlugin() :
45
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
12 FPPPlugins::Plugin("BBBSerial") {
46 4 }
47 4 virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override {
48
2/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
8 return new BBBSerialOutput(startChannel, channelCount);
49 }
50 };
51
52 extern "C" {
53 4 FPPPlugins::Plugin* createPlugin() {
54
2/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
8 return new BBBSerialPlugin();
55 }
56 }
57
58 /////////////////////////////////////////////////////////////////////////////
59
60 /*
61 *
62 */
63 4 BBBSerialOutput::BBBSerialOutput(unsigned int startChannel,
64 4 unsigned int channelCount) :
65 ThreadedChannelOutput(startChannel, channelCount),
66 4 m_pixelnet(0),
67 4 m_lastData(NULL),
68 4 m_curData(NULL),
69 4 m_curFrame(0),
70 4 m_pru(NULL),
71 4 m_serialData(NULL) {
72
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 LogDebug(VB_CHANNELOUT, "BBBSerialOutput::BBBSerialOutput(%u, %u)\n",
73 startChannel, channelCount);
74 4 }
75
76 /*
77 *
78 */
79 16 BBBSerialOutput::~BBBSerialOutput() {
80 8 LogDebug(VB_CHANNELOUT, "BBBSerialOutput::~BBBSerialOutput()\n");
81
82
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
8 if (m_lastData)
83 8 free(m_lastData);
84
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
8 if (m_curData)
85 8 free(m_curData);
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
8 if (m_pru)
87 ✗ delete m_pru;
88 16 }
89
90 4 static void compileSerialPRUCode(std::vector<std::string>& sargs) {
91
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 CompilePRUProgram("/opt/fpp/src/pru/compileSerial.sh", sargs);
92 4 }
93
94 /*
95 *
96 */
97 4 int BBBSerialOutput::Init(Json::Value config) {
98
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 LogDebug(VB_CHANNELOUT, "BBBSerialOutput::Init(JSON)\n");
99
100 4 std::vector<std::string> args;
101
102 // Always send 8 outputs worth of data to PRU for now
103 4 m_outputs = 8;
104
105
4/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
4 if (config["subType"].asString() == "Pixelnet") {
106 ✗ args.push_back("-DPIXELNET");
107 ✗ m_pixelnet = 1;
108 } else {
109 4 m_pixelnet = 0;
110
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 args.push_back("-DDMX");
111 }
112 #ifdef USING_PRU_RAM
113 args.push_back("-DUSING_PRU_RAM");
114 #endif
115
116
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 m_startChannels.resize(m_outputs);
117
118 // Initialize the outputs
119
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 4 times.
36 for (int i = 0; i < m_outputs; i++) {
120 32 m_startChannels[i] = -1;
121 }
122
123 4 int maxChannel = 0;
124 4 int maxLen = 0;
125
4/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 4 times.
12 for (int i = 0; i < config["outputs"].size(); i++) {
126
3/6
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 Json::Value s = config["outputs"][i];
127
128 // straight from the config: out of range it wrote past m_startChannels
129
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 int outputNumber = s["outputNumber"].asInt();
130
3/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6 times.
8 if (outputNumber < 0 || outputNumber >= m_outputs) {
131
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogErr(VB_CHANNELOUT, "BBBSerial: ignoring output %d, the cape has %d\n", outputNumber + 1, m_outputs);
132 2 continue;
133 }
134
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 m_startChannels[outputNumber] = s["startChannel"].asInt() - 1;
135
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 int l = s["channelCount"].asInt();
136
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 if (l > maxLen) {
137 4 maxLen = l;
138 }
139
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 }
140
141 4 m_channelCount = 0;
142
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 4 times.
36 for (int i = 0; i < m_outputs; i++) {
143
6/6
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 30 times.
32 if (m_startChannels[i] >= 0 && m_channelCount < m_startChannels[i]) {
144 2 m_channelCount = m_startChannels[i];
145 }
146 }
147
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 m_channelCount += (m_pixelnet ? 4096 : 512);
148 4 m_channelCount -= m_startChannel;
149
150
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 m_channelCount = config["channelCount"].asInt();
151
152 4 int pruNumber = BBB_PRU;
153
154
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 string pru_program = "/tmp/FalconSerial.out";
155
156 4 const char* mode = BBB_PRU ? "gpio" : "pruout";
157
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 args.push_back("-DRUNNING_ON_PRU" + std::to_string(BBB_PRU));
158
159
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 std::string verPostf = "";
160
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 std::string device = config["device"].asString();
161
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 Json::Value root;
162
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (!CapeUtils::INSTANCE.getStringConfig(device, root)) {
163 // might have the version number on it
164 ✗ if (config["pinoutVersion"].asString() == "2.x") {
165 ✗ verPostf = "-v2";
166 }
167 ✗ if (config["pinoutVersion"].asString() == "3.x") {
168 ✗ verPostf = "-v3";
169 }
170 ✗ if (!CapeUtils::INSTANCE.getStringConfig(device + verPostf, root)) {
171 ✗ LogErr(VB_CHANNELOUT, "Could not read pin configuration for %s%s\n", device.c_str(), verPostf.c_str());
172 ✗ AddOutputConfigWarning(13, "BBBSerial: Could not read pin configuration for " + device + verPostf);
173 ✗ return 0;
174 }
175 }
176
177 4 int maxOut = 8;
178 4 int countOut = 0;
179
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 std::ofstream outputFile;
180
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 outputFile.open("/tmp/SerialPinConfiguration.hp", std::ofstream::out | std::ofstream::trunc);
181
182
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 maxOut = root["serial"].size();
183
4/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 4 times.
12 for (int x = 0; x < root["serial"].size(); x++) {
184
4/8
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
8 std::string pinName = root["serial"][x]["pin"].asString();
185
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 m_usedGPIOS.push_back(pinName);
186
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 const PinCapabilities& pin = PinCapabilities::getPinByName(pinName);
187
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if (m_startChannels[x] >= 0) {
188
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
24 pin.configPin(mode, true, "BBBSerialOutput");
189 6 countOut++;
190 }
191
6/12
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
8 outputFile << "#define ser" << std::to_string(x + 1) << "_gpio " << std::to_string(pin.mappedGPIOIdx()) << "\n";
192
5/10
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
8 outputFile << "#define ser" << std::to_string(x + 1) << "_pin " << std::to_string(pin.gpio) << "\n\n";
193
194 8 const BBBPinCapabilities* bp = static_cast<const BBBPinCapabilities*>(&pin);
195
5/10
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
8 outputFile << "#define ser" << std::to_string(x + 1) << "_pru30 " << std::to_string(bp->pruPin(BBB_PRU)) << "\n\n";
196 8 }
197
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 outputFile.close();
198
199
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 4 times.
28 for (int xx = maxOut; xx < m_outputs; xx++) {
200 24 m_startChannels[xx] = -1;
201 }
202
203
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!countOut) {
204 ✗ LogInfo(VB_CHANNELOUT, "No Serial Ouput Data needed\n");
205 ✗ return 1;
206 }
207 char buf[256];
208
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (!m_pixelnet) {
209
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (maxLen < 1 || maxLen > 512) {
210 ✗ maxLen = 512;
211 }
212 4 snprintf(buf, sizeof(buf), "-DDATALEN=%d", (maxLen + 1));
213
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 args.push_back(buf);
214 }
215 4 snprintf(buf, sizeof(buf), "-DNUMOUT=%d", maxOut);
216
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 args.push_back(buf);
217
218
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 compileSerialPRUCode(args);
219
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (!FileExists(pru_program.c_str())) {
220 ✗ LogErr(VB_CHANNELOUT, "%s does not exist!\n", pru_program.c_str());
221 ✗ AddOutputConfigWarning(26, "BBBSerial: Could not compile PRU program");
222 ✗ return 0;
223 }
224
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 LogDebug(VB_CHANNELOUT, "Using program %s\n", pru_program.c_str());
225
226
3/8
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4 m_pru = new BBBPru(BBB_PRU);
227 4 m_serialData = (BBBSerialData*)m_pru->data_ram;
228
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 m_ddrArea = BBBPru::ddrAlloc("BBBSerial", DDR_RESERVED, m_ddrPhys);
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!m_ddrArea) {
230 ✗ LogErr(VB_CHANNELOUT, "BBBSerial: no room in the PRU DDR region\n");
231 ✗ AddOutputConfigWarning(20, "BBBSerial: no room in the PRU DDR region");
232 ✗ return 0;
233 }
234
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (!m_pru->run(pru_program)) {
235 ✗ LogErr(VB_CHANNELOUT, "BBBSerial: Unable to start PRU. May require a reboot.\n");
236 ✗ AddOutputConfigWarning(20, "BBBSerial: Unable to start PRU. May require a reboot.");
237 ✗ return 0;
238 }
239 // after run(): it clears the PRU data ram, so anything written to the
240 // control block before this point is thrown away
241 4 m_serialData->address_dma = m_ddrPhys;
242 4 m_serialData->command = 0;
243 4 m_serialData->response = 0;
244
245
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 int sz = m_pixelnet ? (4096 + 6) : (512 + 1);
246
247 4 m_lastData = (uint8_t*)calloc(1, m_outputs * sz);
248 4 m_curData = (uint8_t*)calloc(1, m_outputs * sz);
249
250
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 4 times.
36 for (int i = 0; i < m_outputs; i++) {
251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (m_pixelnet) {
252 ✗ m_curData[i + (0 * m_outputs)] = '\xAA';
253 ✗ m_curData[i + (1 * m_outputs)] = '\x55';
254 ✗ m_curData[i + (2 * m_outputs)] = '\x55';
255 ✗ m_curData[i + (3 * m_outputs)] = '\xAA';
256 ✗ m_curData[i + (4 * m_outputs)] = '\x15';
257 ✗ m_curData[i + (5 * m_outputs)] = '\x5D';
258 } else {
259 32 m_curData[i] = '\x00';
260 }
261 }
262 4 memcpy(m_lastData, m_curData, m_outputs * sz);
263
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 return ThreadedChannelOutput::Init(config);
264 4 }
265
266 8 void BBBSerialOutput::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) {
267
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 8 times.
72 for (int i = 0; i < m_outputs; i++) {
268
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 52 times.
64 if (m_startChannels[i] >= 0) {
269
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 addRange(m_startChannels[i], m_pixelnet ? m_startChannels[i] + 4095 : m_startChannels[i] + 511);
270 }
271 }
272 8 }
273
274 /*
275 *
276 */
277 4 int BBBSerialOutput::Close(void) {
278 4 LogDebug(VB_CHANNELOUT, "BBBSerialOutput::Close()\n");
279
280 // Stop the output thread before anything below it goes away. RawSendData
281 // writes through m_serialData (PRU data ram) and m_ddrArea (the shared DDR
282 // region), and deleting the last BBBPru unmaps both - on a config reload
283 // that leaves the still-running thread writing into unmapped/gated memory.
284 // ThreadedChannelOutput::Close() below calls this again; it is idempotent.
285 4 StopOutputThread();
286
287
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 if (m_serialData && m_pru) {
288 // Send the stop command
289 4 m_serialData->response = 0;
290 4 m_serialData->command = 0xFF;
291
292 4 __asm__ __volatile__("" ::
293 : "memory");
294 4 int cnt = 0;
295
5/6
✓ Branch 0 taken 200 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 200 times.
✓ Branch 5 taken 4 times.
208 while (wait && cnt < 50 && m_serialData->response != 0xFFFF) {
296
1/2
✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
200 std::this_thread::sleep_for(std::chrono::milliseconds(1));
297 200 __asm__ __volatile__("" ::
298 : "memory");
299 200 cnt++;
300 }
301 // Clear the command while the core is still up: m_serialData is PRU
302 // data RAM, which can be unreachable once remoteproc has stopped the
303 // core (the access then takes an external abort - see
304 // BBB48StringOutput::StopPRU()). The firmware has already acked the
305 // stop above, so it no longer reads the command.
306 4 m_serialData->command = 0;
307 4 m_pru->stop();
308
309
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 delete m_pru;
310 4 m_pru = NULL;
311 }
312
313
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 BBBPru::ddrRelease("BBBSerial");
314 4 m_ddrArea = nullptr;
315
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (m_pruStalled) {
316 4 m_pruStalled = false;
317
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 WarningHolder::RemoveWarning(20, PRU_STALLED_WARNING);
318 }
319 4 LogDebug(VB_CHANNELOUT, "BBBSerialOutput::Close() done\n");
320 4 return ThreadedChannelOutput::Close();
321 }
322
323 /*
324 *
325 */
326 21 int BBBSerialOutput::RawSendData(unsigned char* channelData) {
327 21 LogExcess(VB_CHANNELOUT, "BBBSerialOutput::RawSendData(%p)\n",
328 channelData);
329
330
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if (!m_serialData) {
331 ✗ return m_channelCount;
332 }
333
334 21 m_curFrame++;
335
336 // Bypass LEDscape draw routine and format data for PRU ourselves
337
338 21 uint8_t* const out = m_curData;
339 21 uint8_t* c = out;
340 21 uint8_t* s = (uint8_t*)channelData;
341
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 int chCount = m_pixelnet ? 4096 : 512;
342
343
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 21 times.
189 for (int i = 0; i < m_outputs; i++) {
344
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 36 times.
168 if (m_startChannels[i] < 0) {
345 132 continue;
346 }
347 // Skip the headers (6 bytes per output for Pixelnet and 1 byte per output
348 // for DMX) and index into the proper position in the m_outputs number of
349 // bytes in each slice
350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if (m_pixelnet)
351 ✗ c = out + i + (m_outputs * 6);
352 else
353 36 c = out + i + (m_outputs);
354
355 // Get the start channel for this output
356 36 s = (uint8_t*)(channelData + m_startChannels[i] - m_startChannel);
357
358 // Now copy the individual channel data into each slice
359
2/2
✓ Branch 0 taken 18432 times.
✓ Branch 1 taken 36 times.
18468 for (int ch = 0; ch < chCount; ch++) {
360 18432 *c = *s;
361 18432 s++;
362 18432 c += m_outputs;
363 }
364 }
365
366 // Wait for the previous draw to finish. The PRU clears the command as
367 // soon as it picks the frame up, so this normally falls straight through;
368 // bound it anyway so a PRU that never answers stalls this one output
369 // visibly instead of pegging a core forever with nothing in the UI.
370 21 int spins = 0;
371
2/2
✓ Branch 0 taken 17000017 times.
✓ Branch 1 taken 4 times.
17000021 while (m_serialData->command) {
372
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 17000000 times.
17000017 if (++spins > 1000000) {
373
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 13 times.
17 if (!m_pruStalled) {
374 4 m_pruStalled = true;
375 4 LogErr(VB_CHANNELOUT, "BBBSerial: PRU is not consuming frames, serial output stalled\n");
376
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 WarningHolder::AddWarning(20, PRU_STALLED_WARNING);
377 }
378 17 return m_channelCount;
379 }
380 }
381
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (m_pruStalled) {
382 ✗ m_pruStalled = false;
383 ✗ WarningHolder::RemoveWarning(20, PRU_STALLED_WARNING);
384 }
385
386
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 int frame_size = m_pixelnet ? (4096 + 6) : (512 + 1);
387 4 frame_size *= m_outputs;
388
389 4 unsigned frame = 0;
390 // unsigned frame = m_curFrame & 1;
391
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 if (m_curFrame == 1 || memcmp(m_lastData, m_curData, frame_size)) {
392 // don't copy to DMA memory unless really needed to avoid bus contention on the DMA bus
393
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 int sz = m_pixelnet ? (4096 + 6) : (512 + 1);
394 4 sz *= m_outputs;
395 #ifdef USING_PRU_RAM
396 uint8_t* const realout = (uint8_t*)m_pru->data_ram + 512;
397 memcpy(realout, m_curData, sz);
398 #else
399 4 memcpy(m_ddrArea, m_curData, sz);
400
401 4 m_serialData->address_dma = m_ddrPhys;
402 #endif
403
404 4 uint8_t* tmp = m_lastData;
405 4 m_lastData = m_curData;
406 4 m_curData = tmp;
407 }
408
409 // make sure memory is flushed before command is set to 1
410 4 __asm__ __volatile__("" ::
411 : "memory");
412
413 // Send the start command
414 4 m_serialData->command = 1;
415
416 4 return m_channelCount;
417 }
418
419 /*
420 *
421 */
422 8 void BBBSerialOutput::DumpConfig(void) {
423 8 LogDebug(VB_CHANNELOUT, "BBBSerialOutput::DumpConfig()\n");
424
425 8 LogDebug(VB_CHANNELOUT, " Pixelnet : %d\n", m_pixelnet);
426 8 LogDebug(VB_CHANNELOUT, " Outputs :\n");
427
428
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 8 times.
72 for (int i = 0; i < m_outputs; i++) {
429
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 64 times.
64 LogDebug(VB_CHANNELOUT, " #%d: %d-%d (%d Ch)\n",
430 i + 1,
431 m_startChannels[i] + 1,
432 m_pixelnet ? m_startChannels[i] + 4096 : m_startChannels[i] + 512,
433 m_pixelnet ? 4096 : 512);
434 }
435
436 8 ThreadedChannelOutput::DumpConfig();
437 8 }
438