GCC Code Coverage Report


Directory: ./
File: channeloutput/SerialChannelOutput.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 70 83 84.3%
Functions: 5 5 100.0%
Branches: 86 176 48.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 "../config.h"
18 #include "../log.h"
19 #include <unistd.h>
20
21 #include "SerialChannelOutput.h"
22 #include "serialutil.h"
23 #include "ChannelOutputSetup.h"
24 #include "../util/GPIOUtils.h"
25 #ifdef PLATFORM_BB64
26 #include "../util/BBBPruSerial.h"
27 #endif
28
29 #ifdef HAS_CAPEUTILS
30 #include "../non-gpl/CapeUtils/CapeUtils.h"
31 #endif
32
33 42 SerialChannelOutput::SerialChannelOutput() :
34
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
84 m_deviceName("UNKNOWN"),
35 42 m_fd(-1) {
36 42 }
37
38 84 SerialChannelOutput::~SerialChannelOutput() {
39 // an output whose Init() failed after the port opened is deleted without
40 // Close(); the fd would leak and keep its TIOCEXCL lock, so the next
41 // attempt to open the port fails as "in use"
42
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 40 times.
84 if (m_fd >= 0) {
43 4 closeSerialPort();
44 }
45
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 37 times.
84 if (!pinName.empty()) {
46 10 PinCapabilities::getPinByName(pinName).releasePin();
47 }
48 84 }
49
50 40 bool SerialChannelOutput::setupSerialPort(Json::Value& config, int baud, const char* mode) {
51
3/4
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 1 times.
40 if (config.isMember("device")) {
52
2/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 m_deviceName = config["device"].asString();
53 }
54
3/4
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 39 times.
40 if (m_deviceName == "UNKNOWN") {
55
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogErr(VB_CHANNELOUT, "Invalid Config. Unknown device.\n");
56
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 AddOutputConfigWarning(26, "SerialChannelOutput: Invalid Config. Unknown device.");
57 1 return false;
58 }
59
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 std::string desc = "";
60
3/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 38 times.
39 if (config.isMember("subType")) {
61
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 desc = config["subType"].asString();
62
2/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 } else if (config.isMember("type")) {
63
2/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 desc = config["type"].asString();
64 }
65
66 39 const PinCapabilities* pin = nullptr;
67 #ifdef HAS_CAPEUTILS
68
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 Json::Value v = CapeUtils::INSTANCE.getCapeInfo();
69
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (v.isMember("tty-labels")) {
70
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 Json::Value ttyLabels = v["tty-labels"];
71
3/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
3 if (ttyLabels.isMember(m_deviceName)) {
72
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 std::string pinName = ttyLabels[m_deviceName].asString();
73
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 pin = PinCapabilities::getPinByName(pinName).ptr();
74
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 if (pin && !pin->uart.ends_with("-tx")) {
75 // The label names the header pin the cape drives its line
76 // driver from. On another board the same pin can be only a
77 // UART's receive line (P1-10 is UART2 TX on a PocketBeagle but
78 // UART6 RX on a PocketBeagle 2), and opening that UART would
79 // send the data out of some other pin while this one sits idle.
80 #ifdef PLATFORM_BB64
81 // A PRU sends on the pin instead.
82
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 std::string pruDesc = desc + "-" + m_deviceName;
83
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 m_fd = BBBPruSerialOpen(*pin, baud, mode, pruDesc);
84
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (m_fd < 0) {
85 ✗ return false; // BBBPruSerialOpen() raised the warning
86 }
87
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 this->pinName = pinName;
88
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 m_deviceName = pinName + " (PRU)";
89 1 return true;
90 #else
91 // Only a different board or cape changes it; like the other
92 // config errors here it comes down when the config is next
93 // loaded, and is raised again if it still holds.
94 ✗ std::string w = desc + ": " + m_deviceName + " is wired to " + pinName +
95 ✗ ", which has no serial transmit on this board";
96 ✗ LogErr(VB_CHANNELOUT, "%s\n", w.c_str());
97 ✗ AddOutputConfigWarning(26, w);
98 ✗ return false;
99 #endif
100 1 }
101 ✗ if (pin) {
102 ✗ std::string nd = pin->uart.substr(0, pin->uart.find("-"));
103 ✗ LogDebug(VB_CHANNELOUT, "SerialChannelOutput::setupSerialPort: Using cape mapping from %s to %s\n", m_deviceName.c_str(), nd.c_str());
104 ✗ m_deviceName = nd;
105 ✗ this->pinName = pinName;
106 ✗ }
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 }
108
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 }
109 #endif
110
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 if (pin == nullptr) {
111
3/6
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 pin = PinCapabilities::getPinByName(m_deviceName + "-tx").ptr();
112
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 if (!pin) {
113
3/6
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 pin = PinCapabilities::getPinByUART(m_deviceName + "-tx").ptr();
114 }
115
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 34 times.
38 if (pin) {
116
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 this->pinName = pin->name;
117 }
118 }
119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (desc.empty()) {
120 ✗ desc = m_deviceName;
121 } else {
122
2/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 desc = desc + "-" + m_deviceName;
123 }
124
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 m_deviceName = "/dev/" + m_deviceName;
125 // checked before the pin is muxed: SerialOpen() refuses the console too,
126 // but by then this would already have put console output on the pin
127
3/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 37 times.
38 if (SerialIsConsole(m_deviceName.c_str())) {
128
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogErr(VB_CHANNELOUT, "%s: is the system console, not opening it\n", m_deviceName.c_str());
129
4/8
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1 AddOutputConfigWarning(26, desc + ": " + m_deviceName + " is the system console and cannot be used for serial output");
130 1 return false;
131 }
132
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 34 times.
37 if (pin) {
133
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
9 pin->configPin("uart", true, desc);
134 }
135
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 m_fd = SerialOpen(m_deviceName.c_str(), baud, mode, true, pin == nullptr, desc.c_str());
136
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 32 times.
37 if (m_fd < 0) {
137
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 LogErr(VB_CHANNELOUT, "Could not open serial port %s\n", m_deviceName.c_str());
138
4/8
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
5 AddOutputConfigWarning(26, desc + ": could not open " + m_deviceName + ". Is the device connected?");
139 5 return false;
140 }
141 32 return true;
142 39 }
143
144 33 int SerialChannelOutput::closeSerialPort() {
145 33 LogDebug(VB_CHANNELOUT, "SerialChannelOutput::closeSerialPort()\n");
146 33 SerialClose(m_fd);
147 33 m_fd = -1;
148 33 return 1;
149 }
150
151 46 void SerialChannelOutput::dumpSerialConfig(void) {
152 46 LogDebug(VB_CHANNELOUT, " Device Name: %s\n", m_deviceName.c_str());
153 46 LogDebug(VB_CHANNELOUT, " fd : %d\n", m_fd);
154 46 }
155