2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
23 * Dominic Clifton - Serial port abstraction, Separation of common STM32 code for cleanflight, various cleanups.
24 * Hamasaki/Timecop - Initial baseflight code
34 #include "build/build_config.h"
36 #include "common/utils.h"
38 #include "io/serial.h"
39 #include "serial_tcp.h"
41 #define BASE_PORT 5760
43 static const struct serialPortVTable tcpVTable
; // Forward
44 static tcpPort_t tcpSerialPorts
[SERIAL_PORT_COUNT
];
45 static bool tcpPortInitialized
[SERIAL_PORT_COUNT
];
46 static bool tcpStart
= false;
53 static void onData(dyad_Event
*e
)
55 tcpPort_t
* s
= (tcpPort_t
*)(e
->udata
);
56 tcpDataIn(s
, (uint8_t*)e
->data
, e
->size
);
59 static void onClose(dyad_Event
*e
)
61 tcpPort_t
* s
= (tcpPort_t
*)(e
->udata
);
64 fprintf(stderr
, "[CLS]UART%u: %d,%d\n", s
->id
+ 1U, s
->connected
, s
->clientCount
);
65 if (s
->clientCount
== 0) {
70 static void onAccept(dyad_Event
*e
)
72 tcpPort_t
* s
= (tcpPort_t
*)(e
->udata
);
73 fprintf(stderr
, "New connection on UART%u, %d\n", s
->id
+ 1U, s
->clientCount
);
76 if (s
->clientCount
> 0) {
77 dyad_close(e
->remote
);
81 fprintf(stderr
, "[NEW]UART%u: %d,%d\n", s
->id
+ 1U, s
->connected
, s
->clientCount
);
83 dyad_setNoDelay(e
->remote
, 1);
84 dyad_setTimeout(e
->remote
, 120);
85 dyad_addListener(e
->remote
, DYAD_EVENT_DATA
, onData
, e
->udata
);
86 dyad_addListener(e
->remote
, DYAD_EVENT_CLOSE
, onClose
, e
->udata
);
89 static tcpPort_t
* tcpReconfigure(tcpPort_t
*s
, int id
)
91 if (tcpPortInitialized
[id
]) {
92 fprintf(stderr
, "port is already initialized!\n");
96 if (pthread_mutex_init(&s
->txLock
, NULL
) != 0) {
97 fprintf(stderr
, "TX mutex init failed - %d\n", errno
);
98 // TODO: clean up & re-init
102 if (pthread_mutex_init(&s
->rxLock
, NULL
) != 0) {
103 fprintf(stderr
, "RX mutex init failed - %d\n", errno
);
104 // TODO: clean up & re-init
109 tcpPortInitialized
[id
] = true;
111 s
->connected
= false;
115 s
->serv
= dyad_newStream();
116 dyad_setNoDelay(s
->serv
, 1);
117 dyad_addListener(s
->serv
, DYAD_EVENT_ACCEPT
, onAccept
, s
);
119 if (dyad_listenEx(s
->serv
, NULL
, BASE_PORT
+ id
+ 1, 10) == 0) {
120 fprintf(stderr
, "bind port %u for UART%u\n", (unsigned)BASE_PORT
+ id
+ 1, (unsigned)id
+ 1);
122 fprintf(stderr
, "bind port %u for UART%u failed!!\n", (unsigned)BASE_PORT
+ id
+ 1, (unsigned)id
+ 1);
127 serialPort_t
*serTcpOpen(serialPortIdentifier_e identifier
, serialReceiveCallbackPtr rxCallback
, void *rxCallbackData
, uint32_t baudRate
, portMode_e mode
, portOptions_e options
)
131 int id
= findSerialPortIndexByIdentifier(identifier
);
133 if (id
>= 0 && id
< (int)ARRAYLEN(tcpSerialPorts
)) {
134 s
= tcpReconfigure(&tcpSerialPorts
[id
], id
);
141 s
->port
.vTable
= &tcpVTable
;
143 // common serial initialisation code should move to serialPort::init()
144 s
->port
.rxBufferHead
= s
->port
.rxBufferTail
= 0;
145 s
->port
.txBufferHead
= s
->port
.txBufferTail
= 0;
146 s
->port
.rxBufferSize
= RX_BUFFER_SIZE
;
147 s
->port
.txBufferSize
= TX_BUFFER_SIZE
;
148 s
->port
.rxBuffer
= s
->rxBuffer
;
149 s
->port
.txBuffer
= s
->txBuffer
;
151 // callback works for IRQ-based RX ONLY
152 s
->port
.rxCallback
= rxCallback
;
153 s
->port
.rxCallbackData
= rxCallbackData
;
155 s
->port
.baudRate
= baudRate
;
156 s
->port
.options
= options
;
158 return (serialPort_t
*)s
;
161 static uint32_t tcpTotalRxBytesWaiting(const serialPort_t
*instance
)
163 tcpPort_t
*s
= (tcpPort_t
*)instance
;
165 pthread_mutex_lock(&s
->rxLock
);
166 if (s
->port
.rxBufferHead
>= s
->port
.rxBufferTail
) {
167 count
= s
->port
.rxBufferHead
- s
->port
.rxBufferTail
;
169 count
= s
->port
.rxBufferSize
+ s
->port
.rxBufferHead
- s
->port
.rxBufferTail
;
171 pthread_mutex_unlock(&s
->rxLock
);
176 static uint32_t tcpTotalTxBytesFree(const serialPort_t
*instance
)
178 tcpPort_t
*s
= (tcpPort_t
*)instance
;
181 pthread_mutex_lock(&s
->txLock
);
182 if (s
->port
.txBufferHead
>= s
->port
.txBufferTail
) {
183 bytesUsed
= s
->port
.txBufferHead
- s
->port
.txBufferTail
;
185 bytesUsed
= s
->port
.txBufferSize
+ s
->port
.txBufferHead
- s
->port
.txBufferTail
;
187 uint32_t bytesFree
= (s
->port
.txBufferSize
- 1) - bytesUsed
;
188 pthread_mutex_unlock(&s
->txLock
);
193 static bool isTcpTransmitBufferEmpty(const serialPort_t
*instance
)
195 tcpPort_t
*s
= (tcpPort_t
*)instance
;
196 pthread_mutex_lock(&s
->txLock
);
197 bool isEmpty
= s
->port
.txBufferTail
== s
->port
.txBufferHead
;
198 pthread_mutex_unlock(&s
->txLock
);
202 static uint8_t tcpRead(serialPort_t
*instance
)
205 tcpPort_t
*s
= (tcpPort_t
*)instance
;
206 pthread_mutex_lock(&s
->rxLock
);
208 ch
= s
->port
.rxBuffer
[s
->port
.rxBufferTail
];
209 if (s
->port
.rxBufferTail
+ 1 >= s
->port
.rxBufferSize
) {
210 s
->port
.rxBufferTail
= 0;
212 s
->port
.rxBufferTail
++;
214 pthread_mutex_unlock(&s
->rxLock
);
219 static void tcpWrite(serialPort_t
*instance
, uint8_t ch
)
221 tcpPort_t
*s
= (tcpPort_t
*)instance
;
222 pthread_mutex_lock(&s
->txLock
);
224 s
->port
.txBuffer
[s
->port
.txBufferHead
] = ch
;
225 if (s
->port
.txBufferHead
+ 1 >= s
->port
.txBufferSize
) {
226 s
->port
.txBufferHead
= 0;
228 s
->port
.txBufferHead
++;
230 pthread_mutex_unlock(&s
->txLock
);
235 void tcpDataOut(tcpPort_t
*instance
)
237 tcpPort_t
*s
= (tcpPort_t
*)instance
;
238 if (s
->conn
== NULL
) return;
239 pthread_mutex_lock(&s
->txLock
);
241 if (s
->port
.txBufferHead
< s
->port
.txBufferTail
) {
242 // send data till end of buffer
243 int chunk
= s
->port
.txBufferSize
- s
->port
.txBufferTail
;
244 dyad_write(s
->conn
, (const void *)&s
->port
.txBuffer
[s
->port
.txBufferTail
], chunk
);
245 s
->port
.txBufferTail
= 0;
247 int chunk
= s
->port
.txBufferHead
- s
->port
.txBufferTail
;
249 dyad_write(s
->conn
, (const void*)&s
->port
.txBuffer
[s
->port
.txBufferTail
], chunk
);
250 s
->port
.txBufferTail
= s
->port
.txBufferHead
;
252 pthread_mutex_unlock(&s
->txLock
);
255 void tcpDataIn(tcpPort_t
*instance
, uint8_t* ch
, int size
)
257 tcpPort_t
*s
= (tcpPort_t
*)instance
;
258 pthread_mutex_lock(&s
->rxLock
);
261 // printf("%c", *ch);
262 s
->port
.rxBuffer
[s
->port
.rxBufferHead
] = *(ch
++);
263 if (s
->port
.rxBufferHead
+ 1 >= s
->port
.rxBufferSize
) {
264 s
->port
.rxBufferHead
= 0;
266 s
->port
.rxBufferHead
++;
269 pthread_mutex_unlock(&s
->rxLock
);
273 static const struct serialPortVTable tcpVTable
= {
274 .serialWrite
= tcpWrite
,
275 .serialTotalRxWaiting
= tcpTotalRxBytesWaiting
,
276 .serialTotalTxFree
= tcpTotalTxBytesFree
,
277 .serialRead
= tcpRead
,
278 .serialSetBaudRate
= NULL
,
279 .isSerialTransmitBufferEmpty
= isTcpTransmitBufferEmpty
,
281 .setCtrlLineStateCb
= NULL
,
282 .setBaudRateCb
= NULL
,