Browse Source

Merge pull request #3264 from Steven-LiuSF/ymodem

[components]: utilities: ymodem: Add ymodem send protocol and ry_sy application.
Bernard Xiong 5 years ago
parent
commit
b9052a52e7

+ 7 - 0
components/utilities/Kconfig

@@ -2,8 +2,15 @@ menu "Utilities"
 
 
 config RT_USING_RYM
 config RT_USING_RYM
     bool "Enable Ymodem"
     bool "Enable Ymodem"
+    depends on RT_USING_DFS = y
     default n
     default n
 
 
+    if RT_USING_RYM
+        config YMODEM_DISABLE_CRC_TABLE
+        bool "Disable CRC Table"
+        default n
+    endif
+
 config RT_USING_ULOG
 config RT_USING_ULOG
     bool "Enable ulog"
     bool "Enable ulog"
     default n
     default n

+ 237 - 0
components/utilities/ymodem/ry_sy.c

@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2019-12-09     Steven Liu   the first version
+ */
+
+#include <rtthread.h>
+#include <ymodem.h>
+#include <dfs_posix.h>
+#include <stdlib.h>
+#include <board.h>
+#include <string.h>
+
+struct custom_ctx
+{
+    struct rym_ctx parent;
+    int fd;
+    int flen;
+    char fpath[DFS_PATH_MAX];
+};
+
+static enum rym_code _rym_recv_begin(
+    struct rym_ctx *ctx,
+    rt_uint8_t *buf,
+    rt_size_t len)
+{
+    struct custom_ctx *cctx = (struct custom_ctx *)ctx;
+
+    cctx->fpath[0] = '/';
+    rt_strncpy(&(cctx->fpath[1]), (const char *)buf, len - 1);
+    cctx->fd = open(cctx->fpath, O_CREAT | O_WRONLY | O_TRUNC, 0);
+    if (cctx->fd < 0)
+    {
+        rt_err_t err = rt_get_errno();
+        rt_kprintf("error creating file: %d\n", err);
+        return RYM_CODE_CAN;
+    }
+    cctx->flen = atoi((const char *)buf + rt_strnlen((const char *)buf, len - 1));
+    if (cctx->flen == 0)
+        cctx->flen = -1;
+
+    return RYM_CODE_ACK;
+}
+
+static enum rym_code _rym_recv_data(
+    struct rym_ctx *ctx,
+    rt_uint8_t *buf,
+    rt_size_t len)
+{
+    struct custom_ctx *cctx = (struct custom_ctx *)ctx;
+
+    RT_ASSERT(cctx->fd >= 0);
+    if (cctx->flen == -1)
+    {
+        write(cctx->fd, buf, len);
+    }
+    else
+    {
+        int wlen = len > cctx->flen ? cctx->flen : len;
+        write(cctx->fd, buf, wlen);
+        cctx->flen -= wlen;
+    }
+
+    return RYM_CODE_ACK;
+}
+
+static enum rym_code _rym_recv_end(
+    struct rym_ctx *ctx,
+    rt_uint8_t *buf,
+    rt_size_t len)
+{
+    struct custom_ctx *cctx = (struct custom_ctx *)ctx;
+
+    RT_ASSERT(cctx->fd >= 0);
+    close(cctx->fd);
+    cctx->fd = -1;
+
+    return RYM_CODE_ACK;
+}
+
+static enum rym_code _rym_send_begin(
+    struct rym_ctx *ctx,
+    rt_uint8_t *buf,
+    rt_size_t len)
+{
+    struct custom_ctx *cctx = (struct custom_ctx *)ctx;
+    struct stat file_buf;
+    char insert_0 = '\0';
+    rt_err_t err;
+
+    cctx->fd = open(cctx->fpath, O_RDONLY);
+    if (cctx->fd < 0)
+    {
+        err = rt_get_errno();
+        rt_kprintf("error open file: %d\n", err);
+        return RYM_ERR_FILE;
+    }
+    rt_memset(buf, 0, len);
+    err = stat(cctx->fpath, &file_buf);
+    if (err != RT_EOK)
+    {
+        rt_kprintf("error open file.\n");
+        return RYM_ERR_FILE;
+    }
+    sprintf((char *)buf, "%s%c%ld", (char *) & (cctx->fpath[1]), insert_0, file_buf.st_size);
+
+    return RYM_CODE_SOH;
+}
+
+static enum rym_code _rym_send_data(
+    struct rym_ctx *ctx,
+    rt_uint8_t *buf,
+    rt_size_t len)
+{
+    struct custom_ctx *cctx = (struct custom_ctx *)ctx;
+    rt_size_t read_size;
+    int retry_read;
+
+    read_size = 0;
+    for (retry_read = 0; retry_read < 10; retry_read++)
+    {
+        read_size += read(cctx->fd, buf + read_size, len - read_size);
+        if (read_size == len)
+            break;
+    }
+
+    if (read_size < len)
+    {
+        rt_memset(buf + read_size, 0x1A, len - read_size);
+        /* stage = RYM_STAGE_FINISHING */
+        ctx->stage = 4;
+    }
+
+    return RYM_CODE_SOH;
+}
+
+static enum rym_code _rym_send_end(
+    struct rym_ctx *ctx,
+    rt_uint8_t *buf,
+    rt_size_t len)
+{
+    rt_memset(buf, 0, len);
+
+    return RYM_CODE_SOH;
+}
+
+static rt_err_t rym_download_file(rt_device_t idev)
+{
+    rt_err_t res;
+    struct custom_ctx *ctx = rt_calloc(1, sizeof(*ctx));
+
+    if (!ctx)
+    {
+        rt_kprintf("rt_malloc failed\n");
+        return RT_ENOMEM;
+    }
+    ctx->fd = -1;
+    RT_ASSERT(idev);
+    res = rym_recv_on_device(&ctx->parent, idev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
+                             _rym_recv_begin, _rym_recv_data, _rym_recv_end, 1000);
+    rt_free(ctx);
+
+    return res;
+}
+
+static rt_err_t rym_upload_file(rt_device_t idev, const char *file_path)
+{
+    rt_err_t res = 0;
+
+    struct custom_ctx *ctx = rt_calloc(1, sizeof(*ctx));
+    if (!ctx)
+    {
+        rt_kprintf("rt_malloc failed\n");
+        return RT_ENOMEM;
+    }
+    ctx->fd = -1;
+    rt_strncpy(ctx->fpath, file_path, DFS_PATH_MAX);
+    RT_ASSERT(idev);
+    res = rym_send_on_device(&ctx->parent, idev,
+                             RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
+                             _rym_send_begin, _rym_send_data, _rym_send_end, 1000);
+    rt_free(ctx);
+
+    return res;
+}
+
+#ifdef RT_USING_FINSH
+#include <finsh.h>
+
+static rt_err_t ry(uint8_t argc, char **argv)
+{
+    rt_err_t res;
+    rt_device_t dev;
+
+    if (argc > 1)
+        dev = rt_device_find(argv[1]);
+    else
+        dev = rt_console_get_device();
+    if (!dev)
+    {
+        rt_kprintf("could not find device.\n");
+        return -RT_ERROR;
+    }
+    res = rym_download_file(dev);
+
+    return res;
+}
+MSH_CMD_EXPORT(ry, YMODEM Receive e.g: ry [uart0] default by console.);
+
+static rt_err_t sy(uint8_t argc, char **argv)
+{
+    rt_err_t res;
+    /* temporarily support 1 file*/
+    const char *file_path;
+    rt_device_t dev;
+
+    if (argc > 2)
+        dev = rt_device_find(argv[2]);
+    else
+        dev = rt_console_get_device();
+    if (!dev)
+    {
+        rt_kprintf("could not find device.\n");
+        return -RT_ERROR;
+    }
+    file_path = argv[1];
+    res = rym_upload_file(dev, file_path);
+
+    return res;
+}
+MSH_CMD_EXPORT(sy, YMODEM Send e.g: sy file_path [uart0] default by console.);
+
+#endif /* RT_USING_FINSH */

