Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d36f6b4bee | |||
| 2679db4129 | |||
| 245d98f58e |
+122
-15
@@ -17,6 +17,8 @@ typedef struct {
|
|||||||
uint8_t rx_ring[TCP_CLIENT_RX_BUFFER_SIZE];
|
uint8_t rx_ring[TCP_CLIENT_RX_BUFFER_SIZE];
|
||||||
uint16_t rx_head;
|
uint16_t rx_head;
|
||||||
uint16_t rx_tail;
|
uint16_t rx_tail;
|
||||||
|
struct pbuf *hold_pbuf;
|
||||||
|
uint16_t hold_offset;
|
||||||
uint32_t next_retry_ms;
|
uint32_t next_retry_ms;
|
||||||
uint8_t index;
|
uint8_t index;
|
||||||
tcp_client_instance_config_t config;
|
tcp_client_instance_config_t config;
|
||||||
@@ -30,10 +32,65 @@ 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);
|
return (head >= tail) ? (uint16_t)(size - head + tail - 1u) : (uint16_t)(tail - head - 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint16_t ring_used(uint16_t head, uint16_t tail, uint16_t size)
|
||||||
|
{
|
||||||
|
return (head >= tail) ? (uint16_t)(head - tail) : (uint16_t)(size - tail + head);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcp_client_reset_rx_state(tcp_client_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
if (ctx == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx->hold_pbuf != NULL) {
|
||||||
|
pbuf_free(ctx->hold_pbuf);
|
||||||
|
ctx->hold_pbuf = NULL;
|
||||||
|
}
|
||||||
|
ctx->hold_offset = 0u;
|
||||||
|
ctx->rx_head = 0u;
|
||||||
|
ctx->rx_tail = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcp_client_fill_ring_from_pbuf(tcp_client_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
struct pbuf *q;
|
||||||
|
uint16_t offset;
|
||||||
|
|
||||||
|
if (ctx == NULL || ctx->hold_pbuf == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
q = ctx->hold_pbuf;
|
||||||
|
offset = ctx->hold_offset;
|
||||||
|
while (q != NULL && offset >= q->len) {
|
||||||
|
offset = (uint16_t)(offset - q->len);
|
||||||
|
q = q->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (q != NULL) {
|
||||||
|
const uint8_t *src = (const uint8_t *)q->payload;
|
||||||
|
for (uint16_t i = offset; i < q->len; ++i) {
|
||||||
|
if (ring_free(ctx->rx_head, ctx->rx_tail, TCP_CLIENT_RX_BUFFER_SIZE) == 0u) {
|
||||||
|
ctx->hold_offset = (uint16_t)(ctx->hold_offset + i - offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
ctx->hold_offset = (uint16_t)(ctx->hold_offset + q->len - offset);
|
||||||
|
offset = 0u;
|
||||||
|
q = q->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbuf_free(ctx->hold_pbuf);
|
||||||
|
ctx->hold_pbuf = NULL;
|
||||||
|
ctx->hold_offset = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
static err_t tcp_client_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
|
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;
|
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||||
struct pbuf *q;
|
|
||||||
|
|
||||||
if (ctx == NULL) {
|
if (ctx == NULL) {
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
@@ -59,21 +116,16 @@ static err_t tcp_client_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p,
|
|||||||
return ERR_ABRT;
|
return ERR_ABRT;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (q = p; q != NULL; q = q->next) {
|
if (ctx->hold_pbuf != NULL) {
|
||||||
const uint8_t *src = (const uint8_t *)q->payload;
|
ctx->status.errors++;
|
||||||
for (uint16_t i = 0; i < q->len; ++i) {
|
return ERR_MEM;
|
||||||
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_ref(p);
|
||||||
|
ctx->hold_pbuf = p;
|
||||||
|
ctx->hold_offset = 0u;
|
||||||
pbuf_free(p);
|
pbuf_free(p);
|
||||||
|
tcp_client_fill_ring_from_pbuf(ctx);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +145,7 @@ static void tcp_client_on_err(void *arg, err_t err)
|
|||||||
if (ctx == NULL) {
|
if (ctx == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
tcp_client_reset_rx_state(ctx);
|
||||||
ctx->pcb = NULL;
|
ctx->pcb = NULL;
|
||||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||||
ctx->status.errors++;
|
ctx->status.errors++;
|
||||||
@@ -213,6 +266,7 @@ int tcp_client_disconnect(uint8_t instance)
|
|||||||
}
|
}
|
||||||
ctx = &g_clients[instance];
|
ctx = &g_clients[instance];
|
||||||
if (ctx->pcb != NULL) {
|
if (ctx->pcb != NULL) {
|
||||||
|
tcp_client_reset_rx_state(ctx);
|
||||||
tcp_arg(ctx->pcb, NULL);
|
tcp_arg(ctx->pcb, NULL);
|
||||||
tcp_recv(ctx->pcb, NULL);
|
tcp_recv(ctx->pcb, NULL);
|
||||||
tcp_sent(ctx->pcb, NULL);
|
tcp_sent(ctx->pcb, NULL);
|
||||||
@@ -221,8 +275,7 @@ int tcp_client_disconnect(uint8_t instance)
|
|||||||
ctx->pcb = NULL;
|
ctx->pcb = NULL;
|
||||||
}
|
}
|
||||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||||
ctx->rx_head = 0u;
|
tcp_client_reset_rx_state(ctx);
|
||||||
ctx->rx_tail = 0u;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,13 +325,66 @@ int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ctx = &g_clients[instance];
|
ctx = &g_clients[instance];
|
||||||
|
tcp_client_fill_ring_from_pbuf(ctx);
|
||||||
while (copied < max_len && ctx->rx_tail != ctx->rx_head) {
|
while (copied < max_len && ctx->rx_tail != ctx->rx_head) {
|
||||||
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
||||||
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
if (copied > 0u && ctx->pcb != NULL) {
|
||||||
|
tcp_recved(ctx->pcb, copied);
|
||||||
|
}
|
||||||
return (int)copied;
|
return (int)copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t tcp_client_rx_available(uint8_t instance)
|
||||||
|
{
|
||||||
|
if (instance >= TCP_CLIENT_INSTANCE_COUNT) {
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
tcp_client_fill_ring_from_pbuf(&g_clients[instance]);
|
||||||
|
return ring_used(g_clients[instance].rx_head, g_clients[instance].rx_tail, TCP_CLIENT_RX_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t tcp_client_peek(uint8_t instance, uint8_t *data, uint16_t max_len)
|
||||||
|
{
|
||||||
|
uint16_t copied = 0u;
|
||||||
|
uint16_t tail;
|
||||||
|
tcp_client_ctx_t *ctx;
|
||||||
|
|
||||||
|
if (instance >= TCP_CLIENT_INSTANCE_COUNT || data == NULL || max_len == 0u) {
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = &g_clients[instance];
|
||||||
|
tcp_client_fill_ring_from_pbuf(ctx);
|
||||||
|
tail = ctx->rx_tail;
|
||||||
|
while (copied < max_len && tail != ctx->rx_head) {
|
||||||
|
data[copied++] = ctx->rx_ring[tail];
|
||||||
|
tail = (uint16_t)((tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
return copied;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tcp_client_drop(uint8_t instance, uint16_t len)
|
||||||
|
{
|
||||||
|
tcp_client_ctx_t *ctx;
|
||||||
|
uint16_t dropped = 0u;
|
||||||
|
|
||||||
|
if (instance >= TCP_CLIENT_INSTANCE_COUNT || len == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = &g_clients[instance];
|
||||||
|
while (dropped < len && ctx->rx_tail != ctx->rx_head) {
|
||||||
|
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
||||||
|
dropped++;
|
||||||
|
}
|
||||||
|
if (dropped > 0u && ctx->pcb != NULL) {
|
||||||
|
tcp_recved(ctx->pcb, dropped);
|
||||||
|
}
|
||||||
|
tcp_client_fill_ring_from_pbuf(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
bool tcp_client_is_connected(uint8_t instance)
|
bool tcp_client_is_connected(uint8_t instance)
|
||||||
{
|
{
|
||||||
return (instance < TCP_CLIENT_INSTANCE_COUNT) &&
|
return (instance < TCP_CLIENT_INSTANCE_COUNT) &&
|
||||||
@@ -299,6 +405,7 @@ void tcp_client_poll(void)
|
|||||||
|
|
||||||
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
||||||
tcp_client_ctx_t *ctx = &g_clients[i];
|
tcp_client_ctx_t *ctx = &g_clients[i];
|
||||||
|
tcp_client_fill_ring_from_pbuf(ctx);
|
||||||
if (!ctx->config.enabled || !ctx->config.auto_reconnect || tcp_client_is_connected(i)) {
|
if (!ctx->config.enabled || !ctx->config.auto_reconnect || tcp_client_is_connected(i)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -14,7 +14,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TCP_CLIENT_INSTANCE_COUNT 2u
|
#define TCP_CLIENT_INSTANCE_COUNT 2u
|
||||||
#define TCP_CLIENT_RX_BUFFER_SIZE 512u
|
#define TCP_CLIENT_RX_BUFFER_SIZE 480u
|
||||||
#define TCP_CLIENT_RECONNECT_DELAY_MS 3000u
|
#define TCP_CLIENT_RECONNECT_DELAY_MS 3000u
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -48,6 +48,9 @@ int tcp_client_connect(uint8_t instance);
|
|||||||
int tcp_client_disconnect(uint8_t instance);
|
int tcp_client_disconnect(uint8_t instance);
|
||||||
int tcp_client_send(uint8_t instance, const uint8_t *data, uint16_t len);
|
int tcp_client_send(uint8_t instance, const uint8_t *data, uint16_t len);
|
||||||
int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len);
|
int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len);
|
||||||
|
uint16_t tcp_client_rx_available(uint8_t instance);
|
||||||
|
uint16_t tcp_client_peek(uint8_t instance, uint8_t *data, uint16_t max_len);
|
||||||
|
void tcp_client_drop(uint8_t instance, uint16_t len);
|
||||||
bool tcp_client_is_connected(uint8_t instance);
|
bool tcp_client_is_connected(uint8_t instance);
|
||||||
void tcp_client_get_status(uint8_t instance, tcp_client_status_t *status);
|
void tcp_client_get_status(uint8_t instance, tcp_client_status_t *status);
|
||||||
void tcp_client_poll(void);
|
void tcp_client_poll(void);
|
||||||
|
|||||||
+128
-15
@@ -18,6 +18,8 @@ typedef struct {
|
|||||||
uint8_t rx_ring[TCP_SERVER_RX_BUFFER_SIZE];
|
uint8_t rx_ring[TCP_SERVER_RX_BUFFER_SIZE];
|
||||||
uint16_t rx_head;
|
uint16_t rx_head;
|
||||||
uint16_t rx_tail;
|
uint16_t rx_tail;
|
||||||
|
struct pbuf *hold_pbuf;
|
||||||
|
uint16_t hold_offset;
|
||||||
uint8_t index;
|
uint8_t index;
|
||||||
tcp_server_instance_config_t config;
|
tcp_server_instance_config_t config;
|
||||||
tcp_server_status_t status;
|
tcp_server_status_t status;
|
||||||
@@ -30,10 +32,65 @@ 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);
|
return (head >= tail) ? (uint16_t)(size - head + tail - 1u) : (uint16_t)(tail - head - 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint16_t ring_used(uint16_t head, uint16_t tail, uint16_t size)
|
||||||
|
{
|
||||||
|
return (head >= tail) ? (uint16_t)(head - tail) : (uint16_t)(size - tail + head);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcp_server_reset_rx_state(tcp_server_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
if (ctx == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx->hold_pbuf != NULL) {
|
||||||
|
pbuf_free(ctx->hold_pbuf);
|
||||||
|
ctx->hold_pbuf = NULL;
|
||||||
|
}
|
||||||
|
ctx->hold_offset = 0u;
|
||||||
|
ctx->rx_head = 0u;
|
||||||
|
ctx->rx_tail = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcp_server_fill_ring_from_pbuf(tcp_server_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
struct pbuf *q;
|
||||||
|
uint16_t offset;
|
||||||
|
|
||||||
|
if (ctx == NULL || ctx->hold_pbuf == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
q = ctx->hold_pbuf;
|
||||||
|
offset = ctx->hold_offset;
|
||||||
|
while (q != NULL && offset >= q->len) {
|
||||||
|
offset = (uint16_t)(offset - q->len);
|
||||||
|
q = q->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (q != NULL) {
|
||||||
|
const uint8_t *src = (const uint8_t *)q->payload;
|
||||||
|
for (uint16_t i = offset; i < q->len; ++i) {
|
||||||
|
if (ring_free(ctx->rx_head, ctx->rx_tail, TCP_SERVER_RX_BUFFER_SIZE) == 0u) {
|
||||||
|
ctx->hold_offset = (uint16_t)(ctx->hold_offset + i - offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
ctx->hold_offset = (uint16_t)(ctx->hold_offset + q->len - offset);
|
||||||
|
offset = 0u;
|
||||||
|
q = q->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbuf_free(ctx->hold_pbuf);
|
||||||
|
ctx->hold_pbuf = NULL;
|
||||||
|
ctx->hold_offset = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
static err_t tcp_server_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
|
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;
|
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
||||||
struct pbuf *q;
|
|
||||||
|
|
||||||
if (ctx == NULL) {
|
if (ctx == NULL) {
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
@@ -58,21 +115,16 @@ static err_t tcp_server_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p,
|
|||||||
return ERR_ABRT;
|
return ERR_ABRT;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (q = p; q != NULL; q = q->next) {
|
if (ctx->hold_pbuf != NULL) {
|
||||||
const uint8_t *src = (const uint8_t *)q->payload;
|
ctx->status.errors++;
|
||||||
for (uint16_t i = 0; i < q->len; ++i) {
|
return ERR_MEM;
|
||||||
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_ref(p);
|
||||||
|
ctx->hold_pbuf = p;
|
||||||
|
ctx->hold_offset = 0u;
|
||||||
pbuf_free(p);
|
pbuf_free(p);
|
||||||
|
tcp_server_fill_ring_from_pbuf(ctx);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,6 +144,7 @@ static void tcp_server_on_err(void *arg, err_t err)
|
|||||||
if (ctx == NULL) {
|
if (ctx == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
tcp_server_reset_rx_state(ctx);
|
||||||
ctx->client_pcb = NULL;
|
ctx->client_pcb = NULL;
|
||||||
ctx->status.state = ctx->config.enabled ? TCP_SERVER_STATE_LISTENING : TCP_SERVER_STATE_IDLE;
|
ctx->status.state = ctx->config.enabled ? TCP_SERVER_STATE_LISTENING : TCP_SERVER_STATE_IDLE;
|
||||||
ctx->status.errors++;
|
ctx->status.errors++;
|
||||||
@@ -193,6 +246,7 @@ int tcp_server_stop(uint8_t instance)
|
|||||||
ctx = &g_servers[instance];
|
ctx = &g_servers[instance];
|
||||||
|
|
||||||
if (ctx->client_pcb != NULL) {
|
if (ctx->client_pcb != NULL) {
|
||||||
|
tcp_server_reset_rx_state(ctx);
|
||||||
tcp_arg(ctx->client_pcb, NULL);
|
tcp_arg(ctx->client_pcb, NULL);
|
||||||
tcp_recv(ctx->client_pcb, NULL);
|
tcp_recv(ctx->client_pcb, NULL);
|
||||||
tcp_sent(ctx->client_pcb, NULL);
|
tcp_sent(ctx->client_pcb, NULL);
|
||||||
@@ -210,8 +264,7 @@ int tcp_server_stop(uint8_t instance)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx->status.state = TCP_SERVER_STATE_IDLE;
|
ctx->status.state = TCP_SERVER_STATE_IDLE;
|
||||||
ctx->rx_head = 0u;
|
tcp_server_reset_rx_state(ctx);
|
||||||
ctx->rx_tail = 0u;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,13 +315,66 @@ int tcp_server_recv(uint8_t instance, uint8_t *data, uint16_t max_len)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ctx = &g_servers[instance];
|
ctx = &g_servers[instance];
|
||||||
|
tcp_server_fill_ring_from_pbuf(ctx);
|
||||||
while (copied < max_len && ctx->rx_tail != ctx->rx_head) {
|
while (copied < max_len && ctx->rx_tail != ctx->rx_head) {
|
||||||
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
||||||
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_SERVER_RX_BUFFER_SIZE);
|
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_SERVER_RX_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
if (copied > 0u && ctx->client_pcb != NULL) {
|
||||||
|
tcp_recved(ctx->client_pcb, copied);
|
||||||
|
}
|
||||||
return (int)copied;
|
return (int)copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t tcp_server_rx_available(uint8_t instance)
|
||||||
|
{
|
||||||
|
if (instance >= TCP_SERVER_INSTANCE_COUNT) {
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
tcp_server_fill_ring_from_pbuf(&g_servers[instance]);
|
||||||
|
return ring_used(g_servers[instance].rx_head, g_servers[instance].rx_tail, TCP_SERVER_RX_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t tcp_server_peek(uint8_t instance, uint8_t *data, uint16_t max_len)
|
||||||
|
{
|
||||||
|
uint16_t copied = 0u;
|
||||||
|
uint16_t tail;
|
||||||
|
tcp_server_ctx_t *ctx;
|
||||||
|
|
||||||
|
if (instance >= TCP_SERVER_INSTANCE_COUNT || data == NULL || max_len == 0u) {
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = &g_servers[instance];
|
||||||
|
tcp_server_fill_ring_from_pbuf(ctx);
|
||||||
|
tail = ctx->rx_tail;
|
||||||
|
while (copied < max_len && tail != ctx->rx_head) {
|
||||||
|
data[copied++] = ctx->rx_ring[tail];
|
||||||
|
tail = (uint16_t)((tail + 1u) % TCP_SERVER_RX_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
return copied;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tcp_server_drop(uint8_t instance, uint16_t len)
|
||||||
|
{
|
||||||
|
tcp_server_ctx_t *ctx;
|
||||||
|
uint16_t dropped = 0u;
|
||||||
|
|
||||||
|
if (instance >= TCP_SERVER_INSTANCE_COUNT || len == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = &g_servers[instance];
|
||||||
|
while (dropped < len && ctx->rx_tail != ctx->rx_head) {
|
||||||
|
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_SERVER_RX_BUFFER_SIZE);
|
||||||
|
dropped++;
|
||||||
|
}
|
||||||
|
if (dropped > 0u && ctx->client_pcb != NULL) {
|
||||||
|
tcp_recved(ctx->client_pcb, dropped);
|
||||||
|
}
|
||||||
|
tcp_server_fill_ring_from_pbuf(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
bool tcp_server_is_connected(uint8_t instance)
|
bool tcp_server_is_connected(uint8_t instance)
|
||||||
{
|
{
|
||||||
return (instance < TCP_SERVER_INSTANCE_COUNT) && (g_servers[instance].client_pcb != NULL);
|
return (instance < TCP_SERVER_INSTANCE_COUNT) && (g_servers[instance].client_pcb != NULL);
|
||||||
@@ -280,3 +386,10 @@ void tcp_server_get_status(uint8_t instance, tcp_server_status_t *status)
|
|||||||
*status = g_servers[instance].status;
|
*status = g_servers[instance].status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tcp_server_poll(void)
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) {
|
||||||
|
tcp_server_fill_ring_from_pbuf(&g_servers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+5
-1
@@ -14,7 +14,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TCP_SERVER_INSTANCE_COUNT 2u
|
#define TCP_SERVER_INSTANCE_COUNT 2u
|
||||||
#define TCP_SERVER_RX_BUFFER_SIZE 512u
|
#define TCP_SERVER_RX_BUFFER_SIZE 480u
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TCP_SERVER_STATE_IDLE = 0,
|
TCP_SERVER_STATE_IDLE = 0,
|
||||||
@@ -42,8 +42,12 @@ int tcp_server_start(uint8_t instance);
|
|||||||
int tcp_server_stop(uint8_t instance);
|
int tcp_server_stop(uint8_t instance);
|
||||||
int tcp_server_send(uint8_t instance, const uint8_t *data, uint16_t len);
|
int tcp_server_send(uint8_t instance, const uint8_t *data, uint16_t len);
|
||||||
int tcp_server_recv(uint8_t instance, uint8_t *data, uint16_t max_len);
|
int tcp_server_recv(uint8_t instance, uint8_t *data, uint16_t max_len);
|
||||||
|
uint16_t tcp_server_rx_available(uint8_t instance);
|
||||||
|
uint16_t tcp_server_peek(uint8_t instance, uint8_t *data, uint16_t max_len);
|
||||||
|
void tcp_server_drop(uint8_t instance, uint16_t len);
|
||||||
bool tcp_server_is_connected(uint8_t instance);
|
bool tcp_server_is_connected(uint8_t instance);
|
||||||
void tcp_server_get_status(uint8_t instance, tcp_server_status_t *status);
|
void tcp_server_get_status(uint8_t instance, tcp_server_status_t *status);
|
||||||
|
void tcp_server_poll(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,6 +281,14 @@ uint16_t uart_trans_write(uart_channel_t channel, const uint8_t *data, uint16_t
|
|||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t uart_trans_tx_free(uart_channel_t channel)
|
||||||
|
{
|
||||||
|
if (channel >= UART_CHANNEL_MAX) {
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
return ring_free(g_channels[channel].tx_head, g_channels[channel].tx_tail, UART_TX_RING_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats)
|
void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats)
|
||||||
{
|
{
|
||||||
if (channel < UART_CHANNEL_MAX && stats != NULL) {
|
if (channel < UART_CHANNEL_MAX && stats != NULL) {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ void uart_trans_poll(void);
|
|||||||
uint16_t uart_trans_rx_available(uart_channel_t channel);
|
uint16_t uart_trans_rx_available(uart_channel_t channel);
|
||||||
uint16_t uart_trans_read(uart_channel_t channel, uint8_t *data, uint16_t max_len);
|
uint16_t uart_trans_read(uart_channel_t channel, uint8_t *data, uint16_t max_len);
|
||||||
uint16_t uart_trans_write(uart_channel_t channel, const uint8_t *data, uint16_t len);
|
uint16_t uart_trans_write(uart_channel_t channel, const uint8_t *data, uint16_t len);
|
||||||
|
uint16_t uart_trans_tx_free(uart_channel_t channel);
|
||||||
void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats);
|
void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats);
|
||||||
void uart_trans_reset_stats(uart_channel_t channel);
|
void uart_trans_reset_stats(uart_channel_t channel);
|
||||||
void uart_trans_idle_handler(uart_channel_t channel);
|
void uart_trans_idle_handler(uart_channel_t channel);
|
||||||
|
|||||||
+149
-21
@@ -35,10 +35,11 @@
|
|||||||
|
|
||||||
/* Private define ------------------------------------------------------------*/
|
/* Private define ------------------------------------------------------------*/
|
||||||
/* USER CODE BEGIN PD */
|
/* USER CODE BEGIN PD */
|
||||||
#define LED_PIN GPIO_PIN_13
|
#define LED_PIN GPIO_PIN_13
|
||||||
#define LED_PORT GPIOC
|
#define LED_PORT GPIOC
|
||||||
#define APP_ROUTE_BUFFER_SIZE 256u
|
#define APP_ROUTE_BUFFER_SIZE 256u
|
||||||
#define STACK_GUARD_WORD 0xA5A5A5A5u
|
#define APP_TCP_TO_UART_CHUNK_SIZE 128u
|
||||||
|
#define STACK_GUARD_WORD 0xA5A5A5A5u
|
||||||
#define APP_HEALTH_CHECK_INTERVAL_MS 5000u
|
#define APP_HEALTH_CHECK_INTERVAL_MS 5000u
|
||||||
/* USER CODE END PD */
|
/* USER CODE END PD */
|
||||||
|
|
||||||
@@ -67,6 +68,8 @@ static void App_RouteTcpTraffic(void);
|
|||||||
static void StackGuard_Init(void);
|
static void StackGuard_Init(void);
|
||||||
static void StackGuard_Check(void);
|
static void StackGuard_Check(void);
|
||||||
static bool App_SendToUart(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len);
|
static bool App_SendToUart(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len);
|
||||||
|
static uint16_t App_SendTcpPayloadToUartRaw(uint8_t uart_index, const uint8_t *data, uint16_t len);
|
||||||
|
static bool App_SendTcpPayloadToUartMux(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len);
|
||||||
static bool App_SendTcpServerPayload(uint8_t instance, const uint8_t *data, uint16_t len);
|
static bool App_SendTcpServerPayload(uint8_t instance, const uint8_t *data, uint16_t len);
|
||||||
static bool App_SendTcpClientPayload(uint8_t instance, const uint8_t *data, uint16_t len);
|
static bool App_SendTcpClientPayload(uint8_t instance, const uint8_t *data, uint16_t len);
|
||||||
/* USER CODE END PFP */
|
/* USER CODE END PFP */
|
||||||
@@ -296,35 +299,159 @@ static bool App_SendToUart(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint16_t App_SendTcpPayloadToUartRaw(uint8_t uart_index, const uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
uart_channel_t channel = (uart_index == LINK_UART_U1) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
|
||||||
|
return uart_trans_write(channel, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool App_SendTcpPayloadToUartMux(uint8_t uart_index, uint8_t src_id, uint8_t dst_mask, const uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
uart_channel_t channel = (uart_index == LINK_UART_U1) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
|
||||||
|
uint8_t frame[APP_TCP_TO_UART_CHUNK_SIZE + 6u];
|
||||||
|
uint16_t frame_len = 0u;
|
||||||
|
|
||||||
|
if (len == 0u || len > APP_TCP_TO_UART_CHUNK_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (uart_trans_tx_free(channel) < (uint16_t)(len + 6u)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!uart_mux_encode_frame(src_id, dst_mask, data, len, frame, &frame_len, sizeof(frame))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return uart_trans_write(channel, frame, frame_len) == frame_len;
|
||||||
|
}
|
||||||
|
|
||||||
static void App_RouteTcpTraffic(void)
|
static void App_RouteTcpTraffic(void)
|
||||||
{
|
{
|
||||||
const device_config_t *cfg = config_get();
|
const device_config_t *cfg = config_get();
|
||||||
uint8_t buffer[APP_ROUTE_BUFFER_SIZE];
|
uint8_t buffer[APP_TCP_TO_UART_CHUNK_SIZE];
|
||||||
|
|
||||||
for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) {
|
for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) {
|
||||||
int rc = tcp_server_recv(i, buffer, sizeof(buffer));
|
uint16_t available = tcp_server_rx_available(i);
|
||||||
if (rc > 0) {
|
if (available > 0u) {
|
||||||
uint8_t link_index = (i == 0u) ? CONFIG_LINK_S1 : CONFIG_LINK_S2;
|
uint8_t link_index = (i == 0u) ? CONFIG_LINK_S1 : CONFIG_LINK_S2;
|
||||||
if (!App_SendToUart(cfg->links[link_index].uart,
|
uint8_t uart_index = cfg->links[link_index].uart;
|
||||||
config_link_index_to_endpoint(link_index),
|
uint8_t src_id = config_link_index_to_endpoint(link_index);
|
||||||
config_uart_index_to_endpoint(cfg->links[link_index].uart),
|
uint8_t dst_mask = config_uart_index_to_endpoint(uart_index);
|
||||||
buffer,
|
uart_channel_t channel = (uart_index == LINK_UART_U1) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
|
||||||
(uint16_t)rc)) {
|
|
||||||
return;
|
if (cfg->mux_mode == MUX_MODE_FRAME) {
|
||||||
|
uint16_t tx_free = uart_trans_tx_free(channel);
|
||||||
|
uint16_t payload_len;
|
||||||
|
if (tx_free <= 6u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
payload_len = available;
|
||||||
|
if (payload_len > APP_TCP_TO_UART_CHUNK_SIZE) {
|
||||||
|
payload_len = APP_TCP_TO_UART_CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
if (payload_len > (uint16_t)(tx_free - 6u)) {
|
||||||
|
payload_len = (uint16_t)(tx_free - 6u);
|
||||||
|
}
|
||||||
|
if (payload_len == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
payload_len = tcp_server_peek(i, buffer, payload_len);
|
||||||
|
if (payload_len == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!App_SendTcpPayloadToUartMux(uart_index, src_id, dst_mask, buffer, payload_len)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tcp_server_drop(i, payload_len);
|
||||||
|
} else {
|
||||||
|
uint16_t chunk = available;
|
||||||
|
uint16_t tx_free = uart_trans_tx_free(channel);
|
||||||
|
uint16_t written;
|
||||||
|
if (tx_free == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (chunk > APP_TCP_TO_UART_CHUNK_SIZE) {
|
||||||
|
chunk = APP_TCP_TO_UART_CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
if (chunk > tx_free) {
|
||||||
|
chunk = tx_free;
|
||||||
|
}
|
||||||
|
if (chunk == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chunk = tcp_server_peek(i, buffer, chunk);
|
||||||
|
if (chunk == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
written = App_SendTcpPayloadToUartRaw(uart_index, buffer, chunk);
|
||||||
|
if (written > 0u) {
|
||||||
|
tcp_server_drop(i, written);
|
||||||
|
}
|
||||||
|
if (written < chunk) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
||||||
int rc = tcp_client_recv(i, buffer, sizeof(buffer));
|
uint16_t available = tcp_client_rx_available(i);
|
||||||
if (rc > 0) {
|
if (available > 0u) {
|
||||||
uint8_t link_index = (i == 0u) ? CONFIG_LINK_C1 : CONFIG_LINK_C2;
|
uint8_t link_index = (i == 0u) ? CONFIG_LINK_C1 : CONFIG_LINK_C2;
|
||||||
if (!App_SendToUart(cfg->links[link_index].uart,
|
uint8_t uart_index = cfg->links[link_index].uart;
|
||||||
config_link_index_to_endpoint(link_index),
|
uint8_t src_id = config_link_index_to_endpoint(link_index);
|
||||||
config_uart_index_to_endpoint(cfg->links[link_index].uart),
|
uint8_t dst_mask = config_uart_index_to_endpoint(uart_index);
|
||||||
buffer,
|
uart_channel_t channel = (uart_index == LINK_UART_U1) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
|
||||||
(uint16_t)rc)) {
|
|
||||||
return;
|
if (cfg->mux_mode == MUX_MODE_FRAME) {
|
||||||
|
uint16_t tx_free = uart_trans_tx_free(channel);
|
||||||
|
uint16_t payload_len;
|
||||||
|
if (tx_free <= 6u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
payload_len = available;
|
||||||
|
if (payload_len > APP_TCP_TO_UART_CHUNK_SIZE) {
|
||||||
|
payload_len = APP_TCP_TO_UART_CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
if (payload_len > (uint16_t)(tx_free - 6u)) {
|
||||||
|
payload_len = (uint16_t)(tx_free - 6u);
|
||||||
|
}
|
||||||
|
if (payload_len == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
payload_len = tcp_client_peek(i, buffer, payload_len);
|
||||||
|
if (payload_len == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!App_SendTcpPayloadToUartMux(uart_index, src_id, dst_mask, buffer, payload_len)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tcp_client_drop(i, payload_len);
|
||||||
|
} else {
|
||||||
|
uint16_t chunk = available;
|
||||||
|
uint16_t tx_free = uart_trans_tx_free(channel);
|
||||||
|
uint16_t written;
|
||||||
|
if (tx_free == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (chunk > APP_TCP_TO_UART_CHUNK_SIZE) {
|
||||||
|
chunk = APP_TCP_TO_UART_CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
if (chunk > tx_free) {
|
||||||
|
chunk = tx_free;
|
||||||
|
}
|
||||||
|
if (chunk == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chunk = tcp_client_peek(i, buffer, chunk);
|
||||||
|
if (chunk == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
written = App_SendTcpPayloadToUartRaw(uart_index, buffer, chunk);
|
||||||
|
if (written > 0u) {
|
||||||
|
tcp_client_drop(i, written);
|
||||||
|
}
|
||||||
|
if (written < chunk) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -510,6 +637,7 @@ static void App_Poll(void)
|
|||||||
sys_check_timeouts();
|
sys_check_timeouts();
|
||||||
App_StopLinksIfNeeded();
|
App_StopLinksIfNeeded();
|
||||||
App_StartLinksIfNeeded();
|
App_StartLinksIfNeeded();
|
||||||
|
tcp_server_poll();
|
||||||
tcp_client_poll();
|
tcp_client_poll();
|
||||||
uart_trans_poll();
|
uart_trans_poll();
|
||||||
StackGuard_Check();
|
StackGuard_Check();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
778 0 0 2 0 0 ip4.o
|
778 0 0 2 0 0 ip4.o
|
||||||
46 0 4 0 0 0 ip4_addr.o
|
46 0 4 0 0 0 ip4_addr.o
|
||||||
44 0 0 0 12 0 iwdg.o
|
44 0 0 0 12 0 iwdg.o
|
||||||
2842 0 185 12 272 0 main.o
|
3212 0 185 12 272 0 main.o
|
||||||
828 0 0 12 4115 0 mem.o
|
828 0 0 12 4115 0 mem.o
|
||||||
196 0 244 32 6464 0 memp.o
|
196 0 244 32 6464 0 memp.o
|
||||||
582 0 0 12 0 0 netif.o
|
582 0 0 12 0 0 netif.o
|
||||||
@@ -43,13 +43,13 @@
|
|||||||
490 0 0 0 0 0 stm32f1xx_it.o
|
490 0 0 0 0 0 stm32f1xx_it.o
|
||||||
2 0 24 4 0 0 system_stm32f1xx.o
|
2 0 24 4 0 0 system_stm32f1xx.o
|
||||||
3474 0 193 32 0 0 tcp.o
|
3474 0 193 32 0 0 tcp.o
|
||||||
1232 0 0 0 1120 0 tcp_client.o
|
1556 0 0 0 1072 0 tcp_client.o
|
||||||
3684 0 0 36 20 0 tcp_in.o
|
3684 0 0 36 20 0 tcp_in.o
|
||||||
3862 0 0 0 0 0 tcp_out.o
|
3862 0 0 0 0 0 tcp_out.o
|
||||||
986 0 0 0 1104 0 tcp_server.o
|
1346 0 0 0 1048 0 tcp_server.o
|
||||||
164 0 0 0 72 0 tim.o
|
164 0 0 0 72 0 tim.o
|
||||||
374 0 16 12 0 0 timeouts.o
|
374 0 16 12 0 0 timeouts.o
|
||||||
1538 0 0 0 2936 0 uart_trans.o
|
1590 0 0 0 2936 0 uart_trans.o
|
||||||
816 0 0 0 624 0 usart.o
|
816 0 0 0 624 0 usart.o
|
||||||
Object Totals
|
Object Totals
|
||||||
|
|
||||||
@@ -57,8 +57,8 @@ Memory Map of the image
|
|||||||
|
|
||||||
Load Region LR_IROM1
|
Load Region LR_IROM1
|
||||||
|
|
||||||
Execution Region ER_IROM1 (Exec base: 0x08000000, Size: 0x0000D72C, Max: 0x00010000, END)
|
Execution Region ER_IROM1 (Exec base: 0x08000000, Size: 0x0000DB7C, Max: 0x00010000, END)
|
||||||
|
|
||||||
Execution Region RW_IRAM1 (Exec base: 0x20000000, Size: 0x00005000, Max: 0x00005000, END)
|
Execution Region RW_IRAM1 (Exec base: 0x20000000, Size: 0x00004F98, Max: 0x00005000, END)
|
||||||
|
|
||||||
Image component sizes
|
Image component sizes
|
||||||
@@ -184,6 +184,68 @@ EN,LPORT,RIP,RPORT,UART
|
|||||||
2. 统一受 `LINK[idx]` 配置驱动
|
2. 统一受 `LINK[idx]` 配置驱动
|
||||||
3. 由调度层决定实例与 UART 的数据交换路径
|
3. 由调度层决定实例与 UART 的数据交换路径
|
||||||
|
|
||||||
|
### 6.4 `v1.1.0` 低 RAM TCP 背压修复
|
||||||
|
|
||||||
|
`v1.1.0` 起,`TCP -> UART` 路径补充如下实现约束,用于解决“TCP 接收过快、UART 发送过慢时本地缓存被冲垮”的问题,同时尽量不新增静态 RAM:
|
||||||
|
|
||||||
|
1. 继续复用 `tcp_server` / `tcp_client` 现有 `RX ring`,不为每个连接新增独立的大块 pending payload 缓冲。
|
||||||
|
2. `tcp_server_on_recv()` / `tcp_client_on_recv()` 不再在回调内立即 `tcp_recved()`。
|
||||||
|
3. lwIP 交来的 `pbuf` 在回调中通过 `pbuf_ref()` 转为应用持有,再释放回调上下文的原始引用;后续由应用在主循环中继续把数据泵入 `RX ring`,最终在消费完成后释放。
|
||||||
|
4. 当 `RX ring` 暂时装不下时,剩余数据保留在 `hold_pbuf + hold_offset` 中,等待主循环下一轮继续搬运。
|
||||||
|
5. 只有当数据真正从 `TCP RX ring` 被 `drop` 掉,也就是已经被下游 `UART` 接收进入发送路径时,才调用 `tcp_recved()` 释放 TCP 接收窗口。
|
||||||
|
|
||||||
|
这样做的效果是:
|
||||||
|
|
||||||
|
1. `UART` 慢时,TCP 窗口不会继续无条件放大。
|
||||||
|
2. 对端发送速度会被 lwIP 接收窗口自然压制。
|
||||||
|
3. 修复点建立在已有 ring 与主循环调度之上,不引入 `FreeRTOS` 或新的大块静态缓存。
|
||||||
|
|
||||||
|
#### RAW 与 MUX 的分流规则
|
||||||
|
|
||||||
|
在 `v1.1.0` 中,`TCP` 侧拿到的都是纯 payload,因此 `TCP` 背压逻辑在 `RAW` 与 `MUX` 两种模式下共用到 `UART commit` 之前:
|
||||||
|
|
||||||
|
1. `RAW` 模式:
|
||||||
|
- 主循环先查看 `uart_trans_tx_free()`
|
||||||
|
- 再按 `min(tcp_available, tx_free, APP_TCP_TO_UART_CHUNK_SIZE)` 从 TCP ring `peek`
|
||||||
|
- `uart_trans_write()` 实际写入多少,就 `drop + tcp_recved` 多少
|
||||||
|
2. `MUX` 模式:
|
||||||
|
- `TCP` payload 本身不带帧头尾
|
||||||
|
- 只有当 `UART TX free >= payload_len + 6` 时,才在栈上临时编码一帧并一次性写入 `UART TX ring`
|
||||||
|
- 只有整帧成功入队后,才按原始 payload 长度执行 `drop + tcp_recved`
|
||||||
|
|
||||||
|
该设计保证:
|
||||||
|
|
||||||
|
1. `RAW` 模式允许流式逐步提交
|
||||||
|
2. `MUX` 模式保持“单个 UART 输出帧必须完整入队”的语义
|
||||||
|
3. `TCP` 接收窗口始终以真实下游消费进度为准,而不是以“回调里已经 memcpy 到本地”作为提交点
|
||||||
|
|
||||||
|
#### RAM 与 chunk 策略
|
||||||
|
|
||||||
|
为给新增的 `hold_pbuf / hold_offset` 状态字段让位,并进一步降低单轮转发压力,`v1.1.0` 同步采用以下策略:
|
||||||
|
|
||||||
|
1. 新增 `APP_TCP_TO_UART_CHUNK_SIZE = 128`
|
||||||
|
2. `TCP_SERVER_RX_BUFFER_SIZE` 从 `512` 调整为 `480`
|
||||||
|
3. `TCP_CLIENT_RX_BUFFER_SIZE` 从 `512` 调整为 `480`
|
||||||
|
|
||||||
|
设计意图:
|
||||||
|
|
||||||
|
1. 利用更小的单次转发块提升主循环调度颗粒度
|
||||||
|
2. 让 `MUX` 模式下 `payload + 6` 更容易完整进入 `UART TX ring`
|
||||||
|
3. 在静态 RAM 已接近上限时,为少量新状态字段回收空间
|
||||||
|
|
||||||
|
#### 构建基线
|
||||||
|
|
||||||
|
`v1.1.0` 以 `MDK-ARM/TCP2UART.uvprojx` 的 `TCP2UART` Target 为构建验收基线。
|
||||||
|
|
||||||
|
当前一次通过的参考结果:
|
||||||
|
|
||||||
|
1. `errors = 0`
|
||||||
|
2. `warnings = 0`
|
||||||
|
3. `flash_bytes = 56544`
|
||||||
|
4. `ram_bytes = 20376`
|
||||||
|
|
||||||
|
该结果说明修复后工程仍满足 `STM32F103R8T6` 的 `20KB RAM` 上限,但余量已经很小;后续若继续增加功能,应优先考虑复用现有缓冲与状态,而不是增加新的静态大数组。
|
||||||
|
|
||||||
## 七、主循环实现方向
|
## 七、主循环实现方向
|
||||||
|
|
||||||
主循环仍保持裸机轮询风格:
|
主循环仍保持裸机轮询风格:
|
||||||
|
|||||||
Reference in New Issue
Block a user