Переглянути джерело

ymodem receive, ymodem crc, some utilities

Simon Fischer 2 роки тому
батько
коміт
bdbf00d709

+ 28 - 0
api/shellmatta.h

@@ -114,6 +114,23 @@ typedef struct shellmatta_cmd
     struct shellmatta_cmd   *next;      /**< pointer to next command or NULL        */
 } shellmatta_cmd_t;
 
+/** 
+ * @brief state enumeration for ymodem receive state machine 
+ */
+typedef enum
+{ 
+    INACTIVE,           /* YModem module not initialised */
+    WAIT_FOR_START,     /* waiting for start of header */
+    RECEIVE_PACKET,     /* currently receiving a packet */
+} shellmatta_ymodem_state_t;
+
+typedef struct
+{
+    void (*yModemCancelCallback)(void);
+    void (*yModemRecvPacketCallback)(void);
+    void (*ymodemTransmissionCompleteCallback)(void);
+} shellmatta_ymodem_callbacks_t;
+
 #ifdef SHELLMATTA_TRANSPORT_ENABLE
 
 /**
@@ -224,7 +241,9 @@ typedef struct
     bool                            cmdListIsConst;     /**< true if the #cmdList was passed during
                                                              initialization                         */
     shellmatta_opt_t                optionParser;       /**< option parser sructure                 */
+    shellmatta_ymodem_state_t       ymodemState;        /**< current state of the ymodem module     */
 #ifdef SHELLMATTA_TRANSPORT_ENABLE
+    bool                            transportEnabled;   /**< if true the transport layer is enabled */
     uint32_t                        transportBusyMark;  /**< transport processing position during 
                                                              busy cmd execution                     */
     shellmatta_transport_layer_t    transportLayer;     /**< transport layer instance               */
@@ -309,6 +328,15 @@ shellmatta_retCode_t shellmatta_printf(     shellmatta_handle_t handle,
                                             ...);
 #endif
 
+uint8_t shellmatta_ymodem(  shellmatta_handle_t             handle,
+                            uint8_t*                        buffer,
+                            uint16_t*                       fileSize,
+                            uint16_t*                       packetSize,
+                            shellmatta_ymodem_callbacks_t   callbacks);
+
+void shellmatta_ymodem_end( shellmatta_handle_t             handle,
+                            bool                            doCancel);
+
 #endif
 
 /** @} */

+ 2 - 1
makefile

@@ -22,7 +22,8 @@ SOURCES :=  src/shellmatta.c                \
             src/shellmatta_history.c        \
             src/shellmatta_utils.c          \
             src/shellmatta_escape.c         \
-            src/shellmatta_opt.c
+            src/shellmatta_opt.c            \
+            src/shellmatta_ymodem.c
 
 
 SOURCES_TRANPORT_LAYER  :=  $(SOURCES)                      \

+ 102 - 32
src/shellmatta.c

@@ -23,6 +23,7 @@
 #include "shellmatta_utils.h"
 #include "shellmatta_escape.h"
 #include "shellmatta_opt.h"
+#include "shellmatta_ymodem.h"
 #ifdef SHELLMATTA_TRANSPORT_ENABLE
 #include "shellmatta_transport.h"
 #endif
@@ -44,14 +45,15 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
                                                       char                    *data,
                                                       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;
+    shellmatta_ymodem_state_t   yModemState;
 
     /** -# in busy mode - keep calling this command */
     if(NULL != inst->busyCmd)
