#!/usr/bin/env python3 # hn70ap_update_serial.py - Sends an update to the hn70ap board via serial port. # Copyright (C) 2018 Sebastien Lorquet # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Boston, MA 02110-1301 USA # This program sends a firmware update image to a hn70ap port via a serial port. # # Note that this tool is not a part of NuttX and has a different licence than # the NuttX RTOS. import sys, serial, os, time INST_WRITE = (0x00).to_bytes(1, byteorder='big') INST_SPEED = (0x02).to_bytes(1, byteorder='big') FDELIMB = 0x7E FDELIM = FDELIMB.to_bytes(1, byteorder='big') FESCB = 0x7D FESC = FESCB.to_bytes(1, byteorder='big') CRC_INIT = 0xFFFF CRC_GOOD = 0xF0B8 STATE_NOSYNC = 0 STATE_SYNC = 1 STATE_DATA = 2 STATE_ESC = 3 #------------------------------------------------------------------------------- def crc16(crc, val): crc ^= val & 0xFF crc ^= (crc<<4) & 0xFF crc = (crc>>8) ^ ((crc&0xFF)<<8) ^ ((crc&0xFF)<<3) ^ ((crc&0xFF)>>4) return crc #------------------------------------------------------------------------------- def frame_send(port, frame): def write_esc(port,data): if data == FDELIMB or data == FESCB: port.write(FESC) data ^= 0x20 port.write(data.to_bytes(1, byteorder='big')) #print('out dat=',frame.hex()) crc = CRC_INIT port.write(FDELIM) for i in range(len(frame)): b = frame[i] crc = crc16(crc, b) write_esc(port, b) crc ^= 0xFFFF; write_esc(port,crc&0xFF) write_esc(port,crc>>8) #print('out crc=',crc.to_bytes(2,byteorder='little').hex()) port.write(FDELIM) #------------------------------------------------------------------------------- def frame_receive(port, maxlen): state = STATE_NOSYNC packet = bytearray() crc = CRC_INIT maxlen += 2 #include the length required to receive the CRC after the user bytes while True: ret = port.read(1) if len(ret)==0: raise Exception("Receive timeout!") #print("state",state,"char",ret.hex(),"'",ret,"'", "len",len(packet)) b = ret[0] if state == STATE_NOSYNC: if ret == FDELIM: state = STATE_SYNC else: print("\x1B[32m", chr(ret[0]), "\x1B[0m", sep='', end='', flush=True) elif state == STATE_SYNC: if ret != FDELIM: packet.append(b) crc = crc16(crc, b) state = STATE_DATA if len(packet) > maxlen: print("packet too long") break elif state == STATE_DATA: if ret == FESC: state = STATE_ESC elif ret == FDELIM: break else: packet.append(b) crc = crc16(crc, b) if len(packet) > maxlen: print("packet too long") break elif state == STATE_ESC: b ^= 0x20; packet.append(b) crc = crc16(crc, b) if len(packet) > maxlen: print("packet too long") break state = STATE_DATA #check crc #print("rx crc=%04X"%crc) if crc != CRC_GOOD: raise Exception("bad CRC in rx frame") #done return packet[0:len(packet)-2] #------------------------------------------------------------------------------- if len(sys.argv) != 3: print("hn70ap_serial_update.py ") sys.exit(1) up = open(sys.argv[2], 'rb') #a short timeout will not work since erase needs time port = serial.Serial(port=sys.argv[1], baudrate=230400, timeout=100) done = False while not done: print("Sending prompt...") port.reset_input_buffer() port.reset_output_buffer() port.write(b"\r\n\r\nupdate serial\r\n") while True: resp = port.readline() if len(resp) == 0: break #print(">",resp) if b"hn70ap serial update waiting..." in resp: print("Good response") done = True break if not done: print("Unexpected response, trying again") uplen = os.fstat(up.fileno()).st_size print("upload start") speed = 230400 frame_send(port, INST_SPEED+speed.to_bytes(length=4, byteorder='big')) rx = frame_receive(port,1+4) print("change speed status: ",rx[1], " - packet: ", rx.hex()); if rx[1] == 0: print("Using new speed") d = port.get_settings() d['baudrate'] = speed port.apply_settings(d); time.sleep(1) print("ready at new speed") port.reset_input_buffer() port.reset_output_buffer() BLOCKSIZE = 256 done = 0 seq = 0 while True: buf = up.read(BLOCKSIZE) l = len(buf) s = seq.to_bytes(2,byteorder='big') #print("seq=", s.hex(), "len=", l, buf.hex()) frame_send(port, INST_WRITE+s+buf) rx = frame_receive(port,1+2+BLOCKSIZE) #print(rx.hex()) status = rx[3]; #print("status:",status); if status == 3: print("send complete") break done += l print("sent bytes",done,"of",uplen, end='\r') if l < BLOCKSIZE: break seq = (seq + 1) & 0xFFFF print("\nupload complete") port.close() up.close()