Kiibohd Controller
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
Это архивный репозиторий. Вы можете его клонировать или просматривать файлы, но не вносить изменения или открывать задачи/запросы на слияние.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. /* Copyright (C) 2014-2015 by Jacob Alexander
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to deal
  5. * in the Software without restriction, including without limitation the rights
  6. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. * copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. * THE SOFTWARE.
  20. */
  21. // ----- Includes -----
  22. // Compiler Includes
  23. //#include <stdarg.h>
  24. // Project Includes
  25. #include <buildvars.h>
  26. #include "cli.h"
  27. #include <led.h>
  28. #include <print.h>
  29. // ----- Variables -----
  30. // Basic command dictionary
  31. CLIDict_Entry( clear, "Clear the screen.");
  32. CLIDict_Entry( cliDebug, "Enables/Disables hex output of the most recent cli input." );
  33. CLIDict_Entry( help, "You're looking at it :P" );
  34. CLIDict_Entry( led, "Enables/Disables indicator LED. Try a couple times just in case the LED is in an odd state.\r\n\t\t\033[33mWarning\033[0m: May adversely affect some modules..." );
  35. CLIDict_Entry( reload, "Signals microcontroller to reflash/reload." );
  36. CLIDict_Entry( reset, "Resets the terminal back to initial settings." );
  37. CLIDict_Entry( restart, "Sends a software restart, should be similar to powering on the device." );
  38. CLIDict_Entry( version, "Version information about this firmware." );
  39. CLIDict_Def( basicCLIDict, "General Commands" ) = {
  40. CLIDict_Item( clear ),
  41. CLIDict_Item( cliDebug ),
  42. CLIDict_Item( help ),
  43. CLIDict_Item( led ),
  44. CLIDict_Item( reload ),
  45. CLIDict_Item( reset ),
  46. CLIDict_Item( restart ),
  47. CLIDict_Item( version ),
  48. { 0, 0, 0 } // Null entry for dictionary end
  49. };
  50. // ----- Functions -----
  51. inline void prompt()
  52. {
  53. print("\033[2K\r"); // Erases the current line and resets cursor to beginning of line
  54. print("\033[1;34m:\033[0m "); // Blue bold prompt
  55. }
  56. // Initialize the CLI
  57. inline void CLI_init()
  58. {
  59. // Reset the Line Buffer
  60. CLILineBufferCurrent = 0;
  61. // History starts empty
  62. CLIHistoryHead = 0;
  63. CLIHistoryCurrent = 0;
  64. CLIHistoryTail = 0;
  65. // Set prompt
  66. prompt();
  67. // Register first dictionary
  68. CLIDictionariesUsed = 0;
  69. CLI_registerDictionary( basicCLIDict, basicCLIDictName );
  70. // Initialize main LED
  71. init_errorLED();
  72. CLILEDState = 0;
  73. // Hex debug mode is off by default
  74. CLIHexDebugMode = 0;
  75. }
  76. // Query the serial input buffer for any new characters
  77. void CLI_process()
  78. {
  79. // Current buffer position
  80. uint8_t prev_buf_pos = CLILineBufferCurrent;
  81. // Process each character while available
  82. while ( 1 )
  83. {
  84. // No more characters to process
  85. if ( Output_availablechar() == 0 )
  86. break;
  87. // Retrieve from output module
  88. char cur_char = (char)Output_getchar();
  89. // Make sure buffer isn't full
  90. if ( CLILineBufferCurrent >= CLILineBufferMaxSize )
  91. {
  92. print( NL );
  93. erro_print("Serial line buffer is full, dropping character and resetting...");
  94. // Clear buffer
  95. CLILineBufferCurrent = 0;
  96. // Reset the prompt
  97. prompt();
  98. return;
  99. }
  100. // Place into line buffer
  101. CLILineBuffer[CLILineBufferCurrent++] = cur_char;
  102. }
  103. // Display Hex Key Input if enabled
  104. if ( CLIHexDebugMode && CLILineBufferCurrent > prev_buf_pos )
  105. {
  106. print("\033[s\r\n"); // Save cursor position, and move to the next line
  107. print("\033[2K"); // Erases the current line
  108. uint8_t pos = prev_buf_pos;
  109. while ( CLILineBufferCurrent > pos )
  110. {
  111. printHex( CLILineBuffer[pos++] );
  112. print(" ");
  113. }
  114. print("\033[u"); // Restore cursor position
  115. }
  116. // If buffer has changed, output to screen while there are still characters in the buffer not displayed
  117. while ( CLILineBufferCurrent > prev_buf_pos )
  118. {
  119. // Check for control characters
  120. switch ( CLILineBuffer[prev_buf_pos] )
  121. {
  122. case 0x0D: // Enter
  123. CLILineBuffer[CLILineBufferCurrent - 1] = ' '; // Replace Enter with a space (resolves a bug in args)
  124. // Remove the space if there is no command
  125. if ( CLILineBufferCurrent == 1 )
  126. CLILineBufferCurrent--;
  127. // Process the current line buffer
  128. CLI_commandLookup();
  129. // Add the command to the history
  130. cli_saveHistory(CLILineBuffer);
  131. // Keep the array circular, discarding the older entries
  132. if (CLIHistoryTail < CLIHistoryHead)
  133. CLIHistoryHead = (CLIHistoryHead+1)%CLIMaxHistorySize;
  134. CLIHistoryTail++;
  135. if (CLIHistoryTail==CLIMaxHistorySize)
  136. {
  137. CLIHistoryTail = 0;
  138. CLIHistoryHead = 1;
  139. }
  140. CLIHistoryCurrent = CLIHistoryTail; // 'Up' starts at the last item
  141. cli_saveHistory(NULL); // delete the old temp buffer
  142. // Reset the buffer
  143. CLILineBufferCurrent = 0;
  144. // Reset the prompt after processing has finished
  145. print( NL );
  146. prompt();
  147. // XXX There is a potential bug here when resetting the buffer (losing valid keypresses)
  148. // Doesn't look like it will happen *that* often, so not handling it for now -HaaTa
  149. return;
  150. case 0x09: // Tab
  151. // Tab completion for the current command
  152. CLI_tabCompletion();
  153. CLILineBufferCurrent--; // Remove the Tab
  154. // XXX There is a potential bug here when resetting the buffer (losing valid keypresses)
  155. // Doesn't look like it will happen *that* often, so not handling it for now -HaaTa
  156. return;
  157. case 0x1B: // Esc / Escape codes
  158. // Check for other escape sequence
  159. // \e[ is an escape code in vt100 compatable terminals
  160. if (CLILineBufferCurrent>=prev_buf_pos+3
  161. && CLILineBuffer[prev_buf_pos]==0x1B
  162. && CLILineBuffer[prev_buf_pos+1]==0x5B)
  163. {
  164. // Arrow Keys: A (0x41) = Up, B (0x42) = Down, C (0x43) = Right, D (0x44) = Left
  165. if (CLILineBuffer[prev_buf_pos+2]==0x41) // Hist prev
  166. {
  167. if (CLIHistoryCurrent==CLIHistoryTail)
  168. {
  169. // Is first time pressing arrow. Save the current buffer
  170. CLILineBuffer[prev_buf_pos] = '\0';
  171. cli_saveHistory(CLILineBuffer);
  172. }
  173. // Grab the previus item from the history if there is one
  174. if (RING_PREV(CLIHistoryCurrent)!=RING_PREV(CLIHistoryHead))
  175. CLIHistoryCurrent = RING_PREV(CLIHistoryCurrent);
  176. cli_retreiveHistory(CLIHistoryCurrent);
  177. }
  178. if (CLILineBuffer[prev_buf_pos+2]==0x42) // Hist next
  179. {
  180. // Grab the next item from the history if it exists
  181. if (RING_NEXT(CLIHistoryCurrent)!=RING_NEXT(CLIHistoryTail))
  182. CLIHistoryCurrent = RING_NEXT(CLIHistoryCurrent);
  183. cli_retreiveHistory(CLIHistoryCurrent);
  184. }
  185. }
  186. return;
  187. case 0x08:
  188. case 0x7F: // Backspace
  189. // TODO - Does not handle case for arrow editing (arrows disabled atm)
  190. CLILineBufferCurrent--; // Remove the backspace
  191. // If there are characters in the buffer
  192. if ( CLILineBufferCurrent > 0 )
  193. {
  194. // Remove character from current position in the line buffer
  195. CLILineBufferCurrent--;
  196. // Remove character from tty
  197. print("\b \b");
  198. }
  199. break;
  200. default:
  201. // Place a null on the end (to use with string print)
  202. CLILineBuffer[CLILineBufferCurrent] = '\0';
  203. // Output buffer to screen
  204. dPrint( &CLILineBuffer[prev_buf_pos] );
  205. // Buffer reset
  206. prev_buf_pos++;
  207. break;
  208. }
  209. }
  210. }
  211. // Takes a string, returns two pointers
  212. // One to the first non-space character
  213. // The second to the next argument (first NULL if there isn't an argument). delimited by a space
  214. // Places a NULL at the first space after the first argument
  215. void CLI_argumentIsolation( char* string, char** first, char** second )
  216. {
  217. // Mark out the first argument
  218. // This is done by finding the first space after a list of non-spaces and setting it NULL
  219. char* cmdPtr = string - 1;
  220. while ( *++cmdPtr == ' ' ); // Skips leading spaces, and points to first character of cmd
  221. // Locates first space delimiter
  222. char* argPtr = cmdPtr + 1;
  223. while ( *argPtr != ' ' && *argPtr != '\0' )
  224. argPtr++;
  225. // Point to the first character of args or a NULL (no args) and set the space delimiter as a NULL
  226. (++argPtr)[-1] = '\0';
  227. // Set return variables
  228. *first = cmdPtr;
  229. *second = argPtr;
  230. }
  231. // Scans the CLILineBuffer for any valid commands
  232. void CLI_commandLookup()
  233. {
  234. // Ignore command if buffer is 0 length
  235. if ( CLILineBufferCurrent == 0 )
  236. return;
  237. // Set the last+1 character of the buffer to NULL for string processing
  238. CLILineBuffer[CLILineBufferCurrent] = '\0';
  239. // Retrieve pointers to command and beginning of arguments
  240. // Places a NULL at the first space after the command
  241. char* cmdPtr;
  242. char* argPtr;
  243. CLI_argumentIsolation( CLILineBuffer, &cmdPtr, &argPtr );
  244. // Scan array of dictionaries for a valid command match
  245. for ( uint8_t dict = 0; dict < CLIDictionariesUsed; dict++ )
  246. {
  247. // Parse each cmd until a null command entry is found, or an argument match
  248. for ( uint8_t cmd = 0; CLIDict[dict][cmd].name != 0; cmd++ )
  249. {
  250. // Compare the first argument and each command entry
  251. if ( eqStr( cmdPtr, (char*)CLIDict[dict][cmd].name ) == -1 )
  252. {
  253. // Run the specified command function pointer
  254. // argPtr is already pointing at the first character of the arguments
  255. (*(void (*)(char*))CLIDict[dict][cmd].function)( argPtr );
  256. return;
  257. }
  258. }
  259. }
  260. // No match for the command...
  261. print( NL );
  262. erro_dPrint("\"", CLILineBuffer, "\" is not a valid command...type \033[35mhelp\033[0m");
  263. }
  264. // Registers a command dictionary with the CLI
  265. void CLI_registerDictionary( const CLIDictItem *cmdDict, const char* dictName )
  266. {
  267. // Make sure this max limit of dictionaries hasn't been reached
  268. if ( CLIDictionariesUsed >= CLIMaxDictionaries )
  269. {
  270. erro_print("Max number of dictionaries defined already...");
  271. return;
  272. }
  273. // Add dictionary
  274. CLIDictNames[CLIDictionariesUsed] = (char*)dictName;
  275. CLIDict[CLIDictionariesUsed++] = (CLIDictItem*)cmdDict;
  276. }
  277. inline void CLI_tabCompletion()
  278. {
  279. // Ignore command if buffer is 0 length
  280. if ( CLILineBufferCurrent == 0 )
  281. return;
  282. // Set the last+1 character of the buffer to NULL for string processing
  283. CLILineBuffer[CLILineBufferCurrent] = '\0';
  284. // Retrieve pointers to command and beginning of arguments
  285. // Places a NULL at the first space after the command
  286. char* cmdPtr;
  287. char* argPtr;
  288. CLI_argumentIsolation( CLILineBuffer, &cmdPtr, &argPtr );
  289. // Tab match pointer
  290. char* tabMatch = 0;
  291. uint8_t matches = 0;
  292. // Scan array of dictionaries for a valid command match
  293. for ( uint8_t dict = 0; dict < CLIDictionariesUsed; dict++ )
  294. {
  295. // Parse each cmd until a null command entry is found, or an argument match
  296. for ( uint8_t cmd = 0; CLIDict[dict][cmd].name != 0; cmd++ )
  297. {
  298. // Compare the first argument piece to each command entry to see if it is "like"
  299. // NOTE: To save on processing, we only care about the commands and ignore the arguments
  300. // If there are arguments, and a valid tab match is found, buffer is cleared (args lost)
  301. // Also ignores full matches
  302. if ( eqStr( cmdPtr, (char*)CLIDict[dict][cmd].name ) == 0 )
  303. {
  304. // TODO Make list of commands if multiple matches
  305. matches++;
  306. tabMatch = (char*)CLIDict[dict][cmd].name;
  307. }
  308. }
  309. }
  310. // Only tab complete if there was 1 match
  311. if ( matches == 1 )
  312. {
  313. // Reset the buffer
  314. CLILineBufferCurrent = 0;
  315. // Reprint the prompt (automatically clears the line)
  316. prompt();
  317. // Display the command
  318. dPrint( tabMatch );
  319. // There are no index counts, so just copy the whole string to the input buffer
  320. while ( *tabMatch != '\0' )
  321. {
  322. CLILineBuffer[CLILineBufferCurrent++] = *tabMatch++;
  323. }
  324. }
  325. }
  326. inline int wrap(int kX, int const kLowerBound, int const kUpperBound)
  327. {
  328. int range_size = kUpperBound - kLowerBound + 1;
  329. if (kX < kLowerBound)
  330. kX += range_size * ((kLowerBound - kX) / range_size + 1);
  331. return kLowerBound + (kX - kLowerBound) % range_size;
  332. }
  333. inline void cli_saveHistory(char *buff) {
  334. if (buff==NULL) {
  335. //clear the item
  336. CLIHistoryBuffer[CLIHistoryTail][0] = '\0';
  337. return;
  338. }
  339. // Copy the line to the history
  340. int i;
  341. for (i=0; i<CLILineBufferCurrent; i++)
  342. {
  343. CLIHistoryBuffer[CLIHistoryTail][i] = CLILineBuffer[i];
  344. }
  345. }
  346. void cli_retreiveHistory(int index) {
  347. char *histMatch = CLIHistoryBuffer[index];
  348. // Reset the buffer
  349. CLILineBufferCurrent = 0;
  350. // Reprint the prompt (automatically clears the line)
  351. prompt();
  352. // Display the command
  353. dPrint( histMatch );
  354. // There are no index counts, so just copy the whole string to the input buffe
  355. CLILineBufferCurrent = 0;
  356. while ( *histMatch != '\0' )
  357. {
  358. CLILineBuffer[CLILineBufferCurrent++] = *histMatch++;
  359. }
  360. }
  361. // ----- CLI Command Functions -----
  362. void cliFunc_clear( char* args)
  363. {
  364. print("\033[2J\033[H\r"); // Erases the whole screen
  365. }
  366. void cliFunc_cliDebug( char* args )
  367. {
  368. // Toggle Hex Debug Mode
  369. if ( CLIHexDebugMode )
  370. {
  371. print( NL );
  372. info_print("Hex debug mode disabled...");
  373. CLIHexDebugMode = 0;
  374. }
  375. else
  376. {
  377. print( NL );
  378. info_print("Hex debug mode enabled...");
  379. CLIHexDebugMode = 1;
  380. }
  381. }
  382. void cliFunc_help( char* args )
  383. {
  384. // Scan array of dictionaries and print every description
  385. // (no alphabetical here, too much processing/memory to sort...)
  386. for ( uint8_t dict = 0; dict < CLIDictionariesUsed; dict++ )
  387. {
  388. // Print the name of each dictionary as a title
  389. print( NL "\033[1;32m" );
  390. _print( CLIDictNames[dict] ); // This print is requride by AVR (flash)
  391. print( "\033[0m" NL );
  392. // Parse each cmd/description until a null command entry is found
  393. for ( uint8_t cmd = 0; CLIDict[dict][cmd].name != 0; cmd++ )
  394. {
  395. dPrintStrs(" \033[35m", CLIDict[dict][cmd].name, "\033[0m");
  396. // Determine number of spaces to tab by the length of the command and TabAlign
  397. uint8_t padLength = CLIEntryTabAlign - lenStr( (char*)CLIDict[dict][cmd].name );
  398. while ( padLength-- > 0 )
  399. print(" ");
  400. _print( CLIDict[dict][cmd].description ); // This print is required by AVR (flash)
  401. print( NL );
  402. }
  403. }
  404. }
  405. void cliFunc_led( char* args )
  406. {
  407. CLILEDState ^= 1 << 1; // Toggle between 0 and 1
  408. errorLED( CLILEDState ); // Enable/Disable error LED
  409. }
  410. void cliFunc_reload( char* args )
  411. {
  412. // Request to output module to be set into firmware reload mode
  413. Output_firmwareReload();
  414. }
  415. void cliFunc_reset( char* args )
  416. {
  417. print("\033c"); // Resets the terminal
  418. }
  419. void cliFunc_restart( char* args )
  420. {
  421. // Trigger an overall software reset
  422. Output_softReset();
  423. }
  424. void cliFunc_version( char* args )
  425. {
  426. print( NL );
  427. print( " \033[1mRevision:\033[0m " CLI_Revision NL );
  428. print( " \033[1mBranch:\033[0m " CLI_Branch NL );
  429. print( " \033[1mTree Status:\033[0m " CLI_ModifiedStatus CLI_ModifiedFiles NL );
  430. print( " \033[1mRepo Origin:\033[0m " CLI_RepoOrigin NL );
  431. print( " \033[1mCommit Date:\033[0m " CLI_CommitDate NL );
  432. print( " \033[1mCommit Author:\033[0m " CLI_CommitAuthor NL );
  433. print( " \033[1mBuild Date:\033[0m " CLI_BuildDate NL );
  434. print( " \033[1mBuild OS:\033[0m " CLI_BuildOS NL );
  435. print( " \033[1mArchitecture:\033[0m " CLI_Arch NL );
  436. print( " \033[1mChip:\033[0m " CLI_Chip NL );
  437. print( " \033[1mCPU:\033[0m " CLI_CPU NL );
  438. print( " \033[1mDevice:\033[0m " CLI_Device NL );
  439. print( " \033[1mModules:\033[0m " CLI_Modules NL );
  440. }