+ 354 - 47
components/utilities/ymodem/ymodem.c

@@ -7,12 +7,15 @@
  * Change Logs:
  * Change Logs:
  * Date           Author       Notes
  * Date           Author       Notes
  * 2013-04-14     Grissiom     initial implementation
  * 2013-04-14     Grissiom     initial implementation
+ * 2019-12-09     Steven Liu   add YMODEM send protocol
  */
  */
 
 
 #include <rthw.h>
 #include <rthw.h>
 #include "ymodem.h"
 #include "ymodem.h"
 
 
-static const rt_uint16_t ccitt_table[256] = {
+#ifndef YMODEM_DISABLE_CRC_TABLE
+static const rt_uint16_t ccitt_table[256] =
+{
     0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
     0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
     0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
     0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
     0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
     0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
@@ -46,7 +49,7 @@ static const rt_uint16_t ccitt_table[256] = {
     0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
     0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
     0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
     0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
 };
 };
-rt_uint16_t CRC16(unsigned char *q, int len)
+static rt_uint16_t CRC16(unsigned char *q, int len)
 {
 {
     rt_uint16_t crc = 0;
     rt_uint16_t crc = 0;
 
 
@@ -54,6 +57,30 @@ rt_uint16_t CRC16(unsigned char *q, int len)
         crc = (crc << 8) ^ ccitt_table[((crc >> 8) ^ *q++) & 0xff];
         crc = (crc << 8) ^ ccitt_table[((crc >> 8) ^ *q++) & 0xff];
     return crc;
     return crc;
 }
 }
+#else
+static rt_uint16_t CRC16(unsigned char *q, int len)
+{
+    rt_uint16_t crc;
+    char i;
+
+    crc = 0;
+    while (--len >= 0)
+    {
+        crc = crc ^ (int) * q++ << 8;
+        i = 8;
+        do
+        {
+            if (crc & 0x8000)
+                crc = crc << 1 ^ 0x1021;
+            else
+                crc = crc << 1;
+        }
+        while (--i);
+    }
+
+    return (crc);
+}
+#endif
 
 
 // we could only use global varible because we could not use
 // we could only use global varible because we could not use
 // rt_device_t->user_data(it is used by the serial driver)...
 // rt_device_t->user_data(it is used by the serial driver)...
@@ -69,15 +96,16 @@ static rt_err_t _rym_rx_ind(rt_device_t dev, rt_size_t size)
 #define _RYM_STX_PKG_SZ (1+2+1024+2)
 #define _RYM_STX_PKG_SZ (1+2+1024+2)
 
 
 static enum rym_code _rym_read_code(
 static enum rym_code _rym_read_code(
-        struct rym_ctx *ctx,
-        rt_tick_t timeout)
+    struct rym_ctx *ctx,
+    rt_tick_t timeout)
 {
 {
     /* Fast path */
     /* Fast path */
     if (rt_device_read(ctx->dev, 0, ctx->buf, 1) == 1)
     if (rt_device_read(ctx->dev, 0, ctx->buf, 1) == 1)
         return (enum rym_code)(*ctx->buf);
         return (enum rym_code)(*ctx->buf);
 
 
     /* Slow path */
     /* Slow path */
-    do {
+    do
+    {
         rt_size_t rsz;
         rt_size_t rsz;
 
 
         /* No data yet, wait for one */
         /* No data yet, wait for one */
@@ -88,13 +116,14 @@ static enum rym_code _rym_read_code(
         rsz = rt_device_read(ctx->dev, 0, ctx->buf, 1);
         rsz = rt_device_read(ctx->dev, 0, ctx->buf, 1);
         if (rsz == 1)
         if (rsz == 1)
             return (enum rym_code)(*ctx->buf);
             return (enum rym_code)(*ctx->buf);
-    } while (1);
+    }
+    while (1);
 }
 }
 
 
 /* the caller should at least alloc _RYM_STX_PKG_SZ buffer */
 /* the caller should at least alloc _RYM_STX_PKG_SZ buffer */
 static rt_size_t _rym_read_data(
 static rt_size_t _rym_read_data(
-        struct rym_ctx *ctx,
-        rt_size_t len)
+    struct rym_ctx *ctx,
+    rt_size_t len)
 {
 {
     /* we should already have had the code */
     /* we should already have had the code */
     rt_uint8_t *buf = ctx->buf + 1;
     rt_uint8_t *buf = ctx->buf + 1;
@@ -103,23 +132,61 @@ static rt_size_t _rym_read_data(
     do
     do
     {
     {
         readlen += rt_device_read(ctx->dev,
         readlen += rt_device_read(ctx->dev,
-                0, buf+readlen, len-readlen);
+                                  0, buf + readlen, len - readlen);
         if (readlen >= len)
         if (readlen >= len)
             return readlen;
             return readlen;
-    } while (rt_sem_take(&ctx->sem, RYM_WAIT_CHR_TICK) == RT_EOK);
+    }
+    while (rt_sem_take(&ctx->sem, RYM_WAIT_CHR_TICK) == RT_EOK);
 
 
     return readlen;
     return readlen;
 }
 }
 
 
+static rt_err_t _rym_send_packet(
+    struct rym_ctx *ctx,
+    enum rym_code code,
+    rt_uint8_t index)
+{
+    rt_uint16_t send_crc;
+    rt_uint8_t index_inv = ~index;
+    rt_size_t writelen = 0;
+
+    send_crc = CRC16(ctx->buf + 3, _RYM_SOH_PKG_SZ - 5);
+    ctx->buf[0] = code;
+    ctx->buf[1] = index;
+    ctx->buf[2] = index_inv;
+    ctx->buf[131] = (rt_uint8_t)(send_crc >> 8);
+    ctx->buf[132] = (rt_uint8_t)send_crc & 0xff;
+
+    do
+    {
+        writelen += rt_device_write(ctx->dev, 0, ctx->buf + writelen,
+                                    _RYM_SOH_PKG_SZ - writelen);
+    }
+    while (writelen < _RYM_SOH_PKG_SZ);
+
+    return RT_EOK;
+}
+
 static rt_size_t _rym_putchar(struct rym_ctx *ctx, rt_uint8_t code)
 static rt_size_t _rym_putchar(struct rym_ctx *ctx, rt_uint8_t code)
 {
 {
     rt_device_write(ctx->dev, 0, &code, sizeof(code));
     rt_device_write(ctx->dev, 0, &code, sizeof(code));
     return 1;
     return 1;
 }
 }
 
 
+static rt_size_t _rym_getchar(struct rym_ctx *ctx)
+{
+    rt_uint8_t getc_ack;
+
+    while (rt_device_read(ctx->dev, 0, &getc_ack, 1) != 1)
+    {
+        rt_sem_take(&ctx->sem, RT_WAITING_FOREVER);
+    }
+    return getc_ack;
+}
+
 static rt_err_t _rym_do_handshake(
 static rt_err_t _rym_do_handshake(
-        struct rym_ctx *ctx,
-        int tm_sec)
+    struct rym_ctx *ctx,
+    int tm_sec)
 {
 {
     enum rym_code code;
     enum rym_code code;
     rt_size_t i;
     rt_size_t i;
@@ -133,13 +200,13 @@ static rt_err_t _rym_do_handshake(
     {
     {
         _rym_putchar(ctx, RYM_CODE_C);
         _rym_putchar(ctx, RYM_CODE_C);
         code = _rym_read_code(ctx,
         code = _rym_read_code(ctx,
-                RYM_CHD_INTV_TICK);
+                              RYM_CHD_INTV_TICK);
         if (code == RYM_CODE_SOH)
         if (code == RYM_CODE_SOH)
         {
         {
             data_sz = _RYM_SOH_PKG_SZ;
             data_sz = _RYM_SOH_PKG_SZ;
             break;
             break;
         }
         }
-        else if(code == RYM_CODE_STX)
+        else if (code == RYM_CODE_STX)
         {
         {
             data_sz = _RYM_STX_PKG_SZ;
             data_sz = _RYM_STX_PKG_SZ;
             break;
             break;
@@ -154,7 +221,7 @@ static rt_err_t _rym_do_handshake(
     i = 0;
     i = 0;
     /* automatic exit after receiving specified length data, timeout: 100ms */
     /* automatic exit after receiving specified length data, timeout: 100ms */
     tick = rt_tick_get();
     tick = rt_tick_get();
-    while (rt_tick_get() <= (tick + rt_tick_from_millisecond(100)) && i < (data_sz-1))
+    while (rt_tick_get() <= (tick + rt_tick_from_millisecond(100)) && i < (data_sz - 1))
     {
     {
         i += _rym_read_data(ctx, data_sz - 1);
         i += _rym_read_data(ctx, data_sz - 1);
         rt_thread_mdelay(5);
         rt_thread_mdelay(5);
@@ -167,24 +234,79 @@ static rt_err_t _rym_do_handshake(
     if (ctx->buf[1] != 0 || ctx->buf[2] != 0xFF)
     if (ctx->buf[1] != 0 || ctx->buf[2] != 0xFF)
         return -RYM_ERR_SEQ;
         return -RYM_ERR_SEQ;
 
 
-    recv_crc = (rt_uint16_t)(*(ctx->buf+data_sz-2) << 8) | *(ctx->buf+data_sz-1);
-    cal_crc = CRC16(ctx->buf+3, data_sz-5);
+    recv_crc = (rt_uint16_t)(*(ctx->buf + data_sz - 2) << 8) | *(ctx->buf + data_sz - 1);
+    cal_crc = CRC16(ctx->buf + 3, data_sz - 5);
     if (recv_crc != cal_crc)
     if (recv_crc != cal_crc)
         return -RYM_ERR_CRC;
         return -RYM_ERR_CRC;
 
 
     /* congratulations, check passed. */
     /* congratulations, check passed. */
-    if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf+3, data_sz-5) != RYM_CODE_ACK)
+    if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_ACK)
         return -RYM_ERR_CAN;
         return -RYM_ERR_CAN;
 
 
     return RT_EOK;
     return RT_EOK;
 }
 }
 
 
+static rt_err_t _rym_do_send_handshake(
+    struct rym_ctx *ctx,
+    int tm_sec)
+{
+    enum rym_code code;
+    rt_size_t i;
+    rt_size_t data_sz;
+    rt_uint8_t index = 0;
+    rt_uint8_t getc_ack;
+
+    ctx->stage = RYM_STAGE_ESTABLISHING;
+    data_sz = _RYM_SOH_PKG_SZ;
+
+    /* receive C every second */
+    for (i = 0; i < tm_sec; i++)
+    {
+        code = _rym_read_code(ctx,
+                              RYM_CHD_INTV_TICK);
+        if (code == RYM_CODE_C)
+        {
+            break;
+        }
+    }
+    if (i == tm_sec)
+    {
+        return -RYM_ERR_TMO;
+    }
+
+    /* congratulations, check passed. */
+    if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_SOH)
+        return -RYM_ERR_CODE;
+
+    code = RYM_CODE_SOH;
+    _rym_send_packet(ctx, code, index);
+
+    rt_device_set_rx_indicate(ctx->dev, _rym_rx_ind);
+    getc_ack = _rym_getchar(ctx);
+
+    if (getc_ack != RYM_CODE_ACK)
+    {
+        return -RYM_ERR_ACK;
+    }
+
+    getc_ack = _rym_getchar(ctx);
+
+    if (getc_ack != RYM_CODE_C)
+    {
+        return -RYM_ERR_ACK;
+    }
+
+    ctx->stage = RYM_STAGE_ESTABLISHED;
+
+    return RT_EOK;
+}
+
 static rt_err_t _rym_trans_data(
 static rt_err_t _rym_trans_data(
-        struct rym_ctx *ctx,
-        rt_size_t data_sz,
-        enum rym_code *code)
+    struct rym_ctx *ctx,
+    rt_size_t data_sz,
+    enum rym_code *code)
 {
 {
-    const rt_size_t tsz = 2+data_sz+2;
+    const rt_size_t tsz = 2 + data_sz + 2;
     rt_uint16_t recv_crc;
     rt_uint16_t recv_crc;
 
 
     /* seq + data + crc */
     /* seq + data + crc */
@@ -210,13 +332,13 @@ static rt_err_t _rym_trans_data(
     ctx->stage = RYM_STAGE_TRANSMITTING;
     ctx->stage = RYM_STAGE_TRANSMITTING;
 
 
     /* sanity check */
     /* sanity check */
-    recv_crc = (rt_uint16_t)(*(ctx->buf+tsz-1) << 8) | *(ctx->buf+tsz);
-    if (recv_crc != CRC16(ctx->buf+3, data_sz))
+    recv_crc = (rt_uint16_t)(*(ctx->buf + tsz - 1) << 8) | *(ctx->buf + tsz);
+    if (recv_crc != CRC16(ctx->buf + 3, data_sz))
         return -RYM_ERR_CRC;
         return -RYM_ERR_CRC;
 
 
     /* congratulations, check passed. */
     /* congratulations, check passed. */
     if (ctx->on_data)
     if (ctx->on_data)
-        *code = ctx->on_data(ctx, ctx->buf+3, data_sz);
+        *code = ctx->on_data(ctx, ctx->buf + 3, data_sz);
     else
     else
         *code = RYM_CODE_ACK;
         *code = RYM_CODE_ACK;
     return RT_EOK;
     return RT_EOK;
@@ -235,7 +357,7 @@ static rt_err_t _rym_do_trans(struct rym_ctx *ctx)
         rt_size_t data_sz, i;
         rt_size_t data_sz, i;
 
 
         code = _rym_read_code(ctx,
         code = _rym_read_code(ctx,
-                RYM_WAIT_PKG_TICK);
+                              RYM_WAIT_PKG_TICK);
         switch (code)
         switch (code)
         {
         {
         case RYM_CODE_SOH:
         case RYM_CODE_SOH:
@@ -257,7 +379,8 @@ static rt_err_t _rym_do_trans(struct rym_ctx *ctx)
         {
         {
         case RYM_CODE_CAN:
         case RYM_CODE_CAN:
             /* the spec require multiple CAN */
             /* the spec require multiple CAN */
-            for (i = 0; i < RYM_END_SESSION_SEND_CAN_NUM; i++) {
+            for (i = 0; i < RYM_END_SESSION_SEND_CAN_NUM; i++)
+            {
                 _rym_putchar(ctx, RYM_CODE_CAN);
                 _rym_putchar(ctx, RYM_CODE_CAN);
             }
             }
             return -RYM_ERR_CAN;
             return -RYM_ERR_CAN;
@@ -271,6 +394,41 @@ static rt_err_t _rym_do_trans(struct rym_ctx *ctx)
     }
     }
 }
 }
 
 
+static rt_err_t _rym_do_send_trans(struct rym_ctx *ctx)
+{
+    ctx->stage = RYM_STAGE_TRANSMITTING;
+    enum rym_code code;
+    rt_size_t data_sz;
+    rt_uint32_t index = 1;
+    rt_uint8_t getc_ack;
+
+    data_sz = _RYM_SOH_PKG_SZ;
+
+    while (1)
+    {
+        if (ctx->on_data && ctx->on_data(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_SOH)
+            return -RYM_ERR_CODE;
+
+        code = RYM_CODE_SOH;
+
+        _rym_send_packet(ctx, code, index);
+        index++;
+        rt_device_set_rx_indicate(ctx->dev, _rym_rx_ind);
+
+        getc_ack = _rym_getchar(ctx);
+
+        if (getc_ack != RYM_CODE_ACK)
+        {
+            return -RYM_ERR_ACK;
+        }
+
+        if (ctx->stage == RYM_STAGE_FINISHING)
+            break;
+    }
+
+    return RT_EOK;
+}
+
 static rt_err_t _rym_do_fin(struct rym_ctx *ctx)
 static rt_err_t _rym_do_fin(struct rym_ctx *ctx)
 {
 {
     enum rym_code code;
     enum rym_code code;
@@ -282,7 +440,7 @@ static rt_err_t _rym_do_fin(struct rym_ctx *ctx)
     /* we already got one EOT in the caller. invoke the callback if there is
     /* we already got one EOT in the caller. invoke the callback if there is
      * one. */
      * one. */
     if (ctx->on_end)
     if (ctx->on_end)
-        ctx->on_end(ctx, ctx->buf+3, 128);
+        ctx->on_end(ctx, ctx->buf + 3, 128);
 
 
     _rym_putchar(ctx, RYM_CODE_NAK);
     _rym_putchar(ctx, RYM_CODE_NAK);
     code = _rym_read_code(ctx, RYM_WAIT_PKG_TICK);
     code = _rym_read_code(ctx, RYM_WAIT_PKG_TICK);
@@ -297,15 +455,15 @@ static rt_err_t _rym_do_fin(struct rym_ctx *ctx)
     {
     {
         data_sz = _RYM_SOH_PKG_SZ;
         data_sz = _RYM_SOH_PKG_SZ;
     }
     }
-    else if(code == RYM_CODE_STX)
+    else if (code == RYM_CODE_STX)
     {
     {
         data_sz = _RYM_STX_PKG_SZ;
         data_sz = _RYM_STX_PKG_SZ;
     }
     }
     else
     else
         return -RYM_ERR_CODE;
         return -RYM_ERR_CODE;
 
 
-    i = _rym_read_data(ctx, _RYM_SOH_PKG_SZ-1);
-    if (i != (_RYM_SOH_PKG_SZ-1))
+    i = _rym_read_data(ctx, _RYM_SOH_PKG_SZ - 1);
+    if (i != (_RYM_SOH_PKG_SZ - 1))
         return -RYM_ERR_DSZ;
         return -RYM_ERR_DSZ;
 
 
     /* sanity check
     /* sanity check
@@ -313,14 +471,14 @@ static rt_err_t _rym_do_fin(struct rym_ctx *ctx)
     if (ctx->buf[1] != 0 || ctx->buf[2] != 0xFF)
     if (ctx->buf[1] != 0 || ctx->buf[2] != 0xFF)
         return -RYM_ERR_SEQ;
         return -RYM_ERR_SEQ;
 
 
-    recv_crc = (rt_uint16_t)(*(ctx->buf+_RYM_SOH_PKG_SZ-2) << 8) | *(ctx->buf+_RYM_SOH_PKG_SZ-1);
-    if (recv_crc != CRC16(ctx->buf+3, _RYM_SOH_PKG_SZ-5))
+    recv_crc = (rt_uint16_t)(*(ctx->buf + _RYM_SOH_PKG_SZ - 2) << 8) | *(ctx->buf + _RYM_SOH_PKG_SZ - 1);
+    if (recv_crc != CRC16(ctx->buf + 3, _RYM_SOH_PKG_SZ - 5))
         return -RYM_ERR_CRC;
         return -RYM_ERR_CRC;
 
 
     /*next file transmission*/
     /*next file transmission*/
     if (ctx->buf[3] != 0)
     if (ctx->buf[3] != 0)
     {
     {
-        if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf+3, data_sz-5) != RYM_CODE_ACK)
+        if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_ACK)
             return -RYM_ERR_CAN;
             return -RYM_ERR_CAN;
         return RT_EOK;
         return RT_EOK;
     }
     }
@@ -334,9 +492,54 @@ static rt_err_t _rym_do_fin(struct rym_ctx *ctx)
     return RT_EOK;
     return RT_EOK;
 }
 }
 
 
+static rt_err_t _rym_do_send_fin(struct rym_ctx *ctx)
+{
+    enum rym_code code;
+    rt_size_t data_sz;
+    rt_uint8_t index = 0;
+    rt_uint8_t getc_ack;
+
+    data_sz = _RYM_SOH_PKG_SZ;
+    rt_device_set_rx_indicate(ctx->dev, _rym_rx_ind);
+
+    _rym_putchar(ctx, RYM_CODE_EOT);
+    getc_ack = _rym_getchar(ctx);
+
+    if (getc_ack != RYM_CODE_NAK)
+    {
+        return -RYM_ERR_ACK;
+    }
+
+    _rym_putchar(ctx, RYM_CODE_EOT);
+    getc_ack = _rym_getchar(ctx);
+
+    if (getc_ack != RYM_CODE_ACK)
+    {
+        return -RYM_ERR_ACK;
+    }
+
+    getc_ack = _rym_getchar(ctx);
+
+    if (getc_ack != RYM_CODE_C)
+    {
+        return -RYM_ERR_ACK;
+    }
+
+    if (ctx->on_end && ctx->on_end(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_SOH)
+        return -RYM_ERR_CODE;
+
+    code = RYM_CODE_SOH;
+
+    _rym_send_packet(ctx, code, index);
+
+    ctx->stage = RYM_STAGE_FINISHED;
+
+    return RT_EOK;
+}
+
 static rt_err_t _rym_do_recv(
 static rt_err_t _rym_do_recv(
-        struct rym_ctx *ctx,
-        int handshake_timeout)
+    struct rym_ctx *ctx,
+    int handshake_timeout)
 {
 {
     rt_err_t err;
     rt_err_t err;
 
 
@@ -348,30 +551,78 @@ static rt_err_t _rym_do_recv(
 
 
     err = _rym_do_handshake(ctx, handshake_timeout);
     err = _rym_do_handshake(ctx, handshake_timeout);
     if (err != RT_EOK)
     if (err != RT_EOK)
+    {
+        rt_free(ctx->buf);
         return err;
         return err;
+    }
     while (1)
     while (1)
     {
     {
         err = _rym_do_trans(ctx);
         err = _rym_do_trans(ctx);
         if (err != RT_EOK)
         if (err != RT_EOK)
+        {
+            rt_free(ctx->buf);
             return err;
             return err;
+        }
 
 
         err = _rym_do_fin(ctx);
         err = _rym_do_fin(ctx);
         if (err != RT_EOK)
         if (err != RT_EOK)
+        {
+            rt_free(ctx->buf);
             return err;
             return err;
+        }
+
         if (ctx->stage == RYM_STAGE_FINISHED)
         if (ctx->stage == RYM_STAGE_FINISHED)
             break;
             break;
     }
     }
+    rt_free(ctx->buf);
+    return err;
+}
+
+static rt_err_t _rym_do_send(
+    struct rym_ctx *ctx,
+    int handshake_timeout)
+{
+    rt_err_t err;
+
+    ctx->stage = RYM_STAGE_NONE;
+
+    ctx->buf = rt_malloc(_RYM_STX_PKG_SZ);
+    if (ctx->buf == RT_NULL)
+        return -RT_ENOMEM;
+
+    err = _rym_do_send_handshake(ctx, handshake_timeout);
+    if (err != RT_EOK)
+    {
+        rt_free(ctx->buf);
+        return err;
+    }
+
+    err = _rym_do_send_trans(ctx);
+    if (err != RT_EOK)
+    {
+        rt_free(ctx->buf);
+        return err;
+    }
+
+    err = _rym_do_send_fin(ctx);
+    if (err != RT_EOK)
+    {
+        rt_free(ctx->buf);
+        return err;
+    }
+
+    rt_free(ctx->buf);
     return err;
     return err;
 }
 }
 
 
 rt_err_t rym_recv_on_device(
 rt_err_t rym_recv_on_device(
-        struct rym_ctx *ctx,
-        rt_device_t dev,
-        rt_uint16_t oflag,
-        rym_callback on_begin,
-        rym_callback on_data,
-        rym_callback on_end,
-        int handshake_timeout)
+    struct rym_ctx *ctx,
+    rt_device_t dev,
+    rt_uint16_t oflag,
+    rym_callback on_begin,
+    rym_callback on_data,
+    rym_callback on_end,
+    int handshake_timeout)
 {
 {
     rt_err_t res;
     rt_err_t res;
     rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size);
     rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size);
@@ -393,9 +644,9 @@ rt_err_t rym_recv_on_device(
     int_lvl = rt_hw_interrupt_disable();
     int_lvl = rt_hw_interrupt_disable();
     rt_device_set_rx_indicate(dev, _rym_rx_ind);
     rt_device_set_rx_indicate(dev, _rym_rx_ind);
 
 
-    odev_flag = dev->flag;
+    odev_flag = dev->open_flag;
     /* make sure the device don't change the content. */
     /* make sure the device don't change the content. */
-    dev->flag &= ~RT_DEVICE_FLAG_STREAM;
+    dev->open_flag &= ~RT_DEVICE_FLAG_STREAM;
     rt_hw_interrupt_enable(int_lvl);
     rt_hw_interrupt_enable(int_lvl);
 
 
     res = rt_device_open(dev, oflag);
     res = rt_device_open(dev, oflag);
@@ -412,12 +663,68 @@ __exit:
     int_lvl = rt_hw_interrupt_disable();
     int_lvl = rt_hw_interrupt_disable();
     rt_sem_detach(&ctx->sem);
     rt_sem_detach(&ctx->sem);
 
 
-    dev->flag = odev_flag;
+    dev->open_flag = odev_flag;
+    rt_device_set_rx_indicate(dev, odev_rx_ind);
+    rt_hw_interrupt_enable(int_lvl);
+
+    _rym_the_ctx = RT_NULL;
+
+    return res;
+}
+
+rt_err_t rym_send_on_device(
+    struct rym_ctx *ctx,
+    rt_device_t dev,
+    rt_uint16_t oflag,
+    rym_callback on_begin,
+    rym_callback on_data,
+    rym_callback on_end,
+    int handshake_timeout)
+{
+    rt_err_t res = 0;
+    rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size);
+    rt_uint16_t odev_flag;
+    int int_lvl;
+
+    RT_ASSERT(_rym_the_ctx == 0);
+    _rym_the_ctx = ctx;
+
+    ctx->on_begin = on_begin;
+    ctx->on_data  = on_data;
+    ctx->on_end   = on_end;
+    ctx->dev      = dev;
+    rt_sem_init(&ctx->sem, "rymsem", 0, RT_IPC_FLAG_FIFO);
+
+    odev_rx_ind = dev->rx_indicate;
+    /* no data should be received before the device has been fully setted up.
+     */
+    int_lvl = rt_hw_interrupt_disable();
+    rt_device_set_rx_indicate(dev, _rym_rx_ind);
+
+    odev_flag = dev->open_flag;
+    /* make sure the device don't change the content. */
+    dev->open_flag &= ~RT_DEVICE_FLAG_STREAM;
+    rt_hw_interrupt_enable(int_lvl);
+
+    res = rt_device_open(dev, oflag);
+    if (res != RT_EOK)
+        goto __exit;
+
+    res = _rym_do_send(ctx, handshake_timeout);
+
+    rt_device_close(dev);
+
+__exit:
+
+    int_lvl = rt_hw_interrupt_disable();
+    rt_sem_detach(&ctx->sem);
+
+    dev->open_flag = odev_flag;
     rt_device_set_rx_indicate(dev, odev_rx_ind);
     rt_device_set_rx_indicate(dev, odev_rx_ind);
     rt_hw_interrupt_enable(int_lvl);
     rt_hw_interrupt_enable(int_lvl);
 
 
-    rt_free(ctx->buf);
     _rym_the_ctx = RT_NULL;
     _rym_the_ctx = RT_NULL;
 
 
     return res;
     return res;
 }
 }
+

+ 38 - 8
components/utilities/ymodem/ymodem.h

@@ -7,16 +7,19 @@
  * Change Logs:
  * Change Logs:
  * Date           Author       Notes
  * Date           Author       Notes
  * 2013-04-14     Grissiom     initial implementation
  * 2013-04-14     Grissiom     initial implementation
+ * 2019-12-09     Steven Liu   add YMODEM send protocol
  */
  */
 
 
 #ifndef __YMODEM_H__
 #ifndef __YMODEM_H__
 #define __YMODEM_H__
 #define __YMODEM_H__
 
 
 #include "rtthread.h"
 #include "rtthread.h"
+#include <string.h>
 
 
 /* The word "RYM" is stand for "Real-YModem". */
 /* The word "RYM" is stand for "Real-YModem". */
 
 
-enum rym_code {
+enum rym_code
+{
     RYM_CODE_NONE = 0x00,
     RYM_CODE_NONE = 0x00,
     RYM_CODE_SOH  = 0x01,
     RYM_CODE_SOH  = 0x01,
     RYM_CODE_STX  = 0x02,
     RYM_CODE_STX  = 0x02,
@@ -44,6 +47,10 @@ enum rym_code {
 #define RYM_ERR_DSZ  0x74
 #define RYM_ERR_DSZ  0x74
 /* the transmission is aborted by user */
 /* the transmission is aborted by user */
 #define RYM_ERR_CAN  0x75
 #define RYM_ERR_CAN  0x75
+/* wrong answer, wrong ACK or C */
+#define RYM_ERR_ACK  0x76
+/* transmit file invalid */
+#define RYM_ERR_FILE 0x77
 
 
 /* how many ticks wait for chars between packet. */
 /* how many ticks wait for chars between packet. */
 #ifndef RYM_WAIT_CHR_TICK
 #ifndef RYM_WAIT_CHR_TICK
@@ -63,7 +70,8 @@ enum rym_code {
 #define RYM_END_SESSION_SEND_CAN_NUM  0x07
 #define RYM_END_SESSION_SEND_CAN_NUM  0x07
 #endif
 #endif
 
 
-enum rym_stage {
+enum rym_stage
+{
     RYM_STAGE_NONE,
     RYM_STAGE_NONE,
     /* set when C is send */
     /* set when C is send */
     RYM_STAGE_ESTABLISHING,
     RYM_STAGE_ESTABLISHING,
@@ -80,16 +88,15 @@ enum rym_stage {
 };
 };
 
 
 struct rym_ctx;
 struct rym_ctx;
-/* when receiving files, the buf will be the data received from ymodem protocol
+/* When receiving files, the buf will be the data received from ymodem protocol
  * and the len is the data size.
  * and the len is the data size.
  *
  *
- * TODO:
  * When sending files, the len is the buf size in RYM. The callback need to
  * When sending files, the len is the buf size in RYM. The callback need to
  * fill the buf with data to send. Returning RYM_CODE_EOT will terminate the
  * fill the buf with data to send. Returning RYM_CODE_EOT will terminate the
  * transfer and the buf will be discarded. Any other return values will cause
  * transfer and the buf will be discarded. Any other return values will cause
  * the transfer continue.
  * the transfer continue.
  */
  */
-typedef enum rym_code (*rym_callback)(struct rym_ctx *ctx, rt_uint8_t *buf, rt_size_t len);
+typedef enum rym_code(*rym_callback)(struct rym_ctx *ctx, rt_uint8_t *buf, rt_size_t len);
 
 
 /* Currently RYM only support one transfer session(ctx) for simplicity.
 /* Currently RYM only support one transfer session(ctx) for simplicity.
  *
  *
@@ -112,7 +119,7 @@ struct rym_ctx
     rt_device_t dev;
     rt_device_t dev;
 };
 };
 
 
-/** recv a file on device dev with ymodem session ctx.
+/* recv a file on device dev with ymodem session ctx.
  *
  *
  * If an error happens, you can get where it is failed from ctx->stage.
  * If an error happens, you can get where it is failed from ctx->stage.
  *
  *
@@ -138,7 +145,30 @@ struct rym_ctx
  * second.
  * second.
  */
  */
 rt_err_t rym_recv_on_device(struct rym_ctx *ctx, rt_device_t dev, rt_uint16_t oflag,
 rt_err_t rym_recv_on_device(struct rym_ctx *ctx, rt_device_t dev, rt_uint16_t oflag,
-        rym_callback on_begin, rym_callback on_data, rym_callback on_end,
-        int handshake_timeout);
+                            rym_callback on_begin, rym_callback on_data, rym_callback on_end,
+                            int handshake_timeout);
+
+/* send a file on device dev with ymodem session ctx.
+ *
+ * If an error happens, you can get where it is failed from ctx->stage.
+ *
+ * @param on_begin The callback will be invoked when the first packet is sent.
+ * This packet often contain file names and the size of the file. It is the
+ * on_begin's responsibility to parse the basic information of the file. The
+ * on_begin can not be NULL.
+ *
+ * @param on_data The callback will be invoked when the data packets is sent.
+ * The callback should read file system and prepare the data packets. The
+ * on_data can not be NULL.
+ *
+ * @param on_end The callback will be invoked when one transmission is
+ * finished. The data should be 128 bytes of NULL. The on_end can not be NULL.
+ *
+ * @param handshake_timeout the timeout when hand shaking. The unit is in
+ * second.
+ */
+rt_err_t rym_send_on_device(struct rym_ctx *ctx, rt_device_t dev, rt_uint16_t oflag,
+                            rym_callback on_begin, rym_callback on_data, rym_callback on_end,
+                            int handshake_timeout);
 
 
 #endif
 #endif