atbetaflight/src/main/drivers/serial_tcp.c

269 lines
7.7 KiB
C

/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Authors:
* Dominic Clifton - Serial port abstraction, Separation of common STM32 code for cleanflight, various cleanups.
* Hamasaki/Timecop - Initial baseflight code
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "platform.h"
#include "build/build_config.h"
#include "common/utils.h"
#include "io/serial.h"
#include "serial_tcp.h"
#define BASE_PORT 5760
const struct serialPortVTable uartVTable[]; // Forward
static uartPort_t tcpSerialPorts[SERIAL_PORT_COUNT];
static bool portInited[SERIAL_PORT_COUNT];
static bool tcpStart = false;
bool tcpIsStart(void) {
return tcpStart;
}
static void onData(dyad_Event *e) {
uartPort_t* s = (uartPort_t*)(e->udata);
tcpDataIn(s, (uint8_t*)e->data, e->size);
}
static void onClose(dyad_Event *e) {
uartPort_t* s = (uartPort_t*)(e->udata);
s->clientCount--;
s->conn = NULL;
fprintf(stderr, "[CLS]UART%u: %d,%d\n", s->id + 1, s->connected, s->clientCount);
if(s->clientCount == 0) {
s->connected = false;
}
}
static void onAccept(dyad_Event *e) {
uartPort_t* s = (uartPort_t*)(e->udata);
fprintf(stderr, "New connection on UART%u, %d\n", s->id + 1, s->clientCount);
s->connected = true;
if(s->clientCount > 0) {
dyad_close(e->remote);
return;
}
s->clientCount++;
fprintf(stderr, "[NEW]UART%u: %d,%d\n", s->id + 1, s->connected, s->clientCount);
s->conn = e->remote;
dyad_setNoDelay(e->remote, 1);
dyad_setTimeout(e->remote, 120);
dyad_addListener(e->remote, DYAD_EVENT_DATA, onData, e->udata);
dyad_addListener(e->remote, DYAD_EVENT_CLOSE, onClose, e->udata);
}
static uartPort_t* tcpReconfigure(uartPort_t *s, int id)
{
if(portInited[id]) {
fprintf(stderr, "port had initialed!!\n");
return s;
}
if (pthread_mutex_init(&s->txLock, NULL) != 0) {
fprintf(stderr, "TX mutex init failed - %d\n", errno);
// TODO: clean up & re-init
return NULL;
}
if (pthread_mutex_init(&s->rxLock, NULL) != 0) {
fprintf(stderr, "RX mutex init failed - %d\n", errno);
// TODO: clean up & re-init
return NULL;
}
tcpStart = true;
portInited[id] = true;
s->connected = false;
s->clientCount = 0;
s->id = id;
s->conn = NULL;
s->serv = dyad_newStream();
dyad_setNoDelay(s->serv, 1);
dyad_addListener(s->serv, DYAD_EVENT_ACCEPT, onAccept, s);
if(dyad_listenEx(s->serv, NULL, BASE_PORT + id + 1, 10) == 0) {
fprintf(stderr, "bind port %u for UART%u\n", (unsigned)BASE_PORT + id + 1, (unsigned)id+1);
} else {
fprintf(stderr, "bind port %u for UART%u failed!!\n", (unsigned)BASE_PORT + id + 1, (unsigned)id+1);
}
return s;
}
serialPort_t *uartOpen(USART_TypeDef *USARTx, serialReceiveCallbackPtr rxCallback, uint32_t baudRate, portMode_t mode, portOptions_t options)
{
uartPort_t *s = NULL;
#if defined(USE_UART1) || defined(USE_UART2) || defined(USE_UART3) || defined(USE_UART4) || defined(USE_UART5) || defined(USE_UART6) || defined(USE_UART7) || defined(USE_UART8)
uintptr_t id = ((uintptr_t)USARTx - 1);
s = tcpReconfigure(&tcpSerialPorts[id], id);
#else
return (serialPort_t *)s;
#endif
s->port.vTable = uartVTable;
// common serial initialisation code should move to serialPort::init()
s->port.rxBufferHead = s->port.rxBufferTail = 0;
s->port.txBufferHead = s->port.txBufferTail = 0;
s->port.rxBufferSize = RX_BUFFER_SIZE;
s->port.txBufferSize = TX_BUFFER_SIZE;
s->port.rxBuffer = s->rxBuffer;
s->port.txBuffer = s->txBuffer;
// callback works for IRQ-based RX ONLY
s->port.rxCallback = rxCallback;
s->port.mode = mode;
s->port.baudRate = baudRate;
s->port.options = options;
return (serialPort_t *)s;
}
uint32_t tcpTotalRxBytesWaiting(const serialPort_t *instance)
{
uartPort_t *s = (uartPort_t*)instance;
uint32_t count;
pthread_mutex_lock(&s->rxLock);
if (s->port.rxBufferHead >= s->port.rxBufferTail) {
count = s->port.rxBufferHead - s->port.rxBufferTail;
} else {
count = s->port.rxBufferSize + s->port.rxBufferHead - s->port.rxBufferTail;
}
pthread_mutex_unlock(&s->rxLock);
return count;
}
uint32_t tcpTotalTxBytesFree(const serialPort_t *instance)
{
uartPort_t *s = (uartPort_t*)instance;
uint32_t bytesUsed;
pthread_mutex_lock(&s->txLock);
if (s->port.txBufferHead >= s->port.txBufferTail) {
bytesUsed = s->port.txBufferHead - s->port.txBufferTail;
} else {
bytesUsed = s->port.txBufferSize + s->port.txBufferHead - s->port.txBufferTail;
}
bytesUsed = (s->port.txBufferSize - 1) - bytesUsed;
pthread_mutex_unlock(&s->txLock);
return bytesUsed;
}
bool isTcpTransmitBufferEmpty(const serialPort_t *instance)
{
uartPort_t *s = (uartPort_t *)instance;
pthread_mutex_lock(&s->txLock);
bool isEmpty = s->port.txBufferTail == s->port.txBufferHead;
pthread_mutex_unlock(&s->txLock);
return isEmpty;
}
uint8_t tcpRead(serialPort_t *instance)
{
uint8_t ch;
uartPort_t *s = (uartPort_t *)instance;
pthread_mutex_lock(&s->rxLock);
ch = s->port.rxBuffer[s->port.rxBufferTail];
if (s->port.rxBufferTail + 1 >= s->port.rxBufferSize) {
s->port.rxBufferTail = 0;
} else {
s->port.rxBufferTail++;
}
pthread_mutex_unlock(&s->rxLock);
return ch;
}
void tcpWrite(serialPort_t *instance, uint8_t ch)
{
uartPort_t *s = (uartPort_t *)instance;
pthread_mutex_lock(&s->txLock);
s->port.txBuffer[s->port.txBufferHead] = ch;
if (s->port.txBufferHead + 1 >= s->port.txBufferSize) {
s->port.txBufferHead = 0;
} else {
s->port.txBufferHead++;
}
pthread_mutex_unlock(&s->txLock);
tcpDataOut(s);
}
void tcpDataOut(uartPort_t *instance)
{
uint32_t bytesUsed;
uartPort_t *s = (uartPort_t *)instance;
if(s->conn == NULL) return;
pthread_mutex_lock(&s->txLock);
if (s->port.txBufferHead < s->port.txBufferTail) {
bytesUsed = s->port.txBufferSize + s->port.txBufferHead - s->port.txBufferTail;
dyad_write(s->conn, (const void *)(s->port.txBuffer + s->port.txBufferTail), s->port.txBufferSize - s->port.txBufferTail);
s->port.txBufferTail = 0;
}
bytesUsed = s->port.txBufferHead - s->port.txBufferTail;
dyad_write(s->conn, (const void *)(&(s->port.txBuffer[s->port.txBufferTail])), bytesUsed);
s->port.txBufferTail = s->port.txBufferHead;
pthread_mutex_unlock(&s->txLock);
}
void tcpDataIn(uartPort_t *instance, uint8_t* ch, int size)
{
uartPort_t *s = (uartPort_t *)instance;
pthread_mutex_lock(&s->rxLock);
while(size--) {
// printf("%c", *ch);
s->port.rxBuffer[s->port.rxBufferHead] = *(ch++);
if (s->port.rxBufferHead + 1 >= s->port.rxBufferSize) {
s->port.rxBufferHead = 0;
} else {
s->port.rxBufferHead++;
}
}
pthread_mutex_unlock(&s->rxLock);
// printf("\n");
}
const struct serialPortVTable uartVTable[] = {
{
.serialWrite = tcpWrite,
.serialTotalRxWaiting = tcpTotalRxBytesWaiting,
.serialTotalTxFree = tcpTotalTxBytesFree,
.serialRead = tcpRead,
.serialSetBaudRate = NULL,
.isSerialTransmitBufferEmpty = isTcpTransmitBufferEmpty,
.setMode = NULL,
.writeBuf = NULL,
.beginWrite = NULL,
.endWrite = NULL,
}
};