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

/*
* 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;
}