2024-01-01 13:25:36 -05:00
|
|
|
/*
|
|
|
|
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file shell.c
|
|
|
|
* @brief Simple CLI shell code.
|
|
|
|
*
|
|
|
|
* @addtogroup SHELL
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <cstring>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#include "ch.h"
|
|
|
|
#include "hal.h"
|
|
|
|
#include "shell.hpp"
|
|
|
|
#include "chprintf.h"
|
|
|
|
#include "portapack.hpp"
|
|
|
|
|
2024-05-08 02:26:05 -04:00
|
|
|
#include "platform_detect.h"
|
|
|
|
|
2024-01-01 13:25:36 -05:00
|
|
|
/**
|
|
|
|
* @brief Shell termination event source.
|
|
|
|
*/
|
|
|
|
EventSource shell_terminated;
|
|
|
|
|
|
|
|
static char* _strtok(char* str, const char* delim, char** saveptr) {
|
|
|
|
char* token;
|
|
|
|
if (str)
|
|
|
|
*saveptr = str;
|
|
|
|
token = *saveptr;
|
|
|
|
|
|
|
|
if (!token)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
token += strspn(token, delim);
|
|
|
|
*saveptr = strpbrk(token, delim);
|
|
|
|
if (*saveptr)
|
|
|
|
*(*saveptr)++ = '\0';
|
|
|
|
|
|
|
|
return *token ? token : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usage(BaseSequentialStream* chp, char* p) {
|
|
|
|
chprintf(chp, "Usage: %s\r\n", p);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void list_commands(BaseSequentialStream* chp, const ShellCommand* scp) {
|
|
|
|
while (scp->sc_name != NULL) {
|
|
|
|
chprintf(chp, "%s ", scp->sc_name);
|
|
|
|
scp++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-08 02:26:05 -04:00
|
|
|
static const char* get_board_revision_string(board_rev_t rev) {
|
|
|
|
switch (rev) {
|
|
|
|
case BOARD_REV_HACKRF1_OLD:
|
|
|
|
return "HackRF R1-R5";
|
|
|
|
case BOARD_REV_HACKRF1_R6:
|
|
|
|
return "HackRF R6";
|
|
|
|
case BOARD_REV_HACKRF1_R7:
|
|
|
|
return "HackRF R7";
|
|
|
|
case BOARD_REV_HACKRF1_R8:
|
|
|
|
return "HackRF R8";
|
|
|
|
case BOARD_REV_HACKRF1_R9:
|
|
|
|
return "HackRF R9";
|
|
|
|
case BOARD_REV_HACKRF1_R10:
|
|
|
|
return "HackRF R10";
|
|
|
|
case BOARD_REV_GSG_HACKRF1_R6:
|
|
|
|
return "GSG HackRF R6";
|
|
|
|
case BOARD_REV_GSG_HACKRF1_R7:
|
|
|
|
return "GSG HackRF R7";
|
|
|
|
case BOARD_REV_GSG_HACKRF1_R8:
|
|
|
|
return "GSG HackRF R8";
|
|
|
|
case BOARD_REV_GSG_HACKRF1_R9:
|
|
|
|
return "GSG HackRF R9";
|
|
|
|
case BOARD_REV_GSG_HACKRF1_R10:
|
|
|
|
return "GSG HackRF R10";
|
|
|
|
case BOARD_REV_UNRECOGNIZED:
|
|
|
|
return "Unrecognized";
|
|
|
|
case BOARD_REV_UNDETECTED:
|
|
|
|
return "Undetected";
|
|
|
|
default:
|
|
|
|
return "Unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-01 13:25:36 -05:00
|
|
|
static void cmd_info(BaseSequentialStream* chp, int argc, char* argv[]) {
|
|
|
|
(void)argv;
|
|
|
|
if (argc > 0) {
|
|
|
|
usage(chp, const_cast<char*>("info"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
chprintf(chp, "Kernel: %s\r\n", CH_KERNEL_VERSION);
|
|
|
|
#ifdef CH_COMPILER_NAME
|
|
|
|
chprintf(chp, "Compiler: %s\r\n", CH_COMPILER_NAME);
|
|
|
|
#endif
|
|
|
|
chprintf(chp, "Architecture: %s\r\n", CH_ARCHITECTURE_NAME);
|
|
|
|
#ifdef CH_CORE_VARIANT_NAME
|
|
|
|
chprintf(chp, "Core Variant: %s\r\n", CH_CORE_VARIANT_NAME);
|
|
|
|
#endif
|
|
|
|
#ifdef CH_PORT_INFO
|
|
|
|
chprintf(chp, "Port Info: %s\r\n", CH_PORT_INFO);
|
|
|
|
#endif
|
|
|
|
#ifdef PLATFORM_NAME
|
|
|
|
chprintf(chp, "Platform: %s\r\n", PLATFORM_NAME);
|
|
|
|
#endif
|
|
|
|
#ifdef BOARD_NAME
|
|
|
|
chprintf(chp, "Board: %s\r\n", BOARD_NAME);
|
|
|
|
#endif
|
|
|
|
#ifdef VERSION_STRING
|
|
|
|
chprintf(chp, "Mayhem Version: %s\r\n", VERSION_STRING);
|
|
|
|
#endif
|
2024-05-08 02:26:05 -04:00
|
|
|
|
|
|
|
// Usage
|
|
|
|
board_rev_t revision = detected_revision();
|
|
|
|
const char* revision_string = get_board_revision_string(revision);
|
|
|
|
chprintf(chp, "HackRF Board Rev: %s\r\n", revision_string);
|
2024-01-01 13:25:36 -05:00
|
|
|
chprintf(chp, "Reference Source: %s\r\n", portapack::clock_manager.get_source().c_str());
|
|
|
|
chprintf(chp, "Reference Freq: %s\r\n", portapack::clock_manager.get_freq().c_str());
|
|
|
|
#ifdef __DATE__
|
|
|
|
#ifdef __TIME__
|
|
|
|
chprintf(chp, "Build time: %s%s%s\r\n", __DATE__, " - ", __TIME__);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cmd_systime(BaseSequentialStream* chp, int argc, char* argv[]) {
|
|
|
|
(void)argv;
|
|
|
|
if (argc > 0) {
|
|
|
|
usage(chp, const_cast<char*>("systime"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
chprintf(chp, "%lu\r\n", (unsigned long)chTimeNow());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Array of the default commands.
|
|
|
|
*/
|
|
|
|
static ShellCommand local_commands[] = {
|
|
|
|
{"info", cmd_info},
|
|
|
|
{"systime", cmd_systime},
|
|
|
|
{NULL, NULL}};
|
|
|
|
|
|
|
|
static bool_t cmdexec(const ShellCommand* scp, BaseSequentialStream* chp, char* name, int argc, char* argv[]) {
|
|
|
|
while (scp->sc_name != NULL) {
|
|
|
|
if (strcmp(scp->sc_name, name) == 0) {
|
|
|
|
scp->sc_function(chp, argc, argv);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
scp++;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Shell thread function.
|
|
|
|
*
|
|
|
|
* @param[in] p pointer to a @p BaseSequentialStream object
|
|
|
|
* @return Termination reason.
|
|
|
|
* @retval RDY_OK terminated by command.
|
|
|
|
* @retval RDY_RESET terminated by reset condition on the I/O channel.
|
|
|
|
*
|
|
|
|
* @notapi
|
|
|
|
*/
|
|
|
|
static msg_t shell_thread(void* p) {
|
|
|
|
int n;
|
|
|
|
BaseSequentialStream* chp = ((ShellConfig*)p)->sc_channel;
|
|
|
|
const ShellCommand* scp = ((ShellConfig*)p)->sc_commands;
|
|
|
|
char *lp, *cmd, *tokp, line[SHELL_MAX_LINE_LENGTH];
|
|
|
|
char* args[SHELL_MAX_ARGUMENTS + 1];
|
|
|
|
|
|
|
|
chRegSetThreadName("shell");
|
|
|
|
chprintf(chp, "\r\nChibiOS/RT Shell\r\n");
|
|
|
|
while (TRUE) {
|
|
|
|
chprintf(chp, "ch> ");
|
|
|
|
if (shellGetLine(chp, line, sizeof(line))) {
|
|
|
|
chprintf(chp, "\r\nlogout");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
lp = _strtok(line, " \t", &tokp);
|
|
|
|
cmd = lp;
|
|
|
|
n = 0;
|
|
|
|
while ((lp = _strtok(NULL, " \t", &tokp)) != NULL) {
|
|
|
|
if (n >= SHELL_MAX_ARGUMENTS) {
|
|
|
|
chprintf(chp, "too many arguments\r\n");
|
|
|
|
cmd = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
args[n++] = lp;
|
|
|
|
}
|
|
|
|
args[n] = NULL;
|
|
|
|
if (cmd != NULL) {
|
|
|
|
if (strcmp(cmd, "exit") == 0) {
|
|
|
|
if (n > 0) {
|
|
|
|
usage(chp, const_cast<char*>("exit"));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} else if (strcmp(cmd, "help") == 0) {
|
|
|
|
if (n > 0) {
|
|
|
|
usage(chp, const_cast<char*>("help"));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
chprintf(chp, "Commands: help exit ");
|
|
|
|
list_commands(chp, local_commands);
|
|
|
|
if (scp != NULL)
|
|
|
|
list_commands(chp, scp);
|
|
|
|
chprintf(chp, "\r\n");
|
|
|
|
} else if (cmdexec(local_commands, chp, cmd, n, args) &&
|
|
|
|
((scp == NULL) || cmdexec(scp, chp, cmd, n, args))) {
|
|
|
|
chprintf(chp, "%s", cmd);
|
|
|
|
chprintf(chp, " ?\r\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
shellExit(RDY_OK);
|
|
|
|
/* Never executed, silencing a warning.*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Shell manager initialization.
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
|
|
|
void shellInit(void) {
|
|
|
|
chEvtInit(&shell_terminated);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Terminates the shell.
|
|
|
|
* @note Must be invoked from the command handlers.
|
|
|
|
* @note Does not return.
|
|
|
|
*
|
|
|
|
* @param[in] msg shell exit code
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
|
|
|
void shellExit(msg_t msg) {
|
|
|
|
/* Atomically broadcasting the event source and terminating the thread,
|
|
|
|
there is not a chSysUnlock() because the thread terminates upon return.*/
|
|
|
|
chSysLock();
|
|
|
|
chEvtBroadcastI(&shell_terminated);
|
|
|
|
chThdExitS(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Spawns a new shell.
|
|
|
|
* @pre @p CH_USE_HEAP and @p CH_USE_DYNAMIC must be enabled.
|
|
|
|
*
|
|
|
|
* @param[in] scp pointer to a @p ShellConfig object
|
|
|
|
* @param[in] size size of the shell working area to be allocated
|
|
|
|
* @param[in] prio priority level for the new shell
|
|
|
|
* @return A pointer to the shell thread.
|
|
|
|
* @retval NULL thread creation failed because memory allocation.
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
|
|
|
#if CH_USE_HEAP && CH_USE_DYNAMIC
|
|
|
|
Thread* shellCreate(const ShellConfig* scp, size_t size, tprio_t prio) {
|
|
|
|
return chThdCreateFromHeap(NULL, size, prio, shell_thread, (void*)scp);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Create statically allocated shell thread.
|
|
|
|
*
|
|
|
|
* @param[in] scp pointer to a @p ShellConfig object
|
|
|
|
* @param[in] wsp pointer to a working area dedicated to the shell thread stack
|
|
|
|
* @param[in] size size of the shell working area
|
|
|
|
* @param[in] prio priority level for the new shell
|
|
|
|
* @return A pointer to the shell thread.
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
|
|
|
Thread* shellCreateStatic(const ShellConfig* scp, void* wsp, size_t size, tprio_t prio) {
|
|
|
|
return chThdCreateStatic(wsp, size, prio, shell_thread, (void*)scp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Reads a whole line from the input channel.
|
|
|
|
*
|
|
|
|
* @param[in] chp pointer to a @p BaseSequentialStream object
|
|
|
|
* @param[in] line pointer to the line buffer
|
|
|
|
* @param[in] size buffer maximum length
|
|
|
|
* @return The operation status.
|
|
|
|
* @retval TRUE the channel was reset or CTRL-D pressed.
|
|
|
|
* @retval FALSE operation successful.
|
|
|
|
*
|
|
|
|
* @api
|
|
|
|
*/
|
|
|
|
bool_t shellGetLine(BaseSequentialStream* chp, char* line, unsigned size) {
|
|
|
|
char* p = line;
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
char c;
|
|
|
|
|
|
|
|
if (chSequentialStreamRead(chp, (uint8_t*)&c, 1) == 0)
|
|
|
|
return TRUE;
|
|
|
|
if (c == 4) {
|
|
|
|
chprintf(chp, "^D");
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
if ((c == 8) || (c == 127)) {
|
|
|
|
if (p != line) {
|
|
|
|
chSequentialStreamPut(chp, c);
|
|
|
|
chSequentialStreamPut(chp, 0x20);
|
|
|
|
chSequentialStreamPut(chp, c);
|
|
|
|
p--;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (c == '\r') {
|
|
|
|
chprintf(chp, "\r\n");
|
|
|
|
*p = 0;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (c < 0x20)
|
|
|
|
continue;
|
|
|
|
if (p < line + size - 1) {
|
|
|
|
chSequentialStreamPut(chp, c);
|
|
|
|
*p++ = (char)c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @} */
|