1#!/usr/bin/env python
2import os, sys, re, time
3from ymodem import YModem
4
5try:
6    import serial
7    from serial.tools import miniterm
8except:
9    print("\nNot found pyserial, please install it by: \nsudo python%d -m pip install pyserial" % (sys.version_info.major))
10    sys.exit(-1)
11
12def match_and_send(serialport, pattern, command, timeout):
13    """ receive serial data, and check it with pattern """
14    pattern = re.compile(pattern)
15    start   = time.time()
16    while (time.time() - start) < timeout:
17        #line = serialport.readline()
18        #timeout dont work for 'readline', so using 'read_until' instead
19        line = serialport.read_until(b'\n')
20        if len(line) == 0:
21            continue
22        match = pattern.search(line)
23        if match:
24            if command:
25                serialport.write(command)
26            print(line.decode('UTF-8',errors='ignore'))
27            sys.stdout.flush()
28            return match
29        print(line.decode('UTF-8',errors='ignore'))
30        sys.stdout.flush()
31    return None
32
33def send_and_match(serialport, command, pattern, timeout):
34    """ receive serial data, and check it with pattern """
35    if command:
36        serialport.write(command)
37    if pattern == b'':
38        #only send
39        sys.stdout.flush()
40        return None
41    pattern = re.compile(pattern)
42    start   = time.time()
43    while (time.time() - start) < timeout:
44        #line = serialport.readline()
45        #timeout dont work for 'readline', so using 'read_until' instead
46        line = serialport.read_until(b'\n')
47        if len(line) == 0:
48            continue
49        print(line.decode('UTF-8',errors='ignore'))
50        sys.stdout.flush()
51        match = pattern.search(line)
52        if match:
53            return match
54    return None
55
56#burn file in 2_boot
57def burn_bin_file(serialport, filename, address):
58    if not os.path.exists(filename):
59        print("[ScriptPrint] File \"%s\" is not existed." % filename)
60        return False
61
62    #get address
63    if address == "0" or address == "0x0" or address == "0x00":
64        # get flash address
65        match = send_and_match(serialport, b'1', b'Backup part addr:([0-9a-fxA-F]*)', 5)
66        if not match:
67            print("[ScriptPrint] Can not get flash address")
68            return False
69        address = match.group(1)
70    else:
71        send_and_match(serialport, b'1', b'', 0)
72        address = address.encode()
73
74    # set flash address
75    match = send_and_match(serialport, address + b'\r\n', b'CCCC', 30)
76    if not match:
77        print("[ScriptPrint] Can not enter ymodem mode")
78        return False
79
80    # send binary file
81    def sender_getc(size):
82        return serialport.read(size) or None
83
84    def sender_putc(data, timeout=15):
85        return serialport.write(data)
86
87    sender = YModem(sender_getc, sender_putc)
88    sent = sender.send_file(filename)
89    return True
90
91def burn_bin_files(portnum, baudrate, bin_files):
92    # open serial port
93    serialport = serial.Serial()
94    serialport.port = portnum
95    serialport.baudrate = baudrate
96    serialport.parity   = "N"
97    serialport.bytesize = 8
98    serialport.stopbits = 1
99    serialport.timeout  = 1
100
101    try:
102        serialport.open()
103    except Exception as e:
104        raise Exception("[ScriptPrint] Failed to open serial port: %s!" % portnum)
105
106    print("[ScriptPrint] Try to reboot...")
107
108    # 重启单板并确保进入2nd-boot
109    for i in range(10):
110        match = send_and_match(serialport, b'\n', b'\(ash', 2)
111        if match:
112            # 如果在cli模式,通过reboot重启系统
113            print("[ScriptPrint] Reboot from CLI")
114            send_and_match(serialport, b'reboot\n', b'', 0)
115            match = match_and_send(serialport, b'2ndboot cli menu', b'w', 5)
116            if match:
117                print("[ScriptPrint] check if in boot")
118                match = send_and_match(serialport, b'\n', b'aos boot', 2)
119                if match:
120                    # 进入boot模式,退出
121                    break
122
123        match = send_and_match(serialport, b'\n', b'aos boot', 2)
124        if match:
125            # 如果在boot模式,通过2重启系统
126            print("[ScriptPrint] Reboot from 2nd-boot")
127            send_and_match(serialport, b'2\n', b'', 0)
128            match = match_and_send(serialport, b'2ndboot cli menu', b'w', 5)
129            if match:
130                print("[ScriptPrint] check if in boot")
131                match = send_and_match(serialport, b'\n', b'aos boot', 2)
132                if match:
133                    # 进入boot模式,退出
134                    break
135        else:
136            # 一些solution需要先退出命令行模式回到CLI
137            print("[ScriptPrint] change to CLI mode")
138            send_and_match(serialport, b'\n\x03', b'', 0) #ctrl-C, ETX, 本文结束
139            send_and_match(serialport, b'\n\x04', b'', 0) #ctrl-D, EOT, 传输结束
140            time.sleep(2)
141
142        time.sleep(2)
143
144    if i >= 9 :
145        print("[ScriptPrint] reboot fail")
146        print("[ScriptPrint] Please connect the serial port of the board to the PC, then reset the board");
147        # close serial port
148        serialport.close()
149        return False
150
151    # boot中下载文件
152    print("[ScriptPrint] Downloading files...")
153    for bin_file in bin_files:
154        if not burn_bin_file(serialport, bin_file[0], bin_file[1]):
155            print("[ScriptPrint] Download file %s failed." % bin_file[0])
156            serialport.close()
157            return False
158
159    # switch partition
160    print("[ScriptPrint] Swap AB partition")
161    send_and_match(serialport, b'3\n', b'', 0)
162    time.sleep(0.5)
163    send_and_match(serialport, b'4\n', b'', 0)
164    time.sleep(0.5)
165    send_and_match(serialport, b'3\n', b'', 0)
166    time.sleep(0.5)
167    send_and_match(serialport, b'2\n', b'', 0)
168    time.sleep(0.1)
169    # workaround retry issue in 2nd boot
170    match = send_and_match(serialport, b'\n'*16, b'2ndboot cli menu', 5)
171    if match:
172        print("[ScriptPrint] Burn \"%s\" success." % bin_files)
173
174    # close serial port
175    serialport.close()
176
177    if match:
178        return True
179    else:
180        return False
181
182def main():
183    length = len(sys.argv)
184    if (length < 5) or (length % 2 == 0):
185        print("Usage demo: ./flash_program_ll.py COM6 1500000 sendfile flash_addr\n")
186        return 1
187
188    serialport = sys.argv[1]
189    baudrate = sys.argv[2]
190    bin_files = []
191    for i in range(3, length, 2):
192        filename = sys.argv[i]
193        address = sys.argv[i + 1]
194        bin_files.append((filename, address))
195
196    ret = burn_bin_files(serialport, baudrate, bin_files)
197    sys.exit(ret)
198
199if __name__ == "__main__":
200    main()
201