import RT-Thread@9217865c without bsp, libcpu and components/net
This commit is contained in:
commit
e2376a3709
1414 changed files with 390370 additions and 0 deletions
15
components/utilities/ymodem/SConscript
Normal file
15
components/utilities/ymodem/SConscript
Normal file
|
@ -0,0 +1,15 @@
|
|||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Split('''
|
||||
ymodem.c
|
||||
''')
|
||||
|
||||
CPPPATH = [cwd]
|
||||
|
||||
if GetDepend('YMODEM_USING_FILE_TRANSFER'):
|
||||
src += ['ry_sy.c']
|
||||
|
||||
group = DefineGroup('Utilities', src, depend = ['RT_USING_RYM'], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
294
components/utilities/ymodem/ry_sy.c
Normal file
294
components/utilities/ymodem/ry_sy.c
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* 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
|
||||
* 2021-04-14 Meco Man Check the file path's legitimacy of 'sy' command
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <ymodem.h>
|
||||
#include <dfs_file.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef DFS_USING_POSIX
|
||||
#error "Please enable DFS_USING_POSIX"
|
||||
#endif
|
||||
|
||||
struct custom_ctx
|
||||
{
|
||||
struct rym_ctx parent;
|
||||
int fd;
|
||||
int flen;
|
||||
char fpath[DFS_PATH_MAX];
|
||||
};
|
||||
|
||||
static const char *_get_path_lastname(const char *path)
|
||||
{
|
||||
char *ptr;
|
||||
if ((ptr = (char *)strrchr(path, '/')) == NULL)
|
||||
return path;
|
||||
|
||||
/* skip the '/' then return */
|
||||
return ++ptr;
|
||||
}
|
||||
|
||||
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;
|
||||
struct stat file_buf;
|
||||
char insert_0 = '\0';
|
||||
char *ret;
|
||||
rt_err_t err;
|
||||
ret = strchr(cctx->fpath,insert_0);
|
||||
if(ret)
|
||||
{
|
||||
*ret = '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_kprintf("No end character\n");
|
||||
return RYM_ERR_ACK;
|
||||
}
|
||||
rt_strncpy(ret + 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(1 + (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;
|
||||
}
|
||||
|
||||
const char *fdst = _get_path_lastname(cctx->fpath);
|
||||
if(fdst != cctx->fpath)
|
||||
{
|
||||
fdst = dfs_normalize_path(RT_NULL, fdst);
|
||||
if (fdst == RT_NULL)
|
||||
{
|
||||
return RYM_ERR_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
rt_sprintf((char *)buf, "%s%c%d", fdst, 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);
|
||||
ctx->stage = RYM_STAGE_FINISHING;
|
||||
}
|
||||
|
||||
if (read_size > 128)
|
||||
{
|
||||
return RYM_CODE_STX;
|
||||
}
|
||||
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,const char *file_path)
|
||||
{
|
||||
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_strncpy(ctx->fpath, file_path, DFS_PATH_MAX);
|
||||
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;
|
||||
/* temporarily support 1 file*/
|
||||
const char *file_path;
|
||||
if (argc < 2)
|
||||
{
|
||||
rt_kprintf("invalid file path.\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
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_download_file(dev,file_path);
|
||||
|
||||
return res;
|
||||
}
|
||||
MSH_CMD_EXPORT(ry, YMODEM Receive e.g: ry file_path [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)
|
||||
{
|
||||
rt_kprintf("invalid file path.\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
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 */
|
763
components/utilities/ymodem/ymodem.c
Normal file
763
components/utilities/ymodem/ymodem.c
Normal file
|
@ -0,0 +1,763 @@
|
|||
/*
|
||||
* COPYRIGHT (C) 2011-2023, Real-Thread Information Technology Ltd
|
||||
* All rights reserved
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-04-14 Grissiom initial implementation
|
||||
* 2019-12-09 Steven Liu add YMODEM send protocol
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include "ymodem.h"
|
||||
|
||||
#ifdef YMODEM_USING_CRC_TABLE
|
||||
static const rt_uint16_t ccitt_table[256] =
|
||||
{
|
||||
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
|
||||
};
|
||||
static rt_uint16_t CRC16(unsigned char *q, int len)
|
||||
{
|
||||
rt_uint16_t crc = 0;
|
||||
|
||||
while (len-- > 0)
|
||||
crc = (crc << 8) ^ ccitt_table[((crc >> 8) ^ *q++) & 0xff];
|
||||
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
|
||||
// rt_device_t->user_data(it is used by the serial driver)...
|
||||
static struct rym_ctx *_rym_the_ctx;
|
||||
|
||||
static rt_err_t _rym_rx_ind(rt_device_t dev, rt_size_t size)
|
||||
{
|
||||
return rt_sem_release(&_rym_the_ctx->sem);
|
||||
}
|
||||
|
||||
/* SOH/STX + seq + payload + crc */
|
||||
#define _RYM_SOH_PKG_SZ (1+2+128+2)
|
||||
#define _RYM_STX_PKG_SZ (1+2+1024+2)
|
||||
|
||||
static enum rym_code _rym_read_code(
|
||||
struct rym_ctx *ctx,
|
||||
rt_tick_t timeout)
|
||||
{
|
||||
/* Fast path */
|
||||
if (rt_device_read(ctx->dev, 0, ctx->buf, 1) == 1)
|
||||
return (enum rym_code)(*ctx->buf);
|
||||
|
||||
/* Slow path */
|
||||
do
|
||||
{
|
||||
rt_size_t rsz;
|
||||
|
||||
/* No data yet, wait for one */
|
||||
if (rt_sem_take(&ctx->sem, timeout) != RT_EOK)
|
||||
return RYM_CODE_NONE;
|
||||
|
||||
/* Try to read one */
|
||||
rsz = rt_device_read(ctx->dev, 0, ctx->buf, 1);
|
||||
if (rsz == 1)
|
||||
return (enum rym_code)(*ctx->buf);
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
|
||||
/* the caller should at least alloc _RYM_STX_PKG_SZ buffer */
|
||||
static rt_ssize_t _rym_read_data(
|
||||
struct rym_ctx *ctx,
|
||||
rt_size_t len)
|
||||
{
|
||||
/* we should already have had the code */
|
||||
rt_uint8_t *buf = ctx->buf + 1;
|
||||
rt_size_t readlen = 0;
|
||||
|
||||
do
|
||||
{
|
||||
readlen += rt_device_read(ctx->dev,
|
||||
0, buf + readlen, len - readlen);
|
||||
if (readlen >= len)
|
||||
return readlen;
|
||||
}
|
||||
while (rt_sem_take(&ctx->sem, RYM_WAIT_CHR_TICK) == RT_EOK);
|
||||
|
||||
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;
|
||||
rt_size_t packetlen = 0;
|
||||
|
||||
switch(code)
|
||||
{
|
||||
case RYM_CODE_SOH:
|
||||
packetlen = _RYM_SOH_PKG_SZ;
|
||||
break;
|
||||
case RYM_CODE_STX:
|
||||
packetlen = _RYM_STX_PKG_SZ;
|
||||
break;
|
||||
default:
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
send_crc = CRC16(ctx->buf + 3, packetlen - 5);
|
||||
ctx->buf[0] = code;
|
||||
ctx->buf[1] = index;
|
||||
ctx->buf[2] = index_inv;
|
||||
ctx->buf[packetlen - 2] = (rt_uint8_t)(send_crc >> 8);
|
||||
ctx->buf[packetlen - 1] = (rt_uint8_t)send_crc & 0xff;
|
||||
|
||||
do
|
||||
{
|
||||
writelen += rt_device_write(ctx->dev, 0, ctx->buf + writelen,
|
||||
packetlen - writelen);
|
||||
}
|
||||
while (writelen < packetlen);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_ssize_t _rym_putchar(struct rym_ctx *ctx, rt_uint8_t code)
|
||||
{
|
||||
rt_device_write(ctx->dev, 0, &code, sizeof(code));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static rt_ssize_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(
|
||||
struct rym_ctx *ctx,
|
||||
int tm_sec)
|
||||
{
|
||||
enum rym_code code;
|
||||
rt_size_t i;
|
||||
rt_uint16_t recv_crc, cal_crc;
|
||||
rt_size_t data_sz;
|
||||
rt_tick_t tick;
|
||||
|
||||
ctx->stage = RYM_STAGE_ESTABLISHING;
|
||||
/* send C every second, so the sender could know we are waiting for it. */
|
||||
for (i = 0; i < tm_sec; i++)
|
||||
{
|
||||
_rym_putchar(ctx, RYM_CODE_C);
|
||||
code = _rym_read_code(ctx,
|
||||
RYM_CHD_INTV_TICK);
|
||||
if (code == RYM_CODE_SOH)
|
||||
{
|
||||
data_sz = _RYM_SOH_PKG_SZ;
|
||||
break;
|
||||
}
|
||||
else if (code == RYM_CODE_STX)
|
||||
{
|
||||
data_sz = _RYM_STX_PKG_SZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == tm_sec)
|
||||
{
|
||||
return -RYM_ERR_TMO;
|
||||
}
|
||||
|
||||
/* receive all data */
|
||||
i = 0;
|
||||
/* automatic exit after receiving specified length data, timeout: 100ms */
|
||||
tick = rt_tick_get();
|
||||
while (rt_tick_get() <= (tick + rt_tick_from_millisecond(100)) && i < (data_sz - 1))
|
||||
{
|
||||
i += _rym_read_data(ctx, data_sz - 1);
|
||||
rt_thread_mdelay(5);
|
||||
}
|
||||
|
||||
if (i != (data_sz - 1))
|
||||
return -RYM_ERR_DSZ;
|
||||
|
||||
/* sanity check */
|
||||
if (ctx->buf[1] != 0 || ctx->buf[2] != 0xFF)
|
||||
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);
|
||||
if (recv_crc != cal_crc)
|
||||
return -RYM_ERR_CRC;
|
||||
|
||||
/* congratulations, check passed. */
|
||||
if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_ACK)
|
||||
return -RYM_ERR_CAN;
|
||||
|
||||
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(
|
||||
struct rym_ctx *ctx,
|
||||
rt_size_t data_sz,
|
||||
enum rym_code *code)
|
||||
{
|
||||
const rt_size_t tsz = 2 + data_sz + 2;
|
||||
rt_uint16_t recv_crc;
|
||||
|
||||
/* seq + data + crc */
|
||||
rt_size_t i = _rym_read_data(ctx, tsz);
|
||||
if (i != tsz)
|
||||
return -RYM_ERR_DSZ;
|
||||
|
||||
if ((ctx->buf[1] + ctx->buf[2]) != 0xFF)
|
||||
{
|
||||
return -RYM_ERR_SEQ;
|
||||
}
|
||||
|
||||
/* As we are sending C continuously, there is a chance that the
|
||||
* sender(remote) receive an C after sending the first handshake package.
|
||||
* So the sender will interpret it as NAK and re-send the package. So we
|
||||
* just ignore it and proceed. */
|
||||
if (ctx->stage == RYM_STAGE_ESTABLISHED && ctx->buf[1] == 0x00)
|
||||
{
|
||||
*code = RYM_CODE_NONE;
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
ctx->stage = RYM_STAGE_TRANSMITTING;
|
||||
|
||||
/* sanity check */
|
||||
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;
|
||||
|
||||
/* congratulations, check passed. */
|
||||
if (ctx->on_data)
|
||||
*code = ctx->on_data(ctx, ctx->buf + 3, data_sz);
|
||||
else
|
||||
*code = RYM_CODE_ACK;
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t _rym_do_trans(struct rym_ctx *ctx)
|
||||
{
|
||||
_rym_putchar(ctx, RYM_CODE_ACK);
|
||||
_rym_putchar(ctx, RYM_CODE_C);
|
||||
ctx->stage = RYM_STAGE_ESTABLISHED;
|
||||
rt_size_t errors = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
rt_err_t err;
|
||||
enum rym_code code;
|
||||
rt_size_t data_sz, i;
|
||||
|
||||
code = _rym_read_code(ctx,
|
||||
RYM_WAIT_PKG_TICK);
|
||||
switch (code)
|
||||
{
|
||||
case RYM_CODE_SOH:
|
||||
data_sz = 128;
|
||||
break;
|
||||
case RYM_CODE_STX:
|
||||
data_sz = 1024;
|
||||
break;
|
||||
case RYM_CODE_EOT:
|
||||
return RT_EOK;
|
||||
default:
|
||||
errors++;
|
||||
if(errors > RYM_MAX_ERRORS)
|
||||
{
|
||||
return -RYM_ERR_CODE;/* Abort communication */
|
||||
}
|
||||
else
|
||||
{
|
||||
_rym_putchar(ctx, RYM_CODE_NAK);/* Ask for a packet */
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
err = _rym_trans_data(ctx, data_sz, &code);
|
||||
if (err != RT_EOK)
|
||||
{
|
||||
errors++;
|
||||
if(errors > RYM_MAX_ERRORS)
|
||||
{
|
||||
return err;/* Abort communication */
|
||||
}
|
||||
else
|
||||
{
|
||||
_rym_putchar(ctx, RYM_CODE_NAK);/* Ask for a packet */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errors = 0;
|
||||
}
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case RYM_CODE_CAN:
|
||||
/* the spec require multiple CAN */
|
||||
for (i = 0; i < RYM_END_SESSION_SEND_CAN_NUM; i++)
|
||||
{
|
||||
_rym_putchar(ctx, RYM_CODE_CAN);
|
||||
}
|
||||
return -RYM_ERR_CAN;
|
||||
case RYM_CODE_ACK:
|
||||
_rym_putchar(ctx, RYM_CODE_ACK);
|
||||
break;
|
||||
default:
|
||||
// wrong code
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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_STX_PKG_SZ;
|
||||
while (1)
|
||||
{
|
||||
if (!ctx->on_data)
|
||||
{
|
||||
return -RYM_ERR_CODE;
|
||||
}
|
||||
code = ctx->on_data(ctx, ctx->buf + 3, data_sz - 5);
|
||||
|
||||
_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)
|
||||
{
|
||||
enum rym_code code;
|
||||
rt_uint16_t recv_crc;
|
||||
rt_size_t i;
|
||||
rt_size_t data_sz;
|
||||
|
||||
ctx->stage = RYM_STAGE_FINISHING;
|
||||
/* we already got one EOT in the caller. invoke the callback if there is
|
||||
* one. */
|
||||
if (ctx->on_end)
|
||||
ctx->on_end(ctx, ctx->buf + 3, 128);
|
||||
|
||||
_rym_putchar(ctx, RYM_CODE_NAK);
|
||||
code = _rym_read_code(ctx, RYM_WAIT_PKG_TICK);
|
||||
if (code != RYM_CODE_EOT)
|
||||
return -RYM_ERR_CODE;
|
||||
|
||||
_rym_putchar(ctx, RYM_CODE_ACK);
|
||||
_rym_putchar(ctx, RYM_CODE_C);
|
||||
|
||||
code = _rym_read_code(ctx, RYM_WAIT_PKG_TICK);
|
||||
if (code == RYM_CODE_SOH)
|
||||
{
|
||||
data_sz = _RYM_SOH_PKG_SZ;
|
||||
}
|
||||
else if (code == RYM_CODE_STX)
|
||||
{
|
||||
data_sz = _RYM_STX_PKG_SZ;
|
||||
}
|
||||
else
|
||||
return -RYM_ERR_CODE;
|
||||
|
||||
i = _rym_read_data(ctx, _RYM_SOH_PKG_SZ - 1);
|
||||
if (i != (_RYM_SOH_PKG_SZ - 1))
|
||||
return -RYM_ERR_DSZ;
|
||||
|
||||
/* sanity check
|
||||
*/
|
||||
if (ctx->buf[1] != 0 || ctx->buf[2] != 0xFF)
|
||||
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))
|
||||
return -RYM_ERR_CRC;
|
||||
|
||||
/*next file transmission*/
|
||||
if (ctx->buf[3] != 0)
|
||||
{
|
||||
if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_ACK)
|
||||
return -RYM_ERR_CAN;
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/* congratulations, check passed. */
|
||||
ctx->stage = RYM_STAGE_FINISHED;
|
||||
|
||||
/* put the last ACK */
|
||||
_rym_putchar(ctx, RYM_CODE_ACK);
|
||||
|
||||
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(
|
||||
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_handshake(ctx, handshake_timeout);
|
||||
if (err != RT_EOK)
|
||||
{
|
||||
rt_free(ctx->buf);
|
||||
return err;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
err = _rym_do_trans(ctx);
|
||||
|
||||
err = _rym_do_fin(ctx);
|
||||
if (err != RT_EOK)
|
||||
{
|
||||
rt_free(ctx->buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (ctx->stage == RYM_STAGE_FINISHED)
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
rt_err_t res;
|
||||
rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size);
|
||||
rt_uint16_t odev_flag;
|
||||
rt_base_t level;
|
||||
|
||||
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.
|
||||
*/
|
||||
level = 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(level);
|
||||
|
||||
res = rt_device_open(dev, oflag);
|
||||
if (res != RT_EOK)
|
||||
goto __exit;
|
||||
|
||||
res = _rym_do_recv(ctx, handshake_timeout);
|
||||
|
||||
rt_device_close(dev);
|
||||
|
||||
__exit:
|
||||
/* no rx_ind should be called before the callback has been fully detached.
|
||||
*/
|
||||
level = rt_hw_interrupt_disable();
|
||||
rt_sem_detach(&ctx->sem);
|
||||
|
||||
dev->open_flag = odev_flag;
|
||||
rt_device_set_rx_indicate(dev, odev_rx_ind);
|
||||
rt_hw_interrupt_enable(level);
|
||||
|
||||
_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;
|
||||
rt_base_t level;
|
||||
|
||||
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.
|
||||
*/
|
||||
level = 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(level);
|
||||
|
||||
res = rt_device_open(dev, oflag);
|
||||
if (res != RT_EOK)
|
||||
goto __exit;
|
||||
|
||||
res = _rym_do_send(ctx, handshake_timeout);
|
||||
|
||||
rt_device_close(dev);
|
||||
|
||||
__exit:
|
||||
|
||||
level = rt_hw_interrupt_disable();
|
||||
rt_sem_detach(&ctx->sem);
|
||||
|
||||
dev->open_flag = odev_flag;
|
||||
rt_device_set_rx_indicate(dev, odev_rx_ind);
|
||||
rt_hw_interrupt_enable(level);
|
||||
|
||||
_rym_the_ctx = RT_NULL;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
167
components/utilities/ymodem/ymodem.h
Normal file
167
components/utilities/ymodem/ymodem.h
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* COPYRIGHT (C) 2011-2022, Real-Thread Information Technology Ltd
|
||||
* All rights reserved
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2013-04-14 Grissiom initial implementation
|
||||
* 2019-12-09 Steven Liu add YMODEM send protocol
|
||||
* 2022-08-04 Meco Man move error codes to rym_code to silence warnings
|
||||
*/
|
||||
|
||||
#ifndef __YMODEM_H__
|
||||
#define __YMODEM_H__
|
||||
|
||||
#include "rtthread.h"
|
||||
#include <string.h>
|
||||
|
||||
/* The word "RYM" is stand for "Real-YModem". */
|
||||
enum rym_code
|
||||
{
|
||||
RYM_CODE_NONE = 0x00,
|
||||
RYM_CODE_SOH = 0x01,
|
||||
RYM_CODE_STX = 0x02,
|
||||
RYM_CODE_EOT = 0x04,
|
||||
RYM_CODE_ACK = 0x06,
|
||||
RYM_CODE_NAK = 0x15,
|
||||
RYM_CODE_CAN = 0x18,
|
||||
RYM_CODE_C = 0x43,
|
||||
|
||||
/* RYM error code */
|
||||
RYM_ERR_TMO = 0x70, /* timeout on handshake */
|
||||
RYM_ERR_CODE = 0x71, /* wrong code, wrong SOH, STX etc */
|
||||
RYM_ERR_SEQ = 0x72, /* wrong sequence number */
|
||||
RYM_ERR_CRC = 0x73, /* wrong CRC checksum */
|
||||
RYM_ERR_DSZ = 0x74, /* not enough data received */
|
||||
RYM_ERR_CAN = 0x75, /* the transmission is aborted by user */
|
||||
RYM_ERR_ACK = 0x76, /* wrong answer, wrong ACK or C */
|
||||
RYM_ERR_FILE = 0x77, /* transmit file invalid */
|
||||
};
|
||||
|
||||
/* how many ticks wait for chars between packet. */
|
||||
#ifndef RYM_WAIT_CHR_TICK
|
||||
#define RYM_WAIT_CHR_TICK (RT_TICK_PER_SECOND * 3)
|
||||
#endif
|
||||
/* how many ticks wait for between packet. */
|
||||
#ifndef RYM_WAIT_PKG_TICK
|
||||
#define RYM_WAIT_PKG_TICK (RT_TICK_PER_SECOND * 3)
|
||||
#endif
|
||||
/* how many ticks between two handshake code. */
|
||||
#ifndef RYM_CHD_INTV_TICK
|
||||
#define RYM_CHD_INTV_TICK (RT_TICK_PER_SECOND * 3)
|
||||
#endif
|
||||
|
||||
/* how many CAN be sent when user active end the session. */
|
||||
#ifndef RYM_END_SESSION_SEND_CAN_NUM
|
||||
#define RYM_END_SESSION_SEND_CAN_NUM 0x07
|
||||
#endif
|
||||
|
||||
/* how many retries were made when the error occurred */
|
||||
#ifndef RYM_MAX_ERRORS
|
||||
#define RYM_MAX_ERRORS ((rt_size_t)5)
|
||||
#endif
|
||||
|
||||
enum rym_stage
|
||||
{
|
||||
RYM_STAGE_NONE = 0,
|
||||
/* set when C is send */
|
||||
RYM_STAGE_ESTABLISHING,
|
||||
/* set when we've got the packet 0 and sent ACK and second C */
|
||||
RYM_STAGE_ESTABLISHED,
|
||||
/* set when the sender respond to our second C and recviever got a real
|
||||
* data packet. */
|
||||
RYM_STAGE_TRANSMITTING,
|
||||
/* set when the sender send a EOT */
|
||||
RYM_STAGE_FINISHING,
|
||||
/* set when transmission is really finished, i.e., after the NAK, C, final
|
||||
* NULL packet stuff. */
|
||||
RYM_STAGE_FINISHED,
|
||||
};
|
||||
|
||||
struct rym_ctx;
|
||||
/* When receiving files, the buf will be the data received from ymodem protocol
|
||||
* and the len is the data size.
|
||||
*
|
||||
* 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
|
||||
* transfer and the buf will be discarded. Any other return values will cause
|
||||
* the transfer continue.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* In case we could support multiple sessions in The future, the first argument
|
||||
* of APIs are (struct rym_ctx*).
|
||||
*/
|
||||
struct rym_ctx
|
||||
{
|
||||
rym_callback on_data;
|
||||
rym_callback on_begin;
|
||||
rym_callback on_end;
|
||||
/* When error happened, user need to check this to get when the error has
|
||||
* happened. */
|
||||
enum rym_stage stage;
|
||||
/* user could get the error content through this */
|
||||
rt_uint8_t *buf;
|
||||
|
||||
struct rt_semaphore sem;
|
||||
|
||||
rt_device_t dev;
|
||||
};
|
||||
|
||||
/* recv 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 arrived.
|
||||
* This packet often contain file names and the size of the file, if the sender
|
||||
* support it. So if you want to save the data to a file, you may need to
|
||||
* create the file on need. It is the on_begin's responsibility to parse the
|
||||
* data content. The on_begin can be NULL, in which case the transmission will
|
||||
* continue without any side-effects.
|
||||
*
|
||||
* @param on_data The callback will be invoked on the packets received. The
|
||||
* callback should save the data to the destination. The return value will be
|
||||
* sent to the sender and in turn, only RYM_{ACK,CAN} is valid. When on_data is
|
||||
* NULL, RYM will barely send ACK on every packet and have no side-effects.
|
||||
*
|
||||
* @param on_end The callback will be invoked when one transmission is
|
||||
* finished. The data should be 128 bytes of NULL. You can do some cleaning job
|
||||
* in this callback such as closing the file. The return value of this callback
|
||||
* is ignored. As above, this parameter can be NULL if you don't need such
|
||||
* function.
|
||||
*
|
||||
* @param handshake_timeout the timeout when hand shaking. The unit is in
|
||||
* second.
|
||||
*/
|
||||
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);
|
||||
|
||||
/* 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
|
Loading…
Add table
Add a link
Reference in a new issue