15 Commits

30 changed files with 1840 additions and 231 deletions
+19 -7
View File
@@ -19,6 +19,13 @@
- 配置口:`USART1` - 配置口:`USART1`
- 数据口:`USART2``USART3` - 数据口:`USART2``USART3`
### 2.1 固件版本线
- FreeRTOS + lwIP 版本线从 `V2.0.0` 开始。
- 裸机版本线从 `V1.0.0` 开始。
- 当前 FreeRTOS 固件基线 release`TCP2UART RTOS V2.0.0`
- 固件下载:`https://git.furtherverse.com/gaoro-xiao/TCP2UART/releases/tag/V2.0.0`
职责划分: 职责划分:
- `USART1`AT 配置口 - `USART1`AT 配置口
@@ -92,7 +99,7 @@ SYNC | LEN_H | LEN_L | SRCID | DSTMASK | PAYLOAD | TAIL
```text ```text
AT\r\n AT\r\n
AT+MUX?\r\n AT+MUX?\r\n
AT+NET=192.168.1.100,255.255.255.0,192.168.1.1,02:00:00:00:00:01\r\n AT+NET=192.168.31.100,255.255.255.0,192.168.31.1,00:00:00:00:00:00\r\n
``` ```
### 6.2 持久化规则 ### 6.2 持久化规则
@@ -121,9 +128,11 @@ MUX = 0
### 7.2 NET 默认值 ### 7.2 NET 默认值
```text ```text
NET = 192.168.1.100,255.255.255.0,192.168.1.1,02:00:00:00:00:01 NET = 192.168.31.100,255.255.255.0,192.168.31.1,00:00:00:00:00:00
``` ```
默认 MAC 为全 0,表示 Flash 中不固化板卡 MAC;运行时使用 `CH390D` 内部 MAC。`AT+?``AT+NET?` 回显的是当前生效 MAC。
### 7.3 LINK 默认值 ### 7.3 LINK 默认值
```text ```text
@@ -171,7 +180,7 @@ AT+QUERY\r\n
推荐返回格式: 推荐返回格式:
```text ```text
+NET:IP=192.168.1.100,MASK=255.255.255.0,GW=192.168.1.1,MAC=02:00:00:00:00:01 +NET:IP=192.168.31.100,MASK=255.255.255.0,GW=192.168.31.1,MAC=<当前生效MAC>
+LINK:S1,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0 +LINK:S1,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0
+LINK:S2,EN=0,LPORT=8081,RIP=0.0.0.0,RPORT=0,UART=U1 +LINK:S2,EN=0,LPORT=8081,RIP=0.0.0.0,RPORT=0,UART=U1
+LINK:C1,EN=1,LPORT=9001,RIP=192.168.1.200,RPORT=9000,UART=U1 +LINK:C1,EN=1,LPORT=9001,RIP=192.168.1.200,RPORT=9000,UART=U1
@@ -213,7 +222,7 @@ OK
#### 设置 NET #### 设置 NET
```text ```text
AT+NET=192.168.1.100,255.255.255.0,192.168.1.1,02:00:00:00:00:01\r\n AT+NET=192.168.31.100,255.255.255.0,192.168.31.1,00:00:00:00:00:00\r\n
``` ```
字段顺序: 字段顺序:
@@ -231,13 +240,13 @@ AT+NET?\r\n
返回示例: 返回示例:
```text ```text
+NET:IP=192.168.1.100,MASK=255.255.255.0,GW=192.168.1.1,MAC=02:00:00:00:00:01 +NET:IP=192.168.31.100,MASK=255.255.255.0,GW=192.168.31.1,MAC=<当前生效MAC>
OK OK
``` ```
**MAC 设置说明:** **MAC 设置说明:**
当MAC设置为全0时,固件将使用硬件MAC地址此时通过AT+?查询到的MAC地址为当前生效的硬件MAC地址。 MAC 设置为全 0 时,固件将使用 `CH390D` 内部 MAC 地址此时 Flash 内仍保存全 0,不会把内部 MAC 写回 Flash;`AT+?``AT+NET?` 查询到的 MAC 地址为当前运行时生效的硬件 MAC 地址。
### 8.5 LINK 类命令 ### 8.5 LINK 类命令
@@ -268,6 +277,9 @@ ROLE,EN,LPORT,RIP,RPORT,UART
- `Server``Client` 共用同一条 `LINK` 记录模型 - `Server``Client` 共用同一条 `LINK` 记录模型
- `Server``RIP/RPORT` 可作为允许接入的对端约束或预设对端信息 - `Server``RIP/RPORT` 可作为允许接入的对端约束或预设对端信息
- `Client``RIP/RPORT` 表示远端目标地址与端口 - `Client``RIP/RPORT` 表示远端目标地址与端口
- `Client` 侧当前保留固定 `LPORT` 语义,用于满足部分上位机或现场网络策略对固定源端口的依赖
- 为避免固定 `LPORT` 下频繁重连被 lwIP `TIME_WAIT` 长时间占用阻塞,当前固件对 `Client` 主动断开后的释放路径采用 abortive closeRST)而非优雅 `FIN/ACK` 关闭
- 因此 `Client` 重连场景下,对端可能观察到 `RST` 或“连接被重置”,这属于当前产品约束下的有意设计取舍,不影响 `AT+LINK``LPORT` 的配置语义
#### 查询单条 LINK #### 查询单条 LINK
@@ -354,7 +366,7 @@ OK: Defaults restored
## 11. 推荐配置流程 ## 11. 推荐配置流程
```text ```text
AT+NET=192.168.1.123,255.255.255.0,192.168.1.1,02:00:00:00:00:01\r\n AT+NET=192.168.31.123,255.255.255.0,192.168.31.1,00:00:00:00:00:00\r\n
AT+LINK=S1,1,10001,0.0.0.0,0,U1\r\n AT+LINK=S1,1,10001,0.0.0.0,0,U1\r\n
AT+LINK=S2,1,10003,0.0.0.0,0,U1\r\n AT+LINK=S2,1,10003,0.0.0.0,0,U1\r\n
AT+LINK=C1,1,20001,192.168.1.201,10002,U0\r\n AT+LINK=C1,1,20001,192.168.1.201,10002,U0\r\n
+8
View File
@@ -28,6 +28,14 @@ extern volatile int32_t g_netif_set_up_err;
extern volatile int32_t g_netif_init_ok; extern volatile int32_t g_netif_init_ok;
void app_start_network_tasks(void); void app_start_network_tasks(void);
void app_request_network_task_stop(void);
void app_clear_network_task_stop(void);
BaseType_t app_network_task_stop_requested(void);
BaseType_t app_network_tasks_are_stopped(void);
void app_on_network_task_exit(TaskHandle_t task_handle);
void app_request_network_restart(void);
void app_clear_network_restart_request(void);
BaseType_t app_network_restart_requested(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
+115 -5
View File
@@ -13,6 +13,7 @@
#include "route_msg.h" #include "route_msg.h"
#include "app_runtime.h" #include "app_runtime.h"
#include "debug_log.h" #include "debug_log.h"
#include "ethernetif.h"
#include "uart_trans.h" #include "uart_trans.h"
#define CONFIG_RX_BUFFER_SIZE 160u #define CONFIG_RX_BUFFER_SIZE 160u
@@ -22,6 +23,8 @@
static device_config_t g_config; static device_config_t g_config;
static volatile bool g_reset_requested; static volatile bool g_reset_requested;
static volatile bool g_uart1_tx_busy; static volatile bool g_uart1_tx_busy;
static volatile uint32_t g_config_rx_route_fail_count;
static volatile route_send_result_t g_config_rx_route_fail_reason;
static uint8_t g_uart1_rx_buffer[CONFIG_RX_BUFFER_SIZE]; static uint8_t g_uart1_rx_buffer[CONFIG_RX_BUFFER_SIZE];
static char g_config_cmd_buffer[CONFIG_CMD_MAX_LEN]; static char g_config_cmd_buffer[CONFIG_CMD_MAX_LEN];
static char g_config_response_buffer[CONFIG_TX_BUFFER_SIZE]; static char g_config_response_buffer[CONFIG_TX_BUFFER_SIZE];
@@ -113,6 +116,19 @@ static int parse_link_uart(const char *value, uint8_t *uart)
return -1; return -1;
} }
static int parse_uart_name(const char *value, uint8_t *uart_index)
{
if (equals_ignore_case(value, "U0") || equals_ignore_case(value, "UART2")) {
*uart_index = LINK_UART_U0;
return 0;
}
if (equals_ignore_case(value, "U1") || equals_ignore_case(value, "UART3")) {
*uart_index = LINK_UART_U1;
return 0;
}
return -1;
}
static const char *link_uart_to_str(uint8_t uart) static const char *link_uart_to_str(uint8_t uart)
{ {
return (uart == LINK_UART_U1) ? "U1" : "U0"; return (uart == LINK_UART_U1) ? "U1" : "U0";
@@ -134,6 +150,15 @@ static const char *link_index_to_name(uint32_t index)
} }
} }
static void config_get_display_mac(uint8_t *mac)
{
if (ethernetif_get_effective_mac(mac) != 0u) {
return;
}
memcpy(mac, g_config.net.mac, sizeof(g_config.net.mac));
}
static int parse_link_name(const char *value, uint32_t *index) static int parse_link_name(const char *value, uint32_t *index)
{ {
if (equals_ignore_case(value, "S1")) { if (equals_ignore_case(value, "S1")) {
@@ -229,13 +254,15 @@ static at_result_t handle_summary_query(char *response, uint16_t max_len)
char mask_str[16]; char mask_str[16];
char gw_str[16]; char gw_str[16];
char mac_str[18]; char mac_str[18];
uint8_t display_mac[6];
char rip_str[CONFIG_LINK_COUNT][16]; char rip_str[CONFIG_LINK_COUNT][16];
uint32_t i; uint32_t i;
config_ip_to_str(g_config.net.ip, ip_str); config_ip_to_str(g_config.net.ip, ip_str);
config_ip_to_str(g_config.net.mask, mask_str); config_ip_to_str(g_config.net.mask, mask_str);
config_ip_to_str(g_config.net.gw, gw_str); config_ip_to_str(g_config.net.gw, gw_str);
config_mac_to_str(g_config.net.mac, mac_str); config_get_display_mac(display_mac);
config_mac_to_str(display_mac, mac_str);
for (i = 0; i < CONFIG_LINK_COUNT; ++i) { for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]); config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]);
} }
@@ -315,6 +342,14 @@ const device_config_t *config_get(void)
return &g_config; return &g_config;
} }
uint32_t config_get_uart_baudrate(uint8_t uart_index)
{
if (uart_index >= CONFIG_UART_COUNT) {
return DEFAULT_UART_BAUDRATE;
}
return g_config.uart_baudrate[uart_index];
}
device_config_t *config_get_mutable(void) device_config_t *config_get_mutable(void)
{ {
return &g_config; return &g_config;
@@ -370,6 +405,39 @@ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_
snprintf(response, max_len, "+MUX:%u\r\nOK\r\n", g_config.mux_mode); snprintf(response, max_len, "+MUX:%u\r\nOK\r\n", g_config.mux_mode);
return AT_OK; return AT_OK;
} }
if (equals_ignore_case(p, "BAUD?")) {
snprintf(response, max_len,
"+BAUD:U0=%lu,U1=%lu\r\nOK\r\n",
(unsigned long)g_config.uart_baudrate[0],
(unsigned long)g_config.uart_baudrate[1]);
return AT_OK;
}
if (parse_command_with_value(p, "BAUD", &value)) {
char value_copy[48];
char *cursor;
char *token;
uint8_t uart_index;
uint32_t baudrate;
strncpy(value_copy, value, sizeof(value_copy) - 1u);
value_copy[sizeof(value_copy) - 1u] = '\0';
cursor = value_copy;
token = config_next_token(&cursor);
if (token == NULL || parse_uart_name(token, &uart_index) != 0) {
snprintf(response, max_len, "ERROR: Invalid UART\r\n");
return AT_INVALID_PARAM;
}
token = config_next_token(&cursor);
if (token == NULL || parse_u32_value(token, 1200u, 921600u, &baudrate) != 0) {
snprintf(response, max_len, "ERROR: Invalid baudrate\r\n");
return AT_INVALID_PARAM;
}
g_config.uart_baudrate[uart_index] = baudrate;
snprintf(response, max_len, "OK\r\n");
return AT_NEED_REBOOT;
}
if (parse_command_with_value(p, "MUX", &value)) { if (parse_command_with_value(p, "MUX", &value)) {
uint32_t mux_value; uint32_t mux_value;
if (parse_u32_value(value, 0u, 1u, &mux_value) != 0) { if (parse_u32_value(value, 0u, 1u, &mux_value) != 0) {
@@ -385,11 +453,13 @@ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_
char mask_str[16]; char mask_str[16];
char gw_str[16]; char gw_str[16];
char mac_str[18]; char mac_str[18];
uint8_t display_mac[6];
config_ip_to_str(g_config.net.ip, ip_str); config_ip_to_str(g_config.net.ip, ip_str);
config_ip_to_str(g_config.net.mask, mask_str); config_ip_to_str(g_config.net.mask, mask_str);
config_ip_to_str(g_config.net.gw, gw_str); config_ip_to_str(g_config.net.gw, gw_str);
config_mac_to_str(g_config.net.mac, mac_str); config_get_display_mac(display_mac);
config_mac_to_str(display_mac, mac_str);
snprintf(response, max_len, "+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\nOK\r\n", ip_str, mask_str, gw_str, mac_str); snprintf(response, max_len, "+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\nOK\r\n", ip_str, mask_str, gw_str, mac_str);
return AT_OK; return AT_OK;
} }
@@ -570,19 +640,24 @@ void config_uart_idle_handler(void)
uint16_t len = CONFIG_RX_BUFFER_SIZE - dma_counter; uint16_t len = CONFIG_RX_BUFFER_SIZE - dma_counter;
BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
HAL_StatusTypeDef hal_status; HAL_StatusTypeDef hal_status;
route_send_result_t route_result;
if (g_uart1_tx_busy) { if (g_uart1_tx_busy) {
return; return;
} }
if (len > 0u && xConfigQueue != NULL) { if (len > 0u && xConfigQueue != NULL) {
(void)route_send_from_isr(xConfigQueue, route_result = route_send_from_isr(xConfigQueue,
0u, 0u,
0u, 0u,
ROUTE_CONN_UART1, ROUTE_CONN_UART1,
g_uart1_rx_buffer, g_uart1_rx_buffer,
len, len,
&xHigherPriorityTaskWoken); &xHigherPriorityTaskWoken);
if (route_result != ROUTE_SEND_OK) {
g_config_rx_route_fail_reason = route_result;
g_config_rx_route_fail_count += 1u;
}
} }
hal_status = HAL_UART_DMAStop(&huart1); hal_status = HAL_UART_DMAStop(&huart1);
@@ -625,9 +700,39 @@ static void config_respond_to_uart(route_msg_t *msg, const char *response)
uart_channel_t channel = (msg->src_id == ENDPOINT_UART3) ? UART_CHANNEL_U1 : UART_CHANNEL_U0; uart_channel_t channel = (msg->src_id == ENDPOINT_UART3) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
uint8_t frame[ROUTE_MSG_MAX_PAYLOAD + 6u]; uint8_t frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
uint16_t frame_len = 0u; uint16_t frame_len = 0u;
uart_trans_send_result_t uart_result;
if (uart_mux_encode_frame(msg->src_id, 0u, (const uint8_t *)response, (uint16_t)strlen(response), frame, &frame_len, sizeof(frame))) { if (uart_mux_encode_frame(msg->src_id, 0u, (const uint8_t *)response, (uint16_t)strlen(response), frame, &frame_len, sizeof(frame))) {
(void)uart_trans_send_buffer(channel, frame, frame_len); uart_result = uart_trans_send_buffer(channel, frame, frame_len);
if (uart_result != UART_TRANS_SEND_OK) {
debug_log_printf("[CFG] resp-tx-fail ch=%u rc=%s len=%u\r\n",
(unsigned int)channel,
uart_trans_send_result_to_str(uart_result),
(unsigned int)frame_len);
} }
} else {
debug_log_printf("[CFG] resp-enc-fail src=0x%02X len=%u\r\n",
(unsigned int)msg->src_id,
(unsigned int)strlen(response));
}
}
}
static void config_report_route_failures(uint32_t *reported_route_fail_count)
{
uint32_t fail_count;
route_send_result_t fail_reason;
if (reported_route_fail_count == NULL) {
return;
}
fail_count = g_config_rx_route_fail_count;
fail_reason = g_config_rx_route_fail_reason;
if (fail_count != *reported_route_fail_count) {
*reported_route_fail_count = fail_count;
debug_log_printf("[CFG] rx-route-fail rc=%s cnt=%lu\r\n",
route_send_result_to_str(fail_reason),
(unsigned long)fail_count);
} }
} }
@@ -635,6 +740,7 @@ void ConfigTask(void *argument)
{ {
route_msg_t *msg; route_msg_t *msg;
at_result_t result; at_result_t result;
uint32_t reported_route_fail_count = 0u;
(void)argument; (void)argument;
debug_log_write("[CFG] task-entry\r\n"); debug_log_write("[CFG] task-entry\r\n");
@@ -642,10 +748,14 @@ void ConfigTask(void *argument)
debug_log_write("[CFG] task-ready\r\n"); debug_log_write("[CFG] task-ready\r\n");
for (;;) { for (;;) {
if (xQueueReceive(xConfigQueue, &msg, portMAX_DELAY) != pdPASS) { config_report_route_failures(&reported_route_fail_count);
if (xQueueReceive(xConfigQueue, &msg, pdMS_TO_TICKS(50)) != pdPASS) {
continue; continue;
} }
config_report_route_failures(&reported_route_fail_count);
if (msg->len >= sizeof(g_config_cmd_buffer)) { if (msg->len >= sizeof(g_config_cmd_buffer)) {
msg->len = sizeof(g_config_cmd_buffer) - 1u; msg->len = sizeof(g_config_cmd_buffer) - 1u;
} }
+2 -1
View File
@@ -66,7 +66,7 @@ typedef struct {
#define DEFAULT_NET_IP {192, 168, 31, 100} #define DEFAULT_NET_IP {192, 168, 31, 100}
#define DEFAULT_NET_MASK {255, 255, 255, 0} #define DEFAULT_NET_MASK {255, 255, 255, 0}
#define DEFAULT_NET_GW {192, 168, 31, 1} #define DEFAULT_NET_GW {192, 168, 31, 1}
#define DEFAULT_NET_MAC {0x02, 0x00, 0x00, 0x00, 0x00, 0x01} #define DEFAULT_NET_MAC {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
#define DEFAULT_UART_BAUDRATE 115200u #define DEFAULT_UART_BAUDRATE 115200u
#define DIAG_CH390_RAW_POLL 0 #define DIAG_CH390_RAW_POLL 0
@@ -86,6 +86,7 @@ int config_save(void);
void config_set_defaults(void); void config_set_defaults(void);
const device_config_t *config_get(void); const device_config_t *config_get(void);
device_config_t *config_get_mutable(void); device_config_t *config_get_mutable(void);
uint32_t config_get_uart_baudrate(uint8_t uart_index);
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len); at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len);
void ConfigTask(void *argument); void ConfigTask(void *argument);
void config_uart_idle_handler(void); void config_uart_idle_handler(void);
+68 -15
View File
@@ -12,6 +12,22 @@ typedef struct {
static route_slot_t g_route_slots[ROUTE_MSG_POOL_SIZE]; static route_slot_t g_route_slots[ROUTE_MSG_POOL_SIZE];
const char *route_send_result_to_str(route_send_result_t result)
{
switch (result) {
case ROUTE_SEND_OK:
return "ok";
case ROUTE_SEND_INVALID_INPUT:
return "invalid";
case ROUTE_SEND_POOL_EXHAUSTED:
return "pool";
case ROUTE_SEND_QUEUE_FULL:
return "queue";
default:
return "unknown";
}
}
void route_msg_init(void) void route_msg_init(void)
{ {
memset(g_route_slots, 0, sizeof(g_route_slots)); memset(g_route_slots, 0, sizeof(g_route_slots));
@@ -108,7 +124,7 @@ void route_msg_free_from_isr(route_msg_t *msg)
taskEXIT_CRITICAL_FROM_ISR(saved_interrupt_status); taskEXIT_CRITICAL_FROM_ISR(saved_interrupt_status);
} }
static bool route_prepare(route_msg_t *msg, static route_send_result_t route_prepare(route_msg_t *msg,
uint8_t src_id, uint8_t src_id,
uint8_t dst_mask, uint8_t dst_mask,
uint8_t conn_type, uint8_t conn_type,
@@ -116,7 +132,7 @@ static bool route_prepare(route_msg_t *msg,
uint16_t len) uint16_t len)
{ {
if (msg == NULL || data == NULL || len == 0u || len > ROUTE_MSG_MAX_PAYLOAD) { if (msg == NULL || data == NULL || len == 0u || len > ROUTE_MSG_MAX_PAYLOAD) {
return false; return ROUTE_SEND_INVALID_INPUT;
} }
msg->src_id = src_id; msg->src_id = src_id;
@@ -124,10 +140,21 @@ static bool route_prepare(route_msg_t *msg,
msg->conn_type = conn_type; msg->conn_type = conn_type;
msg->len = len; msg->len = len;
memcpy(msg->data, data, len); memcpy(msg->data, data, len);
return true; return ROUTE_SEND_OK;
} }
bool route_send(QueueHandle_t queue, static route_send_result_t route_validate_args(QueueHandle_t queue,
const uint8_t *data,
uint16_t len)
{
if (queue == NULL || data == NULL || len == 0u || len > ROUTE_MSG_MAX_PAYLOAD) {
return ROUTE_SEND_INVALID_INPUT;
}
return ROUTE_SEND_OK;
}
route_send_result_t route_send(QueueHandle_t queue,
uint8_t src_id, uint8_t src_id,
uint8_t dst_mask, uint8_t dst_mask,
uint8_t conn_type, uint8_t conn_type,
@@ -135,22 +162,35 @@ bool route_send(QueueHandle_t queue,
uint16_t len, uint16_t len,
TickType_t wait_ticks) TickType_t wait_ticks)
{ {
route_msg_t *msg = route_msg_alloc(wait_ticks); route_send_result_t result;
route_msg_t *msg;
if (!route_prepare(msg, src_id, dst_mask, conn_type, data, len)) { result = route_validate_args(queue, data, len);
if (result != ROUTE_SEND_OK) {
return result;
}
msg = route_msg_alloc(wait_ticks);
if (msg == NULL) {
return ROUTE_SEND_POOL_EXHAUSTED;
}
result = route_prepare(msg, src_id, dst_mask, conn_type, data, len);
if (result != ROUTE_SEND_OK) {
route_msg_free(msg); route_msg_free(msg);
return false; return result;
} }
if (xQueueSend(queue, &msg, wait_ticks) != pdPASS) { if (xQueueSend(queue, &msg, wait_ticks) != pdPASS) {
route_msg_free(msg); route_msg_free(msg);
return false; return ROUTE_SEND_QUEUE_FULL;
} }
return true; return ROUTE_SEND_OK;
} }
bool route_send_from_isr(QueueHandle_t queue, route_send_result_t route_send_from_isr(QueueHandle_t queue,
uint8_t src_id, uint8_t src_id,
uint8_t dst_mask, uint8_t dst_mask,
uint8_t conn_type, uint8_t conn_type,
@@ -158,17 +198,30 @@ bool route_send_from_isr(QueueHandle_t queue,
uint16_t len, uint16_t len,
BaseType_t *xHigherPriorityTaskWoken) BaseType_t *xHigherPriorityTaskWoken)
{ {
route_msg_t *msg = route_msg_alloc_from_isr(xHigherPriorityTaskWoken); route_send_result_t result;
route_msg_t *msg;
if (!route_prepare(msg, src_id, dst_mask, conn_type, data, len)) { result = route_validate_args(queue, data, len);
if (result != ROUTE_SEND_OK) {
return result;
}
msg = route_msg_alloc_from_isr(xHigherPriorityTaskWoken);
if (msg == NULL) {
return ROUTE_SEND_POOL_EXHAUSTED;
}
result = route_prepare(msg, src_id, dst_mask, conn_type, data, len);
if (result != ROUTE_SEND_OK) {
route_msg_free_from_isr(msg); route_msg_free_from_isr(msg);
return false; return result;
} }
if (xQueueSendFromISR(queue, &msg, xHigherPriorityTaskWoken) != pdPASS) { if (xQueueSendFromISR(queue, &msg, xHigherPriorityTaskWoken) != pdPASS) {
route_msg_free_from_isr(msg); route_msg_free_from_isr(msg);
return false; return ROUTE_SEND_QUEUE_FULL;
} }
return true; return ROUTE_SEND_OK;
} }
+10 -2
View File
@@ -29,6 +29,13 @@ typedef enum {
ROUTE_CONN_C2 ROUTE_CONN_C2
} route_conn_type_t; } route_conn_type_t;
typedef enum {
ROUTE_SEND_OK = 0,
ROUTE_SEND_INVALID_INPUT,
ROUTE_SEND_POOL_EXHAUSTED,
ROUTE_SEND_QUEUE_FULL
} route_send_result_t;
typedef struct { typedef struct {
uint8_t src_id; uint8_t src_id;
uint8_t dst_mask; uint8_t dst_mask;
@@ -42,14 +49,15 @@ route_msg_t *route_msg_alloc(TickType_t wait_ticks);
route_msg_t *route_msg_alloc_from_isr(BaseType_t *xHigherPriorityTaskWoken); route_msg_t *route_msg_alloc_from_isr(BaseType_t *xHigherPriorityTaskWoken);
void route_msg_free(route_msg_t *msg); void route_msg_free(route_msg_t *msg);
void route_msg_free_from_isr(route_msg_t *msg); void route_msg_free_from_isr(route_msg_t *msg);
bool route_send(QueueHandle_t queue, const char *route_send_result_to_str(route_send_result_t result);
route_send_result_t route_send(QueueHandle_t queue,
uint8_t src_id, uint8_t src_id,
uint8_t dst_mask, uint8_t dst_mask,
uint8_t conn_type, uint8_t conn_type,
const uint8_t *data, const uint8_t *data,
uint16_t len, uint16_t len,
TickType_t wait_ticks); TickType_t wait_ticks);
bool route_send_from_isr(QueueHandle_t queue, route_send_result_t route_send_from_isr(QueueHandle_t queue,
uint8_t src_id, uint8_t src_id,
uint8_t dst_mask, uint8_t dst_mask,
uint8_t conn_type, uint8_t conn_type,
+65
View File
@@ -16,6 +16,64 @@
#include "app_runtime.h" #include "app_runtime.h"
#include "debug_log.h" #include "debug_log.h"
#define CH390_RESTART_HOLD_DOWN_MS 500u
#define NETWORK_TASK_DELETE_SETTLE_MS 50u
#define CH390_EXPECTED_VENDOR_ID 0x1C00u
#define CH390_EXPECTED_PRODUCT_ID 0x9151u
static void net_poll_wait_for_network_tasks_stop(void)
{
while (app_network_tasks_are_stopped() == pdFALSE) {
vTaskDelay(pdMS_TO_TICKS(20));
}
}
static BaseType_t net_poll_restart_network_stack(const device_config_t *cfg)
{
#if !DIAG_CH390_RAW_POLL
ip4_addr_t ipaddr;
ip4_addr_t netmask;
ip4_addr_t gateway;
uint16_t vendor_id;
uint16_t product_id;
uint8_t revision;
IP4_ADDR(&ipaddr, cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3]);
IP4_ADDR(&netmask, cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3]);
IP4_ADDR(&gateway, cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3]);
#endif
ethernetif_force_link_down();
g_netif_ready = pdFALSE;
app_request_network_task_stop();
net_poll_wait_for_network_tasks_stop();
vTaskDelay(pdMS_TO_TICKS(NETWORK_TASK_DELETE_SETTLE_MS));
vTaskDelay(pdMS_TO_TICKS(CH390_RESTART_HOLD_DOWN_MS));
#if DIAG_CH390_RAW_POLL
ethernetif_diag_ch390_init();
#else
ethernetif_force_full_recovery(&ipaddr, &netmask, &gateway, cfg->net.mac);
vendor_id = ethernetif_ch390_get_vendor_id();
product_id = ethernetif_ch390_get_product_id();
revision = ethernetif_ch390_get_revision();
if ((vendor_id != CH390_EXPECTED_VENDOR_ID) || (product_id != CH390_EXPECTED_PRODUCT_ID)) {
debug_log_printf("[NET] restart-recovery id-warn vid=0x%04X pid=0x%04X rev=0x%02X free=%lu min=%lu\r\n",
(unsigned int)vendor_id,
(unsigned int)product_id,
(unsigned int)revision,
(unsigned long)xPortGetFreeHeapSize(),
(unsigned long)xPortGetMinimumEverFreeHeapSize());
}
#endif
app_clear_network_task_stop();
g_netif_ready = pdTRUE;
app_start_network_tasks();
app_clear_network_restart_request();
return pdTRUE;
}
void NetPollTask(void *argument) void NetPollTask(void *argument)
{ {
const device_config_t *cfg; const device_config_t *cfg;
@@ -98,6 +156,11 @@ void NetPollTask(void *argument)
debug_log_write("[NET] loop-enter\r\n"); debug_log_write("[NET] loop-enter\r\n");
loop_logged = pdTRUE; loop_logged = pdTRUE;
} }
if (app_network_restart_requested() != pdFALSE) {
(void)net_poll_restart_network_stack(cfg);
}
(void)xSemaphoreTake(xNetSemaphore, pdMS_TO_TICKS(2)); (void)xSemaphoreTake(xNetSemaphore, pdMS_TO_TICKS(2));
#if DIAG_CH390_RAW_POLL #if DIAG_CH390_RAW_POLL
@@ -120,8 +183,10 @@ void NetPollTask(void *argument)
} }
} }
#else #else
if (g_netif_ready != pdFALSE) {
ethernetif_poll(); ethernetif_poll();
ethernetif_check_link(); ethernetif_check_link();
}
#endif #endif
} }
} }
+110 -11
View File
@@ -5,6 +5,8 @@
#include "queue.h" #include "queue.h"
#include "lwip/api.h" #include "lwip/api.h"
#include "lwip/ip_addr.h" #include "lwip/ip_addr.h"
#include "lwip/tcp.h"
#include "lwip/tcpip.h"
#include "app_runtime.h" #include "app_runtime.h"
#include "config.h" #include "config.h"
@@ -12,7 +14,56 @@
#include "ethernetif.h" #include "ethernetif.h"
#include "route_msg.h" #include "route_msg.h"
#define TCP_CLIENT_CONNECT_TIMEOUT_MS 5000 #define TCP_CLIENT_CONNECT_TIMEOUT_MS 500
#define TCP_CLIENT_RECONNECT_INTERVAL_MS 3000u
#define TCP_CLIENT_STOP_POLL_MS 50u
static BaseType_t tcp_client_stop_requested(void)
{
return (app_network_task_stop_requested() != pdFALSE) ? pdTRUE : pdFALSE;
}
static BaseType_t tcp_client_delay_with_stop(uint32_t delay_ms)
{
uint32_t remaining_ms = delay_ms;
while (remaining_ms > 0u) {
uint32_t slice_ms = (remaining_ms > TCP_CLIENT_STOP_POLL_MS) ? TCP_CLIENT_STOP_POLL_MS : remaining_ms;
if (tcp_client_stop_requested() != pdFALSE) {
return pdFALSE;
}
vTaskDelay(pdMS_TO_TICKS(slice_ms));
remaining_ms -= slice_ms;
}
return (tcp_client_stop_requested() == pdFALSE) ? pdTRUE : pdFALSE;
}
static void tcp_client_abort_and_delete(struct netconn *conn, uint8_t link_index)
{
struct tcp_pcb *pcb;
if (conn == NULL) {
return;
}
pcb = conn->pcb.tcp;
if (pcb != NULL) {
LOCK_TCPIP_CORE();
pcb = conn->pcb.tcp;
if (pcb != NULL) {
tcp_abort(pcb);
conn->pcb.tcp = NULL;
conn->state = NETCONN_NONE;
debug_log_printf("[CLI] idx=%u abort-close\r\n", (unsigned int)link_index);
}
UNLOCK_TCPIP_CORE();
}
netconn_delete(conn);
}
static err_t tcp_client_worker(struct netconn *conn, uint8_t link_index) static err_t tcp_client_worker(struct netconn *conn, uint8_t link_index)
{ {
@@ -22,30 +73,51 @@ static err_t tcp_client_worker(struct netconn *conn, uint8_t link_index)
uint8_t src_endpoint = config_link_index_to_endpoint(link_index); uint8_t src_endpoint = config_link_index_to_endpoint(link_index);
err_t err; err_t err;
route_msg_t *tx_msg; route_msg_t *tx_msg;
route_send_result_t route_result;
netconn_set_recvtimeout(conn, 10); netconn_set_recvtimeout(conn, 10);
for (;;) { for (;;) {
if (tcp_client_stop_requested() != pdFALSE) {
return ERR_CLSD;
}
err = netconn_recv(conn, &buf); err = netconn_recv(conn, &buf);
if (err == ERR_OK) { if (err == ERR_OK) {
do { do {
void *data; void *data;
uint16_t len; uint16_t len;
netbuf_data(buf, &data, &len); netbuf_data(buf, &data, &len);
(void)route_send(xTcpRxQueue, route_result = route_send(xTcpRxQueue,
src_endpoint, src_endpoint,
uart_endpoint, uart_endpoint,
(link_index == CONFIG_LINK_C1) ? ROUTE_CONN_C1 : ROUTE_CONN_C2, (link_index == CONFIG_LINK_C1) ? ROUTE_CONN_C1 : ROUTE_CONN_C2,
(const uint8_t *)data, (const uint8_t *)data,
len, len,
pdMS_TO_TICKS(10)); pdMS_TO_TICKS(10));
if (route_result != ROUTE_SEND_OK) {
debug_log_printf("[CLI] idx=%u rx-route-fail rc=%s len=%u\r\n",
(unsigned int)link_index,
route_send_result_to_str(route_result),
(unsigned int)len);
netbuf_delete(buf);
return ERR_CLSD;
}
} while (netbuf_next(buf) >= 0); } while (netbuf_next(buf) >= 0);
netbuf_delete(buf); netbuf_delete(buf);
} else if (err != ERR_TIMEOUT) { } else if (err == ERR_TIMEOUT) {
if (tcp_client_stop_requested() != pdFALSE) {
return ERR_CLSD;
}
} else {
return err; return err;
} }
while (xQueueReceive(xLinkTxQueues[link_index], &tx_msg, 0) == pdPASS) { while (xQueueReceive(xLinkTxQueues[link_index], &tx_msg, 0) == pdPASS) {
if (tcp_client_stop_requested() != pdFALSE) {
route_msg_free(tx_msg);
return ERR_CLSD;
}
err = netconn_write(conn, tx_msg->data, tx_msg->len, NETCONN_COPY); err = netconn_write(conn, tx_msg->data, tx_msg->len, NETCONN_COPY);
route_msg_free(tx_msg); route_msg_free(tx_msg);
if (err != ERR_OK) { if (err != ERR_OK) {
@@ -69,28 +141,44 @@ static void tcp_client_task(uint8_t link_index)
first_connect_deferred = (link_index == CONFIG_LINK_C1) ? 1u : 0u; first_connect_deferred = (link_index == CONFIG_LINK_C1) ? 1u : 0u;
for (;;) { for (;;) {
if (tcp_client_stop_requested() != pdFALSE) {
break;
}
while ((g_netif_ready == pdFALSE) || (ethernetif_link_is_up() == 0u)) { while ((g_netif_ready == pdFALSE) || (ethernetif_link_is_up() == 0u)) {
if (tcp_client_stop_requested() != pdFALSE) {
goto exit_task;
}
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
} }
cfg = config_get(); cfg = config_get();
if (cfg->links[link_index].enabled == 0u) { if (cfg->links[link_index].enabled == 0u) {
vTaskDelay(pdMS_TO_TICKS(500)); if (tcp_client_stop_requested() != pdFALSE) {
break;
}
if (tcp_client_delay_with_stop(500u) == pdFALSE) {
break;
}
continue; continue;
} }
delay_ms = (cfg->reconnect_interval_ms == 0u) ? 3000u : cfg->reconnect_interval_ms; delay_ms = TCP_CLIENT_RECONNECT_INTERVAL_MS;
if (first_connect_deferred != 0u) { if (first_connect_deferred != 0u) {
first_connect_deferred = 0u; first_connect_deferred = 0u;
debug_log_write("[CLI] C1 first-connect defer\r\n"); debug_log_write("[CLI] C1 first-connect defer\r\n");
vTaskDelay(pdMS_TO_TICKS(delay_ms)); if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
break;
}
continue; continue;
} }
conn = netconn_new(NETCONN_TCP); conn = netconn_new(NETCONN_TCP);
if (conn == NULL) { if (conn == NULL) {
vTaskDelay(pdMS_TO_TICKS(delay_ms)); if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
break;
}
continue; continue;
} }
@@ -102,7 +190,9 @@ static void tcp_client_task(uint8_t link_index)
(int)err, (int)err,
(unsigned int)cfg->links[link_index].local_port); (unsigned int)cfg->links[link_index].local_port);
netconn_delete(conn); netconn_delete(conn);
vTaskDelay(pdMS_TO_TICKS(delay_ms)); if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
break;
}
continue; continue;
} }
} }
@@ -140,10 +230,19 @@ static void tcp_client_task(uint8_t link_index)
} }
} }
netconn_close(conn); tcp_client_abort_and_delete(conn, link_index);
netconn_delete(conn); if (tcp_client_stop_requested() != pdFALSE) {
vTaskDelay(pdMS_TO_TICKS(delay_ms)); break;
} }
if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
break;
}
}
exit_task:
netconn_thread_cleanup();
app_on_network_task_exit(xTaskGetCurrentTaskHandle());
vTaskDelete(NULL);
} }
void TcpCliTask_C1(void *argument) void TcpCliTask_C1(void *argument)
+80 -8
View File
@@ -11,7 +11,33 @@
#include "debug_log.h" #include "debug_log.h"
#include "route_msg.h" #include "route_msg.h"
static void tcp_server_worker(struct netconn *conn, uint8_t link_index) #define TCP_SERVER_ACCEPT_TIMEOUT_MS 100
#define TCP_SERVER_STOP_POLL_MS 50u
static BaseType_t tcp_server_stop_requested(void)
{
return (app_network_task_stop_requested() != pdFALSE) ? pdTRUE : pdFALSE;
}
static BaseType_t tcp_server_delay_with_stop(uint32_t delay_ms)
{
uint32_t remaining_ms = delay_ms;
while (remaining_ms > 0u) {
uint32_t slice_ms = (remaining_ms > TCP_SERVER_STOP_POLL_MS) ? TCP_SERVER_STOP_POLL_MS : remaining_ms;
if (tcp_server_stop_requested() != pdFALSE) {
return pdFALSE;
}
vTaskDelay(pdMS_TO_TICKS(slice_ms));
remaining_ms -= slice_ms;
}
return (tcp_server_stop_requested() == pdFALSE) ? pdTRUE : pdFALSE;
}
static err_t tcp_server_worker(struct netconn *conn, uint8_t link_index)
{ {
struct netbuf *buf; struct netbuf *buf;
const device_config_t *cfg = config_get(); const device_config_t *cfg = config_get();
@@ -19,37 +45,60 @@ static void tcp_server_worker(struct netconn *conn, uint8_t link_index)
uint8_t src_endpoint = config_link_index_to_endpoint(link_index); uint8_t src_endpoint = config_link_index_to_endpoint(link_index);
err_t err; err_t err;
route_msg_t *tx_msg; route_msg_t *tx_msg;
route_send_result_t route_result;
netconn_set_recvtimeout(conn, 10); netconn_set_recvtimeout(conn, 10);
for (;;) { for (;;) {
if (tcp_server_stop_requested() != pdFALSE) {
return ERR_CLSD;
}
err = netconn_recv(conn, &buf); err = netconn_recv(conn, &buf);
if (err == ERR_OK) { if (err == ERR_OK) {
do { do {
void *data; void *data;
uint16_t len; uint16_t len;
netbuf_data(buf, &data, &len); netbuf_data(buf, &data, &len);
(void)route_send(xTcpRxQueue, route_result = route_send(xTcpRxQueue,
src_endpoint, src_endpoint,
uart_endpoint, uart_endpoint,
(link_index == CONFIG_LINK_S1) ? ROUTE_CONN_S1 : ROUTE_CONN_S2, (link_index == CONFIG_LINK_S1) ? ROUTE_CONN_S1 : ROUTE_CONN_S2,
(const uint8_t *)data, (const uint8_t *)data,
len, len,
pdMS_TO_TICKS(10)); pdMS_TO_TICKS(10));
if (route_result != ROUTE_SEND_OK) {
debug_log_printf("[SRV] idx=%u rx-route-fail rc=%s len=%u\r\n",
(unsigned int)link_index,
route_send_result_to_str(route_result),
(unsigned int)len);
netbuf_delete(buf);
return ERR_CLSD;
}
} while (netbuf_next(buf) >= 0); } while (netbuf_next(buf) >= 0);
netbuf_delete(buf); netbuf_delete(buf);
} else if (err != ERR_TIMEOUT) { } else if (err == ERR_TIMEOUT) {
if (tcp_server_stop_requested() != pdFALSE) {
return ERR_CLSD;
}
} else {
break; break;
} }
while (xQueueReceive(xLinkTxQueues[link_index], &tx_msg, 0) == pdPASS) { while (xQueueReceive(xLinkTxQueues[link_index], &tx_msg, 0) == pdPASS) {
if (tcp_server_stop_requested() != pdFALSE) {
route_msg_free(tx_msg);
return ERR_CLSD;
}
err = netconn_write(conn, tx_msg->data, tx_msg->len, NETCONN_COPY); err = netconn_write(conn, tx_msg->data, tx_msg->len, NETCONN_COPY);
route_msg_free(tx_msg); route_msg_free(tx_msg);
if (err != ERR_OK) { if (err != ERR_OK) {
return; return err;
} }
} }
} }
return err;
} }
static void tcp_server_task(uint8_t link_index) static void tcp_server_task(uint8_t link_index)
@@ -61,31 +110,49 @@ static void tcp_server_task(uint8_t link_index)
netconn_thread_init(); netconn_thread_init();
for (;;) { for (;;) {
if (tcp_server_stop_requested() != pdFALSE) {
break;
}
while (g_netif_ready == pdFALSE) { while (g_netif_ready == pdFALSE) {
if (tcp_server_stop_requested() != pdFALSE) {
goto exit_task;
}
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
} }
cfg = config_get(); cfg = config_get();
if (cfg->links[link_index].enabled == 0u) { if (cfg->links[link_index].enabled == 0u) {
vTaskDelay(pdMS_TO_TICKS(500)); if (tcp_server_stop_requested() != pdFALSE) {
break;
}
if (tcp_server_delay_with_stop(500u) == pdFALSE) {
break;
}
continue; continue;
} }
listener = netconn_new(NETCONN_TCP); listener = netconn_new(NETCONN_TCP);
if (listener == NULL) { if (listener == NULL) {
vTaskDelay(pdMS_TO_TICKS(500)); if (tcp_server_delay_with_stop(500u) == pdFALSE) {
break;
}
continue; continue;
} }
netconn_set_recvtimeout(listener, TCP_SERVER_ACCEPT_TIMEOUT_MS);
if (netconn_bind(listener, IP_ADDR_ANY, cfg->links[link_index].local_port) != ERR_OK || if (netconn_bind(listener, IP_ADDR_ANY, cfg->links[link_index].local_port) != ERR_OK ||
netconn_listen(listener) != ERR_OK) { netconn_listen(listener) != ERR_OK) {
netconn_delete(listener); netconn_delete(listener);
vTaskDelay(pdMS_TO_TICKS(500)); if (tcp_server_delay_with_stop(500u) == pdFALSE) {
break;
}
continue; continue;
} }
for (;;) { for (;;) {
if (cfg->links[link_index].enabled == 0u) { if (tcp_server_stop_requested() != pdFALSE || cfg->links[link_index].enabled == 0u) {
break; break;
} }
if (netconn_accept(listener, &newconn) == ERR_OK) { if (netconn_accept(listener, &newconn) == ERR_OK) {
@@ -98,6 +165,11 @@ static void tcp_server_task(uint8_t link_index)
netconn_close(listener); netconn_close(listener);
netconn_delete(listener); netconn_delete(listener);
} }
exit_task:
netconn_thread_cleanup();
app_on_network_task_exit(xTaskGetCurrentTaskHandle());
vTaskDelete(NULL);
} }
void TcpSrvTask_S1(void *argument) void TcpSrvTask_S1(void *argument)
+232 -46
View File
@@ -32,10 +32,27 @@ typedef struct {
volatile uint16_t tx_tail; volatile uint16_t tx_tail;
volatile uint16_t tx_dma_len; volatile uint16_t tx_dma_len;
volatile uint8_t tx_busy; volatile uint8_t tx_busy;
volatile uint8_t tx_kick_fail_logged;
} uart_channel_ctx_t; } uart_channel_ctx_t;
static uart_channel_ctx_t g_channels[UART_CHANNEL_MAX]; static uart_channel_ctx_t g_channels[UART_CHANNEL_MAX];
const char *uart_trans_send_result_to_str(uart_trans_send_result_t result)
{
switch (result) {
case UART_TRANS_SEND_OK:
return "ok";
case UART_TRANS_SEND_INVALID_INPUT:
return "invalid";
case UART_TRANS_SEND_RING_FULL:
return "full";
case UART_TRANS_SEND_KICK_FAILED:
return "kick";
default:
return "unknown";
}
}
static uint16_t ring_used(uint16_t head, uint16_t tail, uint16_t size) static uint16_t ring_used(uint16_t head, uint16_t tail, uint16_t size)
{ {
return (head >= tail) ? (head - tail) : (size - tail + head); return (head >= tail) ? (head - tail) : (size - tail + head);
@@ -66,20 +83,21 @@ static void process_rx_snapshot(uart_channel_t channel)
} }
} }
static void kick_tx(uart_channel_t channel) static uart_trans_send_result_t kick_tx(uart_channel_t channel)
{ {
uart_channel_ctx_t *ctx = &g_channels[channel]; uart_channel_ctx_t *ctx = &g_channels[channel];
uint16_t available; uint16_t available;
uint16_t chunk; uint16_t chunk;
uint16_t tail;
uint16_t i; uint16_t i;
if (ctx->tx_busy != 0u) { if (ctx->tx_busy != 0u) {
return; return UART_TRANS_SEND_OK;
} }
available = ring_used(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE); available = ring_used(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE);
if (available == 0u) { if (available == 0u) {
return; return UART_TRANS_SEND_OK;
} }
chunk = available; chunk = available;
@@ -87,16 +105,28 @@ static void kick_tx(uart_channel_t channel)
chunk = UART_TX_DMA_BUFFER_SIZE; chunk = UART_TX_DMA_BUFFER_SIZE;
} }
tail = ctx->tx_tail;
for (i = 0; i < chunk; ++i) { for (i = 0; i < chunk; ++i) {
ctx->tx_dma_buffer[i] = ctx->tx_ring[ctx->tx_tail]; ctx->tx_dma_buffer[i] = ctx->tx_ring[tail];
ctx->tx_tail = (uint16_t)((ctx->tx_tail + 1u) % UART_TX_RING_BUFFER_SIZE); tail = (uint16_t)((tail + 1u) % UART_TX_RING_BUFFER_SIZE);
} }
if (HAL_UART_Transmit_DMA(ctx->huart, ctx->tx_dma_buffer, chunk) != HAL_OK) {
ctx->tx_dma_len = 0u;
if (ctx->tx_kick_fail_logged == 0u) {
debug_log_printf("[UART] kick-fail ch=%u len=%u\r\n",
(unsigned int)channel,
(unsigned int)chunk);
ctx->tx_kick_fail_logged = 1u;
}
return UART_TRANS_SEND_KICK_FAILED;
}
ctx->tx_tail = tail;
ctx->tx_dma_len = chunk; ctx->tx_dma_len = chunk;
ctx->tx_busy = 1u; ctx->tx_busy = 1u;
if (HAL_UART_Transmit_DMA(ctx->huart, ctx->tx_dma_buffer, chunk) != HAL_OK) { ctx->tx_kick_fail_logged = 0u;
ctx->tx_busy = 0u; return UART_TRANS_SEND_OK;
}
} }
static uint16_t uart_ring_available(uart_channel_t channel) static uint16_t uart_ring_available(uart_channel_t channel)
@@ -143,6 +173,7 @@ static void uart_route_raw_channel(uart_channel_t channel)
uint16_t len; uint16_t len;
uint8_t uart_endpoint = (channel == UART_CHANNEL_U1) ? ENDPOINT_UART3 : ENDPOINT_UART2; uint8_t uart_endpoint = (channel == UART_CHANNEL_U1) ? ENDPOINT_UART3 : ENDPOINT_UART2;
uint32_t i; uint32_t i;
route_send_result_t route_result;
len = uart_ring_read(channel, buffer, sizeof(buffer)); len = uart_ring_read(channel, buffer, sizeof(buffer));
if (len == 0u) { if (len == 0u) {
@@ -154,38 +185,143 @@ static void uart_route_raw_channel(uart_channel_t channel)
continue; continue;
} }
(void)route_send(xLinkTxQueues[i], route_result = route_send(xLinkTxQueues[i],
uart_endpoint, uart_endpoint,
config_link_index_to_endpoint((uint8_t)i), config_link_index_to_endpoint((uint8_t)i),
(channel == UART_CHANNEL_U1) ? ROUTE_CONN_UART3 : ROUTE_CONN_UART2, (channel == UART_CHANNEL_U1) ? ROUTE_CONN_UART3 : ROUTE_CONN_UART2,
buffer, buffer,
len, len,
pdMS_TO_TICKS(10)); pdMS_TO_TICKS(10));
if (route_result != ROUTE_SEND_OK) {
debug_log_printf("[UART] raw-route-fail idx=%u rc=%s len=%u\r\n",
(unsigned int)i,
route_send_result_to_str(route_result),
(unsigned int)len);
}
} }
} }
static void uart_send_tcp_msg_to_uarts(route_msg_t *msg) static uart_trans_send_result_t uart_send_tcp_msg_chunk(route_msg_t *msg,
uint16_t offset,
uint16_t *accepted_len)
{ {
uint8_t frame[ROUTE_MSG_MAX_PAYLOAD + 6u]; uint8_t frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
uint16_t frame_len = 0u; uint16_t frame_len = 0u;
uint16_t remaining;
uint16_t chunk_len;
uint8_t uart_mask;
uart_trans_send_result_t uart_result;
if ((msg->dst_mask & ENDPOINT_UART2) != 0u) { if (accepted_len == NULL || msg == NULL || offset >= msg->len) {
if (config_get()->mux_mode == MUX_MODE_FRAME) { return UART_TRANS_SEND_INVALID_INPUT;
if (uart_mux_encode_frame(msg->src_id, ENDPOINT_UART2, msg->data, msg->len, frame, &frame_len, sizeof(frame))) {
(void)uart_trans_send_buffer(UART_CHANNEL_U0, frame, frame_len);
}
} else {
(void)uart_trans_send_buffer(UART_CHANNEL_U0, msg->data, msg->len);
}
} }
if ((msg->dst_mask & ENDPOINT_UART3) != 0u) { *accepted_len = 0u;
if (config_get()->mux_mode == MUX_MODE_FRAME) {
if (uart_mux_encode_frame(msg->src_id, ENDPOINT_UART3, msg->data, msg->len, frame, &frame_len, sizeof(frame))) { uart_mask = (uint8_t)(msg->dst_mask & (ENDPOINT_UART2 | ENDPOINT_UART3));
(void)uart_trans_send_buffer(UART_CHANNEL_U1, frame, frame_len); if ((msg->dst_mask != uart_mask) ||
(uart_mask != ENDPOINT_UART2 && uart_mask != ENDPOINT_UART3)) {
return UART_TRANS_SEND_INVALID_INPUT;
} }
} else {
(void)uart_trans_send_buffer(UART_CHANNEL_U1, msg->data, msg->len); remaining = (uint16_t)(msg->len - offset);
if (uart_mask == ENDPOINT_UART2) {
if (config_get()->mux_mode == MUX_MODE_FRAME) {
chunk_len = remaining;
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u)) {
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u);
}
if (!uart_mux_encode_frame(msg->src_id, ENDPOINT_UART2, &msg->data[offset], chunk_len, frame, &frame_len, sizeof(frame))) {
return UART_TRANS_SEND_INVALID_INPUT;
}
uart_result = uart_trans_send_buffer(UART_CHANNEL_U0, frame, frame_len);
if (uart_result != UART_TRANS_SEND_OK) {
return uart_result;
}
*accepted_len = chunk_len;
return UART_TRANS_SEND_OK;
}
chunk_len = remaining;
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u)) {
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u);
}
uart_result = uart_trans_send_buffer(UART_CHANNEL_U0, &msg->data[offset], chunk_len);
if (uart_result != UART_TRANS_SEND_OK) {
return uart_result;
}
*accepted_len = chunk_len;
return UART_TRANS_SEND_OK;
}
if (config_get()->mux_mode == MUX_MODE_FRAME) {
chunk_len = remaining;
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u)) {
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u);
}
if (!uart_mux_encode_frame(msg->src_id, ENDPOINT_UART3, &msg->data[offset], chunk_len, frame, &frame_len, sizeof(frame))) {
return UART_TRANS_SEND_INVALID_INPUT;
}
uart_result = uart_trans_send_buffer(UART_CHANNEL_U1, frame, frame_len);
if (uart_result != UART_TRANS_SEND_OK) {
return uart_result;
}
*accepted_len = chunk_len;
return UART_TRANS_SEND_OK;
}
chunk_len = remaining;
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u)) {
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u);
}
uart_result = uart_trans_send_buffer(UART_CHANNEL_U1, &msg->data[offset], chunk_len);
if (uart_result != UART_TRANS_SEND_OK) {
return uart_result;
}
*accepted_len = chunk_len;
return UART_TRANS_SEND_OK;
}
static void uart_try_advance_pending_tcp_msg(route_msg_t **pending_tcp_msg,
uint16_t *pending_tcp_offset,
uart_trans_send_result_t *pending_tcp_result)
{
route_msg_t *msg;
uart_trans_send_result_t uart_result;
uint16_t accepted_len;
if (pending_tcp_msg == NULL || pending_tcp_offset == NULL || pending_tcp_result == NULL) {
return;
}
msg = *pending_tcp_msg;
if (msg == NULL) {
return;
}
for (;;) {
accepted_len = 0u;
uart_result = uart_send_tcp_msg_chunk(msg, *pending_tcp_offset, &accepted_len);
if (uart_result != UART_TRANS_SEND_OK) {
if (uart_result != *pending_tcp_result) {
debug_log_printf("[UART] tcp-pend src=0x%02X dst=0x%02X off=%u rc=%s\r\n",
(unsigned int)msg->src_id,
(unsigned int)msg->dst_mask,
(unsigned int)(*pending_tcp_offset),
uart_trans_send_result_to_str(uart_result));
*pending_tcp_result = uart_result;
}
break;
}
*pending_tcp_offset = (uint16_t)(*pending_tcp_offset + accepted_len);
*pending_tcp_result = UART_TRANS_SEND_OK;
if (*pending_tcp_offset >= msg->len) {
route_msg_free(msg);
*pending_tcp_msg = NULL;
*pending_tcp_offset = 0u;
break;
} }
} }
} }
@@ -194,18 +330,26 @@ static void uart_route_mux_frame(uart_channel_t source_channel, const uart_mux_f
{ {
const device_config_t *cfg = config_get(); const device_config_t *cfg = config_get();
uint32_t i; uint32_t i;
uint8_t endpoint;
uint8_t source_conn = (source_channel == UART_CHANNEL_U1) ? ROUTE_CONN_UART3 : ROUTE_CONN_UART2; uint8_t source_conn = (source_channel == UART_CHANNEL_U1) ? ROUTE_CONN_UART3 : ROUTE_CONN_UART2;
uint8_t out_frame[ROUTE_MSG_MAX_PAYLOAD + 6u]; uint8_t out_frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
uint16_t out_frame_len = 0u; uint16_t out_frame_len = 0u;
route_send_result_t route_result;
uart_trans_send_result_t uart_result;
if (frame->dst_mask == 0u) { if (frame->dst_mask == 0u) {
(void)route_send(xConfigQueue, route_result = route_send(xConfigQueue,
frame->src_id, frame->src_id,
0u, 0u,
source_conn, source_conn,
frame->payload, frame->payload,
frame->payload_len, frame->payload_len,
pdMS_TO_TICKS(10)); pdMS_TO_TICKS(10));
if (route_result != ROUTE_SEND_OK) {
debug_log_printf("[UART] mux-cfg-fail rc=%s len=%u\r\n",
route_send_result_to_str(route_result),
(unsigned int)frame->payload_len);
}
return; return;
} }
@@ -213,21 +357,41 @@ static void uart_route_mux_frame(uart_channel_t source_channel, const uart_mux_f
if (cfg->links[i].enabled == 0u) { if (cfg->links[i].enabled == 0u) {
continue; continue;
} }
uint8_t endpoint = config_link_index_to_endpoint((uint8_t)i); endpoint = config_link_index_to_endpoint((uint8_t)i);
if ((frame->dst_mask & endpoint) != 0u) { if ((frame->dst_mask & endpoint) != 0u) {
(void)route_send(xLinkTxQueues[i], frame->src_id, endpoint, source_conn, frame->payload, frame->payload_len, pdMS_TO_TICKS(10)); route_result = route_send(xLinkTxQueues[i], frame->src_id, endpoint, source_conn, frame->payload, frame->payload_len, pdMS_TO_TICKS(10));
if (route_result != ROUTE_SEND_OK) {
debug_log_printf("[UART] mux-route-fail idx=%u rc=%s len=%u\r\n",
(unsigned int)i,
route_send_result_to_str(route_result),
(unsigned int)frame->payload_len);
}
} }
} }
if ((frame->dst_mask & ENDPOINT_UART2) != 0u && source_channel != UART_CHANNEL_U0) { if ((frame->dst_mask & ENDPOINT_UART2) != 0u && source_channel != UART_CHANNEL_U0) {
if (uart_mux_encode_frame(frame->src_id, ENDPOINT_UART2, frame->payload, frame->payload_len, out_frame, &out_frame_len, sizeof(out_frame))) { if (uart_mux_encode_frame(frame->src_id, ENDPOINT_UART2, frame->payload, frame->payload_len, out_frame, &out_frame_len, sizeof(out_frame))) {
(void)uart_trans_send_buffer(UART_CHANNEL_U0, out_frame, out_frame_len); uart_result = uart_trans_send_buffer(UART_CHANNEL_U0, out_frame, out_frame_len);
if (uart_result != UART_TRANS_SEND_OK) {
debug_log_printf("[UART] mux-u0-tx-fail rc=%s len=%u\r\n",
uart_trans_send_result_to_str(uart_result),
(unsigned int)out_frame_len);
}
} else {
debug_log_printf("[UART] mux-u0-enc-fail len=%u\r\n", (unsigned int)frame->payload_len);
} }
} }
if ((frame->dst_mask & ENDPOINT_UART3) != 0u && source_channel != UART_CHANNEL_U1) { if ((frame->dst_mask & ENDPOINT_UART3) != 0u && source_channel != UART_CHANNEL_U1) {
if (uart_mux_encode_frame(frame->src_id, ENDPOINT_UART3, frame->payload, frame->payload_len, out_frame, &out_frame_len, sizeof(out_frame))) { if (uart_mux_encode_frame(frame->src_id, ENDPOINT_UART3, frame->payload, frame->payload_len, out_frame, &out_frame_len, sizeof(out_frame))) {
(void)uart_trans_send_buffer(UART_CHANNEL_U1, out_frame, out_frame_len); uart_result = uart_trans_send_buffer(UART_CHANNEL_U1, out_frame, out_frame_len);
if (uart_result != UART_TRANS_SEND_OK) {
debug_log_printf("[UART] mux-u1-tx-fail rc=%s len=%u\r\n",
uart_trans_send_result_to_str(uart_result),
(unsigned int)out_frame_len);
}
} else {
debug_log_printf("[UART] mux-u1-enc-fail len=%u\r\n", (unsigned int)frame->payload_len);
} }
} }
} }
@@ -241,13 +405,6 @@ int uart_trans_init(void)
return 0; return 0;
} }
int uart_trans_config(uint8_t uart_index, uint32_t baudrate)
{
UART_HandleTypeDef *huart = (uart_index == LINK_UART_U1) ? &huart3 : &huart2;
huart->Init.BaudRate = baudrate;
return (HAL_UART_Init(huart) == HAL_OK) ? 0 : -1;
}
int uart_trans_start_all(void) int uart_trans_start_all(void)
{ {
uint32_t i; uint32_t i;
@@ -273,22 +430,44 @@ int uart_trans_start_all(void)
return 0; return 0;
} }
bool uart_trans_send_buffer(uart_channel_t channel, const uint8_t *data, uint16_t len) uart_trans_send_result_t uart_trans_send_buffer(uart_channel_t channel, const uint8_t *data, uint16_t len)
{ {
uart_channel_ctx_t *ctx = &g_channels[channel]; uart_channel_ctx_t *ctx;
uart_trans_send_result_t uart_result;
uint16_t original_head;
uint16_t written = 0u; uint16_t written = 0u;
if (data == NULL || len == 0u) { if (channel >= UART_CHANNEL_MAX || data == NULL || len == 0u || len >= UART_TX_RING_BUFFER_SIZE) {
return false; return UART_TRANS_SEND_INVALID_INPUT;
} }
while (written < len && ring_free(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE) > 0u) { ctx = &g_channels[channel];
if (ctx->huart == NULL) {
return UART_TRANS_SEND_INVALID_INPUT;
}
taskENTER_CRITICAL();
original_head = ctx->tx_head;
if (ring_free(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE) < len) {
taskEXIT_CRITICAL();
return UART_TRANS_SEND_RING_FULL;
}
while (written < len) {
ctx->tx_ring[ctx->tx_head] = data[written++]; ctx->tx_ring[ctx->tx_head] = data[written++];
ctx->tx_head = (uint16_t)((ctx->tx_head + 1u) % UART_TX_RING_BUFFER_SIZE); ctx->tx_head = (uint16_t)((ctx->tx_head + 1u) % UART_TX_RING_BUFFER_SIZE);
} }
kick_tx(channel); uart_result = kick_tx(channel);
return (written == len); if (uart_result != UART_TRANS_SEND_OK) {
ctx->tx_head = original_head;
taskEXIT_CRITICAL();
return uart_result;
}
taskEXIT_CRITICAL();
return UART_TRANS_SEND_OK;
} }
void uart_trans_notify_rx_from_isr(uart_channel_t channel, BaseType_t *xHigherPriorityTaskWoken) void uart_trans_notify_rx_from_isr(uart_channel_t channel, BaseType_t *xHigherPriorityTaskWoken)
@@ -412,8 +591,11 @@ void UartRxTask(void *argument)
uint32_t notify_value; uint32_t notify_value;
BaseType_t notified; BaseType_t notified;
route_msg_t *msg; route_msg_t *msg;
route_msg_t *pending_tcp_msg = NULL;
uint16_t pending_tcp_offset = 0u;
uart_mux_frame_t frame; uart_mux_frame_t frame;
const device_config_t *cfg; const device_config_t *cfg;
uart_trans_send_result_t pending_tcp_result = UART_TRANS_SEND_OK;
(void)argument; (void)argument;
if (uart_trans_start_all() != 0) { if (uart_trans_start_all() != 0) {
@@ -439,9 +621,13 @@ void UartRxTask(void *argument)
g_channels[UART_CHANNEL_U1].tx_busy = 0u; g_channels[UART_CHANNEL_U1].tx_busy = 0u;
} }
while (xQueueReceive(xTcpRxQueue, &msg, 0) == pdPASS) { uart_try_advance_pending_tcp_msg(&pending_tcp_msg, &pending_tcp_offset, &pending_tcp_result);
uart_send_tcp_msg_to_uarts(msg);
route_msg_free(msg); while (pending_tcp_msg == NULL && xQueueReceive(xTcpRxQueue, &msg, 0) == pdPASS) {
pending_tcp_msg = msg;
pending_tcp_offset = 0u;
pending_tcp_result = UART_TRANS_SEND_OK;
uart_try_advance_pending_tcp_msg(&pending_tcp_msg, &pending_tcp_offset, &pending_tcp_result);
} }
cfg = config_get(); cfg = config_get();
+9 -2
View File
@@ -16,6 +16,13 @@ typedef enum {
UART_CHANNEL_MAX UART_CHANNEL_MAX
} uart_channel_t; } uart_channel_t;
typedef enum {
UART_TRANS_SEND_OK = 0,
UART_TRANS_SEND_INVALID_INPUT,
UART_TRANS_SEND_RING_FULL,
UART_TRANS_SEND_KICK_FAILED
} uart_trans_send_result_t;
typedef struct { typedef struct {
uint8_t src_id; uint8_t src_id;
uint8_t dst_mask; uint8_t dst_mask;
@@ -29,9 +36,9 @@ typedef struct {
#define UART_TX_RING_BUFFER_SIZE 256u #define UART_TX_RING_BUFFER_SIZE 256u
int uart_trans_init(void); int uart_trans_init(void);
int uart_trans_config(uint8_t uart_index, uint32_t baudrate);
int uart_trans_start_all(void); int uart_trans_start_all(void);
bool uart_trans_send_buffer(uart_channel_t channel, const uint8_t *data, uint16_t len); const char *uart_trans_send_result_to_str(uart_trans_send_result_t result);
uart_trans_send_result_t uart_trans_send_buffer(uart_channel_t channel, const uint8_t *data, uint16_t len);
void uart_trans_notify_rx_from_isr(uart_channel_t channel, BaseType_t *xHigherPriorityTaskWoken); void uart_trans_notify_rx_from_isr(uart_channel_t channel, BaseType_t *xHigherPriorityTaskWoken);
void uart_trans_tx_cplt_handler(uart_channel_t channel); void uart_trans_tx_cplt_handler(uart_channel_t channel);
void UartRxTask(void *argument); void UartRxTask(void *argument);
+245
View File
@@ -0,0 +1,245 @@
# CH390 / lwIP 固定次数 ping 失败问题修复复盘
## 1. 问题现象
在 TCP2UART 固件运行后,设备初期可以正常 ARP 和 ping,但连续 ping 一段时间后不再响应。
典型现象:
- 设备 IP`192.168.31.100`
- 设备 MAC`02:00:00:00:00:01`
- 对端/网关 IP`192.168.31.1`
- 对端/网关 MAC`00:e0:4c:28:1e:60`
- 失败后设备仍持续发送 TCP SYN/RST 或 client timeout 相关流量,说明 TX、任务调度和应用层并未整体死机。
- 失败后对端继续向设备 MAC 发送 ICMP/ARP,但设备不再回复。
关键抓包:
- `WiresharkLog/04290150.pcapng`
- `seq=1884..1891` 共 8 次 ping reply 正常。
- 第 9 次 `seq=1892` 开始无 reply。
- `WiresharkLog/04290206.pcapng`
- 曾把 `PBUF_POOL_SIZE` / `MEMP_NUM_TCPIP_MSG_INPKT` 从 8 临时扩大到 16。
- 成功 ping 从 8 次变为 `seq=1900..1915` 共 16 次。
- 第 17 次 `seq=1916` 开始无 reply。
这个“成功次数随池大小移动”的现象证明:问题不是 CH390 随机丢包,也不是 PHY/TX 死掉,而是每次成功处理 ping 后都有某个 pbuf 引用没有释放,最终耗尽 `PBUF_POOL`
## 2. 排查过程中的重要结论
### 2.1 CH390 RX 读包路径曾存在风险,但不是最终根因
早期排查时发现 CH390 RX 路径与参考驱动存在若干不一致,已修正:
- `ch390_receive_packet()` 按参考序列读取 RX ready:先读 `MRCMDX` dummy,再读 `MRCMDX1`
- 校验 RX header 的 `Head` 字节必须为 `CH390_PKT_RDY`
- CH390 RX SRAM 中的 `rx_len` 包含 Ethernet FCS,交给 lwIP 前需要减去 4 字节。
- `ch390_rx_reset()` 显式写 `MPTRCR_RST_RX` 复位 RX memory pointer。
这些修正确保 CH390 RX FIFO 读包更接近参考实现,但无法解释“固定 8 次/16 次后失败”。
### 2.2 扩大 lwIP 池只能延迟问题
曾临时将如下配置从 8 提到 16
```c
#define PBUF_POOL_SIZE 16
#define MEMP_NUM_PBUF 16
#define MEMP_NUM_TCPIP_MSG_INPKT 16
```
结果成功 ping 次数也从 8 变成 16。这说明扩大池子只是延迟耗尽,不能作为根修复。
最终已恢复为 8
```c
#define PBUF_POOL_SIZE 8
#define MEMP_NUM_PBUF 8
#define MEMP_NUM_TCPIP_MSG_INPKT 8
```
### 2.3 `tcpip_input()` 异步队列不是最终根因
项目启用了 lwIP core locking。为避免每个 RX 包占用 `MEMP_TCPIP_MSG_INPKT`,配置已改为同步输入:
```c
#define LWIP_TCPIP_CORE_LOCKING 1
#define LWIP_TCPIP_CORE_LOCKING_INPUT 1
```
这样 `tcpip_input()` 会在 core lock 下同步调用 `ethernet_input()`,不再通过 `TCPIP_MSG_INPKT` 邮箱异步排队。
但用户后续验证仍然固定 8 次后停止,且每次成功 ping 都已经有 reply,因此说明 RX 包确实已经进入 ICMP 处理路径,问题更可能是 reply 输出路径增加了 pbuf 引用但未释放。
## 3. 最终根因
最终根因位于:
```text
Drivers/LwIP/src/netif/ethernet.c
```
`ethernet_output()` 实现:
```c
q = pbuf_alloc(PBUF_RAW_TX, SIZEOF_ETH_HDR, PBUF_RAM);
if (q == NULL) {
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
return ERR_MEM;
}
pbuf_chain(q, p);
ethhdr = (struct eth_hdr *)q->payload;
SMEMCPY(&ethhdr->dest, dst, sizeof(struct eth_addr));
SMEMCPY(&ethhdr->src, src, sizeof(struct eth_addr));
ethhdr->type = lwip_htons(eth_type);
return netif->linkoutput(netif, q);
```
问题在 `pbuf_chain(q, p)`
lwIP 的 `pbuf_chain()` 会执行:
```c
pbuf_ref(t);
```
也就是给被挂接的原始 pbuf `p` 引用计数加 1。
ICMP echo reply 路径会复用 RX pbuf
```text
ethernetif_poll()
-> tcpip_input()
-> ethernet_input()
-> ip4_input()
-> icmp_input()
-> ip4_output_if()
-> etharp_output()
-> ethernet_output()
```
`icmp_input()` 末尾本身会 `pbuf_free(p)`,这部分是正确的。但在原实现中,`ethernet_output()` 通过 `pbuf_chain(q, p)``p` 额外加了一次引用,却没有在 `linkoutput()` 返回后释放临时 header pbuf `q`
因此每次 ping 的引用计数变化是:
```text
RX pbuf 初始 ref = 1
pbuf_chain(q, p) 后 ref = 2
icmp_input() 末尾 pbuf_free(p) 后 ref = 1
=> p 永远没有回到 0PBUF_POOL 泄漏 1 个
```
所以:
- `PBUF_POOL_SIZE=8` 时,8 次 ping reply 后耗尽。
- 临时扩大到 16 时,16 次 ping reply 后耗尽。
## 4. 修复方案
修复 `ethernet_output()`,在同步 `linkoutput()` 完成后释放临时 header pbuf 链:
```c
err_t ethernet_output(struct netif *netif,
struct pbuf *p,
const struct eth_addr *src,
const struct eth_addr *dst,
u16_t eth_type)
{
struct pbuf *q;
struct eth_hdr *ethhdr;
err_t err;
LWIP_ASSERT("netif != NULL", netif != NULL);
LWIP_ASSERT("p != NULL", p != NULL);
LWIP_ASSERT("src != NULL", src != NULL);
LWIP_ASSERT("dst != NULL", dst != NULL);
q = pbuf_alloc(PBUF_RAW_TX, SIZEOF_ETH_HDR, PBUF_RAM);
if (q == NULL) {
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
return ERR_MEM;
}
pbuf_chain(q, p);
ethhdr = (struct eth_hdr *)q->payload;
SMEMCPY(&ethhdr->dest, dst, sizeof(struct eth_addr));
SMEMCPY(&ethhdr->src, src, sizeof(struct eth_addr));
ethhdr->type = lwip_htons(eth_type);
err = netif->linkoutput(netif, q);
pbuf_free(q);
return err;
}
```
为什么这里可以释放 `q`
- 本项目 `low_level_output()` 是同步发送。
- 它会立即遍历 pbuf 链,把数据复制到 `s_tx_buffer`
- 随后调用 `ch390_runtime_send_packet()` 把连续 buffer 发给 CH390。
- `low_level_output()` 返回后不再持有 pbuf 指针。
因此 `ethernet_output()``linkoutput()` 返回后释放 `q` 是正确的。
`pbuf_free(q)` 会同时:
- 释放临时 Ethernet header pbuf `q`
- 解除 `pbuf_chain()` 对原始 RX pbuf `p` 增加的引用;
- 之后 `icmp_input()` 末尾的 `pbuf_free(p)` 可以真正把 RX pbuf 归还 `PBUF_POOL`
## 5. 不要做的错误修复
### 5.1 不要在 `netif->input()` 成功后手动释放 pbuf
驱动层当前逻辑是正确的:
```c
input_err = ch390_netif.input(p, &ch390_netif);
if (input_err != ERR_OK) {
pbuf_free(p);
}
```
`netif->input()` 返回 `ERR_OK` 时,pbuf ownership 已经交给 lwIP。此时驱动不能再 `pbuf_free(p)`,否则会造成 double-free 或 use-after-free。
### 5.2 不要只扩大 `PBUF_POOL_SIZE`
扩大池子只会让失败次数从 8 变 16、32……不会修复泄漏。
### 5.3 不要继续优先怀疑 CH390 PHY/TX
抓包中失败后设备仍持续发送 TCP SYN/RST,说明 TX 和任务仍活着。固定次数失败更符合 pbuf 引用泄漏。
## 6. 验证结果
修复后 Keil 构建通过:
```text
"TCP2UART\TCP2UART.axf" - 0 Error(s), 0 Warning(s).
Program Size: Code=93376 RO-data=2768 RW-data=456 ZI-data=56032
```
用户烧录验证后确认问题已修复。
## 7. 后续排查建议
如后续再次出现固定次数网络停止,优先检查:
1. 是否存在 `pbuf_chain()` / `pbuf_ref()` 后没有配对 `pbuf_free()` 的路径。
2. 是否有 ARP pending queue 长时间持有 pbuf。
3. 是否有 TCP `recvmbox` / 应用桥接队列背压长期持有 pbuf。
4. 是否有人在 `netif->input()` 成功后错误释放 pbuf,导致内存破坏。
推荐排查点:
- `Drivers/LwIP/src/netif/ethernet.c`
- `Drivers/LwIP/src/core/ipv4/icmp.c`
- `Drivers/LwIP/src/core/ipv4/etharp.c`
- `Drivers/LwIP/src/core/pbuf.c`
- `Drivers/LwIP/src/netif/ethernetif.c`
+1
View File
@@ -47,6 +47,7 @@ void MX_USART2_UART_Init(void);
void MX_USART3_UART_Init(void); void MX_USART3_UART_Init(void);
/* USER CODE BEGIN Prototypes */ /* USER CODE BEGIN Prototypes */
void USART_SetConfiguredBaudrates(uint32_t usart2_baudrate, uint32_t usart3_baudrate);
/* USER CODE END Prototypes */ /* USER CODE END Prototypes */
+69
View File
@@ -37,6 +37,8 @@ static TaskHandle_t xTcpCliTaskC1Handle = NULL;
static TaskHandle_t xTcpCliTaskC2Handle = NULL; static TaskHandle_t xTcpCliTaskC2Handle = NULL;
static TaskHandle_t xDefaultTaskHandle = NULL; static TaskHandle_t xDefaultTaskHandle = NULL;
static BaseType_t xNetworkTasksStarted = pdFALSE; static BaseType_t xNetworkTasksStarted = pdFALSE;
static volatile BaseType_t xNetworkTaskStopRequested = pdFALSE;
static volatile BaseType_t xNetworkRestartRequested = pdFALSE;
void app_start_network_tasks(void) void app_start_network_tasks(void)
{ {
@@ -49,6 +51,11 @@ void app_start_network_tasks(void)
return; return;
} }
if (xNetworkTaskStopRequested != pdFALSE) {
debug_log_write("[NET] start-network-tasks stop-pending\r\n");
return;
}
cfg = config_get(); cfg = config_get();
debug_log_printf("[NET] start-network-tasks enter free=%lu min=%lu\r\n", debug_log_printf("[NET] start-network-tasks enter free=%lu min=%lu\r\n",
@@ -107,6 +114,68 @@ void app_start_network_tasks(void)
#endif #endif
} }
void app_request_network_task_stop(void)
{
xNetworkTaskStopRequested = pdTRUE;
}
void app_clear_network_task_stop(void)
{
xNetworkTaskStopRequested = pdFALSE;
}
BaseType_t app_network_task_stop_requested(void)
{
return xNetworkTaskStopRequested;
}
BaseType_t app_network_tasks_are_stopped(void)
{
return (xTcpSrvTaskS1Handle == NULL &&
xTcpSrvTaskS2Handle == NULL &&
xTcpCliTaskC1Handle == NULL &&
xTcpCliTaskC2Handle == NULL) ? pdTRUE : pdFALSE;
}
void app_on_network_task_exit(TaskHandle_t task_handle)
{
taskENTER_CRITICAL();
if (task_handle == xTcpSrvTaskS1Handle) {
xTcpSrvTaskS1Handle = NULL;
} else if (task_handle == xTcpSrvTaskS2Handle) {
xTcpSrvTaskS2Handle = NULL;
} else if (task_handle == xTcpCliTaskC1Handle) {
xTcpCliTaskC1Handle = NULL;
} else if (task_handle == xTcpCliTaskC2Handle) {
xTcpCliTaskC2Handle = NULL;
}
if (xTcpSrvTaskS1Handle == NULL &&
xTcpSrvTaskS2Handle == NULL &&
xTcpCliTaskC1Handle == NULL &&
xTcpCliTaskC2Handle == NULL) {
xNetworkTasksStarted = pdFALSE;
}
taskEXIT_CRITICAL();
}
void app_request_network_restart(void)
{
xNetworkRestartRequested = pdTRUE;
}
void app_clear_network_restart_request(void)
{
xNetworkRestartRequested = pdFALSE;
}
BaseType_t app_network_restart_requested(void)
{
return xNetworkRestartRequested;
}
static void StartDefaultTask(void *argument) static void StartDefaultTask(void *argument)
{ {
BaseType_t iwdg_ready = pdFALSE; BaseType_t iwdg_ready = pdFALSE;
+8 -2
View File
@@ -76,9 +76,15 @@ void MX_GPIO_Init(void)
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* EXTI interrupt init*/ /* EXTI interrupt init
* Keep CH390 INT masked during early boot. PB0 may already be asserted at
* power-on, while the FreeRTOS semaphore is not created until
* MX_FREERTOS_Init(). The network driver enables EXTI0 after CH390 and the
* RTOS objects are ready.
*/
HAL_NVIC_DisableIRQ(EXTI0_IRQn);
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
HAL_NVIC_SetPriority(EXTI0_IRQn, 6, 0); HAL_NVIC_SetPriority(EXTI0_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
} }
+9 -2
View File
@@ -68,6 +68,7 @@ void MX_FREERTOS_Init(void);
/* USER CODE BEGIN PFP */ /* USER CODE BEGIN PFP */
static void CH390_HardwareReset(void); static void CH390_HardwareReset(void);
static void LED_Init(void); static void LED_Init(void);
static void ApplyConfiguredUartBaudrates(void);
void Debug_TrapWithRttHint(const char *tag); void Debug_TrapWithRttHint(const char *tag);
/* USER CODE END PFP */ /* USER CODE END PFP */
@@ -109,6 +110,12 @@ void LED_Toggle(void)
HAL_GPIO_TogglePin(LED_PORT, LED_PIN); HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
} }
static void ApplyConfiguredUartBaudrates(void)
{
USART_SetConfiguredBaudrates(config_get_uart_baudrate(LINK_UART_U0),
config_get_uart_baudrate(LINK_UART_U1));
}
/* USER CODE END 0 */ /* USER CODE END 0 */
/** /**
@@ -143,6 +150,8 @@ int main(void)
MX_GPIO_Init(); MX_GPIO_Init();
MX_DMA_Init(); MX_DMA_Init();
MX_USART1_UART_Init(); MX_USART1_UART_Init();
config_init();
ApplyConfiguredUartBaudrates();
MX_USART2_UART_Init(); MX_USART2_UART_Init();
MX_USART3_UART_Init(); MX_USART3_UART_Init();
MX_SPI1_Init(); MX_SPI1_Init();
@@ -155,8 +164,6 @@ int main(void)
/* CH390 硬件复位 */ /* CH390 硬件复位 */
CH390_HardwareReset(); CH390_HardwareReset();
/* Initialize configuration from Flash (fallback to defaults on invalid data) */
config_init();
debug_log_boot("config-ready"); debug_log_boot("config-ready");
/* USER CODE END 2 */ /* USER CODE END 2 */
+4
View File
@@ -2,6 +2,7 @@
#include "stm32f1xx_it.h" #include "stm32f1xx_it.h"
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "semphr.h"
#include "task.h" #include "task.h"
#include "app_runtime.h" #include "app_runtime.h"
@@ -101,9 +102,12 @@ void EXTI0_IRQHandler(void)
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) { if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) {
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
if ((xNetSemaphore != NULL) &&
(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING)) {
xSemaphoreGiveFromISR(xNetSemaphore, &xHigherPriorityTaskWoken); xSemaphoreGiveFromISR(xNetSemaphore, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
} }
}
} }
void SPI1_IRQHandler(void) void SPI1_IRQHandler(void)
+11 -2
View File
@@ -22,6 +22,9 @@
/* USER CODE BEGIN 0 */ /* USER CODE BEGIN 0 */
static uint32_t g_usart2_baudrate = 115200u;
static uint32_t g_usart3_baudrate = 115200u;
/* USER CODE END 0 */ /* USER CODE END 0 */
UART_HandleTypeDef huart1; UART_HandleTypeDef huart1;
@@ -76,7 +79,7 @@ void MX_USART2_UART_Init(void)
/* USER CODE END USART2_Init 1 */ /* USER CODE END USART2_Init 1 */
huart2.Instance = USART2; huart2.Instance = USART2;
huart2.Init.BaudRate = 115200; huart2.Init.BaudRate = g_usart2_baudrate;
huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Parity = UART_PARITY_NONE;
@@ -105,7 +108,7 @@ void MX_USART3_UART_Init(void)
/* USER CODE END USART3_Init 1 */ /* USER CODE END USART3_Init 1 */
huart3.Instance = USART3; huart3.Instance = USART3;
huart3.Init.BaudRate = 115200; huart3.Init.BaudRate = g_usart3_baudrate;
huart3.Init.WordLength = UART_WORDLENGTH_8B; huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1; huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE; huart3.Init.Parity = UART_PARITY_NONE;
@@ -396,4 +399,10 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
/* USER CODE BEGIN 1 */ /* USER CODE BEGIN 1 */
void USART_SetConfiguredBaudrates(uint32_t usart2_baudrate, uint32_t usart3_baudrate)
{
g_usart2_baudrate = usart2_baudrate;
g_usart3_baudrate = usart3_baudrate;
}
/* USER CODE END 1 */ /* USER CODE END 1 */
+100 -6
View File
@@ -12,6 +12,25 @@
#include "CH390.h" #include "CH390.h"
#include "CH390_Interface.h" #include "CH390_Interface.h"
#define CH390_EPCR_POLL_LIMIT 100000u
static int ch390_wait_epcr_ready(void)
{
uint32_t poll_count = CH390_EPCR_POLL_LIMIT;
while ((ch390_read_reg(CH390_EPCR) & 0x01u) != 0u)
{
if (poll_count == 0u)
{
ch390_write_reg(CH390_EPCR, 0x00u);
return -1;
}
--poll_count;
}
return 0;
}
void ch390_probe_rx_header(uint8_t *head) void ch390_probe_rx_header(uint8_t *head)
{ {
if (head == 0) if (head == 0)
@@ -22,6 +41,46 @@ void ch390_probe_rx_header(uint8_t *head)
ch390_read_mem(head, 4); ch390_read_mem(head, 4);
} }
int ch390_peek_packet(uint8_t *rx_status, uint16_t *rx_len)
{
uint8_t nsr;
uint8_t header[4];
uint16_t mrr;
if (rx_status != 0)
{
*rx_status = 0u;
}
if (rx_len != 0)
{
*rx_len = 0u;
}
nsr = ch390_read_reg(CH390_NSR);
if ((nsr & NSR_RXRDY) == 0u)
{
return 0;
}
mrr = (uint16_t)ch390_read_mrrl() | ((uint16_t)ch390_read_mrrh() << 8);
ch390_read_mem(header, 4);
ch390_write_reg(CH390_MRRL, (uint8_t)(mrr & 0xffu));
ch390_write_reg(CH390_MRRH, (uint8_t)((mrr >> 8) & 0xffu));
if (rx_status != 0)
{
*rx_status = header[1];
}
if (rx_len != 0)
{
*rx_len = (uint16_t)header[2] | ((uint16_t)header[3] << 8);
}
return 1;
}
/** /**
* @name ch390_receive_packet * @name ch390_receive_packet
* @brief Receive packet * @brief Receive packet
@@ -33,6 +92,7 @@ void ch390_probe_rx_header(uint8_t *head)
uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status) uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status)
{ {
uint8_t nsr; uint8_t nsr;
uint8_t ready;
uint16_t rx_len = 0; uint16_t rx_len = 0;
uint8_t ReceiveData[4]; uint8_t ReceiveData[4];
@@ -48,6 +108,18 @@ uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status)
return 0; return 0;
} }
(void)ch390_read_mrcmdx();
ready = ch390_read_mrcmdx1();
if (ready == 0u)
{
return 0;
}
if (ready != CH390_PKT_RDY)
{
ch390_rx_reset();
return 0;
}
ch390_read_mem(ReceiveData, 4); ch390_read_mem(ReceiveData, 4);
if (rx_status != 0) if (rx_status != 0)
@@ -56,15 +128,17 @@ uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status)
} }
rx_len = (uint16_t)ReceiveData[2] | ((uint16_t)ReceiveData[3] << 8); rx_len = (uint16_t)ReceiveData[2] | ((uint16_t)ReceiveData[3] << 8);
if (((ReceiveData[1] & 0x3Fu) != 0u) || if ((ReceiveData[0] != CH390_PKT_RDY) ||
(rx_len < 14u) || ((ReceiveData[1] & 0x3Fu) != 0u) ||
(rx_len < (uint16_t)(14u + CH390_PKT_CRC_LEN)) ||
(rx_len > CH390_PKT_MAX)) (rx_len > CH390_PKT_MAX))
{ {
ch390_rx_reset();
return 0; return 0;
} }
ch390_read_mem(buff, rx_len); ch390_read_mem(buff, rx_len);
return rx_len; return (uint32_t)(rx_len - CH390_PKT_CRC_LEN);
} }
/** /**
@@ -125,6 +199,17 @@ void ch390_drop_packet(uint16_t len)
ch390_write_reg(CH390_MRRH, (mdr >> 8) & 0xff); ch390_write_reg(CH390_MRRH, (mdr >> 8) & 0xff);
} }
void ch390_rx_reset(void)
{
uint8_t rcr = ch390_read_reg(CH390_RCR);
ch390_write_reg(CH390_RCR, (uint8_t)(rcr & (uint8_t)(~RCR_RXEN)));
ch390_write_reg(CH390_MPTRCR, MPTRCR_RST_RX);
ch390_write_reg(CH390_NSR, NSR_RXOV);
ch390_write_reg(CH390_ISR, (uint8_t)(ISR_ROS | ISR_ROO | ISR_PR));
ch390_write_reg(CH390_RCR, (uint8_t)(rcr | RCR_RXEN));
}
/** /**
* @name ch390_read_phy * @name ch390_read_phy
* @brief Read PHY register * @brief Read PHY register
@@ -135,7 +220,10 @@ uint16_t ch390_read_phy(uint8_t reg)
ch390_write_reg(CH390_EPAR, CH390_PHY | reg); ch390_write_reg(CH390_EPAR, CH390_PHY | reg);
// Chose PHY, send read command // Chose PHY, send read command
ch390_write_reg(CH390_EPCR, EPCR_ERPRR | EPCR_EPOS); ch390_write_reg(CH390_EPCR, EPCR_ERPRR | EPCR_EPOS);
while(ch390_read_reg(CH390_EPCR) & 0x01); if (ch390_wait_epcr_ready() != 0)
{
return 0xFFFFu;
}
// Clear read command // Clear read command
ch390_write_reg(CH390_EPCR, 0x00); ch390_write_reg(CH390_EPCR, 0x00);
return (ch390_read_reg(CH390_EPDRH) << 8) | return (ch390_read_reg(CH390_EPDRH) << 8) |
@@ -155,7 +243,10 @@ void ch390_write_phy(uint8_t reg, uint16_t value)
ch390_write_reg(CH390_EPDRH, ((value >> 8) & 0xff)); // High byte ch390_write_reg(CH390_EPDRH, ((value >> 8) & 0xff)); // High byte
// Chose PHY, send write command // Chose PHY, send write command
ch390_write_reg(CH390_EPCR, 0x0A); ch390_write_reg(CH390_EPCR, 0x0A);
while(ch390_read_reg(CH390_EPCR) & 0x01); if (ch390_wait_epcr_ready() != 0)
{
return;
}
// Clear write command // Clear write command
ch390_write_reg(CH390_EPCR, 0x00); ch390_write_reg(CH390_EPCR, 0x00);
} }
@@ -173,7 +264,10 @@ void ch390_write_eeprom(uint8_t reg, uint16_t value)
ch390_write_reg(CH390_EPDRH, ((value >> 8) & 0xff)); // High byte ch390_write_reg(CH390_EPDRH, ((value >> 8) & 0xff)); // High byte
// Chose EEPROM, send write command // Chose EEPROM, send write command
ch390_write_reg(CH390_EPCR, EPCR_ERPRW); ch390_write_reg(CH390_EPCR, EPCR_ERPRW);
while(ch390_read_reg(CH390_EPCR) & 0x01); if (ch390_wait_epcr_ready() != 0)
{
return;
}
// Clear write command // Clear write command
ch390_write_reg(CH390_EPCR, 0x00); ch390_write_reg(CH390_EPCR, 0x00);
} }
+20
View File
@@ -150,6 +150,8 @@ enum ch390_phy_mode
#define CH390_BCASTCR 0x53 #define CH390_BCASTCR 0x53
#define CH390_INTCKCR 0x54 #define CH390_INTCKCR 0x54
#define CH390_MPTRCR 0x55 #define CH390_MPTRCR 0x55
#define MPTRCR_RST_TX (1<<1)
#define MPTRCR_RST_RX (1<<0)
#define CH390_MLEDCR 0x57 #define CH390_MLEDCR 0x57
#define CH390_MRCMDX 0x70 #define CH390_MRCMDX 0x70
#define CH390_MRCMDX1 0x71 #define CH390_MRCMDX1 0x71
@@ -302,6 +304,8 @@ enum ch390_phy_mode
#define CH390_RLENCR 0x52 #define CH390_RLENCR 0x52
#define CH390_BCASTCR 0x53 #define CH390_BCASTCR 0x53
#define CH390_MPTRCR 0x55 #define CH390_MPTRCR 0x55
#define MPTRCR_RST_TX (1<<1)
#define MPTRCR_RST_RX (1<<0)
#define CH390_MRCMDX 0xF0 #define CH390_MRCMDX 0xF0
#define CH390_MRCMDX1 0xF1 #define CH390_MRCMDX1 0xF1
#define CH390_MRCMD 0xF2 #define CH390_MRCMD 0xF2
@@ -356,6 +360,7 @@ enum ch390_phy_mode
#define CH390_PKT_NONE 0x00 /* No packet received */ #define CH390_PKT_NONE 0x00 /* No packet received */
#define CH390_PKT_RDY 0x01 /* Packet ready to receive */ #define CH390_PKT_RDY 0x01 /* Packet ready to receive */
#define CH390_PKT_ERR 0xFE /* Un-stable states */ #define CH390_PKT_ERR 0xFE /* Un-stable states */
#define CH390_PKT_CRC_LEN 4u /* Ethernet FCS stored in RX SRAM */
#define CH390_PKT_MAX 1536 /* Received packet max size */ #define CH390_PKT_MAX 1536 /* Received packet max size */
#define CH390_PKT_MIN 64 #define CH390_PKT_MIN 64
@@ -657,6 +662,21 @@ int ch390_runtime_link_up_from_status(const struct ch390_runtime_status *status)
*/ */
void ch390_probe_rx_header(uint8_t *head); void ch390_probe_rx_header(uint8_t *head);
/**
* @name ch390_peek_packet
* @brief Peek current RX header without consuming the packet.
* @param rx_status - Output abnormal status while receiving packet
* @param rx_len - Output packet length from RX header
* @return 0: no packet pending 1: header sampled
*/
int ch390_peek_packet(uint8_t *rx_status, uint16_t *rx_len);
/**
* @name ch390_rx_reset
* @brief Repair RX datapath after overflow/corruption without full chip reset.
*/
void ch390_rx_reset(void);
/** /**
* @name ch390_runtime_receive_packet * @name ch390_runtime_receive_packet
* @brief Runtime RX entry point for packet receive * @brief Runtime RX entry point for packet receive
+6 -13
View File
@@ -188,6 +188,8 @@ void ch390_interrupt_init(void)
/* EXTI0 is configured in CubeMX for PB0 */ /* EXTI0 is configured in CubeMX for PB0 */
/* NVIC priority should be >= configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY */ /* NVIC priority should be >= configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY */
/* for FreeRTOS compatibility */ /* for FreeRTOS compatibility */
HAL_NVIC_DisableIRQ(EXTI0_IRQn);
__HAL_GPIO_EXTI_CLEAR_IT(CH390_INT_PIN);
HAL_NVIC_SetPriority(EXTI0_IRQn, 6, 0); HAL_NVIC_SetPriority(EXTI0_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn); HAL_NVIC_EnableIRQ(EXTI0_IRQn);
} }
@@ -283,25 +285,16 @@ uint8_t ch390_read_reg(uint8_t reg)
static uint8_t ch390_read_rx_reg(uint8_t reg) static uint8_t ch390_read_rx_reg(uint8_t reg)
{ {
uint8_t tx_buf[3]; uint8_t value;
uint8_t rx_buf[3];
tx_buf[0] = OPC_MEM_DMY_R;
tx_buf[1] = reg;
tx_buf[2] = 0x00u;
CH390_SPI_ATOMIC_ENTER(); CH390_SPI_ATOMIC_ENTER();
ch390_cs(0); ch390_cs(0);
if (HAL_SPI_TransmitReceive(&hspi1, tx_buf, rx_buf, 3, SPI_TIMEOUT) != HAL_OK) ch390_spi_exchange_byte(reg | OPC_REG_R);
{ value = ch390_spi_dummy_read();
ch390_cs(1);
CH390_SPI_ATOMIC_EXIT();
return 0u;
}
ch390_cs(1); ch390_cs(1);
CH390_SPI_ATOMIC_EXIT(); CH390_SPI_ATOMIC_EXIT();
return rx_buf[2]; return value;
} }
uint8_t ch390_read_mrcmdx(void) uint8_t ch390_read_mrcmdx(void)
+4 -4
View File
@@ -30,28 +30,28 @@ uint8_t ch390_read_reg(uint8_t reg);
/** /**
* @name ch390_read_mrcmdx * @name ch390_read_mrcmdx
* @brief Read MRCMDX via memory-dummy-read opcode * @brief Read MRCMDX receive-ready latch
* @return Register value * @return Register value
*/ */
uint8_t ch390_read_mrcmdx(void); uint8_t ch390_read_mrcmdx(void);
/** /**
* @name ch390_read_mrcmdx1 * @name ch390_read_mrcmdx1
* @brief Read MRCMDX1 via memory-dummy-read opcode * @brief Read MRCMDX1 receive-ready latch
* @return Register value * @return Register value
*/ */
uint8_t ch390_read_mrcmdx1(void); uint8_t ch390_read_mrcmdx1(void);
/** /**
* @name ch390_read_mrrl * @name ch390_read_mrrl
* @brief Read MRRL via memory-dummy-read opcode * @brief Read MRRL receive memory pointer register
* @return Register value * @return Register value
*/ */
uint8_t ch390_read_mrrl(void); uint8_t ch390_read_mrrl(void);
/** /**
* @name ch390_read_mrrh * @name ch390_read_mrrh
* @brief Read MRRH via memory-dummy-read opcode * @brief Read MRRH receive memory pointer register
* @return Register value * @return Register value
*/ */
uint8_t ch390_read_mrrh(void); uint8_t ch390_read_mrrh(void);
+5 -5
View File
@@ -7,8 +7,8 @@
* *
* Key design decisions: * Key design decisions:
* - netconn API for thread-safe multi-connection TCP * - netconn API for thread-safe multi-connection TCP
* - tcpip_thread handles all lwIP core operations * - tcpip_thread handles netconn API and timers
* - LWIP_TCPIP_CORE_LOCKING=1 allows direct send from application tasks * - core locking lets the poll task process RX packets synchronously
* - Conservative memory footprint: target ~16KB for lwIP * - Conservative memory footprint: target ~16KB for lwIP
*/ */
@@ -27,9 +27,9 @@
#define LWIP_NETCONN 1 #define LWIP_NETCONN 1
#define LWIP_NETIF_API 1 #define LWIP_NETIF_API 1
/* Core locking: allows netconn_write/recv from any task without going through mbox */ /* Core locking: process netif RX synchronously instead of consuming TCPIP_MSG_INPKT slots. */
#define LWIP_TCPIP_CORE_LOCKING 1 #define LWIP_TCPIP_CORE_LOCKING 1
#define LWIP_TCPIP_CORE_LOCKING_INPUT 0 #define LWIP_TCPIP_CORE_LOCKING_INPUT 1
/* Critical section protection */ /* Critical section protection */
#define SYS_LIGHTWEIGHT_PROT 1 #define SYS_LIGHTWEIGHT_PROT 1
@@ -54,7 +54,7 @@
#define MEM_SIZE (7 * 1024) #define MEM_SIZE (7 * 1024)
/* Number of pbufs in pool. /* Number of pbufs in pool.
* 10 pools for 4 concurrent connections with some headroom. */ * RX is processed synchronously under the core lock, so a small pool is sufficient. */
#define PBUF_POOL_SIZE 8 #define PBUF_POOL_SIZE 8
/* Size of each pbuf in pool (must hold one Ethernet frame) */ /* Size of each pbuf in pool (must hold one Ethernet frame) */
@@ -23,7 +23,23 @@ struct ethernetif {
err_t ethernetif_init(struct netif *netif); err_t ethernetif_init(struct netif *netif);
void ethernetif_input(struct netif *netif); void ethernetif_input(struct netif *netif);
void lwip_netif_init(const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw); void lwip_netif_init(const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw);
void ethernetif_diag_ch390_init(void);
void ethernetif_diag_poll_status(void);
void ethernetif_check_link(void); void ethernetif_check_link(void);
void ethernetif_apply_runtime_netif_config(const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw,
const uint8_t *mac);
void ethernetif_force_full_recovery(const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw,
const uint8_t *mac);
void ethernetif_force_link_down(void);
uint16_t ethernetif_ch390_get_vendor_id(void);
uint16_t ethernetif_ch390_get_product_id(void);
uint8_t ethernetif_ch390_get_revision(void);
uint8_t ethernetif_ch390_health_ok(void);
uint8_t ethernetif_get_effective_mac(uint8_t *mac);
uint8_t ethernetif_link_is_up(void); uint8_t ethernetif_link_is_up(void);
void ethernetif_poll(void); void ethernetif_poll(void);
+4 -1
View File
@@ -36,6 +36,7 @@ err_t ethernet_output(struct netif *netif,
{ {
struct pbuf *q; struct pbuf *q;
struct eth_hdr *ethhdr; struct eth_hdr *ethhdr;
err_t err;
LWIP_ASSERT("netif != NULL", netif != NULL); LWIP_ASSERT("netif != NULL", netif != NULL);
LWIP_ASSERT("p != NULL", p != NULL); LWIP_ASSERT("p != NULL", p != NULL);
@@ -56,7 +57,9 @@ err_t ethernet_output(struct netif *netif,
SMEMCPY(&ethhdr->src, src, sizeof(struct eth_addr)); SMEMCPY(&ethhdr->src, src, sizeof(struct eth_addr));
ethhdr->type = lwip_htons(eth_type); ethhdr->type = lwip_htons(eth_type);
return netif->linkoutput(netif, q); err = netif->linkoutput(netif, q);
pbuf_free(q);
return err;
} }
err_t ethernet_input(struct pbuf *p, struct netif *netif) err_t ethernet_input(struct pbuf *p, struct netif *netif)
+499 -28
View File
@@ -41,12 +41,59 @@ static SemaphoreHandle_t spi_mutex = NULL;
static uint8_t s_rx_buffer[CH390_PKT_MAX]; static uint8_t s_rx_buffer[CH390_PKT_MAX];
static uint8_t s_tx_buffer[CH390_PKT_MAX]; static uint8_t s_tx_buffer[CH390_PKT_MAX];
static uint8_t s_garp_sent = 0u; static uint8_t s_garp_sent = 0u;
static uint8_t s_effective_mac[ETHARP_HWADDR_LEN];
static uint8_t s_effective_mac_valid = 0u;
#define CH390_TX_TIMEOUT_RESTART_THRESHOLD 6u
#define CH390_EXPECTED_VENDOR_ID 0x1C00u
#define CH390_EXPECTED_PRODUCT_ID 0x9151u
#define ETH_RX_LOG_INITIAL_COUNT 3u
#define ETH_RX_LOG_EVERY_COUNT 64u
#define ETH_RECOVERY_NONE 0u
#define ETH_RECOVERY_RX 1u
#define ETH_RECOVERY_FULL 2u
struct ethernetif_recovery_state
{
uint8_t pending;
uint8_t in_progress;
uint8_t tx_timeout_streak;
uint32_t tx_timeout_count;
uint32_t rx_overflow_count;
uint32_t rx_bad_count;
uint32_t rx_recovery_count;
uint32_t full_recovery_count;
};
static struct ethernetif_recovery_state s_recovery = {0};
/* Forward declarations */ /* Forward declarations */
static err_t low_level_init(struct netif *netif); static err_t low_level_init(struct netif *netif);
static err_t low_level_output(struct netif *netif, struct pbuf *p); static err_t low_level_output(struct netif *netif, struct pbuf *p);
static struct pbuf *low_level_input(struct netif *netif); static struct pbuf *low_level_input(struct netif *netif);
static void ethernetif_update_link(uint8_t link_status); static void ethernetif_update_link(uint8_t link_status);
static uint32_t ethernetif_record_counter(uint32_t *counter);
static uint32_t ethernetif_record_event_and_request(uint32_t *counter, uint8_t recovery_type);
static uint8_t ethernetif_claim_recovery(void);
static void ethernetif_finish_recovery(void);
static uint8_t ethernetif_run_pending_recovery(void);
static uint8_t ethernetif_should_log_counter(uint32_t count);
static void ethernetif_clear_tx_timeout_streak(void);
static uint32_t ethernetif_note_tx_timeout_streak(void);
static uint8_t ethernetif_mac_is_all_zero(const uint8_t *mac);
static void ethernetif_store_effective_mac(const uint8_t *mac);
static void ethernetif_apply_configured_or_internal_mac_locked(const uint8_t *configured_mac);
static void ethernetif_prepare_runtime_netif_locked(struct netif *netif);
static void ethernetif_apply_runtime_netif_config_locked(const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw,
const uint8_t *mac);
static void ethernetif_perform_full_recovery(const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw,
const uint8_t *mac,
uint8_t *link_status);
/*--------------------------------------------------------------------------- /*---------------------------------------------------------------------------
* Low Level Hardware Functions * Low Level Hardware Functions
@@ -58,7 +105,6 @@ static void ethernetif_update_link(uint8_t link_status);
*/ */
static err_t low_level_init(struct netif *netif) static err_t low_level_init(struct netif *netif)
{ {
struct ethernetif *ethernetif = netif->state;
const device_config_t *cfg = config_get(); const device_config_t *cfg = config_get();
/* Create SPI mutex */ /* Create SPI mutex */
@@ -88,24 +134,9 @@ static err_t low_level_init(struct netif *netif)
ch390_read_reg(CH390_BCASTCR), ch390_read_reg(CH390_BCASTCR),
ch390_read_reg(CH390_MAR + 7)); ch390_read_reg(CH390_MAR + 7));
/* Apply configured MAC address to CH390 before reading it back into lwIP */ ethernetif_apply_configured_or_internal_mac_locked(cfg->net.mac);
ch390_set_mac_address((uint8_t *)cfg->net.mac);
/* Set MAC hardware address length */ ethernetif_prepare_runtime_netif_locked(netif);
netif->hwaddr_len = ETHARP_HWADDR_LEN;
/* Get MAC address from CH390 */
ch390_get_mac(netif->hwaddr);
/* Maximum transfer unit */
netif->mtu = 1500;
/* Device capabilities */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
/* Initialize state */
ethernetif->rx_len = 0;
ethernetif->rx_status = 0;
/* Enable CH390 interrupt */ /* Enable CH390 interrupt */
ch390_interrupt_init(); ch390_interrupt_init();
@@ -113,6 +144,408 @@ static err_t low_level_init(struct netif *netif)
return ERR_OK; return ERR_OK;
} }
static uint32_t ethernetif_record_counter(uint32_t *counter)
{
uint32_t value;
taskENTER_CRITICAL();
++(*counter);
value = *counter;
taskEXIT_CRITICAL();
return value;
}
static uint32_t ethernetif_record_event_and_request(uint32_t *counter, uint8_t recovery_type)
{
uint32_t value;
taskENTER_CRITICAL();
++(*counter);
if (recovery_type == ETH_RECOVERY_FULL)
{
app_request_network_restart();
}
else if (recovery_type > s_recovery.pending)
{
s_recovery.pending = recovery_type;
}
value = *counter;
taskEXIT_CRITICAL();
return value;
}
static void ethernetif_clear_tx_timeout_streak(void)
{
taskENTER_CRITICAL();
s_recovery.tx_timeout_streak = 0u;
taskEXIT_CRITICAL();
}
static uint32_t ethernetif_note_tx_timeout_streak(void)
{
uint32_t streak;
taskENTER_CRITICAL();
++s_recovery.tx_timeout_count;
if (s_recovery.tx_timeout_streak < 0xFFu)
{
++s_recovery.tx_timeout_streak;
}
streak = s_recovery.tx_timeout_streak;
taskEXIT_CRITICAL();
return streak;
}
static uint8_t ethernetif_claim_recovery(void)
{
uint8_t recovery_type = ETH_RECOVERY_NONE;
taskENTER_CRITICAL();
if ((s_recovery.in_progress == 0u) && (s_recovery.pending != ETH_RECOVERY_NONE))
{
recovery_type = s_recovery.pending;
s_recovery.pending = ETH_RECOVERY_NONE;
s_recovery.in_progress = 1u;
}
taskEXIT_CRITICAL();
return recovery_type;
}
static void ethernetif_finish_recovery(void)
{
taskENTER_CRITICAL();
s_recovery.in_progress = 0u;
taskEXIT_CRITICAL();
}
static uint8_t ethernetif_should_log_counter(uint32_t count)
{
if (count <= ETH_RX_LOG_INITIAL_COUNT)
{
return 1u;
}
return ((count % ETH_RX_LOG_EVERY_COUNT) == 0u) ? 1u : 0u;
}
static uint8_t ethernetif_run_pending_recovery(void)
{
ip4_addr_t ipaddr;
ip4_addr_t netmask;
ip4_addr_t gateway;
uint8_t recovery_type;
uint8_t link_status = 0u;
uint32_t count;
recovery_type = ethernetif_claim_recovery();
if (recovery_type == ETH_RECOVERY_NONE)
{
return ETH_RECOVERY_NONE;
}
if (recovery_type == ETH_RECOVERY_FULL)
{
const device_config_t *cfg = config_get();
IP4_ADDR(&ipaddr, cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3]);
IP4_ADDR(&netmask, cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3]);
IP4_ADDR(&gateway, cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3]);
ethernetif_clear_tx_timeout_streak();
ethernetif_perform_full_recovery(&ipaddr, &netmask, &gateway, cfg->net.mac, &link_status);
}
else
{
if (spi_mutex != NULL)
{
xSemaphoreTake(spi_mutex, portMAX_DELAY);
}
ch390_rx_reset();
if (spi_mutex != NULL)
{
xSemaphoreGive(spi_mutex);
}
}
if (recovery_type == ETH_RECOVERY_FULL)
{
count = ethernetif_record_counter(&s_recovery.full_recovery_count);
debug_log_printf("[ETH] rec full n=%lu link=%u\r\n",
(unsigned long)count,
(unsigned int)link_status);
}
else
{
count = ethernetif_record_counter(&s_recovery.rx_recovery_count);
if (ethernetif_should_log_counter(count) != 0u)
{
debug_log_printf("[ETH] rec rx n=%lu\r\n", (unsigned long)count);
}
}
ethernetif_finish_recovery();
return recovery_type;
}
static void ethernetif_perform_full_recovery(const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw,
const uint8_t *mac,
uint8_t *link_status)
{
uint8_t local_link_status = 0u;
if (spi_mutex != NULL)
{
xSemaphoreTake(spi_mutex, portMAX_DELAY);
}
ch390_spi_init();
ch390_hardware_reset();
ch390_default_config();
ethernetif_apply_configured_or_internal_mac_locked(mac);
ch390_set_phy_mode(CH390_AUTO);
ch390_rx_enable(1);
ethernetif_prepare_runtime_netif_locked(&ch390_netif);
ethernetif_apply_runtime_netif_config_locked(ipaddr,
netmask,
gw,
(s_effective_mac_valid != 0u) ? s_effective_mac : NULL);
local_link_status = (uint8_t)ch390_get_link_status();
if (spi_mutex != NULL)
{
xSemaphoreGive(spi_mutex);
}
ch390_interrupt_init();
if (link_status != NULL)
{
*link_status = local_link_status;
}
ethernetif_update_link(local_link_status);
}
static uint8_t ethernetif_mac_is_all_zero(const uint8_t *mac)
{
uint32_t i;
if (mac == NULL)
{
return 1u;
}
for (i = 0u; i < ETHARP_HWADDR_LEN; ++i)
{
if (mac[i] != 0u)
{
return 0u;
}
}
return 1u;
}
static void ethernetif_store_effective_mac(const uint8_t *mac)
{
if (mac == NULL)
{
s_effective_mac_valid = 0u;
return;
}
MEMCPY(s_effective_mac, mac, ETHARP_HWADDR_LEN);
s_effective_mac_valid = 1u;
}
static void ethernetif_apply_configured_or_internal_mac_locked(const uint8_t *configured_mac)
{
uint8_t runtime_mac[ETHARP_HWADDR_LEN];
if (ethernetif_mac_is_all_zero(configured_mac) == 0u)
{
ch390_set_mac_address((uint8_t *)configured_mac);
ethernetif_store_effective_mac(configured_mac);
return;
}
ch390_get_mac(runtime_mac);
ethernetif_store_effective_mac(runtime_mac);
debug_log_printf("[ETH] use internal mac=%02X:%02X:%02X:%02X:%02X:%02X\r\n",
(unsigned int)runtime_mac[0],
(unsigned int)runtime_mac[1],
(unsigned int)runtime_mac[2],
(unsigned int)runtime_mac[3],
(unsigned int)runtime_mac[4],
(unsigned int)runtime_mac[5]);
}
static void ethernetif_prepare_runtime_netif_locked(struct netif *netif)
{
struct ethernetif *ethernetif;
if (netif == NULL)
{
return;
}
netif->hwaddr_len = ETHARP_HWADDR_LEN;
ch390_get_mac(netif->hwaddr);
ethernetif_store_effective_mac(netif->hwaddr);
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
ethernetif = (struct ethernetif *)netif->state;
if (ethernetif != NULL)
{
ethernetif->rx_len = 0u;
ethernetif->rx_status = 0u;
}
}
static void ethernetif_apply_runtime_netif_config_locked(const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw,
const uint8_t *mac)
{
err_t err;
if (mac != NULL)
{
MEMCPY(ch390_netif.hwaddr, mac, ETHARP_HWADDR_LEN);
s_garp_sent = 0u;
}
if (ipaddr != NULL && netmask != NULL && gw != NULL)
{
err = netifapi_netif_set_addr(&ch390_netif, ipaddr, netmask, gw);
if (err != ERR_OK)
{
debug_log_printf("[ETH] set-addr fail err=%d\r\n", (int)err);
}
}
err = netifapi_netif_set_default(&ch390_netif);
if (err != ERR_OK)
{
debug_log_printf("[ETH] resync-default fail err=%d\r\n", (int)err);
}
err = netifapi_netif_set_up(&ch390_netif);
if (err != ERR_OK)
{
debug_log_printf("[ETH] resync-up fail err=%d\r\n", (int)err);
}
}
void ethernetif_apply_runtime_netif_config(const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw,
const uint8_t *mac)
{
ethernetif_apply_runtime_netif_config_locked(ipaddr, netmask, gw, mac);
}
void ethernetif_force_full_recovery(const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw,
const uint8_t *mac)
{
uint8_t link_status = 0u;
uint32_t count;
ethernetif_clear_tx_timeout_streak();
ethernetif_perform_full_recovery(ipaddr, netmask, gw, mac, &link_status);
count = ethernetif_record_counter(&s_recovery.full_recovery_count);
debug_log_printf("[ETH] rec full n=%lu link=%u\r\n",
(unsigned long)count,
(unsigned int)link_status);
}
void ethernetif_force_link_down(void)
{
ethernetif_update_link(0u);
}
uint8_t ethernetif_ch390_health_ok(void)
{
return ((ethernetif_ch390_get_vendor_id() == CH390_EXPECTED_VENDOR_ID) &&
(ethernetif_ch390_get_product_id() == CH390_EXPECTED_PRODUCT_ID)) ? 1u : 0u;
}
uint8_t ethernetif_get_effective_mac(uint8_t *mac)
{
if ((mac == NULL) || (s_effective_mac_valid == 0u))
{
return 0u;
}
MEMCPY(mac, s_effective_mac, ETHARP_HWADDR_LEN);
return 1u;
}
uint16_t ethernetif_ch390_get_vendor_id(void)
{
uint16_t vendor_id;
if (spi_mutex != NULL)
{
xSemaphoreTake(spi_mutex, portMAX_DELAY);
}
vendor_id = ch390_get_vendor_id();
if (spi_mutex != NULL)
{
xSemaphoreGive(spi_mutex);
}
return vendor_id;
}
uint16_t ethernetif_ch390_get_product_id(void)
{
uint16_t product_id;
if (spi_mutex != NULL)
{
xSemaphoreTake(spi_mutex, portMAX_DELAY);
}
product_id = ch390_get_product_id();
if (spi_mutex != NULL)
{
xSemaphoreGive(spi_mutex);
}
return product_id;
}
uint8_t ethernetif_ch390_get_revision(void)
{
uint8_t revision;
if (spi_mutex != NULL)
{
xSemaphoreTake(spi_mutex, portMAX_DELAY);
}
revision = ch390_get_revision();
if (spi_mutex != NULL)
{
xSemaphoreGive(spi_mutex);
}
return revision;
}
/** /**
* @brief Transmit a packet via CH390 * @brief Transmit a packet via CH390
* @param netif Network interface structure * @param netif Network interface structure
@@ -125,6 +558,7 @@ static err_t low_level_output(struct netif *netif, struct pbuf *p)
uint16_t offset; uint16_t offset;
uint16_t tx_len; uint16_t tx_len;
int tx_rc; int tx_rc;
uint32_t tx_timeout_streak;
(void)netif; (void)netif;
/* Take SPI mutex */ /* Take SPI mutex */
@@ -179,10 +613,18 @@ static err_t low_level_output(struct netif *netif, struct pbuf *p)
{ {
LINK_STATS_INC(link.drop); LINK_STATS_INC(link.drop);
LINK_STATS_INC(link.err); LINK_STATS_INC(link.err);
debug_log_write("[ETH] tx timeout\r\n"); tx_timeout_streak = ethernetif_note_tx_timeout_streak();
debug_log_printf("[ETH] tx timeout n=%lu streak=%lu\r\n",
(unsigned long)s_recovery.tx_timeout_count,
(unsigned long)tx_timeout_streak);
if (tx_timeout_streak >= CH390_TX_TIMEOUT_RESTART_THRESHOLD)
{
app_request_network_restart();
}
return ERR_TIMEOUT; return ERR_TIMEOUT;
} }
ethernetif_clear_tx_timeout_streak();
LINK_STATS_INC(link.xmit); LINK_STATS_INC(link.xmit);
return ERR_OK; return ERR_OK;
@@ -266,7 +708,6 @@ static struct pbuf *low_level_input(struct netif *netif)
else else
{ {
/* No memory - drop packet */ /* No memory - drop packet */
ch390_drop_packet(ethernetif->rx_len);
LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop); LINK_STATS_INC(link.drop);
} }
@@ -428,7 +869,7 @@ void ethernetif_diag_ch390_init(void)
ch390_spi_init(); ch390_spi_init();
ch390_hardware_reset(); ch390_hardware_reset();
ch390_default_config(); ch390_default_config();
ch390_set_mac_address((uint8_t *)cfg->net.mac); ethernetif_apply_configured_or_internal_mac_locked(cfg->net.mac);
ch390_interrupt_init(); ch390_interrupt_init();
g_netif_init_ok = 1; g_netif_init_ok = 1;
@@ -457,8 +898,12 @@ void ethernetif_diag_poll_status(void)
*/ */
static void ethernetif_update_link(uint8_t link_status) static void ethernetif_update_link(uint8_t link_status)
{ {
uint8_t old_link_status;
LOCK_TCPIP_CORE(); LOCK_TCPIP_CORE();
old_link_status = netif_is_link_up(&ch390_netif) ? 1u : 0u;
if (link_status) if (link_status)
{ {
if (!netif_is_link_up(&ch390_netif)) if (!netif_is_link_up(&ch390_netif))
@@ -481,6 +926,22 @@ static void ethernetif_update_link(uint8_t link_status)
} }
UNLOCK_TCPIP_CORE(); UNLOCK_TCPIP_CORE();
if (old_link_status != ((link_status != 0u) ? 1u : 0u))
{
debug_log_printf("[ETH] link %s ip=%u.%u.%u.%u mac=%02X:%02X:%02X:%02X:%02X:%02X\r\n",
(link_status != 0u) ? "up" : "down",
(unsigned int)ip4_addr1_16(netif_ip4_addr(&ch390_netif)),
(unsigned int)ip4_addr2_16(netif_ip4_addr(&ch390_netif)),
(unsigned int)ip4_addr3_16(netif_ip4_addr(&ch390_netif)),
(unsigned int)ip4_addr4_16(netif_ip4_addr(&ch390_netif)),
(unsigned int)ch390_netif.hwaddr[0],
(unsigned int)ch390_netif.hwaddr[1],
(unsigned int)ch390_netif.hwaddr[2],
(unsigned int)ch390_netif.hwaddr[3],
(unsigned int)ch390_netif.hwaddr[4],
(unsigned int)ch390_netif.hwaddr[5]);
}
} }
/** /**
@@ -491,6 +952,8 @@ void ethernetif_poll(void)
struct ch390_runtime_status runtime_status; struct ch390_runtime_status runtime_status;
struct pbuf *p; struct pbuf *p;
err_t input_err; err_t input_err;
uint32_t rx_overflow_count;
uint8_t recovery_type;
uint8_t rx_budget = 4u; uint8_t rx_budget = 4u;
/* Take SPI mutex */ /* Take SPI mutex */
@@ -508,17 +971,23 @@ void ethernetif_poll(void)
xSemaphoreGive(spi_mutex); xSemaphoreGive(spi_mutex);
} }
/* Handle link change */ /* Handle RX overflow */
if ((runtime_status.int_status & ISR_LNKCHG) != 0u) if ((runtime_status.int_status & (ISR_ROS | ISR_ROO)) != 0u)
{ {
ethernetif_update_link((uint8_t)ch390_runtime_link_up_from_status(&runtime_status)); LINK_STATS_INC(link.err);
rx_overflow_count = ethernetif_record_event_and_request(&s_recovery.rx_overflow_count, ETH_RECOVERY_RX);
if (ethernetif_should_log_counter(rx_overflow_count) != 0u)
{
debug_log_printf("[ETH] rx ovf n=%lu\r\n", (unsigned long)rx_overflow_count);
}
} }
/* Handle RX overflow */ recovery_type = ethernetif_run_pending_recovery();
if ((runtime_status.int_status & ISR_ROS) != 0u)
/* Handle link change unless a full recovery already resynchronized link state. */
if ((recovery_type != ETH_RECOVERY_FULL) && ((runtime_status.int_status & ISR_LNKCHG) != 0u))
{ {
/* RX overflow - packets might be corrupted */ ethernetif_update_link((uint8_t)ch390_runtime_link_up_from_status(&runtime_status));
LINK_STATS_INC(link.err);
} }
/* Always attempt to drain RX FIFO so packet handling does not depend only on ISR_PR timing. */ /* Always attempt to drain RX FIFO so packet handling does not depend only on ISR_PR timing. */
@@ -539,6 +1008,8 @@ void ethernetif_poll(void)
--rx_budget; --rx_budget;
} }
(void)ethernetif_run_pending_recovery();
if (rx_budget == 0u) if (rx_budget == 0u)
{ {
taskYIELD(); taskYIELD();
+14
View File
@@ -62,6 +62,20 @@ void ethernetif_diag_poll_status(void);
* @note Call this from the LwIP task periodically or on interrupt * @note Call this from the LwIP task periodically or on interrupt
*/ */
void ethernetif_check_link(void); void ethernetif_check_link(void);
void ethernetif_apply_runtime_netif_config(const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw,
const uint8_t *mac);
void ethernetif_force_full_recovery(const ip4_addr_t *ipaddr,
const ip4_addr_t *netmask,
const ip4_addr_t *gw,
const uint8_t *mac);
void ethernetif_force_link_down(void);
uint16_t ethernetif_ch390_get_vendor_id(void);
uint16_t ethernetif_ch390_get_product_id(void);
uint8_t ethernetif_ch390_get_revision(void);
uint8_t ethernetif_ch390_health_ok(void);
uint8_t ethernetif_get_effective_mac(uint8_t *mac);
/** /**
* @brief Query whether physical Ethernet link is currently up * @brief Query whether physical Ethernet link is currently up
+10 -6
View File
@@ -26,15 +26,19 @@
1. 当前 Keil 工程目标仍是 `STM32F103RC` 1. 当前 Keil 工程目标仍是 `STM32F103RC`
2. 当前代码可以真实构建通过 2. 当前代码可以真实构建通过
3. 当前构建真值应查看: 3. 当前 FreeRTOS + lwIP 固件版本线从 `V2.0.0` 开始,裸机固件版本线从 `V1.0.0` 开始
4. 当前 RTOS 基线 release`https://git.furtherverse.com/gaoro-xiao/TCP2UART/releases/tag/V2.0.0`
5. Release 附件:
- `TCP2UART-RTOS-V2.0.0.hex`
- `TCP2UART-RTOS-V2.0.0.elf`
6. 当前构建真值应查看:
- `MDK-ARM/build_capture.txt` - `MDK-ARM/build_capture.txt`
- `MDK-ARM/TCP2UART/TCP2UART.build_log.htm` - `MDK-ARM/TCP2UART/TCP2UART.build_log.htm`
- `MDK-ARM/TCP2UART/TCP2UART.map` - `MDK-ARM/TCP2UART/TCP2UART.map`
4. 最近一次 Keil 构建示例 7. `V2.0.0` release 构建验证
- `Code=84560` - `"TCP2UART\TCP2UART.axf" - 0 Error(s), 0 Warning(s).`
- `RW-data=432` - `TCP2UART-RTOS-V2.0.0.hex` 用于烧录
- `ZI-data=47056` - `TCP2UART-RTOS-V2.0.0.elf` 用于符号/调试
- `0 Error(s), 0 Warning(s)`
### 3.2 当前调试结论摘要 ### 3.2 当前调试结论摘要
+24
View File
@@ -94,6 +94,11 @@
2. 启动文件:`startup_stm32f103xe.s` 2. 启动文件:`startup_stm32f103xe.s`
3. 目标器件:`STM32F103RC` 3. 目标器件:`STM32F103RC`
4. 预处理器宏:`USE_HAL_DRIVER, STM32F103xE` 4. 预处理器宏:`USE_HAL_DRIVER, STM32F103xE`
5. FreeRTOS + lwIP 固件版本线从 `V2.0.0` 开始;裸机固件版本线从 `V1.0.0` 开始。
6. 当前 RTOS release`https://git.furtherverse.com/gaoro-xiao/TCP2UART/releases/tag/V2.0.0`
7. Release 附件命名:
- `TCP2UART-RTOS-V2.0.0.hex`
- `TCP2UART-RTOS-V2.0.0.elf`
### 4.3 常用调试工具 ### 4.3 常用调试工具
@@ -448,3 +453,22 @@ case ETHTYPE_ARP:
3. 不要忽略 `DIAG_TASK_ISOLATION=1 正常、=0 异常` 这个前提 3. 不要忽略 `DIAG_TASK_ISOLATION=1 正常、=0 异常` 这个前提
4. 不要一次性修改 `C1/S1/CH390/lwIP` 多个方向,避免再次失去因果关系 4. 不要一次性修改 `C1/S1/CH390/lwIP` 多个方向,避免再次失去因果关系
5. 每次只做一个能明显改变故障边界的最小改动,并用 RTT + Keil build 结果交叉验证 5. 每次只做一个能明显改变故障边界的最小改动,并用 RTT + Keil build 结果交叉验证
### 11.8 固定 Client 端口重连策略(TIME_WAIT 取舍)
当前工程的 `Client` 链路保留固定 `LPORT` 配置语义,默认 `C1/C2` 均使用明确的本地源端口。
`lwIP + netconn` 路径下,如果仍沿用优雅 `netconn_close()`,则相同 `LPORT` 的快速重连会受到 TCP `TIME_WAIT` 影响,表现为一段时间内重复 `bind/connect` 失败。
结合本项目的约束,当前版本固化如下取舍:
1. 不取消 `Client` 固定 `LPORT` 语义
2. 不依赖扩大 PCB 池作为主修复手段
3. 不通过降低 `TCP_MSL` 改写全局 TCP 保守语义
4.`Client` 主动断开后的释放路径采用 abortive closeRST),以立即释放 PCB 与本地端口
使用该策略时应明确接受以下副作用:
1. 对端可能看到 `RST` 或“连接被重置”
2. 连接尾部未完成发送的数据不会再走优雅关闭路径
3. 该策略仅用于固定 `Client` 端口快速重连场景,不应直接推广到所有 TCP 关闭路径
+8
View File
@@ -365,6 +365,14 @@ EN,LPORT,RIP,RPORT,UART
5. 内部 ring buffer 可取消(netconn 内部已有 pbuf 缓冲) 5. 内部 ring buffer 可取消(netconn 内部已有 pbuf 缓冲)
6. 每个 TCP 任务内直接处理路由,通过 Queue 指针传递数据 6. 每个 TCP 任务内直接处理路由,通过 Queue 指针传递数据
补充约束(当前实现口径):
1. `Client` 链路保留固定 `LPORT` 配置语义,以满足产品侧对固定源端口的依赖
2.`lwIP + netconn` 模型下,若 `Client` 继续使用优雅 `netconn_close()`,相同本地端口的快速重连会受 `TIME_WAIT` 影响
3. 因此当前工程对 `Client` 会话结束后的释放路径采用 abortive close`tcp_abort` / RST)以立即释放 PCB 与本地端口
4. 该策略只针对 `Client` 固定端口重连路径,不扩展到 `Server` listener 或一般被动关闭场景
5. 该策略的已知代价是:对端可能看到 `RST`,且尾部未完成发送的数据不会再走优雅 `FIN/ACK` 收尾
### 7.4 FreeRTOS 初始化 `freertos.c` ### 7.4 FreeRTOS 初始化 `freertos.c`
CubeMX 生成的 FreeRTOS 初始化文件,职责: CubeMX 生成的 FreeRTOS 初始化文件,职责: