Files
TCP2UART/App/tcp_client.c

315 lines
8.4 KiB
C

/**
* @file tcp_client.c
* @brief Indexed lwIP RAW TCP client manager.
*/
#include "tcp_client.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>
typedef struct {
struct tcp_pcb *pcb;
uint8_t rx_ring[TCP_CLIENT_RX_BUFFER_SIZE];
uint16_t rx_head;
uint16_t rx_tail;
uint32_t next_retry_ms;
uint8_t index;
tcp_client_instance_config_t config;
tcp_client_status_t status;
} tcp_client_ctx_t;
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)
{
return (head >= tail) ? (uint16_t)(size - head + tail - 1u) : (uint16_t)(tail - head - 1u);
}
static err_t tcp_client_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
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);
tcp_sent(pcb, NULL);
tcp_err(pcb, NULL);
tcp_abort(pcb);
ctx->pcb = NULL;
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
return ERR_ABRT;
}
for (q = p; q != NULL; q = q->next) {
const uint8_t *src = (const uint8_t *)q->payload;
for (uint16_t i = 0; i < q->len; ++i) {
if (ring_free(ctx->rx_head, ctx->rx_tail, TCP_CLIENT_RX_BUFFER_SIZE) == 0u) {
ctx->status.errors++;
break;
}
ctx->rx_ring[ctx->rx_head] = src[i];
ctx->rx_head = (uint16_t)((ctx->rx_head + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
ctx->status.rx_bytes++;
}
}
tcp_recved(pcb, p->tot_len);
pbuf_free(p);
return ERR_OK;
}
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;
if (ctx != NULL) {
ctx->status.tx_bytes += len;
}
return ERR_OK;
}
static void tcp_client_on_err(void *arg, err_t err)
{
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
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;
(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;
return err;
}
ctx->pcb = pcb;
ctx->status.state = TCP_CLIENT_STATE_CONNECTED;
tcp_nagle_disable(pcb);
tcp_arg(pcb, ctx);
tcp_recv(pcb, tcp_client_on_recv);
tcp_sent(pcb, tcp_client_on_sent);
tcp_err(pcb, tcp_client_on_err);
return ERR_OK;
}
int tcp_client_init_all(void)
{
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_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 (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) {
ctx->status.errors++;
ctx->status.state = TCP_CLIENT_STATE_ERROR;
return -1;
}
if (ctx->config.local_port != 0u) {
err = tcp_bind(pcb, IP_ANY_TYPE, ctx->config.local_port);
if (err != ERR_OK) {
tcp_abort(pcb);
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,
ctx->config.remote_ip[0],
ctx->config.remote_ip[1],
ctx->config.remote_ip[2],
ctx->config.remote_ip[3]);
ctx->status.state = TCP_CLIENT_STATE_CONNECTING;
tcp_arg(pcb, ctx);
tcp_err(pcb, tcp_client_on_err);
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);
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
ctx->status.errors++;
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
return -1;
}
ctx->pcb = pcb;
return 0;
}
int tcp_client_disconnect(uint8_t instance)
{
tcp_client_ctx_t *ctx;
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(uint8_t instance, const uint8_t *data, uint16_t len)
{
err_t err;
tcp_client_ctx_t *ctx;
if (instance >= TCP_CLIENT_INSTANCE_COUNT || data == NULL || len == 0u) {
return -1;
}
ctx = &g_clients[instance];
if (ctx->pcb == NULL) {
return -1;
}
if (tcp_sndbuf(ctx->pcb) < len) {
ctx->status.errors++;
return 0;
}
err = tcp_write(ctx->pcb, data, len, TCP_WRITE_FLAG_COPY);
if (err == ERR_MEM) {
ctx->status.errors++;
return 0;
}
if (err != ERR_OK) {
ctx->status.errors++;
return -1;
}
err = tcp_output(ctx->pcb);
if (err == ERR_MEM) {
ctx->status.errors++;
return 0;
}
if (err != ERR_OK) {
ctx->status.errors++;
return -1;
}
return (int)len;
}
int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len)
{
uint16_t copied = 0u;
tcp_client_ctx_t *ctx;
if (instance >= TCP_CLIENT_INSTANCE_COUNT || data == NULL || max_len == 0u) {
return -1;
}
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(uint8_t instance)
{
return (instance < TCP_CLIENT_INSTANCE_COUNT) &&
(g_clients[instance].pcb != NULL) &&
(g_clients[instance].status.state == TCP_CLIENT_STATE_CONNECTED);
}
void tcp_client_get_status(uint8_t instance, tcp_client_status_t *status)
{
if (instance < TCP_CLIENT_INSTANCE_COUNT && status != NULL) {
*status = g_clients[instance].status;
}
}
void tcp_client_poll(void)
{
uint32_t now = HAL_GetTick();
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);
}
}
}