super(ChecksumMismatch, self).__init__(msg)
+class PermissionDenied(BootloaderError):
+ def __init__(self, command):
+ msg = 'Target reported permission denied for command {}'.format(command)
+
+ super(PermissionDenied, self).__init__(msg)
+
class Command:
def __init__(self, num, name):
self._num = num
def __init__(self):
super(LoadChunkCommand, self).__init__(0x1b329768, 'LOAD_CHUNK')
+class StartAppCommand(Command):
+ def __init__(self):
+ super(StartAppCommand, self).__init__(0xd27df1bf, 'START_APP')
+
class Application:
SECTOR_SIZE = 1024
MAX_NUM_CHUNKS = 256
self._do_write = args.write
self._do_verify = args.verify
+ self._do_start = args.start
self._filename = args.filename
self._offset = args.offset
def run(self):
chunks = []
- with open(self._filename, 'rb') as f:
- while True:
- chunk = f.read(Application.SECTOR_SIZE)
+ if self._filename:
+ with open(self._filename, 'rb') as f:
+ while True:
+ chunk = f.read(Application.SECTOR_SIZE)
- if len(chunk) == 0:
- break
+ if len(chunk) == 0:
+ break
- chunks.append(chunk)
+ chunks.append(chunk)
- if len(chunks) > Application.MAX_NUM_CHUNKS:
- sys.stderr.write('File too large.\n')
- return 3
+ if len(chunks) > Application.MAX_NUM_CHUNKS:
+ sys.stderr.write('File too large.\n')
+ return 3
- sector0 = self._offset // Application.SECTOR_SIZE
+ # The bootloader only accepts chunks whose size is word-aligned:
+ num_extra = len(chunks[-1]) & 3
+
+ if num_extra != 0:
+ num_pad = 4 - extra
+ chunks[-1] += b'\xff' * num_pad
+
+ # Defaulting to zero seems too dangerous:
+ if self._offset is None:
+ sector0 = None
+ else:
+ sector0 = self._offset // Application.SECTOR_SIZE
try:
return self._run(sector0, chunks)
for i in range(len(chunks)):
sector = sector0 + i
- self._erase(sector)
+ # The bootloader will refuse to erase the second sector
+ # as it contains the precious flash configuration field.
+ if sector != 1:
+ self._erase(sector)
# Write first sector last, to prevent the bootloader from
# jumping to partially programmed code.
for i, chunk in reversed(list(enumerate(chunks))):
sector = sector0 + i
- self._load_chunk(chunk)
- self._program(sector)
+ if sector != 1:
+ self._load_chunk(chunk)
+ self._program(sector)
num_verify_errors = 0
num_verify_errors += 1
+ if self._do_start and num_verify_errors == 0:
+ self._start_app()
+
if num_verify_errors == 0:
return 0
else:
return False
+ def _start_app(self):
+ self._log(0, 'Starting application... ')
+
+ self._prepare_command(StartAppCommand())
+ self._serial.flush()
+ self._read_and_handle_error()
+
+ self._log(0, 'OK\n')
+
def _prepare_command(self, command):
self._last_command = command
raise InvalidArgument(self._last_command)
elif e == 3:
raise ChecksumMismatch(self._last_command)
+ elif e == 4:
+ raise PermissionDenied(self._last_command)
else:
msg = 'Target reported some other error for {}'.format(self._last_command)
parser = argparse.ArgumentParser(description='Flash GPS watch firmware')
parser.add_argument('-d', '--device', type=str, required=True)
- parser.add_argument('-o', '--offset', type=offset, required=True)
+ parser.add_argument('-o', '--offset', type=offset, default=None)
parser.add_argument('-n', '--dry-run', action='store_true')
parser.add_argument('-l', '--log-level', type=int, default=0)
parser.add_argument('-w', '--write', action='store_true')
parser.add_argument('-v', '--verify', action='store_true')
+ parser.add_argument('-s', '--start', action='store_true')
parser.add_argument('filename', type=str, nargs='?', default=None)
args = parser.parse_args()
- if not args.write and not args.verify:
+ if not args.write and not args.verify and not args.start:
parser.error('one or more of the following arguments are required: ' +
- '-w/--write, -v/--verify')
+ '-w/--write, -v/--verify, -s/--start')
if args.filename is None:
if args.write:
elif args.verify:
parser.error('argument -v/--verify: expected one argument')
+ if args.offset is None:
+ if args.write or args.verify:
+ parser.error('the following arguments are required: -o/--offset')
+
sys.exit(Application(args).run())