Skip to content

Commit d8546a9

Browse files
wdfk-progRbb666
authored andcommitted
fix(ymodem): improve send read loop and unify session cleanup
- Accumulate short reads, mark finishing on EOF - Track begin/end callback states and cleanup on error paths - Add ACK handling with retry/error counting in send flow
1 parent 71fd98e commit d8546a9

File tree

3 files changed

+152
-44
lines changed

3 files changed

+152
-44
lines changed

components/utilities/ymodem/ry_sy.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* Date Author Notes
88
* 2019-12-09 Steven Liu the first version
99
* 2021-04-14 Meco Man Check the file path's legitimacy of 'sy' command
10+
* 2026-02-01 wdfk-prog update ymodem transfer behaviors
1011
*/
1112

1213
#include <rtthread.h>
@@ -157,20 +158,35 @@ static enum rym_code _rym_send_data(
157158
{
158159
struct custom_ctx *cctx = (struct custom_ctx *)ctx;
159160
rt_size_t read_size;
160-
int retry_read;
161+
int rlen;
161162

162163
read_size = 0;
163-
for (retry_read = 0; retry_read < 10; retry_read++)
164+
/* Loop until we fill one YMODEM data block, hit EOF, or get a read error. */
165+
while (read_size < len)
164166
{
165-
read_size += read(cctx->fd, buf + read_size, len - read_size);
166-
if (read_size == len)
167+
rlen = read(cctx->fd, buf + read_size, len - read_size);
168+
if (rlen > 0)
169+
{
170+
read_size += rlen;
171+
if (read_size == len)
172+
break;
173+
}
174+
else if (rlen == 0)
175+
{
176+
/* EOF: mark finishing so sender switches to EOT after padding. */
177+
ctx->stage = RYM_STAGE_FINISHING;
167178
break;
179+
}
180+
else
181+
{
182+
/* Read error: abort transfer and report file error to the protocol. */
183+
return RYM_ERR_FILE;
184+
}
168185
}
169186

170187
if (read_size < len)
171188
{
172189
rt_memset(buf + read_size, 0x1A, len - read_size);
173-
ctx->stage = RYM_STAGE_FINISHING;
174190
}
175191

176192
if (read_size > 128)

components/utilities/ymodem/ymodem.c

Lines changed: 97 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* Date Author Notes
99
* 2013-04-14 Grissiom initial implementation
1010
* 2019-12-09 Steven Liu add YMODEM send protocol
11+
* 2026-02-01 wdfk-prog update ymodem callbacks and error handling
1112
*/
1213

1314
#include <rthw.h>
@@ -180,6 +181,27 @@ static rt_err_t _rym_send_packet(
180181
return RT_EOK;
181182
}
182183

184+
/**
185+
* @brief Finalize a transfer, record error state, invoke end callback, and free buffer.
186+
*
187+
* @param ctx Transfer context.
188+
* @param err Transfer result (RT_EOK on success, negative on failure).
189+
*/
190+
static void _rym_cleanup(struct rym_ctx *ctx, rt_err_t err)
191+
{
192+
ctx->last_err = err;
193+
if (ctx->on_end && ctx->begin_called && !ctx->end_called)
194+
{
195+
ctx->on_end(ctx, ctx->buf + 3, 128);
196+
ctx->end_called = 1;
197+
}
198+
if (ctx->buf)
199+
{
200+
rt_free(ctx->buf);
201+
ctx->buf = RT_NULL;
202+
}
203+
}
204+
183205
static rt_ssize_t _rym_putchar(struct rym_ctx *ctx, rt_uint8_t code)
184206
{
185207
rt_device_write(ctx->dev, 0, &code, sizeof(code));
@@ -254,7 +276,13 @@ static rt_err_t _rym_do_handshake(
254276

255277
/* congratulations, check passed. */
256278
if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_ACK)
279+
{
257280
return -RYM_ERR_CAN;
281+
}
282+
else
283+
{
284+
ctx->begin_called = 1;
285+
}
258286

259287
return RT_EOK;
260288
}
@@ -289,12 +317,17 @@ static rt_err_t _rym_do_send_handshake(
289317

290318
/* congratulations, check passed. */
291319
if (ctx->on_begin && ctx->on_begin(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_SOH)
320+
{
292321
return -RYM_ERR_CODE;
322+
}
323+
else
324+
{
325+
ctx->begin_called = 1;
326+
}
293327

294328
code = RYM_CODE_SOH;
295329
_rym_send_packet(ctx, code, index);
296330

297-
rt_device_set_rx_indicate(ctx->dev, _rym_rx_ind);
298331
getc_ack = _rym_getchar(ctx);
299332

300333
if (getc_ack != RYM_CODE_ACK)
@@ -440,25 +473,43 @@ static rt_err_t _rym_do_send_trans(struct rym_ctx *ctx)
440473
rt_size_t data_sz;
441474
rt_uint32_t index = 1;
442475
rt_uint8_t getc_ack;
476+
rt_size_t errors = 0;
477+
rt_uint8_t have_packet = 0;
443478

444479
data_sz = _RYM_STX_PKG_SZ;
445480
while (1)
446481
{
447-
if (!ctx->on_data)
482+
if (!have_packet)
448483
{
449-
return -RYM_ERR_CODE;
484+
if (!ctx->on_data)
485+
{
486+
return -RYM_ERR_CODE;
487+
}
488+
/* Prepare the next payload only once; reuse it on retransmits. */
489+
code = ctx->on_data(ctx, ctx->buf + 3, data_sz - 5);
490+
have_packet = 1;
450491
}
451-
code = ctx->on_data(ctx, ctx->buf + 3, data_sz - 5);
452492

453-
_rym_send_packet(ctx, code, index);
454-
index++;
455-
rt_device_set_rx_indicate(ctx->dev, _rym_rx_ind);
493+
if (_rym_send_packet(ctx, code, index) != RT_EOK)
494+
{
495+
return -RYM_ERR_CODE;
496+
}
456497

457498
getc_ack = _rym_getchar(ctx);
458499
if (getc_ack != RYM_CODE_ACK)
459500
{
460-
return -RYM_ERR_ACK;
501+
if (getc_ack == RYM_CODE_CAN)
502+
return -RYM_ERR_CAN;
503+
504+
if (++errors > RYM_MAX_ERRORS)
505+
return -RYM_ERR_ACK;
506+
507+
/* Not ACK: retry the same packet until we hit the error limit. */
508+
continue;
461509
}
510+
errors = 0;
511+
have_packet = 0;
512+
index++;
462513

463514
if (ctx->stage == RYM_STAGE_FINISHING)
464515
break;
@@ -478,7 +529,10 @@ static rt_err_t _rym_do_fin(struct rym_ctx *ctx)
478529
/* we already got one EOT in the caller. invoke the callback if there is
479530
* one. */
480531
if (ctx->on_end)
532+
{
481533
ctx->on_end(ctx, ctx->buf + 3, 128);
534+
ctx->end_called = 1;
535+
}
482536

483537
_rym_putchar(ctx, RYM_CODE_NAK);
484538
code = _rym_read_code(ctx, RYM_WAIT_PKG_TICK);
@@ -538,7 +592,6 @@ static rt_err_t _rym_do_send_fin(struct rym_ctx *ctx)
538592
rt_uint8_t getc_ack;
539593

540594
data_sz = _RYM_SOH_PKG_SZ;
541-
rt_device_set_rx_indicate(ctx->dev, _rym_rx_ind);
542595

543596
_rym_putchar(ctx, RYM_CODE_EOT);
544597
getc_ack = _rym_getchar(ctx);
@@ -564,7 +617,13 @@ static rt_err_t _rym_do_send_fin(struct rym_ctx *ctx)
564617
}
565618

566619
if (ctx->on_end && ctx->on_end(ctx, ctx->buf + 3, data_sz - 5) != RYM_CODE_SOH)
620+
{
567621
return -RYM_ERR_CODE;
622+
}
623+
else
624+
{
625+
ctx->end_called = 1;
626+
}
568627

569628
code = RYM_CODE_SOH;
570629

@@ -582,32 +641,32 @@ static rt_err_t _rym_do_recv(
582641
rt_err_t err;
583642

584643
ctx->stage = RYM_STAGE_NONE;
644+
ctx->begin_called = 0;
645+
ctx->end_called = 0;
646+
ctx->last_err = RT_EOK;
585647

586648
ctx->buf = rt_malloc(_RYM_STX_PKG_SZ);
587649
if (ctx->buf == RT_NULL)
588650
return -RT_ENOMEM;
589651

590652
err = _rym_do_handshake(ctx, handshake_timeout);
591653
if (err != RT_EOK)
592-
{
593-
rt_free(ctx->buf);
594-
return err;
595-
}
654+
goto __cleanup;
596655
while (1)
597656
{
598657
err = _rym_do_trans(ctx);
658+
if (err != RT_EOK)
659+
goto __cleanup;
599660

600661
err = _rym_do_fin(ctx);
601662
if (err != RT_EOK)
602-
{
603-
rt_free(ctx->buf);
604-
return err;
605-
}
663+
goto __cleanup;
606664

607665
if (ctx->stage == RYM_STAGE_FINISHED)
608666
break;
609667
}
610-
rt_free(ctx->buf);
668+
__cleanup:
669+
_rym_cleanup(ctx, err);
611670
return err;
612671
}
613672

@@ -618,33 +677,27 @@ static rt_err_t _rym_do_send(
618677
rt_err_t err;
619678

620679
ctx->stage = RYM_STAGE_NONE;
680+
ctx->begin_called = 0;
681+
ctx->end_called = 0;
682+
ctx->last_err = RT_EOK;
621683

622684
ctx->buf = rt_malloc(_RYM_STX_PKG_SZ);
623685
if (ctx->buf == RT_NULL)
624686
return -RT_ENOMEM;
625687

626688
err = _rym_do_send_handshake(ctx, handshake_timeout);
627689
if (err != RT_EOK)
628-
{
629-
rt_free(ctx->buf);
630-
return err;
631-
}
690+
goto __cleanup;
632691

633692
err = _rym_do_send_trans(ctx);
634693
if (err != RT_EOK)
635-
{
636-
rt_free(ctx->buf);
637-
return err;
638-
}
694+
goto __cleanup;
639695

640696
err = _rym_do_send_fin(ctx);
641697
if (err != RT_EOK)
642-
{
643-
rt_free(ctx->buf);
644-
return err;
645-
}
646-
647-
rt_free(ctx->buf);
698+
goto __cleanup;
699+
__cleanup:
700+
_rym_cleanup(ctx, err);
648701
return err;
649702
}
650703

@@ -666,9 +719,12 @@ rt_err_t rym_recv_on_device(
666719
_rym_the_ctx = ctx;
667720

668721
ctx->on_begin = on_begin;
669-
ctx->on_data = on_data;
670-
ctx->on_end = on_end;
671-
ctx->dev = dev;
722+
ctx->on_data = on_data;
723+
ctx->on_end = on_end;
724+
ctx->begin_called = 0;
725+
ctx->end_called = 0;
726+
ctx->last_err = RT_EOK;
727+
ctx->dev = dev;
672728
rt_sem_init(&ctx->sem, "rymsem", 0, RT_IPC_FLAG_FIFO);
673729

674730
odev_rx_ind = dev->rx_indicate;
@@ -723,9 +779,12 @@ rt_err_t rym_send_on_device(
723779
_rym_the_ctx = ctx;
724780

725781
ctx->on_begin = on_begin;
726-
ctx->on_data = on_data;
727-
ctx->on_end = on_end;
728-
ctx->dev = dev;
782+
ctx->on_data = on_data;
783+
ctx->on_end = on_end;
784+
ctx->begin_called = 0;
785+
ctx->end_called = 0;
786+
ctx->last_err = RT_EOK;
787+
ctx->dev = dev;
729788
rt_sem_init(&ctx->sem, "rymsem", 0, RT_IPC_FLAG_FIFO);
730789

731790
odev_rx_ind = dev->rx_indicate;

components/utilities/ymodem/ymodem.h

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* 2013-04-14 Grissiom initial implementation
1010
* 2019-12-09 Steven Liu add YMODEM send protocol
1111
* 2022-08-04 Meco Man move error codes to rym_code to silence warnings
12+
* 2026-02-01 wdfk-prog update ymodem callbacks and error handling
1213
*/
1314

1415
#ifndef __YMODEM_H__
@@ -81,13 +82,22 @@ enum rym_stage
8182
};
8283

8384
struct rym_ctx;
84-
/* When receiving files, the buf will be the data received from ymodem protocol
85+
/**
86+
* @brief YMODEM callback signature.
87+
*
88+
* @param ctx The context of the current session.
89+
*
90+
* @note When receiving files, the buf will be the data received from ymodem protocol
8591
* and the len is the data size.
8692
*
8793
* When sending files, the len is the buf size in RYM. The callback need to
8894
* fill the buf with data to send. Returning RYM_CODE_EOT will terminate the
8995
* transfer and the buf will be discarded. Any other return values will cause
9096
* the transfer continue.
97+
*
98+
* @note Keep this typedef unchanged for compatibility with external packages.
99+
* To allow error-aware handling without breaking ABI, add state fields
100+
* (e.g. ctx->last_err) in rym_ctx for callbacks to inspect.
91101
*/
92102
typedef enum rym_code(*rym_callback)(struct rym_ctx *ctx, rt_uint8_t *buf, rt_size_t len);
93103

@@ -98,14 +108,37 @@ typedef enum rym_code(*rym_callback)(struct rym_ctx *ctx, rt_uint8_t *buf, rt_si
98108
*/
99109
struct rym_ctx
100110
{
111+
/**
112+
* @brief Data callback for each received/sent block.
113+
*/
101114
rym_callback on_data;
115+
/**
116+
* @brief Begin callback for the initial header block.
117+
*/
102118
rym_callback on_begin;
119+
/**
120+
* @brief End callback for session finalization.
121+
* Callers should check ctx->last_err to distinguish success vs failure.
122+
*/
103123
rym_callback on_end;
104124
/* When error happened, user need to check this to get when the error has
105125
* happened. */
106126
enum rym_stage stage;
107127
/* user could get the error content through this */
108128
rt_uint8_t *buf;
129+
/**
130+
* @brief Callback lifecycle state: set when on_begin succeeds.
131+
*/
132+
rt_uint8_t begin_called;
133+
/**
134+
* @brief Callback lifecycle state: set when on_end is invoked.
135+
*/
136+
rt_uint8_t end_called;
137+
/**
138+
* @brief Last transfer error seen by the core state machine.
139+
* on_end can inspect this to distinguish success vs failure.
140+
*/
141+
rt_err_t last_err;
109142

110143
struct rt_semaphore sem;
111144

0 commit comments

Comments
 (0)