feat: save stable CH390 bridge baseline
This commit is contained in:
+525
-200
@@ -1,32 +1,61 @@
|
||||
/**
|
||||
* @file config.c
|
||||
* @brief Bare-metal AT command configuration module implementation.
|
||||
* @brief Bare-metal final AT configuration module implementation.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "flash_param.h"
|
||||
#include "usart.h"
|
||||
#include "../Core/Inc/usart.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "SEGGER_RTT.h"
|
||||
|
||||
#define CONFIG_TX_BUFFER_SIZE 256u
|
||||
#define CONFIG_CMD_MAX_LEN 128u
|
||||
#define CONFIG_TX_BUFFER_SIZE 512u
|
||||
#define CONFIG_CMD_MAX_LEN 160u
|
||||
|
||||
#define CONFIG_UART_HANDLE huart1
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
uint16_t version;
|
||||
uint16_t reserved;
|
||||
uint8_t mac[6];
|
||||
uint8_t dhcp_enable;
|
||||
uint8_t reserved2;
|
||||
uint8_t ip[4];
|
||||
uint8_t mask[4];
|
||||
uint8_t gw[4];
|
||||
uint16_t server_port;
|
||||
uint16_t reserved3;
|
||||
uint8_t remote_ip[4];
|
||||
uint16_t remote_port;
|
||||
uint16_t reconnect_interval;
|
||||
uint32_t uart2_baudrate;
|
||||
uint32_t uart3_baudrate;
|
||||
uint8_t uart2_databits;
|
||||
uint8_t uart2_stopbits;
|
||||
uint8_t uart2_parity;
|
||||
uint8_t uart3_databits;
|
||||
uint8_t uart3_stopbits;
|
||||
uint8_t uart3_parity;
|
||||
uint16_t reserved4;
|
||||
uint32_t crc;
|
||||
} legacy_device_config_v2_t;
|
||||
|
||||
static device_config_t g_config;
|
||||
static volatile bool g_reset_requested;
|
||||
static char g_uart_cmd_buffer[CONFIG_CMD_MAX_LEN];
|
||||
static uint8_t g_uart_cmd_buffer[CONFIG_CMD_MAX_LEN];
|
||||
static uint16_t g_uart_cmd_len;
|
||||
static bool g_uart_rx_seen_cr;
|
||||
static char g_pending_cmd_buffer[CONFIG_CMD_MAX_LEN];
|
||||
static volatile uint16_t g_pending_cmd_len;
|
||||
static volatile bool g_pending_cmd_ready;
|
||||
static char g_at_response_buffer[CONFIG_TX_BUFFER_SIZE];
|
||||
static char g_cmd_parse_buffer[CONFIG_CMD_MAX_LEN];
|
||||
static char g_cmd_work_buffer[CONFIG_CMD_MAX_LEN];
|
||||
|
||||
static uint32_t config_calc_crc(const device_config_t *cfg)
|
||||
{
|
||||
@@ -56,8 +85,12 @@ static bool equals_ignore_case(const char *a, const char *b)
|
||||
char c1 = *a++;
|
||||
char c2 = *b++;
|
||||
|
||||
if (c1 >= 'a' && c1 <= 'z') c1 -= 32;
|
||||
if (c2 >= 'a' && c2 <= 'z') c2 -= 32;
|
||||
if (c1 >= 'a' && c1 <= 'z') {
|
||||
c1 -= 32;
|
||||
}
|
||||
if (c2 >= 'a' && c2 <= 'z') {
|
||||
c2 -= 32;
|
||||
}
|
||||
|
||||
if (c1 != c2) {
|
||||
return false;
|
||||
@@ -67,12 +100,31 @@ static bool equals_ignore_case(const char *a, const char *b)
|
||||
return (*a == '\0' && *b == '\0');
|
||||
}
|
||||
|
||||
static int parse_baudrate_value(const char *value, uint32_t *baudrate)
|
||||
static int prefix_equals_ignore_case(const char *str, const char *prefix)
|
||||
{
|
||||
while (*prefix != '\0') {
|
||||
char c1 = *str++;
|
||||
char c2 = *prefix++;
|
||||
|
||||
if (c1 >= 'a' && c1 <= 'z') {
|
||||
c1 -= 32;
|
||||
}
|
||||
if (c2 >= 'a' && c2 <= 'z') {
|
||||
c2 -= 32;
|
||||
}
|
||||
if (c1 != c2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_u32_value(const char *value, uint32_t min_value, uint32_t max_value, uint32_t *parsed_value)
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long parsed;
|
||||
|
||||
if (value == NULL || baudrate == NULL) {
|
||||
if (value == NULL || parsed_value == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -80,54 +132,262 @@ static int parse_baudrate_value(const char *value, uint32_t *baudrate)
|
||||
if (endptr == value || *skip_whitespace(endptr) != '\0') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (parsed < 1200ul || parsed > 921600ul) {
|
||||
if (parsed < min_value || parsed > max_value) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*baudrate = (uint32_t)parsed;
|
||||
*parsed_value = (uint32_t)parsed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static at_result_t handle_query(char *response, uint16_t max_len)
|
||||
static int parse_link_uart(const char *value, uint8_t *uart)
|
||||
{
|
||||
if (equals_ignore_case(value, "U0")) {
|
||||
*uart = LINK_UART_U0;
|
||||
return 0;
|
||||
}
|
||||
if (equals_ignore_case(value, "U1")) {
|
||||
*uart = LINK_UART_U1;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *link_uart_to_str(uint8_t uart)
|
||||
{
|
||||
return (uart == LINK_UART_U1) ? "U1" : "U0";
|
||||
}
|
||||
|
||||
static bool parse_command_with_value(const char *cmd, const char *name, const char **value)
|
||||
{
|
||||
size_t name_len;
|
||||
|
||||
if (cmd == NULL || name == NULL || value == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
name_len = strlen(name);
|
||||
if (!prefix_equals_ignore_case(cmd, name)) {
|
||||
return false;
|
||||
}
|
||||
if (cmd[name_len] != '=') {
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = skip_whitespace(cmd + name_len + 1u);
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *config_next_token(char **cursor)
|
||||
{
|
||||
char *start;
|
||||
char *end;
|
||||
|
||||
if (cursor == NULL || *cursor == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
start = *cursor;
|
||||
while (*start == ' ' || *start == '\t') {
|
||||
++start;
|
||||
}
|
||||
if (*start == '\0') {
|
||||
*cursor = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
end = start;
|
||||
while (*end != '\0' && *end != ',') {
|
||||
++end;
|
||||
}
|
||||
|
||||
if (*end == ',') {
|
||||
*end = '\0';
|
||||
*cursor = end + 1;
|
||||
} else {
|
||||
*cursor = NULL;
|
||||
}
|
||||
|
||||
trim_trailing(start);
|
||||
return start;
|
||||
}
|
||||
|
||||
static void set_link_defaults(void)
|
||||
{
|
||||
static const uint8_t zero_ip[4] = {0u, 0u, 0u, 0u};
|
||||
static const uint8_t c1_ip[4] = {192u, 168u, 1u, 200u};
|
||||
static const uint8_t c2_ip[4] = {192u, 168u, 1u, 201u};
|
||||
|
||||
memset(g_config.links, 0, sizeof(g_config.links));
|
||||
|
||||
g_config.links[CONFIG_LINK_S1].enabled = 1u;
|
||||
g_config.links[CONFIG_LINK_S1].uart = LINK_UART_U0;
|
||||
g_config.links[CONFIG_LINK_S1].local_port = 8080u;
|
||||
memcpy(g_config.links[CONFIG_LINK_S1].remote_ip, zero_ip, sizeof(zero_ip));
|
||||
|
||||
g_config.links[CONFIG_LINK_S2].enabled = 0u;
|
||||
g_config.links[CONFIG_LINK_S2].uart = LINK_UART_U1;
|
||||
g_config.links[CONFIG_LINK_S2].local_port = 8081u;
|
||||
memcpy(g_config.links[CONFIG_LINK_S2].remote_ip, zero_ip, sizeof(zero_ip));
|
||||
|
||||
g_config.links[CONFIG_LINK_C1].enabled = 1u;
|
||||
g_config.links[CONFIG_LINK_C1].uart = LINK_UART_U1;
|
||||
g_config.links[CONFIG_LINK_C1].local_port = 9001u;
|
||||
memcpy(g_config.links[CONFIG_LINK_C1].remote_ip, c1_ip, sizeof(c1_ip));
|
||||
g_config.links[CONFIG_LINK_C1].remote_port = 9000u;
|
||||
|
||||
g_config.links[CONFIG_LINK_C2].enabled = 0u;
|
||||
g_config.links[CONFIG_LINK_C2].uart = LINK_UART_U0;
|
||||
g_config.links[CONFIG_LINK_C2].local_port = 9002u;
|
||||
memcpy(g_config.links[CONFIG_LINK_C2].remote_ip, c2_ip, sizeof(c2_ip));
|
||||
g_config.links[CONFIG_LINK_C2].remote_port = 9001u;
|
||||
}
|
||||
|
||||
static void migrate_legacy_config(const legacy_device_config_v2_t *legacy)
|
||||
{
|
||||
config_set_defaults();
|
||||
|
||||
memcpy(g_config.net.mac, legacy->mac, sizeof(g_config.net.mac));
|
||||
memcpy(g_config.net.ip, legacy->ip, sizeof(g_config.net.ip));
|
||||
memcpy(g_config.net.mask, legacy->mask, sizeof(g_config.net.mask));
|
||||
memcpy(g_config.net.gw, legacy->gw, sizeof(g_config.net.gw));
|
||||
g_config.uart_baudrate[0] = legacy->uart2_baudrate;
|
||||
g_config.uart_baudrate[1] = legacy->uart3_baudrate;
|
||||
g_config.mux_mode = MUX_MODE_RAW;
|
||||
|
||||
g_config.links[CONFIG_LINK_S1].enabled = (legacy->server_port != 0u) ? 1u : 0u;
|
||||
g_config.links[CONFIG_LINK_S1].uart = LINK_UART_U0;
|
||||
g_config.links[CONFIG_LINK_S1].local_port = legacy->server_port;
|
||||
memset(g_config.links[CONFIG_LINK_S1].remote_ip, 0, sizeof(g_config.links[CONFIG_LINK_S1].remote_ip));
|
||||
g_config.links[CONFIG_LINK_S1].remote_port = 0u;
|
||||
|
||||
g_config.links[CONFIG_LINK_S2].enabled = 0u;
|
||||
|
||||
g_config.links[CONFIG_LINK_C1].enabled = (legacy->remote_port != 0u) ? 1u : 0u;
|
||||
g_config.links[CONFIG_LINK_C1].uart = LINK_UART_U1;
|
||||
g_config.links[CONFIG_LINK_C1].local_port = 8081u;
|
||||
memcpy(g_config.links[CONFIG_LINK_C1].remote_ip, legacy->remote_ip, sizeof(g_config.links[CONFIG_LINK_C1].remote_ip));
|
||||
g_config.links[CONFIG_LINK_C1].remote_port = legacy->remote_port;
|
||||
|
||||
g_config.links[CONFIG_LINK_C2].enabled = 0u;
|
||||
g_config.crc = config_calc_crc(&g_config);
|
||||
}
|
||||
|
||||
static bool try_load_legacy_config(void)
|
||||
{
|
||||
legacy_device_config_v2_t legacy;
|
||||
uint32_t expected_crc;
|
||||
|
||||
if (flash_param_read(&legacy, sizeof(legacy)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
expected_crc = flash_param_crc32(&legacy, offsetof(legacy_device_config_v2_t, crc));
|
||||
if (legacy.magic != CONFIG_MAGIC || legacy.version != 0x0002u || legacy.crc != expected_crc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
migrate_legacy_config(&legacy);
|
||||
return true;
|
||||
}
|
||||
|
||||
static at_result_t handle_summary_query(char *response, uint16_t max_len)
|
||||
{
|
||||
char ip_str[16];
|
||||
char mask_str[16];
|
||||
char gw_str[16];
|
||||
char rip_str[16];
|
||||
char mac_str[18];
|
||||
char rip_str[CONFIG_LINK_COUNT][16];
|
||||
|
||||
config_ip_to_str(g_config.ip, ip_str);
|
||||
config_ip_to_str(g_config.mask, mask_str);
|
||||
config_ip_to_str(g_config.gw, gw_str);
|
||||
config_ip_to_str(g_config.remote_ip, rip_str);
|
||||
config_mac_to_str(g_config.mac, mac_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.gw, gw_str);
|
||||
config_mac_to_str(g_config.net.mac, mac_str);
|
||||
for (uint32_t i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||
config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]);
|
||||
}
|
||||
|
||||
snprintf(response, max_len,
|
||||
"MAC: %s\r\n"
|
||||
"DHCP: %u\r\n"
|
||||
"IP: %s\r\n"
|
||||
"MASK: %s\r\n"
|
||||
"GW: %s\r\n"
|
||||
"PORT: %u\r\n"
|
||||
"RIP: %s\r\n"
|
||||
"RPORT: %u\r\n"
|
||||
"BAUD1: %lu\r\n"
|
||||
"BAUD2: %lu\r\n",
|
||||
mac_str,
|
||||
g_config.dhcp_enable,
|
||||
ip_str,
|
||||
mask_str,
|
||||
gw_str,
|
||||
g_config.server_port,
|
||||
rip_str,
|
||||
g_config.remote_port,
|
||||
g_config.uart2_baudrate,
|
||||
g_config.uart3_baudrate);
|
||||
"+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\n"
|
||||
"+LINK:0,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||
"+LINK:1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||
"+LINK:2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||
"+LINK:3,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||
"+MUX:%u\r\n"
|
||||
"+MAP:UART2=0x04,UART3=0x08,C1=0x01,C2=0x02,S1=0x10,S2=0x20\r\n"
|
||||
"+BAUD:U0=%lu,U1=%lu\r\n"
|
||||
"OK\r\n",
|
||||
ip_str, mask_str, gw_str, mac_str,
|
||||
g_config.links[0].enabled, g_config.links[0].local_port, rip_str[0], g_config.links[0].remote_port, link_uart_to_str(g_config.links[0].uart),
|
||||
g_config.links[1].enabled, g_config.links[1].local_port, rip_str[1], g_config.links[1].remote_port, link_uart_to_str(g_config.links[1].uart),
|
||||
g_config.links[2].enabled, g_config.links[2].local_port, rip_str[2], g_config.links[2].remote_port, link_uart_to_str(g_config.links[2].uart),
|
||||
g_config.links[3].enabled, g_config.links[3].local_port, rip_str[3], g_config.links[3].remote_port, link_uart_to_str(g_config.links[3].uart),
|
||||
g_config.mux_mode,
|
||||
g_config.uart_baudrate[0],
|
||||
g_config.uart_baudrate[1]);
|
||||
|
||||
return AT_OK;
|
||||
}
|
||||
|
||||
static at_result_t handle_net_query(char *response, uint16_t max_len)
|
||||
{
|
||||
char ip_str[16];
|
||||
char mask_str[16];
|
||||
char gw_str[16];
|
||||
char mac_str[18];
|
||||
|
||||
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.gw, gw_str);
|
||||
config_mac_to_str(g_config.net.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);
|
||||
return AT_OK;
|
||||
}
|
||||
|
||||
static at_result_t handle_link_query(uint32_t index, char *response, uint16_t max_len)
|
||||
{
|
||||
char rip_str[16];
|
||||
|
||||
if (index >= CONFIG_LINK_COUNT) {
|
||||
snprintf(response, max_len, "ERROR: Invalid route field\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
config_ip_to_str(g_config.links[index].remote_ip, rip_str);
|
||||
snprintf(response,
|
||||
max_len,
|
||||
"+LINK:%lu,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\nOK\r\n",
|
||||
index,
|
||||
g_config.links[index].enabled,
|
||||
g_config.links[index].local_port,
|
||||
rip_str,
|
||||
g_config.links[index].remote_port,
|
||||
link_uart_to_str(g_config.links[index].uart));
|
||||
return AT_OK;
|
||||
}
|
||||
|
||||
static at_result_t handle_all_link_query(char *response, uint16_t max_len)
|
||||
{
|
||||
char rip_str[CONFIG_LINK_COUNT][16];
|
||||
|
||||
for (uint32_t i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
||||
config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]);
|
||||
}
|
||||
|
||||
snprintf(response,
|
||||
max_len,
|
||||
"+LINK:0,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||
"+LINK:1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||
"+LINK:2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||
"+LINK:3,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
||||
"OK\r\n",
|
||||
g_config.links[0].enabled, g_config.links[0].local_port, rip_str[0], g_config.links[0].remote_port, link_uart_to_str(g_config.links[0].uart),
|
||||
g_config.links[1].enabled, g_config.links[1].local_port, rip_str[1], g_config.links[1].remote_port, link_uart_to_str(g_config.links[1].uart),
|
||||
g_config.links[2].enabled, g_config.links[2].local_port, rip_str[2], g_config.links[2].remote_port, link_uart_to_str(g_config.links[2].uart),
|
||||
g_config.links[3].enabled, g_config.links[3].local_port, rip_str[3], g_config.links[3].remote_port, link_uart_to_str(g_config.links[3].uart));
|
||||
return AT_OK;
|
||||
}
|
||||
|
||||
int config_init(void)
|
||||
{
|
||||
flash_param_init();
|
||||
@@ -136,15 +396,19 @@ int config_init(void)
|
||||
|
||||
int config_load(void)
|
||||
{
|
||||
if (flash_param_read(&g_config, sizeof(g_config)) != 0 ||
|
||||
g_config.magic != CONFIG_MAGIC ||
|
||||
g_config.version != CONFIG_VERSION ||
|
||||
g_config.crc != config_calc_crc(&g_config)) {
|
||||
config_set_defaults();
|
||||
return -1;
|
||||
if (flash_param_read(&g_config, sizeof(g_config)) == 0 &&
|
||||
g_config.magic == CONFIG_MAGIC &&
|
||||
g_config.version == CONFIG_VERSION &&
|
||||
g_config.crc == config_calc_crc(&g_config)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (try_load_legacy_config()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
config_set_defaults();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int config_save(void)
|
||||
@@ -157,33 +421,22 @@ int config_save(void)
|
||||
|
||||
void config_set_defaults(void)
|
||||
{
|
||||
const uint8_t default_ip[] = DEFAULT_IP;
|
||||
const uint8_t default_mask[] = DEFAULT_MASK;
|
||||
const uint8_t default_gw[] = DEFAULT_GW;
|
||||
const uint8_t default_mac[] = DEFAULT_MAC;
|
||||
const uint8_t default_rip[] = DEFAULT_REMOTE_IP;
|
||||
const uint8_t default_ip[] = DEFAULT_NET_IP;
|
||||
const uint8_t default_mask[] = DEFAULT_NET_MASK;
|
||||
const uint8_t default_gw[] = DEFAULT_NET_GW;
|
||||
const uint8_t default_mac[] = DEFAULT_NET_MAC;
|
||||
|
||||
memset(&g_config, 0, sizeof(g_config));
|
||||
g_config.magic = CONFIG_MAGIC;
|
||||
g_config.version = CONFIG_VERSION;
|
||||
memcpy(g_config.mac, default_mac, sizeof(g_config.mac));
|
||||
memcpy(g_config.ip, default_ip, sizeof(g_config.ip));
|
||||
memcpy(g_config.mask, default_mask, sizeof(g_config.mask));
|
||||
memcpy(g_config.gw, default_gw, sizeof(g_config.gw));
|
||||
memcpy(g_config.remote_ip, default_rip, sizeof(g_config.remote_ip));
|
||||
|
||||
g_config.dhcp_enable = DEFAULT_DHCP_ENABLE;
|
||||
g_config.server_port = DEFAULT_SERVER_PORT;
|
||||
g_config.remote_port = DEFAULT_REMOTE_PORT;
|
||||
g_config.reconnect_interval = DEFAULT_RECONNECT_MS;
|
||||
g_config.uart2_baudrate = DEFAULT_UART_BAUDRATE;
|
||||
g_config.uart3_baudrate = DEFAULT_UART_BAUDRATE;
|
||||
g_config.uart2_databits = DEFAULT_UART_DATABITS;
|
||||
g_config.uart2_stopbits = DEFAULT_UART_STOPBITS;
|
||||
g_config.uart2_parity = DEFAULT_UART_PARITY;
|
||||
g_config.uart3_databits = DEFAULT_UART_DATABITS;
|
||||
g_config.uart3_stopbits = DEFAULT_UART_STOPBITS;
|
||||
g_config.uart3_parity = DEFAULT_UART_PARITY;
|
||||
g_config.mux_mode = MUX_MODE_RAW;
|
||||
memcpy(g_config.net.ip, default_ip, sizeof(g_config.net.ip));
|
||||
memcpy(g_config.net.mask, default_mask, sizeof(g_config.net.mask));
|
||||
memcpy(g_config.net.gw, default_gw, sizeof(g_config.net.gw));
|
||||
memcpy(g_config.net.mac, default_mac, sizeof(g_config.net.mac));
|
||||
set_link_defaults();
|
||||
g_config.uart_baudrate[0] = DEFAULT_UART_BAUDRATE;
|
||||
g_config.uart_baudrate[1] = DEFAULT_UART_BAUDRATE;
|
||||
g_config.crc = config_calc_crc(&g_config);
|
||||
}
|
||||
|
||||
@@ -199,50 +452,37 @@ device_config_t *config_get_mutable(void)
|
||||
|
||||
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len)
|
||||
{
|
||||
char cmd_copy[CONFIG_CMD_MAX_LEN];
|
||||
char *cmd_name;
|
||||
char *value;
|
||||
const char *value;
|
||||
const char *p;
|
||||
|
||||
if (cmd == NULL || response == NULL || max_len == 0u) {
|
||||
return AT_ERROR;
|
||||
}
|
||||
|
||||
strncpy(cmd_copy, cmd, CONFIG_CMD_MAX_LEN - 1u);
|
||||
cmd_copy[CONFIG_CMD_MAX_LEN - 1u] = '\0';
|
||||
trim_trailing(cmd_copy);
|
||||
p = skip_whitespace(cmd_copy);
|
||||
strncpy(g_cmd_work_buffer, cmd, sizeof(g_cmd_work_buffer) - 1u);
|
||||
g_cmd_work_buffer[sizeof(g_cmd_work_buffer) - 1u] = '\0';
|
||||
trim_trailing(g_cmd_work_buffer);
|
||||
p = skip_whitespace(g_cmd_work_buffer);
|
||||
|
||||
if ((p[0] != 'A' && p[0] != 'a') || (p[1] != 'T' && p[1] != 't')) {
|
||||
snprintf(response, max_len, "ERROR: Unknown command\r\n");
|
||||
return AT_UNKNOWN_CMD;
|
||||
}
|
||||
|
||||
if (p[2] == '\0') {
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_OK;
|
||||
}
|
||||
|
||||
if (p[2] != '+') {
|
||||
snprintf(response, max_len, "ERROR: Unknown command\r\n");
|
||||
return AT_UNKNOWN_CMD;
|
||||
}
|
||||
|
||||
p += 3;
|
||||
value = strchr((char *)p, '=');
|
||||
if (value != NULL) {
|
||||
*value = '\0';
|
||||
++value;
|
||||
value = (char *)skip_whitespace(value);
|
||||
}
|
||||
|
||||
cmd_name = (char *)p;
|
||||
trim_trailing(cmd_name);
|
||||
|
||||
if ((equals_ignore_case(cmd_name, "?") || equals_ignore_case(cmd_name, "QUERY")) && value == NULL) {
|
||||
return handle_query(response, max_len);
|
||||
if ((equals_ignore_case(p, "?") || equals_ignore_case(p, "QUERY"))) {
|
||||
return handle_summary_query(response, max_len);
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "SAVE") && value == NULL) {
|
||||
if (equals_ignore_case(p, "SAVE")) {
|
||||
if (config_save() != 0) {
|
||||
snprintf(response, max_len, "ERROR: Save failed\r\n");
|
||||
return AT_SAVE_FAILED;
|
||||
@@ -250,103 +490,139 @@ at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_
|
||||
snprintf(response, max_len, "OK: Configuration saved\r\n");
|
||||
return AT_OK;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "RESET") && value == NULL) {
|
||||
if (equals_ignore_case(p, "RESET")) {
|
||||
g_reset_requested = true;
|
||||
snprintf(response, max_len, "OK: Resetting...\r\n");
|
||||
return AT_OK;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "DEFAULT") && value == NULL) {
|
||||
if (equals_ignore_case(p, "DEFAULT")) {
|
||||
config_set_defaults();
|
||||
snprintf(response, max_len, "OK: Defaults restored\r\n");
|
||||
return AT_OK;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "IP") && value != NULL) {
|
||||
if (config_str_to_ip(value, g_config.ip) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid IP format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
if (equals_ignore_case(p, "MUX?")) {
|
||||
snprintf(response, max_len, "+MUX:%u\r\nOK\r\n", g_config.mux_mode);
|
||||
return AT_OK;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "MASK") && value != NULL) {
|
||||
if (config_str_to_ip(value, g_config.mask) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid mask format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "GW") && value != NULL) {
|
||||
if (config_str_to_ip(value, g_config.gw) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid gateway format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "RIP") && value != NULL) {
|
||||
if (config_str_to_ip(value, g_config.remote_ip) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid remote IP format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "MAC") && value != NULL) {
|
||||
if (config_str_to_mac(value, g_config.mac) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid MAC format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "PORT") && value != NULL) {
|
||||
int port = atoi(value);
|
||||
if (port < 1 || port > 65535) {
|
||||
snprintf(response, max_len, "ERROR: Invalid port\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
g_config.server_port = (uint16_t)port;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "RPORT") && value != NULL) {
|
||||
int port = atoi(value);
|
||||
if (port < 1 || port > 65535) {
|
||||
snprintf(response, max_len, "ERROR: Invalid port\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
g_config.remote_port = (uint16_t)port;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "BAUD1") && value != NULL) {
|
||||
if (parse_baudrate_value(value, &g_config.uart2_baudrate) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid baudrate\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "BAUD2") && value != NULL) {
|
||||
if (parse_baudrate_value(value, &g_config.uart3_baudrate) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid baudrate\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
if (equals_ignore_case(cmd_name, "DHCP") && value != NULL) {
|
||||
int dhcp = atoi(value);
|
||||
if (dhcp != 0 && dhcp != 1) {
|
||||
if (parse_command_with_value(p, "MUX", &value)) {
|
||||
uint32_t mux_value;
|
||||
if (parse_u32_value(value, 0u, 1u, &mux_value) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid value\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
if (dhcp != 0) {
|
||||
snprintf(response, max_len, "ERROR: DHCP disabled in this build\r\n");
|
||||
g_config.mux_mode = (uint8_t)mux_value;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
if (equals_ignore_case(p, "NET?")) {
|
||||
return handle_net_query(response, max_len);
|
||||
}
|
||||
if (parse_command_with_value(p, "NET", &value)) {
|
||||
char value_copy[96];
|
||||
char *token;
|
||||
char *cursor;
|
||||
uint8_t ip[4];
|
||||
uint8_t mask[4];
|
||||
uint8_t gw[4];
|
||||
uint8_t mac[6];
|
||||
|
||||
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 || config_str_to_ip(token, ip) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid IP format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
g_config.dhcp_enable = (uint8_t)dhcp;
|
||||
token = config_next_token(&cursor);
|
||||
if (token == NULL || config_str_to_ip(token, mask) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid mask format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
token = config_next_token(&cursor);
|
||||
if (token == NULL || config_str_to_ip(token, gw) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid gateway format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
token = config_next_token(&cursor);
|
||||
if (token == NULL || config_str_to_mac(token, mac) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid MAC format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
if (config_next_token(&cursor) != NULL) {
|
||||
snprintf(response, max_len, "ERROR: Invalid value\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
memcpy(g_config.net.ip, ip, sizeof(ip));
|
||||
memcpy(g_config.net.mask, mask, sizeof(mask));
|
||||
memcpy(g_config.net.gw, gw, sizeof(gw));
|
||||
memcpy(g_config.net.mac, mac, sizeof(mac));
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
if (equals_ignore_case(p, "LINK?")) {
|
||||
return handle_all_link_query(response, max_len);
|
||||
}
|
||||
if (parse_command_with_value(p, "LINK", &value)) {
|
||||
char value_copy[96];
|
||||
char *cursor;
|
||||
char *token;
|
||||
uint32_t index;
|
||||
uint32_t enabled;
|
||||
uint32_t local_port;
|
||||
uint32_t remote_port;
|
||||
uint8_t rip[4];
|
||||
uint8_t uart;
|
||||
|
||||
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_u32_value(token, 0u, CONFIG_LINK_COUNT - 1u, &index) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid route field\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
token = config_next_token(&cursor);
|
||||
if (token == NULL) {
|
||||
return handle_link_query(index, response, max_len);
|
||||
}
|
||||
if (parse_u32_value(token, 0u, 1u, &enabled) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid value\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
token = config_next_token(&cursor);
|
||||
if (token == NULL || parse_u32_value(token, 1u, 65535u, &local_port) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid port\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
token = config_next_token(&cursor);
|
||||
if (token == NULL || config_str_to_ip(token, rip) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid remote IP format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
token = config_next_token(&cursor);
|
||||
if (token == NULL || parse_u32_value(token, 0u, 65535u, &remote_port) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid port\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
token = config_next_token(&cursor);
|
||||
if (token == NULL || parse_link_uart(token, &uart) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid route field\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
if (config_next_token(&cursor) != NULL) {
|
||||
snprintf(response, max_len, "ERROR: Invalid value\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
g_config.links[index].enabled = (uint8_t)enabled;
|
||||
g_config.links[index].local_port = (uint16_t)local_port;
|
||||
memcpy(g_config.links[index].remote_ip, rip, sizeof(rip));
|
||||
g_config.links[index].remote_port = (uint16_t)remote_port;
|
||||
g_config.links[index].uart = uart;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
@@ -362,7 +638,10 @@ void config_ip_to_str(const uint8_t *ip, char *str)
|
||||
|
||||
int config_str_to_ip(const char *str, uint8_t *ip)
|
||||
{
|
||||
int a, b, c, d;
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
|
||||
if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) {
|
||||
return -1;
|
||||
@@ -398,7 +677,6 @@ int config_str_to_mac(const char *str, uint8_t *mac)
|
||||
}
|
||||
mac[i] = (uint8_t)a[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -406,7 +684,6 @@ void config_poll(void)
|
||||
{
|
||||
if (g_pending_cmd_ready) {
|
||||
uint16_t len = g_pending_cmd_len;
|
||||
|
||||
g_pending_cmd_ready = false;
|
||||
g_pending_cmd_len = 0u;
|
||||
(void)config_try_process_frame((const uint8_t *)g_pending_cmd_buffer, len);
|
||||
@@ -415,8 +692,13 @@ void config_poll(void)
|
||||
|
||||
void config_uart_rx_byte(uint8_t byte)
|
||||
{
|
||||
if (byte == '\r' || byte == '\n') {
|
||||
if (g_uart_cmd_len > 0u) {
|
||||
if (byte == '\r') {
|
||||
g_uart_rx_seen_cr = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (byte == '\n') {
|
||||
if (g_uart_rx_seen_cr && g_uart_cmd_len > 0u) {
|
||||
if (!g_pending_cmd_ready) {
|
||||
memcpy(g_pending_cmd_buffer, g_uart_cmd_buffer, g_uart_cmd_len);
|
||||
g_pending_cmd_buffer[g_uart_cmd_len] = '\0';
|
||||
@@ -425,43 +707,58 @@ void config_uart_rx_byte(uint8_t byte)
|
||||
}
|
||||
g_uart_cmd_len = 0u;
|
||||
}
|
||||
g_uart_rx_seen_cr = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_uart_rx_seen_cr) {
|
||||
g_uart_cmd_len = 0u;
|
||||
g_uart_rx_seen_cr = false;
|
||||
}
|
||||
|
||||
if (g_uart_cmd_len < (CONFIG_CMD_MAX_LEN - 1u)) {
|
||||
g_uart_cmd_buffer[g_uart_cmd_len++] = (char)byte;
|
||||
g_uart_cmd_buffer[g_uart_cmd_len++] = byte;
|
||||
g_uart_cmd_buffer[g_uart_cmd_len] = '\0';
|
||||
} else {
|
||||
g_uart_cmd_len = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
bool config_build_response_frame(const uint8_t *data,
|
||||
uint16_t len,
|
||||
char *response,
|
||||
uint16_t max_len,
|
||||
at_result_t *result)
|
||||
{
|
||||
if (data == NULL || response == NULL || len < 2u || max_len == 0u) {
|
||||
return false;
|
||||
}
|
||||
if (len >= CONFIG_CMD_MAX_LEN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(g_cmd_parse_buffer, data, len);
|
||||
g_cmd_parse_buffer[len] = '\0';
|
||||
if (((g_cmd_parse_buffer[0] != 'A') && (g_cmd_parse_buffer[0] != 'a')) ||
|
||||
((g_cmd_parse_buffer[1] != 'T') && (g_cmd_parse_buffer[1] != 't'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = config_process_at_cmd(g_cmd_parse_buffer, response, max_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool config_try_process_frame(const uint8_t *data, uint16_t len)
|
||||
{
|
||||
char response[CONFIG_TX_BUFFER_SIZE];
|
||||
char cmd_buffer[CONFIG_CMD_MAX_LEN];
|
||||
at_result_t result;
|
||||
at_result_t result = AT_ERROR;
|
||||
|
||||
if (data == NULL || len < 2u) {
|
||||
if (!config_build_response_frame(data, len, g_at_response_buffer, sizeof(g_at_response_buffer), &result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (len >= CONFIG_CMD_MAX_LEN) {
|
||||
len = CONFIG_CMD_MAX_LEN - 1u;
|
||||
}
|
||||
|
||||
memcpy(cmd_buffer, data, len);
|
||||
cmd_buffer[len] = '\0';
|
||||
|
||||
if (((cmd_buffer[0] != 'A') && (cmd_buffer[0] != 'a')) ||
|
||||
((cmd_buffer[1] != 'T') && (cmd_buffer[1] != 't'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result = config_process_at_cmd(cmd_buffer, response, sizeof(response));
|
||||
if (HAL_UART_Transmit(&CONFIG_UART_HANDLE,
|
||||
(uint8_t *)response,
|
||||
(uint16_t)strlen(response),
|
||||
(uint8_t *)g_at_response_buffer,
|
||||
(uint16_t)strlen(g_at_response_buffer),
|
||||
1000u) != HAL_OK) {
|
||||
return false;
|
||||
}
|
||||
@@ -488,3 +785,31 @@ void config_clear_reset_requested(void)
|
||||
{
|
||||
g_reset_requested = false;
|
||||
}
|
||||
|
||||
uint8_t config_link_index_to_endpoint(uint8_t index)
|
||||
{
|
||||
switch (index) {
|
||||
case CONFIG_LINK_S1:
|
||||
return ENDPOINT_S1;
|
||||
case CONFIG_LINK_S2:
|
||||
return ENDPOINT_S2;
|
||||
case CONFIG_LINK_C1:
|
||||
return ENDPOINT_C1;
|
||||
case CONFIG_LINK_C2:
|
||||
return ENDPOINT_C2;
|
||||
default:
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t config_uart_index_to_endpoint(uint8_t uart_index)
|
||||
{
|
||||
return (uart_index == LINK_UART_U1) ? ENDPOINT_UART3 : ENDPOINT_UART2;
|
||||
}
|
||||
|
||||
bool config_endpoint_is_single(uint8_t endpoint)
|
||||
{
|
||||
return endpoint == ENDPOINT_C1 || endpoint == ENDPOINT_C2 ||
|
||||
endpoint == ENDPOINT_UART2 || endpoint == ENDPOINT_UART3 ||
|
||||
endpoint == ENDPOINT_S1 || endpoint == ENDPOINT_S2;
|
||||
}
|
||||
|
||||
+63
-159
@@ -1,96 +1,78 @@
|
||||
/**
|
||||
* @file config.h
|
||||
* @brief AT command configuration module for TCP2UART
|
||||
*
|
||||
* Handles UART1 AT commands for network and serial port configuration.
|
||||
*
|
||||
* Supported AT commands:
|
||||
* - AT+IP=192.168.1.100 Set device IP
|
||||
* - AT+MASK=255.255.255.0 Set subnet mask
|
||||
* - AT+GW=192.168.1.1 Set gateway
|
||||
* - AT+PORT=8080 Set TCP Server listen port
|
||||
* - AT+RIP=192.168.1.200 Set TCP Client remote IP
|
||||
* - AT+RPORT=9000 Set TCP Client remote port
|
||||
* - AT+BAUD1=115200 Set UART2 baudrate
|
||||
* - AT+BAUD2=115200 Set UART3 baudrate
|
||||
* - AT+MAC=00:11:22:33:44:55 Set MAC address
|
||||
* - AT+DHCP=0/1 Enable/disable DHCP
|
||||
* - AT+SAVE Save parameters to Flash
|
||||
* - AT+RESET Reset device
|
||||
* - AT+DEFAULT Restore factory defaults
|
||||
* - AT+? Query current configuration
|
||||
* @brief Final AT configuration model for TCP2UART.
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_H__
|
||||
#define __CONFIG_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Configuration magic number "TCPU" */
|
||||
#define CONFIG_MAGIC 0x54435055
|
||||
#define CONFIG_MAGIC 0x54435055u
|
||||
#define CONFIG_VERSION 0x0003u
|
||||
|
||||
/* Configuration version for compatibility */
|
||||
#define CONFIG_VERSION 0x0002
|
||||
#define CONFIG_UART_COUNT 2u
|
||||
#define CONFIG_LINK_COUNT 4u
|
||||
|
||||
#define CONFIG_LINK_S1 0u
|
||||
#define CONFIG_LINK_S2 1u
|
||||
#define CONFIG_LINK_C1 2u
|
||||
#define CONFIG_LINK_C2 3u
|
||||
|
||||
#define ENDPOINT_C1 0x01u
|
||||
#define ENDPOINT_C2 0x02u
|
||||
#define ENDPOINT_UART2 0x04u
|
||||
#define ENDPOINT_UART3 0x08u
|
||||
#define ENDPOINT_S1 0x10u
|
||||
#define ENDPOINT_S2 0x20u
|
||||
|
||||
#define LINK_UART_U0 0u
|
||||
#define LINK_UART_U1 1u
|
||||
|
||||
typedef enum {
|
||||
MUX_MODE_RAW = 0,
|
||||
MUX_MODE_FRAME = 1
|
||||
} mux_mode_t;
|
||||
|
||||
/* Device configuration structure */
|
||||
typedef struct {
|
||||
uint32_t magic; /* Magic number for validation */
|
||||
uint16_t version; /* Configuration version */
|
||||
uint16_t reserved; /* Reserved for alignment */
|
||||
|
||||
/* Network settings */
|
||||
uint8_t mac[6]; /* MAC address */
|
||||
uint8_t dhcp_enable; /* DHCP enable flag */
|
||||
uint8_t reserved2; /* Reserved for alignment */
|
||||
uint8_t ip[4]; /* Device IP address */
|
||||
uint8_t mask[4]; /* Subnet mask */
|
||||
uint8_t gw[4]; /* Gateway */
|
||||
|
||||
/* TCP Server settings */
|
||||
uint16_t server_port; /* Server listen port */
|
||||
uint16_t reserved3; /* Reserved for alignment */
|
||||
|
||||
/* TCP Client settings */
|
||||
uint8_t remote_ip[4]; /* Remote server IP */
|
||||
uint16_t remote_port; /* Remote server port */
|
||||
uint16_t reconnect_interval;/* Reconnect interval (ms) */
|
||||
|
||||
/* UART settings */
|
||||
uint32_t uart2_baudrate; /* UART2 (Server) baudrate */
|
||||
uint32_t uart3_baudrate; /* UART3 (Client) baudrate */
|
||||
uint8_t uart2_databits; /* UART2 data bits */
|
||||
uint8_t uart2_stopbits; /* UART2 stop bits */
|
||||
uint8_t uart2_parity; /* UART2 parity */
|
||||
uint8_t uart3_databits; /* UART3 data bits */
|
||||
uint8_t uart3_stopbits; /* UART3 stop bits */
|
||||
uint8_t uart3_parity; /* UART3 parity */
|
||||
uint16_t reserved4; /* Reserved for alignment */
|
||||
|
||||
/* CRC32 checksum (must be last) */
|
||||
uint8_t ip[4];
|
||||
uint8_t mask[4];
|
||||
uint8_t gw[4];
|
||||
uint8_t mac[6];
|
||||
uint8_t reserved[2];
|
||||
} net_config_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t enabled;
|
||||
uint8_t uart;
|
||||
uint16_t local_port;
|
||||
uint8_t remote_ip[4];
|
||||
uint16_t remote_port;
|
||||
uint16_t reserved;
|
||||
} link_config_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
uint16_t version;
|
||||
uint8_t mux_mode;
|
||||
uint8_t reserved0;
|
||||
net_config_t net;
|
||||
link_config_t links[CONFIG_LINK_COUNT];
|
||||
uint32_t uart_baudrate[CONFIG_UART_COUNT];
|
||||
uint32_t crc;
|
||||
} device_config_t;
|
||||
|
||||
/* Default configuration values */
|
||||
#define DEFAULT_IP {192, 168, 31, 100}
|
||||
#define DEFAULT_MASK {255, 255, 255, 0}
|
||||
#define DEFAULT_GW {192, 168, 31, 1}
|
||||
#define DEFAULT_MAC {0x02, 0x00, 0x00, 0x00, 0x00, 0x01}
|
||||
#define DEFAULT_SERVER_PORT 8080
|
||||
#define DEFAULT_REMOTE_IP {192, 168, 31, 1}
|
||||
#define DEFAULT_REMOTE_PORT 8081
|
||||
#define DEFAULT_UART_BAUDRATE 115200
|
||||
#define DEFAULT_UART_DATABITS 8
|
||||
#define DEFAULT_UART_STOPBITS 1
|
||||
#define DEFAULT_UART_PARITY 0
|
||||
#define DEFAULT_DHCP_ENABLE 0
|
||||
#define DEFAULT_RECONNECT_MS 3000
|
||||
#define DEFAULT_NET_IP {192, 168, 1, 100}
|
||||
#define DEFAULT_NET_MASK {255, 255, 255, 0}
|
||||
#define DEFAULT_NET_GW {192, 168, 1, 1}
|
||||
#define DEFAULT_NET_MAC {0x02, 0x00, 0x00, 0x00, 0x00, 0x01}
|
||||
#define DEFAULT_UART_BAUDRATE 115200u
|
||||
|
||||
/* AT command result codes */
|
||||
typedef enum {
|
||||
AT_OK = 0,
|
||||
AT_ERROR,
|
||||
@@ -100,108 +82,30 @@ typedef enum {
|
||||
AT_NEED_REBOOT
|
||||
} at_result_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize configuration module
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int config_init(void);
|
||||
|
||||
/**
|
||||
* @brief Load configuration from Flash
|
||||
* @return 0 on success, negative on error (defaults loaded)
|
||||
*/
|
||||
int config_load(void);
|
||||
|
||||
/**
|
||||
* @brief Save configuration to Flash
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int config_save(void);
|
||||
|
||||
/**
|
||||
* @brief Reset configuration to factory defaults
|
||||
*/
|
||||
void config_set_defaults(void);
|
||||
|
||||
/**
|
||||
* @brief Get current configuration
|
||||
* @return Pointer to current configuration (read-only)
|
||||
*/
|
||||
const device_config_t *config_get(void);
|
||||
|
||||
/**
|
||||
* @brief Get mutable configuration for modification
|
||||
* @return Pointer to configuration structure
|
||||
*/
|
||||
device_config_t *config_get_mutable(void);
|
||||
|
||||
/**
|
||||
* @brief Process AT command received from UART1
|
||||
* @param cmd Command string (null-terminated)
|
||||
* @param response Response buffer
|
||||
* @param max_len Maximum response length
|
||||
* @return AT command result code
|
||||
*/
|
||||
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len);
|
||||
|
||||
/**
|
||||
* @brief Poll configuration UART and process pending AT commands
|
||||
*/
|
||||
void config_poll(void);
|
||||
|
||||
/**
|
||||
* @brief Feed one byte received from the config UART.
|
||||
* @param byte Received byte.
|
||||
*/
|
||||
void config_uart_rx_byte(uint8_t byte);
|
||||
|
||||
/**
|
||||
* @brief Try to process one AT command frame from an external UART source.
|
||||
* @param data Input bytes.
|
||||
* @param len Input length.
|
||||
* @return true if the frame was recognized as an AT/config command.
|
||||
*/
|
||||
bool config_try_process_frame(const uint8_t *data, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief Check whether AT+RESET requested a system reset
|
||||
*/
|
||||
bool config_build_response_frame(const uint8_t *data,
|
||||
uint16_t len,
|
||||
char *response,
|
||||
uint16_t max_len,
|
||||
at_result_t *result);
|
||||
bool config_is_reset_requested(void);
|
||||
|
||||
/**
|
||||
* @brief Clear the pending reset request flag
|
||||
*/
|
||||
void config_clear_reset_requested(void);
|
||||
|
||||
/**
|
||||
* @brief Format IP address to string
|
||||
* @param ip IP address bytes
|
||||
* @param str Output string buffer (min 16 bytes)
|
||||
*/
|
||||
void config_ip_to_str(const uint8_t *ip, char *str);
|
||||
|
||||
/**
|
||||
* @brief Parse IP address from string
|
||||
* @param str IP address string (e.g. "192.168.1.100")
|
||||
* @param ip Output IP address bytes
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int config_str_to_ip(const char *str, uint8_t *ip);
|
||||
|
||||
/**
|
||||
* @brief Format MAC address to string
|
||||
* @param mac MAC address bytes
|
||||
* @param str Output string buffer (min 18 bytes)
|
||||
*/
|
||||
void config_mac_to_str(const uint8_t *mac, char *str);
|
||||
|
||||
/**
|
||||
* @brief Parse MAC address from string
|
||||
* @param str MAC address string (e.g. "00:11:22:33:44:55")
|
||||
* @param mac Output MAC address bytes
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int config_str_to_mac(const char *str, uint8_t *mac);
|
||||
uint8_t config_link_index_to_endpoint(uint8_t index);
|
||||
uint8_t config_uart_index_to_endpoint(uint8_t uart_index);
|
||||
bool config_endpoint_is_single(uint8_t endpoint);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+14
-45
@@ -19,41 +19,10 @@
|
||||
* Private Variables
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
/* CRC32 lookup table */
|
||||
static uint32_t g_crc_table[256];
|
||||
static bool g_crc_table_initialized = false;
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Private Functions
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Initialize CRC32 lookup table
|
||||
*/
|
||||
static void crc32_init_table(void)
|
||||
{
|
||||
uint32_t i, j, crc;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
crc = i;
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
if (crc & 1)
|
||||
{
|
||||
crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
g_crc_table[i] = crc;
|
||||
}
|
||||
|
||||
g_crc_table_initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unlock Flash for writing
|
||||
*/
|
||||
@@ -105,12 +74,6 @@ static HAL_StatusTypeDef flash_program_halfword(uint32_t addr, uint16_t data)
|
||||
*/
|
||||
int flash_param_init(void)
|
||||
{
|
||||
/* Initialize CRC table */
|
||||
if (!g_crc_table_initialized)
|
||||
{
|
||||
crc32_init_table();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -243,16 +206,22 @@ uint32_t flash_param_crc32(const void *data, uint32_t len)
|
||||
const uint8_t *p = (const uint8_t *)data;
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
uint32_t i;
|
||||
|
||||
/* Initialize table if needed */
|
||||
if (!g_crc_table_initialized)
|
||||
{
|
||||
crc32_init_table();
|
||||
}
|
||||
|
||||
uint32_t j;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
crc = g_crc_table[(crc ^ p[i]) & 0xFF] ^ (crc >> 8);
|
||||
crc ^= p[i];
|
||||
for (j = 0; j < 8u; ++j)
|
||||
{
|
||||
if ((crc & 1u) != 0u)
|
||||
{
|
||||
crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc ^ 0xFFFFFFFF;
|
||||
|
||||
+120
-145
@@ -1,17 +1,14 @@
|
||||
/**
|
||||
* @file tcp_client.c
|
||||
* @brief lwIP RAW TCP client for the UART3 bridge.
|
||||
* @brief Indexed lwIP RAW TCP client manager.
|
||||
*/
|
||||
|
||||
#include "tcp_client.h"
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include "SEGGER_RTT.h"
|
||||
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "../Core/Inc/main.h"
|
||||
#include "../Drivers/LwIP/src/include/lwip/ip_addr.h"
|
||||
#include "../Drivers/LwIP/src/include/lwip/pbuf.h"
|
||||
#include "../Drivers/LwIP/src/include/lwip/tcp.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -21,11 +18,12 @@ typedef struct {
|
||||
uint16_t rx_head;
|
||||
uint16_t rx_tail;
|
||||
uint32_t next_retry_ms;
|
||||
tcp_client_config_t config;
|
||||
uint8_t index;
|
||||
tcp_client_instance_config_t config;
|
||||
tcp_client_status_t status;
|
||||
} tcp_client_ctx_t;
|
||||
|
||||
static tcp_client_ctx_t g_client;
|
||||
static tcp_client_ctx_t g_clients[TCP_CLIENT_INSTANCE_COUNT];
|
||||
|
||||
static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size)
|
||||
{
|
||||
@@ -37,13 +35,18 @@ static err_t tcp_client_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p,
|
||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||
struct pbuf *q;
|
||||
|
||||
if (ctx == NULL) {
|
||||
if (p != NULL) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
return ERR_ARG;
|
||||
}
|
||||
if (err != ERR_OK) {
|
||||
if (p != NULL) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
tcp_arg(pcb, NULL);
|
||||
tcp_recv(pcb, NULL);
|
||||
@@ -80,7 +83,9 @@ static err_t tcp_client_on_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
|
||||
{
|
||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||
(void)pcb;
|
||||
ctx->status.tx_bytes += len;
|
||||
if (ctx != NULL) {
|
||||
ctx->status.tx_bytes += len;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@@ -90,34 +95,30 @@ static void tcp_client_on_err(void *arg, err_t err)
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->pcb = NULL;
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
SEGGER_RTT_printf(0, "TCP client error=%d, reconnect scheduled\r\n", (int)err);
|
||||
(void)err;
|
||||
}
|
||||
|
||||
static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
|
||||
{
|
||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||
|
||||
if (ctx == NULL) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if (err != ERR_OK) {
|
||||
ctx->pcb = NULL;
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
SEGGER_RTT_printf(0, "TCP client connect callback failed err=%d\r\n", (int)err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ctx->pcb = pcb;
|
||||
ctx->status.state = TCP_CLIENT_STATE_CONNECTED;
|
||||
SEGGER_RTT_printf(0,
|
||||
"TCP client connected to %u.%u.%u.%u:%u\r\n",
|
||||
ctx->config.server_ip[0], ctx->config.server_ip[1],
|
||||
ctx->config.server_ip[2], ctx->config.server_ip[3],
|
||||
ctx->config.server_port);
|
||||
tcp_nagle_disable(pcb);
|
||||
tcp_arg(pcb, ctx);
|
||||
tcp_recv(pcb, tcp_client_on_recv);
|
||||
@@ -126,207 +127,181 @@ static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int tcp_client_init(const tcp_client_config_t *config)
|
||||
int tcp_client_init_all(void)
|
||||
{
|
||||
memset(&g_client, 0, sizeof(g_client));
|
||||
g_client.config.server_ip[0] = 192u;
|
||||
g_client.config.server_ip[1] = 168u;
|
||||
g_client.config.server_ip[2] = 31u;
|
||||
g_client.config.server_ip[3] = 1u;
|
||||
g_client.config.local_port = TCP_CLIENT_DEFAULT_PORT;
|
||||
g_client.config.server_port = TCP_CLIENT_DEFAULT_PORT;
|
||||
g_client.config.auto_reconnect = true;
|
||||
g_client.config.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS;
|
||||
g_client.status.state = TCP_CLIENT_STATE_IDLE;
|
||||
|
||||
if (config != NULL) {
|
||||
g_client.config = *config;
|
||||
memset(g_clients, 0, sizeof(g_clients));
|
||||
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
||||
g_clients[i].index = i;
|
||||
g_clients[i].status.state = TCP_CLIENT_STATE_IDLE;
|
||||
g_clients[i].config.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS;
|
||||
g_clients[i].config.auto_reconnect = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_client_connect(void)
|
||||
int tcp_client_config(uint8_t instance, const tcp_client_instance_config_t *config)
|
||||
{
|
||||
if (instance >= TCP_CLIENT_INSTANCE_COUNT || config == NULL) {
|
||||
return -1;
|
||||
}
|
||||
g_clients[instance].config = *config;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_client_connect(uint8_t instance)
|
||||
{
|
||||
struct tcp_pcb *pcb;
|
||||
ip_addr_t remote_addr;
|
||||
err_t err;
|
||||
tcp_client_ctx_t *ctx;
|
||||
|
||||
if (g_client.pcb != NULL) {
|
||||
if (instance >= TCP_CLIENT_INSTANCE_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
ctx = &g_clients[instance];
|
||||
if (!ctx->config.enabled) {
|
||||
return 0;
|
||||
}
|
||||
if (ctx->pcb != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
|
||||
if (pcb == NULL) {
|
||||
g_client.status.errors++;
|
||||
g_client.status.state = TCP_CLIENT_STATE_ERROR;
|
||||
SEGGER_RTT_WriteString(0, "TCP client connect failed: no PCB\r\n");
|
||||
ctx->status.errors++;
|
||||
ctx->status.state = TCP_CLIENT_STATE_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (g_client.config.local_port != 0u) {
|
||||
err = tcp_bind(pcb, IP_ANY_TYPE, g_client.config.local_port);
|
||||
if (ctx->config.local_port != 0u) {
|
||||
err = tcp_bind(pcb, IP_ANY_TYPE, ctx->config.local_port);
|
||||
if (err != ERR_OK) {
|
||||
tcp_abort(pcb);
|
||||
g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
g_client.status.errors++;
|
||||
g_client.next_retry_ms = HAL_GetTick() + g_client.config.reconnect_interval_ms;
|
||||
SEGGER_RTT_printf(0, "TCP client bind failed err=%d\r\n", (int)err);
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
IP_ADDR4(&remote_addr,
|
||||
g_client.config.server_ip[0],
|
||||
g_client.config.server_ip[1],
|
||||
g_client.config.server_ip[2],
|
||||
g_client.config.server_ip[3]);
|
||||
ctx->config.remote_ip[0],
|
||||
ctx->config.remote_ip[1],
|
||||
ctx->config.remote_ip[2],
|
||||
ctx->config.remote_ip[3]);
|
||||
|
||||
g_client.status.state = TCP_CLIENT_STATE_CONNECTING;
|
||||
tcp_arg(pcb, &g_client);
|
||||
ctx->status.state = TCP_CLIENT_STATE_CONNECTING;
|
||||
tcp_arg(pcb, ctx);
|
||||
tcp_err(pcb, tcp_client_on_err);
|
||||
err = tcp_connect(pcb, &remote_addr, g_client.config.server_port, tcp_client_on_connected);
|
||||
err = tcp_connect(pcb, &remote_addr, ctx->config.remote_port, tcp_client_on_connected);
|
||||
if (err != ERR_OK) {
|
||||
tcp_err(pcb, NULL);
|
||||
tcp_abort(pcb);
|
||||
g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
g_client.status.errors++;
|
||||
g_client.next_retry_ms = HAL_GetTick() + g_client.config.reconnect_interval_ms;
|
||||
SEGGER_RTT_printf(0, "TCP client connect start failed err=%d\r\n", (int)err);
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_client.pcb = pcb;
|
||||
SEGGER_RTT_printf(0,
|
||||
"TCP client connecting to %u.%u.%u.%u:%u\r\n",
|
||||
g_client.config.server_ip[0], g_client.config.server_ip[1],
|
||||
g_client.config.server_ip[2], g_client.config.server_ip[3],
|
||||
g_client.config.server_port);
|
||||
ctx->pcb = pcb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_client_disconnect(void)
|
||||
int tcp_client_disconnect(uint8_t instance)
|
||||
{
|
||||
if (g_client.pcb != NULL) {
|
||||
tcp_arg(g_client.pcb, NULL);
|
||||
tcp_recv(g_client.pcb, NULL);
|
||||
tcp_sent(g_client.pcb, NULL);
|
||||
tcp_err(g_client.pcb, NULL);
|
||||
tcp_abort(g_client.pcb);
|
||||
g_client.pcb = NULL;
|
||||
}
|
||||
tcp_client_ctx_t *ctx;
|
||||
|
||||
g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
SEGGER_RTT_WriteString(0, "TCP client disconnected\r\n");
|
||||
if (instance >= TCP_CLIENT_INSTANCE_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
ctx = &g_clients[instance];
|
||||
if (ctx->pcb != NULL) {
|
||||
tcp_arg(ctx->pcb, NULL);
|
||||
tcp_recv(ctx->pcb, NULL);
|
||||
tcp_sent(ctx->pcb, NULL);
|
||||
tcp_err(ctx->pcb, NULL);
|
||||
tcp_abort(ctx->pcb);
|
||||
ctx->pcb = NULL;
|
||||
}
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->rx_head = 0u;
|
||||
ctx->rx_tail = 0u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_client_send(const uint8_t *data, uint16_t len)
|
||||
int tcp_client_send(uint8_t instance, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
err_t err;
|
||||
tcp_client_ctx_t *ctx;
|
||||
|
||||
if (g_client.pcb == NULL || data == NULL || len == 0u) {
|
||||
if (instance >= TCP_CLIENT_INSTANCE_COUNT || data == NULL || len == 0u) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((g_client.pcb->flags & TF_RXCLOSED) != 0u) {
|
||||
g_client.status.errors++;
|
||||
ctx = &g_clients[instance];
|
||||
if (ctx->pcb == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tcp_sndbuf(g_client.pcb) < len) {
|
||||
if (tcp_sndbuf(ctx->pcb) < len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = tcp_write(g_client.pcb, data, len, TCP_WRITE_FLAG_COPY);
|
||||
err = tcp_write(ctx->pcb, data, len, TCP_WRITE_FLAG_COPY);
|
||||
if (err != ERR_OK) {
|
||||
g_client.status.errors++;
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = tcp_output(g_client.pcb);
|
||||
err = tcp_output(ctx->pcb);
|
||||
if (err != ERR_OK) {
|
||||
g_client.status.errors++;
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms)
|
||||
int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len)
|
||||
{
|
||||
uint16_t copied = 0u;
|
||||
(void)timeout_ms;
|
||||
tcp_client_ctx_t *ctx;
|
||||
|
||||
if (data == NULL || max_len == 0u) {
|
||||
if (instance >= TCP_CLIENT_INSTANCE_COUNT || data == NULL || max_len == 0u) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (copied < max_len && g_client.rx_tail != g_client.rx_head) {
|
||||
data[copied++] = g_client.rx_ring[g_client.rx_tail];
|
||||
g_client.rx_tail = (uint16_t)((g_client.rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
||||
ctx = &g_clients[instance];
|
||||
while (copied < max_len && ctx->rx_tail != ctx->rx_head) {
|
||||
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
||||
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
return (int)copied;
|
||||
}
|
||||
|
||||
bool tcp_client_is_connected(void)
|
||||
bool tcp_client_is_connected(uint8_t instance)
|
||||
{
|
||||
return (g_client.pcb != NULL) && (g_client.status.state == TCP_CLIENT_STATE_CONNECTED);
|
||||
return (instance < TCP_CLIENT_INSTANCE_COUNT) &&
|
||||
(g_clients[instance].pcb != NULL) &&
|
||||
(g_clients[instance].status.state == TCP_CLIENT_STATE_CONNECTED);
|
||||
}
|
||||
|
||||
int tcp_client_set_server(const uint8_t *ip, uint16_t port)
|
||||
void tcp_client_get_status(uint8_t instance, tcp_client_status_t *status)
|
||||
{
|
||||
if (ip == NULL || port == 0u) {
|
||||
return -1;
|
||||
if (instance < TCP_CLIENT_INSTANCE_COUNT && status != NULL) {
|
||||
*status = g_clients[instance].status;
|
||||
}
|
||||
|
||||
memcpy(g_client.config.server_ip, ip, 4u);
|
||||
g_client.config.server_port = port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tcp_client_get_status(tcp_client_status_t *status)
|
||||
{
|
||||
if (status != NULL) {
|
||||
*status = g_client.status;
|
||||
}
|
||||
}
|
||||
|
||||
void *tcp_client_get_rx_stream(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *tcp_client_get_tx_stream(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void tcp_client_task(void *argument)
|
||||
{
|
||||
(void)argument;
|
||||
}
|
||||
|
||||
void tcp_client_poll(void)
|
||||
{
|
||||
uint32_t now;
|
||||
uint32_t now = HAL_GetTick();
|
||||
|
||||
if (!g_client.config.auto_reconnect || tcp_client_is_connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((g_client.pcb != NULL) && (g_client.status.state == TCP_CLIENT_STATE_CONNECTING)) {
|
||||
return;
|
||||
}
|
||||
|
||||
now = HAL_GetTick();
|
||||
if (now >= g_client.next_retry_ms) {
|
||||
g_client.status.reconnect_count++;
|
||||
g_client.next_retry_ms = now + g_client.config.reconnect_interval_ms;
|
||||
SEGGER_RTT_printf(0,
|
||||
"TCP client reconnect attempt %lu\r\n",
|
||||
g_client.status.reconnect_count);
|
||||
(void)tcp_client_connect();
|
||||
for (uint8_t i = 0; i < TCP_CLIENT_INSTANCE_COUNT; ++i) {
|
||||
tcp_client_ctx_t *ctx = &g_clients[i];
|
||||
if (!ctx->config.enabled || !ctx->config.auto_reconnect || tcp_client_is_connected(i)) {
|
||||
continue;
|
||||
}
|
||||
if ((ctx->pcb != NULL) && (ctx->status.state == TCP_CLIENT_STATE_CONNECTING)) {
|
||||
continue;
|
||||
}
|
||||
if (now >= ctx->next_retry_ms) {
|
||||
ctx->status.reconnect_count++;
|
||||
ctx->next_retry_ms = now + ctx->config.reconnect_interval_ms;
|
||||
(void)tcp_client_connect(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-97
@@ -1,49 +1,39 @@
|
||||
/**
|
||||
* @file tcp_client.h
|
||||
* @brief TCP Client module for transparent transmission with UART3
|
||||
* @brief Indexed lwIP RAW TCP client manager.
|
||||
*/
|
||||
|
||||
#ifndef __TCP_CLIENT_H__
|
||||
#define __TCP_CLIENT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Default TCP Client settings */
|
||||
#define TCP_CLIENT_DEFAULT_PORT 8081
|
||||
#define TCP_CLIENT_DEFAULT_SERVER "192.168.1.100"
|
||||
#define TCP_CLIENT_INSTANCE_COUNT 2u
|
||||
#define TCP_CLIENT_RX_BUFFER_SIZE 512u
|
||||
#define TCP_CLIENT_RECONNECT_DELAY_MS 3000u
|
||||
|
||||
/* Reconnect settings */
|
||||
#define TCP_CLIENT_RECONNECT_DELAY_MS 3000
|
||||
#define TCP_CLIENT_MAX_RECONNECT_TRIES 0 /* 0 = infinite */
|
||||
|
||||
/* Buffer sizes */
|
||||
#define TCP_CLIENT_RX_BUFFER_SIZE 512
|
||||
#define TCP_CLIENT_TX_BUFFER_SIZE 512
|
||||
|
||||
/* TCP Client state */
|
||||
typedef enum {
|
||||
TCP_CLIENT_STATE_IDLE,
|
||||
TCP_CLIENT_STATE_IDLE = 0,
|
||||
TCP_CLIENT_STATE_CONNECTING,
|
||||
TCP_CLIENT_STATE_CONNECTED,
|
||||
TCP_CLIENT_STATE_DISCONNECTED,
|
||||
TCP_CLIENT_STATE_ERROR
|
||||
} tcp_client_state_t;
|
||||
|
||||
/* TCP Client configuration */
|
||||
typedef struct {
|
||||
uint8_t server_ip[4]; /* Server IP address */
|
||||
uint16_t local_port; /* Local source port */
|
||||
uint16_t server_port; /* Server port */
|
||||
bool auto_reconnect; /* Auto reconnect on disconnect */
|
||||
uint16_t reconnect_interval_ms; /* Reconnect interval */
|
||||
} tcp_client_config_t;
|
||||
uint8_t remote_ip[4];
|
||||
uint16_t local_port;
|
||||
uint16_t remote_port;
|
||||
uint16_t reconnect_interval_ms;
|
||||
bool enabled;
|
||||
bool auto_reconnect;
|
||||
} tcp_client_instance_config_t;
|
||||
|
||||
/* TCP Client status */
|
||||
typedef struct {
|
||||
tcp_client_state_t state;
|
||||
uint32_t rx_bytes;
|
||||
@@ -52,80 +42,14 @@ typedef struct {
|
||||
uint32_t errors;
|
||||
} tcp_client_status_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize TCP Client module
|
||||
* @param config Client configuration
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int tcp_client_init(const tcp_client_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Connect to remote server
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int tcp_client_connect(void);
|
||||
|
||||
/**
|
||||
* @brief Disconnect from server
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int tcp_client_disconnect(void);
|
||||
|
||||
/**
|
||||
* @brief Send data to server
|
||||
* @param data Data buffer
|
||||
* @param len Data length
|
||||
* @return Number of bytes sent, negative on error
|
||||
*/
|
||||
int tcp_client_send(const uint8_t *data, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief Receive data from server
|
||||
* @param data Data buffer
|
||||
* @param max_len Maximum length to receive
|
||||
* @param timeout_ms Timeout in milliseconds (0 = non-blocking)
|
||||
* @return Number of bytes received, 0 if no data, negative on error
|
||||
*/
|
||||
int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Check if connected to server
|
||||
* @return true if connected
|
||||
*/
|
||||
bool tcp_client_is_connected(void);
|
||||
|
||||
/**
|
||||
* @brief Update server configuration (for AT command)
|
||||
* @param ip Server IP address (4 bytes)
|
||||
* @param port Server port
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int tcp_client_set_server(const uint8_t *ip, uint16_t port);
|
||||
|
||||
/**
|
||||
* @brief Get TCP Client status
|
||||
* @param status Pointer to status structure
|
||||
*/
|
||||
void tcp_client_get_status(tcp_client_status_t *status);
|
||||
|
||||
/**
|
||||
* @brief Get TCP Client RX StreamBuffer handle for UART integration
|
||||
* @return StreamBuffer handle for receiving data from TCP
|
||||
*/
|
||||
void *tcp_client_get_rx_stream(void);
|
||||
|
||||
/**
|
||||
* @brief Get TCP Client TX StreamBuffer handle for UART integration
|
||||
* @return StreamBuffer handle for sending data to TCP
|
||||
*/
|
||||
void *tcp_client_get_tx_stream(void);
|
||||
|
||||
/**
|
||||
* @brief TCP Client task function (for FreeRTOS)
|
||||
* @param argument Task argument (unused)
|
||||
*/
|
||||
void tcp_client_task(void *argument);
|
||||
|
||||
int tcp_client_init_all(void);
|
||||
int tcp_client_config(uint8_t instance, const tcp_client_instance_config_t *config);
|
||||
int tcp_client_connect(uint8_t instance);
|
||||
int tcp_client_disconnect(uint8_t instance);
|
||||
int tcp_client_send(uint8_t instance, const uint8_t *data, uint16_t len);
|
||||
int tcp_client_recv(uint8_t instance, uint8_t *data, uint16_t max_len);
|
||||
bool tcp_client_is_connected(uint8_t instance);
|
||||
void tcp_client_get_status(uint8_t instance, tcp_client_status_t *status);
|
||||
void tcp_client_poll(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
+103
-96
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* @file tcp_server.c
|
||||
* @brief lwIP RAW TCP server for the UART2 bridge.
|
||||
* @brief Indexed lwIP RAW TCP server manager.
|
||||
*/
|
||||
|
||||
#include "tcp_server.h"
|
||||
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "../Drivers/LwIP/src/include/lwip/pbuf.h"
|
||||
#include "../Drivers/LwIP/src/include/lwip/tcp.h"
|
||||
|
||||
#include "SEGGER_RTT.h"
|
||||
|
||||
@@ -18,11 +18,12 @@ typedef struct {
|
||||
uint8_t rx_ring[TCP_SERVER_RX_BUFFER_SIZE];
|
||||
uint16_t rx_head;
|
||||
uint16_t rx_tail;
|
||||
tcp_server_config_t config;
|
||||
uint8_t index;
|
||||
tcp_server_instance_config_t config;
|
||||
tcp_server_status_t status;
|
||||
} tcp_server_ctx_t;
|
||||
|
||||
static tcp_server_ctx_t g_server;
|
||||
static tcp_server_ctx_t g_servers[TCP_SERVER_INSTANCE_COUNT];
|
||||
|
||||
static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size)
|
||||
{
|
||||
@@ -34,13 +35,18 @@ static err_t tcp_server_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p,
|
||||
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
||||
struct pbuf *q;
|
||||
|
||||
if (ctx == NULL) {
|
||||
if (p != NULL) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
return ERR_ARG;
|
||||
}
|
||||
if (err != ERR_OK) {
|
||||
if (p != NULL) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
tcp_arg(pcb, NULL);
|
||||
tcp_recv(pcb, NULL);
|
||||
@@ -50,8 +56,7 @@ static err_t tcp_server_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p,
|
||||
tcp_abort(pcb);
|
||||
}
|
||||
ctx->client_pcb = NULL;
|
||||
ctx->status.state = TCP_SERVER_STATE_LISTENING;
|
||||
SEGGER_RTT_WriteString(0, "TCP server peer disconnected\r\n");
|
||||
ctx->status.state = ctx->config.enabled ? TCP_SERVER_STATE_LISTENING : TCP_SERVER_STATE_IDLE;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@@ -77,28 +82,31 @@ static err_t tcp_server_on_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
|
||||
{
|
||||
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
||||
(void)pcb;
|
||||
ctx->status.tx_bytes += len;
|
||||
if (ctx != NULL) {
|
||||
ctx->status.tx_bytes += len;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void tcp_server_on_err(void *arg, err_t err)
|
||||
{
|
||||
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
||||
(void)err;
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
ctx->client_pcb = NULL;
|
||||
ctx->status.state = TCP_SERVER_STATE_LISTENING;
|
||||
ctx->status.state = ctx->config.enabled ? TCP_SERVER_STATE_LISTENING : TCP_SERVER_STATE_IDLE;
|
||||
ctx->status.errors++;
|
||||
SEGGER_RTT_printf(0, "TCP server connection error=%d\r\n", (int)err);
|
||||
SEGGER_RTT_printf(0, "TCP server[%u] connection error=%d\r\n", ctx->index, (int)err);
|
||||
}
|
||||
|
||||
static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
|
||||
{
|
||||
tcp_server_ctx_t *ctx = (tcp_server_ctx_t *)arg;
|
||||
|
||||
if (err != ERR_OK) {
|
||||
return err;
|
||||
if (ctx == NULL || err != ERR_OK) {
|
||||
return (ctx == NULL) ? ERR_ARG : err;
|
||||
}
|
||||
|
||||
if (ctx->client_pcb != NULL) {
|
||||
tcp_abort(newpcb);
|
||||
return ERR_ABRT;
|
||||
@@ -107,10 +115,7 @@ static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
|
||||
ctx->client_pcb = newpcb;
|
||||
ctx->status.state = TCP_SERVER_STATE_CONNECTED;
|
||||
ctx->status.connections++;
|
||||
SEGGER_RTT_WriteString(0, "TCP server client connected\r\n");
|
||||
|
||||
tcp_nagle_disable(newpcb);
|
||||
|
||||
tcp_arg(newpcb, ctx);
|
||||
tcp_recv(newpcb, tcp_server_on_recv);
|
||||
tcp_sent(newpcb, tcp_server_on_sent);
|
||||
@@ -118,149 +123,151 @@ static err_t tcp_server_on_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int tcp_server_init(const tcp_server_config_t *config)
|
||||
int tcp_server_init_all(void)
|
||||
{
|
||||
memset(&g_server, 0, sizeof(g_server));
|
||||
g_server.config.port = TCP_SERVER_DEFAULT_PORT;
|
||||
g_server.config.auto_reconnect = true;
|
||||
g_server.status.state = TCP_SERVER_STATE_IDLE;
|
||||
|
||||
if (config != NULL) {
|
||||
g_server.config = *config;
|
||||
memset(g_servers, 0, sizeof(g_servers));
|
||||
for (uint8_t i = 0; i < TCP_SERVER_INSTANCE_COUNT; ++i) {
|
||||
g_servers[i].index = i;
|
||||
g_servers[i].status.state = TCP_SERVER_STATE_IDLE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_server_start(void)
|
||||
int tcp_server_config(uint8_t instance, const tcp_server_instance_config_t *config)
|
||||
{
|
||||
if (instance >= TCP_SERVER_INSTANCE_COUNT || config == NULL) {
|
||||
return -1;
|
||||
}
|
||||
g_servers[instance].config = *config;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_server_start(uint8_t instance)
|
||||
{
|
||||
struct tcp_pcb *pcb;
|
||||
err_t err;
|
||||
tcp_server_ctx_t *ctx;
|
||||
|
||||
if (g_server.listen_pcb != NULL) {
|
||||
if (instance >= TCP_SERVER_INSTANCE_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
ctx = &g_servers[instance];
|
||||
if (!ctx->config.enabled) {
|
||||
ctx->status.state = TCP_SERVER_STATE_IDLE;
|
||||
return 0;
|
||||
}
|
||||
if (ctx->listen_pcb != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
|
||||
if (pcb == NULL) {
|
||||
g_server.status.errors++;
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = tcp_bind(pcb, IP_ANY_TYPE, g_server.config.port);
|
||||
err = tcp_bind(pcb, IP_ANY_TYPE, ctx->config.port);
|
||||
if (err != ERR_OK) {
|
||||
tcp_abort(pcb);
|
||||
g_server.status.errors++;
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_server.listen_pcb = tcp_listen_with_backlog(pcb, 1);
|
||||
if (g_server.listen_pcb == NULL) {
|
||||
g_server.status.errors++;
|
||||
ctx->listen_pcb = tcp_listen_with_backlog(pcb, 1);
|
||||
if (ctx->listen_pcb == NULL) {
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
tcp_arg(g_server.listen_pcb, &g_server);
|
||||
tcp_accept(g_server.listen_pcb, tcp_server_on_accept);
|
||||
g_server.status.state = TCP_SERVER_STATE_LISTENING;
|
||||
SEGGER_RTT_printf(0, "TCP server listening on %u\r\n", g_server.config.port);
|
||||
tcp_arg(ctx->listen_pcb, ctx);
|
||||
tcp_accept(ctx->listen_pcb, tcp_server_on_accept);
|
||||
ctx->status.state = TCP_SERVER_STATE_LISTENING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_server_stop(void)
|
||||
int tcp_server_stop(uint8_t instance)
|
||||
{
|
||||
if (g_server.client_pcb != NULL) {
|
||||
tcp_arg(g_server.client_pcb, NULL);
|
||||
tcp_recv(g_server.client_pcb, NULL);
|
||||
tcp_sent(g_server.client_pcb, NULL);
|
||||
tcp_err(g_server.client_pcb, NULL);
|
||||
tcp_abort(g_server.client_pcb);
|
||||
g_server.client_pcb = NULL;
|
||||
tcp_server_ctx_t *ctx;
|
||||
|
||||
if (instance >= TCP_SERVER_INSTANCE_COUNT) {
|
||||
return -1;
|
||||
}
|
||||
ctx = &g_servers[instance];
|
||||
|
||||
if (ctx->client_pcb != NULL) {
|
||||
tcp_arg(ctx->client_pcb, NULL);
|
||||
tcp_recv(ctx->client_pcb, NULL);
|
||||
tcp_sent(ctx->client_pcb, NULL);
|
||||
tcp_err(ctx->client_pcb, NULL);
|
||||
tcp_abort(ctx->client_pcb);
|
||||
ctx->client_pcb = NULL;
|
||||
}
|
||||
if (ctx->listen_pcb != NULL) {
|
||||
tcp_arg(ctx->listen_pcb, NULL);
|
||||
tcp_accept(ctx->listen_pcb, NULL);
|
||||
tcp_close(ctx->listen_pcb);
|
||||
ctx->listen_pcb = NULL;
|
||||
}
|
||||
|
||||
if (g_server.listen_pcb != NULL) {
|
||||
tcp_arg(g_server.listen_pcb, NULL);
|
||||
tcp_accept(g_server.listen_pcb, NULL);
|
||||
tcp_close(g_server.listen_pcb);
|
||||
g_server.listen_pcb = NULL;
|
||||
}
|
||||
|
||||
g_server.status.state = TCP_SERVER_STATE_IDLE;
|
||||
ctx->status.state = TCP_SERVER_STATE_IDLE;
|
||||
ctx->rx_head = 0u;
|
||||
ctx->rx_tail = 0u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tcp_server_send(const uint8_t *data, uint16_t len)
|
||||
int tcp_server_send(uint8_t instance, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
err_t err;
|
||||
tcp_server_ctx_t *ctx;
|
||||
|
||||
if (g_server.client_pcb == NULL || data == NULL || len == 0u) {
|
||||
if (instance >= TCP_SERVER_INSTANCE_COUNT || data == NULL || len == 0u) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((g_server.client_pcb->flags & TF_RXCLOSED) != 0u) {
|
||||
g_server.status.errors++;
|
||||
ctx = &g_servers[instance];
|
||||
if (ctx->client_pcb == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tcp_sndbuf(g_server.client_pcb) < len) {
|
||||
if (tcp_sndbuf(ctx->client_pcb) < len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = tcp_write(g_server.client_pcb, data, len, TCP_WRITE_FLAG_COPY);
|
||||
err = tcp_write(ctx->client_pcb, data, len, TCP_WRITE_FLAG_COPY);
|
||||
if (err != ERR_OK) {
|
||||
g_server.status.errors++;
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = tcp_output(g_server.client_pcb);
|
||||
err = tcp_output(ctx->client_pcb);
|
||||
if (err != ERR_OK) {
|
||||
g_server.status.errors++;
|
||||
ctx->status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
int tcp_server_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms)
|
||||
int tcp_server_recv(uint8_t instance, uint8_t *data, uint16_t max_len)
|
||||
{
|
||||
uint16_t copied = 0u;
|
||||
(void)timeout_ms;
|
||||
tcp_server_ctx_t *ctx;
|
||||
|
||||
if (data == NULL || max_len == 0u) {
|
||||
if (instance >= TCP_SERVER_INSTANCE_COUNT || data == NULL || max_len == 0u) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (copied < max_len && g_server.rx_tail != g_server.rx_head) {
|
||||
data[copied++] = g_server.rx_ring[g_server.rx_tail];
|
||||
g_server.rx_tail = (uint16_t)((g_server.rx_tail + 1u) % TCP_SERVER_RX_BUFFER_SIZE);
|
||||
ctx = &g_servers[instance];
|
||||
while (copied < max_len && ctx->rx_tail != ctx->rx_head) {
|
||||
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
||||
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % TCP_SERVER_RX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
return (int)copied;
|
||||
}
|
||||
|
||||
bool tcp_server_is_connected(void)
|
||||
bool tcp_server_is_connected(uint8_t instance)
|
||||
{
|
||||
return g_server.client_pcb != NULL;
|
||||
return (instance < TCP_SERVER_INSTANCE_COUNT) && (g_servers[instance].client_pcb != NULL);
|
||||
}
|
||||
|
||||
void tcp_server_get_status(tcp_server_status_t *status)
|
||||
void tcp_server_get_status(uint8_t instance, tcp_server_status_t *status)
|
||||
{
|
||||
if (status != NULL) {
|
||||
*status = g_server.status;
|
||||
if (instance < TCP_SERVER_INSTANCE_COUNT && status != NULL) {
|
||||
*status = g_servers[instance].status;
|
||||
}
|
||||
}
|
||||
|
||||
void *tcp_server_get_rx_stream(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *tcp_server_get_tx_stream(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void tcp_server_task(void *argument)
|
||||
{
|
||||
(void)argument;
|
||||
}
|
||||
|
||||
+15
-82
@@ -1,43 +1,33 @@
|
||||
/**
|
||||
* @file tcp_server.h
|
||||
* @brief TCP Server module for transparent transmission with UART2
|
||||
* @brief Indexed lwIP RAW TCP server manager.
|
||||
*/
|
||||
|
||||
#ifndef __TCP_SERVER_H__
|
||||
#define __TCP_SERVER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Default TCP Server port */
|
||||
#define TCP_SERVER_DEFAULT_PORT 8080
|
||||
#define TCP_SERVER_INSTANCE_COUNT 2u
|
||||
#define TCP_SERVER_RX_BUFFER_SIZE 512u
|
||||
|
||||
/* Maximum number of simultaneous connections */
|
||||
#define TCP_SERVER_MAX_CONNECTIONS 1
|
||||
|
||||
/* Buffer sizes */
|
||||
#define TCP_SERVER_RX_BUFFER_SIZE 512
|
||||
#define TCP_SERVER_TX_BUFFER_SIZE 512
|
||||
|
||||
/* TCP Server state */
|
||||
typedef enum {
|
||||
TCP_SERVER_STATE_IDLE,
|
||||
TCP_SERVER_STATE_IDLE = 0,
|
||||
TCP_SERVER_STATE_LISTENING,
|
||||
TCP_SERVER_STATE_CONNECTED,
|
||||
TCP_SERVER_STATE_ERROR
|
||||
} tcp_server_state_t;
|
||||
|
||||
/* TCP Server configuration */
|
||||
typedef struct {
|
||||
uint16_t port;
|
||||
bool auto_reconnect;
|
||||
} tcp_server_config_t;
|
||||
bool enabled;
|
||||
} tcp_server_instance_config_t;
|
||||
|
||||
/* TCP Server status */
|
||||
typedef struct {
|
||||
tcp_server_state_t state;
|
||||
uint32_t rx_bytes;
|
||||
@@ -46,71 +36,14 @@ typedef struct {
|
||||
uint32_t errors;
|
||||
} tcp_server_status_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize TCP Server module
|
||||
* @param config Server configuration
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int tcp_server_init(const tcp_server_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Start TCP Server (begin listening)
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int tcp_server_start(void);
|
||||
|
||||
/**
|
||||
* @brief Stop TCP Server
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int tcp_server_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Send data to connected client
|
||||
* @param data Data buffer
|
||||
* @param len Data length
|
||||
* @return Number of bytes sent, negative on error
|
||||
*/
|
||||
int tcp_server_send(const uint8_t *data, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief Receive data from connected client
|
||||
* @param data Data buffer
|
||||
* @param max_len Maximum length to receive
|
||||
* @param timeout_ms Timeout in milliseconds (0 = non-blocking)
|
||||
* @return Number of bytes received, 0 if no data, negative on error
|
||||
*/
|
||||
int tcp_server_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Check if client is connected
|
||||
* @return true if connected
|
||||
*/
|
||||
bool tcp_server_is_connected(void);
|
||||
|
||||
/**
|
||||
* @brief Get TCP Server status
|
||||
* @param status Pointer to status structure
|
||||
*/
|
||||
void tcp_server_get_status(tcp_server_status_t *status);
|
||||
|
||||
/**
|
||||
* @brief Get TCP Server StreamBuffer handle for UART integration
|
||||
* @return StreamBuffer handle for receiving data from TCP
|
||||
*/
|
||||
void *tcp_server_get_rx_stream(void);
|
||||
|
||||
/**
|
||||
* @brief Get TCP Server TX StreamBuffer handle for UART integration
|
||||
* @return StreamBuffer handle for sending data to TCP
|
||||
*/
|
||||
void *tcp_server_get_tx_stream(void);
|
||||
|
||||
/**
|
||||
* @brief TCP Server task function (for FreeRTOS)
|
||||
* @param argument Task argument (unused)
|
||||
*/
|
||||
void tcp_server_task(void *argument);
|
||||
int tcp_server_init_all(void);
|
||||
int tcp_server_config(uint8_t instance, const tcp_server_instance_config_t *config);
|
||||
int tcp_server_start(uint8_t instance);
|
||||
int tcp_server_stop(uint8_t instance);
|
||||
int tcp_server_send(uint8_t instance, const uint8_t *data, uint16_t len);
|
||||
int tcp_server_recv(uint8_t instance, uint8_t *data, uint16_t max_len);
|
||||
bool tcp_server_is_connected(uint8_t instance);
|
||||
void tcp_server_get_status(uint8_t instance, tcp_server_status_t *status);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+113
-82
@@ -1,14 +1,17 @@
|
||||
/**
|
||||
* @file uart_trans.c
|
||||
* @brief Bare-metal UART DMA/IDLE transport layer.
|
||||
* @brief Bare-metal UART DMA/IDLE transport and MUX helpers.
|
||||
*/
|
||||
|
||||
#include "uart_trans.h"
|
||||
|
||||
#include "usart.h"
|
||||
#include "../Core/Inc/usart.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define UART_MUX_SYNC 0x7Eu
|
||||
#define UART_MUX_TAIL 0x7Fu
|
||||
|
||||
typedef struct {
|
||||
UART_HandleTypeDef *huart;
|
||||
uint8_t rx_dma_buffer[UART_RX_DMA_BUFFER_SIZE];
|
||||
@@ -40,55 +43,23 @@ static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size)
|
||||
return (uint16_t)(size - ring_used(head, tail, size) - 1u);
|
||||
}
|
||||
|
||||
static void apply_default_config(uart_channel_ctx_t *ctx)
|
||||
{
|
||||
ctx->config.baudrate = UART_DEFAULT_BAUDRATE;
|
||||
ctx->config.data_bits = UART_DEFAULT_DATA_BITS;
|
||||
ctx->config.stop_bits = UART_DEFAULT_STOP_BITS;
|
||||
ctx->config.parity = UART_DEFAULT_PARITY;
|
||||
}
|
||||
|
||||
static int apply_uart_config(uart_channel_t channel)
|
||||
{
|
||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||
UART_HandleTypeDef *huart = ctx->huart;
|
||||
uint32_t word_length;
|
||||
uint32_t parity;
|
||||
|
||||
if (huart == NULL) {
|
||||
if (ctx->huart == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->running) {
|
||||
HAL_UART_DMAStop(huart);
|
||||
HAL_UART_DMAStop(ctx->huart);
|
||||
ctx->running = false;
|
||||
}
|
||||
|
||||
huart->Init.BaudRate = ctx->config.baudrate;
|
||||
huart->Init.StopBits = (ctx->config.stop_bits == 2u) ? UART_STOPBITS_2 : UART_STOPBITS_1;
|
||||
|
||||
switch (ctx->config.parity) {
|
||||
case 1:
|
||||
parity = UART_PARITY_ODD;
|
||||
break;
|
||||
case 2:
|
||||
parity = UART_PARITY_EVEN;
|
||||
break;
|
||||
default:
|
||||
parity = UART_PARITY_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (parity == UART_PARITY_NONE) {
|
||||
word_length = (ctx->config.data_bits == 9u) ? UART_WORDLENGTH_9B : UART_WORDLENGTH_8B;
|
||||
} else {
|
||||
word_length = (ctx->config.data_bits >= 8u) ? UART_WORDLENGTH_9B : UART_WORDLENGTH_8B;
|
||||
}
|
||||
|
||||
huart->Init.WordLength = word_length;
|
||||
huart->Init.Parity = parity;
|
||||
|
||||
return (HAL_UART_Init(huart) == HAL_OK) ? 0 : -1;
|
||||
ctx->huart->Init.BaudRate = ctx->config.baudrate;
|
||||
ctx->huart->Init.WordLength = UART_WORDLENGTH_8B;
|
||||
ctx->huart->Init.StopBits = UART_STOPBITS_1;
|
||||
ctx->huart->Init.Parity = UART_PARITY_NONE;
|
||||
return (HAL_UART_Init(ctx->huart) == HAL_OK) ? 0 : -1;
|
||||
}
|
||||
|
||||
static void process_rx_snapshot(uart_channel_t channel, uint16_t dma_write_index)
|
||||
@@ -96,9 +67,7 @@ static void process_rx_snapshot(uart_channel_t channel, uint16_t dma_write_index
|
||||
uart_channel_ctx_t *ctx = &g_channels[channel];
|
||||
|
||||
while (ctx->rx_dma_read_index != dma_write_index) {
|
||||
uint16_t next_head;
|
||||
|
||||
next_head = (uint16_t)((ctx->rx_head + 1u) % UART_RX_RING_BUFFER_SIZE);
|
||||
uint16_t next_head = (uint16_t)((ctx->rx_head + 1u) % UART_RX_RING_BUFFER_SIZE);
|
||||
if (next_head == ctx->rx_tail) {
|
||||
ctx->stats.errors++;
|
||||
break;
|
||||
@@ -149,16 +118,12 @@ static void kick_tx(uart_channel_t channel)
|
||||
int uart_trans_init(void)
|
||||
{
|
||||
memset(g_channels, 0, sizeof(g_channels));
|
||||
|
||||
g_channels[UART_CHANNEL_SERVER].huart = &huart2;
|
||||
g_channels[UART_CHANNEL_CLIENT].huart = &huart3;
|
||||
|
||||
apply_default_config(&g_channels[UART_CHANNEL_SERVER]);
|
||||
apply_default_config(&g_channels[UART_CHANNEL_CLIENT]);
|
||||
|
||||
g_channels[UART_CHANNEL_SERVER].initialized = true;
|
||||
g_channels[UART_CHANNEL_CLIENT].initialized = true;
|
||||
|
||||
g_channels[UART_CHANNEL_U0].huart = &huart2;
|
||||
g_channels[UART_CHANNEL_U1].huart = &huart3;
|
||||
g_channels[UART_CHANNEL_U0].config.baudrate = UART_DEFAULT_BAUDRATE;
|
||||
g_channels[UART_CHANNEL_U1].config.baudrate = UART_DEFAULT_BAUDRATE;
|
||||
g_channels[UART_CHANNEL_U0].initialized = true;
|
||||
g_channels[UART_CHANNEL_U1].initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -167,7 +132,6 @@ int uart_trans_config(uart_channel_t channel, const uart_config_t *config)
|
||||
if (channel >= UART_CHANNEL_MAX || config == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_channels[channel].config = *config;
|
||||
return apply_uart_config(channel);
|
||||
}
|
||||
@@ -208,29 +172,16 @@ int uart_trans_stop(uart_channel_t channel)
|
||||
if (channel >= UART_CHANNEL_MAX) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
HAL_UART_DMAStop(g_channels[channel].huart);
|
||||
g_channels[channel].running = false;
|
||||
g_channels[channel].tx_busy = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats)
|
||||
void uart_trans_poll(void)
|
||||
{
|
||||
if (channel >= UART_CHANNEL_MAX || stats == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
*stats = g_channels[channel].stats;
|
||||
}
|
||||
|
||||
void uart_trans_reset_stats(uart_channel_t channel)
|
||||
{
|
||||
if (channel >= UART_CHANNEL_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&g_channels[channel].stats, 0, sizeof(g_channels[channel].stats));
|
||||
kick_tx(UART_CHANNEL_U0);
|
||||
kick_tx(UART_CHANNEL_U1);
|
||||
}
|
||||
|
||||
uint16_t uart_trans_rx_available(uart_channel_t channel)
|
||||
@@ -238,7 +189,6 @@ uint16_t uart_trans_rx_available(uart_channel_t channel)
|
||||
if (channel >= UART_CHANNEL_MAX) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
return ring_used(g_channels[channel].rx_head, g_channels[channel].rx_tail, UART_RX_RING_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
@@ -256,11 +206,9 @@ uint16_t uart_trans_read(uart_channel_t channel, uint8_t *data, uint16_t max_len
|
||||
data[copied++] = ctx->rx_ring[ctx->rx_tail];
|
||||
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % UART_RX_RING_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
if (copied > 0u) {
|
||||
ctx->stats.rx_packets++;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
@@ -287,10 +235,18 @@ uint16_t uart_trans_write(uart_channel_t channel, const uint8_t *data, uint16_t
|
||||
return written;
|
||||
}
|
||||
|
||||
void uart_trans_poll(void)
|
||||
void uart_trans_get_stats(uart_channel_t channel, uart_stats_t *stats)
|
||||
{
|
||||
kick_tx(UART_CHANNEL_SERVER);
|
||||
kick_tx(UART_CHANNEL_CLIENT);
|
||||
if (channel < UART_CHANNEL_MAX && stats != NULL) {
|
||||
*stats = g_channels[channel].stats;
|
||||
}
|
||||
}
|
||||
|
||||
void uart_trans_reset_stats(uart_channel_t channel)
|
||||
{
|
||||
if (channel < UART_CHANNEL_MAX) {
|
||||
memset(&g_channels[channel].stats, 0, sizeof(g_channels[channel].stats));
|
||||
}
|
||||
}
|
||||
|
||||
void uart_trans_idle_handler(uart_channel_t channel)
|
||||
@@ -301,14 +257,12 @@ void uart_trans_idle_handler(uart_channel_t channel)
|
||||
if (channel >= UART_CHANNEL_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
huart = g_channels[channel].huart;
|
||||
g_channels[channel].stats.idle_events++;
|
||||
dma_write_index = (uint16_t)(UART_RX_DMA_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx));
|
||||
if (dma_write_index >= UART_RX_DMA_BUFFER_SIZE) {
|
||||
dma_write_index = 0u;
|
||||
}
|
||||
|
||||
process_rx_snapshot(channel, dma_write_index);
|
||||
}
|
||||
|
||||
@@ -317,7 +271,6 @@ void uart_trans_rx_half_cplt_handler(uart_channel_t channel)
|
||||
if (channel >= UART_CHANNEL_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_channels[channel].stats.rx_half_events++;
|
||||
process_rx_snapshot(channel, UART_RX_DMA_BUFFER_SIZE / 2u);
|
||||
}
|
||||
@@ -327,7 +280,6 @@ void uart_trans_rx_cplt_handler(uart_channel_t channel)
|
||||
if (channel >= UART_CHANNEL_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_channels[channel].stats.rx_full_events++;
|
||||
process_rx_snapshot(channel, 0u);
|
||||
}
|
||||
@@ -337,9 +289,88 @@ void uart_trans_tx_cplt_handler(uart_channel_t channel)
|
||||
if (channel >= UART_CHANNEL_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_channels[channel].tx_busy = false;
|
||||
g_channels[channel].stats.tx_bytes += g_channels[channel].tx_dma_len;
|
||||
g_channels[channel].tx_dma_len = 0u;
|
||||
kick_tx(channel);
|
||||
}
|
||||
|
||||
bool uart_mux_try_extract_frame(uart_channel_t channel, uart_mux_frame_t *frame)
|
||||
{
|
||||
uint8_t header[5];
|
||||
uint16_t available;
|
||||
uint16_t payload_len;
|
||||
|
||||
if (channel >= UART_CHANNEL_MAX || frame == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
available = uart_trans_rx_available(channel);
|
||||
if (available < 6u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uart_trans_read(channel, header, sizeof(header)) != sizeof(header)) {
|
||||
return false;
|
||||
}
|
||||
if (header[0] != UART_MUX_SYNC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
payload_len = (uint16_t)(((uint16_t)header[1] << 8) | header[2]);
|
||||
if (payload_len > sizeof(frame->payload)) {
|
||||
return false;
|
||||
}
|
||||
if (uart_trans_rx_available(channel) < (uint16_t)(payload_len + 1u)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
frame->src_id = header[3];
|
||||
frame->dst_mask = header[4];
|
||||
frame->payload_len = payload_len;
|
||||
if (payload_len > 0u) {
|
||||
if (uart_trans_read(channel, frame->payload, payload_len) != payload_len) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t tail = 0u;
|
||||
if (uart_trans_read(channel, &tail, 1u) != 1u || tail != UART_MUX_TAIL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uart_mux_encode_frame(uint8_t src_id,
|
||||
uint8_t dst_mask,
|
||||
const uint8_t *payload,
|
||||
uint16_t payload_len,
|
||||
uint8_t *out,
|
||||
uint16_t *out_len,
|
||||
uint16_t out_capacity)
|
||||
{
|
||||
uint16_t frame_len;
|
||||
|
||||
if (out == NULL || out_len == NULL) {
|
||||
return false;
|
||||
}
|
||||
frame_len = (uint16_t)(payload_len + 6u);
|
||||
if (frame_len > out_capacity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out[0] = UART_MUX_SYNC;
|
||||
out[1] = (uint8_t)(payload_len >> 8);
|
||||
out[2] = (uint8_t)(payload_len & 0xFFu);
|
||||
out[3] = src_id;
|
||||
out[4] = dst_mask;
|
||||
if (payload_len > 0u && payload != NULL) {
|
||||
memcpy(&out[5], payload, payload_len);
|
||||
}
|
||||
out[5 + payload_len] = UART_MUX_TAIL;
|
||||
*out_len = frame_len;
|
||||
return true;
|
||||
}
|
||||
|
||||
+22
-14
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file uart_trans.h
|
||||
* @brief Bare-metal UART DMA/IDLE transport layer.
|
||||
* @brief Bare-metal UART DMA/IDLE transport and MUX framing helpers.
|
||||
*/
|
||||
|
||||
#ifndef __UART_TRANS_H__
|
||||
@@ -14,28 +14,28 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
UART_CHANNEL_SERVER = 0,
|
||||
UART_CHANNEL_CLIENT = 1,
|
||||
UART_CHANNEL_U0 = 0,
|
||||
UART_CHANNEL_U1 = 1,
|
||||
UART_CHANNEL_MAX
|
||||
} uart_channel_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t src_id;
|
||||
uint8_t dst_mask;
|
||||
uint16_t payload_len;
|
||||
uint8_t payload[256];
|
||||
} uart_mux_frame_t;
|
||||
|
||||
#define UART_RX_DMA_BUFFER_SIZE 128u
|
||||
#define UART_TX_DMA_BUFFER_SIZE 128u
|
||||
#define UART_RX_RING_BUFFER_SIZE 512u
|
||||
#define UART_TX_RING_BUFFER_SIZE 512u
|
||||
#define UART_RX_RING_BUFFER_SIZE 256u
|
||||
#define UART_TX_RING_BUFFER_SIZE 256u
|
||||
#define UART_DEFAULT_BAUDRATE 115200u
|
||||
|
||||
typedef struct {
|
||||
uint32_t baudrate;
|
||||
uint8_t data_bits;
|
||||
uint8_t stop_bits;
|
||||
uint8_t parity;
|
||||
} uart_config_t;
|
||||
|
||||
#define UART_DEFAULT_BAUDRATE 115200u
|
||||
#define UART_DEFAULT_DATA_BITS 8u
|
||||
#define UART_DEFAULT_STOP_BITS 1u
|
||||
#define UART_DEFAULT_PARITY 0u
|
||||
|
||||
typedef struct {
|
||||
uint32_t rx_bytes;
|
||||
uint32_t tx_bytes;
|
||||
@@ -61,9 +61,17 @@ void uart_trans_idle_handler(uart_channel_t channel);
|
||||
void uart_trans_rx_half_cplt_handler(uart_channel_t channel);
|
||||
void uart_trans_rx_cplt_handler(uart_channel_t channel);
|
||||
void uart_trans_tx_cplt_handler(uart_channel_t channel);
|
||||
bool uart_mux_try_extract_frame(uart_channel_t channel, uart_mux_frame_t *frame);
|
||||
bool uart_mux_encode_frame(uint8_t src_id,
|
||||
uint8_t dst_mask,
|
||||
const uint8_t *payload,
|
||||
uint16_t payload_len,
|
||||
uint8_t *out,
|
||||
uint16_t *out_len,
|
||||
uint16_t out_capacity);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif /* __UART_TRANS_H__ */
|
||||
|
||||
Reference in New Issue
Block a user