697 lines
22 KiB
C
697 lines
22 KiB
C
#include "config.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
#include "queue.h"
|
|
#include "flash_param.h"
|
|
#include "usart.h"
|
|
#include "route_msg.h"
|
|
#include "app_runtime.h"
|
|
#include "debug_log.h"
|
|
#include "uart_trans.h"
|
|
|
|
#define CONFIG_RX_BUFFER_SIZE 160u
|
|
#define CONFIG_TX_BUFFER_SIZE 512u
|
|
#define CONFIG_CMD_MAX_LEN 160u
|
|
|
|
static device_config_t g_config;
|
|
static volatile bool g_reset_requested;
|
|
static volatile bool g_uart1_tx_busy;
|
|
static uint8_t g_uart1_rx_buffer[CONFIG_RX_BUFFER_SIZE];
|
|
static char g_config_cmd_buffer[CONFIG_CMD_MAX_LEN];
|
|
static char g_config_response_buffer[CONFIG_TX_BUFFER_SIZE];
|
|
|
|
static uint32_t config_calc_crc(const device_config_t *cfg)
|
|
{
|
|
return flash_param_crc32(cfg, offsetof(device_config_t, crc));
|
|
}
|
|
|
|
static const char *skip_whitespace(const char *str)
|
|
{
|
|
while (*str == ' ' || *str == '\t') {
|
|
++str;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
static void trim_trailing(char *str)
|
|
{
|
|
int len = (int)strlen(str);
|
|
while (len > 0 && (str[len - 1] == ' ' || str[len - 1] == '\t' || str[len - 1] == '\r' || str[len - 1] == '\n')) {
|
|
str[--len] = '\0';
|
|
}
|
|
}
|
|
|
|
static bool equals_ignore_case(const char *a, const char *b)
|
|
{
|
|
while (*a != '\0' && *b != '\0') {
|
|
char c1 = *a++;
|
|
char c2 = *b++;
|
|
if (c1 >= 'a' && c1 <= 'z') {
|
|
c1 -= 32;
|
|
}
|
|
if (c2 >= 'a' && c2 <= 'z') {
|
|
c2 -= 32;
|
|
}
|
|
if (c1 != c2) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return (*a == '\0' && *b == '\0');
|
|
}
|
|
|
|
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;
|
|
|
|
parsed = strtoul(value, &endptr, 10);
|
|
if (endptr == value || *skip_whitespace(endptr) != '\0') {
|
|
return -1;
|
|
}
|
|
if (parsed < min_value || parsed > max_value) {
|
|
return -1;
|
|
}
|
|
*parsed_value = (uint32_t)parsed;
|
|
return 0;
|
|
}
|
|
|
|
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 const char *link_index_to_name(uint32_t index)
|
|
{
|
|
switch (index) {
|
|
case CONFIG_LINK_S1:
|
|
return "S1";
|
|
case CONFIG_LINK_S2:
|
|
return "S2";
|
|
case CONFIG_LINK_C1:
|
|
return "C1";
|
|
case CONFIG_LINK_C2:
|
|
return "C2";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
static int parse_link_name(const char *value, uint32_t *index)
|
|
{
|
|
if (equals_ignore_case(value, "S1")) {
|
|
*index = CONFIG_LINK_S1;
|
|
return 0;
|
|
}
|
|
if (equals_ignore_case(value, "S2")) {
|
|
*index = CONFIG_LINK_S2;
|
|
return 0;
|
|
}
|
|
if (equals_ignore_case(value, "C1")) {
|
|
*index = CONFIG_LINK_C1;
|
|
return 0;
|
|
}
|
|
if (equals_ignore_case(value, "C2")) {
|
|
*index = CONFIG_LINK_C2;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static bool parse_command_with_value(const char *cmd, const char *name, const char **value)
|
|
{
|
|
size_t name_len = strlen(name);
|
|
if (!prefix_equals_ignore_case(cmd, name) || cmd[name_len] != '=') {
|
|
return false;
|
|
}
|
|
*value = skip_whitespace(cmd + name_len + 1u);
|
|
return true;
|
|
}
|
|
|
|
static char *config_next_token(char **cursor)
|
|
{
|
|
char *start = *cursor;
|
|
char *end;
|
|
|
|
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 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 mac_str[18];
|
|
char rip_str[CONFIG_LINK_COUNT][16];
|
|
uint32_t i;
|
|
|
|
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 (i = 0; i < CONFIG_LINK_COUNT; ++i) {
|
|
config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]);
|
|
}
|
|
|
|
snprintf(response, max_len,
|
|
"+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\n"
|
|
"+LINK:S1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
|
"+LINK:S2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
|
"+LINK:C1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
|
"+LINK:C2,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,
|
|
(unsigned long)g_config.uart_baudrate[0],
|
|
(unsigned long)g_config.uart_baudrate[1]);
|
|
return AT_OK;
|
|
}
|
|
|
|
int config_init(void)
|
|
{
|
|
flash_param_init();
|
|
return config_load();
|
|
}
|
|
|
|
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)) {
|
|
return 0;
|
|
}
|
|
|
|
config_set_defaults();
|
|
return -1;
|
|
}
|
|
|
|
int config_save(void)
|
|
{
|
|
g_config.magic = CONFIG_MAGIC;
|
|
g_config.version = CONFIG_VERSION;
|
|
g_config.crc = config_calc_crc(&g_config);
|
|
return flash_param_write(&g_config, sizeof(g_config));
|
|
}
|
|
|
|
void config_set_defaults(void)
|
|
{
|
|
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;
|
|
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.reconnect_interval_ms = 3000u;
|
|
g_config.crc = config_calc_crc(&g_config);
|
|
}
|
|
|
|
const device_config_t *config_get(void)
|
|
{
|
|
return &g_config;
|
|
}
|
|
|
|
device_config_t *config_get_mutable(void)
|
|
{
|
|
return &g_config;
|
|
}
|
|
|
|
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len)
|
|
{
|
|
char cmd_copy[CONFIG_CMD_MAX_LEN];
|
|
const char *value;
|
|
const char *p;
|
|
|
|
strncpy(cmd_copy, cmd, sizeof(cmd_copy) - 1u);
|
|
cmd_copy[sizeof(cmd_copy) - 1u] = '\0';
|
|
trim_trailing(cmd_copy);
|
|
p = skip_whitespace(cmd_copy);
|
|
|
|
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;
|
|
if (equals_ignore_case(p, "?") || equals_ignore_case(p, "QUERY")) {
|
|
return handle_summary_query(response, max_len);
|
|
}
|
|
if (equals_ignore_case(p, "SAVE")) {
|
|
if (config_save() != 0) {
|
|
snprintf(response, max_len, "ERROR: Save failed\r\n");
|
|
return AT_SAVE_FAILED;
|
|
}
|
|
snprintf(response, max_len, "OK: Configuration saved\r\n");
|
|
return AT_OK;
|
|
}
|
|
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(p, "DEFAULT")) {
|
|
config_set_defaults();
|
|
snprintf(response, max_len, "OK: Defaults restored\r\n");
|
|
return AT_OK;
|
|
}
|
|
if (equals_ignore_case(p, "MUX?")) {
|
|
snprintf(response, max_len, "+MUX:%u\r\nOK\r\n", g_config.mux_mode);
|
|
return AT_OK;
|
|
}
|
|
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;
|
|
}
|
|
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?")) {
|
|
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;
|
|
}
|
|
if (parse_command_with_value(p, "NET", &value)) {
|
|
char value_copy[96];
|
|
char *cursor;
|
|
char *token;
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
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?")) {
|
|
char rip_str[CONFIG_LINK_COUNT][16];
|
|
uint32_t i;
|
|
for (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:S1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
|
"+LINK:S2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
|
"+LINK:C1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
|
|
"+LINK:C2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\nOK\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;
|
|
}
|
|
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_link_name(token, &index) != 0) {
|
|
snprintf(response, max_len, "ERROR: Invalid route field\r\n");
|
|
return AT_INVALID_PARAM;
|
|
}
|
|
token = config_next_token(&cursor);
|
|
if (token == NULL) {
|
|
char rip_str[16];
|
|
config_ip_to_str(g_config.links[index].remote_ip, rip_str);
|
|
snprintf(response, max_len,
|
|
"+LINK:%s,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\nOK\r\n",
|
|
link_index_to_name(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;
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
snprintf(response, max_len, "ERROR: Unknown command\r\n");
|
|
return AT_UNKNOWN_CMD;
|
|
}
|
|
|
|
void config_ip_to_str(const uint8_t *ip, char *str)
|
|
{
|
|
sprintf(str, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
|
|
}
|
|
|
|
int config_str_to_ip(const char *str, uint8_t *ip)
|
|
{
|
|
int a, b, c, d;
|
|
if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) {
|
|
return -1;
|
|
}
|
|
if (a < 0 || a > 255 || b < 0 || b > 255 || c < 0 || c > 255 || d < 0 || d > 255) {
|
|
return -1;
|
|
}
|
|
ip[0] = (uint8_t)a;
|
|
ip[1] = (uint8_t)b;
|
|
ip[2] = (uint8_t)c;
|
|
ip[3] = (uint8_t)d;
|
|
return 0;
|
|
}
|
|
|
|
void config_mac_to_str(const uint8_t *mac, char *str)
|
|
{
|
|
sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
}
|
|
|
|
int config_str_to_mac(const char *str, uint8_t *mac)
|
|
{
|
|
int a[6];
|
|
int i;
|
|
|
|
if (sscanf(str, "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6 &&
|
|
sscanf(str, "%x-%x-%x-%x-%x-%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < 6; ++i) {
|
|
if (a[i] < 0 || a[i] > 255) {
|
|
return -1;
|
|
}
|
|
mac[i] = (uint8_t)a[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void config_uart_idle_handler(void)
|
|
{
|
|
uint16_t dma_counter = __HAL_DMA_GET_COUNTER(huart1.hdmarx);
|
|
uint16_t len = CONFIG_RX_BUFFER_SIZE - dma_counter;
|
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
|
HAL_StatusTypeDef hal_status;
|
|
|
|
if (g_uart1_tx_busy) {
|
|
return;
|
|
}
|
|
|
|
if (len > 0u && xConfigQueue != NULL) {
|
|
(void)route_send_from_isr(xConfigQueue,
|
|
0u,
|
|
0u,
|
|
ROUTE_CONN_UART1,
|
|
g_uart1_rx_buffer,
|
|
len,
|
|
&xHigherPriorityTaskWoken);
|
|
}
|
|
|
|
hal_status = HAL_UART_DMAStop(&huart1);
|
|
if (hal_status != HAL_OK) {
|
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
|
return;
|
|
}
|
|
|
|
hal_status = HAL_UART_Receive_DMA(&huart1, g_uart1_rx_buffer, CONFIG_RX_BUFFER_SIZE);
|
|
if (hal_status != HAL_OK) {
|
|
__HAL_UART_DISABLE_IT(&huart1, UART_IT_IDLE);
|
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
|
return;
|
|
}
|
|
|
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
|
}
|
|
|
|
void config_start_reception(void)
|
|
{
|
|
debug_log_write("[CFG] rx-start enter\r\n");
|
|
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
|
|
if (HAL_UART_Receive_DMA(&huart1, g_uart1_rx_buffer, CONFIG_RX_BUFFER_SIZE) != HAL_OK) {
|
|
debug_log_write("[CFG] rx-start fail\r\n");
|
|
Debug_TrapWithRttHint("cfg-rx-start-fail");
|
|
return;
|
|
}
|
|
debug_log_write("[CFG] rx-start exit\r\n");
|
|
}
|
|
|
|
static void config_respond_to_uart(route_msg_t *msg, const char *response)
|
|
{
|
|
if (msg->conn_type == ROUTE_CONN_UART1) {
|
|
g_uart1_tx_busy = true;
|
|
__HAL_UART_DISABLE_IT(&huart1, UART_IT_IDLE);
|
|
(void)HAL_UART_Transmit(&huart1, (const uint8_t *)response, (uint16_t)strlen(response), 200u);
|
|
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
|
|
g_uart1_tx_busy = false;
|
|
} else if (msg->src_id == ENDPOINT_UART2 || msg->src_id == ENDPOINT_UART3) {
|
|
uart_channel_t channel = (msg->src_id == ENDPOINT_UART3) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
|
|
uint8_t frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
|
|
uint16_t frame_len = 0u;
|
|
if (uart_mux_encode_frame(msg->src_id, 0u, (const uint8_t *)response, (uint16_t)strlen(response), frame, &frame_len, sizeof(frame))) {
|
|
(void)uart_trans_send_buffer(channel, frame, frame_len);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConfigTask(void *argument)
|
|
{
|
|
route_msg_t *msg;
|
|
at_result_t result;
|
|
|
|
(void)argument;
|
|
debug_log_write("[CFG] task-entry\r\n");
|
|
config_start_reception();
|
|
debug_log_write("[CFG] task-ready\r\n");
|
|
|
|
for (;;) {
|
|
if (xQueueReceive(xConfigQueue, &msg, portMAX_DELAY) != pdPASS) {
|
|
continue;
|
|
}
|
|
|
|
if (msg->len >= sizeof(g_config_cmd_buffer)) {
|
|
msg->len = sizeof(g_config_cmd_buffer) - 1u;
|
|
}
|
|
memcpy(g_config_cmd_buffer, msg->data, msg->len);
|
|
g_config_cmd_buffer[msg->len] = '\0';
|
|
|
|
result = config_process_at_cmd(g_config_cmd_buffer, g_config_response_buffer, sizeof(g_config_response_buffer));
|
|
config_respond_to_uart(msg, g_config_response_buffer);
|
|
if (result == AT_NEED_REBOOT) {
|
|
config_respond_to_uart(msg, "Note: Use AT+SAVE then AT+RESET to apply changes\r\n");
|
|
}
|
|
route_msg_free(msg);
|
|
|
|
if (g_reset_requested) {
|
|
g_reset_requested = false;
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
NVIC_SystemReset();
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|