1# Copyright (c) 2017 Linaro Limited. 2# 3# SPDX-License-Identifier: Apache-2.0 4 5'''Runner for flashing with dfu-util.''' 6 7from collections import namedtuple 8import sys 9import time 10 11from runners.core import ZephyrBinaryRunner, RunnerCaps, \ 12 BuildConfiguration 13 14 15DfuSeConfig = namedtuple('DfuSeConfig', ['address', 'options']) 16 17 18class DfuUtilBinaryRunner(ZephyrBinaryRunner): 19 '''Runner front-end for dfu-util.''' 20 21 def __init__(self, cfg, pid, alt, img, exe='dfu-util', 22 dfuse_config=None): 23 super().__init__(cfg) 24 self.alt = alt 25 self.img = img 26 self.cmd = [exe, '-d,{}'.format(pid)] 27 try: 28 self.list_pattern = ', alt={},'.format(int(self.alt)) 29 except ValueError: 30 self.list_pattern = ', name="{}",'.format(self.alt) 31 32 if dfuse_config is None: 33 self.dfuse = False 34 else: 35 self.dfuse = True 36 self.dfuse_config = dfuse_config 37 self.reset = False 38 39 @classmethod 40 def name(cls): 41 return 'dfu-util' 42 43 @classmethod 44 def capabilities(cls): 45 return RunnerCaps(commands={'flash'}, flash_addr=True) 46 47 @classmethod 48 def do_add_parser(cls, parser): 49 # Required: 50 parser.add_argument("--pid", required=True, 51 help="USB VID:PID of the board") 52 parser.add_argument("--alt", required=True, 53 help="interface alternate setting number or name") 54 55 # Optional: 56 parser.add_argument("--img", 57 help="binary to flash, default is --bin-file") 58 parser.add_argument("--dfuse", default=False, action='store_true', 59 help='''use the DfuSe protocol extensions 60 supported by STMicroelectronics 61 devices (if given, the image flash 62 address respects 63 CONFIG_FLASH_BASE_ADDRESS and 64 CONFIG_FLASH_LOAD_OFFSET)''') 65 parser.add_argument("--dfuse-modifiers", default='leave', 66 help='''colon-separated list of additional 67 DfuSe modifiers for dfu-util's -s 68 option (default is 69 "-s <flash-address>:leave", which starts 70 execution immediately); requires 71 --dfuse 72 ''') 73 parser.add_argument('--dfu-util', default='dfu-util', 74 help='dfu-util executable; defaults to "dfu-util"') 75 76 @classmethod 77 def do_create(cls, cfg, args): 78 if args.img is None: 79 args.img = cfg.bin_file 80 81 if args.dfuse: 82 args.dt_flash = True # --dfuse implies --dt-flash. 83 build_conf = BuildConfiguration(cfg.build_dir) 84 dcfg = DfuSeConfig(address=cls.get_flash_address(args, build_conf), 85 options=args.dfuse_modifiers) 86 else: 87 dcfg = None 88 89 ret = DfuUtilBinaryRunner(cfg, args.pid, args.alt, args.img, 90 exe=args.dfu_util, dfuse_config=dcfg) 91 ret.ensure_device() 92 return ret 93 94 def ensure_device(self): 95 if not self.find_device(): 96 self.reset = True 97 print('Please reset your board to switch to DFU mode...') 98 while not self.find_device(): 99 time.sleep(0.1) 100 101 def find_device(self): 102 cmd = list(self.cmd) + ['-l'] 103 output = self.check_output(cmd) 104 output = output.decode(sys.getdefaultencoding()) 105 return self.list_pattern in output 106 107 def do_run(self, command, **kwargs): 108 self.require(self.cmd[0]) 109 self.ensure_output('bin') 110 111 if not self.find_device(): 112 raise RuntimeError('device not found') 113 114 cmd = list(self.cmd) 115 if self.dfuse: 116 # http://dfu-util.sourceforge.net/dfuse.html 117 dcfg = self.dfuse_config 118 addr_opts = hex(dcfg.address) + ':' + dcfg.options 119 cmd.extend(['-s', addr_opts]) 120 cmd.extend(['-a', self.alt, '-D', self.img]) 121 self.check_call(cmd) 122 123 if self.dfuse and 'leave' in dcfg.options.split(':'): 124 # Normal DFU devices generally need to be reset to switch 125 # back to the flashed program. 126 # 127 # DfuSe targets do as well, except when 'leave' is given 128 # as an option. 129 self.reset = False 130 if self.reset: 131 print('Now reset your board again to switch back to runtime mode.') 132