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