From bc849d9662b99edf04335d19236caa6c08c35247 Mon Sep 17 00:00:00 2001 From: binaryDiv Date: Sun, 4 Apr 2021 02:50:25 +0200 Subject: [PATCH] Refactor protocol/parsing code; implement address parsing --- firmware/src/commands.c | 132 +++++++++++++++++++++++++++++++++++++ firmware/src/commands.h | 18 +++++ firmware/src/main.c | 4 +- firmware/src/parsing.c | 122 ++++++++++++++++++++++++++++++++++ firmware/src/parsing.h | 29 +++++++++ firmware/src/protocol.c | 141 ---------------------------------------- firmware/src/protocol.h | 16 ----- 7 files changed, 303 insertions(+), 159 deletions(-) create mode 100644 firmware/src/commands.c create mode 100644 firmware/src/commands.h create mode 100644 firmware/src/parsing.c create mode 100644 firmware/src/parsing.h delete mode 100644 firmware/src/protocol.c delete mode 100644 firmware/src/protocol.h diff --git a/firmware/src/commands.c b/firmware/src/commands.c new file mode 100644 index 0000000..6f8b27d --- /dev/null +++ b/firmware/src/commands.c @@ -0,0 +1,132 @@ +#include "config.h" +#include "commands.h" +#include "parsing.h" +#include "uart.h" +#include "eeprom.h" + +#include +#include +#include +#include + +// Execute a single parsed command +void executeCommand(CommandLine cmdLine) { + // Parse command + if (strcmp(cmdLine.command, "INIT") == 0) { + // INIT command: Initializes connection. + commandInit(); + } + else if (strcmp(cmdLine.command, "READ") == 0) { + // READ command: Takes a hex address range (or single address) as argument, + // reads data and returns it in hexadecimal ASCII format. + commandRead(cmdLine.arg); + } + else if (strcmp(cmdLine.command, "WRITE") == 0) { + // WRITE command: Takes a hex address as argument, reads data from UART and writes it to the EEPROM. + commandRead(cmdLine.arg); + } + else if (strcmp(cmdLine.command, "TESTREAD") == 0) { + // TESTREAD command: for testing purposes, reads a few bytes and returns them in a human readable format. + commandTestRead(); + } + else if (strcmp(cmdLine.command, "TESTWRITE") == 0) { + // TESTWRITE command: for testing purposes, writes a few bytes + commandTestWrite(cmdLine.arg); + } + else { + // unknown command: return error message + uartPutString("ERR invalid command\r\n"); + } +} + +void commandInit() { + // TODO init... or something? + uartPutString("OK\n"); +} + +void commandRead(char* arg) { + if (arg == NULL) { + uartPutString("ERR READ needs an address or address range\r\n"); + return; + } + + // Parse address(es) + AddressRange range = parseAddressRange(arg); + if (!range.isValid) { + uartPutString("ERR invalid address format\r\n"); + return; + } + + // TODO read data and output it + uartPutString("ERR not implemented\n"); +} + +void commandWrite(char* arg) { + if (arg == NULL) { + uartPutString("ERR WRITE needs a start address\r\n"); + return; + } + + // Parse address + AddressRange range = parseSingleAddress(arg); + if (!range.isValid) { + uartPutString("ERR invalid address format\r\n"); + return; + } + + // TODO read data from input and write to EEPROM + uartPutString("ERR not implemented\n"); +} + +void commandErase() { + // TODO + uartPutString("ERR not implemented\n"); +} + +// TESTREAD command: for testing purposes, reads a few bytes and returns them in a human readable format. +void commandTestRead() { + uint8_t byte; + char outBuffer[20]; + + eepromSetReadMode(); + + for (int i = 0x00; i < 0x20; i++) { + itoa(i, outBuffer, 16); + uartPutString("TESTREAD 0x"); + uartPutString(outBuffer); + uartPutString(": "); + + byte = eepromReadByte(i); + itoa(byte, outBuffer, 16); + + uartPutChar(byte); + uartPutString(" (0x"); + uartPutString(outBuffer); + uartPutString(")\r\n"); + } +} + +// TESTWRITE command: for testing purposes, writes a few bytes +void commandTestWrite(char* arg) { + char str[] = "Ohai world"; + address_t addr = 0x00; + + char* writeStr = str; + if (arg != NULL) { + writeStr = arg; + } + + eepromSetWriteMode(); + + // write input line instead of static string + for (char* p = writeStr; *p != '\0'; p++) { + eepromWriteByte(addr, *p); + //_delay_ms(100); + addr++; + } + + // TODO necessary? + _delay_ms(100); + + uartPutString("TESTWRITE success\r\n"); +} diff --git a/firmware/src/commands.h b/firmware/src/commands.h new file mode 100644 index 0000000..159a8f3 --- /dev/null +++ b/firmware/src/commands.h @@ -0,0 +1,18 @@ +#ifndef COMMANDS_H_ +#define COMMANDS_H_ + +#include "config.h" +#include "parsing.h" + +void executeCommand(CommandLine cmdLine); + +void commandInit(); +void commandRead(char* arg); +void commandWrite(char* arg); +void commandErase(); + +// TODO commands only for testing +void commandTestRead(); +void commandTestWrite(char* arg); + +#endif /* COMMANDS_H_ */ diff --git a/firmware/src/main.c b/firmware/src/main.c index ec59571..cc580f7 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -1,7 +1,7 @@ #include "config.h" #include "uart.h" #include "eeprom.h" -#include "protocol.h" +#include "parsing.h" #include @@ -18,6 +18,6 @@ int main(void) { while(1) { // Run main loop endlessly - protocolMainLoop(); + parseNextCommand(); } } diff --git a/firmware/src/parsing.c b/firmware/src/parsing.c new file mode 100644 index 0000000..96b14a5 --- /dev/null +++ b/firmware/src/parsing.c @@ -0,0 +1,122 @@ +#include "config.h" +#include "parsing.h" +#include "commands.h" +#include "uart.h" + +#include +#include +#include +#include + +// Read and parse command line and dispatch command +void parseNextCommand() { + const int bufferLength = 80; + char buffer[bufferLength]; + + // Read next command + CommandLine cmdLine = readNextCommand(buffer, bufferLength); + + // Execute command + executeCommand(cmdLine); +} + +// Read next command line from UART +CommandLine readNextCommand(char* buffer, uint8_t bufferLength) { + while (1) { + // Read next command + int readChars = uartGetLine(buffer, bufferLength); + + if (readChars >= bufferLength - 1) { + // Reading was aborted after bufferLength-1 characters to prevent buffer overflow. + uartPutString("ERR buffer overflow, discarding line\r\n"); + + // Discard everything until we read an actual end of line + for (unsigned char c = 0; c != '\n' && c != '\r'; c = uartGetChar()); + readChars = 0; + } + + if (readChars != 0) { + return tokenizeCommand(buffer); + } + } +} + +// Splits a command "CMD ARGS" to two strings "CMD" and "ARGS". Returns pointer +// to "ARGS" or NULL if no arguments were found. Changes the input string! +CommandLine tokenizeCommand(char* cmd) { + CommandLine cmdLine = { + cmd, + NULL + }; + + if (cmd == NULL) { + return cmdLine; + } + + // Search for a space character + for (char* p = cmd; *p != '\0'; p++) { + if (*p == ' ') { + // Split strings by replacing the space by \0, then set argument pointer. + *p++ = '\0'; + cmdLine.arg = p; + break; + } + } + return cmdLine; +} + +// Address parsing +bool parseSingleAddressTo(char* addressStr, address_t* address) { + if (addressStr == NULL || strlen(addressStr) != 4 || address == NULL) { + return false; + } + + for (char* p = addressStr; *p != '\0'; p++) { + if ((*p < '0' || *p > '9') && (*p < 'A' || *p > 'F')) { + return false; + } + } + + *address = strtol(addressStr, NULL, 16); + return true; +} + +// Parses a single 2-byte hexadecimal address string "XXXX:YYYY" to an actual address. +AddressRange parseSingleAddress(char* addressStr) { + AddressRange range = {true, 0, 0}; + if (!parseSingleAddressTo(addressStr, &range.from)) { + return (AddressRange) {false, 0, 0}; + } + range.to = range.from; + return range; +} + +// Parses a hexadecimal address range string "XXXX:YYYY" to actual addresses. +// A single address "XXXX" is equivalent to "XXXX:XXXX", with from == to. +AddressRange parseAddressRange(char* addressStr) { + if (addressStr == NULL) { + return (AddressRange) {false, 0, 0}; + } + + int len = strlen(addressStr); + if (len != 4 && len != 9) { + return (AddressRange) {false, 0, 0}; + } + + if (len == 9) { + if (addressStr[4] != ':') { + return (AddressRange) {false, 0, 0}; + } + addressStr[4] = '\0'; + } + + AddressRange range = parseSingleAddress(addressStr); + + if (range.isValid && len == 9) { + if (!parseSingleAddressTo(addressStr + 4, &range.to)) { + return (AddressRange) {false, 0, 0}; + } + } + + return range; +} diff --git a/firmware/src/parsing.h b/firmware/src/parsing.h new file mode 100644 index 0000000..9885dee --- /dev/null +++ b/firmware/src/parsing.h @@ -0,0 +1,29 @@ +#ifndef PARSING_H_ +#define PARSING_H_ + +#include "config.h" +#include "eeprom.h" +#include + +// Type definition for a command line with optional argument (actually just two pointers to strings) +typedef struct { + char* command; + char* arg; +} CommandLine; + +// Type definition for address ranges (from-to) +typedef struct { + bool isValid; + address_t from; + address_t to; +} AddressRange; + +void parseNextCommand(); +CommandLine readNextCommand(char* buffer, uint8_t bufferLength); +CommandLine tokenizeCommand(char* cmd); + +bool parseSingleAddressTo(char* addressStr, address_t* address); +AddressRange parseSingleAddress(char* addressStr); +AddressRange parseAddressRange(char* addressStr); + +#endif /* PARSING_H_ */ diff --git a/firmware/src/protocol.c b/firmware/src/protocol.c deleted file mode 100644 index db054ce..0000000 --- a/firmware/src/protocol.c +++ /dev/null @@ -1,141 +0,0 @@ -#include "config.h" -#include "protocol.h" - -#include "uart.h" -#include "eeprom.h" - -#include -#include -#include - -// Splits a command "CMD ARGS" to two strings "CMD" and "ARGS". Returns pointer -// to "ARGS" or NULL if no arguments were found. Changes the input string! -char* tokenizeCommand(char* cmd) { - if (cmd == NULL) { - return NULL; - } - - // Search for a space character - for (char* p = cmd; *p != '\0'; p++) { - if (*p == ' ') { - // Split strings by replacing the space by \0, then return pointer - // to command arguments - *p++ = '\0'; - return p; - } - } - - // No space character found: command has no arguments, return NULL - return NULL; -} - -// Reads, parses and executes next command -void parseNextCommand() { - const int bufferLength = 80; - char buffer[bufferLength]; - - // Read next command - int readChars = uartGetLine(buffer, bufferLength); - - // Check if line is non-empty and has been read completely - if (readChars == 0) { - return; - } - else if (readChars >= bufferLength-1) { - // Reading was aborted after bufferLength-1 characters to prevent - // buffer overflow. - // TODO Actually this isn't quite correct: if exactly bufferLen-1 - // characters have been read including the \n, this could be - // true as well... test this? - uartPutString("ERROR buffer overflow while reading line\r\n"); - return; - } - - // Tokenize command - char* cmd = buffer; - char* args = tokenizeCommand(cmd); - - // Parse command - if (strcmp(cmd, "HELLO") == 0) { - // HELLO command: initializes connection - uartPutString("OHAI\r\n"); - } - else if (strcmp(cmd, "READ") == 0) { - // READ command: takes a hex address or address range as argument, - // reads data and returns them in hexadecimal ASCII format. - - // Check if arguments exist - if (args == NULL) { - uartPutString("ERROR READ needs an address\r\n"); - return; - } - - // Parse address(es) - // TODO - uartPutString(args); - - // Read... - // TODO - } - else if (strcmp(cmd, "TESTREAD") == 0) { - // TESTREAD command: for testing purposes, reads a few bytes and - // returns them in a human readable format. - - uint8_t byte; - char outBuffer[20]; - - eepromSetReadMode(); - - for (int i = 0x00; i < 0x20; i++) { - itoa(i, outBuffer, 16); - uartPutString("TESTREAD 0x"); - uartPutString(outBuffer); - uartPutString(": "); - - byte = eepromReadByte(i); - itoa(byte, outBuffer, 16); - - uartPutChar(byte); - uartPutString(" (0x"); - uartPutString(outBuffer); - uartPutString(")\r\n"); - } - } - else if (strcmp(cmd, "TESTWRITE") == 0) { - // TESTWRITE command: for testing purposes, writes a few bytes - - char str[] = "Ohai world"; - address_t addr = 0x00; - - char* writeStr = str; - if (args != NULL) { - writeStr = args; - } - - eepromSetWriteMode(); - - // write input line instead of static string - for (char* p = writeStr; *p != '\0'; p++) { - eepromWriteByte(addr, *p); - //_delay_ms(100); - addr++; - } - - // TODO necessary? - _delay_ms(100); - - uartPutString("TESTWRITE success\r\n"); - } - else { - // unknown command: return error message - uartPutString("ERROR invalid command\r\n"); - } -} - -// Runs main loop to parse commands -void protocolMainLoop() { - while(1) { - // Parse next command - parseNextCommand(); - } -} diff --git a/firmware/src/protocol.h b/firmware/src/protocol.h deleted file mode 100644 index 57fc50e..0000000 --- a/firmware/src/protocol.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef PROTOCOL_H_ -#define PROTOCOL_H_ - -#include "config.h" -#include "protocol.h" - -#include "uart.h" -#include "eeprom.h" - -// Reads, parses and executes next command -void parseNextCommand(); - -// Runs main loop to parse commands -void protocolMainLoop(); - -#endif /* PROTOCOL_H_ */