GCC Code Coverage Report


Directory: ./
File: channeloutput/SPIws2801.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 60 71 84.5%
Functions: 10 10 100.0%
Branches: 27 66 40.9%

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 "../log.h"
18
19 #include "SPIws2801.h"
20 #include <fcntl.h>
21
22 #define SPIWS2801_MAX_CHANNELS 1530
23
24 #include "Plugin.h"
25 class SPIws2801Plugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin {
26 public:
27 1 SPIws2801Plugin() :
28
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
3 FPPPlugins::Plugin("SPIws2801") {
29 1 }
30 1 virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override {
31
2/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2 return new SPIws2801Output(startChannel, channelCount);
32 }
33 };
34
35 extern "C" {
36 1 FPPPlugins::Plugin* createPlugin() {
37
2/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2 return new SPIws2801Plugin();
38 }
39 }
40
41 /////////////////////////////////////////////////////////////////////////////
42
43 1 SPIws2801Output::SPIws2801Output(unsigned int startChannel, unsigned int channelCount) :
44 ThreadedChannelOutput(startChannel, channelCount),
45 1 m_port(-1),
46 1 m_pi36(0),
47 1 m_pi36Data(NULL),
48 1 m_pi36DataSize(0),
49 1 m_spi(nullptr) {
50
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogDebug(VB_CHANNELOUT, "SPIws2801Output::SPIws2801Output(%u, %u)\n",
51 startChannel, channelCount);
52 1 }
53
54 4 SPIws2801Output::~SPIws2801Output() {
55 2 LogDebug(VB_CHANNELOUT, "SPIws2801Output::~SPIws2801Output()\n");
56
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 if (m_spi) {
57
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 delete m_spi;
58 }
59
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 delete[] m_pi36Data;
60 4 }
61
62 1 int SPIws2801Output::Init(Json::Value config) {
63 1 LogDebug(VB_CHANNELOUT, "SPIws2801Output::Init()\n");
64
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (config.isMember("device")) {
65
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 std::string dev = config["device"].asString();
66
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if (dev == "spidev0.0") {
67 1 m_port = 0;
68 ✗ } else if (dev == "spidev0.1") {
69 ✗ m_port = 1;
70 }
71 1 }
72
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (config.isMember("pi36")) {
73
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (config["pi36"].asInt() == 1) {
74 1 m_pi36 = 1;
75 1 m_pi36DataSize = m_channelCount >= 36 ? m_channelCount : 36;
76 1 m_pi36Data = new unsigned char[m_pi36DataSize];
77 }
78 }
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (m_port == -1) {
80 ✗ LogErr(VB_CHANNELOUT, "Invalid Config: No port\n");
81 ✗ return 0;
82 }
83
84 1 LogDebug(VB_CHANNELOUT, "Using SPI Port %d\n", m_port);
85
2/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 m_spi = new SPIUtils(m_port, 1000000);
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!m_spi->isOk()) {
87 ✗ LogErr(VB_CHANNELOUT, "Unable to open SPI device\n");
88 ✗ delete m_spi;
89 ✗ m_spi = nullptr;
90 ✗ return 0;
91 }
92
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return ThreadedChannelOutput::Init(config);
93 }
94
95 1 int SPIws2801Output::Close(void) {
96 1 LogDebug(VB_CHANNELOUT, "SPIws2801Output::Close()\n");
97
98 1 return ThreadedChannelOutput::Close();
99 }
100 2 void SPIws2801Output::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) {
101 // the Pi36 layout always reads its 36 WS2803 channels, even when fewer
102 // are configured
103
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 int count = m_pi36 ? std::max((int)m_channelCount, 36) : (int)m_channelCount;
104 2 addRange(m_startChannel, m_startChannel + count - 1);
105 2 }
106
107 5 int SPIws2801Output::RawSendData(unsigned char* channelData) {
108 5 LogDebug(VB_CHANNELOUT, "SPIws2801Output::RawSendData(%p)\n", channelData);
109
110
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (!m_spi) {
111 ✗ return 0;
112 }
113
114
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (m_pi36) {
115 // Hanson Electronics Pi36 controller has 2x WS2803 onboard and a
116 // chained output for ws2801 pixels. WS2803 pull data from the
117 // end of the stream while WS2801 pull from the beginning, so we
118 // need to swap some channels around before sending.
119 5 int ws2801Channels = m_pi36DataSize - 36;
120
121 5 memcpy(m_pi36Data + ws2801Channels + 18, channelData, 18);
122 5 memcpy(m_pi36Data + ws2801Channels, channelData + 18, 18);
123
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (ws2801Channels)
125 ✗ memcpy(m_pi36Data, channelData + 36, ws2801Channels);
126
127 5 m_spi->xfer(m_pi36Data, nullptr, m_pi36DataSize);
128 } else {
129 ✗ m_spi->xfer(channelData, nullptr, m_channelCount);
130 }
131
132 5 return m_channelCount;
133 }
134
135 2 void SPIws2801Output::DumpConfig(void) {
136 2 LogDebug(VB_CHANNELOUT, "SPIws2801Output::DumpConfig()\n");
137
138 2 LogDebug(VB_CHANNELOUT, " port : %d\n", m_port);
139
140 2 ThreadedChannelOutput::DumpConfig();
141 2 }
142