# 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 .core import ZephyrBinaryRunner, RunnerCaps, BuildConfiguration DfuSeConfig = namedtuple('DfuSeConfig', ['address', 'options']) class DfuUtilBinaryRunner(ZephyrBinaryRunner): '''Runner front-end for dfu-util.''' def __init__(self, pid, alt, img, exe='dfu-util', dfuse_config=None, debug=False): super(DfuUtilBinaryRunner, self).__init__(debug=debug) 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_from_args(cls, args): if args.img is None: args.img = args.kernel_bin if args.dfuse: args.dt_flash = True # --dfuse implies --dt-flash. build_conf = BuildConfiguration(os.getcwd()) dcfg = DfuSeConfig(address=cls.get_flash_address(args, build_conf), options=args.dfuse_modifiers) else: dcfg = None return DfuUtilBinaryRunner(args.pid, args.alt, args.img, exe=args.dfu_util, dfuse_config=dcfg, debug=args.verbose) 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 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.')