/** * @file tcp_server.c * @brief Indexed lwIP RAW TCP server manager. */ #include "tcp_server.h" #include "../Drivers/LwIP/src/include/lwip/pbuf.h" #include "../Drivers/LwIP/src/include/lwip/tcp.h" #include "SEGGER_RTT.h" #include typedef struct { struct tcp_pcb *listen_pcb; struct tcp_pcb *client_pcb; uint8_t rx_ring[TCP_SERVER_RX_BUFFER_SIZE]; uint16_t rx_head; uint16_t rx_tail; uint8_t index; tcp_server_instance_config_t config; tcp_server_status_t status; } tcp_server_ctx_t; static tcp_server_ctx_t g_servers[TCP_SERVER_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_server_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { tcp_server_ctx_t *ctx = (tcp_server_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->client_pcb = NULL; ctx->status.state = ctx->config.enabled ? TCP_SERVER_STATE_LISTENING : TCP_SERVER_STATE_IDLE; 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_SERVER_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_SERVER_RX_BUFFER_SIZE); ctx->status.rx_bytes++; } } tcp_recved(pcb, p->tot_len); pbuf_free(p); return ERR_OK; } static err_t tcp_server_on_sent(void *arg, struct tcp_pcb *pcb, u16_t len) { tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg; (void)pcb; if (ctx != NULL) { ctx->status.tx_bytes += len; } return ERR_OK; } static void tcp_server_on_err(void *arg, err_t err) { tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg; if (ctx == NULL) { return; } ctx->client_pcb = NULL; ctx->status.state = ctx->config.enabled ? TCP_SERVER_STATE_LISTENING : TCP_SERVER_STATE_IDLE; ctx->status.errors++; SEGGER_RTT_printf(0, "TCP server[%u] connection error=%d\r\n", ctx->index, (int)err); } static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err) { tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg; if (ctx == NULL || err != ERR_OK) { return (ctx == NULL) ? ERR_ARG : err; } if (ctx->client_pcb != NULL) { tcp_abort(newpcb); return ERR_ABRT; } ctx->client_pcb = newpcb; ctx->status.state = TCP_SERVER_STATE_CONNECTED; ctx->status.connections++; tcp_nagle_disable(newpcb); tcp_arg(newpcb, ctx); tcp_recv(newpcb, tcp_server_on_recv); tcp_sent(newpcb, tcp_server_on_sent); tcp_err(newpcb, tcp_server_on_err); return ERR_OK; } int tcp_server_init_all(void) { memset(g_servers, 0, sizeof(g_servers)); for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) { g_servers[i].index = i; g_servers[i].status.state = TCP_SERVER_STATE_IDLE; } return 0; } int tcp_server_config(uint8_t instance, const tcp_server_instance_config_t *config) { if (instance >= TCP_SERVER_INSTANCE_COUNT || config == NULL) { return -1; } g_servers[instance].config = *config; return 0; } int tcp_server_start(uint8_t instance) { struct tcp_pcb *pcb; err_t err; tcp_server_ctx_t *ctx; if (instance >= TCP_SERVER_INSTANCE_COUNT) { return -1; } ctx = &g_servers[instance]; if (!ctx->config.enabled) { ctx->status.state = TCP_SERVER_STATE_IDLE; return 0; } if (ctx->listen_pcb != NULL) { return 0; } pcb = tcp_new_ip_type(IPADDR_TYPE_V4); if (pcb == NULL) { ctx->status.errors++; return -1; } err = tcp_bind(pcb, IP_ANY_TYPE, ctx->config.port); if (err != ERR_OK) { tcp_abort(pcb); ctx->status.errors++; return -1; } ctx->listen_pcb = tcp_listen_with_backlog(pcb, 1); if (ctx->listen_pcb == NULL) { ctx->status.errors++; return -1; } tcp_arg(ctx->listen_pcb, ctx); tcp_accept(ctx->listen_pcb, tcp_server_on_accept); ctx->status.state = TCP_SERVER_STATE_LISTENING; return 0; } int tcp_server_stop(uint8_t instance) { tcp_server_ctx_t *ctx; if (instance >= TCP_SERVER_INSTANCE_COUNT) { return -1; } ctx = &g_servers[instance]; if (ctx->client_pcb != NULL) { tcp_arg(ctx->client_pcb, NULL); tcp_recv(ctx->client_pcb, NULL); tcp_sent(ctx->client_pcb, NULL); tcp_err(ctx->client_pcb, NULL); tcp_abort(ctx->client_pcb); ctx->client_pcb = NULL; } if (ctx->listen_pcb != NULL) { tcp_arg(ctx->listen_pcb, NULL); tcp_accept(ctx->listen_pcb, NULL); if (tcp_close(ctx->listen_pcb) != ERR_OK) { tcp_abort(ctx->listen_pcb); } ctx->listen_pcb = NULL; } ctx->status.state = TCP_SERVER_STATE_IDLE; ctx->rx_head = 0u; ctx->rx_tail = 0u; return 0; } int tcp_server_send(uint8_t instance, const uint8_t *data, uint16_t len) { err_t err; tcp_server_ctx_t *ctx; if (instance >= TCP_SERVER_INSTANCE_COUNT || data == NULL || len == 0u) { return -1; } ctx = &g_servers[instance]; if (ctx->client_pcb == NULL) { return -1; } if (tcp_sndbuf(ctx->client_pcb) < len) { ctx->status.errors++; return 0; } err = tcp_write(ctx->client_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->client_pcb); if (err == ERR_MEM) { ctx->status.errors++; return 0; } if (err != ERR_OK) { ctx->status.errors++; return -1; } return (int)len; } int tcp_server_recv(uint8_t instance, uint8_t *data, uint16_t max_len) { uint16_t copied = 0u; tcp_server_ctx_t *ctx; if (instance >= TCP_SERVER_INSTANCE_COUNT || data == NULL || max_len == 0u) { return -1; } ctx = &g_servers[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_SERVER_RX_BUFFER_SIZE); } return (int)copied; } bool tcp_server_is_connected(uint8_t instance) { return (instance < TCP_SERVER_INSTANCE_COUNT) && (g_servers[instance].client_pcb != NULL); } void tcp_server_get_status(uint8_t instance, tcp_server_status_t *status) { if (instance < TCP_SERVER_INSTANCE_COUNT && status != NULL) { *status = g_servers[instance].status; } }