mirror of
https://github.com/opentx/opentx.git
synced 2025-07-25 01:05:10 +03:00
353 lines
8.3 KiB
C++
353 lines
8.3 KiB
C++
/*
|
|
* Authors (alphabetical order)
|
|
* - Andre Bernet <bernet.andre@gmail.com>
|
|
* - Andreas Weitl
|
|
* - Bertrand Songis <bsongis@gmail.com>
|
|
* - Bryan J. Rentoul (Gruvin) <gruvin@gmail.com>
|
|
* - Cameron Weeks <th9xer@gmail.com>
|
|
* - Erez Raviv
|
|
* - Gabriel Birkus
|
|
* - Jean-Pierre Parisy
|
|
* - Karl Szmutny
|
|
* - Michael Blandford
|
|
* - Michal Hlavinka
|
|
* - Pat Mackenzie
|
|
* - Philip Moss
|
|
* - Rob Thomson
|
|
* - Romolo Manfredini <romolo.manfredini@gmail.com>
|
|
* - Thomas Husterer
|
|
*
|
|
* opentx is based on code named
|
|
* gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/,
|
|
* er9x by Erez Raviv: http://code.google.com/p/er9x/,
|
|
* and the original (and ongoing) project by
|
|
* Thomas Husterer, th9x: http://code.google.com/p/th9x/
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include "opentx.h"
|
|
#include <ctype.h>
|
|
|
|
#define CLI_COMMAND_MAX_ARGS 8
|
|
#define CLI_COMMAND_MAX_LEN 256
|
|
|
|
OS_TID cliTaskId;
|
|
TaskStack<CLI_STACK_SIZE> cliStack;
|
|
Fifo<256> cliRxFifo;
|
|
uint8_t cliTracesEnabled = false;
|
|
|
|
typedef int (* CliFunction) (const char ** args);
|
|
|
|
struct CliCommand
|
|
{
|
|
const char * name;
|
|
CliFunction func;
|
|
const char * args;
|
|
};
|
|
|
|
struct MemArea
|
|
{
|
|
const char * name;
|
|
void * start;
|
|
int size;
|
|
};
|
|
|
|
void cliPrompt()
|
|
{
|
|
serialPutc('>');
|
|
}
|
|
|
|
int toInt(const char ** argv, int index, int * val)
|
|
{
|
|
if (*argv[index] == '\0') {
|
|
return 0;
|
|
}
|
|
else {
|
|
int base = 10;
|
|
const char * s = argv[index];
|
|
if (strlen(s) > 2 && s[0] == '0' && s[1] == 'x') {
|
|
base = 16;
|
|
s = &argv[index][2];
|
|
}
|
|
char * endptr = NULL;
|
|
*val = strtol(s, &endptr, base);
|
|
if (*endptr == '\0')
|
|
return 1;
|
|
else {
|
|
serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[index]);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
int cliBeep(const char ** argv)
|
|
{
|
|
int freq = BEEP_DEFAULT_FREQ;
|
|
int duration = 100;
|
|
if (toInt(argv, 1, &freq) >= 0 && toInt(argv, 2, &duration) >= 0) {
|
|
audioQueue.playTone(freq, duration, 20, PLAY_NOW);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cliPlay(const char ** argv)
|
|
{
|
|
audioQueue.playFile(argv[1], PLAY_NOW);
|
|
return 0;
|
|
}
|
|
|
|
int cliLs(const char ** argv)
|
|
{
|
|
FILINFO fno;
|
|
DIR dir;
|
|
char *fn; /* This function is assuming non-Unicode cfg. */
|
|
#if _USE_LFN
|
|
TCHAR lfn[_MAX_LFN + 1];
|
|
fno.lfname = lfn;
|
|
fno.lfsize = sizeof(lfn);
|
|
#endif
|
|
|
|
FRESULT res = f_opendir(&dir, argv[1]); /* Open the directory */
|
|
if (res == FR_OK) {
|
|
for (;;) {
|
|
res = f_readdir(&dir, &fno); /* Read a directory item */
|
|
if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */
|
|
|
|
#if _USE_LFN
|
|
fn = *fno.lfname ? fno.lfname : fno.fname;
|
|
#else
|
|
fn = fno.fname;
|
|
#endif
|
|
serialPrint(fn);
|
|
}
|
|
}
|
|
else {
|
|
serialPrint("%s: Invalid directory \"%s\"", argv[0], argv[1]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cliTrace(const char ** argv)
|
|
{
|
|
if (!strcmp(argv[1], "on")) {
|
|
cliTracesEnabled = true;
|
|
}
|
|
else if (!strcmp(argv[1], "off")) {
|
|
cliTracesEnabled = false;
|
|
}
|
|
else {
|
|
serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[1]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cliStackInfo(const char ** argv)
|
|
{
|
|
int tid = 0;
|
|
if (toInt(argv, 1, &tid) > 0) {
|
|
int available = 0;
|
|
int total = 0;
|
|
switch(tid) {
|
|
case MENU_TASK_INDEX:
|
|
total = menusStack.size();
|
|
available = menusStack.available();
|
|
break;
|
|
case MIXER_TASK_INDEX:
|
|
total = mixerStack.size();
|
|
available = mixerStack.available();
|
|
break;
|
|
case AUDIO_TASK_INDEX:
|
|
total = audioStack.size();
|
|
available = audioStack.available();
|
|
break;
|
|
case CLI_TASK_INDEX:
|
|
total = cliStack.size();
|
|
available = cliStack.available();
|
|
break;
|
|
case MAIN_TASK_INDEX:
|
|
total = stackAvailable();
|
|
available = stackSize();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
serialPrint("%d available (%d total)", available, total);
|
|
}
|
|
else {
|
|
serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[1]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cliVolume(const char ** argv)
|
|
{
|
|
int level = 0;
|
|
if (toInt(argv, 1, &level) > 0) {
|
|
setVolume(level);
|
|
}
|
|
else {
|
|
serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[1]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const MemArea memAreas[] = {
|
|
{ "RCC", RCC, sizeof(RCC_TypeDef) },
|
|
{ "GPIOA", GPIOA, sizeof(GPIO_TypeDef) },
|
|
{ "GPIOB", GPIOB, sizeof(GPIO_TypeDef) },
|
|
{ "GPIOC", GPIOC, sizeof(GPIO_TypeDef) },
|
|
{ "GPIOD", GPIOD, sizeof(GPIO_TypeDef) },
|
|
{ "GPIOE", GPIOE, sizeof(GPIO_TypeDef) },
|
|
{ "GPIOF", GPIOF, sizeof(GPIO_TypeDef) },
|
|
{ "GPIOG", GPIOG, sizeof(GPIO_TypeDef) },
|
|
{ "USART1", USART1, sizeof(USART_TypeDef) },
|
|
{ "USART3", USART3, sizeof(USART_TypeDef) },
|
|
{ NULL, NULL, 0 },
|
|
};
|
|
|
|
int cliDisplay(const char ** argv)
|
|
{
|
|
int address = 0;
|
|
|
|
for (const MemArea * area = memAreas; area->name != NULL; area++) {
|
|
if (!strcmp(area->name, argv[1])) {
|
|
dump((uint8_t *)area->start, area->size);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!strcmp(argv[1], "adc")) {
|
|
for (int i=0; i<NUMBER_ANALOG; i++) {
|
|
serialPrint("adc[%d] = %04X", i, Analog_values[i]);
|
|
}
|
|
}
|
|
else if (!strcmp(argv[1], "outputs")) {
|
|
for (int i=0; i<NUM_CHNOUT; i++) {
|
|
serialPrint("outputs[%d] = %04X", i, channelOutputs[i]);
|
|
}
|
|
}
|
|
else if (toInt(argv, 1, &address) > 0) {
|
|
int size = 256;
|
|
if (toInt(argv, 2, &size) >= 0) {
|
|
dump((uint8_t *)address, size);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cliHelp(const char ** argv);
|
|
|
|
const CliCommand cliCommands[] = {
|
|
{ "beep", cliBeep, "[<frequency>] [<duration>]" },
|
|
{ "ls", cliLs, "<directory>" },
|
|
{ "play", cliPlay, "<filename>" },
|
|
{ "print", cliDisplay, "<address> [<size>] | <what>" },
|
|
{ "stackinfo", cliStackInfo, "<tid>" },
|
|
{ "trace", cliTrace, "on | off" },
|
|
{ "volume", cliVolume, "<level>" },
|
|
{ "help", cliHelp, "[<command>]" },
|
|
{ NULL, NULL, NULL } /* sentinel */
|
|
};
|
|
|
|
int cliHelp(const char ** argv)
|
|
{
|
|
for (const CliCommand * command = cliCommands; command->name != NULL; command++) {
|
|
if (argv[1][0] == '\0' || !strcmp(command->name, argv[0])) {
|
|
serialPrint("%s %s", command->name, command->args);
|
|
if (argv[1][0] != '\0') {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
if (argv[1][0] != '\0') {
|
|
serialPrint("Invalid command \"%s\"", argv[0]);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int cliExecCommand(const char ** argv)
|
|
{
|
|
if (argv[0][0] == '\0')
|
|
return 0;
|
|
|
|
for (const CliCommand * command = cliCommands; command->name != NULL; command++) {
|
|
if (!strcmp(command->name, argv[0])) {
|
|
return command->func(argv);
|
|
}
|
|
}
|
|
serialPrint("Invalid command \"%s\"", argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
int cliExecLine(char * line)
|
|
{
|
|
int len = strlen(line);
|
|
const char * argv[CLI_COMMAND_MAX_ARGS];
|
|
memset(argv, 0, sizeof(argv));
|
|
int argc = 1;
|
|
argv[0] = line;
|
|
for (int i=0; i<len; i++) {
|
|
if (line[i] == ' ') {
|
|
line[i] = '\0';
|
|
if (argc < CLI_COMMAND_MAX_ARGS) {
|
|
argv[argc++] = &line[i+1];
|
|
}
|
|
}
|
|
}
|
|
return cliExecCommand(argv);
|
|
}
|
|
|
|
void cliTask(void * pdata)
|
|
{
|
|
char line[CLI_COMMAND_MAX_LEN+1];
|
|
int pos = 0;
|
|
|
|
cliPrompt();
|
|
|
|
for (;;) {
|
|
uint8_t c;
|
|
|
|
while (!cliRxFifo.pop(c)) {
|
|
CoTickDelay(10); // 20ms
|
|
}
|
|
|
|
if (c == 12) {
|
|
// clear screen
|
|
serialPrint("\033[2J\033[1;1H");
|
|
cliPrompt();
|
|
}
|
|
else if (c == 127) {
|
|
// backspace
|
|
if (pos) {
|
|
line[--pos] = '\0';
|
|
serialPutc(c);
|
|
}
|
|
}
|
|
else if (c == '\r' || c == '\n') {
|
|
// enter
|
|
serialCrlf();
|
|
line[pos] = '\0';
|
|
cliExecLine(line);
|
|
pos = 0;
|
|
cliPrompt();
|
|
}
|
|
else if (isascii(c) && pos < CLI_COMMAND_MAX_LEN) {
|
|
line[pos++] = c;
|
|
serialPutc(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cliStart()
|
|
{
|
|
cliTaskId = CoCreateTaskEx(cliTask, NULL, 10, &cliStack.stack[CLI_STACK_SIZE-1], CLI_STACK_SIZE, 1, false);
|
|
}
|