Compare commits
7 Commits
fbe76bbdd5
...
v1.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ce1eed850 | |||
| 004057a6fa | |||
| e203db13ca | |||
| d36f6b4bee | |||
| 2679db4129 | |||
| 245d98f58e | |||
| 5567c7412d |
+170
-16
@@ -17,7 +17,10 @@ typedef struct {
|
||||
uint8_t rx_ring[TCP_CLIENT_RX_BUFFER_SIZE];
|
||||
uint16_t rx_head;
|
||||
uint16_t rx_tail;
|
||||
struct pbuf *hold_pbuf;
|
||||
uint16_t hold_offset;
|
||||
uint32_t next_retry_ms;
|
||||
uint32_t connect_start_ms;
|
||||
uint8_t index;
|
||||
tcp_client_instance_config_t config;
|
||||
tcp_client_status_t status;
|
||||
@@ -30,10 +33,95 @@ 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 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 bool tick_reached(uint32_t now, uint32_t deadline)
|
||||
{
|
||||
return ((int32_t)(now - deadline) >= 0);
|
||||
}
|
||||
|
||||
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_abort_connect_timeout(tcp_client_ctx_t *ctx, uint32_t now)
|
||||
{
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
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_client_reset_rx_state(ctx);
|
||||
tcp_abort(ctx->pcb);
|
||||
ctx->pcb = NULL;
|
||||
} else {
|
||||
tcp_client_reset_rx_state(ctx);
|
||||
}
|
||||
|
||||
ctx->connect_start_ms = 0u;
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->status.connect_timeout_count++;
|
||||
ctx->next_retry_ms = now + ctx->config.reconnect_interval_ms;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||
struct pbuf *q;
|
||||
|
||||
if (ctx == NULL) {
|
||||
if (p != NULL) {
|
||||
@@ -54,26 +142,22 @@ static err_t tcp_client_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p,
|
||||
tcp_err(pcb, NULL);
|
||||
tcp_abort(pcb);
|
||||
ctx->pcb = NULL;
|
||||
ctx->connect_start_ms = 0u;
|
||||
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++;
|
||||
}
|
||||
if (ctx->hold_pbuf != NULL) {
|
||||
ctx->status.errors++;
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
tcp_recved(pcb, p->tot_len);
|
||||
pbuf_ref(p);
|
||||
ctx->hold_pbuf = p;
|
||||
ctx->hold_offset = 0u;
|
||||
pbuf_free(p);
|
||||
tcp_client_fill_ring_from_pbuf(ctx);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@@ -93,7 +177,9 @@ static void tcp_client_on_err(void *arg, err_t err)
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
tcp_client_reset_rx_state(ctx);
|
||||
ctx->pcb = NULL;
|
||||
ctx->connect_start_ms = 0u;
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
@@ -109,6 +195,7 @@ static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
|
||||
}
|
||||
if (err != ERR_OK) {
|
||||
ctx->pcb = NULL;
|
||||
ctx->connect_start_ms = 0u;
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
@@ -116,6 +203,7 @@ static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
|
||||
}
|
||||
|
||||
ctx->pcb = pcb;
|
||||
ctx->connect_start_ms = 0u;
|
||||
ctx->status.state = TCP_CLIENT_STATE_CONNECTED;
|
||||
tcp_nagle_disable(pcb);
|
||||
tcp_arg(pcb, ctx);
|
||||
@@ -175,6 +263,7 @@ int tcp_client_connect(uint8_t instance)
|
||||
if (err != ERR_OK) {
|
||||
tcp_abort(pcb);
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->connect_start_ms = 0u;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
return -1;
|
||||
@@ -188,6 +277,7 @@ int tcp_client_connect(uint8_t instance)
|
||||
ctx->config.remote_ip[3]);
|
||||
|
||||
ctx->status.state = TCP_CLIENT_STATE_CONNECTING;
|
||||
ctx->connect_start_ms = HAL_GetTick();
|
||||
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);
|
||||
@@ -195,6 +285,7 @@ int tcp_client_connect(uint8_t instance)
|
||||
tcp_err(pcb, NULL);
|
||||
tcp_abort(pcb);
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->connect_start_ms = 0u;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
return -1;
|
||||
@@ -213,6 +304,7 @@ int tcp_client_disconnect(uint8_t instance)
|
||||
}
|
||||
ctx = &g_clients[instance];
|
||||
if (ctx->pcb != NULL) {
|
||||
tcp_client_reset_rx_state(ctx);
|
||||
tcp_arg(ctx->pcb, NULL);
|
||||
tcp_recv(ctx->pcb, NULL);
|
||||
tcp_sent(ctx->pcb, NULL);
|
||||
@@ -220,9 +312,9 @@ int tcp_client_disconnect(uint8_t instance)
|
||||
tcp_abort(ctx->pcb);
|
||||
ctx->pcb = NULL;
|
||||
}
|
||||
ctx->connect_start_ms = 0u;
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->rx_head = 0u;
|
||||
ctx->rx_tail = 0u;
|
||||
tcp_client_reset_rx_state(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -272,13 +364,71 @@ int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len)
|
||||
return -1;
|
||||
}
|
||||
ctx = &g_clients[instance];
|
||||
tcp_client_fill_ring_from_pbuf(ctx);
|
||||
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);
|
||||
}
|
||||
if (copied > 0u && ctx->pcb != NULL) {
|
||||
tcp_recved(ctx->pcb, 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 acked = 0u;
|
||||
|
||||
if (instance >= TCP_CLIENT_INSTANCE_COUNT || len == 0u) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = &g_clients[instance];
|
||||
while (acked < len) {
|
||||
tcp_client_fill_ring_from_pbuf(ctx);
|
||||
if (ctx->rx_tail == ctx->rx_head) {
|
||||
break;
|
||||
}
|
||||
while (acked < len && ctx->rx_tail != ctx->rx_head) {
|
||||
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
||||
acked++;
|
||||
}
|
||||
}
|
||||
if (acked > 0u && ctx->pcb != NULL) {
|
||||
tcp_recved(ctx->pcb, acked);
|
||||
}
|
||||
}
|
||||
|
||||
bool tcp_client_is_connected(uint8_t instance)
|
||||
{
|
||||
return (instance < TCP_CLIENT_INSTANCE_COUNT) &&
|
||||
@@ -299,13 +449,17 @@ void tcp_client_poll(void)
|
||||
|
||||
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++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)) {
|
||||
continue;
|
||||
}
|
||||
if ((ctx->pcb != NULL) && (ctx->status.state == TCP_CLIENT_STATE_CONNECTING)) {
|
||||
if ((uint32_t)(now - ctx->connect_start_ms) >= TCP_CLIENT_CONNECT_TIMEOUT_MS) {
|
||||
tcp_client_abort_connect_timeout(ctx, now);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (now >= ctx->next_retry_ms) {
|
||||
if (tick_reached(now, ctx->next_retry_ms)) {
|
||||
ctx->status.reconnect_count++;
|
||||
ctx->next_retry_ms = now + ctx->config.reconnect_interval_ms;
|
||||
(void)tcp_client_connect(i);
|
||||
|
||||
+6
-1
@@ -14,8 +14,9 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#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_CONNECT_TIMEOUT_MS 10000u
|
||||
|
||||
typedef enum {
|
||||
TCP_CLIENT_STATE_IDLE = 0,
|
||||
@@ -39,6 +40,7 @@ typedef struct {
|
||||
uint32_t rx_bytes;
|
||||
uint32_t tx_bytes;
|
||||
uint32_t reconnect_count;
|
||||
uint32_t connect_timeout_count;
|
||||
uint32_t errors;
|
||||
} tcp_client_status_t;
|
||||
|
||||
@@ -48,6 +50,9 @@ int tcp_client_connect(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_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);
|
||||
void tcp_client_get_status(uint8_t instance, tcp_client_status_t *status);
|
||||
void tcp_client_poll(void);
|
||||
|
||||
+128
-15
@@ -18,6 +18,8 @@ typedef struct {
|
||||
uint8_t rx_ring[TCP_SERVER_RX_BUFFER_SIZE];
|
||||
uint16_t rx_head;
|
||||
uint16_t rx_tail;
|
||||
struct pbuf *hold_pbuf;
|
||||
uint16_t hold_offset;
|
||||
uint8_t index;
|
||||
tcp_server_instance_config_t config;
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
||||
struct pbuf *q;
|
||||
|
||||
if (ctx == 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;
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
if (ctx->hold_pbuf != NULL) {
|
||||
ctx->status.errors++;
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
tcp_recved(pcb, p->tot_len);
|
||||
pbuf_ref(p);
|
||||
ctx->hold_pbuf = p;
|
||||
ctx->hold_offset = 0u;
|
||||
pbuf_free(p);
|
||||
tcp_server_fill_ring_from_pbuf(ctx);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@@ -92,6 +144,7 @@ static void tcp_server_on_err(void *arg, err_t err)
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
tcp_server_reset_rx_state(ctx);
|
||||
ctx->client_pcb = NULL;
|
||||
ctx->status.state = ctx->config.enabled ? TCP_SERVER_STATE_LISTENING : TCP_SERVER_STATE_IDLE;
|
||||
ctx->status.errors++;
|
||||
@@ -193,6 +246,7 @@ int tcp_server_stop(uint8_t instance)
|
||||
ctx = &g_servers[instance];
|
||||
|
||||
if (ctx->client_pcb != NULL) {
|
||||
tcp_server_reset_rx_state(ctx);
|
||||
tcp_arg(ctx->client_pcb, NULL);
|
||||
tcp_recv(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->rx_head = 0u;
|
||||
ctx->rx_tail = 0u;
|
||||
tcp_server_reset_rx_state(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -262,13 +315,66 @@ int tcp_server_recv(uint8_t instance, uint8_t *data, uint16_t max_len)
|
||||
return -1;
|
||||
}
|
||||
ctx = &g_servers[instance];
|
||||
tcp_server_fill_ring_from_pbuf(ctx);
|
||||
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);
|
||||
}
|
||||
if (copied > 0u && ctx->client_pcb != NULL) {
|
||||
tcp_recved(ctx->client_pcb, 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#define TCP_SERVER_INSTANCE_COUNT 2u
|
||||
#define TCP_SERVER_RX_BUFFER_SIZE 512u
|
||||
#define TCP_SERVER_RX_BUFFER_SIZE 480u
|
||||
|
||||
typedef enum {
|
||||
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_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);
|
||||
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);
|
||||
void tcp_server_get_status(uint8_t instance, tcp_server_status_t *status);
|
||||
void tcp_server_poll(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -281,6 +281,14 @@ uint16_t uart_trans_write(uart_channel_t channel, const uint8_t *data, uint16_t
|
||||
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)
|
||||
{
|
||||
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_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_tx_free(uart_channel_t channel);
|
||||
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_idle_handler(uart_channel_t channel);
|
||||
|
||||
+162
-21
@@ -35,10 +35,11 @@
|
||||
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PD */
|
||||
#define LED_PIN GPIO_PIN_13
|
||||
#define LED_PORT GPIOC
|
||||
#define APP_ROUTE_BUFFER_SIZE 256u
|
||||
#define STACK_GUARD_WORD 0xA5A5A5A5u
|
||||
#define LED_PIN GPIO_PIN_13
|
||||
#define LED_PORT GPIOC
|
||||
#define APP_ROUTE_BUFFER_SIZE 256u
|
||||
#define APP_TCP_TO_UART_CHUNK_SIZE 128u
|
||||
#define STACK_GUARD_WORD 0xA5A5A5A5u
|
||||
#define APP_HEALTH_CHECK_INTERVAL_MS 5000u
|
||||
/* USER CODE END PD */
|
||||
|
||||
@@ -67,6 +68,8 @@ static void App_RouteTcpTraffic(void);
|
||||
static void StackGuard_Init(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 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_SendTcpClientPayload(uint8_t instance, const uint8_t *data, uint16_t len);
|
||||
/* USER CODE END PFP */
|
||||
@@ -146,6 +149,19 @@ static void BootDiag_ReportCh390(void)
|
||||
cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3],
|
||||
cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3],
|
||||
cfg->mux_mode);
|
||||
SEGGER_RTT_printf(0,
|
||||
"ETH rx ok=%u drop=%u pbuf_fail=%u filt=%u ipv6=%u udp=%u igmp=%u lldp=%u other_eth=%u other_ipv4=%u last=0x%04X\r\n",
|
||||
(unsigned int)diag.rx_packets_ok,
|
||||
(unsigned int)diag.rx_packets_drop,
|
||||
(unsigned int)diag.rx_pbuf_alloc_failed,
|
||||
(unsigned int)diag.rx_filtered_frames,
|
||||
(unsigned int)diag.rx_filtered_ipv6,
|
||||
(unsigned int)diag.rx_filtered_udp,
|
||||
(unsigned int)diag.rx_filtered_igmp,
|
||||
(unsigned int)diag.rx_filtered_lldp,
|
||||
(unsigned int)diag.rx_filtered_other_eth,
|
||||
(unsigned int)diag.rx_filtered_other_ipv4,
|
||||
diag.last_eth_type);
|
||||
}
|
||||
|
||||
static void App_ConfigureLinks(const device_config_t *cfg)
|
||||
@@ -296,35 +312,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)
|
||||
{
|
||||
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) {
|
||||
int rc = tcp_server_recv(i, buffer, sizeof(buffer));
|
||||
if (rc > 0) {
|
||||
uint16_t available = tcp_server_rx_available(i);
|
||||
if (available > 0u) {
|
||||
uint8_t link_index = (i == 0u) ? CONFIG_LINK_S1 : CONFIG_LINK_S2;
|
||||
if (!App_SendToUart(cfg->links[link_index].uart,
|
||||
config_link_index_to_endpoint(link_index),
|
||||
config_uart_index_to_endpoint(cfg->links[link_index].uart),
|
||||
buffer,
|
||||
(uint16_t)rc)) {
|
||||
return;
|
||||
uint8_t uart_index = cfg->links[link_index].uart;
|
||||
uint8_t src_id = config_link_index_to_endpoint(link_index);
|
||||
uint8_t dst_mask = config_uart_index_to_endpoint(uart_index);
|
||||
uart_channel_t channel = (uart_index == LINK_UART_U1) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
|
||||
|
||||
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) {
|
||||
int rc = tcp_client_recv(i, buffer, sizeof(buffer));
|
||||
if (rc > 0) {
|
||||
uint16_t available = tcp_client_rx_available(i);
|
||||
if (available > 0u) {
|
||||
uint8_t link_index = (i == 0u) ? CONFIG_LINK_C1 : CONFIG_LINK_C2;
|
||||
if (!App_SendToUart(cfg->links[link_index].uart,
|
||||
config_link_index_to_endpoint(link_index),
|
||||
config_uart_index_to_endpoint(cfg->links[link_index].uart),
|
||||
buffer,
|
||||
(uint16_t)rc)) {
|
||||
return;
|
||||
uint8_t uart_index = cfg->links[link_index].uart;
|
||||
uint8_t src_id = config_link_index_to_endpoint(link_index);
|
||||
uint8_t dst_mask = config_uart_index_to_endpoint(uart_index);
|
||||
uart_channel_t channel = (uart_index == LINK_UART_U1) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
|
||||
|
||||
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 +650,7 @@ static void App_Poll(void)
|
||||
sys_check_timeouts();
|
||||
App_StopLinksIfNeeded();
|
||||
App_StartLinksIfNeeded();
|
||||
tcp_server_poll();
|
||||
tcp_client_poll();
|
||||
uart_trans_poll();
|
||||
StackGuard_Check();
|
||||
|
||||
@@ -22,11 +22,18 @@ static uint8_t ch390_runtime_drain_rx(struct netif *netif, uint8_t max_frames)
|
||||
{
|
||||
struct pbuf *p;
|
||||
uint8_t drained = 0u;
|
||||
uint8_t rx_ready;
|
||||
|
||||
while (drained < max_frames) {
|
||||
p = ch390_runtime_input_frame(netif);
|
||||
if (p == NULL) {
|
||||
break;
|
||||
ch390_read_reg(CH390_MRCMDX);
|
||||
rx_ready = ch390_read_reg(CH390_MRCMDX);
|
||||
if ((rx_ready & CH390_PKT_RDY) == 0u) {
|
||||
break;
|
||||
}
|
||||
drained++;
|
||||
continue;
|
||||
}
|
||||
ch390_runtime_dispatch_frame(netif, p);
|
||||
drained++;
|
||||
@@ -49,7 +56,179 @@ static uint8_t g_link_restart_pending;
|
||||
#define HEALTH_FAIL_SHIFT 4u
|
||||
#define HEALTH_FAIL_MASK 0xF0u
|
||||
|
||||
#define CH390_RX_FILTER_ENABLE 1u
|
||||
#define CH390_RX_PREFIX_LEN 38u
|
||||
#define CH390_ETH_HEADER_LEN 14u
|
||||
#define CH390_IPV4_MIN_HEADER_LEN 20u
|
||||
#define CH390_ETH_TYPE_IPV4 0x0800u
|
||||
#define CH390_ETH_TYPE_ARP 0x0806u
|
||||
#define CH390_ETH_TYPE_VLAN 0x8100u
|
||||
#define CH390_ETH_TYPE_IPV6 0x86DDu
|
||||
#define CH390_ETH_TYPE_QINQ 0x88A8u
|
||||
#define CH390_ETH_TYPE_LLDP 0x88CCu
|
||||
#define CH390_IP_PROTO_ICMP 1u
|
||||
#define CH390_IP_PROTO_IGMP 2u
|
||||
#define CH390_IP_PROTO_TCP 6u
|
||||
#define CH390_IP_PROTO_UDP 17u
|
||||
|
||||
typedef enum {
|
||||
CH390_RX_ACCEPT = 0,
|
||||
CH390_RX_DROP_MALFORMED,
|
||||
CH390_RX_DROP_IPV6,
|
||||
CH390_RX_DROP_LLDP,
|
||||
CH390_RX_DROP_UDP,
|
||||
CH390_RX_DROP_IGMP,
|
||||
CH390_RX_DROP_OTHER_ETH,
|
||||
CH390_RX_DROP_OTHER_IPV4
|
||||
} ch390_rx_filter_result_t;
|
||||
|
||||
static bool ch390_mac_address_valid(const uint8_t *mac);
|
||||
static ch390_rx_filter_result_t ch390_runtime_filter_frame(const uint8_t *prefix, uint16_t frame_len, uint16_t prefix_len);
|
||||
static void ch390_runtime_count_filtered_frame(ch390_rx_filter_result_t result);
|
||||
static void ch390_runtime_copy_prefix_to_pbuf(struct pbuf *p, const uint8_t *prefix, uint16_t prefix_len);
|
||||
static void ch390_runtime_read_remaining_to_pbuf(struct pbuf *p, uint16_t offset);
|
||||
|
||||
static ch390_rx_filter_result_t ch390_runtime_filter_frame(const uint8_t *prefix, uint16_t frame_len, uint16_t prefix_len)
|
||||
{
|
||||
uint16_t eth_type;
|
||||
uint16_t l2_header_len = CH390_ETH_HEADER_LEN;
|
||||
uint16_t ip_offset;
|
||||
uint8_t ip_version;
|
||||
uint8_t ip_ihl;
|
||||
uint8_t ip_proto;
|
||||
|
||||
if ((prefix == NULL) || (frame_len < CH390_ETH_HEADER_LEN) || (prefix_len < CH390_ETH_HEADER_LEN)) {
|
||||
return CH390_RX_DROP_MALFORMED;
|
||||
}
|
||||
|
||||
eth_type = (uint16_t)(((uint16_t)prefix[12] << 8) | prefix[13]);
|
||||
g_diag.last_eth_type = eth_type;
|
||||
|
||||
if ((eth_type == CH390_ETH_TYPE_VLAN) || (eth_type == CH390_ETH_TYPE_QINQ)) {
|
||||
if ((frame_len < (CH390_ETH_HEADER_LEN + 4u)) || (prefix_len < (CH390_ETH_HEADER_LEN + 4u))) {
|
||||
return CH390_RX_DROP_MALFORMED;
|
||||
}
|
||||
l2_header_len = (uint16_t)(CH390_ETH_HEADER_LEN + 4u);
|
||||
eth_type = (uint16_t)(((uint16_t)prefix[16] << 8) | prefix[17]);
|
||||
g_diag.last_eth_type = eth_type;
|
||||
}
|
||||
|
||||
if (eth_type == CH390_ETH_TYPE_ARP) {
|
||||
g_diag.rx_arp_frames++;
|
||||
return CH390_RX_ACCEPT;
|
||||
}
|
||||
|
||||
if (eth_type == CH390_ETH_TYPE_IPV6) {
|
||||
return CH390_RX_DROP_IPV6;
|
||||
}
|
||||
|
||||
if (eth_type == CH390_ETH_TYPE_LLDP) {
|
||||
return CH390_RX_DROP_LLDP;
|
||||
}
|
||||
|
||||
if (eth_type != CH390_ETH_TYPE_IPV4) {
|
||||
return CH390_RX_DROP_OTHER_ETH;
|
||||
}
|
||||
|
||||
ip_offset = l2_header_len;
|
||||
if ((frame_len < (uint16_t)(ip_offset + CH390_IPV4_MIN_HEADER_LEN)) ||
|
||||
(prefix_len < (uint16_t)(ip_offset + CH390_IPV4_MIN_HEADER_LEN))) {
|
||||
return CH390_RX_DROP_MALFORMED;
|
||||
}
|
||||
|
||||
ip_version = (uint8_t)(prefix[ip_offset] >> 4);
|
||||
ip_ihl = (uint8_t)(prefix[ip_offset] & 0x0Fu);
|
||||
if ((ip_version != 4u) || (ip_ihl < 5u)) {
|
||||
return CH390_RX_DROP_MALFORMED;
|
||||
}
|
||||
|
||||
ip_proto = prefix[ip_offset + 9u];
|
||||
g_diag.rx_ip_frames++;
|
||||
|
||||
if (ip_proto == CH390_IP_PROTO_ICMP) {
|
||||
g_diag.rx_ipv4_icmp_frames++;
|
||||
return CH390_RX_ACCEPT;
|
||||
}
|
||||
|
||||
if (ip_proto == CH390_IP_PROTO_TCP) {
|
||||
g_diag.rx_ipv4_tcp_frames++;
|
||||
return CH390_RX_ACCEPT;
|
||||
}
|
||||
|
||||
if (ip_proto == CH390_IP_PROTO_UDP) {
|
||||
g_diag.rx_ipv4_udp_frames++;
|
||||
return CH390_RX_DROP_UDP;
|
||||
}
|
||||
|
||||
if (ip_proto == CH390_IP_PROTO_IGMP) {
|
||||
g_diag.rx_filtered_igmp++;
|
||||
return CH390_RX_DROP_IGMP;
|
||||
}
|
||||
|
||||
return CH390_RX_DROP_OTHER_IPV4;
|
||||
}
|
||||
|
||||
static void ch390_runtime_count_filtered_frame(ch390_rx_filter_result_t result)
|
||||
{
|
||||
g_diag.rx_filtered_frames++;
|
||||
switch (result) {
|
||||
case CH390_RX_DROP_MALFORMED:
|
||||
g_diag.rx_filtered_malformed++;
|
||||
break;
|
||||
case CH390_RX_DROP_IPV6:
|
||||
g_diag.rx_filtered_ipv6++;
|
||||
break;
|
||||
case CH390_RX_DROP_LLDP:
|
||||
g_diag.rx_filtered_lldp++;
|
||||
break;
|
||||
case CH390_RX_DROP_UDP:
|
||||
g_diag.rx_filtered_udp++;
|
||||
break;
|
||||
case CH390_RX_DROP_IGMP:
|
||||
break;
|
||||
case CH390_RX_DROP_OTHER_ETH:
|
||||
g_diag.rx_filtered_other_eth++;
|
||||
break;
|
||||
case CH390_RX_DROP_OTHER_IPV4:
|
||||
g_diag.rx_filtered_other_ipv4++;
|
||||
break;
|
||||
case CH390_RX_ACCEPT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ch390_runtime_copy_prefix_to_pbuf(struct pbuf *p, const uint8_t *prefix, uint16_t prefix_len)
|
||||
{
|
||||
struct pbuf *q;
|
||||
uint16_t copied = 0u;
|
||||
|
||||
for (q = p; (q != NULL) && (copied < prefix_len); q = q->next) {
|
||||
uint16_t chunk = (uint16_t)(prefix_len - copied);
|
||||
if (chunk > q->len) {
|
||||
chunk = q->len;
|
||||
}
|
||||
memcpy(q->payload, &prefix[copied], chunk);
|
||||
copied = (uint16_t)(copied + chunk);
|
||||
}
|
||||
}
|
||||
|
||||
static void ch390_runtime_read_remaining_to_pbuf(struct pbuf *p, uint16_t offset)
|
||||
{
|
||||
struct pbuf *q;
|
||||
uint16_t skipped = 0u;
|
||||
|
||||
for (q = p; q != NULL; q = q->next) {
|
||||
if (skipped >= offset) {
|
||||
ch390_read_mem((uint8_t *)q->payload, q->len);
|
||||
} else if ((uint16_t)(skipped + q->len) > offset) {
|
||||
uint16_t in_chunk_offset = (uint16_t)(offset - skipped);
|
||||
uint16_t read_len = (uint16_t)(q->len - in_chunk_offset);
|
||||
ch390_read_mem(&((uint8_t *)q->payload)[in_chunk_offset], read_len);
|
||||
}
|
||||
skipped = (uint16_t)(skipped + q->len);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t ch390_runtime_is_restart_pending(void)
|
||||
{
|
||||
@@ -154,12 +333,14 @@ struct pbuf *ch390_runtime_input_frame(struct netif *netif)
|
||||
{
|
||||
struct ethernetif *ethernetif = (struct ethernetif *)netif->state;
|
||||
struct pbuf *p = NULL;
|
||||
struct pbuf *q;
|
||||
uint16_t len;
|
||||
uint16_t frame_len;
|
||||
uint16_t prefix_len;
|
||||
uint8_t rcr;
|
||||
uint8_t rx_ready;
|
||||
uint8_t rx_header[4];
|
||||
uint8_t frame_prefix[CH390_RX_PREFIX_LEN];
|
||||
ch390_rx_filter_result_t filter_result;
|
||||
ch390_read_reg(CH390_MRCMDX);
|
||||
rx_ready = ch390_read_reg(CH390_MRCMDX);
|
||||
|
||||
@@ -196,6 +377,23 @@ struct pbuf *ch390_runtime_input_frame(struct netif *netif)
|
||||
}
|
||||
|
||||
ethernetif->rx_len = frame_len;
|
||||
prefix_len = frame_len;
|
||||
if (prefix_len > CH390_RX_PREFIX_LEN) {
|
||||
prefix_len = CH390_RX_PREFIX_LEN;
|
||||
}
|
||||
ch390_read_mem(frame_prefix, prefix_len);
|
||||
|
||||
#if CH390_RX_FILTER_ENABLE
|
||||
filter_result = ch390_runtime_filter_frame(frame_prefix, frame_len, prefix_len);
|
||||
if (filter_result != CH390_RX_ACCEPT) {
|
||||
ch390_drop_packet((uint16_t)(frame_len - prefix_len));
|
||||
LINK_STATS_INC(link.drop);
|
||||
g_diag.rx_packets_drop++;
|
||||
ch390_runtime_count_filtered_frame(filter_result);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
len = ethernetif->rx_len;
|
||||
#if ETH_PAD_SIZE
|
||||
len += ETH_PAD_SIZE;
|
||||
@@ -206,9 +404,8 @@ struct pbuf *ch390_runtime_input_frame(struct netif *netif)
|
||||
#if ETH_PAD_SIZE
|
||||
pbuf_remove_header(p, ETH_PAD_SIZE);
|
||||
#endif
|
||||
for (q = p; q != NULL; q = q->next) {
|
||||
ch390_read_mem((uint8_t *)q->payload, q->len);
|
||||
}
|
||||
ch390_runtime_copy_prefix_to_pbuf(p, frame_prefix, prefix_len);
|
||||
ch390_runtime_read_remaining_to_pbuf(p, prefix_len);
|
||||
#if ETH_PAD_SIZE
|
||||
pbuf_add_header(p, ETH_PAD_SIZE);
|
||||
#endif
|
||||
@@ -218,10 +415,11 @@ struct pbuf *ch390_runtime_input_frame(struct netif *netif)
|
||||
g_diag.last_frame_len = frame_len;
|
||||
g_diag.last_payload_len = p->tot_len;
|
||||
} else {
|
||||
ch390_drop_packet(ethernetif->rx_len);
|
||||
ch390_drop_packet((uint16_t)(frame_len - prefix_len));
|
||||
LINK_STATS_INC(link.memerr);
|
||||
LINK_STATS_INC(link.drop);
|
||||
g_diag.rx_packets_drop++;
|
||||
g_diag.rx_pbuf_alloc_failed++;
|
||||
}
|
||||
|
||||
return p;
|
||||
|
||||
@@ -38,8 +38,20 @@ typedef struct {
|
||||
uint32_t rx_packets_drop;
|
||||
uint32_t tx_packets_ok;
|
||||
uint32_t tx_packets_timeout;
|
||||
uint32_t rx_pbuf_alloc_failed;
|
||||
uint32_t rx_filtered_frames;
|
||||
uint32_t rx_filtered_ipv6;
|
||||
uint32_t rx_filtered_udp;
|
||||
uint32_t rx_filtered_igmp;
|
||||
uint32_t rx_filtered_lldp;
|
||||
uint32_t rx_filtered_other_eth;
|
||||
uint32_t rx_filtered_other_ipv4;
|
||||
uint32_t rx_filtered_malformed;
|
||||
uint32_t rx_arp_frames;
|
||||
uint32_t rx_ip_frames;
|
||||
uint32_t rx_ipv4_icmp_frames;
|
||||
uint32_t rx_ipv4_tcp_frames;
|
||||
uint32_t rx_ipv4_udp_frames;
|
||||
uint32_t rx_other_frames;
|
||||
uint32_t rx_unicast_self_frames;
|
||||
uint32_t rx_broadcast_frames;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Code (inc. data) RO Data RW Data ZI Data Debug Object Name
|
||||
632 0 0 0 0 0 ch390.o
|
||||
616 0 64 0 0 0 ch390_interface.o
|
||||
2050 0 85 6 88 0 ch390_runtime.o
|
||||
2546 0 85 6 136 0 ch390_runtime.o
|
||||
3958 0 591 8 1240 0 config.o
|
||||
8 0 0 0 0 0 def.o
|
||||
124 0 0 0 0 0 dma.o
|
||||
@@ -17,7 +17,7 @@
|
||||
778 0 0 2 0 0 ip4.o
|
||||
46 0 4 0 0 0 ip4_addr.o
|
||||
44 0 0 0 12 0 iwdg.o
|
||||
2842 0 185 12 272 0 main.o
|
||||
3264 0 300 12 272 0 main.o
|
||||
828 0 0 12 4115 0 mem.o
|
||||
196 0 244 32 6464 0 memp.o
|
||||
582 0 0 12 0 0 netif.o
|
||||
@@ -43,13 +43,13 @@
|
||||
490 0 0 0 0 0 stm32f1xx_it.o
|
||||
2 0 24 4 0 0 system_stm32f1xx.o
|
||||
3474 0 193 32 0 0 tcp.o
|
||||
1232 0 0 0 1120 0 tcp_client.o
|
||||
1734 0 0 0 1088 0 tcp_client.o
|
||||
3684 0 0 36 20 0 tcp_in.o
|
||||
3862 0 0 0 0 0 tcp_out.o
|
||||
986 0 0 0 1104 0 tcp_server.o
|
||||
1364 0 0 0 1048 0 tcp_server.o
|
||||
164 0 0 0 72 0 tim.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
|
||||
Object Totals
|
||||
|
||||
@@ -57,8 +57,8 @@ Memory Map of the image
|
||||
|
||||
Load Region LR_IROM1
|
||||
|
||||
Execution Region ER_IROM1 (Exec base: 0x08000000, Size: 0x0000D72C, Max: 0x00010000, END)
|
||||
Execution Region ER_IROM1 (Exec base: 0x08000000, Size: 0x0000DED8, Max: 0x00010000, END)
|
||||
|
||||
Execution Region RW_IRAM1 (Exec base: 0x20000000, Size: 0x00005000, Max: 0x00005000, END)
|
||||
Execution Region RW_IRAM1 (Exec base: 0x20000000, Size: 0x00004FD8, Max: 0x00005000, END)
|
||||
|
||||
Image component sizes
|
||||
@@ -225,11 +225,21 @@
|
||||
5. `AT+MUX?`
|
||||
6. `AT+NET=...`
|
||||
7. `AT+NET?`
|
||||
8. `AT+LINK=...`
|
||||
9. `AT+LINK?`
|
||||
10. `AT+SAVE`
|
||||
11. `AT+RESET`
|
||||
12. `AT+DEFAULT`
|
||||
8. `AT+BAUD=...`
|
||||
9. `AT+BAUD?`
|
||||
10. `AT+LINK=...`
|
||||
11. `AT+LINK?`
|
||||
12. `AT+SAVE`
|
||||
13. `AT+RESET`
|
||||
14. `AT+DEFAULT`
|
||||
|
||||
其中与数据串口相关的固定映射为:
|
||||
|
||||
1. `U0 = USART2`
|
||||
2. `U1 = USART3`
|
||||
3. `AT+BAUD=U0,<baud>` / `AT+BAUD=U1,<baud>` 只更新当前运行配置记录
|
||||
4. 新波特率不会立即重初始化 `USART2/USART3`,必须执行 `AT+SAVE` + `AT+RESET` 后按保存值生效
|
||||
5. 当前代码接受的波特率范围为 `1200 ~ 921600`
|
||||
|
||||
### 6.2 现场关键规则
|
||||
|
||||
@@ -238,6 +248,7 @@
|
||||
1. 当前现场验证时,配置命令必须保证以换行完成帧。
|
||||
2. 若主机侧发送方式不对,现象会很像“配置口完全无响应”。
|
||||
3. 因此,配置口不响应时,第一优先级不是改 parser,而是先验证主机端发送格式与接线。
|
||||
4. `BAUD` 类命令若查询值已变化,但 `USART2/USART3` 现场波特率尚未变化,不应立即归因为命令无效,应先确认是否已经执行 `AT+SAVE` 与 `AT+RESET`。
|
||||
|
||||
### 6.3 最小验证步骤
|
||||
|
||||
@@ -247,13 +258,16 @@
|
||||
2. 先发 `AT`
|
||||
3. 再发 `AT+QUERY`
|
||||
4. 再发 `AT+NET?`
|
||||
5. 再发 `AT+LINK?`
|
||||
6. 修改一个最小参数,例如:
|
||||
- `AT+MUX=1`
|
||||
7. 执行:
|
||||
- `AT+SAVE`
|
||||
- `AT+RESET`
|
||||
8. 复位后再次查询,确认配置是否保留
|
||||
5. 再发 `AT+BAUD?`
|
||||
6. 再发 `AT+LINK?`
|
||||
7. 修改一个最小参数,例如:
|
||||
- `AT+MUX=1`
|
||||
- 或 `AT+BAUD=U1,38400`
|
||||
8. 执行:
|
||||
- `AT+SAVE`
|
||||
- `AT+RESET`
|
||||
9. 复位后再次查询,确认配置是否保留
|
||||
10. 若本轮验证的是 `AT+BAUD`,还应同步用上位机重新按新波特率连接 `USART2/USART3`,确认数据口实际生效
|
||||
|
||||
### 6.4 持久化失败时怎么查
|
||||
|
||||
@@ -488,12 +502,15 @@ Keil MDK-ARM 构建 0 Error(s), 0 Warning(s)。Flash 52.7 KB / 64.0 KB (82.5%)
|
||||
|
||||
在 CH390 发生 TX timeout 并触发 `ch390_runtime_emergency_reset()` 后,芯片寄存器访问恢复正常,`VID` 可读、PHY 链路也可能保持 `up`,但 TCP 业务流量仍可能长时间不恢复,表现为“芯片还活着,但网络像失联一样,通常只能重启恢复”。
|
||||
|
||||
在后续实现收敛中,又确认仅依赖单次 VID 异常或单次 TX busy 即立刻 reset 过于激进,容易把瞬时抖动误判为芯片失活,因此当前代码已经演化为“带阈值的恢复策略”。
|
||||
|
||||
#### 根因
|
||||
|
||||
`ch390_runtime_emergency_reset()` 旧实现仅执行 `ch390_software_reset()`、`ch390_default_config()` 与 `diag` 刷新,缺少 cold init 里已有的两层恢复语义:
|
||||
|
||||
1. **MAC 对齐未恢复**:旧代码没有重新写回 CH390 `PAR`,也没有把硬件 MAC 重新同步到 `netif->hwaddr`。若软件复位后 CH390 的 MAC 过滤状态与 lwIP 侧缓存身份不一致,现象会表现为寄存器可访问、链路仍在,但单播业务流量不通。
|
||||
2. **上层链路回收未触发**:TX-timeout 路径直接调用 `ch390_runtime_emergency_reset()`,没有保证 `App_StopLinksIfNeeded()` / `App_StartLinksIfNeeded()` 观察到一次有效的 link-down 周期,导致旧 TCP client/server 状态可能跨芯片复位残留,业务层没有完成重建。
|
||||
3. **恢复策略缺少抖动抑制**:若仅凭单次 TX busy 或单次 VID 异常立即 reset,容易在瞬时总线/链路抖动下过度恢复,放大业务扰动,因此当前实现增加了连续失败阈值和失败计数清零逻辑。
|
||||
|
||||
#### 修复内容
|
||||
|
||||
@@ -505,12 +522,40 @@ Keil MDK-ARM 构建 0 Error(s), 0 Warning(s)。Flash 52.7 KB / 64.0 KB (82.5%)
|
||||
| `Drivers/CH390/ch390_runtime.c` | emergency reset 成功后清 `g_ch390_irq_pending` 并置位 `g_link_restart_pending` | 避免复位前遗留中断状态影响恢复 |
|
||||
| `Drivers/CH390/ch390_runtime.c` | `ch390_runtime_check_link()` 增加一次性 hold-down 逻辑 | 保证主循环至少看到一次 link-down,从而触发 app 层 stop/start 回收重建 |
|
||||
| `Drivers/CH390/ch390_runtime.c` | TX-timeout 与 health-check 两条 reset 路径统一传入 `netif` | 让两类恢复路径都走同一套 MAC 重同步与链路重建语义 |
|
||||
| `Drivers/CH390/ch390_runtime.c` | 为 TX timeout 与 health-check 增加连续失败阈值 | 降低瞬时抖动导致的过度 reset 风险 |
|
||||
|
||||
#### 当前实现语义(以源码为准)
|
||||
|
||||
1. **TX timeout 阈值**
|
||||
- 单次判定条件:`CH390_TCR.TXREQ` busy-wait 持续超过 `10 ms`
|
||||
- 连续阈值:累计 `6` 次后才触发 `ch390_runtime_emergency_reset()`
|
||||
- 只要有一次成功发送,`g_tx_consecutive_timeout` 立即清零,因此该阈值针对的是**连续失败**,不是累计历史失败次数。
|
||||
|
||||
2. **health-check 阈值**
|
||||
- `VID` 单次读到 `0x0000` / `0xFFFF` 并不会立即 reset。
|
||||
- 只有连续 `3` 次异常 VID 才触发 emergency reset。
|
||||
- 若 `g_ch390_ready == 0`,则 health-check 会直接尝试 reset,不再等待 VID 连续计数。
|
||||
|
||||
3. **restart-pending 的单次语义**
|
||||
- emergency reset 成功后会置位 restart-pending。
|
||||
- 下一次 `ch390_runtime_check_link()` 先强制执行一次 `netif_set_link_down()`,随后立即清除此标志并提前返回。
|
||||
- 该设计用于保证主循环至少看到一次有效的 logical link-down,从而沿用现有 `App_StopLinksIfNeeded()` / `App_StartLinksIfNeeded()` 路径回收并重建 TCP links。
|
||||
|
||||
4. **内部计数与状态**
|
||||
- `g_chip_reset_count`:记录 emergency reset 尝试次数,饱和递增到 `0xFF`。
|
||||
- `g_tx_consecutive_timeout`:记录连续 TX busy 超时次数;成功发送或进入 reset 路径后清零。
|
||||
- health-check 连续失败计数当前与 `g_link_restart_pending` 共用一个状态字节的高位 nibble 存储;当 VID 恢复正常、达到 reset 阈值或 emergency reset 成功时会清零。
|
||||
|
||||
5. **失败路径差异**
|
||||
- 只有当 emergency reset 完成后 `g_diag.id_valid` 仍然有效,才会置位 restart-pending 并进入后续 app recycle 语义。
|
||||
- 若 reset 后芯片仍不响应,则仅记录失败并返回,不会伪装成可恢复状态。
|
||||
|
||||
#### 预期结果
|
||||
|
||||
1. CH390 发生 emergency reset 后,硬件 MAC、`netif->hwaddr` 与当前业务身份重新对齐。
|
||||
2. 即使物理网线始终保持连接,主循环仍会在后续 poll 中观察到一次有效 link-down,并按既有 `App_StopLinksIfNeeded()` / `App_StartLinksIfNeeded()` 路径回收并重建 TCP links。
|
||||
3. 复位后的恢复语义与 cold init 更接近,不再停留在“芯片寄存器恢复正常,但业务流量仍死掉”的半恢复状态。
|
||||
3. 恢复策略对瞬时异常更保守:只有连续超时或连续 VID 异常达到阈值才会触发 reset,降低误触发恢复的概率。
|
||||
4. 复位后的恢复语义与 cold init 更接近,不再停留在“芯片寄存器恢复正常,但业务流量仍死掉”的半恢复状态。
|
||||
|
||||
#### 构建验证
|
||||
|
||||
|
||||
@@ -184,6 +184,117 @@ EN,LPORT,RIP,RPORT,UART
|
||||
2. 统一受 `LINK[idx]` 配置驱动
|
||||
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` 上限,但余量已经很小;后续若继续增加功能,应优先考虑复用现有缓冲与状态,而不是增加新的静态大数组。
|
||||
|
||||
### 6.3 客户现场脏网络恢复增强
|
||||
|
||||
客户现场换 PC 后曾出现设备持续 ARP、`ping` 不通、TCP Client 不恢复的现象。抓包显示故障前后存在 IPv6、DHCPv6、mDNS、IGMP、LLDP 等与当前业务无关的网络噪声;当前固件为静态 IPv4、TCP2UART 与 ICMP 诊断模型,不依赖 UDP、IPv6、DHCP 或多播发现。
|
||||
|
||||
本阶段采用低 RAM 优先的恢复策略,不先扩大 `PBUF_POOL_SIZE`,而是在更靠近入口的位置减少无关帧对 lwIP pool 的占用:
|
||||
|
||||
1. `TCP Client CONNECTING` 增加应用层超时:
|
||||
- `TCP_CLIENT_CONNECT_TIMEOUT_MS = 10000`
|
||||
- `tcp_connect()` 返回 `ERR_OK` 后记录 `connect_start_ms`
|
||||
- `tcp_client_poll()` 发现 `CONNECTING` 超过超时时间后,注销回调、`tcp_abort()` 当前 PCB、释放 `hold_pbuf`,再按原有 `reconnect_interval_ms` 重连
|
||||
- `tcp_client_status_t.connect_timeout_count` 记录发生次数
|
||||
2. `CH390` RX 入口增加 `pre-pbuf` 协议过滤:
|
||||
- 在 `pbuf_alloc(PBUF_RAW, ..., PBUF_POOL)` 之前先读取以太网头与最小 IPv4 头
|
||||
- 允许进入 lwIP 的协议限定为 `ARP`、`IPv4 ICMP`、`IPv4 TCP`
|
||||
- 默认丢弃 `IPv6`、`IPv4 UDP`、`IPv4 IGMP`、`LLDP`、未知 EtherType 与畸形头
|
||||
- 丢弃帧只跳过 CH390 RX FIFO 剩余字节,不分配 pbuf
|
||||
3. 软件 MAC 过滤暂不启用:
|
||||
- 第一版只做协议层过滤,避免误杀广播 ARP 或未来硬件过滤策略变化
|
||||
- 目的 MAC 相关判断保留为后续可选增强
|
||||
|
||||
新增 CH390 诊断字段用于现场判断是否仍存在资源压力:
|
||||
|
||||
1. `rx_pbuf_alloc_failed`
|
||||
2. `rx_filtered_frames`
|
||||
3. `rx_filtered_ipv6`
|
||||
4. `rx_filtered_udp`
|
||||
5. `rx_filtered_igmp`
|
||||
6. `rx_filtered_lldp`
|
||||
7. `rx_filtered_other_eth`
|
||||
8. `rx_filtered_other_ipv4`
|
||||
9. `rx_filtered_malformed`
|
||||
10. `rx_ipv4_icmp_frames`
|
||||
11. `rx_ipv4_tcp_frames`
|
||||
12. `rx_ipv4_udp_frames`
|
||||
|
||||
验收口径:
|
||||
|
||||
1. 正常 `ARP / ping / TCP Server / TCP Client` 功能不受影响
|
||||
2. 客户脏网络中的 IPv6、UDP、IGMP、LLDP 噪声不会进入 lwIP pbuf pool
|
||||
3. 若远端 TCP Server 不监听或静默丢 SYN,TCP Client 不再永久停留在 `CONNECTING`
|
||||
4. 若过滤后 `rx_pbuf_alloc_failed` 仍持续增长,再评估从无关功能中回收 RAM 并调整 `PBUF_POOL_SIZE`
|
||||
|
||||
本阶段 Keil 构建验收结果:
|
||||
|
||||
1. `errors = 0`
|
||||
2. `warnings = 0`
|
||||
3. `flash_bytes = 57404`
|
||||
4. `ram_bytes = 20440`
|
||||
|
||||
## 七、主循环实现方向
|
||||
|
||||
主循环仍保持裸机轮询风格:
|
||||
|
||||
Reference in New Issue
Block a user