ymodem:set the transport retry as optional

The waiting duration of the rb/sb command can be determined based on
the number of retransmissions, so that ymodem can restart to the normal
system after running in the bootloader for a short period of time.

Signed-off-by: anjiahao <anjiahao@xiaomi.com>
This commit is contained in:
anjiahao 2023-12-27 16:16:02 +08:00 committed by Xiang Xiao
parent e4e46c3f53
commit 85ae6f0dfd
5 changed files with 114 additions and 63 deletions

View file

@ -330,6 +330,12 @@ static void show_usage(FAR const char *progname)
fprintf(stderr,
"\t-t|--threshold <size>: Threshold for writing asynchronously."
"Threshold must be less than or equal buffersize, Default: 0kB\n");
fprintf(stderr,
"\t-i|--interval <time>: Waiting interval for transmitting data."
"Max:255 Min:1 Default:15 unit: 100 milliseconds\n");
fprintf(stderr,
"\t-r|--retry <retry>: Number of retries."
"Will try <retry> times to transmitting, Default:100\n");
fprintf(stderr,
"\t-k <size>: Use a custom size to tansfer, Default: 1kB\n");
@ -352,13 +358,17 @@ int main(int argc, FAR char *argv[])
{"buffersize", 1, NULL, 'b'},
{"skip_prefix", 1, NULL, 'p'},
{"skip_suffix", 1, NULL, 's'},
{"threshold", 1, NULL, 't'}
{"threshold", 1, NULL, 't'},
{"interval", 1, NULL, 'i'},
{"retry", 1, NULL, 'r'},
};
memset(&priv, 0, sizeof(priv));
memset(&ctx, 0, sizeof(ctx));
while ((ret = getopt_long(argc, argv, "b:d:f:hk:p:s:t:", options, NULL))
!= ERROR)
ctx.interval = 15;
ctx.retry = 100;
while ((ret = getopt_long(argc, argv, "b:d:f:hk:p:s:t:i:r:",
options, NULL)) != ERROR)
{
switch (ret)
{
@ -391,6 +401,12 @@ int main(int argc, FAR char *argv[])
case 't':
priv.threshold = atoi(optarg) * 1024;
break;
case 'i':
ctx.interval = atoi(optarg);
break;
case 'r':
ctx.retry = atoi(optarg);
break;
case '?':
default:

View file

@ -275,6 +275,12 @@ static void show_usage(FAR const char *progname)
fprintf(stderr,
"\t-b|--buffersize <size>: Asynchronously send buffer size."
"If greater than 0, accept data asynchronously, Default: 0kB\n");
fprintf(stderr,
"\t-i|--interval <time>: Waiting interval for transmitting data."
"Max:255 Min:1 Default:15 unit: 100 milliseconds\n");
fprintf(stderr,
"\t-r|--retry <retry>: Number of retries."
"Will try <retry> times to transmitting, Default:100\n");
fprintf(stderr,
"\t-k <size>: Use a custom size to tansfer, Default: 1kB\n");
@ -294,10 +300,14 @@ int main(int argc, FAR char *argv[])
struct option options[] =
{
{"buffersize", 1, NULL, 'b'},
{"interval", 1, NULL, 'i'},
{"retry", 1, NULL, 'r'},
};
memset(&priv, 0, sizeof(priv));
memset(&ctx, 0, sizeof(ctx));
ctx.interval = 15;
ctx.retry = 100;
while ((ret = getopt_long(argc, argv, "b:d:k:h", options, NULL))
!= ERROR)
{
@ -317,6 +327,13 @@ int main(int argc, FAR char *argv[])
}
break;
case 'i':
ctx.interval = atoi(optarg);
break;
case 'r':
ctx.retry = atoi(optarg);
break;
case 'h':
case '?':
default:

View file

@ -21,7 +21,6 @@ import binascii
import datetime
import io
import os
import signal
import sys
import termios
@ -54,47 +53,35 @@ def format_time(seconds):
return time
class Timeout(Exception):
pass
def timeout_handle(signum, frame):
sys.stderr.write("timeout!\n")
sys.stderr.flush()
raise Timeout("Timeout")
def ymodem_stdread(size):
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
signal.signal(signal.SIGALRM, timeout_handle)
signal.alarm(3)
try:
new_settings = termios.tcgetattr(fd)
new_settings[3] &= ~(termios.ICANON | termios.ECHO)
termios.tcsetattr(fd, termios.TCSADRAIN, new_settings)
data = sys.stdin.buffer.read(size)
sys.stdin.flush()
return data
except Timeout:
return
finally:
signal.alarm(0)
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
new_settings = termios.tcgetattr(fd)
new_settings[3] &= ~(termios.ICANON | termios.ECHO)
termios.tcsetattr(fd, termios.TCSADRAIN, new_settings)
data = sys.stdin.buffer.read(size)
termios.tcflush(sys.stdin, termios.TCIFLUSH)
sys.stdin.flush()
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return data
def ymodem_stdwrite(data):
fd = sys.stdout.fileno()
old_settings = termios.tcgetattr(fd)
try:
new_settings = termios.tcgetattr(fd)
new_settings[3] &= ~(termios.ICANON | termios.ECHO)
termios.tcsetattr(fd, termios.TCSADRAIN, new_settings)
data = sys.stdout.buffer.write(data)
sys.stdout.flush()
return data
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
new_settings = termios.tcgetattr(fd)
new_settings[3] &= ~(termios.ICANON | termios.ECHO)
termios.tcsetattr(fd, termios.TCSADRAIN, new_settings)
data = sys.stdout.buffer.write(data)
termios.tcflush(sys.stdout, termios.TCIFLUSH)
sys.stdout.flush()
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return data
def ymodem_stdclear():
sys.stdin.flush()
sys.stdout.flush()
def ymodem_stdprogress(data):
@ -115,6 +102,13 @@ def ymodem_ser_write(data):
fd_serial.flush()
def ymodem_ser_clear():
global fd_serial
fd_serial.reset_input_buffer()
fd_serial.reset_output_buffer()
def calc_crc16(data, crc=0):
crctable = [
0x0000,
@ -388,15 +382,21 @@ class ymodem:
read=ymodem_stdread,
write=ymodem_stdwrite,
progress=ymodem_stdprogress,
clear=ymodem_stdclear,
timeout=100,
maxretry=RETRIESMAX,
debug="",
customsize=0,
):
self.read = read
self.write = write
self.clear = clear
self.timeout = timeout
self.maxretry = maxretry
self.progress = progress
self.customsize = customsize
self.retries = 0
if debug != "":
self.debugfd = open(debug, "w+")
else:
@ -460,24 +460,28 @@ class ymodem:
return 0
def send_handshake(self):
self.write(CRC)
while self.retries < self.maxretry:
chunk = self.read(1)
if chunk == CRC:
return True
else:
self.retries += 1
self.clear()
self.progress("too many retries\n")
return False
def send(self, filelist):
retries = 0
need_sendfile_num = len(filelist)
cnt = 0
now = datetime.datetime.now()
base = float(int(now.timestamp() * 1000)) / 1000
totolbytes = 0
while retries < 10:
self.write(CRC)
chunk = self.read(1)
if chunk == CRC:
break
else:
retries += 1
if retries == 10:
return False
if not self.send_handshake():
return -EINVAL
while need_sendfile_num != 0:
now = datetime.datetime.now()
@ -485,13 +489,10 @@ class ymodem:
self.init_pkt()
self.head = SOH
filename = os.path.basename(filelist[cnt])
self.progress("name:" + filename)
self.data = filename.encode("utf-8")
self.data = self.data + bytes([0x00] * 1)
filesize = os.path.getsize(filelist[cnt])
sendfilesize = 0
self.progress(" filesize:%d\n" % (filesize))
self.data = self.data + str(filesize).encode("utf-8")
self.data = self.data.ljust(self.get_pkt_size(), b"\x00")
self.send_pkt()
@ -500,14 +501,21 @@ class ymodem:
if ret == -EAGAIN:
continue
elif ret == -EINVAL:
if self.send_handshake():
continue
return ret
ret = self.recv_cmd(CRC)
if ret == -EAGAIN:
continue
elif ret == -EINVAL:
if self.send_handshake():
continue
return ret
self.progress("name:" + filename)
self.progress(" filesize:%d\n" % (filesize))
self.add_seq()
readfd = open(filelist[cnt], "rb")
self.progress(" ")
@ -656,7 +664,6 @@ class ymodem:
return 0
def recv(self):
retries = 0
now = datetime.datetime.now()
base = float(int(now.timestamp() * 1000)) / 1000
totolbytes = 0
@ -673,12 +680,12 @@ class ymodem:
continue
elif ret < 0:
if retries > RETRIESMAX:
if self.retries > self.maxretry:
return -1
self.progress("recv ret %d\n" % ret)
self.debug("recv frist packet\n")
retries += 1
self.retries += 1
continue
filename = bytes.decode(self.data.split(b"\x00")[0], "utf-8")
@ -699,9 +706,9 @@ class ymodem:
ret = self.recv_packet()
if ret < 0:
self.debug("recv a bad data packet\n")
if retries > RETRIESMAX:
if self.retries > self.maxretry:
return -1
retries += 1
self.retries += 1
continue
size = 0
@ -791,6 +798,13 @@ if __name__ == "__main__":
""",
)
parser.add_argument(
"--maxretry",
type=int,
default=RETRIESMAX,
help="This opthin set max retry for transmission",
)
parser.add_argument(
"--debug", help="This opthin is save debug log on host", default=""
)
@ -821,9 +835,13 @@ if __name__ == "__main__":
customsize=args.kblocksize * 1024,
read=ymodem_ser_read,
write=ymodem_ser_write,
clear=ymodem_ser_clear,
maxretry=args.maxretry,
)
else:
sbrb = ymodem(debug=args.debug, customsize=args.kblocksize * 1024)
sbrb = ymodem(
debug=args.debug, customsize=args.kblocksize * 1024, maxretry=args.maxretry
)
if len(args.filelist) == 0:
sbrb.progress("receiving\n")

View file

@ -60,8 +60,6 @@
#define CAN 0x18 /* Two of these in succession aborts transfer */
#define CRC 0x43 /* 'C' == 0x43, request 16-bit CRC */
#define MAX_RETRIES 100
/****************************************************************************
* Private Functions
****************************************************************************/
@ -216,7 +214,7 @@ recv_packet:
/* other errors, like ETIMEDOUT, EILSEQ, EBADMSG... */
tcflush(ctx->recvfd, TCIOFLUSH);
if (++retries > MAX_RETRIES)
if (++retries > ctx->retry)
{
ymodem_debug("recv_file: too many errors, cancel!!\n");
goto cancel;
@ -340,7 +338,7 @@ static int ymodem_send_file(FAR struct ymodem_ctx_s *ctx)
int ret;
ymodem_debug("waiting handshake\n");
for (retries = 0; retries < MAX_RETRIES; retries++)
for (retries = 0; retries < ctx->retry; retries++)
{
ret = ymodem_recv_cmd(ctx, CRC);
if (ret >= 0)
@ -349,7 +347,7 @@ static int ymodem_send_file(FAR struct ymodem_ctx_s *ctx)
}
}
if (retries >= MAX_RETRIES)
if (retries >= ctx->retry)
{
ymodem_debug("waiting handshake error\n");
return -ETIMEDOUT;
@ -581,7 +579,7 @@ int ymodem_recv(FAR struct ymodem_ctx_s *ctx)
tcgetattr(ctx->recvfd, &term);
memcpy(&saveterm, &term, sizeof(struct termios));
cfmakeraw(&term);
term.c_cc[VTIME] = 15;
term.c_cc[VTIME] = ctx->interval;
term.c_cc[VMIN] = 255;
tcsetattr(ctx->recvfd, TCSANOW, &term);

View file

@ -50,6 +50,8 @@ struct ymodem_ctx_s
CODE int (*packet_handler)(FAR struct ymodem_ctx_s *ctx);
size_t custom_size;
FAR void *priv;
uint8_t interval;
int retry;
/* Public data */