/* * Authors (alphabetical order) * - Andre Bernet * - Andreas Weitl * - Bertrand Songis * - Bryan J. Rentoul (Gruvin) * - Cameron Weeks * - Erez Raviv * - Gabriel Birkus * - Jean-Pierre Parisy * - Karl Szmutny * - Michael Blandford * - Michal Hlavinka * - Pat Mackenzie * - Philip Moss * - Rob Thomson * - Romolo Manfredini * - 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 #define CLI_COMMAND_MAX_ARGS 8 #define CLI_COMMAND_MAX_LEN 256 OS_TID cliTaskId; TaskStack 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 = stackSize() * 4; available = stackAvailable(); 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 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, "[] []" }, { "ls", cliLs, "" }, { "play", cliPlay, "" }, { "print", cliDisplay, "
[] | " }, { "stackinfo", cliStackInfo, "" }, { "trace", cliTrace, "on | off" }, { "volume", cliVolume, "" }, { "help", cliHelp, "[]" }, { 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