GCC Code Coverage Report


Directory: ./
File: channeloutput/USBDMX.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 69 81 85.2%
Functions: 11 11 100.0%
Branches: 37 74 50.0%

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 <fcntl.h>
18 #include <termios.h>
19 #include <unistd.h>
20
21 #include "../common_mini.h" // FPPstrerror() -- needed directly for NOPCH builds
22 #include "../log.h"
23
24 #include "USBDMX.h"
25 #include "serialutil.h"
26 #include "ChannelOutputSetup.h"
27
28 #define DMX_MAX_CHANNELS 512
29
30 #include "Plugin.h"
31 class USBDMXPlugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin {
32 public:
33 7 USBDMXPlugin() :
34
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
21 FPPPlugins::Plugin("USBDMX") {
35 7 }
36 8 virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override {
37
2/6
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
16 return new USBDMXOutput(startChannel, channelCount);
38 }
39 };
40
41 extern "C" {
42 7 FPPPlugins::Plugin* createPlugin() {
43
2/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
14 return new USBDMXPlugin();
44 }
45 }
46
47 /////////////////////////////////////////////////////////////////////////////
48
49 8 USBDMXOutput::USBDMXOutput(unsigned int startChannel, unsigned int channelCount) :
50
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 ThreadedChannelOutput(startChannel, channelCount), SerialChannelOutput(), m_dongleType(DMX_DVC_UNKNOWN) {
51
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 LogDebug(VB_CHANNELOUT, "USBDMXOutput::USBDMXOutput(%u, %u)\n",
52 startChannel, channelCount);
53
54 8 m_useDoubleBuffer = 1;
55 // DMX protocol requires data to be sent at least every 250ms
56 8 m_maxWait = 250;
57 8 m_dataOffset = 1;
58 8 memset(m_outputData, 0, sizeof(m_outputData));
59
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (m_channelCount > DMX_MAX_CHANNELS) {
60
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 AddOutputConfigWarning(28, "USBDMX: Invalid Config. Channel count of " + std::to_string(m_channelCount) + " exceeds the DMX universe size of 512");
61 2 m_channelCount = DMX_MAX_CHANNELS;
62 }
63 8 }
64
65 32 USBDMXOutput::~USBDMXOutput() {
66 16 LogDebug(VB_CHANNELOUT, "USBDMXOutput::~USBDMXOutput()\n");
67 32 }
68 2 void USBDMXOutput::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) {
69 2 addRange(m_startChannel, m_startChannel + m_channelCount - 1);
70 2 }
71
72 8 int USBDMXOutput::Init(Json::Value config) {
73 8 LogDebug(VB_CHANNELOUT, "USBDMXOutput::Init()\n");
74
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (config.isMember("type")) {
75
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 std::string type = config["type"].asString();
76
3/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 5 times.
8 if (type == "DMX-Open") {
77
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 LogDebug(VB_CHANNELOUT, "Treating device as DMX-Open compatible\n");
78 3 m_dongleType = DMX_DVC_OPEN;
79
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 } else if (type == "DMX-Pro") {
80
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 LogDebug(VB_CHANNELOUT, "Treating device as DMX-Pro compatible\n");
81 5 m_dongleType = DMX_DVC_PRO;
82 }
83 8 }
84
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 if (m_dongleType == DMX_DVC_OPEN) {
85
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (!setupSerialPort(config, 250000, "8N2")) {
86 1 return 0;
87 }
88
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 } else if (m_dongleType == DMX_DVC_PRO) {
89
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (!setupSerialPort(config, 115200, "8N1")) {
90 5 return 0;
91 }
92 } else {
93 ✗ LogErr(VB_CHANNELOUT, "Invalid Config. Unknown dongle type.\n");
94 ✗ AddOutputConfigWarning(28, "USBDMX: Invalid Config. Unknown dongle type.");
95 ✗ return 0;
96 }
97
98
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (m_dongleType == DMX_DVC_OPEN) {
99
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (SerialResetRTS(m_fd) != 0) {
100 1 LogErr(VB_CHANNELOUT, "Error %d resetting RTS on %s: %s\n",
101 errno, m_deviceName.c_str(), FPPstrerror(errno));
102 1 return 0;
103 }
104 1 m_dataLen = m_channelCount + 1;
105 ✗ } else if (m_dongleType == DMX_DVC_PRO) {
106 ✗ m_outputData[0] = 0x7E;
107 ✗ m_outputData[1] = 0x06;
108 ✗ m_outputData[2] = (m_channelCount + 1) & 0xFF;
109 ✗ m_outputData[3] = ((m_channelCount + 1) >> 8) & 0xFF;
110 ✗ m_outputData[4] = 0x00;
111 ✗ m_dataOffset = 5;
112
113 ✗ m_outputData[m_channelCount + 5] = 0xE7;
114 ✗ m_dataLen = m_channelCount + 6;
115 }
116
117
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);
118 }
119
120 1 int USBDMXOutput::Close(void) {
121 1 LogDebug(VB_CHANNELOUT, "USBDMXOutput::Close()\n");
122 1 closeSerialPort();
123 1 return ThreadedChannelOutput::Close();
124 }
125
126 7 int USBDMXOutput::RawSendData(unsigned char* channelData) {
127 7 LogExcess(VB_CHANNELOUT, "USBDMXOutput::RawSendData(%p)\n", channelData);
128 7 memcpy(m_outputData + m_dataOffset, channelData, m_channelCount);
129 7 WaitTimedOut();
130 7 return m_channelCount;
131 }
132 7 void USBDMXOutput::WaitTimedOut() {
133
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (m_dongleType == DMX_DVC_OPEN) {
134 // TIOCSBRK does not wait for output to drain: a break sent while the
135 // previous frame (~23ms for 513 slots) is still going out lands in
136 // the middle of it
137 7 SerialWaitDrained(m_fd, 50);
138 7 SerialSendBreak(m_fd, 200);
139 7 usleep(20);
140 }
141 7 SerialWriteAll(m_fd, m_outputData, m_dataLen, 500);
142 7 }
143
144 2 void USBDMXOutput::DumpConfig(void) {
145 2 LogDebug(VB_CHANNELOUT, "USBDMXOutput::DumpConfig()\n");
146
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 LogDebug(VB_CHANNELOUT, " Dongle Type: %s\n",
147 m_dongleType == DMX_DVC_PRO ? "Pro" : m_dongleType == DMX_DVC_OPEN ? "Open"
148 : "UNKNOWN");
149 2 dumpSerialConfig();
150 2 ThreadedChannelOutput::DumpConfig();
151 2 }
152