@@ -108,6 +110,12 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
         /** -# in continuous mode - pass data directly to the command */
         if(NULL != inst->continuousCmd)
         {
+            yModemState = shellmatta_ymodem_get_state(handle);
+            /* if data is coming in while ymodem module is active, forward data to it */
+            if (yModemState != INACTIVE)
+            {
+                shellmatta_ymodem_receive_packet(handle, data[inst->byteCounter]);
+            }
             /** -# copy data and call command function */
             inst->buffer[inst->stdinIdx]        = data[inst->byteCounter];
             inst->buffer[inst->stdinIdx + 1u]   = '\0';
@@ -123,8 +131,17 @@ static shellmatta_retCode_t shellmatta_processDataInt(shellmatta_handle_t     ha
             }
             else if(('\x03' == data[inst->byteCounter]))
             {
-                /** -# cancel continue session */
-                utils_terminateInput(inst);
+                /* accept cancel only if ymodem is inactive or between packets */
+                if (yModemState == INACTIVE || yModemState == WAIT_FOR_START)
+                {
+                    /** -# cancel continue session */
+                    utils_terminateInput(inst);
+                    if (yModemState == WAIT_FOR_START)
+                    {
+                        /** -# explicitly reset ymodem with cancel, if it was active */
+                        shellmatta_ymodem_reset(handle, true);
+                    }
+                }
                 ret = SHELLMATTA_OK;
             }
             else if(SHELLMATTA_CONTINUE == ret)
@@ -451,6 +468,7 @@ shellmatta_retCode_t shellmatta_doInit(
         inst->continuousCmd         = NULL;
         inst->busyCmd               = NULL;
         inst->cmdListIsConst        = false;
+        inst->ymodemState           = INACTIVE;
         shellmatta_opt_init(inst, 0u);
 
         /** -# copy the help command structure to this instance */
@@ -467,6 +485,7 @@ shellmatta_retCode_t shellmatta_doInit(
 
 #ifdef SHELLMATTA_TRANSPORT_ENABLE
         /* init transport layer */
+        inst->transportEnabled      = true;
         inst->transportBusyMark = 0u;
         shellmatta_transport_init(&inst->transportLayer, inst->write);
 #endif
@@ -515,6 +534,7 @@ shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle, bool pri
         inst->hereStartIdx          = 0u;
         inst->hereDelimiterIdx      = 0u;
         inst->hereLength            = 0u;
+        inst->ymodemState           = INACTIVE;
         shellmatta_opt_init(inst, 0u);
 
         if(true == printPrompt)
@@ -739,44 +759,51 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t     handle,
         &&  (SHELLMATTA_MAGIC   == inst->magic))
     {
 #ifdef SHELLMATTA_TRANSPORT_ENABLE
-        for (i = inst->transportBusyMark; i < size; i ++)
+        if (inst->transportEnabled)
         {
-            ret = shellmatta_transport_process(&inst->transportLayer, data[i], &tmpData, &tmpSize);
-            if (SHELLMATTA_OK == ret)
+            for (i = inst->transportBusyMark; i < size; i ++)
             {
-                ret = shellmatta_processDataInt(handle, tmpData, tmpSize);
-                processingDone = true;
+                ret = shellmatta_transport_process(&inst->transportLayer, data[i], &tmpData, &tmpSize);
+                if (SHELLMATTA_OK == ret)
+                {
+                    ret = shellmatta_processDataInt(handle, tmpData, tmpSize);
+                    processingDone = true;
 
-                if (SHELLMATTA_BUSY == ret)
+                    if (SHELLMATTA_BUSY == ret)
+                    {
+                        inst->transportBusyMark = i;
+                        break;
+                    }
+                    else
+                    {
+                        inst->transportBusyMark = 0u;
+                    }
+                }
+                else if (SHELLMATTA_ERROR == ret)
                 {
-                    inst->transportBusyMark = i;
-                    break;
+                    utils_writeEcho(inst, "crc error\r\n", 11);
+                    utils_terminateInput(inst);
                 }
                 else
                 {
-                    inst->transportBusyMark = 0u;
+                    /* nothing to do - transport layer busy */
                 }
             }
-            else if (SHELLMATTA_ERROR == ret)
+
+            /*! -# call the internal processing at least once - for continued and busy commands */
+            if (true != processingDone)
             {
-                utils_writeEcho(inst, "crc error\r\n", 11);
-                utils_terminateInput(inst);
+                ret = shellmatta_processDataInt(handle, tmpData, 0);
             }
-            else
+
+            if (false == inst->transportLayer.disableAutoFlush)
             {
-                /* nothing to do - transport layer busy */
+                (void)shellmatta_transport_flush(handle);
             }
         }
-
-        /*! -# call the internal processing at least once - for continued and busy commands */
-        if (true != processingDone)
-        {
-            ret = shellmatta_processDataInt(handle, tmpData, 0);
-        }
-
-        if (false == inst->transportLayer.disableAutoFlush)
+        else
         {
-            (void)shellmatta_transport_flush(handle);
+            ret = shellmatta_processDataInt(handle, data, size);
         }
 #else
         ret = shellmatta_processDataInt(handle, data, size);
@@ -896,4 +923,47 @@ shellmatta_retCode_t shellmatta_printf( shellmatta_handle_t handle,
 }
 #endif
 
+/** 
+ * @brief       starts the ymodem receive module 
+ * @param[in]   handle      shellmatta instance handle
+ * @param[in]   buffer      pointer to where the received data is stored
+ * @param[in]   fileSize    pointer to where the file size is stored
+ * @param[in]   packetSize  pointer to where the packet size is stored
+ * @param[in]   callbacks   callback functions for ymodem module
+ * @note        disables the transport layer if active
+*/
+uint8_t shellmatta_ymodem(  shellmatta_handle_t             handle,
+                            uint8_t*                        buffer,
+                            uint16_t*                       fileSize,
+                            uint16_t*                       packetSize,
+                            shellmatta_ymodem_callbacks_t   callbacks)
+{
+    if (shellmatta_ymodem_get_state(handle) == INACTIVE)
+    {
+#ifdef SHELLMATTA_TRANSPORT_ENABLE
+        /* disable transport layer so control symbols won't be caught by it */
+        ((shellmatta_instance_t*)handle)->transportEnabled = false;
+#endif
+        shellmatta_ymodem_init(handle, buffer, fileSize, packetSize, callbacks);
+
+        /* send initial ymodem symbol to start transmission */
+        shellmatta_ymodem_control(handle, YMODEM_CRC);
+        return 0;
+    }
+    else
+    {
+        return 1;
+    }
+}
+
+/**
+ * @brief       Resets the ymodem module
+ * @param[in]   doCancel    Set this flag to execute the cancel-callback function within the ymodem-reset function
+ * @note        call this function after file transmission is done or cancelled
+*/
+void shellmatta_ymodem_end(shellmatta_handle_t handle, bool doCancel)
+{
+    shellmatta_ymodem_reset(handle, doCancel);
+}
+
 /** @} */

+ 87 - 4
src/shellmatta_crc.c

@@ -52,6 +52,28 @@ uint32_t crc32Table[] = {
 };
 #endif
 
+#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
+};
+#endif
+
 #ifdef SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP
 /**
  * @brief       Reverses bits of a value.
@@ -69,9 +91,8 @@ static uint32_t reverse(uint32_t x, uint32_t 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]   size            amount of bytes to be processed
  * @return      crc             calculated crc32 value
@@ -130,8 +151,7 @@ static uint32_t crc32Fast(const char* data, uint32_t size, const uint32_t* looku
 
     return ~crcTemp;
 }
-#endif
-
+#endif /* SHELLMATTA_TRANSPORT_CRC_NO_LOOKUP */
 
 /**
  * @brief       Computes the crc32-checksum of a buffer.
@@ -147,3 +167,66 @@ uint32_t crc32Calc(const char* data, uint32_t size)
         return crc32Fast(data, size, crc32Table);
     #endif
 }
+
+#ifdef SHELLMATTA_YMODEM_CRC_NO_LOOKUP
+/**
+ * @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;
+    uint8_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);
+}
+
+#else
+/**
+ * @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 /* SHELLMATTA_YMODEM_CRC_NO_LOOKUP */
+
+/**
+ * @brief       Computes the crc16-checksum of a buffer.
+ * @param[in]   data            pointer to data buffer
+ * @param[in]   size            amount of bytes to be processed
+ * @return      crc             calculated crc16 value
+*/
+uint16_t crc16Calc(const char* data, uint32_t size)
+{
+    #ifdef SHELLMATTA_YMODEM_CRC_NO_LOOKUP
+        return crc16Slow(data, size);
+    #else
+        return crc16Fast(data, size, crc16Table);
+    #endif /* SHELLMATTA_YMODEM_CRC_NO_LOOKUP */
+}

+ 4 - 2
src/shellmatta_crc.h

@@ -17,9 +17,11 @@
 
 #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);
+uint16_t crc16Calc(const char* data, uint32_t size);
 
 #endif /* _SHELLMATTA_CRC_H_ */

+ 39 - 0
src/shellmatta_utils.c

@@ -85,6 +85,45 @@ uint32_t utils_shellItoa(int32_t value, char *buffer, uint32_t base)
     return bufferIdx;
 }
 
+/**
+ * @brief       turns a string of digits into an unsigned 32-bit integer
+ * @warning     length must match amount of digits
+ * @warning     will overflow if number is larger than UINT32_MAX
+ * @note        will return 0 in case of parameter error or invalid characters in string
+ * @param[in]   str     pointer to the string
+ * @param[in]   size    length of the string excluding null-terminator
+ * @return      the value of the string as unsigned 32-bit integer
+*/
+uint32_t utils_shellAsciiToUInt32(char* str, uint8_t size)
+{
+    uint8_t i, j;
+    char c;
+    uint32_t ret = 0u;
+    uint16_t pot = 0u;
+    uint32_t val = 0u;
+    /* parameter check */
+    if (size == 0u || str == (void*)0u) return ret;
+    for (i = 0; i < size; i++)
+    {
+        c = str[i];
+        /* check character for digit */
+        if (c >= 48 && c <= 57)
+        {
+            val = c - 48;
+        }
+        else return 0;
+        pot = size - i - 1;
+        /* compute val to the power of pot */
+        for (j = 0; j < pot; j++)
+        {
+            val *= 10;
+        }
+        /* add result to ret */
+        ret += val;
+    }
+    return ret;
+}
+
 /**
  * @brief       tells the terminal to save the current cursor position
  * @param[in]   inst    pointer to shellmatta instance

+ 34 - 17
src/shellmatta_utils.h

@@ -106,23 +106,40 @@ 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,
-                        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);
+
+uint32_t utils_shellAsciiToUInt32(  char* str, 
+                                    uint8_t size);
+
+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,
+                                    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
 

+ 537 - 0
src/shellmatta_ymodem.c

@@ -0,0 +1,537 @@
+/**
+ * @file    shellmatta_ymodem.c
+ * @brief   ymodem functions of shellmatta
+ * @author  Simon Fischer <dev@s-fischer.net>
+ */
+
+#include "shellmatta_ymodem.h"
+#include "shellmatta_crc.h"
+#include "shellmatta_utils.h"
+
+/* current ymodem packet size */
+uint16_t    yModemPacketSize = YMODEM_PACKET_SIZE;
+/* packet counter - counts all correctly received packets during one transmission */
+uint8_t     shellmatta_ymodem_packet_counter = 0u;
+/* counts total amount of data bytes received during one transmission */
+uint16_t    shellmatta_ymodem_total_bytes_received = 0u;
+/* counts total amount of data bytes received during one packet */
+uint16_t    shellmatta_ymodem_byte_counter = 0u;
+/* structure of a packet consisting of data and metadata */
+shellmatta_ymodem_packet_t shellmatta_ymodem_packet;
+/* holds information about which data type is currently actice: YMODEM_NONE, YMODEM_HEADER, YMODEM_BODY, YMODEM_FOOTER */
+shellmatta_ymodem_datatype_t shellmatta_ymodem_current_data_type = YMODEM_NONE;
+/* structure of ymodem callback functions */
+shellmatta_ymodem_callbacks_t shellmatta_ymodem_callbacks;
+
+/**
+ * @brief                       Initialise the ymodem prior to actually receiving data
+ * @param[in, out]  handle      shellmatta handle of the instance
+ * @param[in]       recvBuffer  pointer to the buffer to save the received payload in
+ * @param[in]       fileSize    pointer to the file size variable
+ * @param[in]       packetSize  pointer to the packet size variable
+ * @param[in]       callbacks   callback functions for the ymodem module
+*/
+void shellmatta_ymodem_init(shellmatta_handle_t handle, 
+                            uint8_t* recvBuffer, 
+                            uint16_t* fileSize,
+                            uint16_t* packetSize,
+                            shellmatta_ymodem_callbacks_t callbacks)
+{
+    if ((void*)0u == recvBuffer) return;
+    /* init packet structure */
+    shellmatta_ymodem_packet.packetNumber = 0u;
+    shellmatta_ymodem_packet.reversePacketNumber = 0u;
+    shellmatta_ymodem_packet.packetCrc = 0u;
+    shellmatta_ymodem_packet.packetSize = packetSize;
+    shellmatta_ymodem_packet.fileSize = fileSize;
+    shellmatta_ymodem_packet.packetData = recvBuffer;
+
+    /* init callbacks */
+    shellmatta_ymodem_callbacks = callbacks;
+
+    /* init ymodem state */
+    shellmatta_ymodem_set_state(handle, WAIT_FOR_START);
+
+    /* init current data type */
+    shellmatta_ymodem_current_data_type = YMODEM_NONE;
+}
+
+/**
+ * @brief                       change the current state of the ymodem module
+ * @param[in, out]  handle      shellmatta handle of the instance
+ * @param[in]       newState    the state the ymodem module should be changed to
+*/
+void shellmatta_ymodem_set_state(shellmatta_handle_t handle, shellmatta_ymodem_state_t newState)
+{
+    ((shellmatta_instance_t*)handle)->ymodemState = newState;
+}
+
+/**
+ * @brief                   Returns the current state of the ymodem module
+ * @param[in, out]  handle  shellmatta handle of the instance
+ * @return                  the current state of the ymodem module
+*/
+shellmatta_ymodem_state_t shellmatta_ymodem_get_state(shellmatta_handle_t handle)
+{
+    return ((shellmatta_instance_t*)handle)->ymodemState;
+}
+
+/**
+ * @brief                   State machine that processes bytewise input during active ymodem transmission
+ * @param[in, out]  handle  shellmatta handle of the instance
+ * @param[in]       byteIn  the byte to be processed
+ * @note                    won't do anything if ymodem module is not initialized
+*/
+void shellmatta_ymodem_receive_packet(shellmatta_handle_t handle, uint8_t byteIn)
+{
+    static uint8_t fileNameDelimiterPosition = 255u;
+    static uint8_t fileSizeDelimiterPosition = 255u;
+    static uint32_t fileSize = 0u;
+    static char fileSizeStr[7u]; /* hopefully no more bytes than a million will ever be transmitted */
+    shellmatta_ymodem_rcv_retcode_t recvRetCode;
+    recvRetCode = shellmatta_ymodem_receive_byte(handle, byteIn);
+    switch (shellmatta_ymodem_get_state(handle))
+    {
+    case INACTIVE:
+        /* cant do anything if ymodem module was not correctly initialized */
+        return;
+    case WAIT_FOR_START:
+        /* wait for start symbol of a packet */
+        switch (shellmatta_ymodem_current_data_type)
+        {
+        case YMODEM_NONE: /* go here if the header packet is to be expected */
+            if (recvRetCode == SOH_RECEIVED || recvRetCode == STX_RECEIVED)
+            {
+                /* reset packet counter */
+                shellmatta_ymodem_packet_counter = 0u;
+                shellmatta_ymodem_byte_counter = 0u;
+                shellmatta_printf(handle, "SOH receive\r\n");
+                shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
+                shellmatta_ymodem_current_data_type = YMODEM_HEADER;
+            }
+            break;
+        
+        case YMODEM_HEADER: /* go here if the first body packet is to be expected */
+            if (recvRetCode == SOH_RECEIVED || recvRetCode == STX_RECEIVED)
+            {
+                shellmatta_ymodem_byte_counter = 0u;
+                shellmatta_printf(handle, "SOH receive\r\n");
+                shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
+                shellmatta_ymodem_current_data_type = YMODEM_BODY;
+            }
+            break;
+
+        case YMODEM_BODY: /* go here if the data transmission loop is active */
+            if (recvRetCode == SOH_RECEIVED || recvRetCode == STX_RECEIVED)
+            {
+                shellmatta_ymodem_byte_counter = 0u;
+                /* stay in body when SOH was received */
+                shellmatta_printf(handle, "SOH receive\r\n");
+                shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
+                shellmatta_ymodem_current_data_type = YMODEM_BODY;
+            }
+            else /* go here if the end of transmission symbol is received */
+            if (recvRetCode == EOT_RECEIVED)
+            {
+                shellmatta_ymodem_byte_counter = 0u;
+                /* go to footer when EOT was received */
+                shellmatta_printf(handle, "EOT receive\r\n");
+                /* answer with ACK */
+                shellmatta_ymodem_ack(handle);
+                /* then send 0x43 symbol */
+                shellmatta_ymodem_control(handle, YMODEM_CRC);
+                /* then wait for SOH */
+                shellmatta_ymodem_current_data_type = YMODEM_FOOTER;
+            }
+            break;
+        case YMODEM_FOOTER: /* go here if the end of transmission packet is to be expected */
+            /* it _may_ be, that EOT is sent multiple times, repeat previous procedure */
+            if (recvRetCode == EOT_RECEIVED)
+            {
+                /* answer with ACK */
+                shellmatta_ymodem_ack(handle);
+                /* then send 0x43 symbol */
+                shellmatta_ymodem_control(handle, YMODEM_CRC);
+            }
+            if (recvRetCode == SOH_RECEIVED)
+            {
+                /* reset packet counter */
+                shellmatta_ymodem_packet_counter = 0u;
+                /* reset byte counter to avoid error by one caused by previously received EOT symbol */
+                shellmatta_ymodem_byte_counter = 0u;
+                shellmatta_printf(handle, "EOT packet starting\r\n");
+                shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
+            }
+            break;
+
+        default:
+            break;
+        }
+        break;
+    case RECEIVE_PACKET:
+        /* receiving data */
+        if (recvRetCode == DATA_RECEIVED)
+        {
+            switch (shellmatta_ymodem_current_data_type)
+            {
+            case YMODEM_HEADER:
+                switch (shellmatta_ymodem_byte_counter)
+                {
+                /* first two bytes are packet number and reverse packet number */
+                case 0:
+                    shellmatta_ymodem_packet.packetNumber = byteIn;
+                    shellmatta_printf(handle, "packet number = %u\r\n", shellmatta_ymodem_packet.packetNumber);
+                    break;
+                case 1:
+                    shellmatta_ymodem_packet.reversePacketNumber = byteIn;
+                    shellmatta_printf(handle, "reverse packet number = %u\r\n", shellmatta_ymodem_packet.reversePacketNumber);
+                    break;
+                default:
+                    break;
+                }
+                /* after packet numbers, data field begins */
+                if (shellmatta_ymodem_byte_counter >= YMODEM_HEADER_DATA_OFFSET && shellmatta_ymodem_byte_counter < YMODEM_HEADER_CRC_POSITION)
+                {
+                    shellmatta_ymodem_packet.packetData[shellmatta_ymodem_byte_counter - YMODEM_HEADER_DATA_OFFSET] = byteIn;
+                    /* check for filename */
+                    if (shellmatta_ymodem_byte_counter < fileNameDelimiterPosition)
+                    {
+                        /* find NULL-Terminator in filename */
+                        if (byteIn == YMODEM_NULL)
+                        {
+                            fileNameDelimiterPosition = shellmatta_ymodem_byte_counter + 1;
+                            shellmatta_printf(handle, "%s\r\n", shellmatta_ymodem_packet.packetData);
+                        }
+                    }
+                    /* after filename the filesize begins */
+                    else
+                    if (shellmatta_ymodem_byte_counter >= fileNameDelimiterPosition && shellmatta_ymodem_byte_counter < fileSizeDelimiterPosition)
+                    {
+                        /* find space as delimiter after filesize */
+                        if (byteIn == YMODEM_SPACE)
+                        {
+                            fileSizeDelimiterPosition = shellmatta_ymodem_byte_counter;
+                            /* convert file size string to actual number */
+                            fileSize = utils_shellAsciiToUInt32(fileSizeStr, fileSizeDelimiterPosition - fileNameDelimiterPosition);
+                            *shellmatta_ymodem_packet.fileSize = fileSize;
+                            shellmatta_printf(handle, "fileSize = %u\r\n", fileSize);
+                        }
+                        /* save characters in string otherwise */
+                        else
+                        {
+                            fileSizeStr[shellmatta_ymodem_byte_counter - fileNameDelimiterPosition] = byteIn;
+                        }
+                    }
+                    else
+                    {
+                        /* some meta data from sender, possibly file date */
+                        /* not needed for now */
+                    }
+                }
+                /* after data field, crc begins */
+                if (shellmatta_ymodem_byte_counter >= YMODEM_HEADER_CRC_POSITION)
+                {
+                    if (shellmatta_ymodem_byte_counter == YMODEM_HEADER_CRC_POSITION)
+                    {
+                        /* upper byte */
+                        shellmatta_ymodem_packet.packetCrc = byteIn << 8u;
+                    }
+                    if (shellmatta_ymodem_byte_counter == YMODEM_HEADER_CRC_POSITION + 1)
+                    {
+                        /* lower byte */
+                        shellmatta_ymodem_packet.packetCrc |= byteIn;
+                        /* change ymodem state to check packet */
+                        shellmatta_printf(handle, "packetCrc = 0x%04x\r\n", shellmatta_ymodem_packet.packetCrc);
+                        shellmatta_ymodem_check_packet(handle);
+                    }
+                }
+                shellmatta_ymodem_byte_counter++;
+                break;
+
+            case YMODEM_BODY:
+                switch (shellmatta_ymodem_byte_counter)
+                {
+                case 0:
+                    shellmatta_ymodem_packet.packetNumber = byteIn;
+                    shellmatta_printf(handle, "packet number = %u\r\n", shellmatta_ymodem_packet.packetNumber);
+                    break;
+                case 1:
+                    shellmatta_ymodem_packet.reversePacketNumber = byteIn;
+                    shellmatta_printf(handle, "reverse packet number = %u\r\n", shellmatta_ymodem_packet.reversePacketNumber);
+                    break;
+                default:
+                    break;
+                }
+
+                // TODO: Something might still be wrong here
+                /* after data field, crc begins */
+                if (shellmatta_ymodem_byte_counter > yModemPacketSize + 2)
+                {
+                    if (shellmatta_ymodem_byte_counter == yModemPacketSize + 3)
+                    {
+                        /* upper byte */
+                        shellmatta_ymodem_packet.packetCrc = byteIn << 8u;
+                    }
+                    if (shellmatta_ymodem_byte_counter == yModemPacketSize + 4)
+                    {
+                        /* lower byte */
+                        shellmatta_ymodem_packet.packetCrc |= byteIn;
+                        /* change ymodem state to check packet */
+                        shellmatta_printf(handle, "packetCrc = 0x%04x\r\n", shellmatta_ymodem_packet.packetCrc);
+                        shellmatta_ymodem_check_packet(handle);
+                    }
+                }
+                else
+                if (shellmatta_ymodem_byte_counter > 1)
+                {
+                    shellmatta_ymodem_packet.packetData[shellmatta_ymodem_byte_counter - YMODEM_HEADER_DATA_OFFSET] = byteIn;
+                }
+                shellmatta_ymodem_byte_counter++;
+                break;
+
+            case YMODEM_FOOTER:
+                /* reset packet counter */
+                shellmatta_ymodem_packet_counter = 0;
+
+                switch (shellmatta_ymodem_byte_counter)
+                {
+                case 0:
+                    shellmatta_ymodem_packet.packetNumber = byteIn;
+                    shellmatta_printf(handle, "packet number = %u\r\n", shellmatta_ymodem_packet.packetNumber);
+                    break;
+                case 1:
+                    shellmatta_ymodem_packet.reversePacketNumber = byteIn;
+                    shellmatta_printf(handle, "reverse packet number = %u\r\n", shellmatta_ymodem_packet.reversePacketNumber);
+                    break;
+                default:
+                    break;
+                }
+
+                if (shellmatta_ymodem_byte_counter == YMODEM_PACKET_SIZE)
+                {
+                    shellmatta_ymodem_check_packet(handle);
+                }
+                shellmatta_ymodem_byte_counter++;
+                break;
+            
+            default:
+                break;
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+/**
+ * @brief                   Gives a return code depending on symbol received at different states of the ymodem module
+ * @param[in, out]  handle  shellmatta handle of the instance
+ * @param[in]       byteIn  currently processed input byte
+*/
+shellmatta_ymodem_rcv_retcode_t shellmatta_ymodem_receive_byte(shellmatta_handle_t handle, uint8_t byteIn)
+{
+    shellmatta_ymodem_rcv_retcode_t ret;
+    if (WAIT_FOR_START == shellmatta_ymodem_get_state(handle))
+    {
+        switch (byteIn)
+        {
+        case YMODEM_SOH:
+            /* start of header -> default packet size */
+            yModemPacketSize = YMODEM_PACKET_SIZE;
+            ret = SOH_RECEIVED;
+            break;
+        
+        case YMODEM_STX:
+            /* start of header -> extended packet size */
+            yModemPacketSize = YMODEM_PACKET_SIZE_1K;
+            ret = STX_RECEIVED;
+            break;
+
+        case YMODEM_EOT:
+            /* end of transmission -> data transmission complete */
+            ret = EOT_RECEIVED;
+            break;
+        
+        case YMODEM_CA:
+            /* cancel transmssion -> reset state machine */
+            ret = CA_RECEIVED;
+            break;
+
+        default:
+            /* no other data should be received in this state */
+            ret = ERROR;
+            break;
+        }
+        /* give packet size back to upper layer */
+        *shellmatta_ymodem_packet.packetSize = yModemPacketSize;
+    }
+    else
+    if (RECEIVE_PACKET == shellmatta_ymodem_get_state(handle))
+    {
+        ret = DATA_RECEIVED;
+    }
+    return ret;
+}
+
+/**
+ * @brief                   Checks packet data for validity and sends ACK or NAK accordingly
+ * @param[in, out]  handle  shellmatta handle of the instance
+ * @note                    Only to be called after full packet is received
+ * @note                    Will call yModemRecvPacketCallback when packet is received correctly
+ * @note                    Will call ymodemTransmissionCompleteCallback when transmission is completed
+*/
+void shellmatta_ymodem_check_packet(shellmatta_handle_t handle)
+{
+    uint8_t computedCrc = 0u;
+    if (shellmatta_ymodem_packet.packetNumber != (0xFF - shellmatta_ymodem_packet.reversePacketNumber))
+    {
+        shellmatta_printf(handle, "wrong packet number pair\r\n");
+        shellmatta_ymodem_nak(handle);
+        return;
+    }
+    /* compare to internal packet counter */
+    if (shellmatta_ymodem_packet.packetNumber != shellmatta_ymodem_packet_counter)
+    {
+        shellmatta_printf(handle, "wrong packet number\r\n");
+        shellmatta_printf(handle, "expected %u, got %u\r\n", shellmatta_ymodem_packet_counter, shellmatta_ymodem_packet.packetNumber);
+        shellmatta_ymodem_nak(handle);
+        return;
+    }
+    /* compare packet crc only after other checks succeeded */
+    computedCrc = crc16Calc((const char*)shellmatta_ymodem_packet.packetData, yModemPacketSize);
+    //! uncomment for debug purposes
+    // if (shellmatta_ymodem_packet.packetCrc != computedCrc)
+    // {
+    //     shellmatta_printf(handle, "wrong packet crc: expected 0x%04x\r\n", computedCrc);
+    //     shellmatta_ymodem_nak(handle);
+    //     return;
+    // }
+    /* if previous checks passed, packet is acknowledged */
+    (void) computedCrc;
+    shellmatta_ymodem_total_bytes_received += shellmatta_ymodem_byte_counter;
+    shellmatta_ymodem_byte_counter = 0u;
+    shellmatta_ymodem_ack(handle);
+    if (shellmatta_ymodem_current_data_type == YMODEM_HEADER)
+    {
+        /* send additional CRC symbol if the packet was a header packet */
+        shellmatta_ymodem_control(handle, YMODEM_CRC);
+    }
+
+    /* callback handling */
+    switch (shellmatta_ymodem_current_data_type)
+    {
+    case YMODEM_NONE:
+        /* shouldn't happen */
+        break;
+    
+    case YMODEM_HEADER:
+        /* nothing to do after receiving the header packet */
+        break;
+
+    case YMODEM_BODY:
+        /* call the ymodem receive packet callback */
+        if (shellmatta_ymodem_callbacks.yModemRecvPacketCallback != (void*)0u)
+        {
+            shellmatta_ymodem_callbacks.yModemRecvPacketCallback();
+        }
+        break;
+
+    case YMODEM_FOOTER:
+        /* call the ymodem transmission complete callback */
+        if (shellmatta_ymodem_callbacks.ymodemTransmissionCompleteCallback != (void*)0u)
+        {
+            shellmatta_ymodem_callbacks.ymodemTransmissionCompleteCallback();
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+/**
+ * @brief                   Acknowledge handling. Will send ACK and set ymodem state back to WAIT_FOR_START
+ * @param[in, out]  handle  shellmatta handle of the instance
+*/
+void shellmatta_ymodem_ack(shellmatta_handle_t handle)
+{
+    shellmatta_ymodem_control(handle, YMODEM_ACK);
+    shellmatta_ymodem_packet_counter++;
+    shellmatta_ymodem_set_state(handle, WAIT_FOR_START);
+}
+
+/**
+ * @brief                   Not-Acknowledge handling. Will send NAK and set ymodem state back to WAIT_FOR_START
+ * @param[in, out]  handle  shellmatta handle of the instance
+*/
+void shellmatta_ymodem_nak(shellmatta_handle_t handle)
+{
+    shellmatta_ymodem_control(handle, YMODEM_NAK);
+    shellmatta_ymodem_set_state(handle, WAIT_FOR_START);
+    /* set back current data type to prevent false forward stepping */
+    switch (shellmatta_ymodem_current_data_type)
+    {
+    case YMODEM_NONE:
+        /* no handling needed */
+        break;
+    
+    case YMODEM_HEADER:
+        shellmatta_ymodem_current_data_type = YMODEM_NONE;
+        break;
+
+    case YMODEM_BODY:
+        /* YMODEM_BODY stays in YMODEM_BODY */
+        shellmatta_ymodem_current_data_type = YMODEM_BODY;
+        break;
+
+    case YMODEM_FOOTER:
+        /* YMODEM_FOOTER as well */
+        shellmatta_ymodem_current_data_type = YMODEM_FOOTER;
+        break;
+
+    default:
+        break;
+    }
+}
+
+void shellmatta_ymodem_control(shellmatta_handle_t handle, const char c)
+{
+    shellmatta_printf(handle, "0x%02x\r\n", c);
+    ((shellmatta_instance_t*)handle)->write(&c, 1);
+}
+
+/**
+ * @brief                       reset function for the ymodem module
+ * @param[in, out]  handle      shellmatta handle of the instance
+ * @param[in]       doCancel    flag to execute the cancel-callback
+*/
+void shellmatta_ymodem_reset(shellmatta_handle_t handle, bool doCancel)
+{
+    /* call cancel callback function */
+    if (doCancel)
+    {
+        if (shellmatta_ymodem_callbacks.yModemCancelCallback != (void*)0u)
+        {
+            shellmatta_ymodem_callbacks.yModemCancelCallback();
+        }
+    }
+
+    /* reset all ymodem_packet data */
+    shellmatta_ymodem_packet.packetNumber = 0u;
+    shellmatta_ymodem_packet.reversePacketNumber = 0u;
+    shellmatta_ymodem_packet.packetCrc = 0u;
+    shellmatta_ymodem_packet.packetSize = 0u;
+    shellmatta_ymodem_packet.fileSize = 0u;
+    shellmatta_ymodem_packet.packetData = 0u;
+
+    /* set ymodem to inactive */
+    shellmatta_ymodem_set_state(handle, INACTIVE);
+
+    shellmatta_ymodem_current_data_type = YMODEM_NONE;
+
+#ifdef SHELLMATTA_TRANSPORT_ENABLE
+    /* re-enable transport layer */
+    ((shellmatta_instance_t*)handle)->transportEnabled = true;
+#endif
+}

+ 90 - 0
src/shellmatta_ymodem.h

@@ -0,0 +1,90 @@
+/**
+ * @file    shellmatta_ymodem.h
+ * @brief   ymodem functions of shellmatta
+ * @author  Simon Fischer <dev@s-fischer.net>
+ */
+
+#ifndef _SHELLMATTA_YMODEM_H_
+#define _SHELLMATTA_YMODEM_H_
+
+#include <stdint.h>
+#include "shellmatta.h"
+
+#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_HEADER_DATA_OFFSET   2u      /* offset position of data field in header packet */
+#define YMODEM_HEADER_CRC_POSITION  130u    /* position of crc16 in header packet, relative to packet number */
+
+/** @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;
+
+/** @brief packet structure that holds several information about its content */
+typedef struct
+{
+    uint8_t packetNumber;           /* packet number in this packet */
+    uint8_t reversePacketNumber;    /* reverse packet number in this packet */
+    uint16_t packetCrc;             /* crc checksum in this packet */
+    uint8_t* packetData;            /* pointer to the data of this packet */
+    uint16_t* packetSize;           /* pointer to the length of this packet */
+    uint16_t* fileSize;             /* pointer to the amount of bytes in this packet */
+} shellmatta_ymodem_packet_t;
+
+/**
+ * @brief return codes for receive byte function of ymodem module
+*/
+typedef enum {
+    SOH_RECEIVED,
+    STX_RECEIVED,
+    EOT_RECEIVED,
+    CA_RECEIVED,
+    DATA_RECEIVED,
+    ERROR
+} shellmatta_ymodem_rcv_retcode_t;
+
+typedef enum {
+    YMODEM_NONE,   /* no data is transmitted */
+    YMODEM_HEADER, /* header packet is transmittet */
+    YMODEM_BODY,   /* data packet is transmitted */
+    YMODEM_FOOTER  /* end of transmission packet is transmitted */
+} shellmatta_ymodem_datatype_t;
+
+void shellmatta_ymodem_init(            shellmatta_handle_t             handle, 
+                                        uint8_t*                        recvBuffer, 
+                                        uint16_t*                       fileSize,
+                                        uint16_t*                       packetSize,
+                                        shellmatta_ymodem_callbacks_t   callbacks);
+
+void shellmatta_ymodem_receive_packet(  shellmatta_handle_t             handle, 
+                                        uint8_t                         byteIn);
+
+void shellmatta_ymodem_set_state(       shellmatta_handle_t             handle, 
+                                        shellmatta_ymodem_state_t       newState);
+
+shellmatta_ymodem_state_t shellmatta_ymodem_get_state(shellmatta_handle_t handle);
+
+shellmatta_ymodem_rcv_retcode_t shellmatta_ymodem_receive_byte( shellmatta_handle_t handle, 
+                                                                uint8_t             byteIn);
+
+void shellmatta_ymodem_control(         shellmatta_handle_t handle, 
+                                        const char          c);
+
+void shellmatta_ymodem_ack(             shellmatta_handle_t handle);
+
+void shellmatta_ymodem_nak(             shellmatta_handle_t handle);
+
+void shellmatta_ymodem_check_packet(    shellmatta_handle_t handle);
+
+void shellmatta_ymodem_reset(           shellmatta_handle_t handle,
+                                        bool                doCancel);
+
+#endif /* _SHELLMATTA_YMODEM_H_ */