You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
137 lines
4.9 KiB
137 lines
4.9 KiB
# Copyright (c) 2017 Linaro Limited. |
|
# |
|
# SPDX-License-Identifier: Apache-2.0 |
|
|
|
'''Runner for flashing with dfu-util.''' |
|
|
|
import sys |
|
import time |
|
from collections import namedtuple |
|
|
|
from runners.core import BuildConfiguration, RunnerCaps, ZephyrBinaryRunner |
|
|
|
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.')
|
|
|