blob: 875780e48025245357a6307a1127cd1f5cdeea15 [file] [log] [blame]
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
'''Runner for flashing with dfu-util.'''
from collections import namedtuple
import sys
import time
from runners.core import ZephyrBinaryRunner, RunnerCaps, \
BuildConfiguration
DfuSeConfig = namedtuple('DfuSeConfig', ['address', 'options'])
class DfuUtilBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for dfu-util.'''
def __init__(self, cfg, dev_id, alt, img, exe='dfu-util',
dfuse_config=None):
super().__init__(cfg)
self.dev_id = dev_id # Used only for error checking in do_run
self.alt = alt
self.img = img
self.cmd = [exe, '-d,{}'.format(dev_id)]
try:
self.list_pattern = ', alt={},'.format(int(self.alt))
except ValueError:
self.list_pattern = ', name="{}",'.format(self.alt)
if dfuse_config is None:
self.dfuse = False
else:
self.dfuse = True
self.dfuse_config = dfuse_config
self.reset = False
@classmethod
def name(cls):
return 'dfu-util'
@classmethod
def capabilities(cls):
return RunnerCaps(commands={'flash'}, dev_id=True, flash_addr=True)
@classmethod
def dev_id_help(cls) -> str:
return 'USB VID:PID of the connected device.'
@classmethod
def do_add_parser(cls, parser):
parser.add_argument("--alt", required=True,
help="interface alternate setting number or name")
# Optional:
parser.add_argument("--pid", dest='dev_id',
help=cls.dev_id_help())
parser.add_argument("--img",
help="binary to flash, default is --bin-file")
parser.add_argument("--dfuse", default=False, action='store_true',
help='''use the DfuSe protocol extensions
supported by STMicroelectronics
devices (if given, the image flash
address respects
CONFIG_FLASH_BASE_ADDRESS and
CONFIG_FLASH_LOAD_OFFSET)''')
parser.add_argument("--dfuse-modifiers", default='leave',
help='''colon-separated list of additional
DfuSe modifiers for dfu-util's -s
option (default is
"-s <flash-address>:leave", which starts
execution immediately); requires
--dfuse
''')
parser.add_argument('--dfu-util', default='dfu-util',
help='dfu-util executable; defaults to "dfu-util"')
@classmethod
def do_create(cls, cfg, args):
if args.img is None:
args.img = cfg.bin_file
if args.dfuse:
args.dt_flash = True # --dfuse implies --dt-flash.
build_conf = BuildConfiguration(cfg.build_dir)
dcfg = DfuSeConfig(address=cls.get_flash_address(args, build_conf),
options=args.dfuse_modifiers)
else:
dcfg = None
ret = DfuUtilBinaryRunner(cfg, args.dev_id, args.alt, args.img,
exe=args.dfu_util, dfuse_config=dcfg)
ret.ensure_device()
return ret
def ensure_device(self):
if not self.find_device():
self.reset = True
print('Please reset your board to switch to DFU mode...')
while not self.find_device():
time.sleep(0.1)
def find_device(self):
cmd = list(self.cmd) + ['-l']
output = self.check_output(cmd)
output = output.decode(sys.getdefaultencoding())
return self.list_pattern in output
def do_run(self, command, **kwargs):
if not self.dev_id:
raise RuntimeError('Please specify a USB VID:PID with the '
'-i/--dev-id or --pid command-line switch.')
self.require(self.cmd[0])
self.ensure_output('bin')
if not self.find_device():
raise RuntimeError('device not found')
cmd = list(self.cmd)
if self.dfuse:
# http://dfu-util.sourceforge.net/dfuse.html
dcfg = self.dfuse_config
addr_opts = hex(dcfg.address) + ':' + dcfg.options
cmd.extend(['-s', addr_opts])
cmd.extend(['-a', self.alt, '-D', self.img])
self.check_call(cmd)
if self.dfuse and 'leave' in dcfg.options.split(':'):
# Normal DFU devices generally need to be reset to switch
# back to the flashed program.
#
# DfuSe targets do as well, except when 'leave' is given
# as an option.
self.reset = False
if self.reset:
print('Now reset your board again to switch back to runtime mode.')