Browse Source

Merge branch 'feature/add_ymodem_receive_functionalities' of shimatta/shellmatta into develop

shimatta 10 months ago
parent
commit
42e4707486
47 changed files with 2540 additions and 403 deletions
  1. 3 0
      README.md
  2. 103 10
      api/shellmatta.h
  3. 30 5
      cfg/cppcheck/cppcheck_suppressions.xml
  4. 575 271
      cfg/doxygen/doxyfile
  5. 2 0
      doc/shellmatta.dox
  6. 0 5
      doc/shellmatta_transport_layer.dox
  7. 102 0
      doc/shellmatta_ymodem.dox
  8. 86 4
      example/main.c
  9. 11 5
      makefile
  10. 22 8
      src/shellmatta.c
  11. 12 12
      src/shellmatta_auth.c
  12. 111 25
      src/shellmatta_crc.c
  13. 4 2
      src/shellmatta_crc.h
  14. 13 3
      src/shellmatta_transport.c
  15. 8 1
      src/shellmatta_transport.h
  16. 76 8
      src/shellmatta_utils.c
  17. 36 19
      src/shellmatta_utils.h
  18. 531 0
      src/shellmatta_ymodem.c
  19. 33 0
      src/shellmatta_ymodem.h
  20. 2 2
      test/integrationtest/test_integration_help.cpp
  21. 3 3
      test/integrationtest/test_integration_history.cpp
  22. 226 0
      test/integrationtest/test_integration_ymodem.cpp
  23. 14 3
      test/unittest/shellmatta/test_shellmatta_doInit.cpp
  24. 14 2
      test/unittest/shellmatta_autocomplete/test_autocomplete_run.cpp
  25. 35 0
      test/unittest/shellmatta_crc/test_crc16Fast.cpp
  26. 35 0
      test/unittest/shellmatta_crc/test_crc16Slow.cpp
  27. 17 2
      test/unittest/shellmatta_crc/test_crc32Fast.cpp
  28. 17 2
      test/unittest/shellmatta_crc/test_crc32Slow.cpp
  29. 18 3
      test/unittest/shellmatta_crc/test_crc32_data.h
  30. 15 1
      test/unittest/shellmatta_escape/test_escape_processArrowKeys.cpp
  31. 16 4
      test/unittest/shellmatta_history/test_appendHistoryByte.cpp
  32. 14 0
      test/unittest/shellmatta_opt/test_opt_findNextHunk.cpp
  33. 14 0
      test/unittest/shellmatta_opt/test_opt_peekNextHunk.cpp
  34. 14 0
      test/unittest/shellmatta_utils/test_utils_clearInput.cpp
  35. 14 0
      test/unittest/shellmatta_utils/test_utils_eraseLine.cpp
  36. 14 2
      test/unittest/shellmatta_utils/test_utils_forwardCursor.cpp
  37. 14 0
      test/unittest/shellmatta_utils/test_utils_insertChars.cpp
  38. 14 0
      test/unittest/shellmatta_utils/test_utils_removeChars.cpp
  39. 14 0
      test/unittest/shellmatta_utils/test_utils_restoreCursorPos.cpp
  40. 14 0
      test/unittest/shellmatta_utils/test_utils_rewindCursor.cpp
  41. 14 0
      test/unittest/shellmatta_utils/test_utils_saveCursorPos.cpp
  42. 170 0
      test/unittest/shellmatta_utils/test_utils_shellAsciiToUInt32.cpp
  43. 14 0
      test/unittest/shellmatta_utils/test_utils_shellItoa.cpp
  44. 14 0
      test/unittest/shellmatta_utils/test_utils_terminateInput.cpp
  45. 14 0
      test/unittest/shellmatta_utils/test_utils_writeEcho.cpp
  46. 18 0
      test/unittest/shellmatta_ymodem/test_ymodem.cpp
  47. 0 1
      test/unittest/test_main.cpp

+ 3 - 0
README.md

@@ -149,6 +149,9 @@ There are some defines you can use to change the behaviour of the shellmatta:
 | SHELLMATTA_HELP_HELP_TEXT              | string to overwrite the help command help      |
 | SHELLMATTA_HELP_HELP_TEXT              | string to overwrite the help command help      |
 | SHELLMATTA_HELP_USAGE_TEXT             | string to overwrite the help command usage     |
 | SHELLMATTA_HELP_USAGE_TEXT             | string to overwrite the help command usage     |
 | SHELLMATTA_AUTHENTICATION              | if defined this enables the authentication     |
 | SHELLMATTA_AUTHENTICATION              | if defined this enables the authentication     |
+| SHELLMATTA_TRANSPORT                   | if defined this enables the transport layer    |
+| SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP     | disables lookup for transport layer crc32      |
+| SHELLMATTA_YMODEM_CRC_NO_LOOKUP        | disables lookup for ymodem crc16               |
 
 
 ## Example
 ## Example
 
 

+ 103 - 10
api/shellmatta.h

@@ -26,7 +26,7 @@
 /* global defines */
 /* global defines */
 
 
 
 
-/*
+/**
  * Define the printf format specifier for all GCC versions > 3.3
  * Define the printf format specifier for all GCC versions > 3.3
  * This will let the compiler know that shellmatta_printf() is a function taking printf-like format specifiers.
  * This will let the compiler know that shellmatta_printf() is a function taking printf-like format specifiers.
  */
  */
@@ -192,6 +192,85 @@ typedef struct shellmatta_cmd
     struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
     struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
 } shellmatta_cmd_t;
 } shellmatta_cmd_t;
 
 
+/**
+ * @brief shellmatta ymodem cancel callback definition
+ * @param[in]   handle      pointer to the instance which is calling the callback
+ */
+typedef void (*shellmatta_ymodem_cancel_t)(const shellmatta_handle_t handle);
+
+/**
+ * @brief shellmatta ymodem header receive callback definition
+ * @param[in]   handle      pointer to the instance which is calling the callback
+ * @param[in]   fileSize    file size of the file to be received
+ * @param[in]   fileName    file name of the file to be received
+ */
+typedef void (*shellmatta_ymodem_recvHeader_t)(const shellmatta_handle_t    handle,
+                                               uint32_t                     fileSize,
+                                               char*                        fileName);
+
+/**
+ * @brief shellmatta ymodem packet receive callback definition
+ * @param[in]   handle      pointer to the instance which is calling the callback
+ * @param[in]   data        received data
+ * @param[in]   packetSize  size of the data in the packet
+ * @param[in]   packetNum   number of the received packet
+ */
+typedef void (*shellmatta_ymodem_recvPacket_t)(const shellmatta_handle_t    handle,
+                                               uint8_t                      *data,
+                                               uint32_t                     packetSize,
+                                               uint32_t                     packetNum);
+
+/**
+ * @brief shellmatta ymodem transmission complete callback definition
+ * @param[in]   handle      pointer to the instance which is calling the callback
+ * @param[in]   result      #SHELLMATTA_OK
+ *                          #SHELLMATTA_ERROR - missing data
+ */
+typedef void (*shellmatta_ymodem_complete_t)(const shellmatta_handle_t      handle,
+                                             shellmatta_retCode_t           result);
+
+/**
+ * @brief state enumeration for ymodem receive state machine
+ */
+typedef enum
+{
+    SHELLMATTA_YMODEM_INACTIVE,         /**< YModem module not initialised  */
+    SHELLMATTA_YMODEM_WAIT_FOR_START,   /**< waiting for start of header    */
+    SHELLMATTA_YMODEM_RECEIVE_HEADER,   /**< reading header data            */
+    SHELLMATTA_YMODEM_RECEIVE_DATA,     /**< reading payload                */
+    SHELLMATTA_YMODEM_RECEIVE_CRC,      /**< reading crc                    */
+    SHELLMATTA_YMODEM_WAIT_FOR_END      /**< wait until EOTs stop           */
+} shellmatta_ymodem_state_t;
+
+/** @brief packet structure that holds several information about its content */
+typedef struct
+{
+    uint16_t size;          /**< size of the packet (128 or 1024)     */
+    uint8_t packetNumber;   /**< packet number in this packet         */
+    uint8_t* packetData;    /**< pointer to the data of this packet   */
+    uint16_t crc;           /**< crc checksum in this packet          */
+} shellmatta_ymodem_packet_t;
+
+/**
+ * @brief definition of shellmatta ymodem instance
+ */
+typedef struct
+{
+    shellmatta_ymodem_state_t state;                            /**< current state of the ymodem module             */
+    uint32_t byteCounter;                                       /**< internal counter for processing input data     */
+    uint32_t packetCounter;                                     /**< counter of received packets                    */
+    uint32_t totalBytesReceived;                                /**< counter of received bytes                      */
+    uint32_t fileSize;                                          /**< size of the file received in packet 0          */
+    bool pauseRequested;                                        /**< pause requested from the application           */
+    uint32_t pollCyclesLeft;                                    /**< number of poll cycles left before finish       */
+    uint32_t cancelCounter;                                     /**< counter to count the amount of cancels         */
+    shellmatta_ymodem_packet_t packet;                          /**< currently processed packet                     */
+    shellmatta_ymodem_cancel_t cancelCallback;                  /**< callback to pass cancel events                 */
+    shellmatta_ymodem_recvHeader_t recvHeaderCallback;          /**< callback to pass received header data          */
+    shellmatta_ymodem_recvPacket_t recvPacketCallback;          /**< callback to pass received data                 */
+    shellmatta_ymodem_complete_t transmissionCompleteCallback;  /**< callback to be called on a complete transfer   */
+} shellmatta_ymodem_t;
+
 #ifdef SHELLMATTA_TRANSPORT
 #ifdef SHELLMATTA_TRANSPORT
 
 
 /**
 /**
@@ -224,14 +303,14 @@ typedef uint32_t (*shellmatta_transport_crc_t)(const char* data, const uint32_t
  */
  */
 typedef struct __attribute__((__packed__))
 typedef struct __attribute__((__packed__))
 {
 {
-    uint8_t startOfHeader;      /** start of header field               */
-    uint8_t protocolVersion;    /** protocol version of the packet      */
-    uint8_t packetType;         /** type of the packet                  */
-    uint8_t payloadLength;      /** length of the payload               */
-    uint8_t source;             /** source of the packet                */
-    uint8_t destination;        /** destination of the packet           */
-    uint8_t sequenceH2S;        /** sequence counter host to shellmatta */
-    uint8_t sequenceS2H;        /** sequence counter shellmatta to host */
+    uint8_t startOfHeader;      /**< start of header field                  */
+    uint8_t protocolVersion;    /**< protocol version of the packet         */
+    uint8_t packetType;         /**< type of the packet                     */
+    uint8_t payloadLength;      /**< length of the payload                  */
+    uint8_t source;             /**< source of the packet                   */
+    uint8_t destination;        /**< destination of the packet              */
+    uint8_t sequenceH2S;        /**< sequence counter host to shellmatta    */
+    uint8_t sequenceS2H;        /**< sequence counter shellmatta to host    */
 } shellmatta_transport_header_t;
 } shellmatta_transport_header_t;
 
 
 /**
 /**
@@ -253,6 +332,7 @@ typedef struct
     bool                            active;             /**< is transport layer communication active        */
     bool                            active;             /**< is transport layer communication active        */
     bool                            disableAutoFlush;   /**< enforce manual flushing                        */
     bool                            disableAutoFlush;   /**< enforce manual flushing                        */
     bool                            mandatory;          /**< is the transport layer enforced                */
     bool                            mandatory;          /**< is the transport layer enforced                */
+    bool                            suspendOptional;    /**< forces the transport layer to not run optional */
     uint8_t                         sequenceH2S;        /**< sequence counter host to shellmatta            */
     uint8_t                         sequenceH2S;        /**< sequence counter host to shellmatta            */
     uint8_t                         sequenceS2H;        /**< sequenc counter shellmatta to host             */
     uint8_t                         sequenceS2H;        /**< sequenc counter shellmatta to host             */
     uint32_t                        headerIndex;        /**< read index of the header                       */
     uint32_t                        headerIndex;        /**< read index of the header                       */
@@ -308,6 +388,7 @@ typedef struct
     bool                            cmdListIsConst;     /**< true if the #cmdList was passed during
     bool                            cmdListIsConst;     /**< true if the #cmdList was passed during
                                                              initialization                         */
                                                              initialization                         */
     shellmatta_opt_t                optionParser;       /**< option parser sructure                 */
     shellmatta_opt_t                optionParser;       /**< option parser sructure                 */
+    shellmatta_ymodem_t             ymodem;             /**< ymodem instance                        */
 #ifdef SHELLMATTA_AUTHENTICATION
 #ifdef SHELLMATTA_AUTHENTICATION
     shellmatta_auth_state_t         loginState;         /**< state variable of the login cmd        */
     shellmatta_auth_state_t         loginState;         /**< state variable of the login cmd        */
     shellmatta_cmd_t                loginCmd;           /**< login command structure                */
     shellmatta_cmd_t                loginCmd;           /**< login command structure                */
@@ -323,7 +404,7 @@ typedef struct
     shellmatta_auth_log_t           logFct;             /**< auth log function                      */
     shellmatta_auth_log_t           logFct;             /**< auth log function                      */
 #endif
 #endif
 #ifdef SHELLMATTA_TRANSPORT
 #ifdef SHELLMATTA_TRANSPORT
-    uint32_t                        transportBusyMark;  /**< transport processing position during 
+    uint32_t                        transportBusyMark;  /**< transport processing position during
                                                              busy cmd execution                     */
                                                              busy cmd execution                     */
     shellmatta_transport_layer_t    transportLayer;     /**< transport layer instance               */
     shellmatta_transport_layer_t    transportLayer;     /**< transport layer instance               */
 #endif
 #endif
