# Copyright (c) 2017 Linaro Limited. # # SPDX-License-Identifier: Apache-2.0 '''Runner for flashing with dfu-util.''' from collections import namedtuple import os import sys import time from .. import log from .core import ZephyrBinaryRunner, RunnerCaps, BuildConfiguration DfuSeConfig = namedtuple('DfuSeConfig', ['address', 'options']) class DfuUtilBinaryRunner(ZephyrBinaryRunner): '''Runner front-end for dfu-util.''' def __init__(self, cfg, pid, alt, img, exe='dfu-util', dfuse_config=None): super(DfuUtilBinaryRunner, self).__init__(cfg) self.alt = alt self.img = img self.cmd = [exe, '-d,{}'.format(pid)] 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 @classmethod def name(cls): return 'dfu-util' @classmethod def capabilities(cls): return RunnerCaps(commands={'flash'}, flash_addr=True) @classmethod def do_add_parser(cls, parser): # Required: parser.add_argument("--pid", required=True, help="USB VID:PID of the board") parser.add_argument("--alt", required=True, help="interface alternate setting number or name") # Optional: parser.add_argument("--img", help="binary to flash, default is --kernel-bin") parser.add_argument("--dfuse", default=False, action='store_true', help='''set if target is a DfuSe device; implies --dt-flash.''') parser.add_argument("--dfuse-modifiers", default='leave', help='''colon-separated list of DfuSe modifiers (default is "leave", which starts execution immediately); --dfuse must also be given for this option to take effect.''') parser.add_argument('--dfu-util', default='dfu-util', help='dfu-util executable; defaults to "dfu-util"') @classmethod def create(cls, cfg, args): if args.img is None: args.img = cfg.kernel_bin 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 return DfuUtilBinaryRunner(cfg, args.pid, args.alt, args.img, exe=args.dfu_util, dfuse_config=dcfg) 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): reset = False if not self.find_device(): reset = True log.dbg('Device not found, waiting for it', level=log.VERBOSE_EXTREME) # Use of print() here is advised. We don't want to lose # this information in a separate log -- this is # interactive and requires a terminal. print('Please reset your board to switch to DFU mode...') while not self.find_device(): time.sleep(0.1) 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. reset = False if reset: print('Now reset your board again to switch back to runtime mode.')