Files
zTC1/mico-os/MiCO/system/command_console/mico_cli.c
2025-03-11 15:54:45 +08:00

843 lines
21 KiB
C

/**
* UNPUBLISHED PROPRIETARY SOURCE CODE
* Copyright (c) 2016 MXCHIP Inc.
*
* The contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of MXCHIP Corporation.
*
*/
#include "mico.h"
#include "mico_cli.h"
#include "stdarg.h"
#include "platform_config.h"
#include "tftp_ota/tftp.h"
#ifdef MICO_CLI_ENABLE
//int cli_printf(const char *msg, ...);
//int cli_putstr(const char *msg);
//int cli_getchar(char *inbuf);
/// CLI ///
#define RX_WAIT MICO_WAIT_FOREVER
#define SEND_WAIT MICO_WAIT_FOREVER
#define RET_CHAR '\n'
#define END_CHAR '\r'
#define PROMPT "\r\n# "
#define EXIT_MSG "exit"
#define NUM_BUFFERS 1
#define MAX_COMMANDS 50
#define INBUF_SIZE 80
#define OUTBUF_SIZE 1024
#ifndef MOC
static void task_Command( char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv );
#endif
#if (defined CONFIG_PLATFORM_8195A) & (!defined MOC100)
#define LOG_SERVICE_BUFLEN 100
int wifi_set_mac_address(char * mac);
int wifi_get_mac_address(char * mac);
char log_buf[LOG_SERVICE_BUFLEN];
struct cli_st {
int initialized;
const struct cli_command *commands[MAX_COMMANDS];
unsigned int num_commands;
int echo_disabled;
char outbuf[OUTBUF_SIZE];
} ;
static struct cli_st *pCli = NULL;
mico_semaphore_t log_rx_interrupt_sema;
#else
struct cli_st {
int initialized;
unsigned int bp; /* buffer pointer */
char inbuf[INBUF_SIZE];
char outbuf[OUTBUF_SIZE];
const struct cli_command *commands[MAX_COMMANDS];
unsigned int num_commands;
int echo_disabled;
} ;
static struct cli_st *pCli = NULL;
static uint8_t *cli_rx_data;
static ring_buffer_t cli_rx_buffer;
static const mico_uart_config_t cli_uart_config =
{
.baud_rate = STDIO_UART_BAUDRATE,
.data_width = DATA_WIDTH_8BIT,
.parity = NO_PARITY,
.stop_bits = STOP_BITS_1,
.flow_control = FLOW_CONTROL_DISABLED,
.flags = UART_WAKEUP_DISABLE,
};
static int cli_putstr(const char *msg);
#endif
/* Find the command 'name' in the cli commands table.
* If len is 0 then full match will be performed else upto len bytes.
* Returns: a pointer to the corresponding cli_command struct or NULL.
*/
static const struct cli_command *lookup_command(char *name, int len)
{
int i = 0;
int n = 0;
while (i < MAX_COMMANDS && n < pCli->num_commands) {
if (pCli->commands[i]->name == NULL) {
i++;
continue;
}
/* See if partial or full match is expected */
if (len != 0) {
if (!strncmp(pCli->commands[i]->name, name, len))
return pCli->commands[i];
} else {
if (!strcmp(pCli->commands[i]->name, name))
return pCli->commands[i];
}
i++;
n++;
}
return NULL;
}
/* Parse input line and locate arguments (if any), keeping count of the number
* of arguments and their locations. Look up and call the corresponding cli
* function if one is found and pass it the argv array.
*
* Returns: 0 on success: the input line contained at least a function name and
* that function exists and was called.
* 1 on lookup failure: there is no corresponding function for the
* input line.
* 2 on invalid syntax: the arguments list couldn't be parsed
*/
static int handle_input(char *inbuf)
{
struct {
unsigned inArg:1;
unsigned inQuote:1;
unsigned done:1;
} stat;
static char *argv[16];
int argc = 0;
int i = 0;
const struct cli_command *command = NULL;
const char *p;
memset((void *)&argv, 0, sizeof(argv));
memset(&stat, 0, sizeof(stat));
do {
switch (inbuf[i]) {
case '\0':
if (stat.inQuote)
return 2;
stat.done = 1;
break;
case '"':
if (i > 0 && inbuf[i - 1] == '\\' && stat.inArg) {
memcpy(&inbuf[i - 1], &inbuf[i],
strlen(&inbuf[i]) + 1);
--i;
break;
}
if (!stat.inQuote && stat.inArg)
break;
if (stat.inQuote && !stat.inArg)
return 2;
if (!stat.inQuote && !stat.inArg) {
stat.inArg = 1;
stat.inQuote = 1;
argc++;
argv[argc - 1] = &inbuf[i + 1];
} else if (stat.inQuote && stat.inArg) {
stat.inArg = 0;
stat.inQuote = 0;
inbuf[i] = '\0';
}
break;
case ' ':
if (i > 0 && inbuf[i - 1] == '\\' && stat.inArg) {
memcpy(&inbuf[i - 1], &inbuf[i],
strlen(&inbuf[i]) + 1);
--i;
break;
}
if (!stat.inQuote && stat.inArg) {
stat.inArg = 0;
inbuf[i] = '\0';
}
break;
default:
if (!stat.inArg) {
stat.inArg = 1;
argc++;
argv[argc - 1] = &inbuf[i];
}
break;
}
} while (!stat.done && ++i < INBUF_SIZE);
if (stat.inQuote)
return 2;
if (argc < 1)
return 0;
if (!pCli->echo_disabled)
cli_printf("\r\n");
/*
* Some comamands can allow extensions like foo.a, foo.b and hence
* compare commands before first dot.
*/
i = ((p = strchr(argv[0], '.')) == NULL) ? 0 :
(p - argv[0]);
command = lookup_command(argv[0], i);
if (command == NULL)
return 1;
memset(pCli->outbuf, 0, OUTBUF_SIZE);
cli_putstr("\r\n");
command->function(pCli->outbuf, OUTBUF_SIZE, argc, argv);
cli_putstr(pCli->outbuf);
return 0;
}
#if (!defined CONFIG_PLATFORM_8195A) | (defined MOC100)
/* Perform basic tab-completion on the input buffer by string-matching the
* current input line against the cli functions table. The current input line
* is assumed to be NULL-terminated. */
static void tab_complete(char *inbuf, unsigned int *bp)
{
int i, n, m;
const char *fm = NULL;
cli_printf("\r\n");
/* show matching commands */
for (i = 0, n = 0, m = 0; i < MAX_COMMANDS && n < pCli->num_commands;
i++) {
if (pCli->commands[i]->name != NULL) {
if (!strncmp(inbuf, pCli->commands[i]->name, *bp)) {
m++;
if (m == 1)
fm = pCli->commands[i]->name;
else if (m == 2)
cli_printf("%s %s ", fm,
pCli->commands[i]->name);
else
cli_printf("%s ",
pCli->commands[i]->name);
}
n++;
}
}
/* there's only one match, so complete the line */
if (m == 1 && fm) {
n = strlen(fm) - *bp;
if (*bp + n < INBUF_SIZE) {
memcpy(inbuf + *bp, fm + *bp, n);
*bp += n;
inbuf[(*bp)++] = ' ';
inbuf[*bp] = '\0';
}
}
/* just redraw input line */
cli_printf("%s%s", PROMPT, inbuf);
}
/* Get an input line.
*
* Returns: 1 if there is input, 0 if the line should be ignored. */
static int get_input(char *inbuf, unsigned int *bp)
{
if (inbuf == NULL) {
return 0;
}
while (cli_getchar(&inbuf[*bp]) == 1) {
if (inbuf[*bp] == RET_CHAR)
continue;
if (inbuf[*bp] == END_CHAR) { /* end of input line */
inbuf[*bp] = '\0';
*bp = 0;
return 1;
}
if ((inbuf[*bp] == 0x08) || /* backspace */
(inbuf[*bp] == 0x7f)) { /* DEL */
if (*bp > 0) {
(*bp)--;
if (!pCli->echo_disabled)
cli_printf("%c %c", 0x08, 0x08);
}
continue;
}
if (inbuf[*bp] == '\t') {
inbuf[*bp] = '\0';
tab_complete(inbuf, bp);
continue;
}
if (!pCli->echo_disabled)
cli_printf("%c", inbuf[*bp]);
(*bp)++;
if (*bp >= INBUF_SIZE) {
cli_printf("Error: input buffer overflow\r\n");
cli_printf(PROMPT);
*bp = 0;
return 0;
}
}
return 0;
}
#endif
/* Print out a bad command string, including a hex
* representation of non-printable characters.
* Non-printable characters show as "\0xXX".
*/
static void print_bad_command(char *cmd_string)
{
if (cmd_string != NULL) {
char *c = cmd_string;
cli_printf("command '");
while (*c != '\0') {
if (isprint(*c)) {
cli_printf("%c", *c);
} else {
cli_printf("\\0x%x", *c);
}
++c;
}
cli_printf("' not found\r\n");
}
}
/* Main CLI processing thread
*
* Waits to receive a command buffer pointer from an input collector, and
* then processes. Note that it must cleanup the buffer when done with it.
*
* Input collectors handle their own lexical analysis and must pass complete
* command lines to CLI.
*/
static void cli_main( uint32_t data )
{
while (1) {
int ret;
char *msg = NULL;
#if (!defined CONFIG_PLATFORM_8195A) | (defined MOC100)
if (!get_input(pCli->inbuf, &pCli->bp))
continue;
msg = pCli->inbuf;
#else
while(mico_rtos_get_semaphore(&log_rx_interrupt_sema, MICO_NEVER_TIMEOUT) != kNoErr);
msg = log_buf;
#endif
if (msg != NULL) {
if (strcmp(msg, EXIT_MSG) == 0)
break;
ret = handle_input(msg);
if (ret == 1)
print_bad_command(msg);
else if (ret == 2)
cli_printf("syntax error\r\n");
cli_printf(PROMPT);
}
}
cli_printf("CLI exited\r\n");
free(pCli);
pCli = NULL;
mico_rtos_delete_thread(NULL);
}
#ifndef MOC
static void task_Command( char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv )
{
mico_rtos_print_thread_status( pcWriteBuffer, xWriteBufferLen );
}
#endif
static void tftp_Command(char *pcWriteBuffer, int xWriteBufferLen,int argc, char **argv)
{
tftp_file_info_t cmdinfo;
int tftpcmd;
uint32_t ip;
mico_partition_t parttype;
mico_logic_partition_t *partition;
if (argc != 7) {
goto WRONGCMD;
}
if (strcmp(argv[2], "put")==0) {
tftpcmd = 0;
} else if (strcmp(argv[2], "get") == 0) {
tftpcmd = 1;
} else {
goto WRONGCMD;
}
ip = inet_addr(argv[1]);
parttype = (mico_partition_t)atoi(argv[4]);
partition = MicoFlashGetInfo( parttype );
if (partition) {
cmdinfo.flashtype = parttype;
} else {
goto WRONGCMD;
}
cmdinfo.flashaddr = strtoul(argv[5], NULL, 0);
strncpy(cmdinfo.filename, argv[3], 32);
cmdinfo.filelen= strtoul(argv[6], NULL, 0);
if (tftpcmd == 0) { // put
cmd_printf("tftp put to %s, filenmae %s. from %s flash, address 0x%lx, len %ld\r\n", argv[1], cmdinfo.filename,
partition->partition_description, cmdinfo.flashaddr, cmdinfo.filelen);
tsend(&cmdinfo, ip);
} else { // get
cmd_printf("tftp get from %s, filenmae %s. to %s flash, address 0x%lx, len %ld\r\n", argv[1], cmdinfo.filename,
partition->partition_description, cmdinfo.flashaddr, cmdinfo.filelen);
tget(&cmdinfo, ip);
}
return;
WRONGCMD:
cmd_printf("Usage: tftp <ip> put <filename> <partition type> <flashaddr> <flashlen>\r\n"
" tftp <ip> get <filenmae> <partition type> <flashaddr> <flashlen>\r\n"
);
}
static void partShow_Command(char *pcWriteBuffer, int xWriteBufferLen,int argc, char **argv)
{
mico_partition_t i;
mico_logic_partition_t *partition;
for( i = MICO_PARTITION_BOOTLOADER; i <= MICO_PARTITION_MAX; i++ ){
partition = MicoFlashGetInfo( i );
if (partition == NULL)
continue;
if (partition->partition_owner == MICO_FLASH_NONE)
continue;
cmd_printf( "%4d | %11s | Dev:%d | 0x%08lx | 0x%08lx |\r\n", i,
partition->partition_description, partition->partition_owner,
partition->partition_start_addr, partition->partition_length);
};
}
static void uptime_Command(char *pcWriteBuffer, int xWriteBufferLen,int argc, char **argv)
{
cmd_printf("UP time %ldms\r\n", mico_rtos_get_time());
}
extern void tftp_ota( void );
void tftp_ota_thread( mico_thread_arg_t arg )
{
tftp_ota( );
mico_rtos_delete_thread( NULL );
}
static void ota_Command( char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv )
{
mico_rtos_create_thread( NULL, MICO_APPLICATION_PRIORITY, "LOCAL OTA", tftp_ota_thread, 0x4096, 0 );
}
static void help_command(char *pcWriteBuffer, int xWriteBufferLen,int argc, char **argv);
/*
* Command buffer API
*/
static void get_version(char *pcWriteBuffer, int xWriteBufferLen,int argc, char **argv)
{
char ver[128];
uint8_t major, minor, revision;
int ret;
mico_sdk_version( &major, &minor, &revision );
cmd_printf( "Product module: %s\r\n", MODEL );
cmd_printf( "Hardware version: %s\r\n", HARDWARE_REVISION );
cmd_printf( "Manufacture: %s\r\n", MANUFACTURER );
cmd_printf( "Kernel version: %s\r\n", MicoGetVer() );
cmd_printf( "MiCO version: %d.%d.%d\r\n", major, minor, revision );
cmd_printf("Firmware version: %s\r\n", FIRMWARE_REVISION );
cmd_printf("Application info: %s\r\n", APP_INFO );
cmd_printf("Bootloader version: %s\r\n", mico_get_bootloader_ver() );
memset(ver, 0, sizeof(ver));
ret = MicoGetRfVer(ver, sizeof(ver));
if (ret == 0)
cmd_printf("WIFI version: %s\r\n", ver);
else
cmd_printf("Can't get WIFI version, return %d\r\n", ret);
}
static void reboot(char *pcWriteBuffer, int xWriteBufferLen,int argc, char **argv)
{
MicoSystemReboot();
}
static void echo_cmd_handler(char *pcWriteBuffer, int xWriteBufferLen,int argc, char **argv)
{
if (argc == 1) {
cmd_printf("Usage: echo on/off. Echo is currently %s\r\n",
pCli->echo_disabled ? "Disabled" : "Enabled");
return;
}
if (!strcasecmp(argv[1], "on")) {
cmd_printf("Enable echo\r\n");
pCli->echo_disabled = 0;
} else if (!strcasecmp(argv[1], "off")) {
cmd_printf("Disable echo\r\n");
pCli->echo_disabled = 1;
}
}
static void cli_exit_handler(char *pcWriteBuffer, int xWriteBufferLen,int argc, char **argv)
{
// exit command not executed
}
static const struct cli_command built_ins[] = {
{"help", NULL, help_command},
{"version", NULL, get_version},
{"echo", NULL, echo_cmd_handler},
{"exit", "CLI exit", cli_exit_handler},
/// WIFI
{"scan", "scan ap", wifiscan_Command},
{"wifistate", "Show wifi state", wifistate_Command},
{"wifidebug", "wifidebug on/off", wifidebug_Command},
// network
{"ifconfig", "Show IP address", ifconfig_Command},
{"arp", "arp show/clean", arp_Command},
{"ping", "ping <ip>", ping_Command},
{"dns", "show/clean/<domain>", dns_Command},
{"sockshow", "Show all sockets", socket_show_Command},
// os
{"tasklist", "list all thread name status", task_Command},
// others
{"memshow", "print memory information", memory_show_Command},
{"memdump", "<addr> <length>", memory_dump_Command},
{"memset", "<addr> <value 1> [<value 2> ... <value n>]", memory_set_Command},
{"memp", "print memp list", memp_dump_Command},
{"wifidriver", "show wifi driver status", driver_state_Command}, // bus credite, flow control...
{"reboot", "reboot MiCO system", reboot},
{"tftp", "tftp", tftp_Command},
{"time", "system time", uptime_Command},
{"ota", "system ota", ota_Command},
{"flash", "Flash memory map", partShow_Command},
};
/* Built-in "help" command: prints all registered commands and their help
* text string, if any. */
static void help_command(char *pcWriteBuffer, int xWriteBufferLen,int argc, char **argv)
{
int i, n;
uint32_t build_in_count = sizeof(built_ins)/sizeof(struct cli_command);
#if (DEBUG)
build_in_count++; //For command: micodebug
#endif
cmd_printf( "====Build-in Commands====\r\n" );
for (i = 0, n = 0; i < MAX_COMMANDS && n < pCli->num_commands; i++) {
if (pCli->commands[i]->name) {
cmd_printf("%s: %s\r\n", pCli->commands[i]->name,
pCli->commands[i]->help ?
pCli->commands[i]->help : "");
n++;
if( n == build_in_count ){
cmd_printf("\r\n====User Commands====\r\n");
}
}
}
}
int cli_register_command(const struct cli_command *command)
{
int i;
if (!command->name || !command->function)
return 1;
if (pCli->num_commands < MAX_COMMANDS) {
/* Check if the command has already been registered.
* Return 0, if it has been registered.
*/
for (i = 0; i < pCli->num_commands; i++) {
if (pCli->commands[i] == command)
return 0;
}
pCli->commands[pCli->num_commands++] = command;
return 0;
}
return 1;
}
int cli_unregister_command(const struct cli_command *command)
{
int i;
if (!command->name || !command->function)
return 1;
for (i = 0; i < pCli->num_commands; i++) {
if (pCli->commands[i] == command) {
pCli->num_commands--;
int remaining_cmds = pCli->num_commands - i;
if (remaining_cmds > 0) {
memmove(&pCli->commands[i], &pCli->commands[i + 1],
(remaining_cmds *
sizeof(struct cli_command *)));
}
pCli->commands[pCli->num_commands] = NULL;
return 0;
}
}
return 1;
}
int cli_register_commands(const struct cli_command *commands, int num_commands)
{
int i;
for (i = 0; i < num_commands; i++)
if (cli_register_command(commands++))
return 1;
return 0;
}
int cli_unregister_commands(const struct cli_command *commands,
int num_commands)
{
int i;
for (i = 0; i < num_commands; i++)
if (cli_unregister_command(commands++))
return 1;
return 0;
}
#if (DEBUG)
extern int mico_debug_enabled;
static void micodebug_Command(char *pcWriteBuffer, int xWriteBufferLen,int argc, char **argv)
{
if (argc == 1) {
cmd_printf("Usage: micodebug on/off. MICO debug is currently %s\r\n",
mico_debug_enabled ? "Enabled" : "Disabled");
return;
}
if (!strcasecmp(argv[1], "on")) {
cmd_printf("Enable MICO debug\r\n");
mico_debug_enabled = 1;
} else if (!strcasecmp(argv[1], "off")) {
cmd_printf("Disable MICO debug\r\n");
mico_debug_enabled = 0;
}
}
static const struct cli_command user_clis[1] = {
{"micodebug", "micodebug on/off", micodebug_Command},
};
#endif
#if (defined CONFIG_PLATFORM_8195A) & (!defined MOC100)
static void mac_command(char *pcWriteBuffer, int xWriteBufferLen,int argc, char **argv)
{
int i, n;
char mac[32];
if (argc == 1) {
wifi_get_mac_address(mac);
cmd_printf("MAC address: %s\r\n", mac);
} else {
wifi_set_mac_address(argv[1]);
cmd_printf("Set MAC address to %s\r\n", argv[1]);
}
}
static const struct cli_command rtl8195_clis[1] = {
{"mac", "mac <mac>, Get mac/Set mac. <mac>: c89346000001", mac_command},
};
void log_service_init(void)
{
}
int cli_init(void)
{
int ret;
console_init();
pCli = (struct cli_st*)malloc(sizeof(struct cli_st));
if (pCli == NULL)
return kNoMemoryErr;
memset((void *)pCli, 0, sizeof(struct cli_st));
mico_rtos_init_semaphore(&log_rx_interrupt_sema, 1);
/* add our built-in commands */
if (cli_register_commands(&built_ins[0],
sizeof(built_ins) /
sizeof(struct cli_command))) {
free(pCli);
pCli = NULL;
return kGeneralErr;
}
#if (DEBUG)
cli_register_commands(user_clis, sizeof(user_clis)/sizeof(struct cli_command));
#endif
cli_register_commands(rtl8195_clis, sizeof(rtl8195_clis)/sizeof(struct cli_command));
ret = mico_rtos_create_thread(NULL, MICO_DEFAULT_WORKER_PRIORITY, "cli", cli_main, 4096, 0);
if (ret != kNoErr) {
printf("Error: Failed to create cli thread: %d\r\n",
ret);
free(pCli);
pCli = NULL;
return kGeneralErr;
}
pCli->initialized = 1;
return kNoErr;
}
#else
int cli_init(void)
{
int ret;
pCli = (struct cli_st*)malloc(sizeof(struct cli_st));
if (pCli == NULL)
return kNoMemoryErr;
cli_rx_data = (uint8_t*)malloc(INBUF_SIZE);
if (cli_rx_data == NULL) {
free(pCli);
pCli = NULL;
return kNoMemoryErr;
}
memset((void *)pCli, 0, sizeof(struct cli_st));
ring_buffer_init ( (ring_buffer_t*)&cli_rx_buffer, (uint8_t*)cli_rx_data, INBUF_SIZE );
MicoUartInitialize( CLI_UART, &cli_uart_config, (ring_buffer_t*)&cli_rx_buffer );
/* add our built-in commands */
if (cli_register_commands(&built_ins[0],
sizeof(built_ins) /
sizeof(struct cli_command))) {
free(pCli);
pCli = NULL;
return kGeneralErr;
}
#if (DEBUG)
cli_register_commands(user_clis, 1);
#endif
ret = mico_rtos_create_thread(NULL, MICO_DEFAULT_WORKER_PRIORITY, "cli", cli_main, 1500, 0);
if (ret != kNoErr) {
cli_printf("Error: Failed to create cli thread: %d\r\n",
ret);
free(pCli);
pCli = NULL;
return kGeneralErr;
}
pCli->initialized = 1;
return kNoErr;
}
/* ========= CLI input&output APIs ============ */
int cli_printf(const char *msg, ...)
{
va_list ap;
char *pos, message[256];
int sz;
int nMessageLen = 0;
memset(message, 0, 256);
pos = message;
sz = 0;
va_start(ap, msg);
nMessageLen = vsnprintf(pos, 256 - sz, msg, ap);
va_end(ap);
if( nMessageLen<=0 ) return 0;
cli_putstr((const char*)message);
return 0;
}
int cli_putstr(const char *msg)
{
if (msg[0] != 0)
MicoUartSend( CLI_UART, (const char*)msg, strlen(msg) );
return 0;
}
int cli_getchar(char *inbuf)
{
if (MicoUartRecv(CLI_UART, inbuf, 1, MICO_WAIT_FOREVER) == 0)
return 1;
else
return 0;
}
#endif
#endif