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+
183205static 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 ;
0 commit comments