Compare commits
15 Commits
00be10f134
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 58361589d8 | |||
| 0681b8bbe4 | |||
| 3cb49fc4f8 | |||
| db714471b8 | |||
| 8c204aad77 | |||
| a6040e7d68 | |||
| 60d2af0a27 | |||
| ac0c464910 | |||
| c9ece65182 | |||
| c519f90149 | |||
| d6a1565503 | |||
| ab3b6bfc9a | |||
| c1a0822227 | |||
| 22bc6a7fef | |||
| fe03bee588 |
+19
-7
@@ -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 close(RST)而非优雅 `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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+121
-11
@@ -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,16 +700,47 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ConfigTask(void *argument)
|
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
@@ -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);
|
||||||
|
|||||||
+85
-32
@@ -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,15 +124,15 @@ 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,
|
||||||
const uint8_t *data,
|
const uint8_t *data,
|
||||||
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,51 +140,88 @@ 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,
|
||||||
uint8_t src_id,
|
const uint8_t *data,
|
||||||
uint8_t dst_mask,
|
uint16_t len)
|
||||||
uint8_t conn_type,
|
|
||||||
const uint8_t *data,
|
|
||||||
uint16_t len,
|
|
||||||
TickType_t wait_ticks)
|
|
||||||
{
|
{
|
||||||
route_msg_t *msg = route_msg_alloc(wait_ticks);
|
if (queue == NULL || data == NULL || len == 0u || len > ROUTE_MSG_MAX_PAYLOAD) {
|
||||||
|
return ROUTE_SEND_INVALID_INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
if (!route_prepare(msg, src_id, dst_mask, conn_type, data, len)) {
|
return ROUTE_SEND_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
route_send_result_t route_send(QueueHandle_t queue,
|
||||||
|
uint8_t src_id,
|
||||||
|
uint8_t dst_mask,
|
||||||
|
uint8_t conn_type,
|
||||||
|
const uint8_t *data,
|
||||||
|
uint16_t len,
|
||||||
|
TickType_t wait_ticks)
|
||||||
|
{
|
||||||
|
route_send_result_t result;
|
||||||
|
route_msg_t *msg;
|
||||||
|
|
||||||
|
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,
|
||||||
const uint8_t *data,
|
const uint8_t *data,
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-14
@@ -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,20 +49,21 @@ 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);
|
||||||
uint8_t src_id,
|
route_send_result_t route_send(QueueHandle_t queue,
|
||||||
uint8_t dst_mask,
|
uint8_t src_id,
|
||||||
uint8_t conn_type,
|
uint8_t dst_mask,
|
||||||
const uint8_t *data,
|
uint8_t conn_type,
|
||||||
uint16_t len,
|
const uint8_t *data,
|
||||||
TickType_t wait_ticks);
|
uint16_t len,
|
||||||
bool route_send_from_isr(QueueHandle_t queue,
|
TickType_t wait_ticks);
|
||||||
uint8_t src_id,
|
route_send_result_t route_send_from_isr(QueueHandle_t queue,
|
||||||
uint8_t dst_mask,
|
uint8_t src_id,
|
||||||
uint8_t conn_type,
|
uint8_t dst_mask,
|
||||||
const uint8_t *data,
|
uint8_t conn_type,
|
||||||
uint16_t len,
|
const uint8_t *data,
|
||||||
BaseType_t *xHigherPriorityTaskWoken);
|
uint16_t len,
|
||||||
|
BaseType_t *xHigherPriorityTaskWoken);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-2
@@ -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
|
||||||
ethernetif_poll();
|
if (g_netif_ready != pdFALSE) {
|
||||||
ethernetif_check_link();
|
ethernetif_poll();
|
||||||
|
ethernetif_check_link();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-17
@@ -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)
|
||||||
|
|||||||
+86
-14
@@ -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)
|
||||||
|
|||||||
+243
-57
@@ -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;
|
||||||
|
|
||||||
|
uart_mask = (uint8_t)(msg->dst_mask & (ENDPOINT_UART2 | ENDPOINT_UART3));
|
||||||
|
if ((msg->dst_mask != uart_mask) ||
|
||||||
|
(uart_mask != ENDPOINT_UART2 && uart_mask != ENDPOINT_UART3)) {
|
||||||
|
return UART_TRANS_SEND_INVALID_INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining = (uint16_t)(msg->len - offset);
|
||||||
|
|
||||||
|
if (uart_mask == ENDPOINT_UART2) {
|
||||||
if (config_get()->mux_mode == MUX_MODE_FRAME) {
|
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))) {
|
chunk_len = remaining;
|
||||||
(void)uart_trans_send_buffer(UART_CHANNEL_U1, frame, frame_len);
|
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u)) {
|
||||||
|
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u);
|
||||||
}
|
}
|
||||||
} else {
|
if (!uart_mux_encode_frame(msg->src_id, ENDPOINT_UART2, &msg->data[offset], chunk_len, frame, &frame_len, sizeof(frame))) {
|
||||||
(void)uart_trans_send_buffer(UART_CHANNEL_U1, msg->data, msg->len);
|
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
@@ -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);
|
||||||
|
|||||||
@@ -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(ðhdr->dest, dst, sizeof(struct eth_addr));
|
||||||
|
SMEMCPY(ðhdr->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 永远没有回到 0,PBUF_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(ðhdr->dest, dst, sizeof(struct eth_addr));
|
||||||
|
SMEMCPY(ðhdr->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`
|
||||||
@@ -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 */
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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 */
|
||||||
|
|||||||
@@ -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,8 +102,11 @@ 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);
|
||||||
xSemaphoreGiveFromISR(xNetSemaphore, &xHigherPriorityTaskWoken);
|
if ((xNetSemaphore != NULL) &&
|
||||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING)) {
|
||||||
|
xSemaphoreGiveFromISR(xNetSemaphore, &xHigherPriorityTaskWoken);
|
||||||
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-2
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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(ðhdr->src, src, sizeof(struct eth_addr));
|
SMEMCPY(ðhdr->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)
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 当前调试结论摘要
|
||||||
|
|
||||||
|
|||||||
@@ -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 close(RST),以立即释放 PCB 与本地端口
|
||||||
|
|
||||||
|
使用该策略时应明确接受以下副作用:
|
||||||
|
|
||||||
|
1. 对端可能看到 `RST` 或“连接被重置”
|
||||||
|
2. 连接尾部未完成发送的数据不会再走优雅关闭路径
|
||||||
|
3. 该策略仅用于固定 `Client` 端口快速重连场景,不应直接推广到所有 TCP 关闭路径
|
||||||
|
|||||||
@@ -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 初始化文件,职责:
|
||||||
|
|||||||
Reference in New Issue
Block a user