X-Git-Url: https://git.donarmstrong.com/?p=kiibohd-controller.git;a=blobdiff_plain;f=Debug%2Fcli%2Fcli.c;h=5c83ee64c98ce4bf60a39c01b80d1f76a8110020;hp=ce843d6d6b492efc52681f25664e15ced5a0347e;hb=ccf4f34e928afd88ee3dd34e8248ce04d48dad63;hpb=30c45e948f706f650e79addb6bf6388330f88559 diff --git a/Debug/cli/cli.c b/Debug/cli/cli.c index ce843d6..5c83ee6 100644 --- a/Debug/cli/cli.c +++ b/Debug/cli/cli.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 by Jacob Alexander +/* Copyright (C) 2014-2015 by Jacob Alexander * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,21 +21,36 @@ // ----- Includes ----- -// Compiler Includes -//#include - // Project Includes +#include #include "cli.h" +#include #include +#include // ----- Variables ----- // Basic command dictionary -CLIDictItem basicCLIDict[] = { - { "help", "This command :P", cliFunc_help }, - { "version", "Version information about this firmware.", cliFunc_version }, +CLIDict_Entry( clear, "Clear the screen."); +CLIDict_Entry( cliDebug, "Enables/Disables hex output of the most recent cli input." ); +CLIDict_Entry( help, "You're looking at it :P" ); +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..." ); +CLIDict_Entry( reload, "Signals microcontroller to reflash/reload." ); +CLIDict_Entry( reset, "Resets the terminal back to initial settings." ); +CLIDict_Entry( restart, "Sends a software restart, should be similar to powering on the device." ); +CLIDict_Entry( version, "Version information about this firmware." ); + +CLIDict_Def( basicCLIDict, "General Commands" ) = { + CLIDict_Item( clear ), + CLIDict_Item( cliDebug ), + CLIDict_Item( help ), + CLIDict_Item( led ), + CLIDict_Item( reload ), + CLIDict_Item( reset ), + CLIDict_Item( restart ), + CLIDict_Item( version ), { 0, 0, 0 } // Null entry for dictionary end }; @@ -45,37 +60,51 @@ CLIDictItem basicCLIDict[] = { inline void prompt() { - print(": "); + print("\033[2K\r"); // Erases the current line and resets cursor to beginning of line + print("\033[1;34m:\033[0m "); // Blue bold prompt } -inline void init_cli() +// Initialize the CLI +inline void CLI_init() { // Reset the Line Buffer CLILineBufferCurrent = 0; + // History starts empty + CLIHistoryHead = 0; + CLIHistoryCurrent = 0; + CLIHistoryTail = 0; + // Set prompt prompt(); // Register first dictionary CLIDictionariesUsed = 0; - registerDictionary_cli( basicCLIDict ); + CLI_registerDictionary( basicCLIDict, basicCLIDictName ); + + // Initialize main LED + init_errorLED(); + CLILEDState = 0; + + // Hex debug mode is off by default + CLIHexDebugMode = 0; } -void process_cli() +// Query the serial input buffer for any new characters +void CLI_process() { // Current buffer position uint8_t prev_buf_pos = CLILineBufferCurrent; // Process each character while available - int result = 0; while ( 1 ) { // No more characters to process - result = usb_serial_getchar(); // Retrieve from serial module // TODO Make USB agnostic - if ( result == -1 ) + if ( Output_availablechar() == 0 ) break; - char cur_char = (char)result; + // Retrieve from output module + char cur_char = (char)Output_getchar(); // Make sure buffer isn't full if ( CLILineBufferCurrent >= CLILineBufferMaxSize ) @@ -96,17 +125,60 @@ void process_cli() CLILineBuffer[CLILineBufferCurrent++] = cur_char; } + // Display Hex Key Input if enabled + if ( CLIHexDebugMode && CLILineBufferCurrent > prev_buf_pos ) + { + print("\033[s\r\n"); // Save cursor position, and move to the next line + print("\033[2K"); // Erases the current line + + uint8_t pos = prev_buf_pos; + while ( CLILineBufferCurrent > pos ) + { + printHex( CLILineBuffer[pos++] ); + print(" "); + } + + print("\033[u"); // Restore cursor position + } + // If buffer has changed, output to screen while there are still characters in the buffer not displayed while ( CLILineBufferCurrent > prev_buf_pos ) { // Check for control characters switch ( CLILineBuffer[prev_buf_pos] ) { - case 0x0D: // Enter - CLILineBufferCurrent--; // Remove the Enter + // Enter + case 0x0A: // LF + case 0x0D: // CR + CLILineBuffer[CLILineBufferCurrent - 1] = ' '; // Replace Enter with a space (resolves a bug in args) + + // Remove the space if there is no command + if ( CLILineBufferCurrent == 1 ) + { + CLILineBufferCurrent--; + } + else + { + // Add the command to the history + CLI_saveHistory( CLILineBuffer ); + + // Process the current line buffer + CLI_commandLookup(); - // Process the current line buffer - commandLookup_cli(); + // Keep the array circular, discarding the older entries + if ( CLIHistoryTail < CLIHistoryHead ) + CLIHistoryHead = ( CLIHistoryHead + 1 ) % CLIMaxHistorySize; + CLIHistoryTail++; + if ( CLIHistoryTail == CLIMaxHistorySize ) + { + CLIHistoryTail = 0; + CLIHistoryHead = 1; + } + + CLIHistoryCurrent = CLIHistoryTail; // 'Up' starts at the last item + CLI_saveHistory( NULL ); // delete the old temp buffer + + } // Reset the buffer CLILineBufferCurrent = 0; @@ -121,12 +193,46 @@ void process_cli() case 0x09: // Tab // Tab completion for the current command - // TODO + CLI_tabCompletion(); + + CLILineBufferCurrent--; // Remove the Tab + + // XXX There is a potential bug here when resetting the buffer (losing valid keypresses) + // Doesn't look like it will happen *that* often, so not handling it for now -HaaTa return; - case 0x1B: // Esc - // Check for escape sequence - // TODO + case 0x1B: // Esc / Escape codes + // Check for other escape sequence + + // \e[ is an escape code in vt100 compatible terminals + if ( CLILineBufferCurrent >= prev_buf_pos + 3 + && CLILineBuffer[ prev_buf_pos ] == 0x1B + && CLILineBuffer[ prev_buf_pos + 1] == 0x5B ) + { + // Arrow Keys: A (0x41) = Up, B (0x42) = Down, C (0x43) = Right, D (0x44) = Left + + if ( CLILineBuffer[ prev_buf_pos + 2 ] == 0x41 ) // Hist prev + { + if ( CLIHistoryCurrent == CLIHistoryTail ) + { + // Is first time pressing arrow. Save the current buffer + CLILineBuffer[ prev_buf_pos ] = '\0'; + CLI_saveHistory( CLILineBuffer ); + } + + // Grab the previus item from the history if there is one + if ( RING_PREV( CLIHistoryCurrent ) != RING_PREV( CLIHistoryHead ) ) + CLIHistoryCurrent = RING_PREV( CLIHistoryCurrent ); + CLI_retreiveHistory( CLIHistoryCurrent ); + } + if ( CLILineBuffer[ prev_buf_pos + 2 ] == 0x42 ) // Hist next + { + // Grab the next item from the history if it exists + if ( RING_NEXT( CLIHistoryCurrent ) != RING_NEXT( CLIHistoryTail ) ) + CLIHistoryCurrent = RING_NEXT( CLIHistoryCurrent ); + CLI_retreiveHistory( CLIHistoryCurrent ); + } + } return; case 0x08: @@ -158,32 +264,18 @@ void process_cli() break; } - - /* TODO Enable via option - uint8_t pos = prev_buf_pos; - while ( CLILineBuffer[pos] != 0 ) - { - printHex( CLILineBuffer[pos++] ); - print(" "); - } - - print( NL ); - */ } } -void commandLookup_cli() +// Takes a string, returns two pointers +// One to the first non-space character +// The second to the next argument (first NULL if there isn't an argument). delimited by a space +// Places a NULL at the first space after the first argument +void CLI_argumentIsolation( char* string, char** first, char** second ) { - // Ignore command if buffer is 0 length - if ( CLILineBufferCurrent == 0 ) - return; - - // Set the last+1 character of the buffer to NULL for string processing - CLILineBuffer[CLILineBufferCurrent] = '\0'; - // Mark out the first argument // This is done by finding the first space after a list of non-spaces and setting it NULL - char* cmdPtr = CLILineBuffer - 1; + char* cmdPtr = string - 1; while ( *++cmdPtr == ' ' ); // Skips leading spaces, and points to first character of cmd // Locates first space delimiter @@ -194,6 +286,27 @@ void commandLookup_cli() // Point to the first character of args or a NULL (no args) and set the space delimiter as a NULL (++argPtr)[-1] = '\0'; + // Set return variables + *first = cmdPtr; + *second = argPtr; +} + +// Scans the CLILineBuffer for any valid commands +void CLI_commandLookup() +{ + // Ignore command if buffer is 0 length + if ( CLILineBufferCurrent == 0 ) + return; + + // Set the last+1 character of the buffer to NULL for string processing + CLILineBuffer[CLILineBufferCurrent] = '\0'; + + // Retrieve pointers to command and beginning of arguments + // Places a NULL at the first space after the command + char* cmdPtr; + char* argPtr; + CLI_argumentIsolation( CLILineBuffer, &cmdPtr, &argPtr ); + // Scan array of dictionaries for a valid command match for ( uint8_t dict = 0; dict < CLIDictionariesUsed; dict++ ) { @@ -201,11 +314,11 @@ void commandLookup_cli() for ( uint8_t cmd = 0; CLIDict[dict][cmd].name != 0; cmd++ ) { // Compare the first argument and each command entry - if ( eqStr( cmdPtr, CLIDict[dict][cmd].name ) ) + if ( eqStr( cmdPtr, (char*)CLIDict[dict][cmd].name ) == -1 ) { // Run the specified command function pointer // argPtr is already pointing at the first character of the arguments - (*CLIDict[dict][cmd].function)( argPtr ); + (*(void (*)(char*))CLIDict[dict][cmd].function)( argPtr ); return; } @@ -214,10 +327,11 @@ void commandLookup_cli() // No match for the command... print( NL ); - erro_dPrint("\"", CLILineBuffer, "\" is not a valid command...try help"); + erro_dPrint("\"", CLILineBuffer, "\" is not a valid command...type \033[35mhelp\033[0m"); } -void registerDictionary_cli( CLIDictItem *cmdDict ) +// Registers a command dictionary with the CLI +void CLI_registerDictionary( const CLIDictItem *cmdDict, const char* dictName ) { // Make sure this max limit of dictionaries hasn't been reached if ( CLIDictionariesUsed >= CLIMaxDictionaries ) @@ -227,24 +341,230 @@ void registerDictionary_cli( CLIDictItem *cmdDict ) } // Add dictionary - CLIDict[CLIDictionariesUsed++] = cmdDict; + CLIDictNames[CLIDictionariesUsed] = (char*)dictName; + CLIDict[CLIDictionariesUsed++] = (CLIDictItem*)cmdDict; +} + +inline void CLI_tabCompletion() +{ + // Ignore command if buffer is 0 length + if ( CLILineBufferCurrent == 0 ) + return; + + // Set the last+1 character of the buffer to NULL for string processing + CLILineBuffer[CLILineBufferCurrent] = '\0'; + + // Retrieve pointers to command and beginning of arguments + // Places a NULL at the first space after the command + char* cmdPtr; + char* argPtr; + CLI_argumentIsolation( CLILineBuffer, &cmdPtr, &argPtr ); + + // Tab match pointer + char* tabMatch = 0; + uint8_t matches = 0; + + // Scan array of dictionaries for a valid command match + for ( uint8_t dict = 0; dict < CLIDictionariesUsed; dict++ ) + { + // Parse each cmd until a null command entry is found, or an argument match + for ( uint8_t cmd = 0; CLIDict[dict][cmd].name != 0; cmd++ ) + { + // Compare the first argument piece to each command entry to see if it is "like" + // NOTE: To save on processing, we only care about the commands and ignore the arguments + // If there are arguments, and a valid tab match is found, buffer is cleared (args lost) + // Also ignores full matches + if ( eqStr( cmdPtr, (char*)CLIDict[dict][cmd].name ) == 0 ) + { + // TODO Make list of commands if multiple matches + matches++; + tabMatch = (char*)CLIDict[dict][cmd].name; + } + } + } + + // Only tab complete if there was 1 match + if ( matches == 1 ) + { + // Reset the buffer + CLILineBufferCurrent = 0; + + // Reprint the prompt (automatically clears the line) + prompt(); + + // Display the command + dPrint( tabMatch ); + + // There are no index counts, so just copy the whole string to the input buffer + while ( *tabMatch != '\0' ) + { + CLILineBuffer[CLILineBufferCurrent++] = *tabMatch++; + } + } +} + +inline int CLI_wrap( int kX, int const kLowerBound, int const kUpperBound ) +{ + int range_size = kUpperBound - kLowerBound + 1; + + if ( kX < kLowerBound ) + kX += range_size * ((kLowerBound - kX) / range_size + 1); + + return kLowerBound + (kX - kLowerBound) % range_size; +} + +inline void CLI_saveHistory( char *buff ) +{ + if ( buff == NULL ) + { + //clear the item + CLIHistoryBuffer[ CLIHistoryTail ][ 0 ] = '\0'; + return; + } + + // Don't write empty lines to the history + const char *cursor = buff; + while (*cursor == ' ') { cursor++; } // advance past the leading whitespace + if (*cursor == '\0') { return ; } + + // Copy the line to the history + int i; + for (i = 0; i < CLILineBufferCurrent; i++) + { + CLIHistoryBuffer[ CLIHistoryTail ][ i ] = CLILineBuffer[ i ]; + } +} + +void CLI_retreiveHistory( int index ) +{ + char *histMatch = CLIHistoryBuffer[ index ]; + + // Reset the buffer + CLILineBufferCurrent = 0; + + // Reprint the prompt (automatically clears the line) + prompt(); + + // Display the command + dPrint( histMatch ); + + // There are no index counts, so just copy the whole string to the input buffe + CLILineBufferCurrent = 0; + while ( *histMatch != '\0' ) + { + CLILineBuffer[ CLILineBufferCurrent++ ] = *histMatch++; + } } // ----- CLI Command Functions ----- +void cliFunc_clear( char* args) +{ + print("\033[2J\033[H\r"); // Erases the whole screen +} + +void cliFunc_cliDebug( char* args ) +{ + // Toggle Hex Debug Mode + if ( CLIHexDebugMode ) + { + print( NL ); + info_print("Hex debug mode disabled..."); + CLIHexDebugMode = 0; + } + else + { + print( NL ); + info_print("Hex debug mode enabled..."); + CLIHexDebugMode = 1; + } +} + void cliFunc_help( char* args ) { - print( NL ); - print("Help!"); - dPrint( args ); + // Scan array of dictionaries and print every description + // (no alphabetical here, too much processing/memory to sort...) + for ( uint8_t dict = 0; dict < CLIDictionariesUsed; dict++ ) + { + // Print the name of each dictionary as a title + print( NL "\033[1;32m" ); + _print( CLIDictNames[dict] ); // This print is requride by AVR (flash) + print( "\033[0m" NL ); + + // Parse each cmd/description until a null command entry is found + for ( uint8_t cmd = 0; CLIDict[dict][cmd].name != 0; cmd++ ) + { + dPrintStrs(" \033[35m", CLIDict[dict][cmd].name, "\033[0m"); + + // Determine number of spaces to tab by the length of the command and TabAlign + uint8_t padLength = CLIEntryTabAlign - lenStr( (char*)CLIDict[dict][cmd].name ); + while ( padLength-- > 0 ) + print(" "); + + _print( CLIDict[dict][cmd].description ); // This print is required by AVR (flash) + print( NL ); + } + } +} + +void cliFunc_led( char* args ) +{ + CLILEDState ^= 1 << 1; // Toggle between 0 and 1 + errorLED( CLILEDState ); // Enable/Disable error LED +} + +void cliFunc_reload( char* args ) +{ + if ( flashModeEnabled_define == 0 ) + { + print( NL ); + warn_print("flashModeEnabled not set, cancelling firmware reload..."); + info_msg("Set flashModeEnabled to 1 in your kll configuration."); + return; + } + + // Request to output module to be set into firmware reload mode + Output_firmwareReload(); +} + +void cliFunc_reset( char* args ) +{ + print("\033c"); // Resets the terminal +} + +void cliFunc_restart( char* args ) +{ + // Trigger an overall software reset + Output_softReset(); } void cliFunc_version( char* args ) { print( NL ); - print("Version!"); - dPrint( args ); + print( " \033[1mRevision:\033[0m " CLI_Revision NL ); + print( " \033[1mBranch:\033[0m " CLI_Branch NL ); + print( " \033[1mTree Status:\033[0m " CLI_ModifiedStatus CLI_ModifiedFiles NL ); + print( " \033[1mRepo Origin:\033[0m " CLI_RepoOrigin NL ); + print( " \033[1mCommit Date:\033[0m " CLI_CommitDate NL ); + print( " \033[1mCommit Author:\033[0m " CLI_CommitAuthor NL ); + print( " \033[1mBuild Date:\033[0m " CLI_BuildDate NL ); + print( " \033[1mBuild OS:\033[0m " CLI_BuildOS NL ); + print( " \033[1mArchitecture:\033[0m " CLI_Arch NL ); + print( " \033[1mChip:\033[0m " CLI_Chip NL ); + print( " \033[1mCPU:\033[0m " CLI_CPU NL ); + print( " \033[1mDevice:\033[0m " CLI_Device NL ); + print( " \033[1mModules:\033[0m " CLI_Modules NL ); +#if defined(_mk20dx128_) || defined(_mk20dx128vlf5_) || defined(_mk20dx256_) || defined(_mk20dx256vlh7_) + print( " \033[1mUnique Id:\033[0m " ); + printHex32_op( SIM_UIDH, 4 ); + printHex32_op( SIM_UIDMH, 4 ); + printHex32_op( SIM_UIDML, 4 ); + printHex32_op( SIM_UIDL, 4 ); +#elif defined(_at90usb162_) || defined(_atmega32u4_) || defined(_at90usb646_) || defined(_at90usb1286_) +#else +#error "No unique id defined." +#endif }