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