335 lines
8.8 KiB
C
335 lines
8.8 KiB
C
/**
|
|
* @file cli_core.c
|
|
* @brief CLI core implementation: command table + handlers + line execution.
|
|
* Transport-agnostic: all output goes through the current cli_out_fn
|
|
* (default: USART1 DMA TX). UART frontend lives in app_cli.c, BLE frame
|
|
* frontend in app/ble/app_ble_proto.c.
|
|
*
|
|
* @note Concurrency: the output callback and the shared TX buffer are single
|
|
* instances. The UART frontend (CliTask) and the BLE frontend (BleTask)
|
|
* are expected to execute commands rarely and never concurrently; the
|
|
* BLE frontend sets/restores the callback around each cli_exec_line()
|
|
* and the UART frontend forces the default (NULL) before dispatch.
|
|
*/
|
|
#include "cli_core.h"
|
|
#include "app_version.h"
|
|
#include "bsp_usart.h"
|
|
#include "bsp_led.h"
|
|
#include "app_tasks.h"
|
|
#include "app_ota.h"
|
|
#include "dfu_layout.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
|
|
#include "boot_crc32.h"
|
|
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
|
|
#define CLI_TX_BUF_SIZE 96
|
|
#define CLI_MAX_ARGS 8
|
|
|
|
typedef void (*CliCmdHandler_t)(int argc, char* argv[]);
|
|
|
|
typedef struct
|
|
{
|
|
const char* name;
|
|
const char* help;
|
|
CliCmdHandler_t handler;
|
|
} CliCmd_t;
|
|
|
|
static char s_txBuf[CLI_TX_BUF_SIZE];
|
|
static cli_out_fn s_out = NULL; /* NULL = default UART DMA output */
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Output */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/**
|
|
* @brief Default output: send a string through USART1 DMA TX.
|
|
* @note Strings must contain explicit "\r\n" (no auto CR like fputc).
|
|
*/
|
|
static void cli_out_uart(const char* str)
|
|
{
|
|
bsp_usart_write_dma((const uint8_t*)str, (uint16_t)strlen(str));
|
|
}
|
|
|
|
void cli_set_output(cli_out_fn out)
|
|
{
|
|
s_out = out;
|
|
}
|
|
|
|
void cli_write(const char* str)
|
|
{
|
|
if (s_out != NULL)
|
|
{
|
|
s_out(str);
|
|
}
|
|
else
|
|
{
|
|
cli_out_uart(str);
|
|
}
|
|
}
|
|
|
|
void cli_printf(const char* fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
vsnprintf(s_txBuf, CLI_TX_BUF_SIZE, fmt, args);
|
|
va_end(args);
|
|
s_txBuf[CLI_TX_BUF_SIZE - 1] = '\0';
|
|
cli_write(s_txBuf);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Built-in commands */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
static void CmdHelp(int argc, char* argv[]);
|
|
|
|
/**
|
|
* @brief "version": show firmware and clock info.
|
|
*/
|
|
static void CmdVersion(int argc, char* argv[])
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
cli_printf("firmware version: %s\r\n", APP_FW_VERSION);
|
|
cli_write("N32WB031 FreeRTOS + UART(DMA) + LED + CLI demo\r\n");
|
|
cli_printf("SystemCoreClock = %lu Hz\r\n", (unsigned long)SystemCoreClock);
|
|
}
|
|
|
|
/**
|
|
* @brief "sysinfo": show FreeRTOS runtime info (uptime, tasks, heap,
|
|
* per-task state/priority/stack high water mark).
|
|
*/
|
|
static void CmdSysInfo(int argc, char* argv[])
|
|
{
|
|
static char s_taskList[384]; /* vTaskList output buffer */
|
|
uint32_t ticks = (uint32_t)xTaskGetTickCount();
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
cli_printf("uptime: %lu ticks (%lu s)\r\n",
|
|
(unsigned long)ticks, (unsigned long)(ticks / configTICK_RATE_HZ));
|
|
cli_printf("tasks: %u\r\n", (unsigned int)uxTaskGetNumberOfTasks());
|
|
cli_printf("heap: total %u B, free %u B, min ever free %u B\r\n",
|
|
(unsigned int)configTOTAL_HEAP_SIZE,
|
|
(unsigned int)xPortGetFreeHeapSize(),
|
|
(unsigned int)xPortGetMinimumEverFreeHeapSize());
|
|
|
|
/* vTaskList lines already end with "\r\n"; stack column is in words */
|
|
cli_write("name state prio stack(w) num\r\n");
|
|
s_taskList[0] = '\0';
|
|
vTaskList(s_taskList);
|
|
cli_write(s_taskList);
|
|
}
|
|
|
|
/**
|
|
* @brief "log [on|off]": show or set the active log printing switch.
|
|
*/
|
|
static void CmdLog(int argc, char* argv[])
|
|
{
|
|
if (argc < 2)
|
|
{
|
|
cli_printf("log is %s\r\n", AppTasks_GetLogEnabled() ? "on" : "off");
|
|
return;
|
|
}
|
|
|
|
if (strcmp(argv[1], "on") == 0)
|
|
{
|
|
AppTasks_SetLogEnabled(1);
|
|
}
|
|
else if (strcmp(argv[1], "off") == 0)
|
|
{
|
|
AppTasks_SetLogEnabled(0);
|
|
}
|
|
else
|
|
{
|
|
cli_write("usage: log [on|off]\r\n");
|
|
return;
|
|
}
|
|
|
|
cli_printf("log set to %s\r\n", AppTasks_GetLogEnabled() ? "on" : "off");
|
|
}
|
|
|
|
/**
|
|
* @brief "led <1|2> <on|off|toggle>": control a board LED.
|
|
*/
|
|
static void CmdLed(int argc, char* argv[])
|
|
{
|
|
GPIO_Module* port;
|
|
uint16_t pin;
|
|
|
|
if (argc < 3)
|
|
{
|
|
cli_write("usage: led <1|2> <on|off|toggle>\r\n");
|
|
return;
|
|
}
|
|
|
|
if (argv[1][0] == '1' && argv[1][1] == '\0')
|
|
{
|
|
port = LED1_PORT;
|
|
pin = LED1_PIN;
|
|
}
|
|
else if (argv[1][0] == '2' && argv[1][1] == '\0')
|
|
{
|
|
port = LED2_PORT;
|
|
pin = LED2_PIN;
|
|
}
|
|
else
|
|
{
|
|
cli_write("usage: led <1|2> <on|off|toggle>\r\n");
|
|
return;
|
|
}
|
|
|
|
if (strcmp(argv[2], "on") == 0)
|
|
{
|
|
bsp_led_on(port, pin);
|
|
}
|
|
else if (strcmp(argv[2], "off") == 0)
|
|
{
|
|
bsp_led_off(port, pin);
|
|
}
|
|
else if (strcmp(argv[2], "toggle") == 0)
|
|
{
|
|
bsp_led_toggle(port, pin);
|
|
}
|
|
else
|
|
{
|
|
cli_write("usage: led <1|2> <on|off|toggle>\r\n");
|
|
return;
|
|
}
|
|
|
|
cli_printf("led %s %s done\r\n", argv[1], argv[2]);
|
|
}
|
|
|
|
/**
|
|
* @brief "bankinfo": show the running bank and the bootsetting record.
|
|
*/
|
|
static void CmdBankInfo(int argc, char* argv[])
|
|
{
|
|
caiic_bootsetting_t bs;
|
|
uint32_t self = app_ota_current_bank_base();
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
cli_printf("running bank: %s (base 0x%08lX)\r\n",
|
|
(self == CAIIC_APP1_BASE) ? "APP1" :
|
|
(self == CAIIC_APP2_BASE) ? "APP2" : "UNKNOWN",
|
|
(unsigned long)self);
|
|
|
|
memcpy(&bs, (const void*)CAIIC_BOOTSETTING_ADDR, sizeof(bs));
|
|
if (bs.magic != CAIIC_BOOT_MAGIC)
|
|
{
|
|
cli_write("bootsetting: invalid (blank or magic mismatch)\r\n");
|
|
return;
|
|
}
|
|
|
|
cli_printf("bootsetting: active_bank=%u, struct_crc %s\r\n",
|
|
(unsigned int)bs.active_bank,
|
|
(bs.crc32 == caiic_crc32((const uint8_t*)CAIIC_BOOTSETTING_ADDR,
|
|
(uint32_t)offsetof(caiic_bootsetting_t, crc32)))
|
|
? "ok" : "BAD");
|
|
cli_printf("bank1: size=%lu crc32=0x%08lX version=0x%06lX\r\n",
|
|
(unsigned long)bs.bank1.size, (unsigned long)bs.bank1.crc32,
|
|
(unsigned long)bs.bank1.version);
|
|
cli_printf("bank2: size=%lu crc32=0x%08lX version=0x%06lX\r\n",
|
|
(unsigned long)bs.bank2.size, (unsigned long)bs.bank2.crc32,
|
|
(unsigned long)bs.bank2.version);
|
|
}
|
|
|
|
/* Command table; add new commands here */
|
|
static const CliCmd_t s_cmds[] = {
|
|
{"help", "list all commands", CmdHelp},
|
|
{"version", "show firmware version", CmdVersion},
|
|
{"sysinfo", "show FreeRTOS runtime info (uptime/tasks/heap/task list)", CmdSysInfo},
|
|
{"log", "log [on|off]: active printing switch (default off)", CmdLog},
|
|
{"led", "led <1|2> <on|off|toggle>: control board LED", CmdLed},
|
|
{"bankinfo", "show running bank and bootsetting record", CmdBankInfo},
|
|
};
|
|
|
|
#define CLI_CMD_COUNT (sizeof(s_cmds) / sizeof(s_cmds[0]))
|
|
|
|
/**
|
|
* @brief "help": list all registered commands.
|
|
*/
|
|
static void CmdHelp(int argc, char* argv[])
|
|
{
|
|
uint32_t i;
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
cli_write("commands:\r\n");
|
|
for (i = 0; i < CLI_CMD_COUNT; i++)
|
|
{
|
|
cli_printf(" %-8s %s\r\n", s_cmds[i].name, s_cmds[i].help);
|
|
}
|
|
cli_write("keys: TAB = complete, UP/DOWN = history, Ctrl+Z = log off\r\n");
|
|
}
|
|
|
|
uint32_t cli_cmd_count(void)
|
|
{
|
|
return CLI_CMD_COUNT;
|
|
}
|
|
|
|
const char* cli_cmd_name(uint32_t idx)
|
|
{
|
|
return (idx < CLI_CMD_COUNT) ? s_cmds[idx].name : NULL;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Line parsing / dispatch */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
int cli_exec_line(char* line)
|
|
{
|
|
char* argv[CLI_MAX_ARGS];
|
|
int argc = 0;
|
|
char* p = line;
|
|
uint32_t i;
|
|
|
|
/* Split on spaces/tabs */
|
|
while (*p != '\0' && argc < CLI_MAX_ARGS)
|
|
{
|
|
while (*p == ' ' || *p == '\t')
|
|
{
|
|
p++;
|
|
}
|
|
if (*p == '\0')
|
|
{
|
|
break;
|
|
}
|
|
argv[argc++] = p;
|
|
while (*p != '\0' && *p != ' ' && *p != '\t')
|
|
{
|
|
p++;
|
|
}
|
|
if (*p != '\0')
|
|
{
|
|
*p++ = '\0';
|
|
}
|
|
}
|
|
|
|
if (argc == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < CLI_CMD_COUNT; i++)
|
|
{
|
|
if (strcmp(argv[0], s_cmds[i].name) == 0)
|
|
{
|
|
s_cmds[i].handler(argc, argv);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
cli_printf("unknown command: %s (try 'help')\r\n", argv[0]);
|
|
return 1;
|
|
}
|