/* * Copyright (C) OpenTX * * Based on code named * th9x - http://code.google.com/p/th9x * er9x - http://code.google.com/p/er9x * gruvin9x - http://code.google.com/p/gruvin9x * * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html * * 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 "diskio.h" #include #include #define CLI_COMMAND_MAX_ARGS 8 #define CLI_COMMAND_MAX_LEN 256 OS_TID cliTaskId; TaskStack _ALIGNED(8) cliStack; // stack must be aligned to 8 bytes otherwise printf for %f does not work! Fifo cliRxFifo; uint8_t cliTracesEnabled = true; // char cliLastLine[CLI_COMMAND_MAX_LEN+1]; typedef int (* CliFunction) (const char ** args); int cliExecLine(char * line); int cliExecCommand(const char ** argv); int cliHelp(const char ** argv); struct CliCommand { const char * name; CliFunction func; const char * args; }; struct MemArea { const char * name; void * start; int size; }; void cliPrompt() { serialPutc('>'); } int toLongLongInt(const char ** argv, int index, long long 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 = strtoll(s, &endptr, base); if (*endptr == '\0') return 1; else { serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[index]); return -1; } } } int toInt(const char ** argv, int index, int * val) { long long int lval = 0; int result = toLongLongInt(argv, index, &lval); *val = (int)lval; return result; } 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 cliRead(const char ** argv) { FIL file; uint32_t bytesRead = 0; int bufferSize; if (toInt(argv, 2, &bufferSize) == 0 || bufferSize < 0 ) { serialPrint("%s: Invalid buffer size \"%s\"", argv[0], argv[2]); return 0; } uint8_t * buffer = (uint8_t*) malloc(bufferSize); FRESULT result = f_open(&file, argv[1], FA_OPEN_EXISTING | FA_READ); if (result != FR_OK) { free(buffer); serialPrint("%s: File not found \"%s\"", argv[0], argv[1]); return 0; } tmr10ms_t start = get_tmr10ms(); while (true) { UINT read; result = f_read(&file, buffer, sizeof(buffer), &read); if (result == FR_OK) { if (read == 0) { // end of file f_close(&file); break; } bytesRead += read; } } uint32_t elapsedTime = (get_tmr10ms() - start) * 10; if (elapsedTime == 0) elapsedTime = 1; uint32_t speed = bytesRead / elapsedTime; serialPrint("Read %d bytes in %d ms, speed %d kB/s", bytesRead, elapsedTime, speed); free(buffer); return 0; } int cliReadSD(const char ** argv) { int startSector; int numberOfSectors; int bufferSectors; if (toInt(argv, 1, &startSector) == 0 || startSector < 0 ) { serialPrint("%s: Invalid start sector \"%s\"", argv[0], argv[1]); return 0; } if (toInt(argv, 2, &numberOfSectors) == 0 || numberOfSectors < 0 ) { serialPrint("%s: Invalid number of sectors \"%s\"", argv[0], argv[2]); return 0; } if (toInt(argv, 3, &bufferSectors) == 0 || bufferSectors < 0 ) { serialPrint("%s: Invalid number of buffrer sectors \"%s\"", argv[0], argv[3]); return 0; } uint8_t * buffer = (uint8_t*) malloc(512*bufferSectors); uint32_t bytesRead = numberOfSectors * 512; tmr10ms_t start = get_tmr10ms(); while (numberOfSectors > 0) { DRESULT res = disk_read(0, buffer, startSector, bufferSectors); if (res != RES_OK) { serialPrint("disk_read error: %d", res); free(buffer); return 0; } if (numberOfSectors >= bufferSectors) { numberOfSectors -= bufferSectors; } else { numberOfSectors = 0; } } uint32_t elapsedTime = (get_tmr10ms() - start) * 10; if (elapsedTime == 0) elapsedTime = 1; uint32_t speed = bytesRead / elapsedTime; serialPrint("Read %d bytes in %d ms, speed %d kB/s", bytesRead, elapsedTime, speed); free(buffer); 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) { serialPrint("[MAIN] %d available / %d", stackAvailable(), stackSize() * 4); // stackSize() returns size in 32bit chunks serialPrint("[MENUS] %d available / %d", menusStack.available(), menusStack.size()); serialPrint("[MIXER] %d available / %d", mixerStack.available(), mixerStack.size()); serialPrint("[AUDIO] %d available / %d", audioStack.available(), audioStack.size()); serialPrint("[CLI] %d available / %d", cliStack.available(), cliStack.size()); return 0; } int cliMemoryInfo(const char ** argv) { // struct mallinfo { // int arena; /* total space allocated from system */ // int ordblks; /* number of non-inuse chunks */ // int smblks; /* unused -- always zero */ // int hblks; /* number of mmapped regions */ // int hblkhd; /* total space in mmapped regions */ // int usmblks; /* unused -- always zero */ // int fsmblks; /* unused -- always zero */ // int uordblks; /* total allocated space */ // int fordblks; /* total non-inuse space */ // int keepcost; /* top-most, releasable (via malloc_trim) space */ // }; struct mallinfo info = mallinfo(); serialPrint("arena %d", info.arena); serialPrint("ordblks %d", info.ordblks); serialPrint("uordblks %d", info.uordblks); serialPrint("fordblks %d", info.fordblks); serialPrint("keepcost %d", info.keepcost); return 0; } int cliReboot(const char ** argv) { #if !defined(SIMU) if (!strcmp(argv[1], "wdt")) { // do a user requested watchdog test by pausing mixer thread pausePulses(); } else { NVIC_SystemReset(); } #endif return 0; } #if defined(PCBFLAMENCO) int cliReadBQ24195(const char ** argv) { int index = 0; if (toInt(argv, 1, &index) > 0) { serialPrint("BQ24195[%d] = 0x%02x", index, i2cReadBQ24195(index)); } else { serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1]); } return 0; } int cliWriteBQ24195(const char ** argv) { int index = 0; int data = 0; if (toInt(argv, 1, &index) > 0 && toInt(argv, 2, &data) > 0) { i2cWriteBQ24195(index, data); } else { serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1], argv[2]); } return 0; } #endif 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) }, { "USART2", USART2, sizeof(USART_TypeDef) }, { "USART3", USART3, sizeof(USART_TypeDef) }, { NULL, NULL, 0 }, }; int cliSet(const char ** argv) { if (!strcmp(argv[1], "rtc")) { struct gtm t; int year, month, day, hour, minute, second; if (toInt(argv, 2, &year) > 0 && toInt(argv, 3, &month) > 0 && toInt(argv, 4, &day) > 0 && toInt(argv, 5, &hour) > 0 && toInt(argv, 6, &minute) > 0 && toInt(argv, 7, &second) > 0) { t.tm_year = year-1900; t.tm_mon = month-1; t.tm_mday = day; t.tm_hour = hour; t.tm_min = minute; t.tm_sec = second; g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated rtcSetTime(&t); } else { serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1], argv[2]); } } else if (!strcmp(argv[1], "volume")) { int level = 0; if (toInt(argv, 2, &level) > 0) { setVolume(level); } else { serialPrint("%s: Invalid argument \"%s\" \"%s\"", argv[0], argv[1], argv[2]); } return 0; } return 0; } #if defined(DEBUG_INTERRUPTS) void printInterrupts() { __disable_irq(); struct InterruptCounters ic = interruptCounters; memset(&interruptCounters, 0, sizeof(interruptCounters)); interruptCounters.resetTime = get_tmr10ms(); __enable_irq(); serialPrint("Interrupts count in the last %u ms:", (get_tmr10ms() - ic.resetTime) * 10); for(int n = 0; n < INT_LAST; n++) { serialPrint("%s: %u", interruptNames[n], ic.cnt[n]); } } #endif //#if defined(DEBUG_INTERRUPTS) #if defined(DEBUG_TASKS) void printTaskSwitchLog() { serialPrint("Tasks legend [, ]:"); for(int n = 0; n <= CFG_MAX_USER_TASKS+1; n++) { if (0 == n) { serialPrint("%d: Idle", n); } if (cliTaskId == n) { serialPrint("%d: CLI", n); } else if (menusTaskId == n) { serialPrint("%d: menus", n); } else if (mixerTaskId == n) { serialPrint("%d: mixer", n); } else if (audioTaskId == n) { serialPrint("%d: audio", n); } #if defined(BLUETOOTH) else if (btTaskId == n) { serialPrint("%d: BT", n); } #endif } serialCrlf(); serialPrint("Tasks switch log at %u [