/* * 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 #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; 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 */ serialPrint(fno.fname); } f_closedir(&dir); } 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); if (!buffer) { serialPrint("Not enough memory"); return 0; } 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 buffer sectors \"%s\"", argv[0], argv[3]); return 0; } uint8_t * buffer = (uint8_t*) malloc(512*bufferSectors); if (!buffer) { serialPrint("Not enough memory"); return 0; } 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, sector: %d(%d)", res, startSector, numberOfSectors); } #if 0 for(uint32_t n=0; n= bufferSectors) { numberOfSectors -= bufferSectors; startSector += 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 cliTestSD(const char ** argv) { // Do the read test on the SD card and report back the result // get sector count uint32_t sectorCount; if (disk_ioctl(0, GET_SECTOR_COUNT, §orCount) != RES_OK) { serialPrint("Error: can't read sector count"); return 0; } serialPrint("SD card has %u sectors", sectorCount); // read last 16 sectors one sector at the time serialPrint("Starting single sector read test, reading 16 sectors one by one"); uint8_t * buffer = (uint8_t*) malloc(512); if (!buffer) { serialPrint("Not enough memory"); return 0; } for (uint32_t s = sectorCount - 16; s& cats = modList.getCategories(); while(1) { for (list::const_iterator cat_it = cats.begin(); cat_it != cats.end(); ++cat_it) { for (ModelsCategory::iterator mod_it = (*cat_it)->begin(); mod_it != (*cat_it)->end(); mod_it++) { if (!(*mod_it)->fetchRfData()) { serialPrint("Error while fetching RF data..."); return 0; } if (++count >= 100) goto done; } } } done: uint32_t actualRuntime = (uint32_t)CoGetOSTime() - start; serialPrint("Done fetching %ix RF data: %d ms", count, actualRuntime*2); return 0; } #endif // #if defined(COLORLCD) int cliTest(const char ** argv) { if (!strcmp(argv[1], "new")) { return cliTestNew(); } else if (!strcmp(argv[1], "std::exception")) { serialPrint("Not implemented"); } #if defined(COLORLCD) else if (!strcmp(argv[1], "graphics")) { return cliTestGraphics(); } else if (!strcmp(argv[1], "memspd")) { return cliTestMemorySpeed(); } else if (!strcmp(argv[1], "modelslist")) { return cliTestModelsList(); } #endif else { serialPrint("%s: Invalid argument \"%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) { 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; } extern int _end; extern int _heap_end; extern unsigned char *heap; 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("mallinfo:"); serialPrint("\tarena %d bytes", info.arena); serialPrint("\tordblks %d bytes", info.ordblks); serialPrint("\tuordblks %d bytes", info.uordblks); serialPrint("\tfordblks %d bytes", info.fordblks); serialPrint("\tkeepcost %d bytes", info.keepcost); serialPrint("\nHeap:"); serialPrint("\tstart %p", (unsigned char *)&_end); serialPrint("\tend %p", (unsigned char *)&_heap_end); serialPrint("\tcurr %p", heap); serialPrint("\tused %d bytes", (int)(heap - (unsigned char *)&_end)); serialPrint("\tfree %d bytes", (int)((unsigned char *)&_heap_end - heap)); #if defined(LUA) serialPrint("\nLua:"); uint32_t s = luaGetMemUsed(lsScripts); serialPrint("\tScripts %u", s); #if defined(COLORLCD) uint32_t w = luaGetMemUsed(lsWidgets); uint32_t e = luaExtraMemoryUsage; serialPrint("\tWidgets %u", w); serialPrint("\tExtra %u", e); serialPrint("------------"); serialPrint("\tTotal %u", s + w + e); #endif #endif 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; } 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-TM_YEAR_BASE; 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]); } } #if !defined(SOFTWARE_VOLUME) 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; } #endif 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); } } serialCrlf(); serialPrint("Tasks switch log at %u [