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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user