@@ -432,6 +513,18 @@ shellmatta_retCode_t shellmatta_auth_chpasswd(              shellmatta_handle_t
 
 
 #endif
 #endif
 
 
+shellmatta_retCode_t shellmatta_ymodem_init(    shellmatta_handle_t                 handle,
+                                                uint8_t*                            recvBuffer,
+                                                shellmatta_ymodem_cancel_t          cancelCallback,
+                                                shellmatta_ymodem_recvHeader_t      recvHeaderCallback,
+                                                shellmatta_ymodem_recvPacket_t      recvPacketCallback,
+                                                shellmatta_ymodem_complete_t        transmissionCompleteCallback);
+
+shellmatta_retCode_t shellmatta_ymodem_pause(   shellmatta_handle_t                 handle);
+shellmatta_retCode_t shellmatta_ymodem_resume(  shellmatta_handle_t                 handle);
+shellmatta_retCode_t shellmatta_ymodem_cancel(  shellmatta_handle_t                 handle,
+                                                bool                                doCancel);
+
 #endif
 #endif
 
 
 /** @} */
 /** @} */

+ 30 - 5
cfg/cppcheck/cppcheck_suppressions.xml

@@ -19,11 +19,6 @@
         <fileName>src/shellmatta.c</fileName>
         <fileName>src/shellmatta.c</fileName>
         <symbolName>shellmatta_resetShell</symbolName>
         <symbolName>shellmatta_resetShell</symbolName>
     </suppress>
     </suppress>
-    <suppress>
-        <id>unusedFunction</id>
-        <fileName>src/shellmatta.c</fileName>
-        <symbolName>shellmatta_addCmd</symbolName>
-    </suppress>
     <suppress>
     <suppress>
         <id>unusedFunction</id>
         <id>unusedFunction</id>
         <fileName>src/shellmatta.c</fileName>
         <fileName>src/shellmatta.c</fileName>
@@ -44,6 +39,16 @@
         <fileName>src/shellmatta.c</fileName>
         <fileName>src/shellmatta.c</fileName>
         <symbolName>shellmatta_printf</symbolName>
         <symbolName>shellmatta_printf</symbolName>
     </suppress>
     </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta.c</fileName>
+        <symbolName>shellmatta_write</symbolName>
+    </suppress>
+    <suppress>
+        <id>constVariablePointer</id>
+        <fileName>src/shellmatta.c</fileName>
+        <lineNumber>780</lineNumber>
+    </suppress>
     <suppress>
     <suppress>
         <id>unusedFunction</id>
         <id>unusedFunction</id>
         <fileName>src/shellmatta_opt.c</fileName>
         <fileName>src/shellmatta_opt.c</fileName>
@@ -69,4 +74,24 @@
         <fileName>src/shellmatta_auth.c</fileName>
         <fileName>src/shellmatta_auth.c</fileName>
         <symbolName>shellmatta_auth_chpasswd</symbolName>
         <symbolName>shellmatta_auth_chpasswd</symbolName>
     </suppress>
     </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta_ymodem.c</fileName>
+        <symbolName>shellmatta_ymodem_init</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta_ymodem.c</fileName>
+        <symbolName>shellmatta_ymodem_pause</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta_ymodem.c</fileName>
+        <symbolName>shellmatta_ymodem_resume</symbolName>
+    </suppress>
+    <suppress>
+        <id>unusedFunction</id>
+        <fileName>src/shellmatta_ymodem.c</fileName>
+        <symbolName>shellmatta_ymodem_cancel</symbolName>
+    </suppress>
 </suppressions>
 </suppressions>

File diff suppressed because it is too large
+ 575 - 271
cfg/doxygen/doxyfile


+ 2 - 0
doc/shellmatta.dox

@@ -45,4 +45,6 @@
 
 
     @subpage shellmatta_transport_layer
     @subpage shellmatta_transport_layer
 
 
+    @subpage shellmatta_ymodem
+
 */
 */

+ 0 - 5
doc/shellmatta_transport_layer.dox

@@ -149,9 +149,4 @@
     To enable the lookup table less CRC just define
     To enable the lookup table less CRC just define
     ``SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP`` during compilation.
     ``SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP`` during compilation.
 
 
-
-    @section shellmatta_transport_layer_control Controlling the transport layer
-
-    @todo Not implemented yet
-
 */
 */

+ 102 - 0
doc/shellmatta_ymodem.dox

@@ -0,0 +1,102 @@
+/**
+
+    @page shellmatta_ymodem Shellmatta ymodem
+
+    To be able to pass binary files to the shellmatta there is a ymodem module
+    implemented.
+    The ymodem reception can be started from a shellmatta command and handles
+    the handshaking as well as the data transmission.
+
+    As the shellmatta does not use dynamic memory every packet is passed
+    directly to the application using a callback.
+    Only the currently received packet is stored in the shellmatta internally.
+
+    In order to transmit the start characters to the sender you need to poll the
+    shellmatta_processData() function cyclically even if no new data has been
+    received.
+    Do this in 0.1s - 1s periods (depending on the latency you are after).
+
+    When the transmission is complete the shellmatta requires to be polled
+    #YMODEM_POLL_NUMBER times. This ensures a short wait time to flush all
+    unwanted bytes from the transmitter.
+
+    The shellmatta_ymodem_init() function will return and the command it was
+    called from will not be executed again. The rest of the ymodem processing
+    will be handled by the shellmatta. The data will be passed using the
+    callbacks registered during the start.
+
+    To cancel the ymodem (e.g. timeout) just call shellmatta_ymodem_cancel()
+
+
+    @section shellmatta_ymodem_sequence Basic sequence
+
+    @startuml
+        Application -> Shellmatta: shellmatta_ymodem_init()
+        Shellmatta -> Host: send C to start transmission
+        Shellmatta -> Application: return
+        loop until ymodem transmission starts (or is canceled)
+            Application -> Shellmatta: shellmatta_processData(null)
+            Shellmatta -> Host: send C to start transmission
+        end
+        Application -> Shellmatta: shellmatta_processData(first packet)
+        Shellmatta -> Application: packetHeader(fileSize, FileName) callback
+        loop until shellmatta transmission is complete
+            Host -> Shellmatta: Data transmission
+            Shellmatta -> Application: packetRcv() callback
+        end
+        Shellmatta -> Application: transmissionComplete() callback
+    @enduml
+
+    While the ymodem is active no command can be executed.
+    When the ymodem is canceled from the Host the data received afterwards will
+    be processed as usual.
+
+    @warning    Ymodem cannot cooperate with optional transport layer.
+                When the transport layer is enabled but not mandatory it is
+                forced to stay in the mode it was when the ymodem transfer
+                starts.
+                When the transport layer was active it has to keep active.
+                If it is inactive it cannot be activated during the ymodem
+                transfer.
+
+
+    @section shellmatta_ymodem_internal_buffer Internal buffer
+
+    To save some memory you can use the shellmatta ymodem module using the
+    internal buffer which usually is used to cache normal input before
+    processing it.
+
+    Just set the recvBuffer to NULL in #shellmatta_ymodem_init calls.
+
+
+    @warning    Ensure that your shellmattas buffer is at least
+                #YMODEM_PACKET_SIZE_1K large to fit the ymodem packet.
+                This is checked during initialization.
+
+
+    @section shellmatta_ymodem_paused_mode Paused mode
+
+    When the application requires time to process the currently received packet
+    the application can call #shellmatta_ymodem_pause from the callbacks.
+
+    This will prevent the shellmatta from sending the ACK to the sender.
+
+    When the processing is done just call #shellmatta_ymodem_resume.
+
+    @section shellmatta_ymodem_crc CRC calculation
+
+    The shellmatta ymodem uses the CRC16 algorithm to secure the
+    transmission (0x1021).
+
+    By default the shellmatta crc implementation uses a lookup table to
+    calculate the crc in a performant way.
+
+    This uses quite a lot of read only memory on the device. If the memory
+    consumption is an Issue you can switch to a slower implementation without
+    the lookup table. This will increase CPU load quite a bit, but saves nearly
+    0.5K of read only memory.
+
+    To enable the lookup table less CRC just define
+    ``SHELLMATTA_YMODEM_CRC_NO_LOOKUP`` during compilation.
+
+*/

+ 86 - 4
example/main.c

@@ -95,7 +95,7 @@ static shellmatta_retCode_t reset(shellmatta_handle_t handle, const char *argume
     uint32_t argLen;
     uint32_t argLen;
     bool printPrompt = false;
     bool printPrompt = false;
 
 
-    static const shellmatta_opt_long_t options[] = 
+    static const shellmatta_opt_long_t options[] =
     {
     {
         {"prompt",  'p',    SHELLMATTA_OPT_ARG_REQUIRED},
         {"prompt",  'p',    SHELLMATTA_OPT_ARG_REQUIRED},
         {NULL,      '\0',   SHELLMATTA_OPT_ARG_NONE}
         {NULL,      '\0',   SHELLMATTA_OPT_ARG_NONE}
@@ -175,6 +175,75 @@ static shellmatta_retCode_t busy(shellmatta_handle_t handle, const char *argumen
 }
 }
 shellmatta_cmd_t busyCommand = {"busy", NULL, NULL, NULL, busy, NULL, NULL};
 shellmatta_cmd_t busyCommand = {"busy", NULL, NULL, NULL, busy, NULL, NULL};
 
 
+shellmatta_retCode_t ret = SHELLMATTA_OK;
+
+void ymodemCallbackCancel(shellmatta_handle_t handle) {
+
+    (void)handle;
+    printf("Received cancel callback\n");
+
+    return;
+}
+
+void ymodemCallbackReceiveHeader(shellmatta_handle_t handle, uint32_t fileSize, char* fileName) {
+
+    (void)handle;
+    printf("Received Packet Header: %u %s\n", fileSize, fileName);
+
+    return;
+}
+
+void ymodemCallbackReceivePacket(shellmatta_handle_t handle, uint8_t *data, uint32_t packetSize, uint32_t packetNum)
+{
+
+    uint32_t i;
+
+    (void)handle;
+
+    printf("Received Packet: %u %u\n", packetNum, packetSize);
+
+    for(i = 0u; i < packetSize; i ++)
+    {
+        printf("0x%02x ", data[i]);
+
+        if(15u == (i % 16))
+        {
+            printf("\n");
+        }
+    }
+    printf("\n");
+
+    return;
+}
+
+void ymodemCallbackTransmissionComplete(shellmatta_handle_t handle, shellmatta_retCode_t result) {
+
+    (void)handle;
+
+    ret = SHELLMATTA_OK;
+    printf("\nTransmission complete: %s\n", result == SHELLMATTA_OK ? "success" : "error");
+    return;
+}
+
+
+uint8_t ymodemBuffer[1024u] = {0};
+
+static shellmatta_retCode_t ymodem(shellmatta_handle_t handle, const char *arguments, uint32_t length)
+{
+    (void)arguments;
+    (void)length;
+    shellmatta_printf(handle, "Starting ymodem session\r\n");
+    shellmatta_ymodem_init(handle,
+                      ymodemBuffer,
+                      ymodemCallbackCancel,
+                      ymodemCallbackReceiveHeader,
+                      ymodemCallbackReceivePacket,
+                      ymodemCallbackTransmissionComplete);
+
+    return SHELLMATTA_OK;
+}
+shellmatta_cmd_t ymodemCommand = {"ymodem", NULL, NULL, NULL, ymodem, NULL, NULL};
+
 
 
 shellmatta_retCode_t writeFct(const char* data, uint32_t length)
 shellmatta_retCode_t writeFct(const char* data, uint32_t length)
 {
 {
@@ -203,6 +272,8 @@ int main(int argc, char **argv)
         return f;
         return f;
     }
     }
 
 
+    printf("Starting Shellmatta\n");
+
     shellmatta_doInit(  &instance,
     shellmatta_doInit(  &instance,
                         &handle,
                         &handle,
                         buffer,
                         buffer,
@@ -220,6 +291,7 @@ int main(int argc, char **argv)
     shellmatta_addCmd(handle, &resetCommand);
     shellmatta_addCmd(handle, &resetCommand);
     shellmatta_addCmd(handle, &continuousCommand);
     shellmatta_addCmd(handle, &continuousCommand);
     shellmatta_addCmd(handle, &busyCommand);
     shellmatta_addCmd(handle, &busyCommand);
+    shellmatta_addCmd(handle, &ymodemCommand);
 
 
 
 
     shellmatta_auth_user_t userList[] = {
     shellmatta_auth_user_t userList[] = {
@@ -236,18 +308,28 @@ int main(int argc, char **argv)
 
 
     shellmatta_auth_init(handle, userList, 2, permList, 2, false, NULL, NULL);
     shellmatta_auth_init(handle, userList, 2, permList, 2, false, NULL, NULL);
 
 
+    int i = 0;
     while(exitRequest == false)
     while(exitRequest == false)
     {
     {
         char c;
         char c;
         shellmatta_retCode_t ret;
         shellmatta_retCode_t ret;
+        int flags = fcntl(f, F_GETFL, 0);
+        fcntl(f, F_SETFL, flags | O_NONBLOCK);
         int res = 0;
         int res = 0;
         res = read (f, &c, 1);
         res = read (f, &c, 1);
-
-        fprintf(stdout, "0x%02x \n", c);
-        fflush(stdout);
+        if (res == -1)
+        {
+            i = (i + 1) % 10000;
+            usleep(1);
+            if (i != 0)
+            {
+                continue;
+            }
+        }
 
 
         do
         do
         {
         {
+            res = res > 0 ? res : 0;
             ret = shellmatta_processData(handle, &c, res);
             ret = shellmatta_processData(handle, &c, res);
             if(SHELLMATTA_BUSY == ret)
             if(SHELLMATTA_BUSY == ret)
             {
             {

+ 11 - 5
makefile

@@ -31,12 +31,13 @@ SOURCES :=  src/shellmatta.c                \
             src/shellmatta_history.c        \
             src/shellmatta_history.c        \
             src/shellmatta_utils.c          \
             src/shellmatta_utils.c          \
             src/shellmatta_escape.c         \
             src/shellmatta_escape.c         \
-            src/shellmatta_opt.c
+            src/shellmatta_opt.c            \
+            src/shellmatta_ymodem.c         \
+            src/shellmatta_crc.c
 
 
 
 
 SOURCES_TRANPORT_LAYER  :=  $(SOURCES)                      \
 SOURCES_TRANPORT_LAYER  :=  $(SOURCES)                      \
-                            src/shellmatta_transport.c      \
-                            src/shellmatta_crc.c
+                            src/shellmatta_transport.c
 AUTH_SOURCES     := $(SOURCES) src/shellmatta_auth.c
 AUTH_SOURCES     := $(SOURCES) src/shellmatta_auth.c
 EXAMPLE_SOURCES  := example/main.c
 EXAMPLE_SOURCES  := example/main.c
 EXAMPLE_SOURCES  += $(filter-out $(EXAMPLE_SOURCES),$(AUTH_SOURCES))
 EXAMPLE_SOURCES  += $(filter-out $(EXAMPLE_SOURCES),$(AUTH_SOURCES))
@@ -62,6 +63,7 @@ UNITTEST_SOURCES := test/unittest/test_main.cpp
                     test/unittest/shellmatta_opt/test_opt_peekNextHunk.cpp              \
                     test/unittest/shellmatta_opt/test_opt_peekNextHunk.cpp              \
                     test/unittest/shellmatta_utils/test_utils_writeEcho.cpp             \
                     test/unittest/shellmatta_utils/test_utils_writeEcho.cpp             \
                     test/unittest/shellmatta_utils/test_utils_shellItoa.cpp             \
                     test/unittest/shellmatta_utils/test_utils_shellItoa.cpp             \
+                    test/unittest/shellmatta_utils/test_utils_shellAsciiToUInt32.cpp    \
                     test/unittest/shellmatta_utils/test_utils_saveCursorPos.cpp         \
                     test/unittest/shellmatta_utils/test_utils_saveCursorPos.cpp         \
                     test/unittest/shellmatta_utils/test_utils_restoreCursorPos.cpp      \
                     test/unittest/shellmatta_utils/test_utils_restoreCursorPos.cpp      \
                     test/unittest/shellmatta_utils/test_utils_eraseLine.cpp             \
                     test/unittest/shellmatta_utils/test_utils_eraseLine.cpp             \
@@ -76,7 +78,10 @@ UNITTEST_SOURCES := test/unittest/test_main.cpp
                     test/unittest/shellmatta_history/test_appendHistoryByte.cpp         \
                     test/unittest/shellmatta_history/test_appendHistoryByte.cpp         \
                     test/unittest/shellmatta/test_shellmatta_doInit.cpp                 \
                     test/unittest/shellmatta/test_shellmatta_doInit.cpp                 \
                     test/unittest/shellmatta_crc/test_crc32Slow.cpp                     \
                     test/unittest/shellmatta_crc/test_crc32Slow.cpp                     \
-                    test/unittest/shellmatta_crc/test_crc32Fast.cpp
+                    test/unittest/shellmatta_crc/test_crc32Fast.cpp                     \
+                    test/unittest/shellmatta_crc/test_crc16Slow.cpp                     \
+                    test/unittest/shellmatta_crc/test_crc16Fast.cpp                     \
+                    test/unittest/shellmatta_ymodem/test_ymodem.cpp
 
 
 INTEGRATIONTEST_SOURCES :=  test/integrationtest/test_main.cpp                  \
 INTEGRATIONTEST_SOURCES :=  test/integrationtest/test_main.cpp                  \
                             test/integrationtest/test_integration.cpp           \
                             test/integrationtest/test_integration.cpp           \
@@ -85,7 +90,8 @@ INTEGRATIONTEST_SOURCES :=  test/integrationtest/test_main.cpp
                             test/integrationtest/test_integration_continue.cpp  \
                             test/integrationtest/test_integration_continue.cpp  \
                             test/integrationtest/test_integration_busy.cpp      \
                             test/integrationtest/test_integration_busy.cpp      \
                             test/integrationtest/test_integration_history.cpp   \
                             test/integrationtest/test_integration_history.cpp   \
-                            test/integrationtest/test_integration_help.cpp
+                            test/integrationtest/test_integration_help.cpp      \
+                            test/integrationtest/test_integration_ymodem.cpp
 
 
 INTEGRATIONTEST_TRANSPORT_SOURCES  :=  $(INTEGRATIONTEST_SOURCES)                           \
 INTEGRATIONTEST_TRANSPORT_SOURCES  :=  $(INTEGRATIONTEST_SOURCES)                           \
                                        test/integrationtest_transport/test_integration_transport.cpp
                                        test/integrationtest_transport/test_integration_transport.cpp

+ 22 - 8
src/shellmatta.c

@@ -23,6 +23,7 @@
 #include "shellmatta_utils.h"
 #include "shellmatta_utils.h"
 #include "shellmatta_escape.h"
 #include "shellmatta_escape.h"
 #include "shellmatta_opt.h"
 #include "shellmatta_opt.h"
+#include "shellmatta_ymodem.h"
 #ifdef SHELLMATTA_TRANSPORT
 #ifdef SHELLMATTA_TRANSPORT
 #include "shellmatta_transport.h"
 #include "shellmatta_transport.h"
 #endif
 #endif
@@ -47,14 +48,14 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
                                                       char                    *data,
                                                       char                    *data,
                                                       uint32_t                 size)
                                                       uint32_t                 size)
 {
 {
-    shellmatta_cmd_t        *cmd;
-    uint8_t                 cmdExecuted = 0u;
-    uint32_t                cmdLen;
-    char                    *tempString;
+    shellmatta_cmd_t            *cmd;
+    uint8_t                     cmdExecuted = 0u;
+    uint32_t                    cmdLen;
+    char                        *tempString;
 
 
-    shellmatta_retCode_t    ret = SHELLMATTA_OK;
-    shellmatta_retCode_t    cmdRet;
-    shellmatta_instance_t   *inst = (shellmatta_instance_t*)handle;
+    shellmatta_retCode_t        ret = SHELLMATTA_OK;
+    shellmatta_retCode_t        cmdRet;
+    shellmatta_instance_t       *inst = (shellmatta_instance_t*)handle;
 
 
     /** -# in busy mode - keep calling this command */
     /** -# in busy mode - keep calling this command */
     if(NULL != inst->busyCmd)
     if(NULL != inst->busyCmd)
@@ -77,6 +78,11 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
             utils_terminateInput(inst);
             utils_terminateInput(inst);
         }
         }
     }
     }
+    /** -# poll shellmatta ymodem to send out the request to the sender */
+    if((0u == size) && (SHELLMATTA_YMODEM_INACTIVE != inst->ymodem.state))
+    {
+        (void)shellmatta_ymodem_poll(handle);
+    }
     /** -# call continuous function even if there is no data */
     /** -# call continuous function even if there is no data */
     else if((0u == size) && (NULL != inst->continuousCmd))
     else if((0u == size) && (NULL != inst->continuousCmd))
     {
     {
@@ -108,8 +114,14 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
     /** -# process byte wise */
     /** -# process byte wise */
     for (; (inst->byteCounter < size) && (NULL == inst->busyCmd); inst->byteCounter++)
     for (; (inst->byteCounter < size) && (NULL == inst->busyCmd); inst->byteCounter++)
     {
     {
+
+        /** -# handle ymodem when a session is active */
+        if (inst->ymodem.state != SHELLMATTA_YMODEM_INACTIVE)
+        {
+            ret = shellmatta_ymodem_processByte(handle, data[inst->byteCounter]);
+        }
         /** -# in continuous mode - pass data directly to the command */
         /** -# in continuous mode - pass data directly to the command */
-        if(NULL != inst->continuousCmd)
+        else if(NULL != inst->continuousCmd)
         {
         {
             /** -# copy data and call command function */
             /** -# copy data and call command function */
             inst->buffer[inst->stdinIdx]        = data[inst->byteCounter];
             inst->buffer[inst->stdinIdx]        = data[inst->byteCounter];
@@ -128,6 +140,7 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
             {
             {
                 /** -# cancel continue session */
                 /** -# cancel continue session */
                 utils_terminateInput(inst);
                 utils_terminateInput(inst);
+
                 ret = SHELLMATTA_OK;
                 ret = SHELLMATTA_OK;
             }
             }
             else if(SHELLMATTA_CONTINUE == ret)
             else if(SHELLMATTA_CONTINUE == ret)
@@ -531,6 +544,7 @@ shellmatta_retCode_t shellmatta_resetShell(shellmatta_handle_t handle, bool prin
         inst->hereStartIdx          = 0u;
         inst->hereStartIdx          = 0u;
         inst->hereDelimiterIdx      = 0u;
         inst->hereDelimiterIdx      = 0u;
         inst->hereLength            = 0u;
         inst->hereLength            = 0u;
+        inst->ymodem.state          = SHELLMATTA_YMODEM_INACTIVE;
         shellmatta_opt_init(inst, 0u);
         shellmatta_opt_init(inst, 0u);
 
 
 #ifdef SHELLMATTA_AUTHENTICATION
 #ifdef SHELLMATTA_AUTHENTICATION

+ 12 - 12
src/shellmatta_auth.c

@@ -34,7 +34,7 @@
  * @return      #SHELLMATTA_BUSY    => reading in data
  * @return      #SHELLMATTA_BUSY    => reading in data
  *              #SHELLMATTA_OK      => delimiter (e.g. enter) detected
  *              #SHELLMATTA_OK      => delimiter (e.g. enter) detected
  */
  */
-shellmatta_retCode_t inputWrapper(shellmatta_instance_t *inst, char data, bool hide)
+static shellmatta_retCode_t inputWrapper(shellmatta_instance_t *inst, char data, bool hide)
 {
 {
     shellmatta_retCode_t ret = SHELLMATTA_BUSY;
     shellmatta_retCode_t ret = SHELLMATTA_BUSY;
     bool echoEnabledBackup = inst->echoEnabled;
     bool echoEnabledBackup = inst->echoEnabled;
@@ -108,7 +108,7 @@ static uint32_t getUserIdFromName(shellmatta_instance_t *inst, const char *usern
  * @param[in]       userId      userId to check the password for
  * @param[in]       userId      userId to check the password for
  * @param[in]       password    password to check for - pointer to string
  * @param[in]       password    password to check for - pointer to string
  */
  */
-void checkPassword(shellmatta_handle_t handle, uint32_t userId, char *password)
+static void checkPassword(shellmatta_handle_t handle, uint32_t userId, char *password)
 {
 {
     shellmatta_instance_t   *inst   = (shellmatta_instance_t*)handle;
     shellmatta_instance_t   *inst   = (shellmatta_instance_t*)handle;
     uint32_t                approvedUserId  = 0u;
     uint32_t                approvedUserId  = 0u;
@@ -144,11 +144,11 @@ void checkPassword(shellmatta_handle_t handle, uint32_t userId, char *password)
     /** -# print login result */
     /** -# print login result */
     if (0 == approvedUserId)
     if (0 == approvedUserId)
     {
     {
-        shellmatta_write(handle, "username or password is wrong\r\n", 31);
+        SHELLMATTA_WRITE("username or password is wrong\r\n", 31);
     }
     }
     else
     else
     {
     {
-        shellmatta_write(handle, "login successful\r\n", 18);
+        SHELLMATTA_WRITE("login successful\r\n", 18);
         (void)shellmatta_auth_login(handle, approvedUserId);
         (void)shellmatta_auth_login(handle, approvedUserId);
     }
     }
 }
 }
@@ -201,7 +201,7 @@ static shellmatta_retCode_t loginCmdFct(const shellmatta_handle_t handle, const
                         }
                         }
                         break;
                         break;
                     default:
                     default:
-                        shellmatta_write(handle, "Unknown option\r\n", 16);
+                        SHELLMATTA_WRITE("Unknown option\r\n", 16);
                         return SHELLMATTA_USE_FAULT;
                         return SHELLMATTA_USE_FAULT;
                         break;
                         break;
                 }
                 }
@@ -220,13 +220,13 @@ static shellmatta_retCode_t loginCmdFct(const shellmatta_handle_t handle, const
                 inst->cursor = 0u;
                 inst->cursor = 0u;
 
 
                 inst->tmpUserId = getUserIdFromName(handle, username);
                 inst->tmpUserId = getUserIdFromName(handle, username);
-                shellmatta_write(handle, "\r\nenter password:\r\n", 19);
+                SHELLMATTA_WRITE("\r\nenter password:\r\n", 19);
                 inst->loginState = SHELLMATTA_AUTH_PASSWORD;
                 inst->loginState = SHELLMATTA_AUTH_PASSWORD;
                 ret = SHELLMATTA_CONTINUE;
                 ret = SHELLMATTA_CONTINUE;
             }
             }
             else if (NULL != password)
             else if (NULL != password)
             {
             {
-                shellmatta_write(handle, "Missing username\r\n", 18);
+                SHELLMATTA_WRITE("Missing username\r\n", 18);
                 ret = SHELLMATTA_USE_FAULT;
                 ret = SHELLMATTA_USE_FAULT;
             }
             }
             else
             else
@@ -234,7 +234,7 @@ static shellmatta_retCode_t loginCmdFct(const shellmatta_handle_t handle, const
                 /** -# no credentials are passed with the command - start the interactive input */
                 /** -# no credentials are passed with the command - start the interactive input */
                 inst->inputCount = 0u;
                 inst->inputCount = 0u;
                 inst->cursor = 0u;
                 inst->cursor = 0u;
-                shellmatta_write(handle, "enter username:\r\n", 17);
+                SHELLMATTA_WRITE("enter username:\r\n", 17);
                 inst->loginState = SHELLMATTA_AUTH_USERNAME;
                 inst->loginState = SHELLMATTA_AUTH_USERNAME;
                 ret = SHELLMATTA_CONTINUE;
                 ret = SHELLMATTA_CONTINUE;
             }
             }
@@ -253,7 +253,7 @@ static shellmatta_retCode_t loginCmdFct(const shellmatta_handle_t handle, const
                 inst->inputCount = 0u;
                 inst->inputCount = 0u;
                 inst->cursor = 0u;
                 inst->cursor = 0u;
 
 
-                shellmatta_write(handle, "\r\nenter password:\r\n", 19);
+                SHELLMATTA_WRITE("\r\nenter password:\r\n", 19);
                 inst->loginState = SHELLMATTA_AUTH_PASSWORD;
                 inst->loginState = SHELLMATTA_AUTH_PASSWORD;
             }
             }
             ret = SHELLMATTA_CONTINUE;
             ret = SHELLMATTA_CONTINUE;
@@ -266,7 +266,7 @@ static shellmatta_retCode_t loginCmdFct(const shellmatta_handle_t handle, const
             {
             {
                 /** -# check input username and password */
                 /** -# check input username and password */
                 inst->buffer[inst->inputCount] = '\0';
                 inst->buffer[inst->inputCount] = '\0';
-                shellmatta_write(handle, "\r\n", 2);
+                SHELLMATTA_WRITE("\r\n", 2);
                 checkPassword(handle, inst->tmpUserId, inst->buffer);
                 checkPassword(handle, inst->tmpUserId, inst->buffer);
                 inst->loginState = SHELLMATTA_AUTH_IDLE;
                 inst->loginState = SHELLMATTA_AUTH_IDLE;
             }
             }
@@ -311,7 +311,7 @@ static shellmatta_retCode_t logoutCmdFct(const shellmatta_handle_t handle, const
     }
     }
 
 
     shellmatta_auth_logout(handle);
     shellmatta_auth_logout(handle);
-    shellmatta_write(handle, "good bye\r\n", 10);
+    SHELLMATTA_WRITE("good bye\r\n", 10);
 
 
     (void)arguments;
     (void)arguments;
     (void)length;
     (void)length;
@@ -410,7 +410,7 @@ shellmatta_retCode_t shellmatta_auth_init(shellmatta_handle_t       handle,
  * @param[in]   handle      shellmatta instance handle
  * @param[in]   handle      shellmatta instance handle
  * @param[in]   userId      userId to login
  * @param[in]   userId      userId to login
  * @return      errorcode   #SHELLMATTA_OK
  * @return      errorcode   #SHELLMATTA_OK
- *                          #SHELLMATTA_ERR (user does not exist)
+ *                          #SHELLMATTA_ERROR (user does not exist)
  *                          #SHELLMATTA_USE_FAULT (param err)
  *                          #SHELLMATTA_USE_FAULT (param err)
  */
  */
 shellmatta_retCode_t shellmatta_auth_login(shellmatta_handle_t handle, uint32_t userId)
 shellmatta_retCode_t shellmatta_auth_login(shellmatta_handle_t handle, uint32_t userId)

+ 111 - 25
src/shellmatta_crc.c

@@ -14,6 +14,8 @@
 
 
 #include "shellmatta_crc.h"
 #include "shellmatta_crc.h"
 
 
+#ifdef SHELLMATTA_TRANSPORT
+
 #ifndef SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
 #ifndef SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
 /** \brief CRC Lookup table for 0x04c11db7 reflected */
 /** \brief CRC Lookup table for 0x04c11db7 reflected */
 uint32_t crc32Table[] = {
 uint32_t crc32Table[] = {
@@ -50,9 +52,29 @@ uint32_t crc32Table[] = {
     0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
     0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
     0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
     0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
 };
 };
-#endif
 
 
-#ifdef SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
+/**
+ * @brief       Computes the crc32-checksum of a buffer. O(n)
+ * @param[in]   data            pointer to data buffer
+ * @param[in]   size            amount of bytes to be processed
+ * @param[in]   lookupTable     pointer to uint32_t lookup table for crc computation
+ * @return      crc             calculated crc32 value
+ */
+static uint32_t crc32Fast(const char* data, uint32_t size, const uint32_t* lookupTable)
+{
+    uint32_t i;
+    uint32_t crcTemp = 0xffffffffu;
+
+    for (i = 0u; i < size; i++)
+    {
+        uint8_t index = data[i] ^ (crcTemp & 0xffu);
+        crcTemp = lookupTable[index] ^ (crcTemp >> 8u);
+    }
+
+    return ~crcTemp;
+}
+
+#else
 /**
 /**
  * @brief       Reverses bits of a value.
  * @brief       Reverses bits of a value.
  * @param[in]   x       input value
  * @param[in]   x       input value
@@ -69,9 +91,8 @@ static uint32_t reverse(uint32_t x, uint32_t bits)
     return x >> (32u - bits);
     return x >> (32u - bits);
 }
 }
 
 
-
 /**
 /**
- * @brief       Computes the crc32-checksum of a buffer. O(4n)
+ * @brief       Computes the crc32-checksum of a buffer. O(8n)
  * @param[in]   data            pointer to data buffer
  * @param[in]   data            pointer to data buffer
  * @param[in]   size            amount of bytes to be processed
  * @param[in]   size            amount of bytes to be processed
  * @return      crc             calculated crc32 value
  * @return      crc             calculated crc32 value
@@ -109,41 +130,106 @@ static uint32_t crc32Slow(const char* data, uint32_t size)
     return crcTemp;
     return crcTemp;
 }
 }
 
 
-#else
+#endif /* SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP */
+
 /**
 /**
- * @brief       Computes the crc32-checksum of a buffer. O(n)
+ * @brief       Computes the crc32-checksum of a buffer.
  * @param[in]   data            pointer to data buffer
  * @param[in]   data            pointer to data buffer
  * @param[in]   size            amount of bytes to be processed
  * @param[in]   size            amount of bytes to be processed
- * @param[in]   lookupTable     pointer to uint32_t lookup table for crc computation
  * @return      crc             calculated crc32 value
  * @return      crc             calculated crc32 value
  */
  */
-static uint32_t crc32Fast(const char* data, uint32_t size, const uint32_t* lookupTable)
+uint32_t crc32Calc(const char* data, uint32_t size)
 {
 {
-    uint32_t i;
-    uint32_t crcTemp = 0xffffffffu;
+    #ifdef  SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
+        return crc32Slow(data, size);
+    #else
+        return crc32Fast(data, size, crc32Table);
+    #endif
+}
 
 
-    for (i = 0u; i < size; i++)
-    {
-        uint8_t index = data[i] ^ (crcTemp & 0xffu);
-        crcTemp = lookupTable[index] ^ (crcTemp >> 8u);
-    }
+#endif /* SHELLMATTA_TRANSPORT */
 
 
-    return ~crcTemp;
+#ifndef SHELLMATTA_YMODEM_CRC_NO_LOOKUP
+/** \brief CRC lookup table for 0x1021 non-reflected */
+uint16_t crc16Table[] = {
+    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+    0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+    0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+    0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+    0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+    0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+    0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+    0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+    0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+    0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+    0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+    0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+    0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+    0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+    0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+    0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+};
+
+/**
+ * @brief       Computes the crc16-checksum of a buffer. O(n)
+ * @param[in]   data            pointer to data buffer
+ * @param[in]   size            amount of bytes to be processed
+ * @param[in]   lookupTable     pointer to uint16_t lookup table for crc computation
+ * @return      crc             calculated crc16 value
+*/
+uint16_t crc16Fast(const char* data, uint32_t size, const uint16_t* lookupTable)
+{
+    uint32_t counter;
+    uint16_t crc = 0;
+    for(counter = 0; counter < size; counter++)
+        crc = (crc<<8) ^ lookupTable[((crc>>8) ^ *(char *)data++)&0x00FF];
+    return crc;
 }
 }
-#endif
 
 
+#else
 
 
 /**
 /**
- * @brief       Computes the crc32-checksum of a buffer.
+ * @brief       Computes the crc16-checksum of a buffer. O(8n)
+ * @param[in]   data                pointer to data buffer
+ * @param[in]   size                amount of bytes to be process
+ * @return      crc                 calculated crc16 value
+ * @note        This code is a modified version of the code presented at http://wiki.synchro.net/ref:xmodem
+*/
+uint16_t crc16Slow(const char* data, uint32_t size)
+{
+    uint16_t crc = 0;
+    uint32_t i, j;
+
+    for (i = 0; i < size; i++) {
+        crc = crc ^ (data[i] << BITS_PER_BYTE);
+        for (j = 0; j < BITS_PER_BYTE; ++j)
+        {
+            if (crc & 0x8000)
+            {
+                crc = (crc << 1) ^ CRC16_XMODEM_POLYNOM;
+            }
+            else
+            {
+                crc = crc << 1;
+            }
+        }
+    }
+    return (crc & 0xFFFF);
+}
+
+#endif /* SHELLMATTA_YMODEM_CRC_NO_LOOKUP */
+
+/**
+ * @brief       Computes the crc16-checksum of a buffer.
  * @param[in]   data            pointer to data buffer
  * @param[in]   data            pointer to data buffer
  * @param[in]   size            amount of bytes to be processed
  * @param[in]   size            amount of bytes to be processed
- * @return      crc             calculated crc32 value
- */
-uint32_t crc32Calc(const char* data, uint32_t size)
+ * @return      crc             calculated crc16 value
+*/
+uint16_t crc16Calc(const char* data, uint32_t size)
 {
 {
-    #ifdef  SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
-        return crc32Slow(data, size);
+    #ifdef SHELLMATTA_YMODEM_CRC_NO_LOOKUP
+        return crc16Slow(data, size);
     #else
     #else
-        return crc32Fast(data, size, crc32Table);
-    #endif
+        return crc16Fast(data, size, crc16Table);
+    #endif /* SHELLMATTA_YMODEM_CRC_NO_LOOKUP */
 }
 }

+ 4 - 2
src/shellmatta_crc.h

@@ -17,9 +17,11 @@
 
 
 #include <stdint.h>
 #include <stdint.h>
 
 
-#define CRC32_POLYNOM   0x04c11db7u     /**< crc-32 ethernet 802.3                          */
-#define BITS_PER_BYTE   ((uint8_t)8)    /**< amount of bits per byte; to avoid magic number */
+#define CRC32_POLYNOM           0x04c11db7u     /**< crc-32 ethernet 802.3                          */
+#define CRC16_XMODEM_POLYNOM    0x1021u         /**< crc16 xmodem                                   */
+#define BITS_PER_BYTE           ((uint8_t)8)    /**< amount of bits per byte; to avoid magic number */
 
 
 uint32_t crc32Calc(const char* data, uint32_t size);
 uint32_t crc32Calc(const char* data, uint32_t size);
+uint16_t crc16Calc(const char* data, uint32_t size);
 
 
 #endif /* _SHELLMATTA_CRC_H_ */
 #endif /* _SHELLMATTA_CRC_H_ */

+ 13 - 3
src/shellmatta_transport.c

@@ -9,8 +9,14 @@
 /**
 /**
  * @file    shellmatta_transport.c
  * @file    shellmatta_transport.c
  * @brief   transport layer functions of shellmatta
  * @brief   transport layer functions of shellmatta
- * @author  Simon Fischer <fischer.simon.1991@gmail.com>
+ * @authors Simon Fischer <dev@s-fischer.net> Stefan Strobel <stefan.strobel@shimatta.net>
  */
  */
+
+/**
+ * @addtogroup shellmatta_transport
+ * @{
+ */
+
 #ifdef SHELLMATTA_TRANSPORT
 #ifdef SHELLMATTA_TRANSPORT
 #include "shellmatta_transport.h"
 #include "shellmatta_transport.h"
 #include "shellmatta.h"
 #include "shellmatta.h"
@@ -223,7 +229,9 @@ shellmatta_retCode_t shellmatta_transport_process(shellmatta_transport_layer_t
     /** -# look for start of header */
     /** -# look for start of header */
     case SHELLMATTA_TRANSPORT_STATE_WAIT:
     case SHELLMATTA_TRANSPORT_STATE_WAIT:
         /** -# if start of header is found, continue transport layer fsm */
         /** -# if start of header is found, continue transport layer fsm */
-        if(SHELLMATTA_TRANSPORT_START_OF_HEADER == byte)
+        /** -# respect suspendedOptional when there is no active session */
+        if((SHELLMATTA_TRANSPORT_START_OF_HEADER == byte) &&
+           !((false == transportLayer->active) && (true == transportLayer->suspendOptional)))
         {
         {
             memset(&transportLayer->inPacket, 0, sizeof(transportLayer->inPacket));
             memset(&transportLayer->inPacket, 0, sizeof(transportLayer->inPacket));
             transportLayer->headerIndex = 1u;
             transportLayer->headerIndex = 1u;
@@ -428,7 +436,7 @@ shellmatta_retCode_t shellmatta_transport_process(shellmatta_transport_layer_t
  * The input data is copied into a new array along with the header and crc32.\n
  * The input data is copied into a new array along with the header and crc32.\n
  * The resulting buffer is the forwarded to the original write function.\n
  * The resulting buffer is the forwarded to the original write function.\n
  *
  *
- * @note        If length of data exceeds #UINT8_MAX, data is sent in multiple packets.\n
+ * @note        If length of data exceeds UINT8_MAX, data is sent in multiple packets.\n
  *
  *
  * @param[in, out]  transportLayer  transport layer instance to work on
  * @param[in, out]  transportLayer  transport layer instance to work on
  * @param[in]       data            pointer to input data to process
  * @param[in]       data            pointer to input data to process
@@ -515,3 +523,5 @@ shellmatta_retCode_t shellmatta_transport_flush(shellmatta_handle_t handle)
     return ret;
     return ret;
 }
 }
 #endif
 #endif
+
+/** @} */

+ 8 - 1
src/shellmatta_transport.h

@@ -9,7 +9,12 @@
 /**
 /**
  * @file    shellmatta_transport.h
  * @file    shellmatta_transport.h
  * @brief   transport layer functions of shellmatta
  * @brief   transport layer functions of shellmatta
- * @author  Simon Fischer <fischer.simon.1991@gmail.com>
+ * @authors Simon Fischer <dev@s-fischer.net> Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_transport
+ * @{
  */
  */
 
 
 #ifndef _SHELLMATTA_TRANSPORT_H_
 #ifndef _SHELLMATTA_TRANSPORT_H_
@@ -78,3 +83,5 @@ shellmatta_retCode_t shellmatta_transport_write(shellmatta_transport_layer_t *tr
                                                 uint32_t length);
                                                 uint32_t length);
 
 
 #endif /* _SHELLMATTA_TRANSPORT_H_ */
 #endif /* _SHELLMATTA_TRANSPORT_H_ */
+
+/** @} */

+ 76 - 8
src/shellmatta_utils.c

@@ -88,6 +88,69 @@ uint32_t utils_shellItoa(int32_t value, char *buffer, uint32_t base)
     return bufferIdx;
     return bufferIdx;
 }
 }
 
 
+/**
+ * @brief       turns a string of digits into an unsigned 32-bit integer
+ * @param[in]   str     pointer to the string
+ * @param[out]  result  the value of the string as unsigned 32-bit integer
+ * @param[in]   base    base of the conversion
+ * @return      errorcode   #SHELLMATTA_OK
+ *                          #SHELLMATTA_USE_FAULT (param err)
+ *                          #SHELLMATTA_ERROR (invalid characters or overflow)
+*/
+shellmatta_retCode_t utils_shellAsciiToUInt32(char* str, uint32_t *result, uint32_t base)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+    uint32_t val;
+
+    if((NULL != str) && (NULL != result) && (base <= 16u))
+    {
+        *result = 0u;
+
+        while(*str && (' ' != *str) && (SHELLMATTA_OK == ret))
+        {
+            /** -# convert character to value */
+            if((*str >= '0') && (*str <= '9'))
+            {
+                val = ((uint32_t)*str - '0');
+            }
+            else if((*str >= 'A') && (*str <= 'F'))
+            {
+                val = ((uint32_t)*str - 'A') + 10u;
+            }
+            else if((*str >= 'a') && (*str <= 'f'))
+            {
+                val = ((uint32_t)*str - 'a') + 10u;
+            }
+            else
+            {
+                ret = SHELLMATTA_ERROR;
+            }
+
+            /** -# return error in case the value is outside of the expected base */
+            if(val > (base - 1u))
+            {
+                ret = SHELLMATTA_ERROR;
+            }
+            else
+            {
+                /** -# check for overflow */
+                if((UINT32_MAX - val) / base < *result)
+                {
+                    ret = SHELLMATTA_ERROR;
+                }
+                *result = (*result * base) + val;
+            }
+            str ++;
+        }
+    }
+    else
+    {
+        ret = SHELLMATTA_USE_FAULT;
+    }
+
+    return ret;
+}
+
 /**
 /**
  * @brief       tells the terminal to save the current cursor position
  * @brief       tells the terminal to save the current cursor position
  * @param[in]   inst    pointer to shellmatta instance
  * @param[in]   inst    pointer to shellmatta instance
@@ -474,16 +537,21 @@ void utils_terminateInput(shellmatta_instance_t *inst)
     inst->stdinLength       = 0u;
     inst->stdinLength       = 0u;
     inst->continuousCmd     = NULL;
     inst->continuousCmd     = NULL;
     inst->busyCmd           = NULL;
     inst->busyCmd           = NULL;
-    SHELLMATTA_WRITE("\r\n", 2u);
-#ifdef SHELLMATTA_AUTHENTICATION
-    inst->loginState        = SHELLMATTA_AUTH_IDLE;
-    if (NULL != inst->userPointer)
+
+    /** -# print prompt and username when no ymodem session is running */
+    if(SHELLMATTA_YMODEM_INACTIVE == inst->ymodem.state)
     {
     {
-        SHELLMATTA_WRITE(inst->userPointer->username, strlen(inst->userPointer->username));
-        SHELLMATTA_WRITE("@", 1);
-    }
+        SHELLMATTA_WRITE("\r\n", 2u);
+#ifdef SHELLMATTA_AUTHENTICATION
+        inst->loginState        = SHELLMATTA_AUTH_IDLE;
+        if (NULL != inst->userPointer)
+        {
+            SHELLMATTA_WRITE(inst->userPointer->username, strlen(inst->userPointer->username));
+            SHELLMATTA_WRITE("@", 1);
+        }
 #endif
 #endif
-    SHELLMATTA_WRITE(inst->prompt, strlen(inst->prompt));
+        SHELLMATTA_WRITE(inst->prompt, strlen(inst->prompt));
+    }
 }
 }
 
 
 /**
 /**

+ 36 - 19
src/shellmatta_utils.h

@@ -16,6 +16,7 @@
  * @addtogroup shellmatta_utils
  * @addtogroup shellmatta_utils
  * @{
  * @{
  */
  */
+
 #ifndef _SHELLMATTA_UTILS_H_
 #ifndef _SHELLMATTA_UTILS_H_
 #define _SHELLMATTA_UTILS_H_
 #define _SHELLMATTA_UTILS_H_
 
 
@@ -50,7 +51,6 @@
  * @brief       calls fct with cnt bytes from buffer (to print cnt same bytes)
  * @brief       calls fct with cnt bytes from buffer (to print cnt same bytes)
  * @param[in]   buffer  buffer to send (shall contain the same char)
  * @param[in]   buffer  buffer to send (shall contain the same char)
  * @param[in]   cnt     count of bytes to send
  * @param[in]   cnt     count of bytes to send
- * @param[in]   fct     write function
  */
  */
 #define SHELLMATTA_PRINT_BUFFER(buffer,cnt)             \
 #define SHELLMATTA_PRINT_BUFFER(buffer,cnt)             \
     while((cnt) > sizeof((buffer)))                     \
     while((cnt) > sizeof((buffer)))                     \
@@ -106,25 +106,42 @@ extern const shellmatta_cmd_t helpCmd;
  *  @}
  *  @}
  */
  */
 
 
-void utils_writeEcho(   shellmatta_instance_t   *inst,
-                        const char              *data,
-                        uint32_t                length);
-uint32_t utils_shellItoa(int32_t value, char *buffer, uint32_t base);
-void utils_saveCursorPos(shellmatta_instance_t *inst);
-void utils_restoreCursorPos(shellmatta_instance_t *inst);
-void utils_eraseLine(shellmatta_instance_t *inst);
-void utils_rewindCursor(shellmatta_instance_t *inst, uint32_t length);
-void utils_forwardCursor(shellmatta_instance_t *inst, uint32_t length);
-void utils_insertChars( shellmatta_instance_t   *inst,
-                        const char              *data,
-                        uint32_t                 length);
-void utils_removeChars( shellmatta_instance_t   *inst,
-                        uint32_t                 length,
-                        bool                     backspace);
-void utils_clearInput(shellmatta_instance_t *inst);
-void utils_terminateInput(shellmatta_instance_t *inst);
+void utils_writeEcho(               shellmatta_instance_t   *inst,
+                                    const char              *data,
+                                    uint32_t                length);
+
+uint32_t utils_shellItoa(           int32_t value, 
+                                    char *buffer, 
+                                    uint32_t base);
+
+shellmatta_retCode_t utils_shellAsciiToUInt32(  char* str,
+                                                uint32_t *result,
+                                                uint32_t base);
+
+void utils_saveCursorPos(           shellmatta_instance_t *inst);
+
+void utils_restoreCursorPos(        shellmatta_instance_t *inst);
+
+void utils_eraseLine(               shellmatta_instance_t *inst);
+
+void utils_rewindCursor(            shellmatta_instance_t *inst, 
+                                    uint32_t length);
+
+void utils_forwardCursor(           shellmatta_instance_t *inst, 
+                                    uint32_t length);
+
+void utils_insertChars(             shellmatta_instance_t   *inst,
+                                    const char              *data,
+                                    uint32_t                 length);
+
+void utils_removeChars(             shellmatta_instance_t   *inst,
+                                    uint32_t                 length,
+                                    bool                     backspace);
+
+void utils_clearInput(              shellmatta_instance_t *inst);
+
+void utils_terminateInput(          shellmatta_instance_t *inst);
 
 
 #endif
 #endif
 
 
 /** @} */
 /** @} */
-

+ 531 - 0
src/shellmatta_ymodem.c

@@ -0,0 +1,531 @@
+/*
+ * Copyright (c) 2023 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    shellmatta_ymodem.c
+ * @brief   ymodem functions of shellmatta
+ * @authors Simon Fischer <dev@s-fischer.net> Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_ymodem
+ * @{
+ */
+
+#include "shellmatta_ymodem.h"
+#include "shellmatta_crc.h"
+#include "shellmatta_utils.h"
+#include <stddef.h>
+#include <string.h>
+
+/** @brief symbols needed for ymodem protocol */
+typedef enum {
+    YMODEM_NULL    = 0x00, /**< NULL-terminator                 */
+    YMODEM_SOH     = 0x01, /**< Start of header                 */
+    YMODEM_STX     = 0x02, /**< Start of header for 1k packet   */
+    YMODEM_EOT     = 0x04, /**< End of transmission             */
+    YMODEM_ACK     = 0x06, /**< Acknowledge                     */
+    YMODEM_NAK     = 0x15, /**< Negative acknowledge            */
+    YMODEM_CA      = 0x18, /**< Cancel                          */
+    YMODEM_SPACE   = 0x20, /**< Space                           */
+    YMODEM_CRC     = 0x43  /**< 'C' to indicate CRC type        */
+} YMODEM_SYMBOLS;
+
+#define YMODEM_PACKET_SIZE          128u    /**< default packet size of ymodem transmission     */
+#define YMODEM_PACKET_SIZE_1K       1024u   /**< extended packet size of ymodem transmission    */
+#define YMODEM_CRC_SIZE             2u      /**< CRC size of the ymodem packet                  */
+#define YMODEM_POLL_NUMBER          2u      /**< Number of polls to be received                 */
+
+/**
+ * @brief               forwards the given character to write-function without formatting
+ * @param[in]   handle  shellmatta handle of the instance
+ * @param[in]   c       character to be sent
+*/
+static void shellmatta_ymodem_control(shellmatta_handle_t handle, const uint8_t c)
+{
+    shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
+    SHELLMATTA_WRITE((char*)&c, 1);
+}
+
+/**
+ * @brief                       reset function for the ymodem module
+ * @param[in, out]  handle      shellmatta instance handle
+ * @param[in]       doCancel    flag to execute the cancel-callback
+*/
+static void shellmatta_ymodem_reset(shellmatta_handle_t handle, bool doCancel)
+{
+    shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
+
+    /** -# call cancel callback function */
+    if (doCancel)
+    {
+        /* send cancel symbol */
+        shellmatta_ymodem_control(handle, YMODEM_CA);
+        if (NULL != inst->ymodem.cancelCallback)
+        {
+            inst->ymodem.cancelCallback(handle);
+        }
+    }
+    /** -# reset instance variables */
+    inst->ymodem.state = SHELLMATTA_YMODEM_INACTIVE;
+    inst->ymodem.byteCounter = 0u;
+    inst->ymodem.packetCounter = 0u;
+    inst->ymodem.totalBytesReceived = 0u;
+    inst->ymodem.fileSize = 0u;
+    inst->ymodem.pauseRequested = false;
+    inst->ymodem.pollCyclesLeft = 0u;
+    inst->ymodem.cancelCounter = 0u;
+
+#ifdef SHELLMATTA_TRANSPORT
+    /** .-# reenable transport layer optional mode */
+    inst->transportLayer.suspendOptional = false;
+#endif
+
+    (void)memset((void *)&inst->ymodem.packet, 0, sizeof(shellmatta_ymodem_packet_t));
+}
+
+/**
+ * @brief                   Processes a completely received ymodem packet
+ * @param[in, out]  handle  shellmatta instance handle
+ * @return      errorcode   #SHELLMATTA_OK
+ *                          #SHELLMATTA_ERROR (invalid file size)
+*/
+shellmatta_retCode_t processPacket(shellmatta_handle_t handle)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+    shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
+    char *fileName;
+    uint32_t packetSize;
+
+    /** -# read filesize and name from first packet - ignore rest */
+    if(0u == inst->ymodem.packetCounter)
+    {
+        fileName = (char*)inst->ymodem.packet.packetData;
+
+        ret = utils_shellAsciiToUInt32((char*)&inst->ymodem.packet.packetData[strlen(fileName) + 1u],
+                                       &inst->ymodem.fileSize,
+                                       10u);
+
+        /** -# pass filename and size to the callback */
+        inst->ymodem.recvHeaderCallback(handle, inst->ymodem.fileSize, fileName);
+    }
+    else
+    {
+        /** -# calculate packet size - when it is the last packet this is limited by the file size */
+        if((0u != inst->ymodem.fileSize) &&
+           (inst->ymodem.totalBytesReceived + inst->ymodem.packet.size) > inst->ymodem.fileSize)
+        {
+            packetSize = inst->ymodem.fileSize % inst->ymodem.packet.size;
+        }
+        else
+        {
+            packetSize = inst->ymodem.packet.size;
+        }
+
+        /** -# pass data to the application using the callback */
+        inst->ymodem.recvPacketCallback(handle, inst->ymodem.packet.packetData, packetSize, inst->ymodem.packetCounter);
+    }
+
+    return ret;
+}
+
+/**
+ * @brief                   Processes incoming bytes using the ymodem state machine
+ * @param[in, out]  handle  shellmatta instance handle
+ * @param[in]       byte    received byte
+ * @return      errorcode   #SHELLMATTA_OK
+ *                          #SHELLMATTA_ERROR (error during packet processing)
+*/
+static shellmatta_retCode_t ymodem_stateMachine(shellmatta_handle_t handle, uint8_t byte)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+    shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
+    uint16_t computedCrc;
+
+    switch(inst->ymodem.state)
+    {
+        case SHELLMATTA_YMODEM_INACTIVE:
+            /** -# skip state machine if inactive */
+            break;
+        case SHELLMATTA_YMODEM_WAIT_FOR_START:
+            /** -# Wait for start character */
+            inst->ymodem.byteCounter = 0u;
+            switch(byte)
+            {
+                case YMODEM_SOH:
+                    inst->ymodem.packet.size = YMODEM_PACKET_SIZE;
+                    inst->ymodem.state = SHELLMATTA_YMODEM_RECEIVE_HEADER;
+                    break;
+                case YMODEM_STX:
+                    inst->ymodem.packet.size = YMODEM_PACKET_SIZE_1K;
+                    inst->ymodem.state = SHELLMATTA_YMODEM_RECEIVE_HEADER;
+                    break;
+                case YMODEM_EOT:
+                    /** -# ACK the successful file reception */
+                    shellmatta_ymodem_control(handle, YMODEM_ACK);
+                    shellmatta_ymodem_control(handle, YMODEM_CRC);
+
+                    /** -# handle additional EOTs in WAIT FOR END state */
+                    inst->ymodem.state = SHELLMATTA_YMODEM_WAIT_FOR_END;
+                    inst->ymodem.pollCyclesLeft = YMODEM_POLL_NUMBER;
+                    break;
+                default:
+                    /** -# ignore unexpected characters on start */
+                    break;
+            }
+            break;
+        case SHELLMATTA_YMODEM_RECEIVE_HEADER:
+            if(0u == inst->ymodem.byteCounter)
+            {
+                inst->ymodem.packet.packetNumber = byte;
+                inst->ymodem.byteCounter ++;
+            }
+            else
+            {
+                if(((0xffu - byte) != inst->ymodem.packet.packetNumber) ||
+                  (inst->ymodem.packetCounter % 256u != inst->ymodem.packet.packetNumber))
+                {
+                    /** -# return error on packet number mismatch or on unexpected packet numbers */
+                    inst->ymodem.state = SHELLMATTA_YMODEM_WAIT_FOR_START;
+                    shellmatta_ymodem_control(handle, YMODEM_NAK);
+                    ret = SHELLMATTA_ERROR;
+                }
+                else
+                {
+                    /** -# start receiving the payload */
+                    inst->ymodem.state = SHELLMATTA_YMODEM_RECEIVE_DATA;
+                    inst->ymodem.byteCounter = 0u;
+                }
+            }
+            break;
+        case SHELLMATTA_YMODEM_RECEIVE_DATA:
+            inst->ymodem.packet.packetData[inst->ymodem.byteCounter] = byte;
+            inst->ymodem.byteCounter ++;
+
+            /** -# load payload until the packet is full */
+            if(inst->ymodem.byteCounter >= inst->ymodem.packet.size)
+            {
+                inst->ymodem.state = SHELLMATTA_YMODEM_RECEIVE_CRC;
+                inst->ymodem.byteCounter = 0u;
+                inst->ymodem.packet.crc = 0u;
+            }
+
+            break;
+        case SHELLMATTA_YMODEM_RECEIVE_CRC:
+            inst->ymodem.byteCounter ++;
+            inst->ymodem.packet.crc |= (uint16_t)byte << (8u * (YMODEM_CRC_SIZE - inst->ymodem.byteCounter));
+
+            if(inst->ymodem.byteCounter >= YMODEM_CRC_SIZE)
+            {
+                inst->ymodem.state = SHELLMATTA_YMODEM_WAIT_FOR_START;
+
+                /** -# check CRC */
+                computedCrc = crc16Calc((const char*)inst->ymodem.packet.packetData,
+                                        inst->ymodem.packet.size);
+                if(computedCrc != inst->ymodem.packet.crc)
+                {
+                    shellmatta_ymodem_control(handle, YMODEM_NAK);
+                    ret = SHELLMATTA_ERROR;
+                }
+                else
+                {
+                    ret = processPacket(handle);
+                    if(SHELLMATTA_OK == ret)
+                    {
+                        if(0u != inst->ymodem.packetCounter)
+                        {
+                            /** -# Calculate the total bytes received */
+                            inst->ymodem.totalBytesReceived += inst->ymodem.packet.size;
+                        }
+                        if(true != inst->ymodem.pauseRequested)
+                        {
+                            /** -# ACK the successful packet reception */
+                            shellmatta_ymodem_control(handle, YMODEM_ACK);
+                            if(0u == inst->ymodem.packetCounter)
+                            {
+                                /** -# send addional CRC flag after packet 0 */
+                                shellmatta_ymodem_control(handle, YMODEM_CRC);
+                            }
+                        }
+                        inst->ymodem.packetCounter ++;
+                    }
+                }
+            }
+            break;
+        case SHELLMATTA_YMODEM_WAIT_FOR_END:
+
+            inst->ymodem.pollCyclesLeft = YMODEM_POLL_NUMBER;
+            if(YMODEM_EOT ==  byte)
+            {
+                /** -# ACK the successful file reception */
+                shellmatta_ymodem_control(handle, YMODEM_ACK);
+                shellmatta_ymodem_control(handle, YMODEM_CRC);
+            }
+            break;
+        default:
+            /** -# unexpected state - should never happen */
+            break;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief                   Processes incoming bytes - checking for cancel requests and calling the state machine
+ * @param[in, out]  handle  shellmatta instance handle
+ * @param[in]       byte    received byte
+ * @return      errorcode   #SHELLMATTA_OK
+ *                          #SHELLMATTA_ERROR (error during packet processing)
+*/
+shellmatta_retCode_t shellmatta_ymodem_processByte(shellmatta_handle_t handle, char byte)
+{
+    shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+
+    /** -# check if session is cancelled - accept only if ymodem is between packets */
+    if((('\x03' == byte) || (YMODEM_CA == byte)) && (inst->ymodem.state == SHELLMATTA_YMODEM_WAIT_FOR_START))
+    {
+        inst->ymodem.cancelCounter ++;
+
+        /** -# cancel after at least 2 cancel characters */
+        if(inst->ymodem.cancelCounter >= 2u)
+        {
+            /** -# explicitly reset ymodem with cancel, if it was active */
+            shellmatta_ymodem_reset(handle, true);
+
+            utils_terminateInput(inst);
+
+            ret = SHELLMATTA_ERROR;
+        }
+    }
+    else
+    {
+        inst->ymodem.cancelCounter = 0u;
+        ret = ymodem_stateMachine(handle, (uint8_t)byte);
+    }
+
+    return ret;
+}
+
+/**
+ * @brief                   Processes a poll from the application without data - shall be calles every second when
+ *                          no data is received
+ * @param[in, out]  handle  shellmatta instance handle
+ * @return      errorcode   #SHELLMATTA_OK
+ *                          #SHELLMATTA_ERROR (error during packet processing)
+*/
+shellmatta_retCode_t shellmatta_ymodem_poll(shellmatta_handle_t handle)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_ERROR;
+    shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
+
+    switch (inst->ymodem.state)
+    {
+    case SHELLMATTA_YMODEM_WAIT_FOR_START:
+        /** -# send ymodem symbol to start transmission */
+        shellmatta_ymodem_control(handle, YMODEM_CRC);
+        ret = SHELLMATTA_OK;
+        break;
+    case SHELLMATTA_YMODEM_WAIT_FOR_END:
+        if(inst->ymodem.pollCyclesLeft > 1u)
+        {
+            inst->ymodem.pollCyclesLeft --;
+        }
+        else
+        {
+            ret = SHELLMATTA_OK;
+            /** -# check if the received data matches the file size */
+            if((0u != inst->ymodem.fileSize) &&
+                ((inst->ymodem.totalBytesReceived < inst->ymodem.fileSize) ||
+                ((inst->ymodem.totalBytesReceived - inst->ymodem.fileSize) >= YMODEM_PACKET_SIZE_1K)))
+            {
+                ret = SHELLMATTA_ERROR;
+            }
+
+#ifdef SHELLMATTA_TRANSPORT
+            /** .-# reenable transport layer optional mode */
+            inst->transportLayer.suspendOptional = false;
+#endif
+
+            shellmatta_ymodem_reset(handle, false);
+            inst->ymodem.transmissionCompleteCallback(handle, ret);
+            (void)utils_terminateInput(inst);
+        }
+        break;
+    default:
+        /* nothing to do */
+        break;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief                                           Initialise the ymodem prior to actually receiving data
+ * @param[in, out]  handle                          shellmatta instance handle
+ * @param[in]       recvBuffer                      pointer to the buffer to save the received payload in
+ * @param[in]       cancelCallback                  callback function for transmission being canceled
+ * @param[in]       recvHeaderCallback              callback function for header received
+ * @param[in]       recvPacketCallback              callback function for data packet received
+ * @param[in]       transmissionCompleteCallback    callback function for transmission complete
+ * @return          errorcode   #SHELLMATTA_OK
+ *                              #SHELLMATTA_USE_FAULT (param err)
+ * @note            Forces the transport layer in a fixed state when not mandatory
+*/
+shellmatta_retCode_t shellmatta_ymodem_init(shellmatta_handle_t             handle,
+                                            uint8_t*                        recvBuffer,
+                                            shellmatta_ymodem_cancel_t      cancelCallback,
+                                            shellmatta_ymodem_recvHeader_t  recvHeaderCallback,
+                                            shellmatta_ymodem_recvPacket_t  recvPacketCallback,
+                                            shellmatta_ymodem_complete_t    transmissionCompleteCallback)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+    shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
+
+    /** -# check parameters for plausibility  */
+    if(     (NULL               != inst)
+        &&  (SHELLMATTA_MAGIC   == inst->magic)
+        &&  (NULL               != cancelCallback)
+        &&  (NULL               != recvHeaderCallback)
+        &&  (NULL               != recvPacketCallback)
+        &&  (NULL               != transmissionCompleteCallback))
+    {
+
+        (void)memset((void *)&inst->ymodem, 0, sizeof(shellmatta_ymodem_t));
+
+        /** -# use instances buffer if no buffer is given */
+        if(NULL == recvBuffer)
+        {
+            if(inst->bufferSize < YMODEM_PACKET_SIZE_1K)
+            {
+                /** -# return use fault if buffer is too small */
+                ret = SHELLMATTA_USE_FAULT;
+            }
+            else
+            {
+                inst->ymodem.packet.packetData = (uint8_t*)inst->buffer;
+            }
+        }
+        else {
+            /** -# store buffer */
+            inst->ymodem.packet.packetData = recvBuffer;
+        }
+
+        /** -# init callbacks */
+        inst->ymodem.cancelCallback = cancelCallback;
+        inst->ymodem.recvHeaderCallback = recvHeaderCallback;
+        inst->ymodem.recvPacketCallback = recvPacketCallback;
+        inst->ymodem.transmissionCompleteCallback = transmissionCompleteCallback;
+
+        inst->ymodem.state = SHELLMATTA_YMODEM_WAIT_FOR_START;
+
+#ifdef SHELLMATTA_TRANSPORT
+        /** -# suspend the transport layer being optional while ymodem is running */
+        inst->transportLayer.suspendOptional = true;
+#endif
+
+        /** -# send initial ymodem symbol to start transmission */
+        shellmatta_ymodem_control(handle, YMODEM_CRC);
+    }
+    else
+    {
+        ret = SHELLMATTA_USE_FAULT;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief                   pauses the shellmatta reception until #shellmatta_ymodem_resume resume is called
+ * @param[in, out]  handle  shellmatta instance handle
+ * @return      errorcode   #SHELLMATTA_OK
+ *                          #SHELLMATTA_USE_FAULT (param err)
+ * @note                    This can be used when an application needs processing time before accepting the next packet
+*/
+shellmatta_retCode_t shellmatta_ymodem_pause(shellmatta_handle_t handle)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+    shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
+
+    /** -# check parameters for plausibility  */
+    if(     (NULL               != inst)
+        &&  (SHELLMATTA_MAGIC   == inst->magic))
+    {
+        inst->ymodem.pauseRequested = true;
+    }
+    else
+    {
+        ret = SHELLMATTA_USE_FAULT;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief                   Resume the ymodem module
+ * @param[in, out]  handle  shellmatta instance handle
+ * @return      errorcode   #SHELLMATTA_OK
+ *                          #SHELLMATTA_USE_FAULT (param err)
+ * @note                    This can be used when an application needs processing time before accepting the next packet
+*/
+shellmatta_retCode_t shellmatta_ymodem_resume(shellmatta_handle_t handle)
+{
+    shellmatta_retCode_t ret = SHELLMATTA_OK;
+    shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
+
+    /** -# check parameters for plausibility  */
+    if(     (NULL               != inst)
+        &&  (SHELLMATTA_MAGIC   == inst->magic))
+    {
+        /** -# ACK the successful packet reception */
+        shellmatta_ymodem_control(handle, YMODEM_ACK);
+        if(1u == inst->ymodem.packetCounter)
+        {
+            /** -# send addional CRC flag after packet 0 */
+            shellmatta_ymodem_control(handle, YMODEM_CRC);
+        }
+        inst->ymodem.pauseRequested = false;
+    }
+    else
+    {
+        ret = SHELLMATTA_USE_FAULT;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief                   Resets the ymodem module
+ * @param[in, out]  handle  shellmatta instance handle
+ * @param[in]   doCancel    Set this flag to execute the cancel-callback function within the ymodem-reset function
+ * @return      errorcode   #SHELLMATTA_OK
+ *                          #SHELLMATTA_USE_FAULT (param err)
+ * @note                    call this function to cancel an ongoing transmission
+*/
+shellmatta_retCode_t shellmatta_ymodem_cancel(shellmatta_handle_t handle, bool doCancel)
+{
+    shellmatta_retCode_t        ret = SHELLMATTA_OK;
+    const shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
+
+    /** -# check parameters for plausibility  */
+    if(     (NULL               != inst)
+        &&  (SHELLMATTA_MAGIC   == inst->magic))
+    {
+        shellmatta_ymodem_reset(handle, doCancel);
+        /* clear any possible leftover inputs */
+        utils_clearInput((shellmatta_instance_t*)handle);
+    }
+    else
+    {
+        ret = SHELLMATTA_USE_FAULT;
+    }
+
+    return ret;
+}
+
+/** @} */

+ 33 - 0
src/shellmatta_ymodem.h

@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2023 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    shellmatta_ymodem.h
+ * @brief   ymodem functions of shellmatta
+ * @authors Simon Fischer <dev@s-fischer.net> Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+/**
+ * @addtogroup shellmatta_transport
+ * @{
+ */
+
+#ifndef _SHELLMATTA_YMODEM_H_
+#define _SHELLMATTA_YMODEM_H_
+
+#include <stdint.h>
+#include "shellmatta.h"
+
+shellmatta_retCode_t shellmatta_ymodem_processByte( shellmatta_handle_t handle,
+                                                    char                byte);
+
+shellmatta_retCode_t shellmatta_ymodem_poll(        shellmatta_handle_t handle);
+
+#endif /* _SHELLMATTA_YMODEM_H_ */
+
+/** @} */

+ 2 - 2
test/integrationtest/test_integration_help.cpp

@@ -55,7 +55,7 @@ static shellmatta_cmd_t cmd3 = {(char*)"cmd3", (char*)"", (char*)"", (char*)"",
 
 
 SCENARIO("Test the help function")
 SCENARIO("Test the help function")
 {
 {
-    GIVEN("An initialized and empty Shellmatte instance")
+    GIVEN("An initialized and empty Shellmatta instance")
     {
     {
         shellmatta_instance_t inst;
         shellmatta_instance_t inst;
         shellmatta_handle_t handle;
         shellmatta_handle_t handle;
@@ -130,7 +130,7 @@ SCENARIO("Test the help function")
 
 
 SCENARIO("Test if the help command prints the usage correctly")
 SCENARIO("Test if the help command prints the usage correctly")
 {
 {
-    GIVEN("An initialized and empty Shellmatte instance")
+    GIVEN("An initialized and empty Shellmatta instance")
     {
     {
         shellmatta_instance_t inst;
         shellmatta_instance_t inst;
         shellmatta_handle_t handle;
         shellmatta_handle_t handle;

+ 3 - 3
test/integrationtest/test_integration_history.cpp

@@ -62,7 +62,7 @@ char *commandSequence[] =
 
 
 SCENARIO("Test the history buffer with a fixed sequence of commands in there")
 SCENARIO("Test the history buffer with a fixed sequence of commands in there")
 {
 {
-    GIVEN("An initialized Shellmatte instance with some commands already in the history buffer")
+    GIVEN("An initialized Shellmatta instance with some commands already in the history buffer")
     {
     {
         shellmatta_instance_t inst;
         shellmatta_instance_t inst;
         shellmatta_handle_t handle;
         shellmatta_handle_t handle;
@@ -244,7 +244,7 @@ SCENARIO("Test the history buffer with a fixed sequence of commands in there")
 
 
 SCENARIO("Test how the history buffer handles more commands than fits inside the buffer")
 SCENARIO("Test how the history buffer handles more commands than fits inside the buffer")
 {
 {
-    GIVEN("An initialized Shellmatte instance with some commands already in the history buffer")
+    GIVEN("An initialized Shellmatta instance with some commands already in the history buffer")
     {
     {
         shellmatta_instance_t inst;
         shellmatta_instance_t inst;
         shellmatta_handle_t handle;
         shellmatta_handle_t handle;
@@ -352,7 +352,7 @@ SCENARIO("Test how the history buffer handles more commands than fits inside the
 
 
 SCENARIO("Test if the history buffer stores changes done during navigating")
 SCENARIO("Test if the history buffer stores changes done during navigating")
 {
 {
-    GIVEN("An initialized Shellmatte instance with some commands already in the history buffer")
+    GIVEN("An initialized Shellmatta instance with some commands already in the history buffer")
     {
     {
         shellmatta_instance_t inst;
         shellmatta_instance_t inst;
         shellmatta_handle_t handle;
         shellmatta_handle_t handle;

+ 226 - 0
test/integrationtest/test_integration_ymodem.cpp

@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2021 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_integration_ymodem.cpp
+ * @brief   integration test implementation for the ymodem module of the shellmatta
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#include "test/framework/catch.hpp"
+extern "C" {
+    #include "shellmatta.h"
+}
+#include <string.h>
+
+static uint32_t write_callCnt = 0u;
+static char write_data[1024];
+static uint32_t write_length;
+static bool cancelled;
+static uint32_t receivedFileSize;
+static char *receivedFileName;
+static uint8_t *receivedPacketData;
+static uint32_t receivedPacketSize;
+static uint32_t receivedPacketNum;
+static bool transmissionCompleted;
+static shellmatta_retCode_t transmissionCompletedResult;
+
+static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
+{
+    write_callCnt ++;
+    while((length > 0) && (write_length < sizeof(write_data)))
+    {
+        write_data[write_length] = *data;
+        data ++;
+        length --;
+        write_length ++;
+    }
+
+    return SHELLMATTA_OK;
+}
+
+static void ymodemCallbackCancel(shellmatta_handle_t handle)
+{
+    (void)handle;
+    cancelled = true;
+}
+
+static void ymodemCallbackReceiveHeader(shellmatta_handle_t handle,
+                                        uint32_t fileSize,
+                                        char* fileName) {
+    
+    shellmatta_ymodem_pause(handle);
+
+    receivedFileSize = fileSize;
+    receivedFileName = fileName;
+}
+
+static void ymodemCallbackReceivePacket(shellmatta_handle_t handle,
+                                        uint8_t *data,
+                                        uint32_t packetSize,
+                                        uint32_t packetNum)
+{
+    (void)handle;
+    receivedPacketData = data;
+    receivedPacketSize = packetSize;
+    receivedPacketNum = packetNum;
+    return;
+}
+
+static void ymodemCallbackTransmissionComplete(shellmatta_handle_t handle, 
+                                               shellmatta_retCode_t result)
+{
+    (void)handle;
+    transmissionCompleted = true;
+    transmissionCompletedResult = result;
+}
+
+SCENARIO("Test successful transmissions in ymodem")
+{
+    GIVEN("An initialized and empty Shellmatta instance")
+    {
+        shellmatta_retCode_t ret;
+        shellmatta_instance_t inst;
+        shellmatta_handle_t handle;
+        char buffer[1024u];
+        char historyBuffer[1024u];
+
+        cancelled = false;
+        receivedFileSize = 0u;
+        receivedFileName = NULL;
+        receivedPacketData = NULL;
+        receivedPacketSize = 0u;
+        receivedPacketNum = 0u;
+        transmissionCompleted = false;
+        transmissionCompletedResult = SHELLMATTA_USE_FAULT;
+
+        CHECK(SHELLMATTA_OK == shellmatta_doInit(   &inst,
+                                                    &handle,
+                                                    buffer,
+                                                    sizeof(buffer),
+                                                    historyBuffer,
+                                                    sizeof(historyBuffer),
+                                                    "shellmatta->",
+                                                    NULL,
+                                                    writeFct));
+
+        WHEN("Starting a ymodem session and passing data.")
+        {
+            ret = shellmatta_ymodem_init(handle,
+                                         NULL,
+                                         ymodemCallbackCancel,
+                                         ymodemCallbackReceiveHeader,
+                                         ymodemCallbackReceivePacket,
+                                         ymodemCallbackTransmissionComplete);
+            CHECK(ret == SHELLMATTA_OK);
+
+            AND_WHEN("ymodem is polled")
+            {
+                write_length = 0u;
+                ret = shellmatta_processData(handle, (char *)"", 0);
+                CHECK(ret == SHELLMATTA_OK);
+
+                THEN("ymodem sends C starting character")
+                {
+                    CHECK(write_length == 1u);
+                    REQUIRE(write_data[0] == 'C');
+                }
+            }
+
+            AND_WHEN("Packet 0 is sent")
+            {
+                char packet0[1029u] = "\x02\x00\xFF" "Filename\0" "1044 \0";
+                packet0[1027u] = '\x55';
+                packet0[1028u] = '\x2E';
+                char packet1[133u] = "\x01\x01\xFE" "Some data...\0";
+                packet1[131u] = '\x09';
+                packet1[132u] = '\x75';
+                char packet2[1029u] = "\x02\x02\xFD" "Some additional data\0";
+                packet2[1027u] = '\xCD';
+                packet2[1028u] = '\xD4';
+
+                write_length = 0u;
+                ret = shellmatta_processData(handle, packet0, sizeof(packet0));
+                CHECK(ret == SHELLMATTA_OK);
+
+                CHECK(write_length == 0u);
+
+                ret = shellmatta_ymodem_resume(handle);
+                CHECK(ret == SHELLMATTA_OK);
+
+                THEN("ymodem calls the received header callback and sends an ACK")
+                {
+                    CHECK_THAT(receivedFileName, Catch::Matchers::Equals("Filename"));
+                    CHECK(receivedFileSize == 1044);
+
+                    CHECK(write_length == 2u);
+                    CHECK(write_data[0] == '\x06');
+                    REQUIRE(write_data[1] == 'C');
+
+                    AND_WHEN("The rest of the packets is sent")
+                    {
+                        write_length = 0u;
+                        ret = shellmatta_processData(handle, packet1, sizeof(packet1));
+                        CHECK(ret == SHELLMATTA_OK);
+
+                        CHECK_THAT((char *)receivedPacketData, Catch::Matchers::Equals("Some data..."));
+                        CHECK(receivedPacketSize == 128);
+                        CHECK(receivedPacketNum == 1);
+
+                        receivedPacketData = NULL;
+                        receivedPacketSize = 0u;
+                        receivedPacketNum = 0u;
+
+                        ret = shellmatta_processData(handle, packet2, sizeof(packet2));
+                        CHECK(ret == SHELLMATTA_OK);
+
+                        CHECK_THAT((char *)receivedPacketData, Catch::Matchers::Equals("Some additional data"));
+                        CHECK(receivedPacketSize == 20);
+                        CHECK(receivedPacketNum == 2);
+
+                        ret = shellmatta_processData(handle, (char*)"\x04", 1);
+                        CHECK(ret == SHELLMATTA_OK);
+
+                        ret = shellmatta_processData(handle, (char*)"\x04", 1);
+                        CHECK(ret == SHELLMATTA_OK);
+
+                        ret = shellmatta_processData(handle, (char*)"\x04", 1);
+                        CHECK(ret == SHELLMATTA_OK);
+
+                        CHECK(write_length == 8u);
+                        CHECK(write_data[0] == '\x06');
+                        CHECK(write_data[1] == '\x06');
+                        CHECK(write_data[2] == '\x06');
+                        CHECK(write_data[3] == 'C');
+                        CHECK(write_data[4] == '\x06');
+                        CHECK(write_data[5] == 'C');
+                        CHECK(write_data[6] == '\x06');
+                        CHECK(write_data[7] == 'C');
+
+                        write_length = 0u;
+                        memset(write_data, 0, sizeof(write_data));
+
+                        ret = shellmatta_processData(handle, (char*)"", 0);
+                        CHECK(ret == SHELLMATTA_OK);
+                        ret = shellmatta_processData(handle, (char*)"", 0);
+                        CHECK(ret == SHELLMATTA_OK);
+
+                        THEN("The transmission finishes successfully")
+                        {
+                            CHECK(write_length == 14);
+                            CHECK_THAT(write_data, Catch::Matchers::Equals("\r\nshellmatta->"));
+
+                            CHECK(transmissionCompleted == true);
+                            REQUIRE(transmissionCompletedResult == SHELLMATTA_OK);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 14 - 3
test/unittest/shellmatta/test_shellmatta_doInit.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_shellmatta_doInit.c
+ * @brief   unittest for shellmatta doInit function - currently not implemented
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta.c"
 #include "src/shellmatta.c"
 #include "src/shellmatta_transport.c"
 #include "src/shellmatta_transport.c"
@@ -7,10 +21,7 @@
 TEST_CASE( "shellmatta dummy" ) {
 TEST_CASE( "shellmatta dummy" ) {
 
 
     shellmatta_instance_t inst;
     shellmatta_instance_t inst;
-    //shellmatta_handle_t handle;
     inst.inputCount = 0u;
     inst.inputCount = 0u;
 
 
-    //shellmatta_doInit(&inst, &handle, )
-
     REQUIRE( inst.inputCount == 0u);
     REQUIRE( inst.inputCount == 0u);
 }
 }

+ 14 - 2
test/unittest/shellmatta_autocomplete/test_autocomplete_run.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_autodomplete_run.c
+ * @brief   unittest for shellmatta aurocomplete_run function - currently not implemented
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_autocomplete.c"
 #include "src/shellmatta_autocomplete.c"
 #include <string.h>
 #include <string.h>
@@ -7,7 +21,5 @@ TEST_CASE( "shellmatta_autocomplete_run dummy" ) {
     shellmatta_instance_t inst;
     shellmatta_instance_t inst;
     inst.inputCount = 0u;
     inst.inputCount = 0u;
 
 
-    autocomplete_run(&inst);
-
     REQUIRE( inst.inputCount == 0u);
     REQUIRE( inst.inputCount == 0u);
 }
 }

+ 35 - 0
test/unittest/shellmatta_crc/test_crc16Fast.cpp

@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_crc16Fast.h
+ * @brief   unittest implementation for crc16 fast function
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#include "test/framework/catch.hpp"
+#include <string.h>
+
+#define SHELLMATTA_TRANSPORT
+#undef SHELLMATTA_YMODEM_CRC_NO_LOOKUP
+#include "test_crc_data.h"
+#include "src/shellmatta_crc.c"
+
+TEST_CASE( "shellmatta_crc crc16Fast" ) {
+
+    uint32_t crc = crc16Fast((char*)"123456789", 9, crc16Table);
+
+    REQUIRE(crc == 0x31C3u);
+}
+
+TEST_CASE( "shellmatta_crc crc16Fast - more data" ) {
+
+    uint32_t crc = crc16Fast((char*)data, sizeof(data), crc16Table);
+
+    REQUIRE(crc == data_crc_16);
+}

+ 35 - 0
test/unittest/shellmatta_crc/test_crc16Slow.cpp

@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_crc16Slow.h
+ * @brief   unittest implementation for crc16 slow function
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#include "test/framework/catch.hpp"
+#include <string.h>
+
+#define SHELLMATTA_TRANSPORT
+#define SHELLMATTA_YMODEM_CRC_NO_LOOKUP
+#include "test_crc_data.h"
+#include "src/shellmatta_crc.c"
+
+TEST_CASE("shellmatta_crc crc16Slow" ) {
+
+    uint32_t crc = crc16Slow((char*)"123456789", 9);
+
+    REQUIRE(crc == 0x31C3u);
+}
+
+TEST_CASE("shellmatta_crc crc16Slow - more data") {
+
+    uint32_t crc = crc16Slow((char*)data, sizeof(data));
+
+    REQUIRE(crc == data_crc_16);
+}

+ 17 - 2
test/unittest/shellmatta_crc/test_crc32Fast.cpp

@@ -1,8 +1,23 @@
+/*
+ * Copyright (c) 2021 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_crc32Fast.h
+ * @brief   unittest implementation for crc32 fast function
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include <string.h>
 #include <string.h>
 
 
+#define SHELLMATTA_TRANSPORT
 #undef SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
 #undef SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
-#include "test_crc32_data.h"
+#include "test_crc_data.h"
 #include "src/shellmatta_crc.c"
 #include "src/shellmatta_crc.c"
 
 
 TEST_CASE( "shellmatta_crc crc32Fast" ) {
 TEST_CASE( "shellmatta_crc crc32Fast" ) {
@@ -16,5 +31,5 @@ TEST_CASE( "shellmatta_crc crc32Fast - more data" ) {
 
 
     uint32_t crc = crc32Fast((char*)data, sizeof(data), crc32Table);
     uint32_t crc = crc32Fast((char*)data, sizeof(data), crc32Table);
 
 
-    REQUIRE(crc == data_crc);
+    REQUIRE(crc == data_crc_32);
 }
 }

+ 17 - 2
test/unittest/shellmatta_crc/test_crc32Slow.cpp

@@ -1,8 +1,23 @@
+/*
+ * Copyright (c) 2021 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_crc16Slow.h
+ * @brief   unittest implementation for crc16 slow function
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include <string.h>
 #include <string.h>
 
 
+#define SHELLMATTA_TRANSPORT
 #define SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
 #define SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
-#include "test_crc32_data.h"
+#include "test_crc_data.h"
 #include "src/shellmatta_crc.c"
 #include "src/shellmatta_crc.c"
 
 
 TEST_CASE( "shellmatta_crc crc32Slow" ) {
 TEST_CASE( "shellmatta_crc crc32Slow" ) {
@@ -16,5 +31,5 @@ TEST_CASE( "shellmatta_crc crc32Slow - more data" ) {
 
 
     uint32_t crc = crc32Slow((char*)data, sizeof(data));
     uint32_t crc = crc32Slow((char*)data, sizeof(data));
 
 
-    REQUIRE(crc == data_crc);
+    REQUIRE(crc == data_crc_32);
 }
 }

+ 18 - 3
test/unittest/shellmatta_crc/test_crc32_data.h

@@ -1,5 +1,19 @@
-#ifndef _TEST_CRC32_DATA_H_
-#define _TEST_CRC32_DATA_H_
+/*
+ * Copyright (c) 2021 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_crc_data.h
+ * @brief   test data for the crc implementation
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#ifndef _TEST_CRC_DATA_H_
+#define _TEST_CRC_DATA_H_
 
 
 const uint8_t data[] = {
 const uint8_t data[] = {
     0xB8, 0xCA, 0xFD, 0x92, 0x55, 0x6A, 0x2F, 0x22, 0x5B, 0xB3, 0x0F, 0xEE, 0x69, 0x4E, 0x83, 0x4A,
     0xB8, 0xCA, 0xFD, 0x92, 0x55, 0x6A, 0x2F, 0x22, 0x5B, 0xB3, 0x0F, 0xEE, 0x69, 0x4E, 0x83, 0x4A,
@@ -260,6 +274,7 @@ const uint8_t data[] = {
     0x14, 0x21, 0x4B, 0xDE, 0x6D, 0xC4, 0xE9, 0xC9, 0x75, 0xBA, 0x9C, 0x2D, 0x14, 0xD4, 0xB2, 0xFF
     0x14, 0x21, 0x4B, 0xDE, 0x6D, 0xC4, 0xE9, 0xC9, 0x75, 0xBA, 0x9C, 0x2D, 0x14, 0xD4, 0xB2, 0xFF
 };
 };
 
 
-const uint32_t data_crc = 0x7322E75E;
+const uint32_t data_crc_16 = 0x5A57u;
+const uint32_t data_crc_32 = 0x7322E75Eu;
 
 
 #endif
 #endif

+ 15 - 1
test/unittest/shellmatta_escape/test_escape_processArrowKeys.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_escape_processArrowKeys.c
+ * @brief   unittest for shellmatta escape_processArrowKeys - currently not implemented
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_escape.c"
 #include "src/shellmatta_escape.c"
 #include <string.h>
 #include <string.h>
@@ -9,5 +23,5 @@ TEST_CASE( "shellmatta_escape dummy" ) {
 
 
     escape_processArrowKeys(&inst);
     escape_processArrowKeys(&inst);
 
 
-    REQUIRE( inst.inputCount == 0u);
+    REQUIRE(inst.inputCount == 0u);
 }
 }

+ 16 - 4
test/unittest/shellmatta_history/test_appendHistoryByte.cpp

@@ -1,13 +1,25 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_appendHistoryByte.c
+ * @brief   unittest for shellmatta appendHistoryByte - currently not implemented
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_history.c"
 #include "src/shellmatta_history.c"
 #include <string.h>
 #include <string.h>
 
 
-TEST_CASE( "shellmatta_history dummy" ) {
+TEST_CASE("shellmatta_history dummy" ) {
 
 
     shellmatta_instance_t inst;
     shellmatta_instance_t inst;
     inst.inputCount = 0u;
     inst.inputCount = 0u;
 
 
-    //appendHistoryByte(&inst, 'a');
-
-    REQUIRE( inst.inputCount == 0u);
+    REQUIRE(inst.inputCount == 0u);
 }
 }

+ 14 - 0
test/unittest/shellmatta_opt/test_opt_findNextHunk.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_opt_findNextHunk.c
+ * @brief   unittest for shellmatta opt_findNextHunk
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_opt.c"
 #include "src/shellmatta_opt.c"
 #include <string.h>
 #include <string.h>

+ 14 - 0
test/unittest/shellmatta_opt/test_opt_peekNextHunk.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_opt_peekNextHunk.c
+ * @brief   unittest for shellmatta opt_peekNextHunk
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_opt.c"
 #include "src/shellmatta_opt.c"
 #include <string.h>
 #include <string.h>

+ 14 - 0
test/unittest/shellmatta_utils/test_utils_clearInput.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_clearInput.c
+ * @brief   unittest for shellmatta utils_clearInput
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_utils.c"
 #include "src/shellmatta_utils.c"
 #include <string.h>
 #include <string.h>

+ 14 - 0
test/unittest/shellmatta_utils/test_utils_eraseLine.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_eraseLine.c
+ * @brief   unittest for shellmatta utils_eraseLine
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_utils.c"
 #include "src/shellmatta_utils.c"
 #include <string.h>
 #include <string.h>

+ 14 - 2
test/unittest/shellmatta_utils/test_utils_forwardCursor.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_forwardCursor.c
+ * @brief   unittest for shellmatta utils_forwardCursor
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_utils.c"
 #include "src/shellmatta_utils.c"
 #include <string.h>
 #include <string.h>
@@ -14,7 +28,6 @@ static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
     return SHELLMATTA_OK;
     return SHELLMATTA_OK;
 }
 }
 
 
-
 TEST_CASE( "shellmatta_utils_forwardCursor normal" ) {
 TEST_CASE( "shellmatta_utils_forwardCursor normal" ) {
 
 
     shellmatta_instance_t inst;
     shellmatta_instance_t inst;
@@ -63,7 +76,6 @@ TEST_CASE( "shellmatta_utils_forwardCursor normal echo off" ) {
     REQUIRE( strncmp("\0\0\0\0", write_data, 4u) == 0);
     REQUIRE( strncmp("\0\0\0\0", write_data, 4u) == 0);
 }
 }
 
 
-
 TEST_CASE( "shellmatta_utils_forwardCursor forward by 12 with cursor at 5 and input count at 10" ) {
 TEST_CASE( "shellmatta_utils_forwardCursor forward by 12 with cursor at 5 and input count at 10" ) {
 
 
     shellmatta_instance_t inst;
     shellmatta_instance_t inst;

+ 14 - 0
test/unittest/shellmatta_utils/test_utils_insertChars.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_insertChars.c
+ * @brief   unittest for shellmatta utils_insertChars
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_utils.c"
 #include "src/shellmatta_utils.c"
 #include <string.h>
 #include <string.h>

+ 14 - 0
test/unittest/shellmatta_utils/test_utils_removeChars.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_removeChars.c
+ * @brief   unittest for shellmatta utils_removeChars
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_utils.h"
 #include "src/shellmatta_utils.h"
 #include <string.h>
 #include <string.h>

+ 14 - 0
test/unittest/shellmatta_utils/test_utils_restoreCursorPos.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_restoreCursorPos.c
+ * @brief   unittest for shellmatta utils_restoreCursorPos
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_utils.c"
 #include "src/shellmatta_utils.c"
 #include <string.h>
 #include <string.h>

+ 14 - 0
test/unittest/shellmatta_utils/test_utils_rewindCursor.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_rewindCursor.c
+ * @brief   unittest for shellmatta utils_rewindCursor
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_utils.c"
 #include "src/shellmatta_utils.c"
 #include <string.h>
 #include <string.h>

+ 14 - 0
test/unittest/shellmatta_utils/test_utils_saveCursorPos.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_saveCursorPos.c
+ * @brief   unittest for shellmatta utils_saveCursorPos
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_utils.c"
 #include "src/shellmatta_utils.c"
 #include <string.h>
 #include <string.h>

+ 170 - 0
test/unittest/shellmatta_utils/test_utils_shellAsciiToUInt32.cpp

@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_shellAsciiToUInt32.c
+ * @brief   unittest for shellmatta utils_shellAsciiToUInt32
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_utils.c"
+#include <string.h>
+
+SCENARIO("Check utils_shellAsciiToUInt32 with valid strings")
+{
+    GIVEN("Initialized shellmatta utils module")
+    {
+        shellmatta_retCode_t ret;
+        uint32_t result = 0xFFFFFFFFu;
+        WHEN("Passing 123456789 with base 10")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"123456789", &result, 10u);
+            THEN("utils_shellAsciiToUInt32 returns successfully")
+            {
+                CHECK(ret == SHELLMATTA_OK);
+                REQUIRE(result == 123456789);
+            }
+        }
+        WHEN("Passing 4294967295 with base 10")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"4294967295", &result, 10u);
+            THEN("utils_shellAsciiToUInt32 returns successfully")
+            {
+                CHECK(ret == SHELLMATTA_OK);
+                REQUIRE(result == 4294967295);
+            }
+        }
+        WHEN("Passing 110100100001110011100011 with base 2")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"110100100001110011100011", &result, 2u);
+            THEN("utils_shellAsciiToUInt32 returns successfully")
+            {
+                CHECK(ret == SHELLMATTA_OK);
+                REQUIRE(result == 0b110100100001110011100011);
+            }
+        }
+        WHEN("Passing 435476600 with base 8")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"435476600", &result, 8u);
+            THEN("utils_shellAsciiToUInt32 returns successfully")
+            {
+                CHECK(ret == SHELLMATTA_OK);
+                REQUIRE(result == 0435476600);
+            }
+        }
+        WHEN("Passing AABBCCDD with base 16")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"AABBCCDD", &result, 16u);
+            THEN("utils_shellAsciiToUInt32 returns successfully")
+            {
+                CHECK(ret == SHELLMATTA_OK);
+                REQUIRE(result == 0xAABBCCDDu);
+            }
+        }
+        WHEN("Passing deadbeef with base 16")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"deadbeef", &result, 16u);
+            THEN("utils_shellAsciiToUInt32 returns successfully")
+            {
+                CHECK(ret == SHELLMATTA_OK);
+                REQUIRE(result == 0xdeadbeefu);
+            }
+        }
+        WHEN("Passing an empty string with base 16")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"", &result, 16u);
+            THEN("utils_shellAsciiToUInt32 returns successfully")
+            {
+                CHECK(ret == SHELLMATTA_OK);
+                REQUIRE(result == 0u);
+            }
+        }
+    }
+}
+
+SCENARIO("Check utils_shellAsciiToUInt32 with invalid strings")
+{
+    GIVEN("Initialized shellmatta utils module")
+    {
+        shellmatta_retCode_t ret;
+        uint32_t result = 0xFFFFFFFFu;
+        WHEN("Passing 4294967296 with base 10 - overflow")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"4294967296", &result, 10u);
+            THEN("utils_shellAsciiToUInt32 returns an error")
+            {
+                REQUIRE(ret == SHELLMATTA_ERROR);
+            }
+        }
+        WHEN("Passing 123456 with base 2 - wrong base")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"123456", &result, 2u);
+            THEN("utils_shellAsciiToUInt32 returns an error")
+            {
+                REQUIRE(ret == SHELLMATTA_ERROR);
+            }
+        }
+        WHEN("Passing FOOBAR with base 16 - wrong characters")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"FOOBAR", &result, 16u);
+            THEN("utils_shellAsciiToUInt32 returns an error")
+            {
+                REQUIRE(ret == SHELLMATTA_ERROR);
+            }
+        }
+        WHEN("Passing = with base 16 - wrong characters")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"=", &result, 16u);
+            THEN("utils_shellAsciiToUInt32 returns an error")
+            {
+                REQUIRE(ret == SHELLMATTA_ERROR);
+            }
+        }
+        WHEN("Passing / with base 16 - wrong characters")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"/", &result, 16u);
+            THEN("utils_shellAsciiToUInt32 returns an error")
+            {
+                REQUIRE(ret == SHELLMATTA_ERROR);
+            }
+        }
+        WHEN("Passing } with base 16 - wrong characters")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"}", &result, 16u);
+            THEN("utils_shellAsciiToUInt32 returns an error")
+            {
+                REQUIRE(ret == SHELLMATTA_ERROR);
+            }
+        }
+        WHEN("Passing invalid string")
+        {
+            ret = utils_shellAsciiToUInt32(NULL, &result, 16u);
+            THEN("utils_shellAsciiToUInt32 returns an error")
+            {
+                CHECK(ret == SHELLMATTA_USE_FAULT);
+            }
+        }
+        WHEN("Passing invalid result")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"", NULL, 16u);
+            THEN("utils_shellAsciiToUInt32 returns an error")
+            {
+                CHECK(ret == SHELLMATTA_USE_FAULT);
+            }
+        }
+        WHEN("Passing invalid base")
+        {
+            ret = utils_shellAsciiToUInt32((char*)"", &result, 17u);
+            THEN("utils_shellAsciiToUInt32 returns an error")
+            {
+                CHECK(ret == SHELLMATTA_USE_FAULT);
+            }
+        }
+    }
+}

+ 14 - 0
test/unittest/shellmatta_utils/test_utils_shellItoa.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_shellItoa.c
+ * @brief   unittest for shellmatta utils_shellItoa
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_utils.c"
 #include "src/shellmatta_utils.c"
 #include <string.h>
 #include <string.h>

+ 14 - 0
test/unittest/shellmatta_utils/test_utils_terminateInput.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_terminateInput.c
+ * @brief   unittest for shellmatta utils_terminateInput
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_utils.c"
 #include "src/shellmatta_utils.c"
 #include <string.h>
 #include <string.h>

+ 14 - 0
test/unittest/shellmatta_utils/test_utils_writeEcho.cpp

@@ -1,3 +1,17 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_utils_writeEcho.c
+ * @brief   unittest for shellmatta utils_writeEcho
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
 #include "src/shellmatta_utils.c"
 #include "src/shellmatta_utils.c"
 #include <string.h>
 #include <string.h>

+ 18 - 0
test/unittest/shellmatta_ymodem/test_ymodem.cpp

@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2019 - 2024 Stefan Strobel <stefan.strobel@shimatta.net>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * @file    test_ymodem.c
+ * @brief   unittest for shellmatta ymodem - currently not implemented
+ * @author  Stefan Strobel <stefan.strobel@shimatta.net>
+ */
+
+#include "test/framework/catch.hpp"
+#include "src/shellmatta_ymodem.c"
+
+/* empty unittest - just added to fix linking issues - no unittests for ymodem right now */

+ 0 - 1
test/unittest/test_main.cpp

@@ -4,4 +4,3 @@
 #define CATCH_CONFIG_MAIN
 #define CATCH_CONFIG_MAIN
 
 
 #include "test/framework/catch.hpp"
 #include "test/framework/catch.hpp"
-