apps/system/ymodem: Optimized ymodem for burn during bootloader

If device automatically enters the ymodem rb mode when it starts up,
the PC tool does not need to send rb command anymore. Here, when a
specified number of consecutive 'C' are received, the subsequent
ymodem protocol content will continue.

Signed-off-by: wangxingxing <wangxingxing@xiaomi.com>
This commit is contained in:
wangxingxing 2025-04-14 17:42:59 +08:00 committed by Donny(董九柱)
parent 8804f086b3
commit 2908f10154

View file

@ -25,6 +25,7 @@ import io
import os
import sys
import termios
import time
import serial
@ -92,25 +93,31 @@ def ymodem_stdprogress(data):
def ymodem_ser_read(size):
global fd_serial
data = fd_serial.read(size)
return data
def ymodem_ser_write(data):
global fd_serial
fd_serial.write(data)
fd_serial.flush()
def ymodem_ser_clear():
global fd_serial
fd_serial.reset_input_buffer()
fd_serial.reset_output_buffer()
def ymodem_ser_recv_repeat_c(num, timeout):
except_data = b"C" * num
start_time = time.time()
while time.time() - start_time < timeout:
read_data = ymodem_ser_read(num)
if read_data == except_data:
return 0
return -1
def calc_crc16(data, crc=0):
crctable = [
0x0000,
@ -385,7 +392,7 @@ class ymodem:
write=ymodem_stdwrite,
progress=ymodem_stdprogress,
clear=ymodem_stdclear,
timeout=100,
timeout=10,
maxretry=RETRIESMAX,
debug="",
customsize=0,
@ -452,15 +459,30 @@ class ymodem:
return PACKET_SIZE
def recv_cmd(self, cmd):
chunk = self.read(1)
if chunk == NAK:
return -EAGAIN
if chunk != cmd:
self.debug("should be " + binascii.hexlify(cmd).decode("utf-8"))
self.debug("but receive " + binascii.hexlify(chunk).decode("utf-8") + "\n")
return -EINVAL
return 0
start_time = time.time()
unexpected_buffer = b""
while time.time() - start_time < self.timeout:
chunk = self.read(1)
if chunk == cmd:
if unexpected_buffer:
# try to decode to UTF-8
decoded = unexpected_buffer.decode("utf-8", errors="replace")
self.progress(f"Ignored unexpected text: {decoded}\n")
return 0
elif chunk == NAK:
if unexpected_buffer:
decoded = unexpected_buffer.decode("utf-8", errors="replace")
self.progress(f"Ignored unexpected text: {decoded}\n")
print("EAGAIN!!!")
return -EAGAIN
elif chunk == b"":
continue
else:
unexpected_buffer += chunk
if unexpected_buffer:
decoded = unexpected_buffer.decode("utf-8", errors="replace")
self.progress(f"Ignored unexpected text: {decoded}\n")
return -EINVAL
def send_handshake(self):
self.write(CRC)
@ -686,7 +708,7 @@ class ymodem:
return -1
self.progress("recv ret %d\n" % ret)
self.debug("recv frist packet\n")
self.debug("recv first packet\n")
self.retries += 1
continue
@ -807,6 +829,29 @@ if __name__ == "__main__":
help="This opthin set max retry for transmission",
)
parser.add_argument(
"-c",
"--countofc",
type=int,
default=0,
help="""
When this value is greater than 0, it means that the rb command is not sent to
device, but to receive a specified number of consecutive 'C'.
If do not receive continuous 'C', time out and exit.
""",
)
parser.add_argument(
"-w",
"--waitctimeout",
type=int,
default=30,
help="""
When the -c parameter is valid, this arg will determine whether it has timed out
while waiting for consecutive Cs. The unit is second.
""",
)
parser.add_argument(
"--debug", help="This opthin is save debug log on host", default=""
)
@ -821,19 +866,34 @@ if __name__ == "__main__":
for i in args.recvfrom:
recvfile += i + " "
if args.kblocksize:
recvfile += "-k %d " % (args.kblocksize)
fd_serial.write(("sb %s\r\n" % (recvfile)).encode())
fd_serial.flush()
tmp = fd_serial.read(len(("sb %s\r\n" % (recvfile)).encode()) - 1)
fd_serial.reset_input_buffer()
else:
if args.sendto:
cmd = ("rb -f %s\r\n" % (args.sendto[0])).encode()
if args.countofc:
ret = ymodem_ser_recv_repeat_c(args.countofc, args.waitctimeout)
if ret < 0:
print("read C timeout")
fd_serial.close()
sys.exit(1)
else:
cmd = ("rb\r\n").encode()
if args.sendto:
cmd = ("rb -f %s" % args.sendto[0]).encode()
else:
cmd = "rb".encode()
fd_serial.write(cmd)
fd_serial.flush()
fd_serial.read(len(cmd))
if args.kblocksize:
cmd += (" -k %d" % args.kblocksize).encode()
cmd += b"\r\n"
fd_serial.write(cmd)
fd_serial.flush()
fd_serial.read(len(cmd))
fd_serial.reset_input_buffer()
sbrb = ymodem(