You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
245 lines
7.7 KiB
245 lines
7.7 KiB
|
15 hours ago
|
/*
|
||
|
|
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||
|
|
*
|
||
|
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
|
*
|
||
|
|
* Radkil adapted in 2026: 移除 RT-Thread 依赖, 对接 rd_stdio + TComCtrl
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "ymodem.h"
|
||
|
|
#include "rd_stdio.h"
|
||
|
|
#include <stdlib.h>
|
||
|
|
|
||
|
|
#ifndef YMODEM_PATH_MAX
|
||
|
|
#define YMODEM_PATH_MAX 256
|
||
|
|
#endif
|
||
|
|
|
||
|
|
struct custom_ctx
|
||
|
|
{
|
||
|
|
struct rym_ctx parent;
|
||
|
|
void *fp; /* Rd_Fopen 返回的文件句柄 */
|
||
|
|
int flen; /* 剩余待接收字节数 */
|
||
|
|
char fpath[YMODEM_PATH_MAX];
|
||
|
|
};
|
||
|
|
|
||
|
|
/* ═══════════════════════════════════════════════════════════════════════ *
|
||
|
|
* TComCtrl → rym_device_t 桥接 *
|
||
|
|
* 读: rd_ComRead (内部自带信号量等待, 无协议回调时直接返回原始字节) *
|
||
|
|
* 写: rd_ComSend *
|
||
|
|
* 无需额外维护信号量 *
|
||
|
|
* ═══════════════════════════════════════════════════════════════════════ */
|
||
|
|
|
||
|
|
static size_t serial_rym_read(void *opaque, uint8_t *buf, size_t len)
|
||
|
|
{
|
||
|
|
TComCtrl *ptCom = (TComCtrl *)opaque;
|
||
|
|
if (ptCom == NULL || len == 0) return 0;
|
||
|
|
|
||
|
|
int ret = rd_ComRead(ptCom, (char *)buf, (uint32_t)len);
|
||
|
|
return (ret > 0) ? (size_t)ret : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static size_t serial_rym_write(void *opaque, const uint8_t *buf, size_t len)
|
||
|
|
{
|
||
|
|
TComCtrl *ptCom = (TComCtrl *)opaque;
|
||
|
|
if (ptCom == NULL) return 0;
|
||
|
|
return (size_t)rd_ComSend(ptCom, (char *)buf, (uint32_t)len);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void ymodem_build_device(TComCtrl *ptCom, rym_device_t *dev)
|
||
|
|
{
|
||
|
|
RD_MEMSET(dev, 0, sizeof(*dev));
|
||
|
|
dev->read = serial_rym_read;
|
||
|
|
dev->write = serial_rym_write;
|
||
|
|
dev->opaque = ptCom;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ═══════════════════════════════════════════════════════════════════════ *
|
||
|
|
* YMODEM 接收回调 (文件写入) *
|
||
|
|
* ═══════════════════════════════════════════════════════════════════════ */
|
||
|
|
|
||
|
|
static enum rym_code _rym_recv_begin(struct rym_ctx *ctx, uint8_t *buf, size_t len)
|
||
|
|
{
|
||
|
|
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||
|
|
char *ret;
|
||
|
|
|
||
|
|
ret = strchr(cctx->fpath, '\0');
|
||
|
|
if (ret == NULL)
|
||
|
|
{
|
||
|
|
log_w("No end character");
|
||
|
|
return RYM_ERR_ACK;
|
||
|
|
}
|
||
|
|
*ret = '/';
|
||
|
|
RD_STRNCPY(ret + 1, (const char *)buf, len - 1);
|
||
|
|
ret[1 + len - 1] = '\0';
|
||
|
|
|
||
|
|
cctx->fp = Rd_Fopen(cctx->fpath, "wb");
|
||
|
|
if (cctx->fp == NULL)
|
||
|
|
{
|
||
|
|
log_e("error creating file: %s", cctx->fpath);
|
||
|
|
return RYM_CODE_CAN;
|
||
|
|
}
|
||
|
|
|
||
|
|
cctx->flen = atoi(1 + (const char *)buf + 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, uint8_t *buf, size_t len)
|
||
|
|
{
|
||
|
|
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||
|
|
|
||
|
|
if (cctx->fp == NULL)
|
||
|
|
return RYM_CODE_CAN;
|
||
|
|
|
||
|
|
if (cctx->flen == -1)
|
||
|
|
{
|
||
|
|
Rd_Mwrite(buf, len, cctx->fp);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
int wlen = (int)(len > (size_t)cctx->flen ? (size_t)cctx->flen : len);
|
||
|
|
Rd_Mwrite(buf, wlen, cctx->fp);
|
||
|
|
cctx->flen -= wlen;
|
||
|
|
}
|
||
|
|
|
||
|
|
return RYM_CODE_ACK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static enum rym_code _rym_recv_end(struct rym_ctx *ctx, uint8_t *buf, size_t len)
|
||
|
|
{
|
||
|
|
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||
|
|
(void)buf;
|
||
|
|
(void)len;
|
||
|
|
|
||
|
|
if (cctx->fp != NULL)
|
||
|
|
{
|
||
|
|
Rd_Mclose(cctx->fp);
|
||
|
|
cctx->fp = NULL;
|
||
|
|
}
|
||
|
|
return RYM_CODE_ACK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ═══════════════════════════════════════════════════════════════════════ *
|
||
|
|
* YMODEM 发送回调 (文件读取) *
|
||
|
|
* ═══════════════════════════════════════════════════════════════════════ */
|
||
|
|
|
||
|
|
static const char *_get_path_lastname(const char *path)
|
||
|
|
{
|
||
|
|
char *ptr = strrchr(path, '/');
|
||
|
|
if (ptr == NULL) return path;
|
||
|
|
return ++ptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
static enum rym_code _rym_send_begin(struct rym_ctx *ctx, uint8_t *buf, size_t len)
|
||
|
|
{
|
||
|
|
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||
|
|
|
||
|
|
cctx->fp = Rd_Fopen(cctx->fpath, "rb");
|
||
|
|
if (cctx->fp == NULL)
|
||
|
|
{
|
||
|
|
log_e("error open file: %s", cctx->fpath);
|
||
|
|
return RYM_ERR_FILE;
|
||
|
|
}
|
||
|
|
|
||
|
|
Rd_Mseek(cctx->fp, 0, SEEK_END);
|
||
|
|
long file_size = Rd_Mtell(cctx->fp);
|
||
|
|
Rd_Mseek(cctx->fp, 0, SEEK_SET);
|
||
|
|
|
||
|
|
RD_MEMSET(buf, 0, len);
|
||
|
|
const char *fname = _get_path_lastname(cctx->fpath);
|
||
|
|
RD_SPRINTF((char *)buf, "%s%c%ld", fname, '\0', file_size);
|
||
|
|
|
||
|
|
return RYM_CODE_SOH;
|
||
|
|
}
|
||
|
|
|
||
|
|
static enum rym_code _rym_send_data(struct rym_ctx *ctx, uint8_t *buf, size_t len)
|
||
|
|
{
|
||
|
|
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||
|
|
size_t read_size = 0;
|
||
|
|
int retry;
|
||
|
|
|
||
|
|
for (retry = 0; retry < 10; retry++)
|
||
|
|
{
|
||
|
|
ssize_t n = Rd_Mread(buf + read_size, len - read_size, cctx->fp);
|
||
|
|
if (n > 0) read_size += (size_t)n;
|
||
|
|
if (read_size == len) break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (read_size < len)
|
||
|
|
{
|
||
|
|
RD_MEMSET(buf + read_size, 0x1A, len - read_size);
|
||
|
|
ctx->stage = RYM_STAGE_FINISHING;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (read_size > 128) ? RYM_CODE_STX : RYM_CODE_SOH;
|
||
|
|
}
|
||
|
|
|
||
|
|
static enum rym_code _rym_send_end(struct rym_ctx *ctx, uint8_t *buf, size_t len)
|
||
|
|
{
|
||
|
|
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
|
||
|
|
RD_MEMSET(buf, 0, len);
|
||
|
|
|
||
|
|
if (cctx->fp != NULL)
|
||
|
|
{
|
||
|
|
Rd_Mclose(cctx->fp);
|
||
|
|
cctx->fp = NULL;
|
||
|
|
}
|
||
|
|
return RYM_CODE_SOH;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ═══════════════════════════════════════════════════════════════════════ *
|
||
|
|
* 公开 API *
|
||
|
|
* ═══════════════════════════════════════════════════════════════════════ */
|
||
|
|
|
||
|
|
int rym_download_file(TComCtrl *ptCom, const char *file_path)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
rym_device_t dev;
|
||
|
|
|
||
|
|
struct custom_ctx *ctx = RD_CALLOC(1, sizeof(*ctx));
|
||
|
|
if (!ctx)
|
||
|
|
{
|
||
|
|
log_e("RD_MALLOC failed");
|
||
|
|
return RD_FAILURE;
|
||
|
|
}
|
||
|
|
ctx->fp = NULL;
|
||
|
|
RD_STRNCPY(ctx->fpath, file_path, YMODEM_PATH_MAX);
|
||
|
|
|
||
|
|
ymodem_build_device(ptCom, &dev);
|
||
|
|
res = rym_recv_on_device(&ctx->parent, &dev,
|
||
|
|
_rym_recv_begin, _rym_recv_data, _rym_recv_end,
|
||
|
|
10);
|
||
|
|
|
||
|
|
if (ctx->fp != NULL)
|
||
|
|
Rd_Mclose(ctx->fp);
|
||
|
|
RD_FREE(ctx);
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
int rym_upload_file(TComCtrl *ptCom, const char *file_path)
|
||
|
|
{
|
||
|
|
int res;
|
||
|
|
rym_device_t dev;
|
||
|
|
|
||
|
|
struct custom_ctx *ctx = RD_CALLOC(1, sizeof(*ctx));
|
||
|
|
if (!ctx)
|
||
|
|
{
|
||
|
|
log_e("RD_MALLOC failed");
|
||
|
|
return RD_FAILURE;
|
||
|
|
}
|
||
|
|
ctx->fp = NULL;
|
||
|
|
RD_STRNCPY(ctx->fpath, file_path, YMODEM_PATH_MAX);
|
||
|
|
|
||
|
|
ymodem_build_device(ptCom, &dev);
|
||
|
|
res = rym_send_on_device(&ctx->parent, &dev,
|
||
|
|
_rym_send_begin, _rym_send_data, _rym_send_end,
|
||
|
|
10);
|
||
|
|
|
||
|
|
if (ctx->fp != NULL)
|
||
|
|
Rd_Mclose(ctx->fp);
|
||
|
|
RD_FREE(ctx);
|
||
|
|
return res;
|
||
|
|
}
|