feat: save stable CH390 bridge baseline
This commit is contained in:
+120
-145
@@ -1,17 +1,14 @@
|
||||
/**
|
||||
* @file tcp_client.c
|
||||
* @brief lwIP RAW TCP client for the UART3 bridge.
|
||||
* @brief Indexed lwIP RAW TCP client manager.
|
||||
*/
|
||||
|
||||
#include "tcp_client.h"
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include "SEGGER_RTT.h"
|
||||
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "../Core/Inc/main.h"
|
||||
#include "../Drivers/LwIP/src/include/lwip/ip_addr.h"
|
||||
#include "../Drivers/LwIP/src/include/lwip/pbuf.h"
|
||||
#include "../Drivers/LwIP/src/include/lwip/tcp.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -21,11 +18,12 @@ typedef struct {
|
||||
uint16_t rx_head;
|
||||
uint16_t rx_tail;
|
||||
uint32_t next_retry_ms;
|
||||
tcp_client_config_t config;
|
||||
uint8_t index;
|
||||
tcp_client_instance_config_t config;
|
||||
tcp_client_status_t status;
|
||||
} tcp_client_ctx_t;
|
||||
|
||||
static tcp_client_ctx_t g_client;
|
||||
static tcp_client_ctx_t g_clients[TCP_CLIENT_INSTANCE_COUNT];
|
||||
|
||||
static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size)
|
||||
{
|
||||
@@ -37,13 +35,18 @@ static err_t tcp_client_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p,
|
||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||
struct pbuf *q;
|
||||
|
||||
if (ctx == NULL) {
|
||||
if (p != NULL) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
return ERR_ARG;
|
||||
}
|
||||
if (err != ERR_OK) {
|
||||
if (p != NULL) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
tcp_arg(pcb, NULL);
|
||||
tcp_recv(pcb, NULL);
|
||||
@@ -80,7 +83,9 @@ static err_t tcp_client_on_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
|
||||
{
|
||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||
(void)pcb;
|
||||
ctx->status.tx_bytes += len;
|
||||
if (ctx != NULL) {
|
||||
ctx->status.tx_bytes += len;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@@ -90,34 +95,30 @@ static void tcp_client_on_err(void *arg, err_t err)
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->pcb = NULL;
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
SEGGER_RTT_printf(0, "TCP client error=%d, reconnect scheduled\r\n", (int)err);
|
||||
(void)err;
|
||||
}
|
||||
|
||||
static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
|
||||
{
|
||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||
|
||||
if (ctx == NULL) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if (err != ERR_OK) {
|
||||
ctx->pcb = NULL;
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
SEGGER_RTT_printf(0, "TCP client connect callback failed err=%d\r\n", (int)err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ctx->pcb = pcb;
|
||||
ctx->status.state = TCP_CLIENT_STATE_CONNECTED;
|
||||
SEGGER_RTT_printf(0,
|
||||
"TCP client connected to %u.%u.%u.%u:%u\r\n",
|
||||
ctx->config.server_ip[0], ctx->config.server_ip[1],
|
||||
ctx->config.server_ip[2], ctx->config.server_ip[3],
|
||||
ctx->config.server_port);
|
||||
tcp_nagle_disable(pcb);
|
||||
tcp_arg(pcb, ctx);
|
||||
tcp_recv(pcb, tcp_client_on_recv);
|
||||
@@ -126,207 +127,181 @@ static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int tcp_client_init(const tcp_client_config_t *config)
|
||||
int tcp_client_init_all(void)
|
||||
{
|
||||
memset(&g_client, 0, sizeof(g_client));
|
||||
g_client.config.server_ip[0] = 192u;
|
||||
g_client.config.server_ip[1] = 168u;
|
||||
g_client.config.server_ip[2] = 31u;
|
||||
g_client.config.server_ip[3] = 1u;
|
||||
g_client.config.local_port = TCP_CLIENT_DEFAULT_PORT;
|
||||
g_client.config.server_port = TCP_CLIENT_DEFAULT_PORT;
|
||||
g_client.config.auto_reconnect = true;
|
||||
g_client.config.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS;
|
||||
g_client.status.state = TCP_CLIENT_STATE_IDLE;
|
||||
|
||||
if (config != NULL) {
|
||||
g_client.config = *config;
|
||||
memset(g_clients, 0, sizeof(g_clients));
|
||||
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
||||
g_clients[i].index = i;
|
||||
g_clients[i].status.state = TCP_CLIENT_STATE_IDLE;
|
||||
g_clients[i].config.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS;
|
||||
g_clients[i].config.auto_reconnect = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_client_connect(void)
|
||||
int tcp_client_config(uint8_t instance, const tcp_client_instance_config_t *config)
|
||||
{
|
||||
if (instance >= TCP_CLIENT_INSTANCE_COUNT || config == NULL) {
|
||||
return -1;
|
||||
}
|
||||
g_clients[instance].config = *config;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_client_connect(uint8_t instance)
|
||||
{
|
||||
struct tcp_pcb *pcb;
|
||||
ip_addr_t remote_addr;
|
||||
err_t err;
|
||||
tcp_client_ctx_t *ctx;
|
||||
|
||||
if (g_client.pcb != NULL) {
|
||||
if (instance >= TCP_CLIENT_INSTANCE_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
ctx = &g_clients[instance];
|
||||
if (!ctx->config.enabled) {
|
||||
return 0;
|
||||
}
|
||||
if (ctx->pcb != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
|
||||
if (pcb == NULL) {
|
||||
g_client.status.errors++;
|
||||
g_client.status.state = TCP_CLIENT_STATE_ERROR;
|
||||
SEGGER_RTT_WriteString(0, "TCP client connect failed: no PCB\r\n");
|
||||
ctx->status.errors++;
|
||||
ctx->status.state = TCP_CLIENT_STATE_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (g_client.config.local_port != 0u) {
|
||||
err = tcp_bind(pcb, IP_ANY_TYPE, g_client.config.local_port);
|
||||
if (ctx->config.local_port != 0u) {
|
||||
err = tcp_bind(pcb, IP_ANY_TYPE, ctx->config.local_port);
|
||||
if (err != ERR_OK) {
|
||||
tcp_abort(pcb);
|
||||
g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
g_client.status.errors++;
|
||||
g_client.next_retry_ms = HAL_GetTick() + g_client.config.reconnect_interval_ms;
|
||||
SEGGER_RTT_printf(0, "TCP client bind failed err=%d\r\n", (int)err);
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
IP_ADDR4(&remote_addr,
|
||||
g_client.config.server_ip[0],
|
||||
g_client.config.server_ip[1],
|
||||
g_client.config.server_ip[2],
|
||||
g_client.config.server_ip[3]);
|
||||
ctx->config.remote_ip[0],
|
||||
ctx->config.remote_ip[1],
|
||||
ctx->config.remote_ip[2],
|
||||
ctx->config.remote_ip[3]);
|
||||
|
||||
g_client.status.state = TCP_CLIENT_STATE_CONNECTING;
|
||||
tcp_arg(pcb, &g_client);
|
||||
ctx->status.state = TCP_CLIENT_STATE_CONNECTING;
|
||||
tcp_arg(pcb, ctx);
|
||||
tcp_err(pcb, tcp_client_on_err);
|
||||
err = tcp_connect(pcb, &remote_addr, g_client.config.server_port, tcp_client_on_connected);
|
||||
err = tcp_connect(pcb, &remote_addr, ctx->config.remote_port, tcp_client_on_connected);
|
||||
if (err != ERR_OK) {
|
||||
tcp_err(pcb, NULL);
|
||||
tcp_abort(pcb);
|
||||
g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
g_client.status.errors++;
|
||||
g_client.next_retry_ms = HAL_GetTick() + g_client.config.reconnect_interval_ms;
|
||||
SEGGER_RTT_printf(0, "TCP client connect start failed err=%d\r\n", (int)err);
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_client.pcb = pcb;
|
||||
SEGGER_RTT_printf(0,
|
||||
"TCP client connecting to %u.%u.%u.%u:%u\r\n",
|
||||
g_client.config.server_ip[0], g_client.config.server_ip[1],
|
||||
g_client.config.server_ip[2], g_client.config.server_ip[3],
|
||||
g_client.config.server_port);
|
||||
ctx->pcb = pcb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_client_disconnect(void)
|
||||
int tcp_client_disconnect(uint8_t instance)
|
||||
{
|
||||
if (g_client.pcb != NULL) {
|
||||
tcp_arg(g_client.pcb, NULL);
|
||||
tcp_recv(g_client.pcb, NULL);
|
||||
tcp_sent(g_client.pcb, NULL);
|
||||
tcp_err(g_client.pcb, NULL);
|
||||
tcp_abort(g_client.pcb);
|
||||
g_client.pcb = NULL;
|
||||
}
|
||||
tcp_client_ctx_t *ctx;
|
||||
|
||||
g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
SEGGER_RTT_WriteString(0, "TCP client disconnected\r\n");
|
||||
if (instance >= TCP_CLIENT_INSTANCE_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
ctx = &g_clients[instance];
|
||||
if (ctx->pcb != NULL) {
|
||||
tcp_arg(ctx->pcb, NULL);
|
||||
tcp_recv(ctx->pcb, NULL);
|
||||
tcp_sent(ctx->pcb, NULL);
|
||||
tcp_err(ctx->pcb, NULL);
|
||||
tcp_abort(ctx->pcb);
|
||||
ctx->pcb = NULL;
|
||||
}
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->rx_head = 0u;
|
||||
ctx->rx_tail = 0u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_client_send(const uint8_t *data, uint16_t len)
|
||||
int tcp_client_send(uint8_t instance, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
err_t err;
|
||||
tcp_client_ctx_t *ctx;
|
||||
|
||||
if (g_client.pcb == NULL || data == NULL || len == 0u) {
|
||||
if (instance >= TCP_CLIENT_INSTANCE_COUNT || data == NULL || len == 0u) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((g_client.pcb->flags & TF_RXCLOSED) != 0u) {
|
||||
g_client.status.errors++;
|
||||
ctx = &g_clients[instance];
|
||||
if (ctx->pcb == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tcp_sndbuf(g_client.pcb) < len) {
|
||||
if (tcp_sndbuf(ctx->pcb) < len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = tcp_write(g_client.pcb, data, len, TCP_WRITE_FLAG_COPY);
|
||||
err = tcp_write(ctx->pcb, data, len, TCP_WRITE_FLAG_COPY);
|
||||
if (err != ERR_OK) {
|
||||
g_client.status.errors++;
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = tcp_output(g_client.pcb);
|
||||
err = tcp_output(ctx->pcb);
|
||||
if (err != ERR_OK) {
|
||||
g_client.status.errors++;
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms)
|
||||
int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len)
|
||||
{
|
||||
uint16_t copied = 0u;
|
||||
(void)timeout_ms;
|
||||
tcp_client_ctx_t *ctx;
|
||||
|
||||
if (data == NULL || max_len == 0u) {
|
||||
if (instance >= TCP_CLIENT_INSTANCE_COUNT || data == NULL || max_len == 0u) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (copied < max_len && g_client.rx_tail != g_client.rx_head) {
|
||||
data[copied++] = g_client.rx_ring[g_client.rx_tail];
|
||||
g_client.rx_tail = (uint16_t)((g_client.rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
||||
ctx = &g_clients[instance];
|
||||
while (copied < max_len && ctx->rx_tail != ctx->rx_head) {
|
||||
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
||||
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
return (int)copied;
|
||||
}
|
||||
|
||||
bool tcp_client_is_connected(void)
|
||||
bool tcp_client_is_connected(uint8_t instance)
|
||||
{
|
||||
return (g_client.pcb != NULL) && (g_client.status.state == TCP_CLIENT_STATE_CONNECTED);
|
||||
return (instance < TCP_CLIENT_INSTANCE_COUNT) &&
|
||||
(g_clients[instance].pcb != NULL) &&
|
||||
(g_clients[instance].status.state == TCP_CLIENT_STATE_CONNECTED);
|
||||
}
|
||||
|
||||
int tcp_client_set_server(const uint8_t *ip, uint16_t port)
|
||||
void tcp_client_get_status(uint8_t instance, tcp_client_status_t *status)
|
||||
{
|
||||
if (ip == NULL || port == 0u) {
|
||||
return -1;
|
||||
if (instance < TCP_CLIENT_INSTANCE_COUNT && status != NULL) {
|
||||
*status = g_clients[instance].status;
|
||||
}
|
||||
|
||||
memcpy(g_client.config.server_ip, ip, 4u);
|
||||
g_client.config.server_port = port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tcp_client_get_status(tcp_client_status_t *status)
|
||||
{
|
||||
if (status != NULL) {
|
||||
*status = g_client.status;
|
||||
}
|
||||
}
|
||||
|
||||
void *tcp_client_get_rx_stream(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *tcp_client_get_tx_stream(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void tcp_client_task(void *argument)
|
||||
{
|
||||
(void)argument;
|
||||
}
|
||||
|
||||
void tcp_client_poll(void)
|
||||
{
|
||||
uint32_t now;
|
||||
uint32_t now = HAL_GetTick();
|
||||
|
||||
if (!g_client.config.auto_reconnect || tcp_client_is_connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((g_client.pcb != NULL) && (g_client.status.state == TCP_CLIENT_STATE_CONNECTING)) {
|
||||
return;
|
||||
}
|
||||
|
||||
now = HAL_GetTick();
|
||||
if (now >= g_client.next_retry_ms) {
|
||||
g_client.status.reconnect_count++;
|
||||
g_client.next_retry_ms = now + g_client.config.reconnect_interval_ms;
|
||||
SEGGER_RTT_printf(0,
|
||||
"TCP client reconnect attempt %lu\r\n",
|
||||
g_client.status.reconnect_count);
|
||||
(void)tcp_client_connect();
|
||||
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
||||
tcp_client_ctx_t *ctx = &g_clients[i];
|
||||
if (!ctx->config.enabled || !ctx->config.auto_reconnect || tcp_client_is_connected(i)) {
|
||||
continue;
|
||||
}
|
||||
if ((ctx->pcb != NULL) && (ctx->status.state == TCP_CLIENT_STATE_CONNECTING)) {
|
||||
continue;
|
||||
}
|
||||
if (now >= ctx->next_retry_ms) {
|
||||
ctx->status.reconnect_count++;
|
||||
ctx->next_retry_ms = now + ctx->config.reconnect_interval_ms;
|
||||
(void)tcp_client_connect(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user